@vibe-agent-toolkit/vat-development-agents 0.1.37 → 0.1.39-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/dist/.claude/plugins/marketplaces/vat-skills/.claude-plugin/marketplace.json +1 -1
  3. package/dist/.claude/plugins/marketplaces/vat-skills/CHANGELOG.md +204 -0
  4. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/.claude-plugin/plugin.json +1 -1
  5. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-adoption-and-configuration/SKILL.md +4 -1
  6. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-agent-authoring/SKILL.md +4 -1
  7. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-audit/SKILL.md +4 -1
  8. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-enterprise-org/SKILL.md +3 -1
  9. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-knowledge-resources/SKILL.md +61 -1
  10. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-rag/SKILL.md +4 -1
  11. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-skill-authoring/SKILL.md +27 -1
  12. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-skill-distribution/SKILL.md +8 -6
  13. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-skill-review/SKILL.md +4 -1
  14. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vibe-agent-toolkit/SKILL.md +5 -1
  15. package/dist/generated/resources/skills/CLAUDE.js +3 -3
  16. package/dist/generated/resources/skills/SKILL.js +3 -3
  17. package/dist/generated/resources/skills/markdown-rewriting.d.ts +28 -0
  18. package/dist/generated/resources/skills/markdown-rewriting.js +48 -0
  19. package/dist/generated/resources/skills/vat-knowledge-resources.d.ts +2 -0
  20. package/dist/generated/resources/skills/vat-knowledge-resources.js +11 -1
  21. package/dist/generated/resources/skills/vat-skill-authoring.d.ts +1 -0
  22. package/dist/generated/resources/skills/vat-skill-authoring.js +6 -1
  23. package/dist/generated/resources/skills/vat-skill-distribution.js +3 -3
  24. package/dist/skills/vat-adoption-and-configuration/SKILL.md +4 -1
  25. package/dist/skills/vat-agent-authoring/SKILL.md +4 -1
  26. package/dist/skills/vat-audit/SKILL.md +4 -1
  27. package/dist/skills/vat-enterprise-org/SKILL.md +3 -1
  28. package/dist/skills/vat-knowledge-resources/SKILL.md +61 -1
  29. package/dist/skills/vat-rag/SKILL.md +4 -1
  30. package/dist/skills/vat-skill-authoring/SKILL.md +27 -1
  31. package/dist/skills/vat-skill-distribution/SKILL.md +8 -6
  32. package/dist/skills/vat-skill-review/SKILL.md +4 -1
  33. package/dist/skills/vibe-agent-toolkit/SKILL.md +5 -1
  34. package/package.json +4 -4
  35. package/dist/.claude/plugins/marketplaces/vat-skills/plugins/vibe-agent-toolkit/skills/vat-skill-distribution/resources/marketplace-distribution.md +0 -427
  36. package/dist/skills/vat-skill-distribution/resources/marketplace-distribution.md +0 -427
@@ -7,7 +7,7 @@ export const meta = {
7
7
  description: "Use when starting VAT work or deciding which VAT sub-skill applies. Router that points at sub-skills for adoption, skill/agent authoring, audit, distribution, RAG, knowledge resources, skill review, and enterprise org admin."
8
8
  };
9
9
 
10
- export const text = "\n# Vibe Agent Toolkit\n\n**Vibe Agent Toolkit (VAT)** is a modular toolkit for building, packaging, and distributing portable AI agents and skills that work across multiple Claude surfaces and adjacent frameworks. Write skill or agent content once; VAT handles validation, packaging, plugin/marketplace layout, and npm publishing.\n\nThis is a router skill. Load the sibling sub-skill that matches the work you\'re doing — each sub-skill owns one slice of VAT\'s surface and is designed to be pulled in on demand.\n\n## When to Use VAT\n\nGood fits:\n\n- Publishing a Claude skill or plugin to npm with a proper marketplace layout\n- Multi-runtime agent projects (Vercel AI SDK, LangChain, OpenAI, Claude Agent SDK)\n- Validating plugins / skills / marketplaces with \`vat audit\` before shipping\n- Enforcing frontmatter schemas across large markdown corpora\n- Wiring RAG indexing into an agent project\n\nPoor fits:\n\n- Simple one-off scripts where the framework is already decided\n- Non-TypeScript/JavaScript projects\n- Cases where you need deep framework-specific features with no reuse goal\n\n## Picking a Sub-skill\n\n| If you\'re working on... | Load |\n|---|---|\n| New project setup, \`vibe-agent-toolkit.config.yaml\` orientation, repo structure, vibe-validate integration, npm postinstall | \`vibe-agent-toolkit:vat-adoption-and-configuration\` |\n| Writing or revising a SKILL.md — frontmatter, body, references, packagingOptions, validation overrides | \`vibe-agent-toolkit:vat-skill-authoring\` |\n| TypeScript agent archetypes, \`agent.yaml\`, result envelopes, orchestration, runtime adapters | \`vibe-agent-toolkit:vat-agent-authoring\` |\n| \`vat audit\` on plugins, marketplaces, skills, or settings — including \`--compat\`, \`--exclude\`, \`--user\`, CI use | \`vibe-agent-toolkit:vat-audit\` |\n| Markdown collections, \`resources:\` config, frontmatter schema validation, \`vat resources validate\` | \`vibe-agent-toolkit:vat-knowledge-resources\` |\n| \`vat build\`, \`vat verify\`, plugin/marketplace layout, npm publishing, postinstall | \`vibe-agent-toolkit:vat-skill-distribution\` |\n| \`vat rag index\` / \`vat rag query\`, embedding providers, vector stores, chunking | \`vibe-agent-toolkit:vat-rag\` |\n| Pre-publication quality review, \`vat skill review\`, validation-code triage | \`vibe-agent-toolkit:vat-skill-review\` |\n| Anthropic Admin API: org users, cost/usage, workspace skills, \`ANTHROPIC_ADMIN_API_KEY\` | \`vibe-agent-toolkit:vat-enterprise-org\` |\n\n## CLI Surface at a Glance\n\n\`\`\`bash\nvat --help # top-level help\nvat build # build all artifacts (skills → claude plugins)\nvat verify # validate all artifacts\nvat skills validate # validate skill quality\nvat resources validate # validate markdown collections\nvat audit # audit plugins/skills/marketplaces/settings\nvat rag index docs/ # index markdown for RAG\nvat skill review <path> # pre-publication review\nvat claude org --help # enterprise admin surface\n\`\`\`\n\nEach sub-skill covers its slice of the CLI in depth — don\'t memorize this table, load the sub-skill when you need detail.\n\n## Getting Help\n\n- \`vat --help\` and \`vat <group> --help --verbose\` for CLI depth\n- Repo docs: the VAT repository\'s \`docs/\` directory carries the full setup guide, architecture notes, and design references (not bundled with this plugin)\n- Contributor reference docs (debugging VAT, install architecture) live under \`docs/contributing/\` in the VAT repo\n";
10
+ export const text = "\n# Vibe Agent Toolkit\n\n**Vibe Agent Toolkit (VAT)** is a modular toolkit for building, packaging, and distributing portable AI agents and skills that work across multiple Claude surfaces and adjacent frameworks. Write skill or agent content once; VAT handles validation, packaging, plugin/marketplace layout, and npm publishing.\n\nThis is a router skill. Load the sibling sub-skill that matches the work you\'re doing — each sub-skill owns one slice of VAT\'s surface and is designed to be pulled in on demand.\n\n## When to Use VAT\n\nGood fits:\n\n- Publishing a Claude skill or plugin to npm with a proper marketplace layout\n- Multi-runtime agent projects (Vercel AI SDK, LangChain, OpenAI, Claude Agent SDK)\n- Validating plugins / skills / marketplaces with \`vat audit\` before shipping\n- Enforcing frontmatter schemas across large markdown corpora\n- Wiring RAG indexing into an agent project\n\nPoor fits:\n\n- Simple one-off scripts where the framework is already decided\n- Non-TypeScript/JavaScript projects\n- Cases where you need deep framework-specific features with no reuse goal\n\n## Picking a Sub-skill\n\n| If you\'re working on... | Load |\n|---|---|\n| New project setup, \`vibe-agent-toolkit.config.yaml\` orientation, repo structure, vibe-validate integration, npm postinstall | \`vibe-agent-toolkit:vat-adoption-and-configuration\` |\n| Writing or revising a SKILL.md — frontmatter, body, references, packagingOptions, validation overrides | \`vibe-agent-toolkit:vat-skill-authoring\` |\n| TypeScript agent archetypes, \`agent.yaml\`, result envelopes, orchestration, runtime adapters | \`vibe-agent-toolkit:vat-agent-authoring\` |\n| \`vat audit\` on plugins, marketplaces, skills, or settings — including \`--compat\`, \`--exclude\`, \`--user\`, CI use | \`vibe-agent-toolkit:vat-audit\` |\n| Markdown collections, \`resources:\` config, frontmatter schema validation, \`vat resources validate\` | \`vibe-agent-toolkit:vat-knowledge-resources\` |\n| \`vat build\`, \`vat verify\`, plugin/marketplace layout, npm publishing, postinstall | \`vibe-agent-toolkit:vat-skill-distribution\` |\n| \`vat rag index\` / \`vat rag query\`, embedding providers, vector stores, chunking | \`vibe-agent-toolkit:vat-rag\` |\n| Pre-publication quality review, \`vat skill review\`, validation-code triage | \`vibe-agent-toolkit:vat-skill-review\` |\n| Anthropic Admin API: org users, cost/usage, workspace skills, \`ANTHROPIC_ADMIN_API_KEY\` | \`vibe-agent-toolkit:vat-enterprise-org\` |\n| Programmatic markdown/frontmatter edits — moving files, updating references, schema-evolution migrations; comment-preserving FrontmatterEditor + rewriteBodyLinks | \`vibe-agent-toolkit:markdown-rewriting\` |\n\n## CLI Surface at a Glance\n\n\`\`\`bash\nvat --help # top-level help\nvat build # build all artifacts (skills → claude plugins)\nvat verify # validate all artifacts\nvat skills validate # validate skill quality\nvat resources validate # validate markdown collections\nvat audit # audit plugins/skills/marketplaces/settings\nvat rag index docs/ # index markdown for RAG\nvat skill review <path> # pre-publication review\nvat claude org --help # enterprise admin surface\n\`\`\`\n\nEach sub-skill covers its slice of the CLI in depth — don\'t memorize this table, load the sub-skill when you need detail.\n\n## Getting Help\n\n- \`vat --help\` and \`vat <group> --help --verbose\` for CLI depth\n- Repo docs: the VAT repository\'s \`docs/\` directory carries the full setup guide, architecture notes, and design references (not bundled with this plugin)\n- Contributor reference docs (debugging VAT, install architecture) live under \`docs/contributing/\` in the VAT repo\n";
11
11
 
12
12
  export const fragments = {
13
13
  whenToUseVat: {
@@ -17,8 +17,8 @@ export const fragments = {
17
17
  },
18
18
  pickingASubSkill: {
19
19
  header: "## Picking a Sub-skill",
20
- body: "| If you\'re working on... | Load |\n|---|---|\n| New project setup, \`vibe-agent-toolkit.config.yaml\` orientation, repo structure, vibe-validate integration, npm postinstall | \`vibe-agent-toolkit:vat-adoption-and-configuration\` |\n| Writing or revising a SKILL.md — frontmatter, body, references, packagingOptions, validation overrides | \`vibe-agent-toolkit:vat-skill-authoring\` |\n| TypeScript agent archetypes, \`agent.yaml\`, result envelopes, orchestration, runtime adapters | \`vibe-agent-toolkit:vat-agent-authoring\` |\n| \`vat audit\` on plugins, marketplaces, skills, or settings — including \`--compat\`, \`--exclude\`, \`--user\`, CI use | \`vibe-agent-toolkit:vat-audit\` |\n| Markdown collections, \`resources:\` config, frontmatter schema validation, \`vat resources validate\` | \`vibe-agent-toolkit:vat-knowledge-resources\` |\n| \`vat build\`, \`vat verify\`, plugin/marketplace layout, npm publishing, postinstall | \`vibe-agent-toolkit:vat-skill-distribution\` |\n| \`vat rag index\` / \`vat rag query\`, embedding providers, vector stores, chunking | \`vibe-agent-toolkit:vat-rag\` |\n| Pre-publication quality review, \`vat skill review\`, validation-code triage | \`vibe-agent-toolkit:vat-skill-review\` |\n| Anthropic Admin API: org users, cost/usage, workspace skills, \`ANTHROPIC_ADMIN_API_KEY\` | \`vibe-agent-toolkit:vat-enterprise-org\` |",
21
- text: "## Picking a Sub-skill\n\n| If you\'re working on... | Load |\n|---|---|\n| New project setup, \`vibe-agent-toolkit.config.yaml\` orientation, repo structure, vibe-validate integration, npm postinstall | \`vibe-agent-toolkit:vat-adoption-and-configuration\` |\n| Writing or revising a SKILL.md — frontmatter, body, references, packagingOptions, validation overrides | \`vibe-agent-toolkit:vat-skill-authoring\` |\n| TypeScript agent archetypes, \`agent.yaml\`, result envelopes, orchestration, runtime adapters | \`vibe-agent-toolkit:vat-agent-authoring\` |\n| \`vat audit\` on plugins, marketplaces, skills, or settings — including \`--compat\`, \`--exclude\`, \`--user\`, CI use | \`vibe-agent-toolkit:vat-audit\` |\n| Markdown collections, \`resources:\` config, frontmatter schema validation, \`vat resources validate\` | \`vibe-agent-toolkit:vat-knowledge-resources\` |\n| \`vat build\`, \`vat verify\`, plugin/marketplace layout, npm publishing, postinstall | \`vibe-agent-toolkit:vat-skill-distribution\` |\n| \`vat rag index\` / \`vat rag query\`, embedding providers, vector stores, chunking | \`vibe-agent-toolkit:vat-rag\` |\n| Pre-publication quality review, \`vat skill review\`, validation-code triage | \`vibe-agent-toolkit:vat-skill-review\` |\n| Anthropic Admin API: org users, cost/usage, workspace skills, \`ANTHROPIC_ADMIN_API_KEY\` | \`vibe-agent-toolkit:vat-enterprise-org\` |"
20
+ body: "| If you\'re working on... | Load |\n|---|---|\n| New project setup, \`vibe-agent-toolkit.config.yaml\` orientation, repo structure, vibe-validate integration, npm postinstall | \`vibe-agent-toolkit:vat-adoption-and-configuration\` |\n| Writing or revising a SKILL.md — frontmatter, body, references, packagingOptions, validation overrides | \`vibe-agent-toolkit:vat-skill-authoring\` |\n| TypeScript agent archetypes, \`agent.yaml\`, result envelopes, orchestration, runtime adapters | \`vibe-agent-toolkit:vat-agent-authoring\` |\n| \`vat audit\` on plugins, marketplaces, skills, or settings — including \`--compat\`, \`--exclude\`, \`--user\`, CI use | \`vibe-agent-toolkit:vat-audit\` |\n| Markdown collections, \`resources:\` config, frontmatter schema validation, \`vat resources validate\` | \`vibe-agent-toolkit:vat-knowledge-resources\` |\n| \`vat build\`, \`vat verify\`, plugin/marketplace layout, npm publishing, postinstall | \`vibe-agent-toolkit:vat-skill-distribution\` |\n| \`vat rag index\` / \`vat rag query\`, embedding providers, vector stores, chunking | \`vibe-agent-toolkit:vat-rag\` |\n| Pre-publication quality review, \`vat skill review\`, validation-code triage | \`vibe-agent-toolkit:vat-skill-review\` |\n| Anthropic Admin API: org users, cost/usage, workspace skills, \`ANTHROPIC_ADMIN_API_KEY\` | \`vibe-agent-toolkit:vat-enterprise-org\` |\n| Programmatic markdown/frontmatter edits — moving files, updating references, schema-evolution migrations; comment-preserving FrontmatterEditor + rewriteBodyLinks | \`vibe-agent-toolkit:markdown-rewriting\` |",
21
+ text: "## Picking a Sub-skill\n\n| If you\'re working on... | Load |\n|---|---|\n| New project setup, \`vibe-agent-toolkit.config.yaml\` orientation, repo structure, vibe-validate integration, npm postinstall | \`vibe-agent-toolkit:vat-adoption-and-configuration\` |\n| Writing or revising a SKILL.md — frontmatter, body, references, packagingOptions, validation overrides | \`vibe-agent-toolkit:vat-skill-authoring\` |\n| TypeScript agent archetypes, \`agent.yaml\`, result envelopes, orchestration, runtime adapters | \`vibe-agent-toolkit:vat-agent-authoring\` |\n| \`vat audit\` on plugins, marketplaces, skills, or settings — including \`--compat\`, \`--exclude\`, \`--user\`, CI use | \`vibe-agent-toolkit:vat-audit\` |\n| Markdown collections, \`resources:\` config, frontmatter schema validation, \`vat resources validate\` | \`vibe-agent-toolkit:vat-knowledge-resources\` |\n| \`vat build\`, \`vat verify\`, plugin/marketplace layout, npm publishing, postinstall | \`vibe-agent-toolkit:vat-skill-distribution\` |\n| \`vat rag index\` / \`vat rag query\`, embedding providers, vector stores, chunking | \`vibe-agent-toolkit:vat-rag\` |\n| Pre-publication quality review, \`vat skill review\`, validation-code triage | \`vibe-agent-toolkit:vat-skill-review\` |\n| Anthropic Admin API: org users, cost/usage, workspace skills, \`ANTHROPIC_ADMIN_API_KEY\` | \`vibe-agent-toolkit:vat-enterprise-org\` |\n| Programmatic markdown/frontmatter edits — moving files, updating references, schema-evolution migrations; comment-preserving FrontmatterEditor + rewriteBodyLinks | \`vibe-agent-toolkit:markdown-rewriting\` |"
22
22
  },
23
23
  cliSurfaceAtAGlance: {
24
24
  header: "## CLI Surface at a Glance",
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Generated TypeScript declarations - DO NOT EDIT
3
+ */
4
+
5
+ export interface Fragment {
6
+ readonly header: string;
7
+ readonly body: string;
8
+ readonly text: string;
9
+ }
10
+
11
+ export const meta: {
12
+ readonly name: string;
13
+ readonly description: string;
14
+ };
15
+
16
+ export const text: string;
17
+
18
+ export const fragments: {
19
+ readonly theRule: Fragment;
20
+ readonly antiPatterns-DoNotUseThese: Fragment;
21
+ readonly canonicalRecipeMoveAFileAndUpdateReferences: Fragment;
22
+ readonly whenYouHaveAJsonSchemaForTheFrontmatter: Fragment;
23
+ readonly bulkMigrationManyFilesAtOnce: Fragment;
24
+ readonly whatsPreservedWhatIsnt: Fragment;
25
+ readonly crossLinks: Fragment;
26
+ };
27
+
28
+ export type FragmentName = keyof typeof fragments;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Generated from markdown file - DO NOT EDIT
3
+ */
4
+
5
+ export const meta = {
6
+ name: "markdown-rewriting",
7
+ description: "Use when programmatically editing markdown or frontmatter — moving files, updating references, batch-renaming, schema-evolution migrations. Steers to comment-preserving FrontmatterEditor + rewriteBodyLinks; away from gray-matter/js-yaml/regex."
8
+ };
9
+
10
+ export const text = "\n# Editing markdown safely\n\nWhen you write code that opens a markdown file, mutates the frontmatter or\nbody, and writes it back, you have ONE job that\'s easy to get wrong: keep\nthe file\'s content stable except for the intentional change. Lose comments,\nblank lines, anchors, or quoting style, and every iteration degrades the\ndocs.\n\nVAT ships canonical primitives for this. Use them.\n\n## The rule\n\nFor **any** programmatic markdown edit, use \`@vibe-agent-toolkit/resources\`:\n\n- \`openFrontmatter(markdown)\` — round-trip-safe editor. Comments,\n blank lines, EOL, YAML style all survive read → mutate → write. Exposes\n \`.body\` (settable), \`.get(path)\`, \`.set(path, value)\`, \`.setArrayItem\`,\n \`.appendArrayItem\`, \`.delete(path)\`, \`.isDirty()\`, and \`.toString()\`.\n The underlying \`yaml.Document\` is intentionally not exposed.\n- \`rewriteBodyLinks(body, rewriteHref)\` — walk inline + reference-style\n body links with a per-href callback.\n- \`rewriteFrontmatterFieldsAtPaths(editor, paths, rewriteHref)\` — rewrite\n specific frontmatter fields you know by name. Path syntax: \`\'name\'\`\n (top-level), \`\'name[]\'\` (array of strings — rewrite each item),\n \`\'meta.parent\'\` (nested), \`\'meta.refs[]\'\` (nested array).\n- \`rewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref)\` —\n walk every schema-annotated URI-reference field automatically.\n\n**The \`rewriteHref\` callback contract.** Return the new href, or return the\ninput string unchanged to skip that link. Anchor-only hrefs (\`#section\`),\nexternal URLs, and refs that don\'t match your rule should all return as-is.\nThe callback receives only the href string — there is no field-path\ncontext. When rules differ per field (e.g. \`parent_spec\` strict; \`related[]\`\npermissive), use \`rewriteFrontmatterFieldsAtPaths\` with one call per path\ngroup rather than the schema-driven helper.\n\n**The primitives are pure (no I/O).** Read and write with whatever FS API\nfits — \`fs/promises\`, \`fs-extra\`, streams, anything. The recipes below\nuse \`readFileSync\` / \`writeFileSync\` for clarity; production code is\nfree to use async equivalents.\n\n## Anti-patterns — do NOT use these\n\n- ❌ \`gray-matter\` — drops comments. Already banned by ESLint.\n- ❌ \`front-matter\` — drops comments. Already banned by ESLint.\n- ❌ \`js-yaml\` — drops comments AND has YAML 1.1 quirks (ISO date\n promotion). Already banned by ESLint.\n- ❌ Raw \`yaml.parse(text) → mutate → yaml.stringify(obj)\` — drops comments\n even with eemeli \`yaml\`. Use \`openFrontmatter\` for the round-trip case.\n- ❌ Regex on \`---\` fences to extract or replace frontmatter — fragile,\n loses style.\n- ❌ Naive \`body.replaceAll(\'/docs/old/\', \'/docs/new/\')\` — matches inside\n code blocks, inline code spans, attribute values, etc. Use\n \`rewriteBodyLinks\` so only actual markdown links are rewritten.\n\n## Canonical recipe: move a file and update references\n\n\`\`\`typescript\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterFieldsAtPaths,\n} from \'@vibe-agent-toolkit/resources\';\nimport { readFileSync, writeFileSync } from \'node:fs\';\n\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/old/\', \'/docs/new/\');\n\nconst filePath = \'docs/specs/foo.md\';\nconst editor = openFrontmatter(readFileSync(filePath, \'utf8\'));\neditor.body = rewriteBodyLinks(editor.body, rewriteHref);\nrewriteFrontmatterFieldsAtPaths(\n editor,\n [\'parent_spec\', \'adrs-cited[]\', \'related-specs[]\'],\n rewriteHref,\n);\nwriteFileSync(filePath, editor.toString());\n\`\`\`\n\n## When you have a JSON Schema for the frontmatter\n\nIf the file\'s frontmatter is governed by a schema (collection-validated, or\njust a hand-written schema you trust), use the schema-driven helper\ninstead of listing field paths by hand. **Compose it with \`rewriteBodyLinks\`\nto cover the body too** — most rewrites (file moves, folder renames) want\nboth sides updated:\n\n\`\`\`typescript\nimport { readFileSync, writeFileSync } from \'node:fs\';\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterUriReferencesFromSchema,\n} from \'@vibe-agent-toolkit/resources\';\n\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/specs/\', \'/docs/architecture/\');\n\nconst schema = JSON.parse(readFileSync(\'schemas/spec.schema.json\', \'utf8\'));\nconst filePath = \'docs/specs/foo.md\';\nconst editor = openFrontmatter(readFileSync(filePath, \'utf8\'));\n\neditor.body = rewriteBodyLinks(editor.body, rewriteHref);\nrewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref);\n\nwriteFileSync(filePath, editor.toString());\n\`\`\`\n\nThe schema-driven call walks every field whose schema position has\n\`format: uri-reference\` (or \`uri\`, \`iri-reference\`, \`iri\`) and rewrites\nthe value via your callback. Fields outside the URI-family are not\ntouched. **Templated-URI formats are intentionally excluded** —\n\`uri-template\` (RFC 6570 templates with \`{var}\` placeholders) and\nJSON-Pointer-derived formats aren\'t file references and don\'t fit the\nrewrite shape.\n\n**Frontmatter-only**: drop the \`rewriteBodyLinks\` line — the rest\nis identical.\n\nAfter running, diff the file (\`git diff <path>\`) to confirm the rewrite\ntouched only the fields and links you expected.\n\n## Bulk migration: many files at once\n\nWhen the rewrite spans dozens or thousands of files (folder rename,\nschema-evolution migration, citation cleanup), the natural shape is\nglob + iterate + dry-run + \`isDirty()\` gate. Pattern:\n\n\`\`\`typescript\nimport { readFileSync, writeFileSync } from \'node:fs\';\nimport { globSync } from \'glob\'; // or \`node:fs\`\'s glob, or fast-glob\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterUriReferencesFromSchema,\n} from \'@vibe-agent-toolkit/resources\';\n\nconst DRY_RUN = process.env[\'DRY_RUN\'] !== \'false\';\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/specs/\', \'/docs/architecture/\');\n\nconst schema = JSON.parse(readFileSync(\'schemas/spec.schema.json\', \'utf8\'));\nconst files = globSync(\'docs/**/*.md\');\n\nlet changed = 0;\nlet unchanged = 0;\nfor (const filePath of files) {\n const original = readFileSync(filePath, \'utf8\');\n const editor = openFrontmatter(original);\n\n editor.body = rewriteBodyLinks(editor.body, rewriteHref);\n rewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref);\n\n // Skip files where nothing material changed. Avoids the no-op churn\n // described in §\"What\'s preserved, what isn\'t\".\n const next = editor.toString();\n if (!editor.isDirty() || next === original) {\n unchanged++;\n continue;\n }\n\n if (DRY_RUN) {\n console.log(\`would update ${filePath}\`);\n } else {\n writeFileSync(filePath, next);\n }\n changed++;\n}\n\nconsole.log(\`${DRY_RUN ? \'DRY RUN: \' : \'\'}${changed} changed, ${unchanged} unchanged\`);\n\`\`\`\n\n**Recommended workflow for bulk runs:**\n\n1. **Sentinel-first.** Run on a single representative file first\n (\`globSync\` pattern that matches exactly one path). Eyeball the diff.\n2. **Dry-run the full set.** \`DRY_RUN=true\` lists every file the script\n would touch. Spot-check at least 3 entries before going wet.\n3. **Iterate.** Adjust the callback or schema until the dry-run plan\n matches your intent.\n4. **Run wet.** \`DRY_RUN=false\`. Then run your project\'s link validator\n (\`vat resources validate\`, link-check CI, etc.) on the corpus before\n committing — the rewrite is reversible via \`git checkout\` if any\n targets are wrong.\n\n**Delegating to a subagent.** Bulk rewrites are good subagent work, but\nbrief them with the same three guardrails: provide the exact callback\nrule, mandate dry-run first, and ask for a structured report (counts\nof files changed/unchanged, a sample of 3 before/after diffs). Without\nthose guardrails, a subagent can silently produce a thousand-file diff\nthat\'s wrong in a subtle way and only catchable on careful review.\n\n## What\'s preserved, what isn\'t\n\n\`openFrontmatter\` inherits its preservation guarantees from \`yaml.Document\`\n(eemeli). Read-only round-trip (no mutations) is byte-identical. Once you\nmutate, the frontmatter section is re-emitted by \`yaml.Document\` and a few\nthings normalize:\n\n| Behavior | Preserved on mutation? |\n|---|---|\n| Inline comment **text** (\`# comment\`) | ✅ |\n| Inline comment **leading whitespace** (\` #\` vs \` #\`) | ❌ collapsed to one space |\n| Leading comments on keys | ✅ (best-effort; comment-attachment is yaml.Document\'s call) |\n| Comments on individual array items | ✅ |\n| Block scalar style (\`|\`, \`>\`, \`|-\`, \`>+\`) | ✅ |\n| Quoting style (plain / single / double) | ✅ |\n| Blank lines between blocks | ✅ |\n| Key ordering | ✅ |\n| EOL (LF or CRLF) | ✅ (detected from first line break) |\n| Anchors and aliases | ✅ on round-trip; mutating them follows \`yaml.Document\` semantics, not ours |\n\n**Consequence:** even a \"no-op\" rewrite (callback returns the input\nunchanged) re-emits frontmatter and can collapse \` #\` → \` #\` on every\nline that has an inline comment. The change is harmless but shows up in\n\`git diff\`. Two ways to skip the write in this case:\n\n- **\`editor.isDirty()\`** — returns \`true\` if any mutator was called or\n \`body\` was reassigned to a different string. Cheap, no string compare,\n but flips on any mutator call even when the value didn\'t change\n (e.g. \`set(\'foo\', sameValue)\`). Fine for most workflows.\n- **Byte-level dirty check** — \`editor.toString() !== originalText\`. Catches\n the no-op-rewrite case exactly; one extra serialize per file. Use this\n when you must produce zero diff on no-op runs.\n\nThe bulk-migration recipe above combines both: \`isDirty()\` as the cheap\nshort-circuit, then a byte compare to filter out comment-whitespace-only\ndeltas.\n\n## Cross-links\n\n- \`vat-knowledge-resources\` — how to validate URI-references in\n frontmatter against a collection schema.\n- \`vat-skill-authoring\` — when to use leading-\`/\` (project-root-relative)\n URI-refs in SKILL.md frontmatter.\n";
11
+
12
+ export const fragments = {
13
+ theRule: {
14
+ header: "## The rule",
15
+ body: "For **any** programmatic markdown edit, use \`@vibe-agent-toolkit/resources\`:\n\n- \`openFrontmatter(markdown)\` — round-trip-safe editor. Comments,\n blank lines, EOL, YAML style all survive read → mutate → write. Exposes\n \`.body\` (settable), \`.get(path)\`, \`.set(path, value)\`, \`.setArrayItem\`,\n \`.appendArrayItem\`, \`.delete(path)\`, \`.isDirty()\`, and \`.toString()\`.\n The underlying \`yaml.Document\` is intentionally not exposed.\n- \`rewriteBodyLinks(body, rewriteHref)\` — walk inline + reference-style\n body links with a per-href callback.\n- \`rewriteFrontmatterFieldsAtPaths(editor, paths, rewriteHref)\` — rewrite\n specific frontmatter fields you know by name. Path syntax: \`\'name\'\`\n (top-level), \`\'name[]\'\` (array of strings — rewrite each item),\n \`\'meta.parent\'\` (nested), \`\'meta.refs[]\'\` (nested array).\n- \`rewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref)\` —\n walk every schema-annotated URI-reference field automatically.\n\n**The \`rewriteHref\` callback contract.** Return the new href, or return the\ninput string unchanged to skip that link. Anchor-only hrefs (\`#section\`),\nexternal URLs, and refs that don\'t match your rule should all return as-is.\nThe callback receives only the href string — there is no field-path\ncontext. When rules differ per field (e.g. \`parent_spec\` strict; \`related[]\`\npermissive), use \`rewriteFrontmatterFieldsAtPaths\` with one call per path\ngroup rather than the schema-driven helper.\n\n**The primitives are pure (no I/O).** Read and write with whatever FS API\nfits — \`fs/promises\`, \`fs-extra\`, streams, anything. The recipes below\nuse \`readFileSync\` / \`writeFileSync\` for clarity; production code is\nfree to use async equivalents.",
16
+ text: "## The rule\n\nFor **any** programmatic markdown edit, use \`@vibe-agent-toolkit/resources\`:\n\n- \`openFrontmatter(markdown)\` — round-trip-safe editor. Comments,\n blank lines, EOL, YAML style all survive read → mutate → write. Exposes\n \`.body\` (settable), \`.get(path)\`, \`.set(path, value)\`, \`.setArrayItem\`,\n \`.appendArrayItem\`, \`.delete(path)\`, \`.isDirty()\`, and \`.toString()\`.\n The underlying \`yaml.Document\` is intentionally not exposed.\n- \`rewriteBodyLinks(body, rewriteHref)\` — walk inline + reference-style\n body links with a per-href callback.\n- \`rewriteFrontmatterFieldsAtPaths(editor, paths, rewriteHref)\` — rewrite\n specific frontmatter fields you know by name. Path syntax: \`\'name\'\`\n (top-level), \`\'name[]\'\` (array of strings — rewrite each item),\n \`\'meta.parent\'\` (nested), \`\'meta.refs[]\'\` (nested array).\n- \`rewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref)\` —\n walk every schema-annotated URI-reference field automatically.\n\n**The \`rewriteHref\` callback contract.** Return the new href, or return the\ninput string unchanged to skip that link. Anchor-only hrefs (\`#section\`),\nexternal URLs, and refs that don\'t match your rule should all return as-is.\nThe callback receives only the href string — there is no field-path\ncontext. When rules differ per field (e.g. \`parent_spec\` strict; \`related[]\`\npermissive), use \`rewriteFrontmatterFieldsAtPaths\` with one call per path\ngroup rather than the schema-driven helper.\n\n**The primitives are pure (no I/O).** Read and write with whatever FS API\nfits — \`fs/promises\`, \`fs-extra\`, streams, anything. The recipes below\nuse \`readFileSync\` / \`writeFileSync\` for clarity; production code is\nfree to use async equivalents."
17
+ },
18
+ antiPatterns-DoNotUseThese: {
19
+ header: "## Anti-patterns — do NOT use these",
20
+ body: "- ❌ \`gray-matter\` — drops comments. Already banned by ESLint.\n- ❌ \`front-matter\` — drops comments. Already banned by ESLint.\n- ❌ \`js-yaml\` — drops comments AND has YAML 1.1 quirks (ISO date\n promotion). Already banned by ESLint.\n- ❌ Raw \`yaml.parse(text) → mutate → yaml.stringify(obj)\` — drops comments\n even with eemeli \`yaml\`. Use \`openFrontmatter\` for the round-trip case.\n- ❌ Regex on \`---\` fences to extract or replace frontmatter — fragile,\n loses style.\n- ❌ Naive \`body.replaceAll(\'/docs/old/\', \'/docs/new/\')\` — matches inside\n code blocks, inline code spans, attribute values, etc. Use\n \`rewriteBodyLinks\` so only actual markdown links are rewritten.",
21
+ text: "## Anti-patterns — do NOT use these\n\n- ❌ \`gray-matter\` — drops comments. Already banned by ESLint.\n- ❌ \`front-matter\` — drops comments. Already banned by ESLint.\n- ❌ \`js-yaml\` — drops comments AND has YAML 1.1 quirks (ISO date\n promotion). Already banned by ESLint.\n- ❌ Raw \`yaml.parse(text) → mutate → yaml.stringify(obj)\` — drops comments\n even with eemeli \`yaml\`. Use \`openFrontmatter\` for the round-trip case.\n- ❌ Regex on \`---\` fences to extract or replace frontmatter — fragile,\n loses style.\n- ❌ Naive \`body.replaceAll(\'/docs/old/\', \'/docs/new/\')\` — matches inside\n code blocks, inline code spans, attribute values, etc. Use\n \`rewriteBodyLinks\` so only actual markdown links are rewritten."
22
+ },
23
+ canonicalRecipeMoveAFileAndUpdateReferences: {
24
+ header: "## Canonical recipe: move a file and update references",
25
+ body: "\`\`\`typescript\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterFieldsAtPaths,\n} from \'@vibe-agent-toolkit/resources\';\nimport { readFileSync, writeFileSync } from \'node:fs\';\n\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/old/\', \'/docs/new/\');\n\nconst filePath = \'docs/specs/foo.md\';\nconst editor = openFrontmatter(readFileSync(filePath, \'utf8\'));\neditor.body = rewriteBodyLinks(editor.body, rewriteHref);\nrewriteFrontmatterFieldsAtPaths(\n editor,\n [\'parent_spec\', \'adrs-cited[]\', \'related-specs[]\'],\n rewriteHref,\n);\nwriteFileSync(filePath, editor.toString());\n\`\`\`",
26
+ text: "## Canonical recipe: move a file and update references\n\n\`\`\`typescript\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterFieldsAtPaths,\n} from \'@vibe-agent-toolkit/resources\';\nimport { readFileSync, writeFileSync } from \'node:fs\';\n\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/old/\', \'/docs/new/\');\n\nconst filePath = \'docs/specs/foo.md\';\nconst editor = openFrontmatter(readFileSync(filePath, \'utf8\'));\neditor.body = rewriteBodyLinks(editor.body, rewriteHref);\nrewriteFrontmatterFieldsAtPaths(\n editor,\n [\'parent_spec\', \'adrs-cited[]\', \'related-specs[]\'],\n rewriteHref,\n);\nwriteFileSync(filePath, editor.toString());\n\`\`\`"
27
+ },
28
+ whenYouHaveAJsonSchemaForTheFrontmatter: {
29
+ header: "## When you have a JSON Schema for the frontmatter",
30
+ body: "If the file\'s frontmatter is governed by a schema (collection-validated, or\njust a hand-written schema you trust), use the schema-driven helper\ninstead of listing field paths by hand. **Compose it with \`rewriteBodyLinks\`\nto cover the body too** — most rewrites (file moves, folder renames) want\nboth sides updated:\n\n\`\`\`typescript\nimport { readFileSync, writeFileSync } from \'node:fs\';\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterUriReferencesFromSchema,\n} from \'@vibe-agent-toolkit/resources\';\n\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/specs/\', \'/docs/architecture/\');\n\nconst schema = JSON.parse(readFileSync(\'schemas/spec.schema.json\', \'utf8\'));\nconst filePath = \'docs/specs/foo.md\';\nconst editor = openFrontmatter(readFileSync(filePath, \'utf8\'));\n\neditor.body = rewriteBodyLinks(editor.body, rewriteHref);\nrewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref);\n\nwriteFileSync(filePath, editor.toString());\n\`\`\`\n\nThe schema-driven call walks every field whose schema position has\n\`format: uri-reference\` (or \`uri\`, \`iri-reference\`, \`iri\`) and rewrites\nthe value via your callback. Fields outside the URI-family are not\ntouched. **Templated-URI formats are intentionally excluded** —\n\`uri-template\` (RFC 6570 templates with \`{var}\` placeholders) and\nJSON-Pointer-derived formats aren\'t file references and don\'t fit the\nrewrite shape.\n\n**Frontmatter-only**: drop the \`rewriteBodyLinks\` line — the rest\nis identical.\n\nAfter running, diff the file (\`git diff <path>\`) to confirm the rewrite\ntouched only the fields and links you expected.",
31
+ text: "## When you have a JSON Schema for the frontmatter\n\nIf the file\'s frontmatter is governed by a schema (collection-validated, or\njust a hand-written schema you trust), use the schema-driven helper\ninstead of listing field paths by hand. **Compose it with \`rewriteBodyLinks\`\nto cover the body too** — most rewrites (file moves, folder renames) want\nboth sides updated:\n\n\`\`\`typescript\nimport { readFileSync, writeFileSync } from \'node:fs\';\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterUriReferencesFromSchema,\n} from \'@vibe-agent-toolkit/resources\';\n\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/specs/\', \'/docs/architecture/\');\n\nconst schema = JSON.parse(readFileSync(\'schemas/spec.schema.json\', \'utf8\'));\nconst filePath = \'docs/specs/foo.md\';\nconst editor = openFrontmatter(readFileSync(filePath, \'utf8\'));\n\neditor.body = rewriteBodyLinks(editor.body, rewriteHref);\nrewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref);\n\nwriteFileSync(filePath, editor.toString());\n\`\`\`\n\nThe schema-driven call walks every field whose schema position has\n\`format: uri-reference\` (or \`uri\`, \`iri-reference\`, \`iri\`) and rewrites\nthe value via your callback. Fields outside the URI-family are not\ntouched. **Templated-URI formats are intentionally excluded** —\n\`uri-template\` (RFC 6570 templates with \`{var}\` placeholders) and\nJSON-Pointer-derived formats aren\'t file references and don\'t fit the\nrewrite shape.\n\n**Frontmatter-only**: drop the \`rewriteBodyLinks\` line — the rest\nis identical.\n\nAfter running, diff the file (\`git diff <path>\`) to confirm the rewrite\ntouched only the fields and links you expected."
32
+ },
33
+ bulkMigrationManyFilesAtOnce: {
34
+ header: "## Bulk migration: many files at once",
35
+ body: "When the rewrite spans dozens or thousands of files (folder rename,\nschema-evolution migration, citation cleanup), the natural shape is\nglob + iterate + dry-run + \`isDirty()\` gate. Pattern:\n\n\`\`\`typescript\nimport { readFileSync, writeFileSync } from \'node:fs\';\nimport { globSync } from \'glob\'; // or \`node:fs\`\'s glob, or fast-glob\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterUriReferencesFromSchema,\n} from \'@vibe-agent-toolkit/resources\';\n\nconst DRY_RUN = process.env[\'DRY_RUN\'] !== \'false\';\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/specs/\', \'/docs/architecture/\');\n\nconst schema = JSON.parse(readFileSync(\'schemas/spec.schema.json\', \'utf8\'));\nconst files = globSync(\'docs/**/*.md\');\n\nlet changed = 0;\nlet unchanged = 0;\nfor (const filePath of files) {\n const original = readFileSync(filePath, \'utf8\');\n const editor = openFrontmatter(original);\n\n editor.body = rewriteBodyLinks(editor.body, rewriteHref);\n rewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref);\n\n // Skip files where nothing material changed. Avoids the no-op churn\n // described in §\"What\'s preserved, what isn\'t\".\n const next = editor.toString();\n if (!editor.isDirty() || next === original) {\n unchanged++;\n continue;\n }\n\n if (DRY_RUN) {\n console.log(\`would update ${filePath}\`);\n } else {\n writeFileSync(filePath, next);\n }\n changed++;\n}\n\nconsole.log(\`${DRY_RUN ? \'DRY RUN: \' : \'\'}${changed} changed, ${unchanged} unchanged\`);\n\`\`\`\n\n**Recommended workflow for bulk runs:**\n\n1. **Sentinel-first.** Run on a single representative file first\n (\`globSync\` pattern that matches exactly one path). Eyeball the diff.\n2. **Dry-run the full set.** \`DRY_RUN=true\` lists every file the script\n would touch. Spot-check at least 3 entries before going wet.\n3. **Iterate.** Adjust the callback or schema until the dry-run plan\n matches your intent.\n4. **Run wet.** \`DRY_RUN=false\`. Then run your project\'s link validator\n (\`vat resources validate\`, link-check CI, etc.) on the corpus before\n committing — the rewrite is reversible via \`git checkout\` if any\n targets are wrong.\n\n**Delegating to a subagent.** Bulk rewrites are good subagent work, but\nbrief them with the same three guardrails: provide the exact callback\nrule, mandate dry-run first, and ask for a structured report (counts\nof files changed/unchanged, a sample of 3 before/after diffs). Without\nthose guardrails, a subagent can silently produce a thousand-file diff\nthat\'s wrong in a subtle way and only catchable on careful review.",
36
+ text: "## Bulk migration: many files at once\n\nWhen the rewrite spans dozens or thousands of files (folder rename,\nschema-evolution migration, citation cleanup), the natural shape is\nglob + iterate + dry-run + \`isDirty()\` gate. Pattern:\n\n\`\`\`typescript\nimport { readFileSync, writeFileSync } from \'node:fs\';\nimport { globSync } from \'glob\'; // or \`node:fs\`\'s glob, or fast-glob\nimport {\n openFrontmatter,\n rewriteBodyLinks,\n rewriteFrontmatterUriReferencesFromSchema,\n} from \'@vibe-agent-toolkit/resources\';\n\nconst DRY_RUN = process.env[\'DRY_RUN\'] !== \'false\';\nconst rewriteHref = (href: string): string =>\n href.replace(\'/docs/specs/\', \'/docs/architecture/\');\n\nconst schema = JSON.parse(readFileSync(\'schemas/spec.schema.json\', \'utf8\'));\nconst files = globSync(\'docs/**/*.md\');\n\nlet changed = 0;\nlet unchanged = 0;\nfor (const filePath of files) {\n const original = readFileSync(filePath, \'utf8\');\n const editor = openFrontmatter(original);\n\n editor.body = rewriteBodyLinks(editor.body, rewriteHref);\n rewriteFrontmatterUriReferencesFromSchema(editor, schema, rewriteHref);\n\n // Skip files where nothing material changed. Avoids the no-op churn\n // described in §\"What\'s preserved, what isn\'t\".\n const next = editor.toString();\n if (!editor.isDirty() || next === original) {\n unchanged++;\n continue;\n }\n\n if (DRY_RUN) {\n console.log(\`would update ${filePath}\`);\n } else {\n writeFileSync(filePath, next);\n }\n changed++;\n}\n\nconsole.log(\`${DRY_RUN ? \'DRY RUN: \' : \'\'}${changed} changed, ${unchanged} unchanged\`);\n\`\`\`\n\n**Recommended workflow for bulk runs:**\n\n1. **Sentinel-first.** Run on a single representative file first\n (\`globSync\` pattern that matches exactly one path). Eyeball the diff.\n2. **Dry-run the full set.** \`DRY_RUN=true\` lists every file the script\n would touch. Spot-check at least 3 entries before going wet.\n3. **Iterate.** Adjust the callback or schema until the dry-run plan\n matches your intent.\n4. **Run wet.** \`DRY_RUN=false\`. Then run your project\'s link validator\n (\`vat resources validate\`, link-check CI, etc.) on the corpus before\n committing — the rewrite is reversible via \`git checkout\` if any\n targets are wrong.\n\n**Delegating to a subagent.** Bulk rewrites are good subagent work, but\nbrief them with the same three guardrails: provide the exact callback\nrule, mandate dry-run first, and ask for a structured report (counts\nof files changed/unchanged, a sample of 3 before/after diffs). Without\nthose guardrails, a subagent can silently produce a thousand-file diff\nthat\'s wrong in a subtle way and only catchable on careful review."
37
+ },
38
+ whatsPreservedWhatIsnt: {
39
+ header: "## What\'s preserved, what isn\'t",
40
+ body: "\`openFrontmatter\` inherits its preservation guarantees from \`yaml.Document\`\n(eemeli). Read-only round-trip (no mutations) is byte-identical. Once you\nmutate, the frontmatter section is re-emitted by \`yaml.Document\` and a few\nthings normalize:\n\n| Behavior | Preserved on mutation? |\n|---|---|\n| Inline comment **text** (\`# comment\`) | ✅ |\n| Inline comment **leading whitespace** (\` #\` vs \` #\`) | ❌ collapsed to one space |\n| Leading comments on keys | ✅ (best-effort; comment-attachment is yaml.Document\'s call) |\n| Comments on individual array items | ✅ |\n| Block scalar style (\`|\`, \`>\`, \`|-\`, \`>+\`) | ✅ |\n| Quoting style (plain / single / double) | ✅ |\n| Blank lines between blocks | ✅ |\n| Key ordering | ✅ |\n| EOL (LF or CRLF) | ✅ (detected from first line break) |\n| Anchors and aliases | ✅ on round-trip; mutating them follows \`yaml.Document\` semantics, not ours |\n\n**Consequence:** even a \"no-op\" rewrite (callback returns the input\nunchanged) re-emits frontmatter and can collapse \` #\` → \` #\` on every\nline that has an inline comment. The change is harmless but shows up in\n\`git diff\`. Two ways to skip the write in this case:\n\n- **\`editor.isDirty()\`** — returns \`true\` if any mutator was called or\n \`body\` was reassigned to a different string. Cheap, no string compare,\n but flips on any mutator call even when the value didn\'t change\n (e.g. \`set(\'foo\', sameValue)\`). Fine for most workflows.\n- **Byte-level dirty check** — \`editor.toString() !== originalText\`. Catches\n the no-op-rewrite case exactly; one extra serialize per file. Use this\n when you must produce zero diff on no-op runs.\n\nThe bulk-migration recipe above combines both: \`isDirty()\` as the cheap\nshort-circuit, then a byte compare to filter out comment-whitespace-only\ndeltas.",
41
+ text: "## What\'s preserved, what isn\'t\n\n\`openFrontmatter\` inherits its preservation guarantees from \`yaml.Document\`\n(eemeli). Read-only round-trip (no mutations) is byte-identical. Once you\nmutate, the frontmatter section is re-emitted by \`yaml.Document\` and a few\nthings normalize:\n\n| Behavior | Preserved on mutation? |\n|---|---|\n| Inline comment **text** (\`# comment\`) | ✅ |\n| Inline comment **leading whitespace** (\` #\` vs \` #\`) | ❌ collapsed to one space |\n| Leading comments on keys | ✅ (best-effort; comment-attachment is yaml.Document\'s call) |\n| Comments on individual array items | ✅ |\n| Block scalar style (\`|\`, \`>\`, \`|-\`, \`>+\`) | ✅ |\n| Quoting style (plain / single / double) | ✅ |\n| Blank lines between blocks | ✅ |\n| Key ordering | ✅ |\n| EOL (LF or CRLF) | ✅ (detected from first line break) |\n| Anchors and aliases | ✅ on round-trip; mutating them follows \`yaml.Document\` semantics, not ours |\n\n**Consequence:** even a \"no-op\" rewrite (callback returns the input\nunchanged) re-emits frontmatter and can collapse \` #\` → \` #\` on every\nline that has an inline comment. The change is harmless but shows up in\n\`git diff\`. Two ways to skip the write in this case:\n\n- **\`editor.isDirty()\`** — returns \`true\` if any mutator was called or\n \`body\` was reassigned to a different string. Cheap, no string compare,\n but flips on any mutator call even when the value didn\'t change\n (e.g. \`set(\'foo\', sameValue)\`). Fine for most workflows.\n- **Byte-level dirty check** — \`editor.toString() !== originalText\`. Catches\n the no-op-rewrite case exactly; one extra serialize per file. Use this\n when you must produce zero diff on no-op runs.\n\nThe bulk-migration recipe above combines both: \`isDirty()\` as the cheap\nshort-circuit, then a byte compare to filter out comment-whitespace-only\ndeltas."
42
+ },
43
+ crossLinks: {
44
+ header: "## Cross-links",
45
+ body: "- \`vat-knowledge-resources\` — how to validate URI-references in\n frontmatter against a collection schema.\n- \`vat-skill-authoring\` — when to use leading-\`/\` (project-root-relative)\n URI-refs in SKILL.md frontmatter.",
46
+ text: "## Cross-links\n\n- \`vat-knowledge-resources\` — how to validate URI-references in\n frontmatter against a collection schema.\n- \`vat-skill-authoring\` — when to use leading-\`/\` (project-root-relative)\n URI-refs in SKILL.md frontmatter."
47
+ }
48
+ };
@@ -24,7 +24,9 @@ export const fragments: {
24
24
  readonly schemaPathFormats: Fragment;
25
25
  readonly addingANewDocType: Fragment;
26
26
  readonly recommendFormatUriReferenceForPathShapedFrontmatterFields: Fragment;
27
+ readonly perCodeSeverityAndAllowResourcesvalidation: Fragment;
27
28
  readonly annotatingFrontmatterSchemasWithZod4: Fragment;
29
+ readonly uriReferencesInFrontmatter: Fragment;
28
30
  readonly validationOutput: Fragment;
29
31
  };
30
32
 
@@ -7,7 +7,7 @@ export const meta = {
7
7
  description: "Use when working with VAT resource collections, per-directory frontmatter schema validation, link validation, or the vat resources command. Covers collection configuration, schema mapping, and validation modes."
8
8
  };
9
9
 
10
- export const text = "\n# VAT Resources: Collections & Frontmatter Validation\n\n## What Resource Collections Are\n\nA **resource collection** is a named group of files that share a validation schema.\nCollections are defined in \`vibe-agent-toolkit.config.yaml\` and enable different\ndirectories to have different required frontmatter — without writing a single line of code.\n\n## Config Format\n\n\`\`\`yaml\nversion: 1\n\nresources:\n collections:\n # Name your collection to match the doc type\n systems:\n include: [\"docs/systems/**/*.md\"]\n exclude: [\"docs/systems/README.md\"] # exclude human-only ToCs\n validation:\n frontmatterSchema: \"schemas/system.schema.json\"\n mode: permissive # required fields enforced; extra fields allowed\n\n adrs:\n include: [\"docs/architecture/adr/**/*.md\"]\n validation:\n frontmatterSchema: \"schemas/adr.schema.json\"\n mode: permissive\n\n skills:\n include: [\"**/SKILL.md\"]\n validation:\n frontmatterSchema: \"@vibe-agent-toolkit/agent-skills/schemas/skill-frontmatter.json\"\n mode: strict # SKILL.md: no extra fields beyond spec\n\`\`\`\n\n## Validation Modes\n\n| Mode | Required fields | Extra fields | Use for |\n|---|---|---|---|\n| \`permissive\` | Enforced | Allowed | Docs with project-specific extras |\n| \`strict\` | Enforced | Error | SKILL.md, API specs, tightly controlled schemas |\n\n## Running Validation\n\n\`\`\`bash\n# Validate all collections (reads vibe-agent-toolkit.config.yaml)\nvat resources validate\n\n# Validate specific collection only\nvat resources validate --collection systems\n\n# Validate with extra schema (adds to collection schemas)\nvat resources validate --frontmatter-schema ./extra.json\n\`\`\`\n\n## A File Can Belong to Multiple Collections\n\nIf a file matches multiple \`include\` patterns, **all matching schemas are validated**.\nValidation fails if any schema fails.\n\n## Schema Path Formats\n\n\`\`\`yaml\nfrontmatterSchema: \"./schemas/system.schema.json\" # relative to config\nfrontmatterSchema: \"/absolute/path/schema.json\" # absolute\nfrontmatterSchema: \"@vibe-agent-toolkit/agent-skills/...\" # npm package export\n\`\`\`\n\n## Adding a New Doc Type\n\n1. Create \`schemas/<type>.schema.json\` with \`required\` fields\n2. Add a collection entry in \`vibe-agent-toolkit.config.yaml\`\n3. Run \`vat resources validate\` — any existing docs missing required fields will be flagged\n4. Fix frontmatter in existing docs, then CI is clean\n\n## Recommend \`format: \"uri-reference\"\` for path-shaped frontmatter fields\n\nWhen designing a schema for a knowledge-base collection that references other files (e.g., \`parent_prd\`, \`supersedes\`, \`adr_citations[*].adr\`, \`artifacts\`), declare \`format: \"uri-reference\"\` on the field. VAT will then validate those values against the file system using the same engine as markdown link checking — broken paths, missing anchors, gitignore violations, and unknown URI schemes all produce errors.\n\nTo require local committed files (no absolute URLs), add a \`pattern\` excluding scheme prefixes. Standard JSON Schema; stays portable.\n\nVAT walks four URI-family formats: \`uri-reference\`, \`uri\`, \`iri-reference\`, \`iri\`. \`uri-template\` (RFC 6570) is intentionally NOT walked — templated values contain placeholders.\n\nAbsolute URLs in URI-reference fields feed into the existing external URL health-check pass when \`checkUrlLinks: true\` is set on the collection.\n\nOpt-out: \`checkFrontmatterLinks: false\` per collection, or \`--no-check-frontmatter-links\` on the CLI.\n\n## Annotating Frontmatter Schemas with Zod 4\n\nIf your project generates JSON Schemas from Zod (via \`z.toJSONSchema()\`), annotate frontmatter fields that hold links with the appropriate \`format\` so VAT\'s link validator picks them up:\n\n\`\`\`typescript\nimport { z } from \'zod\';\n\nconst PrdFrontmatter = z.object({\n spec_ref: z.string().meta({ format: \'uri-reference\' }), // repo-relative or absolute\n roadmap: z.url().meta({ format: \'uri\' }), // full URL only\n doc_anchor: z.string().meta({ format: \'json-pointer\' }), // JSON Pointer\n});\n\`\`\`\n\n\`format\` values VAT walks for link validation: \`uri\`, \`uri-reference\`, \`iri\`, \`iri-reference\`. \`uri-template\` (RFC 6570) is intentionally NOT walked — templated values contain placeholders.\n\n**Zod 3 users:** \`.meta()\` does not exist in Zod 3. Either upgrade your schema-generation step to Zod 4 (runtime consumers can stay on Zod 3 via peer dependency + common-subset usage), or post-process the generated JSON Schema to inject \`format\` on the relevant field paths.\n\n**Tip:** \`format\` is advisory in JSON Schema; pair it with a \`pattern\` regex when you also need parse-time rejection of invalid inputs.\n\n## Validation Output\n\n\`\`\`yaml\nstatus: success\nfilesScanned: 47\ncollections:\n systems:\n resourceCount: 7\n hasSchema: true\n validationMode: permissive\n adrs:\n resourceCount: 12\n hasSchema: true\n validationMode: permissive\nduration: 234ms\n\`\`\`\n\nErrors appear in stderr with \`file:line: message\` format for editor navigation.\n";
10
+ export const text = "\n# VAT Resources: Collections & Frontmatter Validation\n\n## What Resource Collections Are\n\nA **resource collection** is a named group of files that share a validation schema.\nCollections are defined in \`vibe-agent-toolkit.config.yaml\` and enable different\ndirectories to have different required frontmatter — without writing a single line of code.\n\n## Config Format\n\n\`\`\`yaml\nversion: 1\n\nresources:\n collections:\n # Name your collection to match the doc type\n systems:\n include: [\"docs/systems/**/*.md\"]\n exclude: [\"docs/systems/README.md\"] # exclude human-only ToCs\n validation:\n frontmatterSchema: \"schemas/system.schema.json\"\n mode: permissive # required fields enforced; extra fields allowed\n\n adrs:\n include: [\"docs/architecture/adr/**/*.md\"]\n validation:\n frontmatterSchema: \"schemas/adr.schema.json\"\n mode: permissive\n\n skills:\n include: [\"**/SKILL.md\"]\n validation:\n frontmatterSchema: \"@vibe-agent-toolkit/agent-skills/schemas/skill-frontmatter.json\"\n mode: strict # SKILL.md: no extra fields beyond spec\n\`\`\`\n\n## Validation Modes\n\n| Mode | Required fields | Extra fields | Use for |\n|---|---|---|---|\n| \`permissive\` | Enforced | Allowed | Docs with project-specific extras |\n| \`strict\` | Enforced | Error | SKILL.md, API specs, tightly controlled schemas |\n\n## Running Validation\n\n\`\`\`bash\n# Validate all collections (reads vibe-agent-toolkit.config.yaml)\nvat resources validate\n\n# Validate specific collection only\nvat resources validate --collection systems\n\n# Validate with extra schema (adds to collection schemas)\nvat resources validate --frontmatter-schema ./extra.json\n\`\`\`\n\n## A File Can Belong to Multiple Collections\n\nIf a file matches multiple \`include\` patterns, **all matching schemas are validated**.\nValidation fails if any schema fails.\n\n## Schema Path Formats\n\n\`\`\`yaml\nfrontmatterSchema: \"./schemas/system.schema.json\" # relative to config\nfrontmatterSchema: \"/absolute/path/schema.json\" # absolute\nfrontmatterSchema: \"@vibe-agent-toolkit/agent-skills/...\" # npm package export\n\`\`\`\n\n## Adding a New Doc Type\n\n1. Create \`schemas/<type>.schema.json\` with \`required\` fields\n2. Add a collection entry in \`vibe-agent-toolkit.config.yaml\`\n3. Run \`vat resources validate\` — any existing docs missing required fields will be flagged\n4. Fix frontmatter in existing docs, then CI is clean\n\n## Recommend \`format: \"uri-reference\"\` for path-shaped frontmatter fields\n\nWhen designing a schema for a knowledge-base collection that references other files (e.g., \`parent_prd\`, \`supersedes\`, \`adr_citations[*].adr\`, \`artifacts\`), declare \`format: \"uri-reference\"\` on the field. VAT will then validate those values against the file system using the same engine as markdown link checking — broken paths, missing anchors, gitignore violations, and unknown URI schemes all produce errors.\n\nTo require local committed files (no absolute URLs), add a \`pattern\` excluding scheme prefixes. Standard JSON Schema; stays portable.\n\nVAT walks four URI-family formats: \`uri-reference\`, \`uri\`, \`iri-reference\`, \`iri\`. \`uri-template\` (RFC 6570) is intentionally NOT walked — templated values contain placeholders.\n\nAbsolute URLs in URI-reference fields feed into the existing external URL health-check pass when \`checkUrlLinks: true\` is set on the collection.\n\nOpt-out: \`checkFrontmatterLinks: false\` per collection, or \`--no-check-frontmatter-links\` on the CLI.\n\n## Per-code severity and allow (\`resources.validation\`)\n\nEvery resources finding is a registry code (e.g. \`LINK_BROKEN_FILE\`, \`FRONTMATTER_SCHEMA_ERROR\`, \`EXTERNAL_URL_DEAD\`) with a default severity. Tune them per-code under \`resources.validation\`:\n\n\`\`\`yaml\nresources:\n validation:\n severity:\n EXTERNAL_URL_DEAD: ignore # silence dead external links entirely\n LINK_UNKNOWN: error # promote unclassified links to build-failing\n allow:\n LINK_TO_GITIGNORED: # keyed by code; value is a list of allow entries\n - paths: [\"docs/internal/**\"]\n reason: \"internal-only notes, intentionally gitignored\"\n\`\`\`\n\n\`severity\` accepts \`error | warning | info | ignore\`. External-URL findings default to \`warning\` and never fail the build on their own — promote them to \`error\` here if you want network checks to gate CI. \`allow\` is keyed by code; each entry\'s \`paths\` (glob list, default \`**/*\`) and \`reason\` suppress that code for matching files rather than disabling it globally. Add \`expires\` (a date string) to flag the allowance for re-review.\n\n## Annotating Frontmatter Schemas with Zod 4\n\nIf your project generates JSON Schemas from Zod (via \`z.toJSONSchema()\`), annotate frontmatter fields that hold links with the appropriate \`format\` so VAT\'s link validator picks them up:\n\n\`\`\`typescript\nimport { z } from \'zod\';\n\nconst PrdFrontmatter = z.object({\n spec_ref: z.string().meta({ format: \'uri-reference\' }), // repo-relative or absolute\n roadmap: z.url().meta({ format: \'uri\' }), // full URL only\n doc_anchor: z.string().meta({ format: \'json-pointer\' }), // JSON Pointer\n});\n\`\`\`\n\n\`format\` values VAT walks for link validation: \`uri\`, \`uri-reference\`, \`iri\`, \`iri-reference\`. \`uri-template\` (RFC 6570) is intentionally NOT walked — templated values contain placeholders.\n\n**Zod 3 users:** \`.meta()\` does not exist in Zod 3. Either upgrade your schema-generation step to Zod 4 (runtime consumers can stay on Zod 3 via peer dependency + common-subset usage), or post-process the generated JSON Schema to inject \`format\` on the relevant field paths.\n\n**Tip:** \`format\` is advisory in JSON Schema; pair it with a \`pattern\` regex when you also need parse-time rejection of invalid inputs.\n\n## URI-references in frontmatter\n\nVAT validates frontmatter fields whose schema position has \`format: uri-reference\`\n(or \`uri\`, \`iri-reference\`, \`iri\`) against the same rules as body links:\n\n- Leading-\`/\` is RFC 3986 §4.2 absolute-path reference — resolved against\n the project root. Same semantics as body links.\n- Anchor fragments and external URLs are accepted; broken local refs error.\n- Inline comments on URI-ref fields are **preserved** when any tool rewrites\n the file via \`openFrontmatter\` (VAT packager, hand-rolled adopter scripts).\n\nExample:\n\n\`\`\`yaml\n---\nparent_spec: /docs/specs/foo.md # the spec this one supersedes\nadrs-cited:\n - /docs/adrs/0007-storage.md # primary reference\n - /docs/adrs/0011-snapshot.md # impacted by storage choice\n---\n\`\`\`\n\nSchema:\n\n\`\`\`json\n{\n \"type\": \"object\",\n \"properties\": {\n \"parent_spec\": { \"type\": \"string\", \"format\": \"uri-reference\" },\n \"adrs-cited\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\", \"format\": \"uri-reference\" }\n }\n }\n}\n\`\`\`\n\nFor tools that **modify** frontmatter (not just validate it), see\n[[markdown-rewriting]].\n\n## Validation Output\n\n\`\`\`yaml\nstatus: success\nfilesScanned: 47\ncollections:\n systems:\n resourceCount: 7\n hasSchema: true\n validationMode: permissive\n adrs:\n resourceCount: 12\n hasSchema: true\n validationMode: permissive\nduration: 234ms\n\`\`\`\n\nErrors appear in stderr with \`file:line: message\` format for editor navigation.\n";
11
11
 
12
12
  export const fragments = {
13
13
  whatResourceCollectionsAre: {
@@ -50,11 +50,21 @@ export const fragments = {
50
50
  body: "When designing a schema for a knowledge-base collection that references other files (e.g., \`parent_prd\`, \`supersedes\`, \`adr_citations[*].adr\`, \`artifacts\`), declare \`format: \"uri-reference\"\` on the field. VAT will then validate those values against the file system using the same engine as markdown link checking — broken paths, missing anchors, gitignore violations, and unknown URI schemes all produce errors.\n\nTo require local committed files (no absolute URLs), add a \`pattern\` excluding scheme prefixes. Standard JSON Schema; stays portable.\n\nVAT walks four URI-family formats: \`uri-reference\`, \`uri\`, \`iri-reference\`, \`iri\`. \`uri-template\` (RFC 6570) is intentionally NOT walked — templated values contain placeholders.\n\nAbsolute URLs in URI-reference fields feed into the existing external URL health-check pass when \`checkUrlLinks: true\` is set on the collection.\n\nOpt-out: \`checkFrontmatterLinks: false\` per collection, or \`--no-check-frontmatter-links\` on the CLI.",
51
51
  text: "## Recommend \`format: \"uri-reference\"\` for path-shaped frontmatter fields\n\nWhen designing a schema for a knowledge-base collection that references other files (e.g., \`parent_prd\`, \`supersedes\`, \`adr_citations[*].adr\`, \`artifacts\`), declare \`format: \"uri-reference\"\` on the field. VAT will then validate those values against the file system using the same engine as markdown link checking — broken paths, missing anchors, gitignore violations, and unknown URI schemes all produce errors.\n\nTo require local committed files (no absolute URLs), add a \`pattern\` excluding scheme prefixes. Standard JSON Schema; stays portable.\n\nVAT walks four URI-family formats: \`uri-reference\`, \`uri\`, \`iri-reference\`, \`iri\`. \`uri-template\` (RFC 6570) is intentionally NOT walked — templated values contain placeholders.\n\nAbsolute URLs in URI-reference fields feed into the existing external URL health-check pass when \`checkUrlLinks: true\` is set on the collection.\n\nOpt-out: \`checkFrontmatterLinks: false\` per collection, or \`--no-check-frontmatter-links\` on the CLI."
52
52
  },
53
+ perCodeSeverityAndAllowResourcesvalidation: {
54
+ header: "## Per-code severity and allow (\`resources.validation\`)",
55
+ body: "Every resources finding is a registry code (e.g. \`LINK_BROKEN_FILE\`, \`FRONTMATTER_SCHEMA_ERROR\`, \`EXTERNAL_URL_DEAD\`) with a default severity. Tune them per-code under \`resources.validation\`:\n\n\`\`\`yaml\nresources:\n validation:\n severity:\n EXTERNAL_URL_DEAD: ignore # silence dead external links entirely\n LINK_UNKNOWN: error # promote unclassified links to build-failing\n allow:\n LINK_TO_GITIGNORED: # keyed by code; value is a list of allow entries\n - paths: [\"docs/internal/**\"]\n reason: \"internal-only notes, intentionally gitignored\"\n\`\`\`\n\n\`severity\` accepts \`error | warning | info | ignore\`. External-URL findings default to \`warning\` and never fail the build on their own — promote them to \`error\` here if you want network checks to gate CI. \`allow\` is keyed by code; each entry\'s \`paths\` (glob list, default \`**/*\`) and \`reason\` suppress that code for matching files rather than disabling it globally. Add \`expires\` (a date string) to flag the allowance for re-review.",
56
+ text: "## Per-code severity and allow (\`resources.validation\`)\n\nEvery resources finding is a registry code (e.g. \`LINK_BROKEN_FILE\`, \`FRONTMATTER_SCHEMA_ERROR\`, \`EXTERNAL_URL_DEAD\`) with a default severity. Tune them per-code under \`resources.validation\`:\n\n\`\`\`yaml\nresources:\n validation:\n severity:\n EXTERNAL_URL_DEAD: ignore # silence dead external links entirely\n LINK_UNKNOWN: error # promote unclassified links to build-failing\n allow:\n LINK_TO_GITIGNORED: # keyed by code; value is a list of allow entries\n - paths: [\"docs/internal/**\"]\n reason: \"internal-only notes, intentionally gitignored\"\n\`\`\`\n\n\`severity\` accepts \`error | warning | info | ignore\`. External-URL findings default to \`warning\` and never fail the build on their own — promote them to \`error\` here if you want network checks to gate CI. \`allow\` is keyed by code; each entry\'s \`paths\` (glob list, default \`**/*\`) and \`reason\` suppress that code for matching files rather than disabling it globally. Add \`expires\` (a date string) to flag the allowance for re-review."
57
+ },
53
58
  annotatingFrontmatterSchemasWithZod4: {
54
59
  header: "## Annotating Frontmatter Schemas with Zod 4",
55
60
  body: "If your project generates JSON Schemas from Zod (via \`z.toJSONSchema()\`), annotate frontmatter fields that hold links with the appropriate \`format\` so VAT\'s link validator picks them up:\n\n\`\`\`typescript\nimport { z } from \'zod\';\n\nconst PrdFrontmatter = z.object({\n spec_ref: z.string().meta({ format: \'uri-reference\' }), // repo-relative or absolute\n roadmap: z.url().meta({ format: \'uri\' }), // full URL only\n doc_anchor: z.string().meta({ format: \'json-pointer\' }), // JSON Pointer\n});\n\`\`\`\n\n\`format\` values VAT walks for link validation: \`uri\`, \`uri-reference\`, \`iri\`, \`iri-reference\`. \`uri-template\` (RFC 6570) is intentionally NOT walked — templated values contain placeholders.\n\n**Zod 3 users:** \`.meta()\` does not exist in Zod 3. Either upgrade your schema-generation step to Zod 4 (runtime consumers can stay on Zod 3 via peer dependency + common-subset usage), or post-process the generated JSON Schema to inject \`format\` on the relevant field paths.\n\n**Tip:** \`format\` is advisory in JSON Schema; pair it with a \`pattern\` regex when you also need parse-time rejection of invalid inputs.",
56
61
  text: "## Annotating Frontmatter Schemas with Zod 4\n\nIf your project generates JSON Schemas from Zod (via \`z.toJSONSchema()\`), annotate frontmatter fields that hold links with the appropriate \`format\` so VAT\'s link validator picks them up:\n\n\`\`\`typescript\nimport { z } from \'zod\';\n\nconst PrdFrontmatter = z.object({\n spec_ref: z.string().meta({ format: \'uri-reference\' }), // repo-relative or absolute\n roadmap: z.url().meta({ format: \'uri\' }), // full URL only\n doc_anchor: z.string().meta({ format: \'json-pointer\' }), // JSON Pointer\n});\n\`\`\`\n\n\`format\` values VAT walks for link validation: \`uri\`, \`uri-reference\`, \`iri\`, \`iri-reference\`. \`uri-template\` (RFC 6570) is intentionally NOT walked — templated values contain placeholders.\n\n**Zod 3 users:** \`.meta()\` does not exist in Zod 3. Either upgrade your schema-generation step to Zod 4 (runtime consumers can stay on Zod 3 via peer dependency + common-subset usage), or post-process the generated JSON Schema to inject \`format\` on the relevant field paths.\n\n**Tip:** \`format\` is advisory in JSON Schema; pair it with a \`pattern\` regex when you also need parse-time rejection of invalid inputs."
57
62
  },
63
+ uriReferencesInFrontmatter: {
64
+ header: "## URI-references in frontmatter",
65
+ body: "VAT validates frontmatter fields whose schema position has \`format: uri-reference\`\n(or \`uri\`, \`iri-reference\`, \`iri\`) against the same rules as body links:\n\n- Leading-\`/\` is RFC 3986 §4.2 absolute-path reference — resolved against\n the project root. Same semantics as body links.\n- Anchor fragments and external URLs are accepted; broken local refs error.\n- Inline comments on URI-ref fields are **preserved** when any tool rewrites\n the file via \`openFrontmatter\` (VAT packager, hand-rolled adopter scripts).\n\nExample:\n\n\`\`\`yaml\n---\nparent_spec: /docs/specs/foo.md # the spec this one supersedes\nadrs-cited:\n - /docs/adrs/0007-storage.md # primary reference\n - /docs/adrs/0011-snapshot.md # impacted by storage choice\n---\n\`\`\`\n\nSchema:\n\n\`\`\`json\n{\n \"type\": \"object\",\n \"properties\": {\n \"parent_spec\": { \"type\": \"string\", \"format\": \"uri-reference\" },\n \"adrs-cited\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\", \"format\": \"uri-reference\" }\n }\n }\n}\n\`\`\`\n\nFor tools that **modify** frontmatter (not just validate it), see\n[[markdown-rewriting]].",
66
+ text: "## URI-references in frontmatter\n\nVAT validates frontmatter fields whose schema position has \`format: uri-reference\`\n(or \`uri\`, \`iri-reference\`, \`iri\`) against the same rules as body links:\n\n- Leading-\`/\` is RFC 3986 §4.2 absolute-path reference — resolved against\n the project root. Same semantics as body links.\n- Anchor fragments and external URLs are accepted; broken local refs error.\n- Inline comments on URI-ref fields are **preserved** when any tool rewrites\n the file via \`openFrontmatter\` (VAT packager, hand-rolled adopter scripts).\n\nExample:\n\n\`\`\`yaml\n---\nparent_spec: /docs/specs/foo.md # the spec this one supersedes\nadrs-cited:\n - /docs/adrs/0007-storage.md # primary reference\n - /docs/adrs/0011-snapshot.md # impacted by storage choice\n---\n\`\`\`\n\nSchema:\n\n\`\`\`json\n{\n \"type\": \"object\",\n \"properties\": {\n \"parent_spec\": { \"type\": \"string\", \"format\": \"uri-reference\" },\n \"adrs-cited\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\", \"format\": \"uri-reference\" }\n }\n }\n}\n\`\`\`\n\nFor tools that **modify** frontmatter (not just validate it), see\n[[markdown-rewriting]]."
67
+ },
58
68
  validationOutput: {
59
69
  header: "## Validation Output",
60
70
  body: "\`\`\`yaml\nstatus: success\nfilesScanned: 47\ncollections:\n systems:\n resourceCount: 7\n hasSchema: true\n validationMode: permissive\n adrs:\n resourceCount: 12\n hasSchema: true\n validationMode: permissive\nduration: 234ms\n\`\`\`\n\nErrors appear in stderr with \`file:line: message\` format for editor navigation.",
@@ -17,6 +17,7 @@ export const text: string;
17
17
 
18
18
  export const fragments: {
19
19
  readonly skillmdStructure: Fragment;
20
+ readonly crossDocumentReferencesInSkillmdFrontmatter: Fragment;
20
21
  readonly bodyStructure: Fragment;
21
22
  readonly referencesSection: Fragment;
22
23
  readonly packagingoptionsReference: Fragment;
@@ -7,7 +7,7 @@ export const meta = {
7
7
  description: "Use when authoring or revising a SKILL.md file — frontmatter, body structure, references, packagingOptions (linkFollowDepth, excludeReferencesFromBundle), and validation overrides. Paired with vat-skill-review for pre-publication checks."
8
8
  };
9
9
 
10
- export const text = "\n# VAT Skill Authoring: SKILL.md Structure and Packaging\n\nThis skill covers authoring SKILL.md files for portable Claude skills: frontmatter shape, body structure, reference links, and the packaging options that control how the skill is bundled for distribution. For the TypeScript agent side (archetypes, result envelopes, orchestration, runtime adapters) use \`vibe-agent-toolkit:vat-agent-authoring\`.\n\n## SKILL.md Structure\n\nA SKILL.md file is the definition file for a portable skill. It tells Claude what the skill does and how to use it. All SKILL.md files must have YAML frontmatter:\n\n\`\`\`markdown\n---\nname: my-skill\ndescription: One sentence — what this skill does and when to use it (≤250 chars)\n---\n\n# My Skill\n\nRest of the skill documentation...\n\`\`\`\n\nRequired frontmatter fields:\n\n- \`name\` — unique identifier, kebab-case (\`^[a-z][a-z0-9-]*$\`), matches the skill\'s directory name after build. Avoid the reserved words \`claude\` and \`anthropic\` (Claude Code rejects non-certified skills using those words — surfaced as \`RESERVED_WORD_IN_NAME\`).\n- \`description\` — trigger description used for Claude\'s skill routing; be specific about activation conditions.\n\nBest practices for \`description\`:\n\n- Lead with an action verb or \`Use when <concrete trigger>\` — filler openers like \"This skill...\", \"A skill that...\", \"Use when you want to...\" fire \`SKILL_DESCRIPTION_FILLER_OPENER\`.\n- Include 2–4 trigger keywords a user is likely to type.\n- Write in third person. First-person (\"I can...\") and conversational second-person (\"You can use...\") fire \`SKILL_DESCRIPTION_WRONG_PERSON\`.\n- Keep under 250 characters so the Claude Code \`/skills\` listing doesn\'t truncate the tail (target ≤200 for safety, ≤130 if shipping a large skill collection). The hard schema limit is 1024.\n\n## Body Structure\n\n- Lead with a short orientation paragraph: what the skill owns and when to reach for it.\n- Use H2 sections for major content blocks; avoid deeply nested H3/H4 trees — they hurt Claude\'s ability to route attention inside the file.\n- Keep SKILL.md under ~500 lines. Longer than that fires \`SKILL_LENGTH_EXCEEDS_RECOMMENDED\` and is a strong signal to split via progressive disclosure (linked reference files) or to spin the content into a sibling skill.\n- Avoid time-sensitive phrasing like \"as of April 2026\" in the body — it ages the skill quickly (\`SKILL_TIME_SENSITIVE_CONTENT\`).\n\n## References Section\n\nA short \`## References\` section at the bottom is the canonical place to list linked resources. Two patterns:\n\n- **Progressive disclosure** — link to \`.md\` files inside the skill directory that get bundled. Keep reference depth ≤ 2 hops; deeper chains fire \`REFERENCE_TOO_DEEP\`.\n- **Prose references to sibling skills** — write \`vibe-agent-toolkit:vat-audit\`, not \`[vat-audit](./vat-audit.md)\`. Markdown links to sibling SKILL.md files cause the packager to transclude the other skill (and fire \`LINK_TO_SKILL_DEFINITION\`).\n\nAvoid linking to navigation files (\`README.md\`, \`index.md\`) — they\'re excluded from the bundle and the link resolves to nothing (\`LINK_TO_NAVIGATION_FILE\`).\n\n## packagingOptions Reference\n\nPackaging options are configured per skill in \`vibe-agent-toolkit.config.yaml\` under \`skills.config.<name>\`:\n\n\`\`\`yaml\nskills:\n include: [\"resources/skills/SKILL.md\", \"resources/skills/*.md\"]\n defaults:\n linkFollowDepth: 2\n config:\n my-skill:\n linkFollowDepth: 1\n resourceNaming: resource-id\n stripPrefix: knowledge-base\n excludeReferencesFromBundle:\n rules:\n - patterns: [\"**/concepts/**\"]\n template: \"Use search to find: {{link.text}}\"\n defaultTemplate: \"{{link.text}} (search knowledge base)\"\n\`\`\`\n\n**\`linkFollowDepth\`** — How deep to follow links from SKILL.md:\n\n| Value | Behavior |\n|-------|----------|\n| \`0\` | Skill file only (no links followed) |\n| \`1\` | Direct links only |\n| \`2\` | Direct + one transitive level **(default)** |\n| \`\"full\"\` | Complete transitive closure |\n\n**\`resourceNaming\`** — How bundled files are named:\n\n| Strategy | Example | Use When |\n|----------|---------|----------|\n| \`basename\` | \`overview.md\` | Few files, unique names **(default)** |\n| \`resource-id\` | \`topics-quickstart-overview.md\` | Many files, flat output |\n| \`preserve-path\` | \`topics/quickstart/overview.md\` | Preserve structure |\n\nUse \`stripPrefix\` to remove a common directory prefix (e.g., \`\"knowledge-base\"\`).\n\n**\`excludeReferencesFromBundle\`** — Rules for excluding files and rewriting their links:\n\n- \`rules[]\` — ordered glob patterns (first match wins), each with optional Handlebars template\n- \`defaultTemplate\` — applied to depth-exceeded links not matching any rule\n\n**Template variables:**\n\n| Variable | Description |\n|----------|-------------|\n| \`{{link.text}}\` | Link display text |\n| \`{{link.href}}\` | Original href (without fragment) |\n| \`{{link.fragment}}\` | Fragment including \`#\` prefix, or empty |\n| \`{{link.type}}\` | Link type (\`\"local_file\"\`, etc.) |\n| \`{{link.resource.id}}\` | Target resource ID (if resolved) |\n| \`{{link.resource.fileName}}\` | Target filename (if resolved) |\n| \`{{skill.name}}\` | Skill name from frontmatter |\n\n## Validation Overrides\n\nThe \`validation\` sub-key in a skill\'s config provides the unified override framework for VAT validation codes:\n\n\`\`\`yaml\nskills:\n config:\n my-skill:\n validation:\n severity:\n LINK_DROPPED_BY_DEPTH: error # upgrade: block on depth-dropped links\n LINK_TO_NAVIGATION_FILE: ignore # silence: this skill intentionally links to READMEs\n allow:\n PACKAGED_UNREFERENCED_FILE:\n - paths: [\"templates/runtime.json\"]\n reason: \"consumed programmatically at runtime\"\n expires: \"2026-09-30\"\n SKILL_LENGTH_EXCEEDS_RECOMMENDED:\n - reason: \"whole-skill concern; paths defaults to [\'**/*\']\"\n\`\`\`\n\nTwo sub-keys, each covering a different override granularity:\n\n- **\`severity\`** — class-level. Raise any code to \`error\` (blocks build), lower to \`warning\` (emits, non-blocking), or \`ignore\` (fully suppressed). Applies to every instance of that code.\n- **\`allow\`** — per-instance. Suppress specific \`(code, path)\` matches with a required \`reason\` and optional \`expires\` date. \`paths\` is optional (defaults to \`[\"**/*\"]\` — the whole skill). Use for legitimate exceptions that don\'t warrant code-wide silencing.\n\nCommon adjustments:\n\n- Downgrade \`LINK_DROPPED_BY_DEPTH\` to \`ignore\` when intentionally linking out to external docs.\n- Allow specific files under \`PACKAGED_UNREFERENCED_FILE\` when they\'re consumed programmatically by CLI scripts at runtime.\n- Raise \`ALLOW_EXPIRED\` to \`error\` for zero-tolerance expiry policies.\n\nExpired \`allow\` entries still apply — VAT emits \`ALLOW_EXPIRED\` as a reminder rather than silently re-surfacing the underlying issue (no surprise build breaks when a date passes). Unused \`allow\` entries surface as \`ALLOW_UNUSED\` (analogous to ESLint\'s unused-disable).\n\n\`vat audit\` is advisory: it applies \`severity\` for display grouping only, ignores \`allow\`, and always exits 0. Use \`vat skills validate\` or \`vat skills build\` for gated checks.\n\n## Pre-publication Check\n\nBefore shipping a skill, walk through the \`vibe-agent-toolkit:vat-skill-review\` checklist — it covers naming, description quality, structure, validation-code triage, and Anthropic\'s skill-authoring best practices. The \`vat skill review <skill>\` CLI command renders a skill-specific view of the same checklist.\n\n## References\n\n- \`vibe-agent-toolkit:vat-skill-review\` — pre-publication quality checklist (general + CLI-backed items, tied to VAT validation codes)\n- \`vibe-agent-toolkit:vat-skill-distribution\` — plugin/marketplace config, \`vat build\`, \`vat verify\`, npm publishing\n- \`vibe-agent-toolkit:vat-knowledge-resources\` — the \`resources:\` config section for multi-collection frontmatter schema validation\n- [Validation Codes Reference](../../../../docs/validation-codes.md) — full list of codes VAT emits, their default severity, and override recipes.\n- [Skill Quality and Compatibility — VAT\'s Stance](../../../../docs/skill-quality-and-compatibility.md) — what VAT believes makes a skill good and compatible.\n";
10
+ export const text = "\n# VAT Skill Authoring: SKILL.md Structure and Packaging\n\nThis skill covers authoring SKILL.md files for portable Claude skills: frontmatter shape, body structure, reference links, and the packaging options that control how the skill is bundled for distribution. For the TypeScript agent side (archetypes, result envelopes, orchestration, runtime adapters) use \`vibe-agent-toolkit:vat-agent-authoring\`.\n\n## SKILL.md Structure\n\nA SKILL.md file is the definition file for a portable skill. It tells Claude what the skill does and how to use it. All SKILL.md files must have YAML frontmatter:\n\n\`\`\`markdown\n---\nname: my-skill\ndescription: One sentence — what this skill does and when to use it (≤250 chars)\n---\n\n# My Skill\n\nRest of the skill documentation...\n\`\`\`\n\nRequired frontmatter fields:\n\n- \`name\` — unique identifier, kebab-case (\`^[a-z][a-z0-9-]*$\`), matches the skill\'s directory name after build. Avoid the reserved words \`claude\` and \`anthropic\` (Claude Code rejects non-certified skills using those words — surfaced as \`RESERVED_WORD_IN_NAME\`).\n- \`description\` — trigger description used for Claude\'s skill routing; be specific about activation conditions.\n\nBest practices for \`description\`:\n\n- Lead with an action verb or \`Use when <concrete trigger>\` — filler openers like \"This skill...\", \"A skill that...\", \"Use when you want to...\" fire \`SKILL_DESCRIPTION_FILLER_OPENER\`.\n- Include 2–4 trigger keywords a user is likely to type.\n- Write in third person. First-person (\"I can...\") and conversational second-person (\"You can use...\") fire \`SKILL_DESCRIPTION_WRONG_PERSON\`.\n- Keep under 250 characters so the Claude Code \`/skills\` listing doesn\'t truncate the tail (target ≤200 for safety, ≤130 if shipping a large skill collection). The hard schema limit is 1024.\n\n## Cross-document references in SKILL.md frontmatter\n\nWhen SKILL.md frontmatter references other documents (parent specs, ADRs,\nrelated skills), use **leading-\`/\`** URI-references:\n\n\`\`\`yaml\n---\nparent_spec: /docs/specs/foo.md\nrelated-skills:\n - /packages/foo/resources/skills/bar/SKILL.md\n---\n\`\`\`\n\nThese resolve against the project root per RFC 3986 §4.2 (same rule VAT\napplies to body links). Source-relative paths (\`../../docs/foo.md\`) also\nwork but are fragile when skills move.\n\nIf a tool needs to programmatically rewrite these references — e.g., when\nmoving a file — load [[markdown-rewriting]] for the canonical pattern.\nNever use \`gray-matter\`, \`front-matter\`, or \`js-yaml\` directly for SKILL.md\nedits; they silently drop frontmatter comments. ESLint enforces this for\nVAT-internal code.\n\n## Body Structure\n\n- Lead with a short orientation paragraph: what the skill owns and when to reach for it.\n- Use H2 sections for major content blocks; avoid deeply nested H3/H4 trees — they hurt Claude\'s ability to route attention inside the file.\n- Keep SKILL.md under ~500 lines. Longer than that fires \`SKILL_LENGTH_EXCEEDS_RECOMMENDED\` and is a strong signal to split via progressive disclosure (linked reference files) or to spin the content into a sibling skill.\n- Avoid time-sensitive phrasing like \"as of April 2026\" in the body — it ages the skill quickly (\`SKILL_TIME_SENSITIVE_CONTENT\`).\n\n## References Section\n\nA short \`## References\` section at the bottom is the canonical place to list linked resources. Two patterns:\n\n- **Progressive disclosure** — link to \`.md\` files inside the skill directory that get bundled. Keep reference depth ≤ 2 hops; deeper chains fire \`REFERENCE_TOO_DEEP\`.\n- **Prose references to sibling skills** — write \`vibe-agent-toolkit:vat-audit\`, not \`[vat-audit](./vat-audit.md)\`. Markdown links to sibling SKILL.md files cause the packager to transclude the other skill (and fire \`LINK_TO_SKILL_DEFINITION\`).\n\nAvoid linking to navigation files (\`README.md\`, \`index.md\`) — they\'re excluded from the bundle and the link resolves to nothing (\`LINK_TO_NAVIGATION_FILE\`).\n\n## packagingOptions Reference\n\nPackaging options are configured per skill in \`vibe-agent-toolkit.config.yaml\` under \`skills.config.<name>\`:\n\n\`\`\`yaml\nskills:\n include: [\"resources/skills/SKILL.md\", \"resources/skills/*.md\"]\n defaults:\n linkFollowDepth: 2\n config:\n my-skill:\n linkFollowDepth: 1\n resourceNaming: resource-id\n stripPrefix: knowledge-base\n excludeReferencesFromBundle:\n rules:\n - patterns: [\"**/concepts/**\"]\n template: \"Use search to find: {{link.text}}\"\n defaultTemplate: \"{{link.text}} (search knowledge base)\"\n\`\`\`\n\n**\`linkFollowDepth\`** — How deep to follow links from SKILL.md:\n\n| Value | Behavior |\n|-------|----------|\n| \`0\` | Skill file only (no links followed) |\n| \`1\` | Direct links only |\n| \`2\` | Direct + one transitive level **(default)** |\n| \`\"full\"\` | Complete transitive closure |\n\n**\`resourceNaming\`** — How bundled files are named:\n\n| Strategy | Example | Use When |\n|----------|---------|----------|\n| \`basename\` | \`overview.md\` | Few files, unique names **(default)** |\n| \`resource-id\` | \`topics-quickstart-overview.md\` | Many files, flat output |\n| \`preserve-path\` | \`topics/quickstart/overview.md\` | Preserve structure |\n\nUse \`stripPrefix\` to remove a common directory prefix (e.g., \`\"knowledge-base\"\`).\n\n**\`excludeReferencesFromBundle\`** — Rules for excluding files and rewriting their links:\n\n- \`rules[]\` — ordered glob patterns (first match wins), each with optional Handlebars template\n- \`defaultTemplate\` — applied to depth-exceeded links not matching any rule\n\n**Template variables:**\n\n| Variable | Description |\n|----------|-------------|\n| \`{{link.text}}\` | Link display text |\n| \`{{link.href}}\` | Original href (without fragment) |\n| \`{{link.fragment}}\` | Fragment including \`#\` prefix, or empty |\n| \`{{link.type}}\` | Link type (\`\"local_file\"\`, etc.) |\n| \`{{link.resource.id}}\` | Target resource ID (if resolved) |\n| \`{{link.resource.fileName}}\` | Target filename (if resolved) |\n| \`{{skill.name}}\` | Skill name from frontmatter |\n\n## Validation Overrides\n\nThe \`validation\` sub-key in a skill\'s config provides the unified override framework for VAT validation codes:\n\n\`\`\`yaml\nskills:\n config:\n my-skill:\n validation:\n severity:\n LINK_DROPPED_BY_DEPTH: error # upgrade: block on depth-dropped links\n LINK_TO_NAVIGATION_FILE: ignore # silence: this skill intentionally links to READMEs\n allow:\n PACKAGED_UNREFERENCED_FILE:\n - paths: [\"templates/runtime.json\"]\n reason: \"consumed programmatically at runtime\"\n expires: \"2026-09-30\"\n SKILL_LENGTH_EXCEEDS_RECOMMENDED:\n - reason: \"whole-skill concern; paths defaults to [\'**/*\']\"\n\`\`\`\n\nTwo sub-keys, each covering a different override granularity:\n\n- **\`severity\`** — class-level. Raise any code to \`error\` (blocks build), lower to \`warning\` (emits, non-blocking), or \`ignore\` (fully suppressed). Applies to every instance of that code.\n- **\`allow\`** — per-instance. Suppress specific \`(code, path)\` matches with a required \`reason\` and optional \`expires\` date. \`paths\` is optional (defaults to \`[\"**/*\"]\` — the whole skill). Use for legitimate exceptions that don\'t warrant code-wide silencing.\n\nCommon adjustments:\n\n- Downgrade \`LINK_DROPPED_BY_DEPTH\` to \`ignore\` when intentionally linking out to external docs.\n- Allow specific files under \`PACKAGED_UNREFERENCED_FILE\` when they\'re consumed programmatically by CLI scripts at runtime.\n- Raise \`ALLOW_EXPIRED\` to \`error\` for zero-tolerance expiry policies.\n\nExpired \`allow\` entries still apply — VAT emits \`ALLOW_EXPIRED\` as a reminder rather than silently re-surfacing the underlying issue (no surprise build breaks when a date passes). Unused \`allow\` entries surface as \`ALLOW_UNUSED\` (analogous to ESLint\'s unused-disable).\n\n\`vat audit\` is advisory: it applies \`severity\` for display grouping only, ignores \`allow\`, and always exits 0. Use \`vat skills validate\` or \`vat skills build\` for gated checks.\n\n## Pre-publication Check\n\nBefore shipping a skill, walk through the \`vibe-agent-toolkit:vat-skill-review\` checklist — it covers naming, description quality, structure, validation-code triage, and Anthropic\'s skill-authoring best practices. The \`vat skill review <skill>\` CLI command renders a skill-specific view of the same checklist.\n\n## References\n\n- \`vibe-agent-toolkit:vat-skill-review\` — pre-publication quality checklist (general + CLI-backed items, tied to VAT validation codes)\n- \`vibe-agent-toolkit:vat-skill-distribution\` — plugin/marketplace config, \`vat build\`, \`vat verify\`, npm publishing\n- \`vibe-agent-toolkit:vat-knowledge-resources\` — the \`resources:\` config section for multi-collection frontmatter schema validation\n- [Validation Codes Reference](../../../../docs/validation-codes.md) — full list of codes VAT emits, their default severity, and override recipes.\n- [Skill Quality and Compatibility — VAT\'s Stance](../../../../docs/skill-quality-and-compatibility.md) — what VAT believes makes a skill good and compatible.\n";
11
11
 
12
12
  export const fragments = {
13
13
  skillmdStructure: {
@@ -15,6 +15,11 @@ export const fragments = {
15
15
  body: "A SKILL.md file is the definition file for a portable skill. It tells Claude what the skill does and how to use it. All SKILL.md files must have YAML frontmatter:\n\n\`\`\`markdown\n---\nname: my-skill\ndescription: One sentence — what this skill does and when to use it (≤250 chars)\n---\n\n# My Skill\n\nRest of the skill documentation...\n\`\`\`\n\nRequired frontmatter fields:\n\n- \`name\` — unique identifier, kebab-case (\`^[a-z][a-z0-9-]*$\`), matches the skill\'s directory name after build. Avoid the reserved words \`claude\` and \`anthropic\` (Claude Code rejects non-certified skills using those words — surfaced as \`RESERVED_WORD_IN_NAME\`).\n- \`description\` — trigger description used for Claude\'s skill routing; be specific about activation conditions.\n\nBest practices for \`description\`:\n\n- Lead with an action verb or \`Use when <concrete trigger>\` — filler openers like \"This skill...\", \"A skill that...\", \"Use when you want to...\" fire \`SKILL_DESCRIPTION_FILLER_OPENER\`.\n- Include 2–4 trigger keywords a user is likely to type.\n- Write in third person. First-person (\"I can...\") and conversational second-person (\"You can use...\") fire \`SKILL_DESCRIPTION_WRONG_PERSON\`.\n- Keep under 250 characters so the Claude Code \`/skills\` listing doesn\'t truncate the tail (target ≤200 for safety, ≤130 if shipping a large skill collection). The hard schema limit is 1024.",
16
16
  text: "## SKILL.md Structure\n\nA SKILL.md file is the definition file for a portable skill. It tells Claude what the skill does and how to use it. All SKILL.md files must have YAML frontmatter:\n\n\`\`\`markdown\n---\nname: my-skill\ndescription: One sentence — what this skill does and when to use it (≤250 chars)\n---\n\n# My Skill\n\nRest of the skill documentation...\n\`\`\`\n\nRequired frontmatter fields:\n\n- \`name\` — unique identifier, kebab-case (\`^[a-z][a-z0-9-]*$\`), matches the skill\'s directory name after build. Avoid the reserved words \`claude\` and \`anthropic\` (Claude Code rejects non-certified skills using those words — surfaced as \`RESERVED_WORD_IN_NAME\`).\n- \`description\` — trigger description used for Claude\'s skill routing; be specific about activation conditions.\n\nBest practices for \`description\`:\n\n- Lead with an action verb or \`Use when <concrete trigger>\` — filler openers like \"This skill...\", \"A skill that...\", \"Use when you want to...\" fire \`SKILL_DESCRIPTION_FILLER_OPENER\`.\n- Include 2–4 trigger keywords a user is likely to type.\n- Write in third person. First-person (\"I can...\") and conversational second-person (\"You can use...\") fire \`SKILL_DESCRIPTION_WRONG_PERSON\`.\n- Keep under 250 characters so the Claude Code \`/skills\` listing doesn\'t truncate the tail (target ≤200 for safety, ≤130 if shipping a large skill collection). The hard schema limit is 1024."
17
17
  },
18
+ crossDocumentReferencesInSkillmdFrontmatter: {
19
+ header: "## Cross-document references in SKILL.md frontmatter",
20
+ body: "When SKILL.md frontmatter references other documents (parent specs, ADRs,\nrelated skills), use **leading-\`/\`** URI-references:\n\n\`\`\`yaml\n---\nparent_spec: /docs/specs/foo.md\nrelated-skills:\n - /packages/foo/resources/skills/bar/SKILL.md\n---\n\`\`\`\n\nThese resolve against the project root per RFC 3986 §4.2 (same rule VAT\napplies to body links). Source-relative paths (\`../../docs/foo.md\`) also\nwork but are fragile when skills move.\n\nIf a tool needs to programmatically rewrite these references — e.g., when\nmoving a file — load [[markdown-rewriting]] for the canonical pattern.\nNever use \`gray-matter\`, \`front-matter\`, or \`js-yaml\` directly for SKILL.md\nedits; they silently drop frontmatter comments. ESLint enforces this for\nVAT-internal code.",
21
+ text: "## Cross-document references in SKILL.md frontmatter\n\nWhen SKILL.md frontmatter references other documents (parent specs, ADRs,\nrelated skills), use **leading-\`/\`** URI-references:\n\n\`\`\`yaml\n---\nparent_spec: /docs/specs/foo.md\nrelated-skills:\n - /packages/foo/resources/skills/bar/SKILL.md\n---\n\`\`\`\n\nThese resolve against the project root per RFC 3986 §4.2 (same rule VAT\napplies to body links). Source-relative paths (\`../../docs/foo.md\`) also\nwork but are fragile when skills move.\n\nIf a tool needs to programmatically rewrite these references — e.g., when\nmoving a file — load [[markdown-rewriting]] for the canonical pattern.\nNever use \`gray-matter\`, \`front-matter\`, or \`js-yaml\` directly for SKILL.md\nedits; they silently drop frontmatter comments. ESLint enforces this for\nVAT-internal code."
22
+ },
18
23
  bodyStructure: {
19
24
  header: "## Body Structure",
20
25
  body: "- Lead with a short orientation paragraph: what the skill owns and when to reach for it.\n- Use H2 sections for major content blocks; avoid deeply nested H3/H4 trees — they hurt Claude\'s ability to route attention inside the file.\n- Keep SKILL.md under ~500 lines. Longer than that fires \`SKILL_LENGTH_EXCEEDS_RECOMMENDED\` and is a strong signal to split via progressive disclosure (linked reference files) or to spin the content into a sibling skill.\n- Avoid time-sensitive phrasing like \"as of April 2026\" in the body — it ages the skill quickly (\`SKILL_TIME_SENSITIVE_CONTENT\`).",
@@ -7,7 +7,7 @@ export const meta = {
7
7
  description: "Use when setting up \`vat build\`, configuring plugin distribution (marketplace, plugins, managed settings), npm publishing with postinstall hooks, or \`vat verify\` — the full pipeline from skill source to installed plugin."
8
8
  };
9
9
 
10
- export const text = "\n# VAT Distribution: Build, Publish & Install\n\n## Scope\n\nThis skill covers the **file-based install method for Claude Code CLI** (\`~/.claude/plugins/\`).\nThis is the only install method VAT currently supports.\n\nFor the full install landscape — Claude Desktop paths, enterprise CI deployment,\nAnthropic Cloud org management, MDM integration, and the \`vat plugins uninstall\`\ndesign — see the contributor reference at \`docs/contributing/vat-install-architecture.md\`\nin the \`vibe-agent-toolkit\` repo (contributor material, not bundled with this skill).\n\n## Overview\n\nVAT distributes skills as **Claude plugins** via npm packages. The pipeline:\n\n1. \`vat build\` compiles SKILL.md sources into plugin artifacts\n2. \`npm publish\` pushes the package to a registry\n3. \`npm install\` triggers a postinstall hook that registers the plugin in Claude Code\'s plugin system\n\nSkills installed this way appear in Claude Code as \`/plugin-name:skill-name\`.\n\n## Project Structure\n\n\`\`\`\nmy-project/\n├── package.json ← vat.skills + postinstall hook + publishConfig\n├── vibe-agent-toolkit.config.yaml ← skills: + claude: config\n├── resources/\n│ └── skills/\n│ └── SKILL.md\n└── dist/ ← generated by vat build\n ├── skills/my-skill/ ← packaged skill\n └── .claude/plugins/marketplaces/\n └── my-marketplace/plugins/my-plugin/\n ├── .claude-plugin/plugin.json\n └── skills/my-skill/SKILL.md\n\`\`\`\n\n## Step 1: package.json Configuration\n\n\`\`\`json\n{\n \"name\": \"@myorg/my-skills\",\n \"version\": \"1.0.0\",\n \"vat\": {\n \"version\": \"1.0\",\n \"skills\": [\"my-skill\"]\n },\n \"dependencies\": {\n \"vibe-agent-toolkit\": \"latest\"\n },\n \"scripts\": {\n \"build:vat\": \"vat build\",\n \"postinstall\": \"vat claude plugin install --npm-postinstall 2>/dev/null || exit 0\"\n },\n \"files\": [\"dist\", \"README.md\"],\n \"publishConfig\": {\n \"registry\": \"https://registry.npmjs.org\"\n }\n}\n\`\`\`\n\n**\`vibe-agent-toolkit\` must be in \`dependencies\` (not \`devDependencies\`).** npm adds all \`bin\` entries from runtime dependencies to \`./node_modules/.bin/\` and puts that on PATH when running lifecycle scripts. This is how \`vat\` is available during your postinstall without being globally installed. If it is in \`devDependencies\`, npm will not install it on the user\'s machine and the postinstall will fail with \"command not found\".\n\nThe \`vat.skills\` array contains skill name strings for npm discoverability. Skill source paths and packaging config live in \`vibe-agent-toolkit.config.yaml\` (see Step 2).\n\nFor private GitHub Packages registry:\n\`\`\`json\n\"publishConfig\": {\n \"registry\": \"https://npm.pkg.github.com\",\n \"access\": \"restricted\"\n}\n\`\`\`\n\nUsers (or IT) installing from GitHub Packages need \`.npmrc\` configured with the scope registry and a read-only token:\n\`\`\`\n@myorg:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}\n\`\`\`\n\nIT deploying to managed machines should pre-configure \`.npmrc\` at the system or user level before running install commands. End users do not need to understand npm or the registry — IT handles it once.\n\n## Handling Plugin Renames: vat.replaces\n\n> **Use this only when renaming a plugin, merging plugins, or cleaning up legacy flat-skill installs. Normal upgrades (same plugin name, same skills) do NOT need \`vat.replaces\` — the installer already overwrites the plugin directory on every install.**\n\n### The problem: silent stale skills\n\nWhen you rename a plugin or reorganize skills across packages, the old registration remains in Claude Code. Claude Code **does not warn you** when two plugins provide conflicting skill names — the first-registered (stale) skill silently wins. Users continue loading old content from the renamed plugin unless the old registration is explicitly removed.\n\nThis is the scenario where \`vat.replaces\` is needed:\n- Plugin renamed: \`old-plugin-name\` → \`new-plugin-name\`\n- Two old plugins merged into one new plugin\n- Skills previously installed to \`~/.claude/skills/<name>\` (legacy pre-0.1.20 flat install) now delivered via the plugin tree\n\n### How it works\n\nWhen a VAT package is installed (via postinstall hook or \`--dev\`), the installer reads \`vat.replaces\` from \`package.json\` and — **before** installing the new plugin:\n\n1. For each name in \`replaces.plugins\`: uninstalls \`<name>@<marketplace>\` — removes plugin directory, cache entry, registry entry, and \`settings.json\` entry\n2. For each name in \`replaces.flatSkills\`: deletes \`~/.claude/skills/<name>\` — removes legacy pre-0.1.20 flat installs\n\nBoth operations are idempotent — \"not found\" is handled gracefully.\n\n### Schema\n\n\`\`\`json\n\"vat\": {\n \"version\": \"1.0\",\n \"skills\": [\"authoring\", \"audit\"],\n \"replaces\": {\n \"plugins\": [\"my-old-plugin-name\"],\n \"flatSkills\": [\"my-old-skill\", \"another-old-skill\"]\n }\n}\n\`\`\`\n\nBoth \`plugins\` and \`flatSkills\` are optional arrays. The entire \`replaces\` key is optional — omit it when there is nothing to clean up.\n\n### Real example: vat-development-agents 0.1.21\n\nThis package (\`vat-development-agents\`) renamed its plugin from \`vat-development-agents\` to \`vibe-agent-toolkit\` in v0.1.21. Without \`vat.replaces\`, users who had already installed v0.1.20 would have both \`vat-development-agents@vat-skills\` and \`vibe-agent-toolkit@vat-skills\` registered — Claude Code would serve stale skill content from the old plugin.\n\nThe fix in \`package.json\`:\n\n\`\`\`json\n\"vat\": {\n \"version\": \"1.0\",\n \"skills\": [\"vibe-agent-toolkit\", \"resources\", \"distribution\", \"authoring\", \"audit\", \"debugging\", \"install\"],\n \"replaces\": {\n \"plugins\": [\"vat-development-agents\"],\n \"flatSkills\": [\"vibe-agent-toolkit\", \"resources\"]\n }\n}\n\`\`\`\n\n- \`plugins\`: removes the old plugin registration (same marketplace, old plugin name)\n- \`flatSkills\`: removes legacy \`~/.claude/skills/vibe-agent-toolkit\` and \`~/.claude/skills/resources\` entries from users who installed before 0.1.20 switched to the plugin tree\n\n### Non-obvious gotchas\n\n**1. Plugin names in \`replaces.plugins\` have NO \`@marketplace\`**\n\nThe format is just the plugin name — e.g. \`\"vat-development-agents\"\` — NOT \`\"vat-development-agents@vat-skills\"\`. The installer infers the marketplace from the current package\'s dist tree. Using \`@marketplace\` syntax here would be wrong.\n\n**2. \`replaces.plugins\` is for old plugin registrations, not for skills that moved between plugins**\n\nIf a skill moved from \`plugin-a\` to \`plugin-b\` within the same marketplace, list \`\"plugin-a\"\` in \`replaces.plugins\` to clean up the entire old plugin. You do not manage individual skills — you manage plugins.\n\n**3. \`replaces.flatSkills\` is ONLY for the legacy \`~/.claude/skills/\` location**\n\nThis is specifically for skills that were previously installed as flat files to \`~/.claude/skills/<name>\` (pre-plugin-tree, before v0.1.20). Skills within the plugin tree (under \`~/.claude/plugins/\`) are handled via \`replaces.plugins\`. Do not mix them up.\n\n**4. Normal upgrades need nothing**\n\nIf you publish a new version of the same package with the same plugin name, the installer overwrites the plugin directory automatically. \`vat.replaces\` is only for the case where the old name is different from the new name.\n\n**5. The symptom is subtle and delayed**\n\nYou will not see an error. Claude Code simply loads the first-registered skill with a given name. If the stale plugin is registered first (alphabetically or by install order), your new content is invisible until you remove the old registration.\n\n## Step 2: vibe-agent-toolkit.config.yaml\n\n\`\`\`yaml\nversion: 1\n\nskills:\n include:\n - \"resources/skills/**/SKILL.md\"\n\nclaude:\n marketplaces:\n my-marketplace: # org/publisher identity\n owner:\n name: My Organization\n plugins:\n - name: my-plugin # installable unit\n description: My plugin description\n\`\`\`\n\nThe top-level \`skills:\` section drives standalone skill builds (output: \`dist/skills/\`). The \`claude:\` section defines plugins, which are assembled from their own \`plugins/<name>/\` directories (plugin-local skills under \`plugins/<name>/skills/**/SKILL.md\`). Each marketplace has \`owner\` and \`plugins\` fields (strict schema — no extra fields).\n\n**Naming convention:** marketplace = org identity (e.g. \`acme\`), plugin = this package\n(e.g. \`acme-tools\`). Registers as \`my-plugin@my-marketplace\` in Claude\'s plugin registry.\n\n### Multiple skills in one plugin\n\nList all skills in \`vat.skills\` for npm discoverability:\n\n\`\`\`json\n\"vat\": {\n \"version\": \"1.0\",\n \"skills\": [\"my-linting\", \"my-testing\"]\n}\n\`\`\`\n\nEach skill lives as a subdirectory of the plugin under \`plugins/<name>/skills/<skill>/SKILL.md\`:\n\n\`\`\`\nplugins/my-plugin/\n skills/\n my-linting/SKILL.md\n my-testing/SKILL.md\n\`\`\`\n\nAll plugin-local skills found under \`plugins/<name>/skills/\` are packaged into the plugin automatically — no per-plugin selector is needed or supported. Skill names must be globally unique across all plugins.\n\n## Step 3: Build\n\n\`\`\`bash\nvat build # skills phase then claude phase\nvat verify # validates resources + skills + claude artifacts\n\`\`\`\n\n### What vat build does\n\nTwo phases, run in dependency order:\n\n1. **\`vat skills build\`** — reads \`vibe-agent-toolkit.config.yaml skills:\` section, discovers SKILL.md files via include/exclude globs, compiles each into \`dist/skills/<name>/\`\n2. **\`vat claude plugin build\`** — reads \`vibe-agent-toolkit.config.yaml claude:\` section, wraps built skills into \`dist/.claude/plugins/marketplaces/<mp>/plugins/<plugin>/\` structure with \`.claude-plugin/plugin.json\`. Cleans stale output before each build.\n\nIndividual commands still work:\n\`\`\`bash\nvat skills build # skills phase only\nvat claude plugin build # claude plugin phase only (requires skills already built)\n\`\`\`\n\n## Step 4: Publish\n\n\`\`\`bash\nnpm publish --tag next # RC/pre-release\nnpm publish # stable release\n\`\`\`\n\n## Marketplace Distribution\n\nMarketplace distribution publishes a dedicated branch to GitHub that Claude Code users can install directly — no npm account or registry required.\n\n### How it works\n\n1. \`vat build\` compiles skills and plugin artifacts into \`dist/\`\n2. \`vat claude marketplace publish\` pushes the \`dist/.claude/\` tree to a dedicated branch (e.g. \`claude-marketplace\`) in your GitHub repo\n3. Users install via the slash command: \`/plugin marketplace add owner/repo#claude-marketplace\`\n\n### Configuration\n\nAdd a \`publish\` section under your marketplace in \`vibe-agent-toolkit.config.yaml\`:\n\n\`\`\`yaml\nclaude:\n marketplaces:\n my-marketplace:\n owner:\n name: My Organization\n plugins:\n - name: my-plugin\n description: My plugin description\n publish:\n github:\n repo: owner/repo # GitHub repo to publish to\n branch: claude-marketplace # branch that stores the installable artifacts\n\`\`\`\n\n### Publish workflow\n\n\`\`\`bash\nvat build # build all artifacts first\nvat claude marketplace publish # push dist/.claude/ to the publish branch\nvat claude marketplace publish --dry-run # preview what would be published (no push)\n\`\`\`\n\n### Consumer install\n\nOnce published, users install with:\n\n\`\`\`\n/plugin marketplace add owner/repo#claude-marketplace\n\`\`\`\n\nNo npm, no registry, no token required. Claude Code fetches the branch directly from GitHub.\n\n### Testing locally\n\nAfter publishing, verify the marketplace works end-to-end:\n\n\`\`\`bash\nclaude plugin marketplace add owner/repo#claude-marketplace\nclaude plugin install my-plugin@my-marketplace\nclaude plugin validate ~/.claude/plugins/cache/my-marketplace/my-plugin/<version>\nclaude plugin list # verify status: enabled\n\`\`\`\n\nStart a new Claude Code session to confirm skills load. See the [Marketplace Distribution Guide](https://github.com/jdutton/vibe-agent-toolkit/blob/main/docs/guides/marketplace-distribution.md#testing-your-marketplace) for the full testing checklist and known issues.\n\n**Note (Claude Code v2.1.81):** If re-adding a marketplace with the same name as an existing one (e.g., switching from npm to GitHub source), remove the old marketplace first: \`claude plugin marketplace remove <name>\` then re-add. Otherwise the old source is silently reused.\n\n### Per-plugin versioning (multi-plugin marketplaces)\n\nVAT supports two versioning models for a marketplace:\n\n**Single-version (default for skills-only marketplaces).** No \`version\` is declared on individual plugins. All plugins inherit the root \`package.json:version\`. This is the model used by \`vibe-agent-toolkit\` and \`avonrisk-sdlc\` — the marketplace is treated as one release artifact.\n\n**Per-plugin versioning** (multi-plugin marketplaces with independent release cadences). Each plugin declares its own \`version\`. Recommended when topical plugins under one marketplace evolve on independent timelines (e.g., AvonRiskBuilders\' \`ai-digest\`, \`bank-reconciliation\`).\n\n#### Where to declare a per-plugin version\n\nTwo options, in precedence order:\n\n1. **Marketplace config** (\`vibe-agent-toolkit.config.yaml\`) — most explicit:\n \`\`\`yaml\n claude:\n marketplaces:\n my-marketplace:\n plugins:\n - name: ai-digest\n version: 0.2.0\n skills: \'*\'\n \`\`\`\n2. **Plugin source** (\`plugins/<name>/.claude-plugin/plugin.json:version\`) — most ergonomic for plugin authors:\n \`\`\`json\n { \"name\": \"ai-digest\", \"version\": \"0.2.0\", \"description\": \"...\" }\n \`\`\`\n\nIf both declare a version, marketplace config wins and VAT logs a reconciliation warning. If neither is declared, VAT falls back to the root \`package.json:version\` (single-version model).\n\n#### What \`vat claude marketplace publish\` does for multi-plugin marketplaces\n\nFor each plugin with a resolved version:\n\n- The published \`.claude-plugin/marketplace.json\` includes the per-plugin \`version\` field on each plugin entry.\n- After the publish-branch push succeeds, VAT pushes a \`<plugin>-v<version>\` tag to the **source repo** (not the marketplace branch) — e.g., \`ai-digest-v0.2.0\`. \"Source repo\" means the working directory where you invoke \`vat claude marketplace publish\` (i.e., \`process.cwd()\`); if you invoke from a subdirectory of a worktree, tags land in the containing repo, not the subdirectory. Tag-push failures log a warning but do not roll back the publish.\n- If \`<plugin.source>/CHANGELOG.md\` exists in source (or the marketplace plugin entry\'s \`changelog\` field points to a file), it is bundled into the published marketplace at \`plugins/<name>/CHANGELOG.md\`, alongside the marketplace-level CHANGELOG.\n\nThe marketplace-level CHANGELOG (under \`publish.changelog\` in the config) continues to work unchanged.\n\n#### Default CHANGELOG location\n\nThe default per-plugin CHANGELOG path is \`<plugin.source>/CHANGELOG.md\`, anchored to the entry\'s \`source\` field (default \`plugins/<name>\`). It is NOT assumed to be \`plugins/<name>/CHANGELOG.md\` — if you override \`source\`, the default CHANGELOG path follows.\n\nTo use a non-default path, set the \`changelog\` field on the marketplace plugin entry (relative to the plugin source dir):\n\n\`\`\`yaml\nplugins:\n - name: ai-digest\n source: plugins/ai-digest\n changelog: docs/RELEASES.md\n skills: \'*\'\n\`\`\`\n\n#### Backwards compatibility\n\nMarketplaces with no per-plugin version anywhere are unaffected. The root \`package.json\` version flows through to every plugin, the published \`marketplace.json\` either omits per-plugin \`version\` or includes the same value for all plugins, and tag pushes use the inherited version (e.g., both plugins tagged \`<plugin>-v1.0.0\`).\n\n## Step 5: User Install\n\n### Recommended: npm global install (postinstall runs automatically)\n\n\`\`\`bash\nnpm install -g @myorg/my-skills\n\`\`\`\n\nThe postinstall hook fires automatically and registers the plugin in Claude. This is the correct path for IT-managed deployments — no other tools required on the user\'s machine.\n\n### Developer/IT one-off install via npx\n\n\`\`\`bash\nnpx vibe-agent-toolkit claude plugin install npm:@myorg/my-skills\n\`\`\`\n\nDownloads and runs VAT via npx to install a package without a global install. Useful for CI, scripting, or testing from a developer machine. Requires the npm scope registry to be configured (\`.npmrc\`) if installing from a private registry.\n\n### How plugin installation works\n\nWhen \`npm install\` runs the postinstall hook (\`vat claude plugin install --npm-postinstall\`):\n\n- VAT detects \`dist/.claude/plugins/marketplaces/\` directory in the installed package\n- Copies the plugin tree to Claude\'s plugin directory (dumb recursive copy)\n- Writes to these locations:\n 1. \`~/.claude/plugins/marketplaces/<marketplace>/plugins/<plugin>/\` — plugin files\n 2. \`~/.claude/plugins/known_marketplaces.json\` — marketplace registry\n 3. \`~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/\` — version cache\n 4. \`~/.claude/plugins/installed_plugins.json\` — installation record\n 5. \`~/.claude/settings.json\` \`enabledPlugins\` — activates the plugin\n\nIf no \`dist/.claude/plugins/marketplaces/\` directory exists (package wasn\'t built before publish): a guidance message is emitted and the hook exits 0. The publisher must run \`vat build\` and re-publish.\n\nSkills are then available in Claude Code as \`/plugin-name:skill-name\`.\n\n## managed-settings.json Validation (Enterprise)\n\n\`\`\`yaml\nclaude:\n managedSettings: managed-settings.json\n\`\`\`\n\n\`vat verify\` validates this file against the ManagedSettings schema. Catches typos and schema errors before deployment. Does NOT deploy the file — deployment is a separate concern.\n\n## --target claude-web (ZIP Upload)\n\nFor uploading skills directly to \`claude.ai/settings/capabilities\`:\n\n\`\`\`bash\nvat skills package ./SKILL.md -o ./dist/ --target claude-web\n\`\`\`\n\nProduces a ZIP:\n\`\`\`\nmy-skill.zip\n└── my-skill/\n ├── SKILL.md # skill definition (required)\n ├── scripts/ # executable code (.mjs, .py, .sh) — optional\n ├── references/ # markdown reference material — optional\n └── assets/ # static data, templates, config — optional\n\`\`\`\n\nConfigure source path mappings in \`vibe-agent-toolkit.config.yaml\`:\n\`\`\`yaml\nskills:\n include:\n - \"skills/**/SKILL.md\"\n config:\n my-skill:\n claudeWebTarget:\n scripts: [\"./src/helpers/**/*.ts\"]\n assets: [\"./assets/**\"]\n\`\`\`\n\nTypeScript files in \`scripts\` are tree-shaken and compiled to standalone \`.mjs\`.\n\n## Quick Reference\n\n| Task | Command |\n|---|---|\n| Build everything | \`vat build\` |\n| Verify everything | \`vat verify\` |\n| Build skills only | \`vat skills build\` |\n| Build claude plugin artifacts only | \`vat claude plugin build\` |\n| Install via npm (end user) | \`npm install -g @org/pkg\` |\n| Install via npx (developer/IT) | \`npx vibe-agent-toolkit claude plugin install npm:@org/pkg\` |\n| List installed plugins | \`vat claude plugin list\` |\n| Uninstall a plugin | \`vat claude plugin uninstall --all\` |\n| Package for claude.ai upload | \`vat skills package ./SKILL.md -o ./dist/ --target claude-web\` |\n\n## Running VAT Without Global Install\n\n\`\`\`bash\nnpx vibe-agent-toolkit <command> # npm/Node.js\nbunx vibe-agent-toolkit <command> # Bun\n\`\`\`\n\nAll \`vat\` commands in this skill work with these alternatives.\n\n## Future: Zero-Dependency Postinstall (Option B)\n\nA planned improvement: \`vat build\` would bundle the plugin install logic into \`dist/postinstall.js\` — a fully self-contained script with no npm dependencies. The postinstall script would become simply \`node ./dist/postinstall.js\`. This eliminates \`vibe-agent-toolkit\` as a runtime dependency entirely, reducing install footprint for end users. Until then, Option C (runtime \`vibe-agent-toolkit\` dep) is the correct approach.\n\n## Full-plugin authoring (commands, hooks, agents, MCP)\n\nVAT supports bundling any Claude Code plugin asset — not just skills. Drop the plugin\nunder \`plugins/<name>/\` in the same native layout Claude expects. VAT tree-copies\neverything (minus \`skills/\` and \`.claude-plugin/\`), merges author \`plugin.json\` with\nVAT-owned identity fields, and applies any \`files[]\` mappings for artifacts built\noutside the plugin dir.\n\nSee [docs/guides/marketplace-distribution.md](../../../../docs/guides/marketplace-distribution.md) section \"Full-plugin authoring\".\n";
10
+ export const text = "\n# VAT Distribution: Build, Publish & Install\n\n## Scope\n\nThis skill covers the **file-based install method for Claude Code CLI** (\`~/.claude/plugins/\`).\nThis is the only install method VAT currently supports.\n\nFor the full install landscape — Claude Desktop paths, enterprise CI deployment,\nAnthropic Cloud org management, MDM integration, and the \`vat plugins uninstall\`\ndesign — see the contributor reference at \`docs/contributing/vat-install-architecture.md\`\nin the \`vibe-agent-toolkit\` repo (contributor material, not bundled with this skill).\n\n## Overview\n\nVAT distributes skills as **Claude plugins** via npm packages. The pipeline:\n\n1. \`vat build\` compiles SKILL.md sources into plugin artifacts\n2. \`npm publish\` pushes the package to a registry\n3. \`npm install\` triggers a postinstall hook that registers the plugin in Claude Code\'s plugin system\n\nSkills installed this way appear in Claude Code as \`/plugin-name:skill-name\`.\n\n## Project Structure\n\n\`\`\`\nmy-project/\n├── package.json ← vat.skills + postinstall hook + publishConfig\n├── vibe-agent-toolkit.config.yaml ← skills: + claude: config\n├── resources/\n│ └── skills/\n│ └── SKILL.md\n└── dist/ ← generated by vat build\n ├── skills/my-skill/ ← packaged skill\n └── .claude/plugins/marketplaces/\n └── my-marketplace/plugins/my-plugin/\n ├── .claude-plugin/plugin.json\n └── skills/my-skill/SKILL.md\n\`\`\`\n\n## Step 1: package.json Configuration\n\n\`\`\`json\n{\n \"name\": \"@myorg/my-skills\",\n \"version\": \"1.0.0\",\n \"vat\": {\n \"version\": \"1.0\",\n \"skills\": [\"my-skill\"]\n },\n \"dependencies\": {\n \"vibe-agent-toolkit\": \"latest\"\n },\n \"scripts\": {\n \"build:vat\": \"vat build\",\n \"postinstall\": \"vat claude plugin install --npm-postinstall 2>/dev/null || exit 0\"\n },\n \"files\": [\"dist\", \"README.md\"],\n \"publishConfig\": {\n \"registry\": \"https://registry.npmjs.org\"\n }\n}\n\`\`\`\n\n**\`vibe-agent-toolkit\` must be in \`dependencies\` (not \`devDependencies\`).** npm adds all \`bin\` entries from runtime dependencies to \`./node_modules/.bin/\` and puts that on PATH when running lifecycle scripts. This is how \`vat\` is available during your postinstall without being globally installed. If it is in \`devDependencies\`, npm will not install it on the user\'s machine and the postinstall will fail with \"command not found\".\n\nThe \`vat.skills\` array contains skill name strings for npm discoverability. Skill source paths and packaging config live in \`vibe-agent-toolkit.config.yaml\` (see Step 2).\n\nFor private GitHub Packages registry:\n\`\`\`json\n\"publishConfig\": {\n \"registry\": \"https://npm.pkg.github.com\",\n \"access\": \"restricted\"\n}\n\`\`\`\n\nUsers (or IT) installing from GitHub Packages need \`.npmrc\` configured with the scope registry and a read-only token:\n\`\`\`\n@myorg:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}\n\`\`\`\n\nIT deploying to managed machines should pre-configure \`.npmrc\` at the system or user level before running install commands. End users do not need to understand npm or the registry — IT handles it once.\n\n## Handling Plugin Renames: vat.replaces\n\n> **Use this only when renaming a plugin, merging plugins, or cleaning up legacy flat-skill installs. Normal upgrades (same plugin name, same skills) do NOT need \`vat.replaces\` — the installer already overwrites the plugin directory on every install.**\n\n### The problem: silent stale skills\n\nWhen you rename a plugin or reorganize skills across packages, the old registration remains in Claude Code. Claude Code **does not warn you** when two plugins provide conflicting skill names — the first-registered (stale) skill silently wins. Users continue loading old content from the renamed plugin unless the old registration is explicitly removed.\n\nThis is the scenario where \`vat.replaces\` is needed:\n- Plugin renamed: \`old-plugin-name\` → \`new-plugin-name\`\n- Two old plugins merged into one new plugin\n- Skills previously installed to \`~/.claude/skills/<name>\` (legacy pre-0.1.20 flat install) now delivered via the plugin tree\n\n### How it works\n\nWhen a VAT package is installed (via postinstall hook or \`--dev\`), the installer reads \`vat.replaces\` from \`package.json\` and — **before** installing the new plugin:\n\n1. For each name in \`replaces.plugins\`: uninstalls \`<name>@<marketplace>\` — removes plugin directory, cache entry, registry entry, and \`settings.json\` entry\n2. For each name in \`replaces.flatSkills\`: deletes \`~/.claude/skills/<name>\` — removes legacy pre-0.1.20 flat installs\n\nBoth operations are idempotent — \"not found\" is handled gracefully.\n\n### Schema\n\n\`\`\`json\n\"vat\": {\n \"version\": \"1.0\",\n \"skills\": [\"authoring\", \"audit\"],\n \"replaces\": {\n \"plugins\": [\"my-old-plugin-name\"],\n \"flatSkills\": [\"my-old-skill\", \"another-old-skill\"]\n }\n}\n\`\`\`\n\nBoth \`plugins\` and \`flatSkills\` are optional arrays. The entire \`replaces\` key is optional — omit it when there is nothing to clean up.\n\n### Real example: vat-development-agents 0.1.21\n\nThis package (\`vat-development-agents\`) renamed its plugin from \`vat-development-agents\` to \`vibe-agent-toolkit\` in v0.1.21. Without \`vat.replaces\`, users who had already installed v0.1.20 would have both \`vat-development-agents@vat-skills\` and \`vibe-agent-toolkit@vat-skills\` registered — Claude Code would serve stale skill content from the old plugin.\n\nThe fix in \`package.json\`:\n\n\`\`\`json\n\"vat\": {\n \"version\": \"1.0\",\n \"skills\": [\"vibe-agent-toolkit\", \"resources\", \"distribution\", \"authoring\", \"audit\", \"debugging\", \"install\"],\n \"replaces\": {\n \"plugins\": [\"vat-development-agents\"],\n \"flatSkills\": [\"vibe-agent-toolkit\", \"resources\"]\n }\n}\n\`\`\`\n\n- \`plugins\`: removes the old plugin registration (same marketplace, old plugin name)\n- \`flatSkills\`: removes legacy \`~/.claude/skills/vibe-agent-toolkit\` and \`~/.claude/skills/resources\` entries from users who installed before 0.1.20 switched to the plugin tree\n\n### Non-obvious gotchas\n\n**1. Plugin names in \`replaces.plugins\` have NO \`@marketplace\`**\n\nThe format is just the plugin name — e.g. \`\"vat-development-agents\"\` — NOT \`\"vat-development-agents@vat-skills\"\`. The installer infers the marketplace from the current package\'s dist tree. Using \`@marketplace\` syntax here would be wrong.\n\n**2. \`replaces.plugins\` is for old plugin registrations, not for skills that moved between plugins**\n\nIf a skill moved from \`plugin-a\` to \`plugin-b\` within the same marketplace, list \`\"plugin-a\"\` in \`replaces.plugins\` to clean up the entire old plugin. You do not manage individual skills — you manage plugins.\n\n**3. \`replaces.flatSkills\` is ONLY for the legacy \`~/.claude/skills/\` location**\n\nThis is specifically for skills that were previously installed as flat files to \`~/.claude/skills/<name>\` (pre-plugin-tree, before v0.1.20). Skills within the plugin tree (under \`~/.claude/plugins/\`) are handled via \`replaces.plugins\`. Do not mix them up.\n\n**4. Normal upgrades need nothing**\n\nIf you publish a new version of the same package with the same plugin name, the installer overwrites the plugin directory automatically. \`vat.replaces\` is only for the case where the old name is different from the new name.\n\n**5. The symptom is subtle and delayed**\n\nYou will not see an error. Claude Code simply loads the first-registered skill with a given name. If the stale plugin is registered first (alphabetically or by install order), your new content is invisible until you remove the old registration.\n\n## Step 2: vibe-agent-toolkit.config.yaml\n\n\`\`\`yaml\nversion: 1\n\nskills:\n include:\n - \"resources/skills/**/SKILL.md\"\n\nclaude:\n marketplaces:\n my-marketplace: # org/publisher identity\n owner:\n name: My Organization\n plugins:\n - name: my-plugin # installable unit\n description: My plugin description\n\`\`\`\n\nThe top-level \`skills:\` section drives standalone skill builds (output: \`dist/skills/\`). The \`claude:\` section defines plugins, which are assembled from their own \`plugins/<name>/\` directories (plugin-local skills under \`plugins/<name>/skills/**/SKILL.md\`). Each marketplace has \`owner\` and \`plugins\` fields (strict schema — no extra fields).\n\n**Naming convention:** marketplace = org identity (e.g. \`acme\`), plugin = this package\n(e.g. \`acme-tools\`). Registers as \`my-plugin@my-marketplace\` in Claude\'s plugin registry.\n\n### Multiple skills in one plugin\n\nList all skills in \`vat.skills\` for npm discoverability:\n\n\`\`\`json\n\"vat\": {\n \"version\": \"1.0\",\n \"skills\": [\"my-linting\", \"my-testing\"]\n}\n\`\`\`\n\nEach skill lives as a subdirectory of the plugin under \`plugins/<name>/skills/<skill>/SKILL.md\`:\n\n\`\`\`\nplugins/my-plugin/\n skills/\n my-linting/SKILL.md\n my-testing/SKILL.md\n\`\`\`\n\nAll plugin-local skills found under \`plugins/<name>/skills/\` are packaged into the plugin automatically — no per-plugin selector is needed or supported. Skill names must be globally unique across all plugins.\n\n## Step 3: Build\n\n\`\`\`bash\nvat build # skills phase then claude phase\nvat verify # validates resources + skills + claude artifacts\n\`\`\`\n\n### What vat build does\n\nTwo phases, run in dependency order:\n\n1. **\`vat skills build\`** — reads \`vibe-agent-toolkit.config.yaml skills:\` section, discovers SKILL.md files via include/exclude globs, compiles each into \`dist/skills/<name>/\`\n2. **\`vat claude plugin build\`** — reads \`vibe-agent-toolkit.config.yaml claude:\` section, wraps built skills into \`dist/.claude/plugins/marketplaces/<mp>/plugins/<plugin>/\` structure with \`.claude-plugin/plugin.json\`. Cleans stale output before each build.\n\nIndividual commands still work:\n\`\`\`bash\nvat skills build # skills phase only\nvat claude plugin build # claude plugin phase only (requires skills already built)\n\`\`\`\n\n## Step 4: Publish\n\n\`\`\`bash\nnpm publish --tag next # RC/pre-release\nnpm publish # stable release\n\`\`\`\n\n## Marketplace Distribution\n\nMarketplace distribution publishes a dedicated branch to GitHub that Claude Code users can install directly — no npm account or registry required.\n\n### How it works\n\n1. \`vat build\` compiles skills and plugin artifacts into \`dist/\`\n2. \`vat claude marketplace publish\` pushes the \`dist/.claude/\` tree to a dedicated branch (e.g. \`claude-marketplace\`) in your GitHub repo\n3. Users install via the slash command: \`/plugin marketplace add owner/repo#claude-marketplace\`\n\n### Configuration\n\nAdd a \`publish\` section under your marketplace in \`vibe-agent-toolkit.config.yaml\`:\n\n\`\`\`yaml\nclaude:\n marketplaces:\n my-marketplace:\n owner:\n name: My Organization\n plugins:\n - name: my-plugin\n description: My plugin description\n publish:\n github:\n repo: owner/repo # GitHub repo to publish to\n branch: claude-marketplace # branch that stores the installable artifacts\n\`\`\`\n\n### Publish workflow\n\n\`\`\`bash\nvat build # build all artifacts first\nvat claude marketplace publish # push dist/.claude/ to the publish branch\nvat claude marketplace publish --dry-run # preview what would be published (no push)\n\`\`\`\n\n### Consumer install\n\nOnce published, users install with:\n\n\`\`\`\n/plugin marketplace add owner/repo#claude-marketplace\n\`\`\`\n\nNo npm, no registry, no token required. Claude Code fetches the branch directly from GitHub.\n\n### Testing locally\n\nAfter publishing, verify the marketplace works end-to-end:\n\n\`\`\`bash\nclaude plugin marketplace add owner/repo#claude-marketplace\nclaude plugin install my-plugin@my-marketplace\nclaude plugin validate ~/.claude/plugins/cache/my-marketplace/my-plugin/<version>\nclaude plugin list # verify status: enabled\n\`\`\`\n\nStart a new Claude Code session to confirm skills load. See the [Marketplace Distribution Guide](https://github.com/jdutton/vibe-agent-toolkit/blob/main/docs/guides/marketplace-distribution.md#testing-your-marketplace) for the full testing checklist and known issues.\n\n**Note (Claude Code v2.1.81):** If re-adding a marketplace with the same name as an existing one (e.g., switching from npm to GitHub source), remove the old marketplace first: \`claude plugin marketplace remove <name>\` then re-add. Otherwise the old source is silently reused.\n\n### Per-plugin versioning (multi-plugin marketplaces)\n\nVAT supports two versioning models for a marketplace:\n\n**Single-version (default for skills-only marketplaces).** No \`version\` is declared on individual plugins. All plugins inherit the root \`package.json:version\`. This is the model used by \`vibe-agent-toolkit\` — the marketplace is treated as one release artifact.\n\n**Per-plugin versioning** (multi-plugin marketplaces with independent release cadences). Each plugin declares its own \`version\`. Recommended when topical plugins under one marketplace evolve on independent timelines.\n\n#### Where to declare a per-plugin version\n\nTwo options, in precedence order:\n\n1. **Marketplace config** (\`vibe-agent-toolkit.config.yaml\`) — most explicit:\n \`\`\`yaml\n claude:\n marketplaces:\n my-marketplace:\n plugins:\n - name: ai-digest\n version: 0.2.0\n skills: \'*\'\n \`\`\`\n2. **Plugin source** (\`plugins/<name>/.claude-plugin/plugin.json:version\`) — most ergonomic for plugin authors:\n \`\`\`json\n { \"name\": \"ai-digest\", \"version\": \"0.2.0\", \"description\": \"...\" }\n \`\`\`\n\nIf both declare a version, marketplace config wins and VAT logs a reconciliation warning. If neither is declared, VAT falls back to the root \`package.json:version\` (single-version model).\n\n#### What \`vat claude marketplace publish\` does for multi-plugin marketplaces\n\nFor each plugin with a resolved version:\n\n- The published \`.claude-plugin/marketplace.json\` includes the per-plugin \`version\` field on each plugin entry.\n- If \`<plugin.source>/CHANGELOG.md\` exists in source (or the marketplace plugin entry\'s \`changelog\` field points to a file), it is bundled into the published marketplace at \`plugins/<name>/CHANGELOG.md\`, alongside the marketplace-level CHANGELOG.\n\nThe marketplace-level CHANGELOG (under \`publish.changelog\` in the config) continues to work unchanged.\n\n#### Default CHANGELOG location\n\nThe default per-plugin CHANGELOG path is \`<plugin.source>/CHANGELOG.md\`, anchored to the entry\'s \`source\` field (default \`plugins/<name>\`). It is NOT assumed to be \`plugins/<name>/CHANGELOG.md\` — if you override \`source\`, the default CHANGELOG path follows.\n\nTo use a non-default path, set the \`changelog\` field on the marketplace plugin entry (relative to the plugin source dir):\n\n\`\`\`yaml\nplugins:\n - name: ai-digest\n source: plugins/ai-digest\n changelog: docs/RELEASES.md\n skills: \'*\'\n\`\`\`\n\n#### Backwards compatibility\n\nMarketplaces with no per-plugin version anywhere are unaffected. The root \`package.json\` version flows through to every plugin, and the published \`marketplace.json\` either omits per-plugin \`version\` or includes the same value for all plugins.\n\n## Step 5: User Install\n\n### Recommended: npm global install (postinstall runs automatically)\n\n\`\`\`bash\nnpm install -g @myorg/my-skills\n\`\`\`\n\nThe postinstall hook fires automatically and registers the plugin in Claude. This is the correct path for IT-managed deployments — no other tools required on the user\'s machine.\n\n### Developer/IT one-off install via npx\n\n\`\`\`bash\nnpx vibe-agent-toolkit claude plugin install npm:@myorg/my-skills\n\`\`\`\n\nDownloads and runs VAT via npx to install a package without a global install. Useful for CI, scripting, or testing from a developer machine. Requires the npm scope registry to be configured (\`.npmrc\`) if installing from a private registry.\n\n### How plugin installation works\n\nWhen \`npm install\` runs the postinstall hook (\`vat claude plugin install --npm-postinstall\`):\n\n- VAT detects \`dist/.claude/plugins/marketplaces/\` directory in the installed package\n- Copies the plugin tree to Claude\'s plugin directory (dumb recursive copy)\n- Writes to these locations:\n 1. \`~/.claude/plugins/marketplaces/<marketplace>/plugins/<plugin>/\` — plugin files\n 2. \`~/.claude/plugins/known_marketplaces.json\` — marketplace registry\n 3. \`~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/\` — version cache\n 4. \`~/.claude/plugins/installed_plugins.json\` — installation record\n 5. \`~/.claude/settings.json\` \`enabledPlugins\` — activates the plugin\n\nIf no \`dist/.claude/plugins/marketplaces/\` directory exists (package wasn\'t built before publish): a guidance message is emitted and the hook exits 0. The publisher must run \`vat build\` and re-publish.\n\nSkills are then available in Claude Code as \`/plugin-name:skill-name\`.\n\n## managed-settings.json Validation (Enterprise)\n\n\`\`\`yaml\nclaude:\n managedSettings: managed-settings.json\n\`\`\`\n\n\`vat verify\` validates this file against the ManagedSettings schema. Catches typos and schema errors before deployment. Does NOT deploy the file — deployment is a separate concern.\n\n## --target claude-web (ZIP Upload)\n\nFor uploading skills directly to \`claude.ai/settings/capabilities\`:\n\n\`\`\`bash\nvat skills package ./SKILL.md -o ./dist/ --target claude-web\n\`\`\`\n\nProduces a ZIP:\n\`\`\`\nmy-skill.zip\n└── my-skill/\n ├── SKILL.md # skill definition (required)\n ├── scripts/ # executable code (.mjs, .py, .sh) — optional\n ├── references/ # markdown reference material — optional\n └── assets/ # static data, templates, config — optional\n\`\`\`\n\nConfigure source path mappings in \`vibe-agent-toolkit.config.yaml\`:\n\`\`\`yaml\nskills:\n include:\n - \"skills/**/SKILL.md\"\n config:\n my-skill:\n claudeWebTarget:\n scripts: [\"./src/helpers/**/*.ts\"]\n assets: [\"./assets/**\"]\n\`\`\`\n\nTypeScript files in \`scripts\` are tree-shaken and compiled to standalone \`.mjs\`.\n\n## Quick Reference\n\n| Task | Command |\n|---|---|\n| Build everything | \`vat build\` |\n| Verify everything | \`vat verify\` |\n| Build skills only | \`vat skills build\` |\n| Build claude plugin artifacts only | \`vat claude plugin build\` |\n| Install via npm (end user) | \`npm install -g @org/pkg\` |\n| Install via npx (developer/IT) | \`npx vibe-agent-toolkit claude plugin install npm:@org/pkg\` |\n| List installed plugins | \`vat claude plugin list\` |\n| Uninstall a plugin | \`vat claude plugin uninstall --all\` |\n| Package for claude.ai upload | \`vat skills package ./SKILL.md -o ./dist/ --target claude-web\` |\n\n## Running VAT Without Global Install\n\n\`\`\`bash\nnpx vibe-agent-toolkit <command> # npm/Node.js\nbunx vibe-agent-toolkit <command> # Bun\n\`\`\`\n\nAll \`vat\` commands in this skill work with these alternatives.\n\n## Future: Zero-Dependency Postinstall (Option B)\n\nA planned improvement: \`vat build\` would bundle the plugin install logic into \`dist/postinstall.js\` — a fully self-contained script with no npm dependencies. The postinstall script would become simply \`node ./dist/postinstall.js\`. This eliminates \`vibe-agent-toolkit\` as a runtime dependency entirely, reducing install footprint for end users. Until then, Option C (runtime \`vibe-agent-toolkit\` dep) is the correct approach.\n\n## Full-plugin authoring (commands, hooks, agents, MCP)\n\nVAT supports bundling any Claude Code plugin asset — not just skills. Drop the plugin\nunder \`plugins/<name>/\` in the same native layout Claude expects. VAT tree-copies\neverything (minus \`skills/\` and \`.claude-plugin/\`), merges author \`plugin.json\` with\nVAT-owned identity fields, and applies any \`files[]\` mappings for artifacts built\noutside the plugin dir.\n\nSee [docs/guides/marketplace-distribution.md](../../../../docs/guides/marketplace-distribution.md) section \"Full-plugin authoring\".\n";
11
11
 
12
12
  export const fragments = {
13
13
  scope: {
@@ -52,8 +52,8 @@ export const fragments = {
52
52
  },
53
53
  marketplaceDistribution: {
54
54
  header: "## Marketplace Distribution",
55
- body: "Marketplace distribution publishes a dedicated branch to GitHub that Claude Code users can install directly — no npm account or registry required.\n\n### How it works\n\n1. \`vat build\` compiles skills and plugin artifacts into \`dist/\`\n2. \`vat claude marketplace publish\` pushes the \`dist/.claude/\` tree to a dedicated branch (e.g. \`claude-marketplace\`) in your GitHub repo\n3. Users install via the slash command: \`/plugin marketplace add owner/repo#claude-marketplace\`\n\n### Configuration\n\nAdd a \`publish\` section under your marketplace in \`vibe-agent-toolkit.config.yaml\`:\n\n\`\`\`yaml\nclaude:\n marketplaces:\n my-marketplace:\n owner:\n name: My Organization\n plugins:\n - name: my-plugin\n description: My plugin description\n publish:\n github:\n repo: owner/repo # GitHub repo to publish to\n branch: claude-marketplace # branch that stores the installable artifacts\n\`\`\`\n\n### Publish workflow\n\n\`\`\`bash\nvat build # build all artifacts first\nvat claude marketplace publish # push dist/.claude/ to the publish branch\nvat claude marketplace publish --dry-run # preview what would be published (no push)\n\`\`\`\n\n### Consumer install\n\nOnce published, users install with:\n\n\`\`\`\n/plugin marketplace add owner/repo#claude-marketplace\n\`\`\`\n\nNo npm, no registry, no token required. Claude Code fetches the branch directly from GitHub.\n\n### Testing locally\n\nAfter publishing, verify the marketplace works end-to-end:\n\n\`\`\`bash\nclaude plugin marketplace add owner/repo#claude-marketplace\nclaude plugin install my-plugin@my-marketplace\nclaude plugin validate ~/.claude/plugins/cache/my-marketplace/my-plugin/<version>\nclaude plugin list # verify status: enabled\n\`\`\`\n\nStart a new Claude Code session to confirm skills load. See the [Marketplace Distribution Guide](https://github.com/jdutton/vibe-agent-toolkit/blob/main/docs/guides/marketplace-distribution.md#testing-your-marketplace) for the full testing checklist and known issues.\n\n**Note (Claude Code v2.1.81):** If re-adding a marketplace with the same name as an existing one (e.g., switching from npm to GitHub source), remove the old marketplace first: \`claude plugin marketplace remove <name>\` then re-add. Otherwise the old source is silently reused.\n\n### Per-plugin versioning (multi-plugin marketplaces)\n\nVAT supports two versioning models for a marketplace:\n\n**Single-version (default for skills-only marketplaces).** No \`version\` is declared on individual plugins. All plugins inherit the root \`package.json:version\`. This is the model used by \`vibe-agent-toolkit\` and \`avonrisk-sdlc\` — the marketplace is treated as one release artifact.\n\n**Per-plugin versioning** (multi-plugin marketplaces with independent release cadences). Each plugin declares its own \`version\`. Recommended when topical plugins under one marketplace evolve on independent timelines (e.g., AvonRiskBuilders\' \`ai-digest\`, \`bank-reconciliation\`).\n\n#### Where to declare a per-plugin version\n\nTwo options, in precedence order:\n\n1. **Marketplace config** (\`vibe-agent-toolkit.config.yaml\`) — most explicit:\n \`\`\`yaml\n claude:\n marketplaces:\n my-marketplace:\n plugins:\n - name: ai-digest\n version: 0.2.0\n skills: \'*\'\n \`\`\`\n2. **Plugin source** (\`plugins/<name>/.claude-plugin/plugin.json:version\`) — most ergonomic for plugin authors:\n \`\`\`json\n { \"name\": \"ai-digest\", \"version\": \"0.2.0\", \"description\": \"...\" }\n \`\`\`\n\nIf both declare a version, marketplace config wins and VAT logs a reconciliation warning. If neither is declared, VAT falls back to the root \`package.json:version\` (single-version model).\n\n#### What \`vat claude marketplace publish\` does for multi-plugin marketplaces\n\nFor each plugin with a resolved version:\n\n- The published \`.claude-plugin/marketplace.json\` includes the per-plugin \`version\` field on each plugin entry.\n- After the publish-branch push succeeds, VAT pushes a \`<plugin>-v<version>\` tag to the **source repo** (not the marketplace branch) — e.g., \`ai-digest-v0.2.0\`. \"Source repo\" means the working directory where you invoke \`vat claude marketplace publish\` (i.e., \`process.cwd()\`); if you invoke from a subdirectory of a worktree, tags land in the containing repo, not the subdirectory. Tag-push failures log a warning but do not roll back the publish.\n- If \`<plugin.source>/CHANGELOG.md\` exists in source (or the marketplace plugin entry\'s \`changelog\` field points to a file), it is bundled into the published marketplace at \`plugins/<name>/CHANGELOG.md\`, alongside the marketplace-level CHANGELOG.\n\nThe marketplace-level CHANGELOG (under \`publish.changelog\` in the config) continues to work unchanged.\n\n#### Default CHANGELOG location\n\nThe default per-plugin CHANGELOG path is \`<plugin.source>/CHANGELOG.md\`, anchored to the entry\'s \`source\` field (default \`plugins/<name>\`). It is NOT assumed to be \`plugins/<name>/CHANGELOG.md\` — if you override \`source\`, the default CHANGELOG path follows.\n\nTo use a non-default path, set the \`changelog\` field on the marketplace plugin entry (relative to the plugin source dir):\n\n\`\`\`yaml\nplugins:\n - name: ai-digest\n source: plugins/ai-digest\n changelog: docs/RELEASES.md\n skills: \'*\'\n\`\`\`\n\n#### Backwards compatibility\n\nMarketplaces with no per-plugin version anywhere are unaffected. The root \`package.json\` version flows through to every plugin, the published \`marketplace.json\` either omits per-plugin \`version\` or includes the same value for all plugins, and tag pushes use the inherited version (e.g., both plugins tagged \`<plugin>-v1.0.0\`).",
56
- text: "## Marketplace Distribution\n\nMarketplace distribution publishes a dedicated branch to GitHub that Claude Code users can install directly — no npm account or registry required.\n\n### How it works\n\n1. \`vat build\` compiles skills and plugin artifacts into \`dist/\`\n2. \`vat claude marketplace publish\` pushes the \`dist/.claude/\` tree to a dedicated branch (e.g. \`claude-marketplace\`) in your GitHub repo\n3. Users install via the slash command: \`/plugin marketplace add owner/repo#claude-marketplace\`\n\n### Configuration\n\nAdd a \`publish\` section under your marketplace in \`vibe-agent-toolkit.config.yaml\`:\n\n\`\`\`yaml\nclaude:\n marketplaces:\n my-marketplace:\n owner:\n name: My Organization\n plugins:\n - name: my-plugin\n description: My plugin description\n publish:\n github:\n repo: owner/repo # GitHub repo to publish to\n branch: claude-marketplace # branch that stores the installable artifacts\n\`\`\`\n\n### Publish workflow\n\n\`\`\`bash\nvat build # build all artifacts first\nvat claude marketplace publish # push dist/.claude/ to the publish branch\nvat claude marketplace publish --dry-run # preview what would be published (no push)\n\`\`\`\n\n### Consumer install\n\nOnce published, users install with:\n\n\`\`\`\n/plugin marketplace add owner/repo#claude-marketplace\n\`\`\`\n\nNo npm, no registry, no token required. Claude Code fetches the branch directly from GitHub.\n\n### Testing locally\n\nAfter publishing, verify the marketplace works end-to-end:\n\n\`\`\`bash\nclaude plugin marketplace add owner/repo#claude-marketplace\nclaude plugin install my-plugin@my-marketplace\nclaude plugin validate ~/.claude/plugins/cache/my-marketplace/my-plugin/<version>\nclaude plugin list # verify status: enabled\n\`\`\`\n\nStart a new Claude Code session to confirm skills load. See the [Marketplace Distribution Guide](https://github.com/jdutton/vibe-agent-toolkit/blob/main/docs/guides/marketplace-distribution.md#testing-your-marketplace) for the full testing checklist and known issues.\n\n**Note (Claude Code v2.1.81):** If re-adding a marketplace with the same name as an existing one (e.g., switching from npm to GitHub source), remove the old marketplace first: \`claude plugin marketplace remove <name>\` then re-add. Otherwise the old source is silently reused.\n\n### Per-plugin versioning (multi-plugin marketplaces)\n\nVAT supports two versioning models for a marketplace:\n\n**Single-version (default for skills-only marketplaces).** No \`version\` is declared on individual plugins. All plugins inherit the root \`package.json:version\`. This is the model used by \`vibe-agent-toolkit\` and \`avonrisk-sdlc\` — the marketplace is treated as one release artifact.\n\n**Per-plugin versioning** (multi-plugin marketplaces with independent release cadences). Each plugin declares its own \`version\`. Recommended when topical plugins under one marketplace evolve on independent timelines (e.g., AvonRiskBuilders\' \`ai-digest\`, \`bank-reconciliation\`).\n\n#### Where to declare a per-plugin version\n\nTwo options, in precedence order:\n\n1. **Marketplace config** (\`vibe-agent-toolkit.config.yaml\`) — most explicit:\n \`\`\`yaml\n claude:\n marketplaces:\n my-marketplace:\n plugins:\n - name: ai-digest\n version: 0.2.0\n skills: \'*\'\n \`\`\`\n2. **Plugin source** (\`plugins/<name>/.claude-plugin/plugin.json:version\`) — most ergonomic for plugin authors:\n \`\`\`json\n { \"name\": \"ai-digest\", \"version\": \"0.2.0\", \"description\": \"...\" }\n \`\`\`\n\nIf both declare a version, marketplace config wins and VAT logs a reconciliation warning. If neither is declared, VAT falls back to the root \`package.json:version\` (single-version model).\n\n#### What \`vat claude marketplace publish\` does for multi-plugin marketplaces\n\nFor each plugin with a resolved version:\n\n- The published \`.claude-plugin/marketplace.json\` includes the per-plugin \`version\` field on each plugin entry.\n- After the publish-branch push succeeds, VAT pushes a \`<plugin>-v<version>\` tag to the **source repo** (not the marketplace branch) — e.g., \`ai-digest-v0.2.0\`. \"Source repo\" means the working directory where you invoke \`vat claude marketplace publish\` (i.e., \`process.cwd()\`); if you invoke from a subdirectory of a worktree, tags land in the containing repo, not the subdirectory. Tag-push failures log a warning but do not roll back the publish.\n- If \`<plugin.source>/CHANGELOG.md\` exists in source (or the marketplace plugin entry\'s \`changelog\` field points to a file), it is bundled into the published marketplace at \`plugins/<name>/CHANGELOG.md\`, alongside the marketplace-level CHANGELOG.\n\nThe marketplace-level CHANGELOG (under \`publish.changelog\` in the config) continues to work unchanged.\n\n#### Default CHANGELOG location\n\nThe default per-plugin CHANGELOG path is \`<plugin.source>/CHANGELOG.md\`, anchored to the entry\'s \`source\` field (default \`plugins/<name>\`). It is NOT assumed to be \`plugins/<name>/CHANGELOG.md\` — if you override \`source\`, the default CHANGELOG path follows.\n\nTo use a non-default path, set the \`changelog\` field on the marketplace plugin entry (relative to the plugin source dir):\n\n\`\`\`yaml\nplugins:\n - name: ai-digest\n source: plugins/ai-digest\n changelog: docs/RELEASES.md\n skills: \'*\'\n\`\`\`\n\n#### Backwards compatibility\n\nMarketplaces with no per-plugin version anywhere are unaffected. The root \`package.json\` version flows through to every plugin, the published \`marketplace.json\` either omits per-plugin \`version\` or includes the same value for all plugins, and tag pushes use the inherited version (e.g., both plugins tagged \`<plugin>-v1.0.0\`)."
55
+ body: "Marketplace distribution publishes a dedicated branch to GitHub that Claude Code users can install directly — no npm account or registry required.\n\n### How it works\n\n1. \`vat build\` compiles skills and plugin artifacts into \`dist/\`\n2. \`vat claude marketplace publish\` pushes the \`dist/.claude/\` tree to a dedicated branch (e.g. \`claude-marketplace\`) in your GitHub repo\n3. Users install via the slash command: \`/plugin marketplace add owner/repo#claude-marketplace\`\n\n### Configuration\n\nAdd a \`publish\` section under your marketplace in \`vibe-agent-toolkit.config.yaml\`:\n\n\`\`\`yaml\nclaude:\n marketplaces:\n my-marketplace:\n owner:\n name: My Organization\n plugins:\n - name: my-plugin\n description: My plugin description\n publish:\n github:\n repo: owner/repo # GitHub repo to publish to\n branch: claude-marketplace # branch that stores the installable artifacts\n\`\`\`\n\n### Publish workflow\n\n\`\`\`bash\nvat build # build all artifacts first\nvat claude marketplace publish # push dist/.claude/ to the publish branch\nvat claude marketplace publish --dry-run # preview what would be published (no push)\n\`\`\`\n\n### Consumer install\n\nOnce published, users install with:\n\n\`\`\`\n/plugin marketplace add owner/repo#claude-marketplace\n\`\`\`\n\nNo npm, no registry, no token required. Claude Code fetches the branch directly from GitHub.\n\n### Testing locally\n\nAfter publishing, verify the marketplace works end-to-end:\n\n\`\`\`bash\nclaude plugin marketplace add owner/repo#claude-marketplace\nclaude plugin install my-plugin@my-marketplace\nclaude plugin validate ~/.claude/plugins/cache/my-marketplace/my-plugin/<version>\nclaude plugin list # verify status: enabled\n\`\`\`\n\nStart a new Claude Code session to confirm skills load. See the [Marketplace Distribution Guide](https://github.com/jdutton/vibe-agent-toolkit/blob/main/docs/guides/marketplace-distribution.md#testing-your-marketplace) for the full testing checklist and known issues.\n\n**Note (Claude Code v2.1.81):** If re-adding a marketplace with the same name as an existing one (e.g., switching from npm to GitHub source), remove the old marketplace first: \`claude plugin marketplace remove <name>\` then re-add. Otherwise the old source is silently reused.\n\n### Per-plugin versioning (multi-plugin marketplaces)\n\nVAT supports two versioning models for a marketplace:\n\n**Single-version (default for skills-only marketplaces).** No \`version\` is declared on individual plugins. All plugins inherit the root \`package.json:version\`. This is the model used by \`vibe-agent-toolkit\` — the marketplace is treated as one release artifact.\n\n**Per-plugin versioning** (multi-plugin marketplaces with independent release cadences). Each plugin declares its own \`version\`. Recommended when topical plugins under one marketplace evolve on independent timelines.\n\n#### Where to declare a per-plugin version\n\nTwo options, in precedence order:\n\n1. **Marketplace config** (\`vibe-agent-toolkit.config.yaml\`) — most explicit:\n \`\`\`yaml\n claude:\n marketplaces:\n my-marketplace:\n plugins:\n - name: ai-digest\n version: 0.2.0\n skills: \'*\'\n \`\`\`\n2. **Plugin source** (\`plugins/<name>/.claude-plugin/plugin.json:version\`) — most ergonomic for plugin authors:\n \`\`\`json\n { \"name\": \"ai-digest\", \"version\": \"0.2.0\", \"description\": \"...\" }\n \`\`\`\n\nIf both declare a version, marketplace config wins and VAT logs a reconciliation warning. If neither is declared, VAT falls back to the root \`package.json:version\` (single-version model).\n\n#### What \`vat claude marketplace publish\` does for multi-plugin marketplaces\n\nFor each plugin with a resolved version:\n\n- The published \`.claude-plugin/marketplace.json\` includes the per-plugin \`version\` field on each plugin entry.\n- If \`<plugin.source>/CHANGELOG.md\` exists in source (or the marketplace plugin entry\'s \`changelog\` field points to a file), it is bundled into the published marketplace at \`plugins/<name>/CHANGELOG.md\`, alongside the marketplace-level CHANGELOG.\n\nThe marketplace-level CHANGELOG (under \`publish.changelog\` in the config) continues to work unchanged.\n\n#### Default CHANGELOG location\n\nThe default per-plugin CHANGELOG path is \`<plugin.source>/CHANGELOG.md\`, anchored to the entry\'s \`source\` field (default \`plugins/<name>\`). It is NOT assumed to be \`plugins/<name>/CHANGELOG.md\` — if you override \`source\`, the default CHANGELOG path follows.\n\nTo use a non-default path, set the \`changelog\` field on the marketplace plugin entry (relative to the plugin source dir):\n\n\`\`\`yaml\nplugins:\n - name: ai-digest\n source: plugins/ai-digest\n changelog: docs/RELEASES.md\n skills: \'*\'\n\`\`\`\n\n#### Backwards compatibility\n\nMarketplaces with no per-plugin version anywhere are unaffected. The root \`package.json\` version flows through to every plugin, and the published \`marketplace.json\` either omits per-plugin \`version\` or includes the same value for all plugins.",
56
+ text: "## Marketplace Distribution\n\nMarketplace distribution publishes a dedicated branch to GitHub that Claude Code users can install directly — no npm account or registry required.\n\n### How it works\n\n1. \`vat build\` compiles skills and plugin artifacts into \`dist/\`\n2. \`vat claude marketplace publish\` pushes the \`dist/.claude/\` tree to a dedicated branch (e.g. \`claude-marketplace\`) in your GitHub repo\n3. Users install via the slash command: \`/plugin marketplace add owner/repo#claude-marketplace\`\n\n### Configuration\n\nAdd a \`publish\` section under your marketplace in \`vibe-agent-toolkit.config.yaml\`:\n\n\`\`\`yaml\nclaude:\n marketplaces:\n my-marketplace:\n owner:\n name: My Organization\n plugins:\n - name: my-plugin\n description: My plugin description\n publish:\n github:\n repo: owner/repo # GitHub repo to publish to\n branch: claude-marketplace # branch that stores the installable artifacts\n\`\`\`\n\n### Publish workflow\n\n\`\`\`bash\nvat build # build all artifacts first\nvat claude marketplace publish # push dist/.claude/ to the publish branch\nvat claude marketplace publish --dry-run # preview what would be published (no push)\n\`\`\`\n\n### Consumer install\n\nOnce published, users install with:\n\n\`\`\`\n/plugin marketplace add owner/repo#claude-marketplace\n\`\`\`\n\nNo npm, no registry, no token required. Claude Code fetches the branch directly from GitHub.\n\n### Testing locally\n\nAfter publishing, verify the marketplace works end-to-end:\n\n\`\`\`bash\nclaude plugin marketplace add owner/repo#claude-marketplace\nclaude plugin install my-plugin@my-marketplace\nclaude plugin validate ~/.claude/plugins/cache/my-marketplace/my-plugin/<version>\nclaude plugin list # verify status: enabled\n\`\`\`\n\nStart a new Claude Code session to confirm skills load. See the [Marketplace Distribution Guide](https://github.com/jdutton/vibe-agent-toolkit/blob/main/docs/guides/marketplace-distribution.md#testing-your-marketplace) for the full testing checklist and known issues.\n\n**Note (Claude Code v2.1.81):** If re-adding a marketplace with the same name as an existing one (e.g., switching from npm to GitHub source), remove the old marketplace first: \`claude plugin marketplace remove <name>\` then re-add. Otherwise the old source is silently reused.\n\n### Per-plugin versioning (multi-plugin marketplaces)\n\nVAT supports two versioning models for a marketplace:\n\n**Single-version (default for skills-only marketplaces).** No \`version\` is declared on individual plugins. All plugins inherit the root \`package.json:version\`. This is the model used by \`vibe-agent-toolkit\` — the marketplace is treated as one release artifact.\n\n**Per-plugin versioning** (multi-plugin marketplaces with independent release cadences). Each plugin declares its own \`version\`. Recommended when topical plugins under one marketplace evolve on independent timelines.\n\n#### Where to declare a per-plugin version\n\nTwo options, in precedence order:\n\n1. **Marketplace config** (\`vibe-agent-toolkit.config.yaml\`) — most explicit:\n \`\`\`yaml\n claude:\n marketplaces:\n my-marketplace:\n plugins:\n - name: ai-digest\n version: 0.2.0\n skills: \'*\'\n \`\`\`\n2. **Plugin source** (\`plugins/<name>/.claude-plugin/plugin.json:version\`) — most ergonomic for plugin authors:\n \`\`\`json\n { \"name\": \"ai-digest\", \"version\": \"0.2.0\", \"description\": \"...\" }\n \`\`\`\n\nIf both declare a version, marketplace config wins and VAT logs a reconciliation warning. If neither is declared, VAT falls back to the root \`package.json:version\` (single-version model).\n\n#### What \`vat claude marketplace publish\` does for multi-plugin marketplaces\n\nFor each plugin with a resolved version:\n\n- The published \`.claude-plugin/marketplace.json\` includes the per-plugin \`version\` field on each plugin entry.\n- If \`<plugin.source>/CHANGELOG.md\` exists in source (or the marketplace plugin entry\'s \`changelog\` field points to a file), it is bundled into the published marketplace at \`plugins/<name>/CHANGELOG.md\`, alongside the marketplace-level CHANGELOG.\n\nThe marketplace-level CHANGELOG (under \`publish.changelog\` in the config) continues to work unchanged.\n\n#### Default CHANGELOG location\n\nThe default per-plugin CHANGELOG path is \`<plugin.source>/CHANGELOG.md\`, anchored to the entry\'s \`source\` field (default \`plugins/<name>\`). It is NOT assumed to be \`plugins/<name>/CHANGELOG.md\` — if you override \`source\`, the default CHANGELOG path follows.\n\nTo use a non-default path, set the \`changelog\` field on the marketplace plugin entry (relative to the plugin source dir):\n\n\`\`\`yaml\nplugins:\n - name: ai-digest\n source: plugins/ai-digest\n changelog: docs/RELEASES.md\n skills: \'*\'\n\`\`\`\n\n#### Backwards compatibility\n\nMarketplaces with no per-plugin version anywhere are unaffected. The root \`package.json\` version flows through to every plugin, and the published \`marketplace.json\` either omits per-plugin \`version\` or includes the same value for all plugins."
57
57
  },
58
58
  step5UserInstall: {
59
59
  header: "## Step 5: User Install",
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  name: vat-adoption-and-configuration
3
- description: Use when starting a new VAT project, adding VAT to an existing repo, or orienting to `vibe-agent-toolkit.config.yaml`. Covers project setup, repo structure, package.json wiring, vibe-validate integration, and the npm postinstall hook.
3
+ description: Use when starting a new VAT project, adding VAT to an existing
4
+ repo, or orienting to `vibe-agent-toolkit.config.yaml`. Covers project setup,
5
+ repo structure, package.json wiring, vibe-validate integration, and the npm
6
+ postinstall hook.
4
7
  ---
5
8
 
6
9
  # VAT Adoption and Configuration
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  name: vat-agent-authoring
3
- description: Use when authoring TypeScript portable agents — agent archetypes, agent.yaml, result envelopes, orchestration patterns, and runtime adapters (Vercel/LangChain/OpenAI/Claude Agent SDK). Paired with vat-skill-authoring for the SKILL.md side.
3
+ description: Use when authoring TypeScript portable agents — agent archetypes,
4
+ agent.yaml, result envelopes, orchestration patterns, and runtime adapters
5
+ (Vercel/LangChain/OpenAI/Claude Agent SDK). Paired with vat-skill-authoring
6
+ for the SKILL.md side.
4
7
  ---
5
8
 
6
9
  # VAT Agent Authoring: Archetypes, Envelopes, Orchestration
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  name: vat-audit
3
- description: Use when running vat audit to validate Claude plugins, agent skills, or marketplaces. Covers the audit command, --compat flag for surface compatibility analysis, --exclude for noise filtering, and interpreting audit output.
3
+ description: Use when running vat audit to validate Claude plugins, agent
4
+ skills, or marketplaces. Covers the audit command, --compat flag for surface
5
+ compatibility analysis, --exclude for noise filtering, and interpreting audit
6
+ output.
4
7
  ---
5
8
 
6
9
  # VAT Audit: Validating Plugins, Skills & Marketplaces
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: vat-enterprise-org
3
- description: Use for Anthropic Enterprise/Team org administration via the Admin API — user management, API-key auditing, cost/usage reporting, workspace admin, and enterprise skill distribution. Requires ANTHROPIC_ADMIN_API_KEY.
3
+ description: Use for Anthropic Enterprise/Team org administration via the Admin
4
+ API — user management, API-key auditing, cost/usage reporting, workspace
5
+ admin, and enterprise skill distribution. Requires ANTHROPIC_ADMIN_API_KEY.
4
6
  ---
5
7
 
6
8
  # Claude Org Administration