@cyanheads/mcp-ts-core 0.12.8 → 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 (118) hide show
  1. package/AGENTS.md +12 -11
  2. package/CLAUDE.md +12 -11
  3. package/README.md +2 -2
  4. package/biome.json +1 -1
  5. package/changelog/0.12.x/0.12.9.md +36 -0
  6. package/changelog/0.13.x/0.13.0.md +48 -0
  7. package/changelog/template.md +7 -24
  8. package/dist/cli/init.js +2 -2
  9. package/dist/cli/init.js.map +1 -1
  10. package/dist/config/envValue.d.ts +18 -0
  11. package/dist/config/envValue.d.ts.map +1 -0
  12. package/dist/config/envValue.js +35 -0
  13. package/dist/config/envValue.js.map +1 -0
  14. package/dist/config/index.d.ts.map +1 -1
  15. package/dist/config/index.js +5 -7
  16. package/dist/config/index.js.map +1 -1
  17. package/dist/config/parseEnvConfig.d.ts +7 -0
  18. package/dist/config/parseEnvConfig.d.ts.map +1 -1
  19. package/dist/config/parseEnvConfig.js +9 -1
  20. package/dist/config/parseEnvConfig.js.map +1 -1
  21. package/dist/linter/validate.js +2 -2
  22. package/dist/linter/validate.js.map +1 -1
  23. package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
  24. package/dist/mcp-server/transports/http/httpTransport.js +70 -2
  25. package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
  26. package/dist/mcp-server/transports/http/landing-page/sections/connect.d.ts.map +1 -1
  27. package/dist/mcp-server/transports/http/landing-page/sections/connect.js +9 -2
  28. package/dist/mcp-server/transports/http/landing-page/sections/connect.js.map +1 -1
  29. package/dist/mcp-server/transports/http/sessionStore.d.ts +10 -2
  30. package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -1
  31. package/dist/mcp-server/transports/http/sessionStore.js.map +1 -1
  32. package/dist/services/mirror/sqlite/handle.d.ts.map +1 -1
  33. package/dist/services/mirror/sqlite/handle.js +14 -12
  34. package/dist/services/mirror/sqlite/handle.js.map +1 -1
  35. package/dist/services/mirror/sqlite/sqliteMirrorStore.js +8 -9
  36. package/dist/services/mirror/sqlite/sqliteMirrorStore.js.map +1 -1
  37. package/dist/services/mirror/types.d.ts +5 -1
  38. package/dist/services/mirror/types.d.ts.map +1 -1
  39. package/dist/utils/internal/performance.d.ts +1 -1
  40. package/dist/utils/internal/performance.js +2 -2
  41. package/dist/utils/network/fetchWithTimeout.js +1 -1
  42. package/dist/utils/network/retry.js +1 -1
  43. package/dist/utils/security/idGenerator.d.ts +3 -1
  44. package/dist/utils/security/idGenerator.d.ts.map +1 -1
  45. package/dist/utils/security/idGenerator.js +12 -1
  46. package/dist/utils/security/idGenerator.js.map +1 -1
  47. package/framework-skills/README.md +40 -0
  48. package/{skills → framework-skills}/add-app-tool/SKILL.md +2 -2
  49. package/{skills → framework-skills}/add-resource/SKILL.md +2 -2
  50. package/{skills → framework-skills}/add-service/SKILL.md +2 -2
  51. package/{skills → framework-skills}/add-test/SKILL.md +2 -2
  52. package/{skills → framework-skills}/add-tool/SKILL.md +7 -7
  53. package/{skills → framework-skills}/api-config/SKILL.md +3 -1
  54. package/{skills → framework-skills}/api-context/SKILL.md +3 -3
  55. package/{skills → framework-skills}/api-errors/SKILL.md +2 -1
  56. package/{skills → framework-skills}/api-linter/SKILL.md +4 -4
  57. package/{skills → framework-skills}/api-mirror/SKILL.md +3 -1
  58. package/{skills → framework-skills}/design-mcp-server/SKILL.md +59 -101
  59. package/{skills → framework-skills}/field-test/SKILL.md +10 -5
  60. package/{skills → framework-skills}/git-wrapup/SKILL.md +5 -3
  61. package/{skills → framework-skills}/maintenance/SKILL.md +30 -21
  62. package/{skills → framework-skills}/orchestrations/SKILL.md +2 -2
  63. package/{skills → framework-skills}/orchestrations/workflows/field-test-fix.md +8 -8
  64. package/{skills → framework-skills}/orchestrations/workflows/fix-wrapup-release.md +5 -5
  65. package/{skills → framework-skills}/orchestrations/workflows/greenfield-build.md +11 -11
  66. package/{skills → framework-skills}/orchestrations/workflows/maintenance-release.md +12 -12
  67. package/{skills → framework-skills}/polish-docs-meta/SKILL.md +18 -10
  68. package/{skills → framework-skills}/polish-docs-meta/references/agent-protocol.md +1 -1
  69. package/{skills → framework-skills}/polish-docs-meta/references/package-meta.md +1 -1
  70. package/{skills → framework-skills}/polish-docs-meta/references/readme.md +88 -72
  71. package/{skills → framework-skills}/release-and-publish/SKILL.md +4 -1
  72. package/{skills → framework-skills}/release-pr-review/SKILL.md +2 -2
  73. package/{skills → framework-skills}/report-issue-framework/SKILL.md +25 -25
  74. package/{skills → framework-skills}/report-issue-local/SKILL.md +22 -24
  75. package/{skills → framework-skills}/security-pass/SKILL.md +2 -2
  76. package/{skills → framework-skills}/setup/SKILL.md +10 -8
  77. package/package.json +13 -13
  78. package/scripts/check-framework-antipatterns.ts +1 -1
  79. package/scripts/check-skill-versions.ts +16 -9
  80. package/scripts/check-skills-sync.ts +64 -13
  81. package/scripts/clean-mcpb.ts +3 -3
  82. package/scripts/devcheck.ts +37 -27
  83. package/scripts/lint-packaging.ts +158 -24
  84. package/scripts/list-skills.ts +2 -2
  85. package/templates/.claude-plugin/plugin.json +5 -1
  86. package/templates/.env.example +1 -1
  87. package/templates/.github/CONTRIBUTING.md +4 -5
  88. package/templates/.github/ISSUE_TEMPLATE/bug_report.yml +5 -4
  89. package/templates/.github/ISSUE_TEMPLATE/config.yml +6 -1
  90. package/templates/.github/ISSUE_TEMPLATE/feature_request.yml +1 -2
  91. package/templates/AGENTS.md +16 -15
  92. package/templates/CLAUDE.md +16 -15
  93. package/templates/_.mcpbignore +1 -1
  94. package/templates/changelog/template.md +7 -24
  95. package/templates/package.json +4 -3
  96. package/templates/src/mcp-server/resources/definitions/echo-app-ui.app-resource.ts +1 -1
  97. package/skills/README.md +0 -38
  98. /package/{skills → framework-skills}/add-export/SKILL.md +0 -0
  99. /package/{skills → framework-skills}/add-prompt/SKILL.md +0 -0
  100. /package/{skills → framework-skills}/add-provider/SKILL.md +0 -0
  101. /package/{skills → framework-skills}/api-auth/SKILL.md +0 -0
  102. /package/{skills → framework-skills}/api-canvas/SKILL.md +0 -0
  103. /package/{skills → framework-skills}/api-services/SKILL.md +0 -0
  104. /package/{skills → framework-skills}/api-services/references/graph.md +0 -0
  105. /package/{skills → framework-skills}/api-services/references/llm.md +0 -0
  106. /package/{skills → framework-skills}/api-services/references/speech.md +0 -0
  107. /package/{skills → framework-skills}/api-telemetry/SKILL.md +0 -0
  108. /package/{skills → framework-skills}/api-testing/SKILL.md +0 -0
  109. /package/{skills → framework-skills}/api-utils/SKILL.md +0 -0
  110. /package/{skills → framework-skills}/api-utils/references/formatting.md +0 -0
  111. /package/{skills → framework-skills}/api-utils/references/parsing.md +0 -0
  112. /package/{skills → framework-skills}/api-utils/references/security.md +0 -0
  113. /package/{skills → framework-skills}/api-workers/SKILL.md +0 -0
  114. /package/{skills → framework-skills}/code-simplifier/SKILL.md +0 -0
  115. /package/{skills → framework-skills}/polish-docs-meta/references/server-json.md +0 -0
  116. /package/{skills → framework-skills}/techniques/SKILL.md +0 -0
  117. /package/{skills → framework-skills}/techniques/references/outline-on-overflow.md +0 -0
  118. /package/{skills → framework-skills}/tool-defs-analysis/SKILL.md +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/mcp-ts-core",
3
- "version": "0.12.8",
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",
@@ -212,9 +212,9 @@
212
212
  "@opentelemetry/sdk-trace-node": "^2.11.0",
213
213
  "@opentelemetry/semantic-conventions": "^1.43.0",
214
214
  "@socketsecurity/bun-security-scanner": "^1.1.2",
215
- "@supabase/supabase-js": "^2.115.0",
216
- "@types/bun": "^1.4.1",
217
- "@types/node": "26.4.0",
215
+ "@supabase/supabase-js": "^2.116.0",
216
+ "@types/bun": "^1.4.2",
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",
@@ -230,7 +230,7 @@
230
230
  "execa": "^10.0.1",
231
231
  "fast-check": "^4.9.0",
232
232
  "fast-xml-parser": "^5.11.1",
233
- "ignore": "^7.0.8",
233
+ "ignore": "^7.0.9",
234
234
  "js-yaml": "^5.4.1",
235
235
  "linkedom": "^0.18.13",
236
236
  "node-cron": "^4.6.0",
@@ -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": [
@@ -282,7 +282,7 @@
282
282
  ],
283
283
  "packageManager": "bun@1.4.0",
284
284
  "engines": {
285
- "bun": ">=1.3.0",
285
+ "bun": ">=1.4.0",
286
286
  "node": ">=24.0.0"
287
287
  },
288
288
  "depcheck": {
@@ -302,14 +302,14 @@
302
302
  "dependencies": {
303
303
  "@hono/node-server": "^2.1.1",
304
304
  "@modelcontextprotocol/client": "^2.0.0",
305
- "@modelcontextprotocol/ext-apps": "^1.7.5",
305
+ "@modelcontextprotocol/ext-apps": "^2.0.0",
306
306
  "@modelcontextprotocol/server": "^2.0.0",
307
307
  "@opentelemetry/api": "^1.9.1",
308
308
  "dotenv": "^17.4.2",
309
309
  "hono": "^4.13.7",
310
310
  "jose": "^6.2.12",
311
311
  "pino": "^10.3.1",
312
- "zod": "^4.5.4"
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.
@@ -437,7 +437,8 @@ interface OutdatedRow {
437
437
  current: string;
438
438
  /** The declared key when attribution is unambiguous, else null. */
439
439
  declaredKey: string | null;
440
- group: DependencyGroup | null;
440
+ /** The block the row belongs to; `bun outdated` leaves `prod` rows unmarked. */
441
+ group: DependencyGroup;
441
442
  /** The row verbatim, used to key the rendered rewrite back to its line. */
442
443
  line: string;
443
444
  /** First cell as printed, marker included. */
@@ -457,11 +458,11 @@ interface OutdatedRow {
457
458
  */
458
459
  function attributeOutdatedRow(
459
460
  target: string,
460
- group: DependencyGroup | null,
461
+ group: DependencyGroup,
461
462
  current: string,
462
463
  ): Pick<OutdatedRow, 'candidates' | 'declaredKey'> {
463
464
  const matches = DECLARED_DEPENDENCIES.filter(
464
- (declared) => declared.target === target && (group === null || declared.group === group),
465
+ (declared) => declared.target === target && declared.group === group,
465
466
  );
466
467
  const candidates = matches.map((declared) => declared.key);
467
468
  if (candidates.length <= 1) return { candidates, declaredKey: candidates[0] ?? null };
@@ -481,7 +482,7 @@ function parseOutdatedRows(output: string): OutdatedRow[] {
481
482
  const rawName = cells[1] ?? '';
482
483
  // Skip table chrome: header row and separator (e.g., "---")
483
484
  if (!rawName || rawName === 'Package' || /^-+$/.test(rawName)) continue;
484
- const group = (OUTDATED_GROUP_MARKER.exec(rawName)?.[1] ?? null) as DependencyGroup | null;
485
+ const group = (OUTDATED_GROUP_MARKER.exec(rawName)?.[1] ?? 'prod') as DependencyGroup;
485
486
  const target = rawName.replace(OUTDATED_GROUP_MARKER, '');
486
487
  const current = cells[2] ?? '';
487
488
  const update = (cells[3] ?? '').replace(/\*/g, '').trim();
@@ -514,7 +515,7 @@ function renderOutdatedTable(output: string): string {
514
515
  const row = attributed.get(line);
515
516
  let display = cell.trim();
516
517
  if (row?.declaredKey && row.declaredKey !== row.target) {
517
- display = row.group ? `${row.declaredKey} (${row.group})` : row.declaredKey;
518
+ display = row.group === 'prod' ? row.declaredKey : `${row.declaredKey} (${row.group})`;
518
519
  }
519
520
  return { line, display };
520
521
  });
@@ -552,9 +553,12 @@ function renderOutdatedTable(output: string): string {
552
553
  * Parses `bun audit` output and classifies high/critical vulnerabilities as
553
554
  * direct (in our package.json) or upstream (transitive dependency we can't fix).
554
555
  *
555
- * Bun audit format per vulnerability block:
556
- * <package> <version-range> ← header (no indent, 2+ spaces before range)
557
- * <parent> › <child> [› ...] ← dependency path (indented, › = transitive)
556
+ * Bun audit format per vulnerability block — Bun 1.4 changed the header token
557
+ * and the path separator, so both shapes are accepted:
558
+ * <package> <version-range> ← header, Bun <1.4 (no indent, 2+ spaces before range)
559
+ * <package>@<version> ← header, Bun ≥1.4
560
+ * <parent> › <child> [› ...] ← dependency path (indented; › or, Bun ≥1.4, > = transitive)
561
+ * (direct dependency) ← dependency path of a direct dependency, Bun ≥1.4
558
562
  * <severity>: <description> ← advisory (indented)
559
563
  *
560
564
  * Returns null if parsing yields no results (caller should fall back to default behavior).
@@ -567,8 +571,10 @@ function classifyAuditVulns(output: string): { direct: string[]; upstream: strin
567
571
  let i = 0;
568
572
 
569
573
  while (i < lines.length) {
570
- // Package header: non-indented, name followed by 2+ spaces then version constraint
571
- const pkgMatch = lines[i]?.match(/^([@\w][\w./-]*)\s{2,}(.+)$/);
574
+ // Package header: non-indented `name range` (2+ spaces, Bun <1.4) or
575
+ // `name@version` (Bun ≥1.4). A scoped name leads with `@`, so only a
576
+ // later `@` splits the Bun 1.4 form.
577
+ const pkgMatch = lines[i]?.match(/^(@?\w[\w./-]*)(?:\s{2,}|@)(\S.*)$/);
572
578
  if (!pkgMatch) {
573
579
  i++;
574
580
  continue;
@@ -593,14 +599,15 @@ function classifyAuditVulns(output: string): { direct: string[]; upstream: strin
593
599
 
594
600
  if (!hasHighCritical) continue;
595
601
 
596
- // Direct if: the vulnerable package is in our package.json,
597
- // or any dependency path lacks › (meaning it's not pulled in transitively)
602
+ // Direct if: the vulnerable package is in our package.json, or any
603
+ // dependency path lacks a transitive separator — U+203A `›` (Bun <1.4)
604
+ // or `>` (Bun ≥1.4). Bun 1.4's literal `(direct dependency)` path has neither.
598
605
  const pkgName = pkg ?? '';
599
- const isDirect = DIRECT_DEPS.has(pkgName) || paths.some((p) => !p.includes('\u203a'));
606
+ const isDirect = DIRECT_DEPS.has(pkgName) || paths.some((p) => !/[\u203a>]/.test(p));
600
607
  if (isDirect) {
601
608
  direct.push(`${pkgName} ${versionRange}`);
602
609
  } else {
603
- const via = paths[0]?.split(/\s*\u203a\s*/)[0] ?? 'unknown';
610
+ const via = paths[0]?.split(/\s*[\u203a>]\s*/)[0] ?? 'unknown';
604
611
  upstream.push(`${pkgName} ${versionRange} (via ${via})`);
605
612
  }
606
613
  }
@@ -689,7 +696,7 @@ const ALL_CHECKS: Check[] = [
689
696
  canFix: false,
690
697
  getCommand: () => ['bun', 'run', 'scripts/lint-mcp.ts'],
691
698
  tip: (c) =>
692
- `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')}.`,
693
700
  },
694
701
  {
695
702
  name: 'Packaging',
@@ -764,16 +771,19 @@ const ALL_CHECKS: Check[] = [
764
771
  name: 'Skills Sync',
765
772
  flag: '--no-skills-sync',
766
773
  canFix: false,
767
- // Compares canonical skills/ against local mirrors (.agents/skills, .claude/skills).
768
- // Skipped when skills/ or both mirrors are absent (non-mirrored projects).
769
- // Drift is demoted to a warning via isSuccess — intentional ignores live in
770
- // 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`.
771
780
  getCommand: () => {
772
- 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'));
773
783
  const hasMirrors =
774
784
  existsSync(path.join(ROOT_DIR, '.agents/skills')) ||
775
785
  existsSync(path.join(ROOT_DIR, '.claude/skills'));
776
- if (!hasSkills || !hasMirrors) return null;
786
+ if (!hasLegacySkills && (!hasSkills || !hasMirrors)) return null;
777
787
  return ['bun', 'run', 'scripts/check-skills-sync.ts'];
778
788
  },
779
789
  isSuccess: (result) => {
@@ -782,18 +792,18 @@ const ALL_CHECKS: Check[] = [
782
792
  return { success: true, warning: firstLine };
783
793
  },
784
794
  tip: (c) =>
785
- `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')}.`,
786
796
  },
787
797
  {
788
798
  name: 'Skill Versions',
789
799
  flag: '--no-skill-versions',
790
800
  canFix: false,
791
- // Flags skills/<name>/SKILL.md body changes (vs HEAD) that lack a metadata.version
792
- // 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
793
803
  // isSuccess — the typo/whitespace carve-out lives in devcheck.config.json
794
804
  // `skillVersions.ignore`.
795
805
  getCommand: () => {
796
- if (!existsSync(path.join(ROOT_DIR, 'skills'))) return null;
806
+ if (!existsSync(path.join(ROOT_DIR, 'framework-skills'))) return null;
797
807
  return ['bun', 'run', 'scripts/check-skill-versions.ts'];
798
808
  },
799
809
  isSuccess: (result) => {
@@ -896,7 +906,7 @@ const ALL_CHECKS: Check[] = [
896
906
  {
897
907
  name: 'Security Audit',
898
908
  flag: '--no-audit',
899
- canFix: false, // audit --fix exists but often requires manual review.
909
+ canFix: false, // `audit fix` exists but often requires manual review.
900
910
  slowCheck: true,
901
911
  getCommand: () => [PM_CMD, 'audit'],
902
912
  isSuccess: (result, _mode) => {
@@ -944,7 +954,7 @@ const ALL_CHECKS: Check[] = [
944
954
  return true;
945
955
  },
946
956
  tip: (c) =>
947
- `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.`,
948
958
  },
949
959
  {
950
960
  name: 'Dependencies (Outdated)',