{"slug": "skills-not-docs", "title": "Skills, not docs", "summary": "A developer behind the @relax.js/core JavaScript library released an `init-agents` CLI command that writes seven Claude Code skill files into `.claude/skills/`, one per area of the framework, each stamped with the package version it came from so stale copies can be detected after upgrades. The author argues skills should encode design-level habits and wrong defaults rather than API facts, reserving documentation for implementation details, and reports that an earlier single monolithic instruction file rotted because there was no signal for which sentences went stale.", "body_md": "Second in a series on using [@relax.js/core](https://www.npmjs.com/package/@relax.js/core) with a coding agent. The first piece made the argument; this one is about the first thing you do in a project.\n\n```\nnpx @relax.js/core init-agents\n```\n\nThat writes seven files into `.claude/skills/`, one per area: the core model, then templates, forms, routing, services, testing and setup. Claude Code loads a skill when its description matches what the agent is doing. Other tools without skill support can be pointed at `node_modules/@relax.js/core/skills/relaxjs/SKILL.md` from their instruction file; the core skill links to the rest.\n\nEach copy is stamped with the package version it came from. Run the command again after an upgrade and it lists the copies that are behind, and leaves them alone unless you pass `--force`. A skill is a snapshot, and a snapshot that describes an older library is worse than none, because the agent trusts it.\n\nThe interesting decision was not to write the skills. It was deciding what does not go in them.\n\nThe library has a `docs/` folder like any other. An agent could read it, and sometimes it should. But documentation is written for someone who already knows they need this API. It answers \"how does this work, what is available\". \n\nA skill loads before the agent knows it has a problem. Its job is to overwrite a wrong default and route to the right doc. The `skills/README.md` in the package states two tests for where a sentence belongs:\n\nWould an agent that never read this produce code that compiles, type-checks and does nothing? Skill. Would it merely not know a name? Docs.\n\nWould the sentence need editing when the implementation changes? Docs. Only when the design changes? Skill.\n\nThe two must not overlap. A skill that accumulates examples is turning into a doc, and should hand off to one instead. The only thing a skill repeats from a doc is its filename.\n\nHere is the whole \"Do not\" section of the core skill:\n\n```\n## Do not\n\n- Reach for a state store, computed properties or a reactive wrapper. Update the DOM where the\n  change happens.\n- Add a component base class, a render loop or a diffing layer.\n- Use `CustomEvent`, or `enum` where a `declare type` string union works.\n- Duplicate native HTML. Use `<dialog>`, `<details>`, `<input type=\"date\">` and friends before\n  writing a component.\n- Swallow errors. An empty `catch` is a bug.\n```\n\nEvery line is a habit. None of them is a fact about an API. An agent that never reads this will write a store, a base class and a `CustomEvent`, and all three will compile.\n\nCompare the forms skill, which opens with the one thing agents get wrong most:\n\n```\n## FormValidator owns the submit event\n\nIts constructor attaches the listener. Do not add your own, and construct one even when you have\nno validation rules, because taking over submit is what it is for. Supplying `submitCallback`\nsuppresses the native submit, so the page never navigates away.\n```\n\nThen a diagnosis, because skills are also loaded when something is already broken:\n\n```\nA form that still navigates away on submit means no `FormValidator` was constructed for it.\n```\n\nAnd at the bottom, the hand-off:\n\n```\n## Detail\n\n- `@relax.js/core/docs/forms/form-page.md` for the end-to-end shape of an edit page. Start here\n- `@relax.js/core/docs/forms/validation.md` for rules, the error summary and every option\n```\n\n`init-agents` rewrites those `@relax.js/core/docs/` references to the real path of the installed package, so the agent can follow them without knowing where `node_modules` is.\n\nI tried the other shape first: one big instruction file with everything in it. It rotted in two ways.\n\nFirst, every sentence that described how something worked went stale when that thing changed, and there was no signal which sentences. Splitting on \"does this change when the implementation changes, or only when the design changes\" is exactly the signal: the docs get updated with the code, the skills get updated with the design, and the design changes rarely.\n\nSecond, a long file is context spent. A skill that is loaded on every UI task and carries an example of every option costs the same as the code the agent is supposed to be writing. Short skills that route to long docs let the agent spend its context on the problem, and pull the reference in only for the part it is actually touching.\n\nI said in the first piece that greppability matters because agents navigate by search. The skills lean on that. When a skill says \"see `docs/forms/form-page.md`\", the agent opens the file. When it says \"` FormValidator.FindForm(this)`\", the agent greps it and lands in the source. A skill never describes a mechanism the agent cannot then find by name.\n\nThat is also the test I used when writing one. Pick any identifier in the skill and search the package for it. If the search lands on the thing being described, the sentence belongs. If it only lands back in the skill, the sentence is prose about a convention, and conventions are what agents guess at.\n\nNext: the model itself, and why it fits on one page.", "url": "https://wpnews.pro/news/skills-not-docs", "canonical_source": "https://dev.to/jgauffin/skills-not-docs-50b0", "published_at": "2026-09-20 09:03:09+00:00", "updated_at": "2026-09-20 09:24:33.789008+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["@relax.js/core", "Claude Code", "npm"], "alternates": {"html": "https://wpnews.pro/news/skills-not-docs", "markdown": "https://wpnews.pro/news/skills-not-docs.md", "text": "https://wpnews.pro/news/skills-not-docs.txt", "jsonld": "https://wpnews.pro/news/skills-not-docs.jsonld"}}