How to Wire Company-Wide Coding Standards into Cursor Agent Skills
Full-Project Skills Adaptation: Turning a "Code-Writing Agent" into a "Colleague Who Follows Your Standards"
Using Cursor Agent Skills and a B-side frontend self-test skill as an example, this explains how to connect company-level standards to a specific project, rather than just piling up a never-ending prompt.
If you've been using agents like Cursor / Claude Code to write business code recently, you've probably run into the same kind of problems:
- It's great at writing Vue, but it won't follow your directory structure, API wrappers, or permission directives.
- It can fill in a form, but the submit button has no loading state, a failure clears the inputs, and after a query the pagination stays on page 3.
- You paste the standards into the conversation, and it forgets them by the next round; you write them into a super-long Rule, the context gets flooded, and it actually gets duller when writing real code.
In our HR backend (Vue 3 + Vite + TypeScript + Element Plus, evolved from vue-pure-admin), we split this into two layers:
- Project Constitution:
AGENTS.mdholds only the constraints that "always hold true for this repo." - On-Demand Skills: Processes like "test-ready self-check" and "interaction walkthrough" are made into Skills, loaded only when needed.
Below, using the already-implemented frontend-self-test as an example, I'll explain how to do full-project adaptation.
1. First, Distinguish: What Rule, AGENTS.md, and Skill Each Manage
Many people start by stuffing all standards into .cursorrules or an Always Apply Rule. It works short-term but inevitably bloats over time.
| Carrier | Load Timing | Suitable For | Not Suitable For |
|---|---|---|---|
AGENTS.md / Always Rule |
Almost every conversation | Tech stack, directory responsibilities, minimal changes, package manager, prohibitions | Dozens of pages of interaction details, acceptance checklist for a single requirement |
.cursor/rules/*.mdc |
Triggered by file glob | Local conventions for a certain file type (e.g., only constraining src/api/**) |
Cross-page complete workflows |
| Agent Skill | Agent judges relevance, or you manually /skill-name |
Reusable processes: how to self-test, how to release, how to align tables | Project positioning, default tech choices |
Cursor's official positioning for Skills is also clear: it is a version-manageable, on-demand expandable capability pack. A Skill is a directory whose core is SKILL.md; detailed rules can be split into reference.md and scripts/. The agent first sees the name + description, and only reads the body and referenced files when actually executing. This is called progressive disclosure: the main file stays short, and details are opened on demand.
In one sentence:
- AGENTS.md tells the Agent "which project you are in."
- Skill tells the Agent "what process to follow for this kind of task."
Stacked together, this is full-project adaptation, not just "writing one more Markdown file."
2. The Goal of Full-Project Adaptation Is Not "A Few More Skill Files"
Once adaptation is done, anyone on the team opening this repo should see these results:
- New colleagues don't need to memorize standards first. Saying "help me self-test this change" makes the Agent read the standards, scope the area, and produce a report on its own.
- Company and project don't pollute each other. Company-level B-side interaction standards can be reused across multiple backends; this repo's Vue / directory / permission patterns are only written at the project layer.
- Trigger words are stable. "Self-test," "test-ready self-check," "interaction standard check" all hit the same Skill, rather than relying on someone remembering the full filename.
- Context doesn't explode. Changing a resignation form day-to-day won't flood the entire 14-chapter interaction standard into the System Prompt.
The structure we ultimately landed on looks like this:
hr-web/
├── AGENTS.md # Project constitution + custom Skill manifest
├── .gitmodules # Company Skills repo as submodule
├── skills/ # Upstream: universal Skill source
├── .cursor/skills/
│ └── frontend-self-test/
│ ├── SKILL.md # Process: how to self-test, how to report
│ └── reference.md # Details: 14 categories of interaction musts/prohibitions
└── package.json
├── update:skills # Pull submodule
└── skills:sync:cursor # Sync to .cursor/skills
This is a very important chain:
Company Skills Repo → git submodule → sync into .cursor/skills → Cursor auto-discovers
↑
AGENTS.md registers trigger words
.cursor/skills/ is the project-level directory that Cursor actually scans. If the source files in the submodule aren't synced over, the Agent cannot see them. Cloud Agent and remote SSH also can't read your local ~/.cursor/skills/, so to make it consistent for the whole team and all environments, Skills must enter the repo.
3. How to Write the Project Constitution: AGENTS.md Only Contains What "Always Holds True"
Taking this repo as an example, AGENTS.md does not repeat the full interaction standard text; it only locks in what the Agent "will do by default" in this HR backend:
- Tech baseline: Vue 3,
script setup+ TypeScript, Vite 5, Pinia, vue-i18n, use pnpm only. - Directory responsibilities: pages in
src/views, APIs insrc/api, state insrc/store/modules, static routes viasrc/router/modules. - Request layer: prohibit hand-writing axios in pages, uniformly use
@/utils/request. - Modification boundaries: minimal changes, don't overwrite others' uncommitted code, don't casually refactor unrelated modules.
- Verification requirements: if only docs changed, no build needed; if code changed, do static self-check first; do not proactively run
pnpm dev/ full build.
These contents should take effect in every conversation, so they go in the constitution layer.
The Skill manifest gets its own section, acting only as a "routing table":
## Project Custom SKILLS Manifest
When user input contains the following keywords, automatically invoke the corresponding custom skill:
- **frontend-self-test** (Frontend Self-Test)
- Trigger keywords: self-test, frontend self-test, test-ready self-check, interaction standard check,
B-side backend frontend code walkthrough, self-check checklist check, backend project self-test
- Directory: skills/skills/frontend-self-test
Two easily overlooked points here:
- Trigger words must be written in natural language. The Agent matches against the description and conversational intent, not the internal codename in your head. Only writing
frontend-self-testmeans a colleague saying "run me through it before testing" often won't hit. - The directory can point to the source repo, but what Cursor consumes is the synced
.cursor/skills. The manifest is an index for humans; the discovery mechanism still relies on Cursor's official directory.
4. Using the Self-Test Skill as an Example: What a Qualified Skill Looks Like
frontend-self-test doesn't solve "is this code ugly?" but rather:
Against the "B-side Backend Frontend Universal Interaction Implementation Standard," perform a pre-test walkthrough on this change and give actionable conclusions in the conversation.
It is deliberately made into two files, corresponding to the progressive disclosure mentioned earlier.
1. SKILL.md: Only Process, No Encyclopedia
The frontmatter clearly states both what it does and when to do it:
---
name: frontend-self-test
description: >
Based on the "B-side Backend Frontend Universal Interaction Implementation Standard," perform a pre-test self-check on frontend code changes:
analyze git diff or specified files, walk through item by item for request states, duplicate submission prevention, data refresh,
form validation, list pagination, modals/drawers, state distinction, async tasks, permissions, search race conditions,
upload/download, operation feedback, and other interaction rules, and report the self-test results in the conversation.
Use when the user requests frontend self-test, test-ready self-check, interaction standard check,
B-side backend frontend code walkthrough, self-check checklist check.
---
description gets injected into the Agent's skill directory, essentially a "job posting." Writing it in the third person, with trigger scenarios, is far more useful than "I can help you self-test."
The body only specifies five steps and requires copying a checklist to track progress:
- Determine scope: prioritize
git diff master...HEAD; if there are uncommitted changes, also include the working tree diff. - Identify categories: among the 12 interaction categories, only walk through those actually touched by the change; mark the rest "Not Applicable."
- Check against detailed rules: for each item, give only three conclusions — Pass / Risk / Needs Manual Verification.
- Run existing engineering checks: run lint, typecheck, test only if they exist; do not install dependencies without permission, do not proactively build.
- Report using a fixed template: do not save a separate report file, to avoid polluting the repo with Markdown.
Among these five steps, the most valuable part isn't "check for loading" but locking down the Agent's degrees of freedom:
- Only draw conclusions based on the changes read; prohibit guessing about unopened files.
- Where the standard marks a "judgment criterion," judge by that criterion; do not arbitrarily tighten it.
- Page structure, field copy, and business flow belong to product; the Skill only constrains interaction implementation.
- Exceptions declared in the PRD or conversation are directly accepted.
Without these boundaries, the Agent easily turns into "a code reviewer who wants to critique everything."
2. reference.md: Details Externalized, Read Only When Used
The beginning of SKILL.md states: before the walkthrough, you must first read reference.md. The standard text covers roughly 14 categories, including:
- Requests must have state
- Prevent duplicate submissions
- Update the page promptly on success
- Don't discard user input on failure
- Form validation points to the field
- List queries and pagination
- Returning to a list preserves the context
- Modals and drawers
- Separate loading / empty data / failure / no permission states
- Async long-running tasks
- Permissions
- Search and race conditions
- Upload and download
- Operation feedback
The key design is: must-do / prohibited appear in pairs, and quantifiable items are written as 【Judgment Criteria】. For example:
- Successful addition: judged by "the list has been re-requested"; do not report a defect just because sorting causes new data to not appear on the current page.
- Deleting the last item on the current page: if page number > 1, must go back to the previous page before reloading.
- Buttons without permission: hiding or graying out are both acceptable; as long as it's consistent within the same product, it's not a defect.
What the Agent fears most are non-judgeable phrases like "poor experience" or "not elegant enough." The more the criteria resemble test cases, the more stable the walkthrough results.
5. Connecting Universal Standards to a Specific Project: Where Does Adaptation Happen?
A company-level Skill won't write "which Element Plus component you use." It only says "the button must enter a loading state during submission." The actual connection to the HR backend relies on the project constitution + the existing implementations in the repo.
Below, using patterns already present in this repo, I'll illustrate what the Agent should "translate" them into.
1. Request State and Duplicate Prevention: Mapping to loading + Shared Modal
The standard says: button immediately enters loading on click, recovers when the request ends, and must also recover on failure.
The resignation application modal in the project is a typical implementation: LoadingDialog receives :loading, the confirm button binds the same state, and it closes in finally.
<LoadingDialog
v-model="dialog.visible"
:loading="loading"
:title="dialog.title"
destroy-on-close
>
<!-- form -->
<el-button type="primary" :loading="loading" @click="saveForm">Submit</el-button>
</LoadingDialog>
function handleSubmit() {
loading.value = true
const request = dialog.title === "Resignation Application" ? leaveAdd : leaveDirect
request(params)
.then(() => {
ElMessage.success("Submitted successfully")
close()
emits("ok")
})
.finally(() => {
loading.value = false
})
}
During self-test, the Agent shouldn't invent a new "global full-screen Loading" but should check:
- Whether the button and modal share the
loadingstate. - Whether the failure branch misses
finally. - Whether table queries only lock the table area, not
v-loadingthe entire page.
Secondary confirmation is also common. In this repo, before submission, an ElMessageBox may pop up; if the user clicks cancel, the loading that was already set to true must be flipped back, otherwise the button spins forever. This is the concrete form in the project of the standard's "page recovers to operable after request failure / cancellation."
2. Refresh on Success: Mapping to "Close Modal + Emit ok"
The standard requires the page to update immediately after add/edit/delete. In this repo, many drawers and modals don't modify the list within themselves but instead:
ElMessage.success("Submitted successfully")
close()
emits("ok")
The parent list hears ok and then re-fetches the paginated data. During self-test, you must follow the emit to glance at the parent page; you can't just judge "Pass" because the child component "popped a success toast." This is the balance between "only draw conclusions based on changed code" and "related call chains must be read": if you changed a form, you must at least open its parent index.vue.
3. API Layer: Mapping to src/api + Unified request
AGENTS.md prohibits hand-writing axios in pages. The self-test skill itself doesn't audit architecture, but after project adaptation, if the Agent sees a new axios.post appear in a page, it should treat it as a risk: it violates the project constitution and bypasses the unified 401 handling, error prompts, silent, unloading, and other conventions.
This repo's @/utils/request already handles tokens, login expiration redirects, and permission timestamp refresh. The Skill doesn't need to copy the interceptor source code into itself; the project constitution naming "reuse request" is sufficient.
4. Permissions: Mapping to v-auth / hasPerms, Don't Write Roles Yourself
The standard states: frontend permissions only control display and entry points, not serve as security checks; don't hardcode roles in the frontend.
The project counterpart is directives, not if (user.role === 'admin'):
// src/directives/perms/index.ts
!hasPerms(value) && el.parentNode?.removeChild(el)
When walking through the "Permissions" category, check:
- Whether buttons use existing directives or
hasPerms. - Whether requests are still sent when there's no permission.
- Whether no-permission, load-failure, and empty-list are three distinct UIs.
There's no need to mandate a site-wide uniform "hide" or "gray out" because the standard already lists this as a judgment criterion.
5. Form Validation and Failure Preservation: Mapping to el-form Field Rules
The standard requires errors to appear next to the field and the form not to be cleared on submission failure. Element Plus's el-form-item + :rules is the project's default solution.
In the resignation form, when "Add to blacklist" is Yes, the blacklist reason and description fields appear with required rules. This is conditional field validation; during self-test, check: after hidden fields are re-shown, are the rules still present; after submission failure, is formData still there.
There's one "translation" in the project worth the Agent learning: some pages, on validation failure, additionally call ElMessage.error('Please complete the form'). What the standard prohibits is "only saying the form has errors at the top, leaving the user unable to find the field." If field-level el-form errors already exist, an extra summary message is generally not judged a defect; only if there's just a global toast with no field hints is it recorded as a risk.
6. Engineering Checks: Mapping to the Scripts This Repo Actually Has
Step 4 of the Skill requires reading package.json first. This repo actually has:
| Script | During Self-Test |
|---|---|
pnpm lint:eslint |
Only run on changed files, acceptable |
pnpm typecheck |
Acceptable |
pnpm test |
Does not exist, skip and state clearly |
pnpm build |
Only run if user explicitly requests |
"Run if exists, skip if not" is written into the Skill to prevent the Agent from suddenly executing npm test or installing Vitest in the HR backend. This is project adaptation: universal process + local script facts.
6. What a Real Trigger Should Look Like
Suppose you've finished changing "Resignation Application" and the list export, and you say to the Agent:
Help me do a frontend self-test, run through the interaction standards before testing.
Following the adapted behavior, it shouldn't directly modify code but should:
Step 1, scope the area:
git --no-pager diff master...HEAD
git status --porcelain
# If there are uncommitted changes, also:
git --no-pager diff
Step 2, identify categories. Taking the resignation module above as an example, it would typically hit: Request & Loading, Duplicate Submission Prevention, Form Validation, Modal/Drawer, Operation Feedback, Upload/Download; Permissions, Search Race Conditions, Async Tasks might be marked Not Applicable.
Step 3, check against reference.md item by item and give conclusions; risks must include file:line number.
Step 4, run eslint / tsc --noEmit on changed files.
Step 5, report using the template, not generating a new Self-Test Report.md:
## Self-Test Results
Scope: employeeRelations/leave under index / addForm / exportForm
Categories involved: Request & Loading, Duplicate Submission Prevention, Form Validation, Modal, Upload/Download, Operation Feedback
### Pass (n)
- 【Request & Loading】Submit button binds loading, finally closes
- 【Form Validation】Required fields use el-form-item rules
### Risk (n)
- 【Duplicate Submission Prevention】xxx.vue:408 — loading set before secondary confirmation,
if confirmation logic branches without reset, button may freeze — add loading = false in cancel branch
### Needs Manual/Runtime Verification (n)
- 【Modal】After destroy-on-close, does reopening retain validation red text — open, fill incorrectly, close, reopen and check
### Not Applicable (n)
- 【Search Race Condition】No input-triggered search in this change
- 【Async Long Task】Export is still synchronous download, not routed through task center
Engineering checks: eslint run on changed files; typecheck passed; no test script, skipped
Conclusion: 1 risk to fix; does not meet test-ready standard before fix
Risks are sorted by severity: duplicate submission causing dirty data, losing user input, page freeze always rank above "copy not friendly enough."
This output has two benefits: QA can use it as a pre-test checklist; after you fix the risks, you can have the Agent only re-walk the corresponding items, not re-audit the entire page.
7. Pitfalls We Hit When Rolling This Out Across the Project
1. Stuffing the Full Standard Text into Always Rule
The interaction standard is very long. Always loading it squeezes out business code and git diff. The correct split is: AGENTS.md only keeps "there is a self-test skill, trigger words are these"; the detailed rules stay in the Skill's reference.md, read only on the day of the walkthrough.
2. Only Having Submodule, Not Syncing to .cursor/skills
The company repo is suitable as a single source of truth, upgraded via git submodule. But Cursor scans .cursor/skills/ and .agents/skills/. We use:
pnpm update:skills # git submodule update --init --remote
pnpm skills:sync:cursor # sync to the directory Cursor can discover
If someone freshly cloning the repo only runs pnpm install and forgets to pull the submodule, they'll get "the docs mention a self-test skill, but the Agent never uses it in conversation."
3. Description Too Abstract
"Helps improve code quality" won't be triggered. "Based on X standard, walk through diff, used when user says self-test/test-ready self-check" will. Writing in the commands the team already uses internally is more useful than crafting a pretty English summary.
4. Skill and Project Constitution Fighting for Authority
The self-test standard is company-level, cross-project; "don't add new dependencies," "don't modify vite.config.ts," "user-visible copy must sync locales/zh-CN.yaml and en.yaml" belong to this repo. Don't write the latter into a universal Skill, or it will misfire when synced to other projects. Conversely, don't copy the 14 chapters of interaction details into AGENTS.md, or you'll miss changes when upgrading the standard.
5. Letting the Agent Casually Change Business Logic
The Skill states: if the user didn't ask to fix, only report. This aligns with this repo's "minimal changes, no casual refactoring." Otherwise, one "help me self-test" turns into a scope-creeping refactor.
6. Treating Personal Skills as Project Adaptation
~/.cursor/skills/ only works on your machine. Cloud Agent, colleagues, and agents in CI can't see it. The hallmark of full-project adaptation is: after cloning the repo + running the sync script, the behavior is reproducible.
8. A Directly Copyable Adaptation Checklist
If you want to connect the same set of company Skills to the next backend project, follow this order; you don't need to invent new standards from scratch.
1. Write the Project Constitution First (half an hour to half a day)
- Tech stack, package manager, directory responsibilities, request/permission/i18n entry points.
- Modification boundaries and verification boundaries (when builds are allowed, when installing packages is prohibited).
- Explicitly state "which existing capabilities to preferentially reuse": for this repo, it's
src/api,@/utils/request,v-auth/hasPerms, personnel selector, org selector, dictionary store.
2. Connect the Skill Source, Don't Copy-Paste
- Company repo via submodule or package publishing.
- Provide a one-click sync to
.cursor/skills/. - In
AGENTS.md, register the Skill name, natural-language trigger words, and source directory.
3. Every Skill Follows "Short Process + Externalized Details"
SKILL.mdstays within a scannable length (official recommendation: don't let the main file bloat to hundreds of lines constantly adding concepts).descriptionwritten in third person, containing WHAT + WHEN.- Judgments written as Pass / Risk / Not Applicable / Needs Manual Verification; reject "suggest optimizing."
- Output template fixed to reduce improvisation each time.
4. Do One Project Translation, But Don't Modify the Universal Standard Text
Translation happens in the constitution and examples, e.g.:
| Universal Standard | This Project's Counterpart |
|---|---|
| Button loading, duplicate prevention | loading + :loading + finally |
| Table area loading | Table / list container v-loading, don't lock layout |
| Field-level validation | el-form + el-form-item rules |
| Permission control | v-auth / hasPerms, don't hardcode roles |
| Refresh on success | emits('ok') then parent re-fetches list |
| Download feedback | Button loading + downFile; long tasks escalate to async |
| Engineering checks | lint:eslint, typecheck, skip if no tests |
5. Prototype with a Real Requirement
Pick a module that simultaneously has a list, modal, and export (we used resignation under Employee Relations), and just say "self-test" to the Agent. See if it will:
- Read
reference.mdon its own - Diff on its own
- Skip irrelevant categories on its own
- Include line numbers with risks
- Not proactively change code, not proactively do a full build
If this step fails, it means the trigger words, directory, or process instructions aren't connected yet; don't continue rolling out more Skills.
9. What Can Be Connected After the Self-Test Skill
frontend-self-test is suitable as the first project-level Skill because it meets three conditions:
- High frequency: needed before almost every requirement goes to testing.
- Judgeable: easier to write criteria for than "code elegance."
- Complementary to the project constitution: one manages how to change the repo, the other manages whether the change looks like a test-ready backend.
On the same layered foundation, it's easy to connect later:
- API Field Alignment: only allow changing types in
src/apiand page mappings; prohibit casually changing interactions. - i18n Completion: newly added user-visible copy must sync Chinese and English YAML, consistent with this repo's constraints.
- Permission Point Verification: when adding new buttons, prompt for frontend/backend permission keys and whether directives are paired.
- List Page Scaffolding: defaults to pagination returning to page 1, separated empty/failure states, preserving filters on return.
Still recommended: one Skill does only one type of task. Full-project adaptation is "constitution + skill directory," not "one super prompt pack to rule them all."
10. Conclusion
An Agent won't automatically become a colleague familiar with your backend. What it lacks isn't more adjectives but three things written into the repo:
- What this project is —
AGENTS.md - How to do this type of task — the process and output template in
SKILL.md - How to judge right and wrong — the musts/prohibitions with criteria in
reference.md
Company standards live upstream; the project connects them via submodule sync and constitution-layer translation; Cursor only consumes .cursor/skills. Using the self-test skill as an example, we didn't make the Agent learn "HR business"; we only made it, before every test submission, check loading, duplicate submission, refresh, validation, pagination, and permissions against the same checklist.
This is the result that full-project Skills adaptation truly aims to deliver: change the colleague, change the machine, change the conversation — the walkthrough standard remains.