@cyanheads/mcp-ts-core 0.12.9 → 0.13.0

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 (93) hide show
  1. package/AGENTS.md +11 -10
  2. package/CLAUDE.md +11 -10
  3. package/README.md +1 -1
  4. package/biome.json +1 -1
  5. package/changelog/0.13.x/0.13.0.md +48 -0
  6. package/changelog/template.md +7 -24
  7. package/dist/cli/init.js +2 -2
  8. package/dist/cli/init.js.map +1 -1
  9. package/dist/config/envValue.d.ts +18 -0
  10. package/dist/config/envValue.d.ts.map +1 -0
  11. package/dist/config/envValue.js +35 -0
  12. package/dist/config/envValue.js.map +1 -0
  13. package/dist/config/index.d.ts.map +1 -1
  14. package/dist/config/index.js +5 -7
  15. package/dist/config/index.js.map +1 -1
  16. package/dist/config/parseEnvConfig.d.ts +7 -0
  17. package/dist/config/parseEnvConfig.d.ts.map +1 -1
  18. package/dist/config/parseEnvConfig.js +9 -1
  19. package/dist/config/parseEnvConfig.js.map +1 -1
  20. package/dist/linter/validate.js +2 -2
  21. package/dist/linter/validate.js.map +1 -1
  22. package/framework-skills/README.md +40 -0
  23. package/{skills → framework-skills}/add-app-tool/SKILL.md +2 -2
  24. package/{skills → framework-skills}/add-resource/SKILL.md +2 -2
  25. package/{skills → framework-skills}/add-service/SKILL.md +2 -2
  26. package/{skills → framework-skills}/add-test/SKILL.md +2 -2
  27. package/{skills → framework-skills}/add-tool/SKILL.md +4 -4
  28. package/{skills → framework-skills}/api-config/SKILL.md +3 -1
  29. package/{skills → framework-skills}/api-context/SKILL.md +3 -3
  30. package/{skills → framework-skills}/api-linter/SKILL.md +4 -4
  31. package/{skills → framework-skills}/maintenance/SKILL.md +30 -21
  32. package/{skills → framework-skills}/orchestrations/SKILL.md +2 -2
  33. package/{skills → framework-skills}/orchestrations/workflows/field-test-fix.md +8 -8
  34. package/{skills → framework-skills}/orchestrations/workflows/fix-wrapup-release.md +5 -5
  35. package/{skills → framework-skills}/orchestrations/workflows/greenfield-build.md +11 -11
  36. package/{skills → framework-skills}/orchestrations/workflows/maintenance-release.md +12 -12
  37. package/{skills → framework-skills}/polish-docs-meta/SKILL.md +18 -10
  38. package/{skills → framework-skills}/polish-docs-meta/references/agent-protocol.md +1 -1
  39. package/{skills → framework-skills}/polish-docs-meta/references/readme.md +86 -70
  40. package/{skills → framework-skills}/release-pr-review/SKILL.md +2 -2
  41. package/{skills → framework-skills}/report-issue-framework/SKILL.md +25 -25
  42. package/{skills → framework-skills}/report-issue-local/SKILL.md +22 -24
  43. package/{skills → framework-skills}/setup/SKILL.md +10 -8
  44. package/package.json +8 -8
  45. package/scripts/check-framework-antipatterns.ts +1 -1
  46. package/scripts/check-skill-versions.ts +16 -9
  47. package/scripts/check-skills-sync.ts +64 -13
  48. package/scripts/clean-mcpb.ts +3 -3
  49. package/scripts/devcheck.ts +16 -13
  50. package/scripts/lint-packaging.ts +158 -24
  51. package/scripts/list-skills.ts +2 -2
  52. package/templates/.claude-plugin/plugin.json +5 -1
  53. package/templates/.env.example +1 -1
  54. package/templates/.github/CONTRIBUTING.md +4 -5
  55. package/templates/.github/ISSUE_TEMPLATE/bug_report.yml +5 -4
  56. package/templates/.github/ISSUE_TEMPLATE/config.yml +6 -1
  57. package/templates/.github/ISSUE_TEMPLATE/feature_request.yml +1 -2
  58. package/templates/AGENTS.md +15 -14
  59. package/templates/CLAUDE.md +15 -14
  60. package/templates/_.mcpbignore +1 -1
  61. package/templates/changelog/template.md +7 -24
  62. package/templates/package.json +3 -2
  63. package/templates/src/mcp-server/resources/definitions/echo-app-ui.app-resource.ts +1 -1
  64. package/skills/README.md +0 -38
  65. /package/{skills → framework-skills}/add-export/SKILL.md +0 -0
  66. /package/{skills → framework-skills}/add-prompt/SKILL.md +0 -0
  67. /package/{skills → framework-skills}/add-provider/SKILL.md +0 -0
  68. /package/{skills → framework-skills}/api-auth/SKILL.md +0 -0
  69. /package/{skills → framework-skills}/api-canvas/SKILL.md +0 -0
  70. /package/{skills → framework-skills}/api-errors/SKILL.md +0 -0
  71. /package/{skills → framework-skills}/api-mirror/SKILL.md +0 -0
  72. /package/{skills → framework-skills}/api-services/SKILL.md +0 -0
  73. /package/{skills → framework-skills}/api-services/references/graph.md +0 -0
  74. /package/{skills → framework-skills}/api-services/references/llm.md +0 -0
  75. /package/{skills → framework-skills}/api-services/references/speech.md +0 -0
  76. /package/{skills → framework-skills}/api-telemetry/SKILL.md +0 -0
  77. /package/{skills → framework-skills}/api-testing/SKILL.md +0 -0
  78. /package/{skills → framework-skills}/api-utils/SKILL.md +0 -0
  79. /package/{skills → framework-skills}/api-utils/references/formatting.md +0 -0
  80. /package/{skills → framework-skills}/api-utils/references/parsing.md +0 -0
  81. /package/{skills → framework-skills}/api-utils/references/security.md +0 -0
  82. /package/{skills → framework-skills}/api-workers/SKILL.md +0 -0
  83. /package/{skills → framework-skills}/code-simplifier/SKILL.md +0 -0
  84. /package/{skills → framework-skills}/design-mcp-server/SKILL.md +0 -0
  85. /package/{skills → framework-skills}/field-test/SKILL.md +0 -0
  86. /package/{skills → framework-skills}/git-wrapup/SKILL.md +0 -0
  87. /package/{skills → framework-skills}/polish-docs-meta/references/package-meta.md +0 -0
  88. /package/{skills → framework-skills}/polish-docs-meta/references/server-json.md +0 -0
  89. /package/{skills → framework-skills}/release-and-publish/SKILL.md +0 -0
  90. /package/{skills → framework-skills}/security-pass/SKILL.md +0 -0
  91. /package/{skills → framework-skills}/techniques/SKILL.md +0 -0
  92. /package/{skills → framework-skills}/techniques/references/outline-on-overflow.md +0 -0
  93. /package/{skills → framework-skills}/tool-defs-analysis/SKILL.md +0 -0
@@ -4,14 +4,14 @@ description: >
4
4
  Post-init orientation for an MCP server built on @cyanheads/mcp-ts-core. Use after running `@cyanheads/mcp-ts-core init` to understand the project structure, conventions, and skill sync model. Also use when onboarding to an existing project for the first time.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.10"
7
+ version: "1.11"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
11
11
 
12
12
  ## Context
13
13
 
14
- This skill assumes `bunx @cyanheads/mcp-ts-core init [name]` has already run. The CLI created the project's `CLAUDE.md` and `AGENTS.md` for different agents, copied external skills to `skills/`, and scaffolded the directory structure with echo definitions as starting points. This skill covers what was created and what to do next.
14
+ This skill assumes `bunx @cyanheads/mcp-ts-core init [name]` has already run. The CLI created the project's `CLAUDE.md` and `AGENTS.md` for different agents, copied external skills to `framework-skills/`, and scaffolded the directory structure with echo definitions as starting points. This skill covers what was created and what to do next.
15
15
 
16
16
  ## Agent Protocol File
17
17
 
@@ -41,7 +41,7 @@ Dockerfile # Starter multi-stage image
41
41
  server.json # MCP Registry publishing metadata
42
42
  changelog/template.md # Format reference for per-version changelog files
43
43
  scripts/ # build, clean, devcheck, lint-mcp, list-skills, build-changelog, tree, check-docs-sync
44
- skills/ # External skills copied from the package (source of truth)
44
+ framework-skills/ # External skills copied from the package (source of truth)
45
45
  src/
46
46
  index.ts # createApp() entry point
47
47
  mcp-server/
@@ -108,14 +108,14 @@ See the `add-tool`, `add-app-tool`, `add-resource`, `add-prompt`, `add-service`,
108
108
 
109
109
  ## Skill Sync
110
110
 
111
- Copy all project skills into your agent's skill directory so they're available as context. `skills/` is the source of truth.
111
+ Copy all project skills into your agent's skill directory so they're available as context. `framework-skills/` is the source of truth. It is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, and these are development skills, not skills for the agents that install the server — leave `skills/` for those.
112
112
 
113
- **Don't edit `skills/*/SKILL.md` or `skills/*/references/*`.** These are external skill files synced from `@cyanheads/mcp-ts-core` — the `maintenance` skill overwrites them on package updates, so local edits get lost. Project-specific agent context belongs in `CLAUDE.md` / `AGENTS.md`.
113
+ **Don't edit `framework-skills/*/SKILL.md` or `framework-skills/*/references/*`.** These are external skill files synced from `@cyanheads/mcp-ts-core` — the `maintenance` skill overwrites them on package updates, so local edits get lost. Project-specific agent context belongs in `CLAUDE.md` / `AGENTS.md`.
114
114
 
115
115
  **For Claude Code:**
116
116
 
117
117
  ```bash
118
- mkdir -p .claude/skills && cp -R skills/* .claude/skills/
118
+ mkdir -p .claude/skills && cp -R framework-skills/* .claude/skills/
119
119
  ```
120
120
 
121
121
  **For other agents** (Codex, Cursor, Windsurf, etc.) — copy to the equivalent directory (e.g., `.codex/skills/`, `.cursor/skills/`).
@@ -138,7 +138,9 @@ Complete these one-time setup tasks:
138
138
  | `.claude-plugin/plugin.json` | `description` | `lint:packaging` |
139
139
  | `.codex-plugin/plugin.json` | `description`, `interface.shortDescription`, `interface.longDescription` | `lint:packaging` |
140
140
 
141
- Fill the rest of the same blocks while you are in them — `package.json` `description` and `repository.url`, both plugin manifests' `author` / `homepage` / `repository`, the Codex manifest's `interface.developerName` / `category` / `websiteURL`, and `manifest.json` `description` / `author.name`. Nothing gates them, and every install surface reads them.
141
+ Fill the rest of the same blocks while you are in them — `package.json` `description` and `repository.url`, both plugin manifests' `author` / `homepage` / `repository` / `keywords`, the Codex manifest's `interface.developerName` / `category` / `websiteURL`, and `manifest.json` `description` / `author.name`. Nothing gates them, and every install surface reads them.
142
+
143
+ When the server takes a user-supplied value (an API key, a contact email, an instance URL), wire it into the plugin manifests the way each client delivers it — never as `"KEY": ""` in `env`, which `lint:packaging` rejects because the empty value replaces the user's exported key and is read as unset. In `.claude-plugin/plugin.json`, declare the option under `userConfig` (`type`, `title`, `description`; `sensitive: true` for keys and tokens; `required: true` or `default: ""`) and set `"KEY": "${user_config.<option>}"` in `env`. In `.codex-plugin/mcp.json`, list the variable name in `env_vars`. Mirror the `user_config` block you write in `manifest.json`.
142
144
 
143
145
  A server that will never be published or installed as a plugin can drop the plugin-manifest gate instead — set `"packaging": { "pluginManifests": false }` in `devcheck.config.json`.
144
146
  6. **Verify the scaffold builds clean** — `bun run devcheck`. Fix any issues before starting real work.
@@ -172,7 +174,7 @@ Skip or reorder as the project calls for it. The agent protocol's "What's Next?"
172
174
  - [ ] Publishing identity populated (`server.json`, `package.json`, plugin manifests, `manifest.json`) — or the plugin-manifest gate opted out
173
175
  - [ ] Framework docs read (`node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` or `AGENTS.md`)
174
176
  - [ ] Unused echo definitions cleaned up (and unregistered from `src/index.ts`)
175
- - [ ] Skills copied to agent directory (`cp -R skills/* .claude/skills/` or equivalent)
177
+ - [ ] Skills copied to agent directory (`cp -R framework-skills/* .claude/skills/` or equivalent)
176
178
  - [ ] Project structure understood (definitions directories, entry point)
177
179
  - [ ] `bun run devcheck` passes
178
180
  - [ ] Next: if new server, move on to `design-mcp-server` to plan the tool surface
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/mcp-ts-core",
3
- "version": "0.12.9",
3
+ "version": "0.13.0",
4
4
  "mcpName": "io.github.cyanheads/mcp-ts-core",
5
5
  "description": "Agent-native TypeScript framework for MCP servers. Includes runtime infrastructure and agent skills for building, testing, and shipping servers.",
6
6
  "files": [
@@ -21,7 +21,7 @@
21
21
  "scripts/list-skills.ts",
22
22
  "scripts/release-github.ts",
23
23
  "scripts/tree.ts",
24
- "skills/",
24
+ "framework-skills/",
25
25
  "templates/",
26
26
  "AGENTS.md",
27
27
  "CLAUDE.md",
@@ -189,7 +189,7 @@
189
189
  "test:ui": "bunx vitest --ui",
190
190
  "test:coverage": "bunx vitest run --coverage",
191
191
  "audit": "bun audit",
192
- "audit:fix": "bun audit --fix",
192
+ "audit:fix": "bun audit fix",
193
193
  "audit:refresh": "rm -f bun.lock && bun install && bun audit",
194
194
  "changelog:build": "bun run scripts/build-changelog.ts",
195
195
  "changelog:check": "bun run scripts/build-changelog.ts --check",
@@ -197,9 +197,9 @@
197
197
  "publish-mcp": "mcp-publisher login github -token \"$(security find-generic-password -a \"$USER\" -s mcp-publisher-github-pat -w)\" && mcp-publisher publish"
198
198
  },
199
199
  "devDependencies": {
200
- "@biomejs/biome": "2.5.12",
200
+ "@biomejs/biome": "2.5.13",
201
201
  "@cloudflare/vitest-pool-workers": "^0.22.0",
202
- "@cloudflare/workers-types": "5.20260905.1",
202
+ "@cloudflare/workers-types": "5.20260910.1",
203
203
  "@duckdb/node-api": "^1.5.5-r.4",
204
204
  "@hono/otel": "^1.1.2",
205
205
  "@opentelemetry/exporter-metrics-otlp-http": "^0.222.0",
@@ -214,7 +214,7 @@
214
214
  "@socketsecurity/bun-security-scanner": "^1.1.2",
215
215
  "@supabase/supabase-js": "^2.116.0",
216
216
  "@types/bun": "^1.4.2",
217
- "@types/node": "26.4.0",
217
+ "@types/node": "26.5.1",
218
218
  "@types/papaparse": "^5.5.2",
219
219
  "@types/sanitize-html": "^2.16.1",
220
220
  "@types/validator": "^13.15.10",
@@ -247,7 +247,7 @@
247
247
  "typescript-v6": "npm:typescript@^6.0.3",
248
248
  "unpdf": "^1.8.1",
249
249
  "validator": "^13.15.35",
250
- "vite": "8.2.2",
250
+ "vite": "8.3.0",
251
251
  "vitest": "^4.1.11"
252
252
  },
253
253
  "keywords": [
@@ -309,7 +309,7 @@
309
309
  "hono": "^4.13.7",
310
310
  "jose": "^6.2.12",
311
311
  "pino": "^10.3.1",
312
- "zod": "^4.6.0"
312
+ "zod": "^4.6.1"
313
313
  },
314
314
  "peerDependencies": {
315
315
  "@duckdb/node-api": "^1.5.5-r.1",
@@ -152,6 +152,6 @@ for (const f of findings) {
152
152
  console.error('');
153
153
  }
154
154
  console.error(
155
- 'See skills/api-linter/SKILL.md or scripts/check-framework-antipatterns.ts for rule rationale.',
155
+ 'See framework-skills/api-linter/SKILL.md or scripts/check-framework-antipatterns.ts for rule rationale.',
156
156
  );
157
157
  process.exit(1);
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * @fileoverview Enforces the skill-versioning policy (#98 → #99): a change to a
4
- * `skills/<name>/SKILL.md` body must bump `metadata.version` in the same edit.
4
+ * `framework-skills/<name>/SKILL.md` body must bump `metadata.version` in the same edit.
5
5
  * Documenting the policy made the expectation visible; this check makes it stick.
6
6
  * The triggering incident was 7 missed bumps across 2 consecutive releases — the
7
7
  * kind of low-salience checklist item that needs tooling, not vigilance.
8
8
  *
9
- * For each `skills/<name>/SKILL.md` that differs from `HEAD` (working tree, staged
9
+ * For each `framework-skills/<name>/SKILL.md` that differs from `HEAD` (working tree, staged
10
10
  * or not), it compares the frontmatter `metadata.version` and the body across
11
11
  * `HEAD` → working tree. A changed body with an unchanged version is a violation.
12
12
  * Whitespace-only body edits never trigger it (the policy's typo/whitespace
@@ -35,7 +35,7 @@ import { resolve } from 'node:path';
35
35
  import process from 'node:process';
36
36
 
37
37
  const ROOT = resolve('.');
38
- const SKILL_MD_RE = /^skills\/[^/]+\/SKILL\.md$/;
38
+ const SKILL_MD_RE = /^framework-skills\/[^/]+\/SKILL\.md$/;
39
39
 
40
40
  interface DevcheckConfig {
41
41
  skillVersions?: { ignore?: string[] };
@@ -54,7 +54,7 @@ function loadIgnorePatterns(): string[] {
54
54
 
55
55
  /** Match check-skills-sync semantics: full `<name>/SKILL.md` path or the bare `<name>`. */
56
56
  function isIgnored(relPath: string, patterns: string[]): boolean {
57
- const name = relPath.split('/')[1]; // skills/<name>/SKILL.md → <name>
57
+ const name = relPath.split('/')[1]; // framework-skills/<name>/SKILL.md → <name>
58
58
  return patterns.some(
59
59
  (p) => p === relPath || p === name || (name !== undefined && p === `${name}/SKILL.md`),
60
60
  );
@@ -70,10 +70,17 @@ function changedSkillFiles(): string[] {
70
70
  .filter((p) => SKILL_MD_RE.test(p));
71
71
  }
72
72
 
73
- /** Content of a path at `HEAD`, or null when it didn't exist there (new file). */
73
+ /**
74
+ * Content of a path at `HEAD`, or null when it didn't exist there (new file).
75
+ * A tree renamed from the pre-0.13 `skills/` reads its `HEAD` copy from the old
76
+ * path, so the release that carries the rename still checks every body edit.
77
+ */
74
78
  function headContent(relPath: string): string | null {
75
- const result = spawnSync('git', ['show', `HEAD:${relPath}`], { encoding: 'utf-8' });
76
- return result.status === 0 ? result.stdout : null;
79
+ const show = (p: string) => spawnSync('git', ['show', `HEAD:${p}`], { encoding: 'utf-8' });
80
+ const result = show(relPath);
81
+ if (result.status === 0) return result.stdout;
82
+ const legacy = show(relPath.replace(/^framework-skills\//, 'skills/'));
83
+ return legacy.status === 0 ? legacy.stdout : null;
77
84
  }
78
85
 
79
86
  /** `metadata.version` from skill frontmatter, or null when absent/unparseable. */
@@ -95,8 +102,8 @@ function bodiesDiffer(a: string, b: string): boolean {
95
102
  return a.replace(/\s+/g, '') !== b.replace(/\s+/g, '');
96
103
  }
97
104
 
98
- if (!existsSync(resolve(ROOT, 'skills'))) {
99
- console.log('Skipped: no skills/ directory.');
105
+ if (!existsSync(resolve(ROOT, 'framework-skills'))) {
106
+ console.log('Skipped: no framework-skills/ directory.');
100
107
  process.exit(0);
101
108
  }
102
109
 
@@ -1,21 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @fileoverview Verifies that `skills/` (canonical) has been propagated to the
3
+ * @fileoverview Verifies that `framework-skills/` (canonical) has been propagated to the
4
4
  * local mirrors `.agents/skills/` and `.claude/skills/`. The maintenance skill
5
- * updates `skills/` for downstream servers; the mirrors are what local agent
5
+ * updates `framework-skills/` for downstream servers; the mirrors are what local agent
6
6
  * toolchains actually read, and silent drift means agents run on stale guidance.
7
7
  *
8
- * Propagation is one-way (`skills/` → mirrors), so missing or content-drifted
8
+ * Propagation is one-way (`framework-skills/` → mirrors), so missing or content-drifted
9
9
  * files are reported. A skill that exists *only* in a mirror is left alone when
10
10
  * it's externally sourced (globally installed, other tools), but flagged as
11
11
  * stale when its `SKILL.md` carries `metadata.audience: external` — a framework
12
- * skill removed from `skills/` upstream that the mirror never had pruned.
12
+ * skill removed from `framework-skills/` upstream that the mirror never had pruned.
13
13
  *
14
14
  * Behavior:
15
15
  * • In sync → pass
16
16
  * • Mirrors missing entirely → skip (no mirrors to sync)
17
17
  * • Drift (missing or changed files) → exit 1 with details (devcheck demotes to warning)
18
18
  * • Stale framework skill in mirror → exit 1 (mirror-only dir with audience: external)
19
+ * • Unmigrated pre-0.13 `skills/` → exit 1 with the `git mv` step
20
+ * • Leftover pre-0.13 `skills/` → exit 1 with the removal step (both trees present)
19
21
  *
20
22
  * Ignore specific skills or files via `devcheck.config.json`:
21
23
  *
@@ -25,7 +27,7 @@
25
27
  * }
26
28
  * }
27
29
  *
28
- * Patterns match relative paths under `skills/`. A bare name like `add-tool`
30
+ * Patterns match relative paths under `framework-skills/`. A bare name like `add-tool`
29
31
  * ignores the whole directory; `add-tool/SKILL.md` ignores a single file.
30
32
  * `.DS_Store` and other OS cruft are ignored by default.
31
33
  *
@@ -39,7 +41,13 @@ import { relative, resolve, sep } from 'node:path';
39
41
  import process from 'node:process';
40
42
 
41
43
  const ROOT = resolve('.');
42
- const SKILLS_DIR = resolve(ROOT, 'skills');
44
+ const SKILLS_DIR = resolve(ROOT, 'framework-skills');
45
+ /**
46
+ * Where the tree lived before 0.13. Claude Code and Codex auto-load a plugin's
47
+ * root `skills/`, so a server shipping a plugin manifest handed its development
48
+ * skills to every installing agent — hence the move.
49
+ */
50
+ const LEGACY_SKILLS_DIR = resolve(ROOT, 'skills');
43
51
  const MIRRORS: { label: string; path: string }[] = [
44
52
  { label: '.agents/skills', path: resolve(ROOT, '.agents/skills') },
45
53
  { label: '.claude/skills', path: resolve(ROOT, '.claude/skills') },
@@ -95,11 +103,51 @@ function isFrameworkManaged(skillMdPath: string): boolean {
95
103
  return /^\s*audience:\s*external\s*$/m.test(readFileSync(skillMdPath, 'utf-8'));
96
104
  }
97
105
 
106
+ /** Skill directories under `root` whose `SKILL.md` carries `audience: external`. */
107
+ function frameworkManagedDirs(root: string): string[] {
108
+ return skillDirNames(root).filter((name) => isFrameworkManaged(resolve(root, name, 'SKILL.md')));
109
+ }
110
+
98
111
  if (!existsSync(SKILLS_DIR)) {
99
- console.log('Skipped: no skills/ directory.');
112
+ const unmigrated = frameworkManagedDirs(LEGACY_SKILLS_DIR).length > 0;
113
+ if (unmigrated) {
114
+ console.log(
115
+ [
116
+ 'skills/ still holds framework-managed skills and framework-skills/ is absent.',
117
+ 'The framework moved its skill tree in 0.13.0: plugin hosts auto-load a root skills/,',
118
+ 'which surfaced these development skills to every agent that installed the server.',
119
+ '',
120
+ 'Fix: git mv skills framework-skills',
121
+ ' then update the path in CLAUDE.md/AGENTS.md, .mcpbignore (/framework-skills/),',
122
+ ' and .github/CONTRIBUTING.md, and regenerate docs/tree.md.',
123
+ ].join('\n'),
124
+ );
125
+ process.exit(1);
126
+ }
127
+ console.log('Skipped: no framework-skills/ directory.');
100
128
  process.exit(0);
101
129
  }
102
130
 
131
+ // Both trees present. `init` run in place never overwrites an existing file, so an
132
+ // upgrade creates `framework-skills/` and leaves the old copies where a plugin host
133
+ // still auto-loads them. Only a name carried by both trees is a leftover — a `skills/`
134
+ // name of its own is the reserved server-published kind.
135
+ const canonicalDirs = new Set(skillDirNames(SKILLS_DIR));
136
+ const leftover = frameworkManagedDirs(LEGACY_SKILLS_DIR).filter((name) => canonicalDirs.has(name));
137
+ if (leftover.length > 0) {
138
+ console.log(
139
+ [
140
+ `skills/ still holds ${leftover.length} framework skill(s) that framework-skills/ also carries.`,
141
+ 'Plugin hosts auto-load a root skills/, so those development skills still reach every',
142
+ 'agent that installs the server.',
143
+ '',
144
+ `Fix: rm -rf ${leftover.map((name) => `skills/${name}`).join(' ')}`,
145
+ ' Keep skills/ for skills the server publishes to the agents that use it.',
146
+ ].join('\n'),
147
+ );
148
+ process.exit(1);
149
+ }
150
+
103
151
  const presentMirrors = MIRRORS.filter((m) => existsSync(m.path));
104
152
  if (presentMirrors.length === 0) {
105
153
  console.log('Skipped: no skill mirrors (.agents/skills, .claude/skills) present.');
@@ -131,10 +179,9 @@ for (const mirror of presentMirrors) {
131
179
  }
132
180
 
133
181
  // Stale framework skills: a skill dir present only in a mirror (absent from
134
- // canonical skills/) is fine when externally sourced, but stale when it carries
135
- // `audience: external` — a framework skill removed from skills/ that was never
182
+ // canonical framework-skills/) is fine when externally sourced, but stale when it carries
183
+ // `audience: external` — a framework skill removed from framework-skills/ that was never
136
184
  // pruned from the mirror. User/external skills (no marker) are left alone.
137
- const canonicalDirs = new Set(skillDirNames(SKILLS_DIR));
138
185
  const stale: Record<string, string[]> = {};
139
186
  for (const mirror of presentMirrors) {
140
187
  const staleHere = skillDirNames(mirror.path)
@@ -152,13 +199,15 @@ const totals = {
152
199
  const driftCount = totals.missing + totals.drifted + totals.stale;
153
200
 
154
201
  if (driftCount === 0) {
155
- console.log(`skills/ is in sync with ${presentMirrors.map((m) => m.label).join(' and ')}.`);
202
+ console.log(
203
+ `framework-skills/ is in sync with ${presentMirrors.map((m) => m.label).join(' and ')}.`,
204
+ );
156
205
  process.exit(0);
157
206
  }
158
207
 
159
208
  const lines: string[] = [];
160
209
  lines.push(
161
- `skills/ has drifted from ${presentMirrors.length > 1 ? 'mirrors' : 'its mirror'} ` +
210
+ `framework-skills/ has drifted from ${presentMirrors.length > 1 ? 'mirrors' : 'its mirror'} ` +
162
211
  `(${totals.missing} missing, ${totals.drifted} changed, ${totals.stale} stale).`,
163
212
  );
164
213
 
@@ -175,7 +224,9 @@ renderSection('Content differs in', drifted);
175
224
  renderSection('Stale framework skill (deleted upstream) in', stale);
176
225
 
177
226
  lines.push('');
178
- lines.push('Fix: propagate skills/ to the mirror(s) and delete stale framework skills from them,');
227
+ lines.push(
228
+ 'Fix: propagate framework-skills/ to the mirror(s) and delete stale framework skills from them,',
229
+ );
179
230
  lines.push(
180
231
  ' or add entries to devcheck.config.json `skillsSync.ignore` to silence specific paths.',
181
232
  );
@@ -9,8 +9,8 @@
9
9
  * 2. Exact-name strip of two entry classes nested under `node_modules/`,
10
10
  * which root-anchored `.mcpbignore` patterns cannot reach by design
11
11
  * (issues #146/#207):
12
- * a. Dependency-shipped agent docs — `skills/`, `.claude/`, `.agents/`
13
- * trees and stray `SKILL.md` files (issue #230).
12
+ * a. Dependency-shipped agent docs — `framework-skills/`, `skills/`,
13
+ * `.claude/`, `.agents/` trees and stray `SKILL.md` files (issue #230).
14
14
  * b. Platform-specific native bindings, which would otherwise lock the
15
15
  * bundle to the build host's platform and push it past the 25 MB cap
16
16
  * registries enforce (issue #274).
@@ -36,7 +36,7 @@ import { fileURLToPath } from 'node:url';
36
36
  * (post-bundle content check) — a unit test asserts the two are identical.
37
37
  */
38
38
  export const AGENT_DOC_ENTRY =
39
- /^node_modules\/.*(?:\/skills\/|\/\.claude\/|\/\.agents\/|\/SKILL\.md$)/;
39
+ /^node_modules\/.*(?:\/framework-skills\/|\/skills\/|\/\.claude\/|\/\.agents\/|\/SKILL\.md$)/;
40
40
 
41
41
  /**
42
42
  * Platform-specific native binding packages, which must not ship in a bundle.
@@ -696,7 +696,7 @@ const ALL_CHECKS: Check[] = [
696
696
  canFix: false,
697
697
  getCommand: () => ['bun', 'run', 'scripts/lint-mcp.ts'],
698
698
  tip: (c) =>
699
- `Fix definition errors above — each diagnostic links to its rule in ${c.bold('skills/api-linter/SKILL.md')}.`,
699
+ `Fix definition errors above — each diagnostic links to its rule in ${c.bold('framework-skills/api-linter/SKILL.md')}.`,
700
700
  },
701
701
  {
702
702
  name: 'Packaging',
@@ -771,16 +771,19 @@ const ALL_CHECKS: Check[] = [
771
771
  name: 'Skills Sync',
772
772
  flag: '--no-skills-sync',
773
773
  canFix: false,
774
- // Compares canonical skills/ against local mirrors (.agents/skills, .claude/skills).
775
- // Skipped when skills/ or both mirrors are absent (non-mirrored projects).
776
- // Drift is demoted to a warning via isSuccess — intentional ignores live in
777
- // devcheck.config.json `skillsSync.ignore`.
774
+ // Compares canonical framework-skills/ against local mirrors (.agents/skills, .claude/skills).
775
+ // Skipped when framework-skills/ or both mirrors are absent (non-mirrored projects),
776
+ // except that a pre-0.13 `skills/` tree always runs — absent or alongside
777
+ // `framework-skills/` — so the script's migration message surfaces. Drift is demoted to
778
+ // a warning via isSuccess — intentional ignores live in devcheck.config.json
779
+ // `skillsSync.ignore`.
778
780
  getCommand: () => {
779
- const hasSkills = existsSync(path.join(ROOT_DIR, 'skills'));
781
+ const hasSkills = existsSync(path.join(ROOT_DIR, 'framework-skills'));
782
+ const hasLegacySkills = existsSync(path.join(ROOT_DIR, 'skills'));
780
783
  const hasMirrors =
781
784
  existsSync(path.join(ROOT_DIR, '.agents/skills')) ||
782
785
  existsSync(path.join(ROOT_DIR, '.claude/skills'));
783
- if (!hasSkills || !hasMirrors) return null;
786
+ if (!hasLegacySkills && (!hasSkills || !hasMirrors)) return null;
784
787
  return ['bun', 'run', 'scripts/check-skills-sync.ts'];
785
788
  },
786
789
  isSuccess: (result) => {
@@ -789,18 +792,18 @@ const ALL_CHECKS: Check[] = [
789
792
  return { success: true, warning: firstLine };
790
793
  },
791
794
  tip: (c) =>
792
- `Propagate ${c.bold('skills/')} to ${c.bold('.agents/skills/')} and ${c.bold('.claude/skills/')}, or add entries to ${c.bold('devcheck.config.json')} ${c.bold('skillsSync.ignore')}.`,
795
+ `Propagate ${c.bold('framework-skills/')} to ${c.bold('.agents/skills/')} and ${c.bold('.claude/skills/')}, or add entries to ${c.bold('devcheck.config.json')} ${c.bold('skillsSync.ignore')}.`,
793
796
  },
794
797
  {
795
798
  name: 'Skill Versions',
796
799
  flag: '--no-skill-versions',
797
800
  canFix: false,
798
- // Flags skills/<name>/SKILL.md body changes (vs HEAD) that lack a metadata.version
799
- // bump (#99). Skipped when skills/ is absent. Drift is demoted to a warning via
801
+ // Flags framework-skills/<name>/SKILL.md body changes (vs HEAD) that lack a metadata.version
802
+ // bump (#99). Skipped when framework-skills/ is absent. Drift is demoted to a warning via
800
803
  // isSuccess — the typo/whitespace carve-out lives in devcheck.config.json
801
804
  // `skillVersions.ignore`.
802
805
  getCommand: () => {
803
- if (!existsSync(path.join(ROOT_DIR, 'skills'))) return null;
806
+ if (!existsSync(path.join(ROOT_DIR, 'framework-skills'))) return null;
804
807
  return ['bun', 'run', 'scripts/check-skill-versions.ts'];
805
808
  },
806
809
  isSuccess: (result) => {
@@ -903,7 +906,7 @@ const ALL_CHECKS: Check[] = [
903
906
  {
904
907
  name: 'Security Audit',
905
908
  flag: '--no-audit',
906
- canFix: false, // audit --fix exists but often requires manual review.
909
+ canFix: false, // `audit fix` exists but often requires manual review.
907
910
  slowCheck: true,
908
911
  getCommand: () => [PM_CMD, 'audit'],
909
912
  isSuccess: (result, _mode) => {
@@ -951,7 +954,7 @@ const ALL_CHECKS: Check[] = [
951
954
  return true;
952
955
  },
953
956
  tip: (c) =>
954
- `Direct dependency vulnerabilities found. Run ${c.bold(`${PM_CMD} update`)} or ${c.bold(`${PM_CMD} audit --fix`)} to resolve.`,
957
+ `Direct dependency vulnerabilities found. Run ${c.bold(`${PM_CMD} audit fix`)} or ${c.bold(`${PM_CMD} update <pkg>`)} to resolve.`,
955
958
  },
956
959
  {
957
960
  name: 'Dependencies (Outdated)',