深度分析 Cursor 等 AI 编程工具中 SKILL.md 和 hooks 的实际工作原理——它们是提示而非约束,并给出 Layer 1(Skill)+ Layer 2(可失败的 Bash 命令)的可靠执行框架。
If you use Cursor long enough, you will watch it ignore a rule you wrote down.
You add a SKILL.md. You tell it "no explicit any". You maybe add a hook. It agrees. Then it ships id: any anyway.
A comment on a thread about skill files put it cleanly: the model can work around hooks too. That is not a Cursor bug. It is what "the instruction is text" means.
A SKILL.md is a document the agent is supposed to read when the task matches. .cursor/rules and hooks are the same family: they change the prompt, not the type system.
Attention is limited. Mid-thread, the ratio of "your rules" to "the last twenty tool results" gets worse. The model locally prefers completing the user's latest sentence over satisfying a file it saw at the start of the turn.
Skills are good at default shape: interfaces, try/catch, test file next to the change.
Skills are bad at guarantees.
If your workflow assumes the markdown file is a compiler, you will keep getting surprised.
Treat generation and verification as different jobs.
Layer 1 — The Skill
Tell the agent how you want code to look. Keep it short. Long skill files are just more tokens to ignore.
Layer 2 — A Command That Can Fail
After the edit, run whatever this repo already uses:
npx tsc --noEmit
npm test -- --run path/to/changed-file
Use the scripts in package.json if they exist. Do not invent a parallel toolchain so the agent can look thorough.
The skill's job is to remember to run Layer 2, and to refuse the sentence "this is production-ready" when Layer 2 did not run.
That is the whole product. Not a smarter prompt. A prompt that stops lying when tsc is red.
Layer 1 alone often still emits this:
async function getUser(id: any) {
const res = await fetch('/api/user/' + id)
return res.json()
}
You can write a skill that forbids any. Sometimes it works. Sometimes the chat is long and it does not.
Layer 2 does not care about the chat. tsc --noEmit fails until id is typed and res.ok is handled. Tests fail until a non-200 is covered. The agent then has to edit against a failing command, which is a much stronger signal than a paragraph in SKILL.md.
Keep skills tiny: Four rules beat forty.
End requests cleanly: End every "make this production-ready" request with "run typecheck and tests, paste the output."
Reset threads: Start a new chat when the thread is doing two features. Skills do not reset attention math.
Use hooks strictly: If you use hooks, keep them for formatters and secret scans — deterministic tools. Do not expect a hook to be a second personality that the model cannot dodge.
The SKILL.md I use for this is a two-layer review gate: read the diff, then run the repo's typecheck/lint/test and print the commands. It lives in this studio as two-layer-review-gate. Use it as a template; the valuable part is Layer 2, which you already have if your repo has tsc.
I will put the listing on PromptBase after the current reviews clear. Until then, the article is the whole method.
Subscribe if you want the next one: how to hand a session to a fresh chat without dumping 40k tokens of history into the new thread.
Originally published on Substack: https://apparatusappello861559.substack.com/p/skillmd-is-not-a-compiler
For further actions, you may consider blocking this person and/or reporting abuse