@skillcap/gdh 0.4.1 → 0.6.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 (105) hide show
  1. package/INSTALL-BUNDLE.json +1 -1
  2. package/README.md +54 -92
  3. package/node_modules/@gdh/adapters/dist/claude-settings-patch.d.ts +74 -0
  4. package/node_modules/@gdh/adapters/dist/claude-settings-patch.d.ts.map +1 -0
  5. package/node_modules/@gdh/adapters/dist/claude-settings-patch.js +158 -0
  6. package/node_modules/@gdh/adapters/dist/claude-settings-patch.js.map +1 -0
  7. package/node_modules/@gdh/adapters/dist/claude-statusline-render.d.ts +51 -0
  8. package/node_modules/@gdh/adapters/dist/claude-statusline-render.d.ts.map +1 -0
  9. package/node_modules/@gdh/adapters/dist/claude-statusline-render.js +80 -0
  10. package/node_modules/@gdh/adapters/dist/claude-statusline-render.js.map +1 -0
  11. package/node_modules/@gdh/adapters/dist/claude-update-hook-render.d.ts +35 -0
  12. package/node_modules/@gdh/adapters/dist/claude-update-hook-render.d.ts.map +1 -0
  13. package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js +76 -0
  14. package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js.map +1 -0
  15. package/node_modules/@gdh/adapters/dist/claude-update-worker-render.d.ts +28 -0
  16. package/node_modules/@gdh/adapters/dist/claude-update-worker-render.d.ts.map +1 -0
  17. package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js +99 -0
  18. package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js.map +1 -0
  19. package/node_modules/@gdh/adapters/dist/index.d.ts +34 -18
  20. package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
  21. package/node_modules/@gdh/adapters/dist/index.js +596 -145
  22. package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
  23. package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts +51 -0
  24. package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts.map +1 -0
  25. package/node_modules/@gdh/adapters/dist/self-update-mechanics.js +155 -0
  26. package/node_modules/@gdh/adapters/dist/self-update-mechanics.js.map +1 -0
  27. package/node_modules/@gdh/adapters/package.json +8 -8
  28. package/node_modules/@gdh/authoring/dist/index.d.ts +2 -1
  29. package/node_modules/@gdh/authoring/dist/index.d.ts.map +1 -1
  30. package/node_modules/@gdh/authoring/dist/index.js +2 -1
  31. package/node_modules/@gdh/authoring/dist/index.js.map +1 -1
  32. package/node_modules/@gdh/authoring/dist/project.d.ts +24 -0
  33. package/node_modules/@gdh/authoring/dist/project.d.ts.map +1 -1
  34. package/node_modules/@gdh/authoring/dist/project.js +51 -1
  35. package/node_modules/@gdh/authoring/dist/project.js.map +1 -1
  36. package/node_modules/@gdh/authoring/dist/writePinnedVersion.d.ts +17 -0
  37. package/node_modules/@gdh/authoring/dist/writePinnedVersion.d.ts.map +1 -0
  38. package/node_modules/@gdh/authoring/dist/writePinnedVersion.js +50 -0
  39. package/node_modules/@gdh/authoring/dist/writePinnedVersion.js.map +1 -0
  40. package/node_modules/@gdh/authoring/package.json +5 -2
  41. package/node_modules/@gdh/cli/dist/index.d.ts +15 -0
  42. package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
  43. package/node_modules/@gdh/cli/dist/index.js +292 -40
  44. package/node_modules/@gdh/cli/dist/index.js.map +1 -1
  45. package/node_modules/@gdh/cli/dist/migrate.d.ts +1 -0
  46. package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
  47. package/node_modules/@gdh/cli/dist/migrate.js +180 -72
  48. package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
  49. package/node_modules/@gdh/cli/dist/self-update.d.ts +3 -0
  50. package/node_modules/@gdh/cli/dist/self-update.d.ts.map +1 -0
  51. package/node_modules/@gdh/cli/dist/self-update.js +235 -0
  52. package/node_modules/@gdh/cli/dist/self-update.js.map +1 -0
  53. package/node_modules/@gdh/cli/dist/setup.d.ts.map +1 -1
  54. package/node_modules/@gdh/cli/dist/setup.js +49 -1
  55. package/node_modules/@gdh/cli/dist/setup.js.map +1 -1
  56. package/node_modules/@gdh/cli/dist/update-banner.d.ts +42 -0
  57. package/node_modules/@gdh/cli/dist/update-banner.d.ts.map +1 -0
  58. package/node_modules/@gdh/cli/dist/update-banner.js +49 -0
  59. package/node_modules/@gdh/cli/dist/update-banner.js.map +1 -0
  60. package/node_modules/@gdh/cli/package.json +10 -10
  61. package/node_modules/@gdh/core/dist/dev-mode.d.ts +13 -0
  62. package/node_modules/@gdh/core/dist/dev-mode.d.ts.map +1 -0
  63. package/node_modules/@gdh/core/dist/dev-mode.js +21 -0
  64. package/node_modules/@gdh/core/dist/dev-mode.js.map +1 -0
  65. package/node_modules/@gdh/core/dist/index.d.ts +12 -5
  66. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  67. package/node_modules/@gdh/core/dist/index.js +10 -2
  68. package/node_modules/@gdh/core/dist/index.js.map +1 -1
  69. package/node_modules/@gdh/core/dist/update-cache.d.ts +46 -0
  70. package/node_modules/@gdh/core/dist/update-cache.d.ts.map +1 -0
  71. package/node_modules/@gdh/core/dist/update-cache.js +90 -0
  72. package/node_modules/@gdh/core/dist/update-cache.js.map +1 -0
  73. package/node_modules/@gdh/core/dist/update-probe.d.ts +102 -0
  74. package/node_modules/@gdh/core/dist/update-probe.d.ts.map +1 -0
  75. package/node_modules/@gdh/core/dist/update-probe.js +195 -0
  76. package/node_modules/@gdh/core/dist/update-probe.js.map +1 -0
  77. package/node_modules/@gdh/core/package.json +1 -1
  78. package/node_modules/@gdh/docs/dist/guidance.d.ts.map +1 -1
  79. package/node_modules/@gdh/docs/dist/guidance.js +47 -0
  80. package/node_modules/@gdh/docs/dist/guidance.js.map +1 -1
  81. package/node_modules/@gdh/docs/package.json +2 -2
  82. package/node_modules/@gdh/mcp/dist/index.d.ts +20 -0
  83. package/node_modules/@gdh/mcp/dist/index.d.ts.map +1 -1
  84. package/node_modules/@gdh/mcp/dist/index.js +45 -4
  85. package/node_modules/@gdh/mcp/dist/index.js.map +1 -1
  86. package/node_modules/@gdh/mcp/package.json +8 -8
  87. package/node_modules/@gdh/observability/dist/guidance-audit.js +3 -1
  88. package/node_modules/@gdh/observability/dist/guidance-audit.js.map +1 -1
  89. package/node_modules/@gdh/observability/package.json +2 -2
  90. package/node_modules/@gdh/runtime/package.json +2 -2
  91. package/node_modules/@gdh/scan/dist/index.d.ts +2 -0
  92. package/node_modules/@gdh/scan/dist/index.d.ts.map +1 -1
  93. package/node_modules/@gdh/scan/dist/index.js +1 -0
  94. package/node_modules/@gdh/scan/dist/index.js.map +1 -1
  95. package/node_modules/@gdh/scan/dist/inventory-cache.d.ts +15 -0
  96. package/node_modules/@gdh/scan/dist/inventory-cache.d.ts.map +1 -0
  97. package/node_modules/@gdh/scan/dist/inventory-cache.js +53 -0
  98. package/node_modules/@gdh/scan/dist/inventory-cache.js.map +1 -0
  99. package/node_modules/@gdh/scan/dist/onboard.d.ts +7 -0
  100. package/node_modules/@gdh/scan/dist/onboard.d.ts.map +1 -1
  101. package/node_modules/@gdh/scan/dist/onboard.js +7 -1
  102. package/node_modules/@gdh/scan/dist/onboard.js.map +1 -1
  103. package/node_modules/@gdh/scan/package.json +3 -3
  104. package/node_modules/@gdh/verify/package.json +7 -7
  105. package/package.json +11 -11
@@ -4,7 +4,11 @@ import fs from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { promisify } from "node:util";
7
- import { readProjectConfig, resolveProjectRoot, readWorktreeState, resolveAuthoringStatus, } from "@gdh/authoring";
7
+ import { readProjectConfig, resolvePinnedVersion, resolvePinnedVersionOrNull, resolveProjectRoot, readWorktreeState, resolveAuthoringStatus, } from "@gdh/authoring";
8
+ import { CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH, CLAUDE_CHECK_UPDATE_WORKER_RELATIVE_PATH, renderClaudeCheckUpdateHook, } from "./claude-update-hook-render.js";
9
+ import { renderClaudeCheckUpdateWorker } from "./claude-update-worker-render.js";
10
+ import { CLAUDE_STATUSLINE_RELATIVE_PATH, renderClaudeUpdateStatusline, } from "./claude-statusline-render.js";
11
+ import { CLAUDE_SETTINGS_RELATIVE_PATH, patchClaudeSettingsForGdhSessionStart, patchClaudeSettingsForGdhStatusline, } from "./claude-settings-patch.js";
8
12
  import { GDH_AGENT_CONTRACT_VERSION, GDH_CURSOR_RULE_VERSION, GDH_GUIDANCE_INDEX_VERSION, GDH_GUIDANCE_UNIT_VERSION, GDH_MCP_LAUNCHER_VERSION, GDH_PROJECT_CONFIG_VERSION, GDH_RECIPE_SCHEMA_VERSION, GDH_RULES_SCHEMA_VERSION, GDH_SCENARIO_SCHEMA_VERSION, definePackageBoundary, resolveCurrentGdhInstall, resolveGdhProductMetadata, } from "@gdh/core";
9
13
  import { createDefaultGuidanceUnits, getGuidanceStatus, resolveGuidanceQuery, } from "@gdh/docs";
10
14
  import { inspectGuidanceAudit } from "@gdh/observability";
@@ -48,6 +52,18 @@ export const CURSOR_MIGRATE_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-migrate/SK
48
52
  export const CURSOR_CHECK_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-check/SKILL.md";
49
53
  export const CURSOR_PREPARE_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-prepare/SKILL.md";
50
54
  export const CURSOR_VERIFY_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-verify/SKILL.md";
55
+ // Phase 13 SELF-01: /gdh-update skill surface path constants. The rendered
56
+ // bodies shell out to `npx -y @skillcap/gdh@latest self-update` (LITERAL
57
+ // @latest, not the pinned version — D-10) so the NEW CLI performs the update,
58
+ // not the (potentially pre-Phase-12) OLD pinned one. These constants are
59
+ // INTENTIONALLY excluded from VERIFY_DRIFT_SCANNED_FILES (D-13) because the
60
+ // rendered bodies are version-agnostic by design.
61
+ export const CLAUDE_UPDATE_COMMAND_RELATIVE_PATH = ".claude/commands/gdh/update.md";
62
+ export const CODEX_UPDATE_SKILL_RELATIVE_PATH = ".codex/skills/gdh-update/SKILL.md";
63
+ export const CURSOR_UPDATE_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-update/SKILL.md";
64
+ export const CLAUDE_SCAN_COMMAND_RELATIVE_PATH = ".claude/commands/gdh/scan.md";
65
+ export const CODEX_SCAN_SKILL_RELATIVE_PATH = ".codex/skills/gdh-scan/SKILL.md";
66
+ export const CURSOR_SCAN_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-scan/SKILL.md";
51
67
  export const LOCAL_PATH_HINTS_RELATIVE_PATH = ".gdh-state/local-paths.json";
52
68
  export const MCP_LAUNCHER_RELATIVE_PATH = ".gdh/bin/gdh-mcp.mjs";
53
69
  export const GDH_MCP_SERVER_NAME = "gdh";
@@ -56,16 +72,18 @@ export async function getSupportedAgentAdaptersStatus(targetPath, options = {})
56
72
  const includeUserLocal = options.includeUserLocal ?? true;
57
73
  const integrationRootPath = resolveIntegrationRootPath(targetPath, options.integrationRootPath);
58
74
  const guidance = await getGuidanceStatus(targetPath);
75
+ const pinnedVersion = await resolvePinnedVersionOrNull(targetPath);
59
76
  const projectMcp = await inspectProjectMcpSupport(targetPath, {
60
77
  includeUserLocal,
61
78
  integrationRootPath,
79
+ pinnedVersion,
62
80
  });
63
81
  const [codex, claude, cursor] = await Promise.all([
64
- inspectCodexAdapter(targetPath, guidance, projectMcp, {
82
+ inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersion, {
65
83
  includeUserLocal,
66
84
  }),
67
- inspectClaudeAdapter(targetPath, guidance, projectMcp),
68
- inspectCursorAdapter(targetPath, guidance, projectMcp),
85
+ inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVersion),
86
+ inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVersion),
69
87
  ]);
70
88
  return {
71
89
  targetPath,
@@ -80,6 +98,7 @@ export async function installSupportedAgentAdapters(targetPath, options = {}) {
80
98
  const status = await getSupportedAgentAdaptersStatus(targetPath, {
81
99
  integrationRootPath,
82
100
  });
101
+ const pinnedVersion = await resolvePinnedVersion(targetPath);
83
102
  if (!hasReadyVisibilityChain(status.guidance) && options.allowBootstrap !== true) {
84
103
  return {
85
104
  targetPath,
@@ -96,26 +115,41 @@ export async function installSupportedAgentAdapters(targetPath, options = {}) {
96
115
  user: options.user ?? false,
97
116
  devRepoPath: options.devRepoPath ?? null,
98
117
  integrationRootPath,
118
+ pinnedVersion,
99
119
  });
120
+ let appliedActions = actions;
100
121
  if (!dryRun) {
122
+ const newActions = [];
101
123
  for (const action of actions) {
102
124
  if (action.state === "unchanged") {
125
+ newActions.push(action);
103
126
  continue;
104
127
  }
105
- if (action.kind === "ensure_symlink") {
106
- await applySymlinkAction(action);
107
- continue;
108
- }
109
- if (action.kind === "run_command") {
110
- await applyRunCommandAction(action);
111
- continue;
128
+ try {
129
+ if (action.kind === "ensure_symlink") {
130
+ await applySymlinkAction(action);
131
+ }
132
+ else if (action.kind === "run_command") {
133
+ await applyRunCommandAction(action);
134
+ }
135
+ else if (action.command?.[0] === "write_local_hints") {
136
+ await applyLocalHintsAction(action);
137
+ }
138
+ else {
139
+ await applyWriteFileAction(action);
140
+ }
141
+ newActions.push({ ...action, state: "applied" });
112
142
  }
113
- if (action.command?.[0] === "write_local_hints") {
114
- await applyLocalHintsAction(action);
115
- continue;
143
+ catch (error) {
144
+ const errorMessage = error instanceof Error ? error.message : String(error);
145
+ newActions.push({
146
+ ...action,
147
+ state: "blocked",
148
+ summary: `${action.summary} (failed: ${errorMessage})`,
149
+ });
116
150
  }
117
- await applyWriteFileAction(action);
118
151
  }
152
+ appliedActions = newActions;
119
153
  }
120
154
  return {
121
155
  targetPath,
@@ -129,7 +163,7 @@ export async function installSupportedAgentAdapters(targetPath, options = {}) {
129
163
  requestedAgents,
130
164
  guidance: status.guidance,
131
165
  adapters: status.adapters,
132
- actions,
166
+ actions: appliedActions,
133
167
  };
134
168
  }
135
169
  export async function inspectProjectLifecycleCompatibility(targetPath) {
@@ -285,7 +319,7 @@ export function renderCursorRule() {
285
319
  "",
286
320
  ].join("\n");
287
321
  }
288
- export function renderClaudeOnboardCommand() {
322
+ export function renderClaudeOnboardCommand(pinnedVersion) {
289
323
  return [
290
324
  "---",
291
325
  "name: gdh:onboard",
@@ -298,17 +332,17 @@ export function renderClaudeOnboardCommand() {
298
332
  " - AskUserQuestion",
299
333
  "---",
300
334
  "<objective>",
301
- "Finish the first-time GDH handoff after `gdh setup` and turn the generated GDH surface into a trustworthy starting point for real work.",
335
+ `Finish the first-time GDH handoff after \`npx -y @skillcap/gdh@${pinnedVersion} setup\` and turn the generated GDH surface into a trustworthy starting point for real work.`,
302
336
  "</objective>",
303
337
  "",
304
338
  "<process>",
305
339
  "Follow this order:",
306
340
  "",
307
- "1. Confirm the selected target and current GDH state with `gdh status`.",
341
+ `1. Confirm the selected target and current GDH state with \`npx -y @skillcap/gdh@${pinnedVersion} status\`.`,
308
342
  "2. Read `.gdh/project.yaml`, `docs/agent/README.md`, and `docs/agent/00-gdh-onboarding.md` before asking for facts GDH already knows.",
309
- "3. Check immediate readiness with `gdh guidance status` and `gdh target prepare --dry-run`.",
310
- "4. If authoring is available, run `gdh authoring check`; if LSP matters, run `gdh lsp status`.",
311
- "5. Inspect `gdh run-config list` and `gdh verification-scenario list` and call out anything draft, missing, or blocked.",
343
+ `3. Check immediate readiness with \`npx -y @skillcap/gdh@${pinnedVersion} guidance status\` and \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\`.`,
344
+ `4. If authoring is available, run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\`; if LSP matters, run \`npx -y @skillcap/gdh@${pinnedVersion} lsp status\`.`,
345
+ `5. Inspect \`npx -y @skillcap/gdh@${pinnedVersion} run-config list\` and \`npx -y @skillcap/gdh@${pinnedVersion} verification-scenario list\` and call out anything draft, missing, or blocked.`,
312
346
  "6. If GDH reports `godot_editor_not_configured`, explain that the Godot binary path is machine-local and offer to record `.gdh-state/local-paths.json` `godotEditorBinPath` once the human provides the local path.",
313
347
  "7. Ask the human only for narrow unresolved environment or target facts.",
314
348
  "8. End with a concise readiness summary and the exact next development step.",
@@ -318,11 +352,12 @@ export function renderClaudeOnboardCommand() {
318
352
  "- Do not start editing code during `/gdh-onboard` unless the human explicitly asks.",
319
353
  "- Prefer GDH structured surfaces over repo guesswork.",
320
354
  "- Keep the output short, operational, and specific.",
355
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
321
356
  "</rules>",
322
357
  "",
323
358
  ].join("\n");
324
359
  }
325
- export function renderCodexOnboardSkill() {
360
+ export function renderCodexOnboardSkill(pinnedVersion) {
326
361
  return [
327
362
  "---",
328
363
  'name: "gdh-onboard"',
@@ -342,20 +377,20 @@ export function renderCodexOnboardSkill() {
342
377
  "</codex_skill_adapter>",
343
378
  "",
344
379
  "<objective>",
345
- "Finish the first-time GDH handoff after `gdh setup` and turn the generated GDH surface into a trustworthy starting point for real work.",
380
+ `Finish the first-time GDH handoff after \`npx -y @skillcap/gdh@${pinnedVersion} setup\` and turn the generated GDH surface into a trustworthy starting point for real work.`,
346
381
  "</objective>",
347
382
  "",
348
383
  "<process>",
349
384
  "Follow this order:",
350
385
  "",
351
- "- run `gdh status`",
386
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} status\``,
352
387
  "- inspect `.gdh/project.yaml`, `docs/agent/README.md`, and `docs/agent/00-gdh-onboarding.md`",
353
- "- run `gdh guidance status`",
354
- "- run `gdh target prepare --dry-run`",
355
- "- if authoring is available, run `gdh authoring check`",
356
- "- if LSP matters, run `gdh lsp status`",
357
- "- run `gdh run-config list`",
358
- "- run `gdh verification-scenario list`",
388
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} guidance status\``,
389
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\``,
390
+ `- if authoring is available, run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\``,
391
+ `- if LSP matters, run \`npx -y @skillcap/gdh@${pinnedVersion} lsp status\``,
392
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} run-config list\``,
393
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verification-scenario list\``,
359
394
  "- if GDH reports `godot_editor_not_configured`, explain that the Godot binary path is machine-local and offer to record `.gdh-state/local-paths.json` `godotEditorBinPath` once the user provides it",
360
395
  "- ask only for narrow unresolved environment or target facts",
361
396
  "- finish with a concise readiness summary and the exact next development step",
@@ -365,11 +400,12 @@ export function renderCodexOnboardSkill() {
365
400
  "- Do not start editing code during `/gdh-onboard` unless the human explicitly asks.",
366
401
  "- Prefer GDH structured surfaces over repo guesswork.",
367
402
  "- Keep output short, operational, and specific.",
403
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
368
404
  "</rules>",
369
405
  "",
370
406
  ].join("\n");
371
407
  }
372
- export function renderCursorOnboardSkill() {
408
+ export function renderCursorOnboardSkill(pinnedVersion) {
373
409
  return [
374
410
  "---",
375
411
  "name: gdh-onboard",
@@ -387,20 +423,20 @@ export function renderCursorOnboardSkill() {
387
423
  "</cursor_skill_adapter>",
388
424
  "",
389
425
  "<objective>",
390
- "Finish the first-time GDH handoff after `gdh setup` and turn the generated GDH surface into a trustworthy starting point for real work.",
426
+ `Finish the first-time GDH handoff after \`npx -y @skillcap/gdh@${pinnedVersion} setup\` and turn the generated GDH surface into a trustworthy starting point for real work.`,
391
427
  "</objective>",
392
428
  "",
393
429
  "<process>",
394
430
  "Follow this order:",
395
431
  "",
396
- "- run `gdh status`",
432
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} status\``,
397
433
  "- inspect `.gdh/project.yaml`, `docs/agent/README.md`, and `docs/agent/00-gdh-onboarding.md`",
398
- "- run `gdh guidance status`",
399
- "- run `gdh target prepare --dry-run`",
400
- "- if authoring is available, run `gdh authoring check`",
401
- "- if LSP matters, run `gdh lsp status`",
402
- "- run `gdh run-config list`",
403
- "- run `gdh verification-scenario list`",
434
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} guidance status\``,
435
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\``,
436
+ `- if authoring is available, run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\``,
437
+ `- if LSP matters, run \`npx -y @skillcap/gdh@${pinnedVersion} lsp status\``,
438
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} run-config list\``,
439
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verification-scenario list\``,
404
440
  "- if GDH reports `godot_editor_not_configured`, explain that the Godot binary path is machine-local and offer to record `.gdh-state/local-paths.json` `godotEditorBinPath` once the user provides it",
405
441
  "- ask only for narrow unresolved environment or target facts",
406
442
  "- finish with a concise readiness summary and the exact next development step",
@@ -410,12 +446,13 @@ export function renderCursorOnboardSkill() {
410
446
  "- Do not start editing code during `/gdh-onboard` unless the human explicitly asks.",
411
447
  "- Prefer GDH structured surfaces over repo guesswork.",
412
448
  "- Keep output short, operational, and specific.",
449
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
413
450
  "</rules>",
414
451
  "",
415
452
  ].join("\n");
416
453
  }
417
454
  // --- gdh-status skill renders ---
418
- export function renderClaudeStatusCommand() {
455
+ export function renderClaudeStatusCommand(pinnedVersion) {
419
456
  return [
420
457
  "---",
421
458
  "name: gdh:status",
@@ -433,8 +470,8 @@ export function renderClaudeStatusCommand() {
433
470
  "<process>",
434
471
  "Follow this order:",
435
472
  "",
436
- "1. Run `gdh status` and explain each readiness field.",
437
- "2. Check if migration is needed with `gdh migrate`.",
473
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field.`,
474
+ `2. Check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\`.`,
438
475
  "3. Surface any degraded or unavailable capabilities.",
439
476
  "4. Suggest the most productive next step based on current state.",
440
477
  "</process>",
@@ -443,11 +480,12 @@ export function renderClaudeStatusCommand() {
443
480
  "- Do not start editing code.",
444
481
  "- Prefer structured GDH surfaces over repo guesswork.",
445
482
  "- Keep output short and operational.",
483
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
446
484
  "</rules>",
447
485
  "",
448
486
  ].join("\n");
449
487
  }
450
- export function renderCodexStatusSkill() {
488
+ export function renderCodexStatusSkill(pinnedVersion) {
451
489
  return [
452
490
  "---",
453
491
  'name: "gdh-status"',
@@ -473,8 +511,8 @@ export function renderCodexStatusSkill() {
473
511
  "<process>",
474
512
  "Follow this order:",
475
513
  "",
476
- "- run `gdh status` and explain each readiness field",
477
- "- check if migration is needed with `gdh migrate`",
514
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field`,
515
+ `- check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\``,
478
516
  "- surface any degraded or unavailable capabilities",
479
517
  "- suggest the most productive next step based on current state",
480
518
  "</process>",
@@ -483,11 +521,12 @@ export function renderCodexStatusSkill() {
483
521
  "- Do not start editing code.",
484
522
  "- Prefer structured GDH surfaces over repo guesswork.",
485
523
  "- Keep output short and operational.",
524
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
486
525
  "</rules>",
487
526
  "",
488
527
  ].join("\n");
489
528
  }
490
- export function renderCursorStatusSkill() {
529
+ export function renderCursorStatusSkill(pinnedVersion) {
491
530
  return [
492
531
  "---",
493
532
  "name: gdh-status",
@@ -511,8 +550,8 @@ export function renderCursorStatusSkill() {
511
550
  "<process>",
512
551
  "Follow this order:",
513
552
  "",
514
- "- run `gdh status` and explain each readiness field",
515
- "- check if migration is needed with `gdh migrate`",
553
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field`,
554
+ `- check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\``,
516
555
  "- surface any degraded or unavailable capabilities",
517
556
  "- suggest the most productive next step based on current state",
518
557
  "</process>",
@@ -521,12 +560,123 @@ export function renderCursorStatusSkill() {
521
560
  "- Do not start editing code.",
522
561
  "- Prefer structured GDH surfaces over repo guesswork.",
523
562
  "- Keep output short and operational.",
563
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
564
+ "</rules>",
565
+ "",
566
+ ].join("\n");
567
+ }
568
+ // --- gdh-scan skill renders ---
569
+ export function renderClaudeScanCommand(pinnedVersion) {
570
+ return [
571
+ "---",
572
+ "name: gdh:scan",
573
+ "description: Refresh inventory cache and explain persistence effect",
574
+ "allowed-tools:",
575
+ " - Read",
576
+ " - Grep",
577
+ " - Glob",
578
+ " - Bash",
579
+ "---",
580
+ "<objective>",
581
+ "Refresh the inventory cache and explain the persistence effect of the scan.",
582
+ "</objective>",
583
+ "",
584
+ "<process>",
585
+ "Follow this order:",
586
+ "",
587
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} scan\` and parse the \`persisted\` field from the envelope.`,
588
+ "2. Check the `mode` field: `create`, `unchanged`, `overwrite`, or `null`.",
589
+ "3. Explain the persistence effect; if `null`, the target is not onboarded and no disk write occurred.",
590
+ "</process>",
591
+ "",
592
+ "<rules>",
593
+ "- Do not edit the persisted inventory directly.",
594
+ "- Prefer the cached inventory for status reads; re-run scan only when freshness is in question.",
595
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
596
+ "</rules>",
597
+ "",
598
+ ].join("\n");
599
+ }
600
+ export function renderCodexScanSkill(pinnedVersion) {
601
+ return [
602
+ "---",
603
+ 'name: "gdh-scan"',
604
+ 'description: "Refresh inventory cache and explain persistence effect"',
605
+ "metadata:",
606
+ ' short-description: "Refresh inventory cache and explain persistence effect"',
607
+ "---",
608
+ "",
609
+ "<codex_skill_adapter>",
610
+ "## Invocation",
611
+ "- This skill is invoked when the user says `/gdh-scan` or mentions `$gdh-scan`.",
612
+ "- Treat any extra user text as additional scan context.",
613
+ "",
614
+ "## User questions",
615
+ "- Ask only narrow follow-up questions when GDH cannot infer the missing fact safely.",
616
+ "- If structured user-input tooling is unavailable, ask concise plain-text questions instead.",
617
+ "</codex_skill_adapter>",
618
+ "",
619
+ "<objective>",
620
+ "Refresh the inventory cache and explain the persistence effect of the scan.",
621
+ "</objective>",
622
+ "",
623
+ "<process>",
624
+ "Follow this order:",
625
+ "",
626
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} scan\``,
627
+ "- parse the `persisted` field from the envelope",
628
+ "- check `mode`: `create`, `unchanged`, `overwrite`, or `null`",
629
+ "- explain the persistence effect; if `null`, explain the target is not onboarded",
630
+ "</process>",
631
+ "",
632
+ "<rules>",
633
+ "- Do not edit the persisted inventory directly.",
634
+ "- Prefer the cached inventory for status reads; re-run scan only when freshness is in question.",
635
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
636
+ "</rules>",
637
+ "",
638
+ ].join("\n");
639
+ }
640
+ export function renderCursorScanSkill(pinnedVersion) {
641
+ return [
642
+ "---",
643
+ "name: gdh-scan",
644
+ 'description: "Refresh inventory cache and explain persistence effect"',
645
+ "---",
646
+ "",
647
+ "<cursor_skill_adapter>",
648
+ "## Invocation",
649
+ "- This skill is invoked when the user says `/gdh-scan` or mentions `gdh-scan`.",
650
+ "- Treat any extra user text as additional scan context.",
651
+ "",
652
+ "## User questions",
653
+ "- Ask only narrow follow-up questions when GDH cannot infer the missing fact safely.",
654
+ "- Keep questions conversational and concise.",
655
+ "</cursor_skill_adapter>",
656
+ "",
657
+ "<objective>",
658
+ "Refresh the inventory cache and explain the persistence effect of the scan.",
659
+ "</objective>",
660
+ "",
661
+ "<process>",
662
+ "Follow this order:",
663
+ "",
664
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} scan\``,
665
+ "- parse the `persisted` field from the envelope",
666
+ "- check `mode`: `create`, `unchanged`, `overwrite`, or `null`",
667
+ "- explain the persistence effect; if `null`, explain the target is not onboarded",
668
+ "</process>",
669
+ "",
670
+ "<rules>",
671
+ "- Do not edit the persisted inventory directly.",
672
+ "- Prefer the cached inventory for status reads; re-run scan only when freshness is in question.",
673
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
524
674
  "</rules>",
525
675
  "",
526
676
  ].join("\n");
527
677
  }
528
678
  // --- gdh-migrate skill renders ---
529
- export function renderClaudeMigrateCommand() {
679
+ export function renderClaudeMigrateCommand(pinnedVersion) {
530
680
  return [
531
681
  "---",
532
682
  "name: gdh:migrate",
@@ -545,10 +695,10 @@ export function renderClaudeMigrateCommand() {
545
695
  "<process>",
546
696
  "Follow this order:",
547
697
  "",
548
- "1. Run `gdh migrate` to preview pending migrations.",
698
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations.`,
549
699
  "2. Explain what each migration step will change and why.",
550
- "3. Offer to run `gdh migrate --apply` if the user approves.",
551
- "4. After apply, run `gdh status` to verify the migration succeeded.",
700
+ `3. Offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves.`,
701
+ `4. After apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded.`,
552
702
  "</process>",
553
703
  "",
554
704
  "<rules>",
@@ -559,7 +709,7 @@ export function renderClaudeMigrateCommand() {
559
709
  "",
560
710
  ].join("\n");
561
711
  }
562
- export function renderCodexMigrateSkill() {
712
+ export function renderCodexMigrateSkill(pinnedVersion) {
563
713
  return [
564
714
  "---",
565
715
  'name: "gdh-migrate"',
@@ -585,10 +735,10 @@ export function renderCodexMigrateSkill() {
585
735
  "<process>",
586
736
  "Follow this order:",
587
737
  "",
588
- "- run `gdh migrate` to preview pending migrations",
738
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations`,
589
739
  "- explain what each migration step will change and why",
590
- "- offer to run `gdh migrate --apply` if the user approves",
591
- "- after apply, run `gdh status` to verify the migration succeeded",
740
+ `- offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves`,
741
+ `- after apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded`,
592
742
  "</process>",
593
743
  "",
594
744
  "<rules>",
@@ -599,7 +749,7 @@ export function renderCodexMigrateSkill() {
599
749
  "",
600
750
  ].join("\n");
601
751
  }
602
- export function renderCursorMigrateSkill() {
752
+ export function renderCursorMigrateSkill(pinnedVersion) {
603
753
  return [
604
754
  "---",
605
755
  "name: gdh-migrate",
@@ -623,10 +773,10 @@ export function renderCursorMigrateSkill() {
623
773
  "<process>",
624
774
  "Follow this order:",
625
775
  "",
626
- "- run `gdh migrate` to preview pending migrations",
776
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations`,
627
777
  "- explain what each migration step will change and why",
628
- "- offer to run `gdh migrate --apply` if the user approves",
629
- "- after apply, run `gdh status` to verify the migration succeeded",
778
+ `- offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves`,
779
+ `- after apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded`,
630
780
  "</process>",
631
781
  "",
632
782
  "<rules>",
@@ -637,8 +787,135 @@ export function renderCursorMigrateSkill() {
637
787
  "",
638
788
  ].join("\n");
639
789
  }
790
+ // --- gdh-update skill renders (Phase 13 SELF-01) ---
791
+ //
792
+ // D-10 invariant: every rendered body shells out to the LITERAL string
793
+ // `@skillcap/gdh@latest` in every npx line (dry-run, apply, verify drift).
794
+ // The `pinnedVersion` parameter is accepted for planSkillInstallAction
795
+ // signature symmetry with every other renderer, but MUST NOT be interpolated
796
+ // into the shellout — running the OLD pinned CLI (potentially pre-Phase-12
797
+ // and lacking bumpAndRebakePin entirely) to perform its own update is the
798
+ // failure mode Phase 13 exists to close. Check 44 in scripts/validate-docs.mjs
799
+ // enforces both the @latest presence AND the @${pinnedVersion} absence.
800
+ //
801
+ // D-11: preview-then-apply flow WITHOUT a confirmation gate — the human's
802
+ // original intent ("update GDH") IS the approval. No AskUserQuestion for
803
+ // Claude; no `## User questions` H2 for Codex/Cursor. Differs from /gdh-migrate
804
+ // which DOES gate on explicit approval.
805
+ //
806
+ // D-12: accept an optional positional version forwarded verbatim to the CLI.
807
+ // `/gdh-update` = latest; `/gdh-update 0.6.0` = pin that version.
808
+ //
809
+ // D-13: the three rendered skill files are EXCLUDED from VERIFY_DRIFT_SCANNED_FILES
810
+ // because @latest is not a semver literal — the baked-version scanner would
811
+ // permanently report no_baked_version. See rationale comments adjacent to the
812
+ // VERIFY_DRIFT_SCANNED_FILES declaration in packages/cli/src/index.ts.
813
+ export function renderClaudeUpdateCommand(_pinnedVersion) {
814
+ return [
815
+ "---",
816
+ "name: gdh:update",
817
+ "description: Update GDH to npm latest (bump pinned version + re-bake managed surfaces)",
818
+ "allowed-tools:",
819
+ " - Read",
820
+ " - Bash",
821
+ "---",
822
+ "<objective>",
823
+ "Update this project's GDH pinning to npm latest and re-bake every managed surface at the new pin.",
824
+ "Treat any positional argument (e.g., `/gdh-update 0.6.0`) as an explicit version; otherwise default to npm latest.",
825
+ "</objective>",
826
+ "",
827
+ "<process>",
828
+ "Follow this order:",
829
+ "",
830
+ "1. Preview: run `npx -y @skillcap/gdh@latest self-update [version] --dry-run` (omit `[version]` to preview against npm latest).",
831
+ "2. Surface the planned version delta and re-bake action count to the human conversationally.",
832
+ "3. Apply: run `npx -y @skillcap/gdh@latest self-update [version]` (omit `[version]` to apply against npm latest; forward whatever positional the user passed to `/gdh-update`).",
833
+ "4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
834
+ "</process>",
835
+ "",
836
+ "<rules>",
837
+ "- Do NOT ask the user to confirm apply — their original intent (\"update GDH\") IS the approval.",
838
+ "- Bake literal `@latest` in the shellout so the new CLI performs the update, not the old one.",
839
+ "- If `self-update` returns `rolled_back` or `blocked`, surface the failure reason and stop (do not retry).",
840
+ "- If `self-update` returns `skipped_dev_mode`, explain that dev-from-source mode bypasses pinning; no action needed.",
841
+ "</rules>",
842
+ "",
843
+ ].join("\n");
844
+ }
845
+ export function renderCodexUpdateSkill(_pinnedVersion) {
846
+ return [
847
+ "---",
848
+ 'name: "gdh-update"',
849
+ 'description: "Update GDH to npm latest (bump pinned version + re-bake managed surfaces)"',
850
+ "metadata:",
851
+ ' short-description: "Update GDH to npm latest"',
852
+ "---",
853
+ "",
854
+ "<codex_skill_adapter>",
855
+ "## Invocation",
856
+ "- This skill is invoked when the user says `/gdh-update` or mentions `$gdh-update`.",
857
+ "- Treat any positional argument (e.g., `/gdh-update 0.6.0`) as an explicit version; otherwise default to npm latest.",
858
+ "</codex_skill_adapter>",
859
+ "",
860
+ "<objective>",
861
+ "Update this project's GDH pinning to npm latest and re-bake every managed surface at the new pin.",
862
+ "</objective>",
863
+ "",
864
+ "<process>",
865
+ "Follow this order:",
866
+ "",
867
+ "1. Preview: run `npx -y @skillcap/gdh@latest self-update [version] --dry-run` (omit `[version]` to preview against npm latest).",
868
+ "2. Surface the planned version delta and re-bake action count to the human conversationally.",
869
+ "3. Apply: run `npx -y @skillcap/gdh@latest self-update [version]` (omit `[version]` to apply against npm latest; forward whatever positional the user passed to `/gdh-update`).",
870
+ "4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
871
+ "</process>",
872
+ "",
873
+ "<rules>",
874
+ "- Do NOT ask the user to confirm apply — their original intent (\"update GDH\") IS the approval.",
875
+ "- Bake literal `@latest` in the shellout so the new CLI performs the update, not the old one.",
876
+ "- If `self-update` returns `rolled_back` or `blocked`, surface the failure reason and stop (do not retry).",
877
+ "- If `self-update` returns `skipped_dev_mode`, explain that dev-from-source mode bypasses pinning; no action needed.",
878
+ "</rules>",
879
+ "",
880
+ ].join("\n");
881
+ }
882
+ export function renderCursorUpdateSkill(_pinnedVersion) {
883
+ return [
884
+ "---",
885
+ "name: gdh-update",
886
+ 'description: "Update GDH to npm latest (bump pinned version + re-bake managed surfaces)"',
887
+ "---",
888
+ "",
889
+ "<cursor_skill_adapter>",
890
+ "## Invocation",
891
+ "- This skill is invoked when the user says `/gdh-update` or mentions `gdh-update`.",
892
+ "- Treat any positional argument (e.g., `gdh-update 0.6.0`) as an explicit version; otherwise default to npm latest.",
893
+ "</cursor_skill_adapter>",
894
+ "",
895
+ "<objective>",
896
+ "Update this project's GDH pinning to npm latest and re-bake every managed surface at the new pin.",
897
+ "</objective>",
898
+ "",
899
+ "<process>",
900
+ "Follow this order:",
901
+ "",
902
+ "1. Preview: run `npx -y @skillcap/gdh@latest self-update [version] --dry-run` (omit `[version]` to preview against npm latest).",
903
+ "2. Surface the planned version delta and re-bake action count to the human conversationally.",
904
+ "3. Apply: run `npx -y @skillcap/gdh@latest self-update [version]` (omit `[version]` to apply against npm latest; forward whatever positional the user passed to `/gdh-update`).",
905
+ "4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
906
+ "</process>",
907
+ "",
908
+ "<rules>",
909
+ "- Do NOT ask the user to confirm apply — their original intent (\"update GDH\") IS the approval.",
910
+ "- Bake literal `@latest` in the shellout so the new CLI performs the update, not the old one.",
911
+ "- If `self-update` returns `rolled_back` or `blocked`, surface the failure reason and stop (do not retry).",
912
+ "- If `self-update` returns `skipped_dev_mode`, explain that dev-from-source mode bypasses pinning; no action needed.",
913
+ "</rules>",
914
+ "",
915
+ ].join("\n");
916
+ }
640
917
  // --- gdh-check skill renders ---
641
- export function renderClaudeCheckCommand() {
918
+ export function renderClaudeCheckCommand(pinnedVersion) {
642
919
  return [
643
920
  "---",
644
921
  "name: gdh:check",
@@ -656,7 +933,7 @@ export function renderClaudeCheckCommand() {
656
933
  "<process>",
657
934
  "Follow this order:",
658
935
  "",
659
- "1. Run `gdh authoring check`.",
936
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\`.`,
660
937
  "2. Explain each diagnostic finding with severity and provenance.",
661
938
  "3. Surface any import-state caveats or editor-side warnings.",
662
939
  "4. If issues found, suggest concrete remediation steps.",
@@ -670,7 +947,7 @@ export function renderClaudeCheckCommand() {
670
947
  "",
671
948
  ].join("\n");
672
949
  }
673
- export function renderCodexCheckSkill() {
950
+ export function renderCodexCheckSkill(pinnedVersion) {
674
951
  return [
675
952
  "---",
676
953
  'name: "gdh-check"',
@@ -696,7 +973,7 @@ export function renderCodexCheckSkill() {
696
973
  "<process>",
697
974
  "Follow this order:",
698
975
  "",
699
- "- run `gdh authoring check`",
976
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\``,
700
977
  "- explain each diagnostic finding with severity and provenance",
701
978
  "- surface any import-state caveats or editor-side warnings",
702
979
  "- if issues found, suggest concrete remediation steps",
@@ -710,7 +987,7 @@ export function renderCodexCheckSkill() {
710
987
  "",
711
988
  ].join("\n");
712
989
  }
713
- export function renderCursorCheckSkill() {
990
+ export function renderCursorCheckSkill(pinnedVersion) {
714
991
  return [
715
992
  "---",
716
993
  "name: gdh-check",
@@ -734,7 +1011,7 @@ export function renderCursorCheckSkill() {
734
1011
  "<process>",
735
1012
  "Follow this order:",
736
1013
  "",
737
- "- run `gdh authoring check`",
1014
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\``,
738
1015
  "- explain each diagnostic finding with severity and provenance",
739
1016
  "- surface any import-state caveats or editor-side warnings",
740
1017
  "- if issues found, suggest concrete remediation steps",
@@ -749,7 +1026,7 @@ export function renderCursorCheckSkill() {
749
1026
  ].join("\n");
750
1027
  }
751
1028
  // --- gdh-prepare skill renders ---
752
- export function renderClaudePrepareCommand() {
1029
+ export function renderClaudePrepareCommand(pinnedVersion) {
753
1030
  return [
754
1031
  "---",
755
1032
  "name: gdh:prepare",
@@ -768,11 +1045,11 @@ export function renderClaudePrepareCommand() {
768
1045
  "<process>",
769
1046
  "Follow this order:",
770
1047
  "",
771
- "1. Run `gdh target prepare --dry-run` first to preview planned actions.",
1048
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\` first to preview planned actions.`,
772
1049
  "2. Explain what hydration and import refresh will do.",
773
- "3. If the user approves, run `gdh target prepare`.",
1050
+ `3. If the user approves, run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare\`.`,
774
1051
  "4. If a `--source-target` is relevant, explain when and why to use it.",
775
- "5. Verify preparation succeeded with `gdh status`.",
1052
+ `5. Verify preparation succeeded with \`npx -y @skillcap/gdh@${pinnedVersion} status\`.`,
776
1053
  "</process>",
777
1054
  "",
778
1055
  "<rules>",
@@ -783,7 +1060,7 @@ export function renderClaudePrepareCommand() {
783
1060
  "",
784
1061
  ].join("\n");
785
1062
  }
786
- export function renderCodexPrepareSkill() {
1063
+ export function renderCodexPrepareSkill(pinnedVersion) {
787
1064
  return [
788
1065
  "---",
789
1066
  'name: "gdh-prepare"',
@@ -809,11 +1086,11 @@ export function renderCodexPrepareSkill() {
809
1086
  "<process>",
810
1087
  "Follow this order:",
811
1088
  "",
812
- "- run `gdh target prepare --dry-run` first to preview planned actions",
1089
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\` first to preview planned actions`,
813
1090
  "- explain what hydration and import refresh will do",
814
- "- if the user approves, run `gdh target prepare`",
1091
+ `- if the user approves, run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare\``,
815
1092
  "- if a `--source-target` is relevant, explain when and why to use it",
816
- "- verify preparation succeeded with `gdh status`",
1093
+ `- verify preparation succeeded with \`npx -y @skillcap/gdh@${pinnedVersion} status\``,
817
1094
  "</process>",
818
1095
  "",
819
1096
  "<rules>",
@@ -824,7 +1101,7 @@ export function renderCodexPrepareSkill() {
824
1101
  "",
825
1102
  ].join("\n");
826
1103
  }
827
- export function renderCursorPrepareSkill() {
1104
+ export function renderCursorPrepareSkill(pinnedVersion) {
828
1105
  return [
829
1106
  "---",
830
1107
  "name: gdh-prepare",
@@ -848,11 +1125,11 @@ export function renderCursorPrepareSkill() {
848
1125
  "<process>",
849
1126
  "Follow this order:",
850
1127
  "",
851
- "- run `gdh target prepare --dry-run` first to preview planned actions",
1128
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\` first to preview planned actions`,
852
1129
  "- explain what hydration and import refresh will do",
853
- "- if the user approves, run `gdh target prepare`",
1130
+ `- if the user approves, run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare\``,
854
1131
  "- if a `--source-target` is relevant, explain when and why to use it",
855
- "- verify preparation succeeded with `gdh status`",
1132
+ `- verify preparation succeeded with \`npx -y @skillcap/gdh@${pinnedVersion} status\``,
856
1133
  "</process>",
857
1134
  "",
858
1135
  "<rules>",
@@ -864,7 +1141,7 @@ export function renderCursorPrepareSkill() {
864
1141
  ].join("\n");
865
1142
  }
866
1143
  // --- gdh-verify skill renders ---
867
- export function renderClaudeVerifyCommand() {
1144
+ export function renderClaudeVerifyCommand(pinnedVersion) {
868
1145
  return [
869
1146
  "---",
870
1147
  "name: gdh:verify",
@@ -883,8 +1160,8 @@ export function renderClaudeVerifyCommand() {
883
1160
  "Follow this order:",
884
1161
  "",
885
1162
  "1. Identify changed files from git diff or user input.",
886
- "2. Run `gdh verify recommend` with those files to get recommended validation kinds.",
887
- "3. Run `gdh verify done` with performed validations to check done eligibility.",
1163
+ `2. Run \`npx -y @skillcap/gdh@${pinnedVersion} verify recommend\` with those files to get recommended validation kinds.`,
1164
+ `3. Run \`npx -y @skillcap/gdh@${pinnedVersion} verify done\` with performed validations to check done eligibility.`,
888
1165
  "4. Summarize gaps between recommended and performed validation.",
889
1166
  "5. Suggest specific next verification steps.",
890
1167
  "</process>",
@@ -897,7 +1174,7 @@ export function renderClaudeVerifyCommand() {
897
1174
  "",
898
1175
  ].join("\n");
899
1176
  }
900
- export function renderCodexVerifySkill() {
1177
+ export function renderCodexVerifySkill(pinnedVersion) {
901
1178
  return [
902
1179
  "---",
903
1180
  'name: "gdh-verify"',
@@ -924,8 +1201,8 @@ export function renderCodexVerifySkill() {
924
1201
  "Follow this order:",
925
1202
  "",
926
1203
  "- identify changed files from git diff or user input",
927
- "- run `gdh verify recommend` with those files to get recommended validation kinds",
928
- "- run `gdh verify done` with performed validations to check done eligibility",
1204
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verify recommend\` with those files to get recommended validation kinds`,
1205
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verify done\` with performed validations to check done eligibility`,
929
1206
  "- summarize gaps between recommended and performed validation",
930
1207
  "- suggest specific next verification steps",
931
1208
  "</process>",
@@ -938,7 +1215,7 @@ export function renderCodexVerifySkill() {
938
1215
  "",
939
1216
  ].join("\n");
940
1217
  }
941
- export function renderCursorVerifySkill() {
1218
+ export function renderCursorVerifySkill(pinnedVersion) {
942
1219
  return [
943
1220
  "---",
944
1221
  "name: gdh-verify",
@@ -963,8 +1240,8 @@ export function renderCursorVerifySkill() {
963
1240
  "Follow this order:",
964
1241
  "",
965
1242
  "- identify changed files from git diff or user input",
966
- "- run `gdh verify recommend` with those files to get recommended validation kinds",
967
- "- run `gdh verify done` with performed validations to check done eligibility",
1243
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verify recommend\` with those files to get recommended validation kinds`,
1244
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verify done\` with performed validations to check done eligibility`,
968
1245
  "- summarize gaps between recommended and performed validation",
969
1246
  "- suggest specific next verification steps",
970
1247
  "</process>",
@@ -980,7 +1257,9 @@ export function renderCursorVerifySkill() {
980
1257
  async function inspectProjectMcpSupport(targetPath, options) {
981
1258
  const projectConfig = await readProjectConfig(targetPath);
982
1259
  const enabled = resolveProjectMcpEnabled(projectConfig);
983
- const launcherContent = renderManagedMcpLauncher();
1260
+ const launcherContent = options.pinnedVersion === null
1261
+ ? null
1262
+ : renderManagedMcpLauncher(options.pinnedVersion);
984
1263
  const managedMcpEntry = buildManagedMcpServerEntry({
985
1264
  targetPath,
986
1265
  integrationRootPath: options.integrationRootPath,
@@ -1090,6 +1369,13 @@ function inspectLauncherFile(launcherSource, expectedContent, enabled, bootstrap
1090
1369
  summary: "The managed GDH MCP launcher is disabled for this target.",
1091
1370
  };
1092
1371
  }
1372
+ if (expectedContent === null) {
1373
+ return {
1374
+ present: launcherSource !== null,
1375
+ state: "missing",
1376
+ summary: "The managed GDH MCP launcher is pending project onboard: no `gdh_version` is pinned yet, so there is no expected launcher content to compare against.",
1377
+ };
1378
+ }
1093
1379
  if (launcherSource === null) {
1094
1380
  return {
1095
1381
  present: false,
@@ -1182,6 +1468,20 @@ async function inspectCodexRegistration(targetPath, enabled, codexServerName, co
1182
1468
  };
1183
1469
  }
1184
1470
  function inspectCodexSkillSurface(targetPath, relativePath, content, expectedContent, skillName) {
1471
+ if (expectedContent === null) {
1472
+ return [
1473
+ createSurfaceStatus({
1474
+ kind: "skill_file",
1475
+ scope: "repo",
1476
+ targetPath,
1477
+ relativePath,
1478
+ present: content !== null,
1479
+ state: "missing",
1480
+ summary: `Codex ${skillName} skill cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
1481
+ version: null,
1482
+ }),
1483
+ ];
1484
+ }
1185
1485
  return [
1186
1486
  createSurfaceStatus({
1187
1487
  kind: "skill_file",
@@ -1204,6 +1504,20 @@ function inspectCodexSkillSurface(targetPath, relativePath, content, expectedCon
1204
1504
  ];
1205
1505
  }
1206
1506
  function inspectClaudeCommandSurface(targetPath, relativePath, content, expectedContent, commandName) {
1507
+ if (expectedContent === null) {
1508
+ return [
1509
+ createSurfaceStatus({
1510
+ kind: "command_file",
1511
+ scope: "repo",
1512
+ targetPath,
1513
+ relativePath,
1514
+ present: content !== null,
1515
+ state: "missing",
1516
+ summary: `Claude ${commandName} command cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
1517
+ version: null,
1518
+ }),
1519
+ ];
1520
+ }
1207
1521
  return [
1208
1522
  createSurfaceStatus({
1209
1523
  kind: "command_file",
@@ -1226,6 +1540,20 @@ function inspectClaudeCommandSurface(targetPath, relativePath, content, expected
1226
1540
  ];
1227
1541
  }
1228
1542
  function inspectCursorSkillSurface(targetPath, relativePath, content, expectedContent, skillName) {
1543
+ if (expectedContent === null) {
1544
+ return [
1545
+ createSurfaceStatus({
1546
+ kind: "skill_file",
1547
+ scope: "repo",
1548
+ targetPath,
1549
+ relativePath,
1550
+ present: content !== null,
1551
+ state: "missing",
1552
+ summary: `Cursor ${skillName} skill cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
1553
+ version: null,
1554
+ }),
1555
+ ];
1556
+ }
1229
1557
  return [
1230
1558
  createSurfaceStatus({
1231
1559
  kind: "skill_file",
@@ -1247,14 +1575,28 @@ function inspectCursorSkillSurface(targetPath, relativePath, content, expectedCo
1247
1575
  }),
1248
1576
  ];
1249
1577
  }
1250
- async function inspectCodexAdapter(targetPath, guidance, projectMcp, options) {
1578
+ async function inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersion, options) {
1251
1579
  const codexSkillPath = path.join(targetPath, CODEX_ONBOARD_SKILL_RELATIVE_PATH);
1252
1580
  const codexSkillContent = await fs.readFile(codexSkillPath, "utf8").catch(() => null);
1253
1581
  const codexStatusContent = await fs.readFile(path.join(targetPath, CODEX_STATUS_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1254
1582
  const codexMigrateContent = await fs.readFile(path.join(targetPath, CODEX_MIGRATE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1583
+ // Phase 13 Plan 13-03 deliverable — /gdh-update skill for Codex. Inspection
1584
+ // wiring is required so planSkillInstallAction can see the surface state;
1585
+ // otherwise the planner returns `unchanged` for a missing file and install
1586
+ // becomes a no-op (Plan 13-05 integration-test Rule 2 fix).
1587
+ const codexUpdateContent = await fs.readFile(path.join(targetPath, CODEX_UPDATE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1255
1588
  const codexCheckContent = await fs.readFile(path.join(targetPath, CODEX_CHECK_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1256
1589
  const codexPrepareContent = await fs.readFile(path.join(targetPath, CODEX_PREPARE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1257
1590
  const codexVerifyContent = await fs.readFile(path.join(targetPath, CODEX_VERIFY_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1591
+ const codexScanContent = await fs.readFile(path.join(targetPath, CODEX_SCAN_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1592
+ const expectedCodexOnboardSkill = pinnedVersion === null ? null : renderCodexOnboardSkill(pinnedVersion);
1593
+ const expectedCodexStatusSkill = pinnedVersion === null ? null : renderCodexStatusSkill(pinnedVersion);
1594
+ const expectedCodexMigrateSkill = pinnedVersion === null ? null : renderCodexMigrateSkill(pinnedVersion);
1595
+ const expectedCodexUpdateSkill = pinnedVersion === null ? null : renderCodexUpdateSkill(pinnedVersion);
1596
+ const expectedCodexCheckSkill = pinnedVersion === null ? null : renderCodexCheckSkill(pinnedVersion);
1597
+ const expectedCodexPrepareSkill = pinnedVersion === null ? null : renderCodexPrepareSkill(pinnedVersion);
1598
+ const expectedCodexVerifySkill = pinnedVersion === null ? null : renderCodexVerifySkill(pinnedVersion);
1599
+ const expectedCodexScanSkill = pinnedVersion === null ? null : renderCodexScanSkill(pinnedVersion);
1258
1600
  const surfaces = [
1259
1601
  createSurfaceStatus({
1260
1602
  kind: "canonical_entrypoint",
@@ -1282,21 +1624,27 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, options) {
1282
1624
  present: codexSkillContent !== null,
1283
1625
  state: codexSkillContent === null
1284
1626
  ? "missing"
1285
- : codexSkillContent === renderCodexOnboardSkill()
1286
- ? "ready"
1287
- : "misconfigured",
1627
+ : expectedCodexOnboardSkill === null
1628
+ ? "missing"
1629
+ : codexSkillContent === expectedCodexOnboardSkill
1630
+ ? "ready"
1631
+ : "misconfigured",
1288
1632
  summary: codexSkillContent === null
1289
1633
  ? "Codex onboarding handoff is missing and should install `/gdh-onboard` under .codex/skills/."
1290
- : codexSkillContent === renderCodexOnboardSkill()
1291
- ? "Codex can discover the managed `/gdh-onboard` handoff skill."
1292
- : "Codex onboarding handoff exists but no longer matches the expected managed GDH skill.",
1634
+ : expectedCodexOnboardSkill === null
1635
+ ? "Codex onboarding handoff cannot be validated yet: no `gdh_version` is pinned (run `gdh setup` or `gdh migrate --apply`)."
1636
+ : codexSkillContent === expectedCodexOnboardSkill
1637
+ ? "Codex can discover the managed `/gdh-onboard` handoff skill."
1638
+ : "Codex onboarding handoff exists but no longer matches the expected managed GDH skill.",
1293
1639
  version: null,
1294
1640
  }),
1295
- ...inspectCodexSkillSurface(targetPath, CODEX_STATUS_SKILL_RELATIVE_PATH, codexStatusContent, renderCodexStatusSkill(), "gdh-status"),
1296
- ...inspectCodexSkillSurface(targetPath, CODEX_MIGRATE_SKILL_RELATIVE_PATH, codexMigrateContent, renderCodexMigrateSkill(), "gdh-migrate"),
1297
- ...inspectCodexSkillSurface(targetPath, CODEX_CHECK_SKILL_RELATIVE_PATH, codexCheckContent, renderCodexCheckSkill(), "gdh-check"),
1298
- ...inspectCodexSkillSurface(targetPath, CODEX_PREPARE_SKILL_RELATIVE_PATH, codexPrepareContent, renderCodexPrepareSkill(), "gdh-prepare"),
1299
- ...inspectCodexSkillSurface(targetPath, CODEX_VERIFY_SKILL_RELATIVE_PATH, codexVerifyContent, renderCodexVerifySkill(), "gdh-verify"),
1641
+ ...inspectCodexSkillSurface(targetPath, CODEX_STATUS_SKILL_RELATIVE_PATH, codexStatusContent, expectedCodexStatusSkill, "gdh-status"),
1642
+ ...inspectCodexSkillSurface(targetPath, CODEX_MIGRATE_SKILL_RELATIVE_PATH, codexMigrateContent, expectedCodexMigrateSkill, "gdh-migrate"),
1643
+ ...inspectCodexSkillSurface(targetPath, CODEX_UPDATE_SKILL_RELATIVE_PATH, codexUpdateContent, expectedCodexUpdateSkill, "gdh-update"),
1644
+ ...inspectCodexSkillSurface(targetPath, CODEX_CHECK_SKILL_RELATIVE_PATH, codexCheckContent, expectedCodexCheckSkill, "gdh-check"),
1645
+ ...inspectCodexSkillSurface(targetPath, CODEX_PREPARE_SKILL_RELATIVE_PATH, codexPrepareContent, expectedCodexPrepareSkill, "gdh-prepare"),
1646
+ ...inspectCodexSkillSurface(targetPath, CODEX_VERIFY_SKILL_RELATIVE_PATH, codexVerifyContent, expectedCodexVerifySkill, "gdh-verify"),
1647
+ ...inspectCodexSkillSurface(targetPath, CODEX_SCAN_SKILL_RELATIVE_PATH, codexScanContent, expectedCodexScanSkill, "gdh-scan"),
1300
1648
  ];
1301
1649
  if (projectMcp.enabled) {
1302
1650
  surfaces.push(createSurfaceStatus({
@@ -1325,20 +1673,40 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, options) {
1325
1673
  }
1326
1674
  return createAgentStatus("codex", guidance, surfaces);
1327
1675
  }
1328
- async function inspectClaudeAdapter(targetPath, guidance, projectMcp) {
1676
+ async function inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVersion) {
1329
1677
  const absolutePath = path.join(targetPath, CLAUDE_SHIM_RELATIVE_PATH);
1330
1678
  const onboardCommandPath = path.join(targetPath, CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH);
1331
1679
  const lstat = await fs.lstat(absolutePath).catch(() => null);
1332
1680
  const onboardCommandContent = await fs.readFile(onboardCommandPath, "utf8").catch(() => null);
1333
1681
  const claudeStatusContent = await fs.readFile(path.join(targetPath, CLAUDE_STATUS_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1334
1682
  const claudeMigrateContent = await fs.readFile(path.join(targetPath, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1683
+ // Phase 13 Plan 13-03 deliverable — /gdh-update slash command for Claude.
1684
+ // Inspection wiring is required so planSkillInstallAction can see the surface
1685
+ // state; otherwise the planner returns `unchanged` for a missing file and
1686
+ // install becomes a no-op (Plan 13-05 integration-test Rule 2 fix).
1687
+ const claudeUpdateContent = await fs.readFile(path.join(targetPath, CLAUDE_UPDATE_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1335
1688
  const claudeCheckContent = await fs.readFile(path.join(targetPath, CLAUDE_CHECK_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1336
1689
  const claudePrepareContent = await fs.readFile(path.join(targetPath, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1337
1690
  const claudeVerifyContent = await fs.readFile(path.join(targetPath, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1691
+ const claudeScanContent = await fs.readFile(path.join(targetPath, CLAUDE_SCAN_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1692
+ const claudeCheckUpdateHookContent = await fs.readFile(path.join(targetPath, CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH), "utf8").catch(() => null);
1693
+ const claudeCheckUpdateWorkerContent = await fs.readFile(path.join(targetPath, CLAUDE_CHECK_UPDATE_WORKER_RELATIVE_PATH), "utf8").catch(() => null);
1694
+ const claudeStatuslineContent = await fs.readFile(path.join(targetPath, CLAUDE_STATUSLINE_RELATIVE_PATH), "utf8").catch(() => null);
1338
1695
  let detectedTarget = null;
1339
1696
  if (lstat?.isSymbolicLink()) {
1340
1697
  detectedTarget = await fs.readlink(absolutePath).catch(() => null);
1341
1698
  }
1699
+ const expectedClaudeOnboardCommand = pinnedVersion === null ? null : renderClaudeOnboardCommand(pinnedVersion);
1700
+ const expectedClaudeCheckUpdateHook = pinnedVersion === null ? null : renderClaudeCheckUpdateHook(pinnedVersion);
1701
+ const expectedClaudeCheckUpdateWorker = pinnedVersion === null ? null : renderClaudeCheckUpdateWorker(pinnedVersion);
1702
+ const expectedClaudeStatusline = pinnedVersion === null ? null : renderClaudeUpdateStatusline(pinnedVersion);
1703
+ const expectedClaudeStatusCommand = pinnedVersion === null ? null : renderClaudeStatusCommand(pinnedVersion);
1704
+ const expectedClaudeMigrateCommand = pinnedVersion === null ? null : renderClaudeMigrateCommand(pinnedVersion);
1705
+ const expectedClaudeUpdateCommand = pinnedVersion === null ? null : renderClaudeUpdateCommand(pinnedVersion);
1706
+ const expectedClaudeCheckCommand = pinnedVersion === null ? null : renderClaudeCheckCommand(pinnedVersion);
1707
+ const expectedClaudePrepareCommand = pinnedVersion === null ? null : renderClaudePrepareCommand(pinnedVersion);
1708
+ const expectedClaudeVerifyCommand = pinnedVersion === null ? null : renderClaudeVerifyCommand(pinnedVersion);
1709
+ const expectedClaudeScanCommand = pinnedVersion === null ? null : renderClaudeScanCommand(pinnedVersion);
1342
1710
  const surfaces = [
1343
1711
  createSurfaceStatus({
1344
1712
  kind: "symlink",
@@ -1368,21 +1736,30 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp) {
1368
1736
  present: onboardCommandContent !== null,
1369
1737
  state: onboardCommandContent === null
1370
1738
  ? "missing"
1371
- : onboardCommandContent === renderClaudeOnboardCommand()
1372
- ? "ready"
1373
- : "misconfigured",
1739
+ : expectedClaudeOnboardCommand === null
1740
+ ? "missing"
1741
+ : onboardCommandContent === expectedClaudeOnboardCommand
1742
+ ? "ready"
1743
+ : "misconfigured",
1374
1744
  summary: onboardCommandContent === null
1375
1745
  ? "Claude onboarding handoff is missing and should install `/gdh-onboard` under .claude/commands/."
1376
- : onboardCommandContent === renderClaudeOnboardCommand()
1377
- ? "Claude can discover the managed `/gdh-onboard` handoff command."
1378
- : "Claude onboarding handoff exists but no longer matches the expected managed GDH command.",
1746
+ : expectedClaudeOnboardCommand === null
1747
+ ? "Claude onboarding handoff cannot be validated yet: no `gdh_version` is pinned (run `gdh setup` or `gdh migrate --apply`)."
1748
+ : onboardCommandContent === expectedClaudeOnboardCommand
1749
+ ? "Claude can discover the managed `/gdh-onboard` handoff command."
1750
+ : "Claude onboarding handoff exists but no longer matches the expected managed GDH command.",
1379
1751
  version: null,
1380
1752
  }),
1381
- ...inspectClaudeCommandSurface(targetPath, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, claudeStatusContent, renderClaudeStatusCommand(), "gdh-status"),
1382
- ...inspectClaudeCommandSurface(targetPath, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, claudeMigrateContent, renderClaudeMigrateCommand(), "gdh-migrate"),
1383
- ...inspectClaudeCommandSurface(targetPath, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, claudeCheckContent, renderClaudeCheckCommand(), "gdh-check"),
1384
- ...inspectClaudeCommandSurface(targetPath, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, claudePrepareContent, renderClaudePrepareCommand(), "gdh-prepare"),
1385
- ...inspectClaudeCommandSurface(targetPath, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, claudeVerifyContent, renderClaudeVerifyCommand(), "gdh-verify"),
1753
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, claudeStatusContent, expectedClaudeStatusCommand, "gdh-status"),
1754
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, claudeMigrateContent, expectedClaudeMigrateCommand, "gdh-migrate"),
1755
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_UPDATE_COMMAND_RELATIVE_PATH, claudeUpdateContent, expectedClaudeUpdateCommand, "gdh-update"),
1756
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, claudeCheckContent, expectedClaudeCheckCommand, "gdh-check"),
1757
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, claudePrepareContent, expectedClaudePrepareCommand, "gdh-prepare"),
1758
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, claudeVerifyContent, expectedClaudeVerifyCommand, "gdh-verify"),
1759
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_SCAN_COMMAND_RELATIVE_PATH, claudeScanContent, expectedClaudeScanCommand, "gdh-scan"),
1760
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH, claudeCheckUpdateHookContent, expectedClaudeCheckUpdateHook, "gdh-check-update-hook"),
1761
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_CHECK_UPDATE_WORKER_RELATIVE_PATH, claudeCheckUpdateWorkerContent, expectedClaudeCheckUpdateWorker, "gdh-check-update-worker"),
1762
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_STATUSLINE_RELATIVE_PATH, claudeStatuslineContent, expectedClaudeStatusline, "gdh-statusline"),
1386
1763
  ];
1387
1764
  if (projectMcp.enabled) {
1388
1765
  surfaces.push(createSurfaceStatus({
@@ -1409,18 +1786,32 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp) {
1409
1786
  }
1410
1787
  return createAgentStatus("claude", guidance, surfaces);
1411
1788
  }
1412
- async function inspectCursorAdapter(targetPath, guidance, projectMcp) {
1789
+ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVersion) {
1413
1790
  const absolutePath = path.join(targetPath, CURSOR_RULE_RELATIVE_PATH);
1414
1791
  const onboardSkillPath = path.join(targetPath, CURSOR_ONBOARD_SKILL_RELATIVE_PATH);
1415
1792
  const content = await fs.readFile(absolutePath, "utf8").catch(() => null);
1416
1793
  const onboardSkillContent = await fs.readFile(onboardSkillPath, "utf8").catch(() => null);
1417
1794
  const cursorStatusContent = await fs.readFile(path.join(targetPath, CURSOR_STATUS_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1418
1795
  const cursorMigrateContent = await fs.readFile(path.join(targetPath, CURSOR_MIGRATE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1796
+ // Phase 13 Plan 13-03 deliverable — /gdh-update skill for Cursor. Inspection
1797
+ // wiring required so planSkillInstallAction sees the surface state; otherwise
1798
+ // the planner returns `unchanged` for a missing file and install becomes a
1799
+ // no-op (Plan 13-05 integration-test Rule 2 fix).
1800
+ const cursorUpdateContent = await fs.readFile(path.join(targetPath, CURSOR_UPDATE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1419
1801
  const cursorCheckContent = await fs.readFile(path.join(targetPath, CURSOR_CHECK_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1420
1802
  const cursorPrepareContent = await fs.readFile(path.join(targetPath, CURSOR_PREPARE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1421
1803
  const cursorVerifyContent = await fs.readFile(path.join(targetPath, CURSOR_VERIFY_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1804
+ const cursorScanContent = await fs.readFile(path.join(targetPath, CURSOR_SCAN_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1422
1805
  const expectedContent = renderCursorRule();
1423
1806
  const version = readCursorRuleVersion(content);
1807
+ const expectedCursorOnboardSkill = pinnedVersion === null ? null : renderCursorOnboardSkill(pinnedVersion);
1808
+ const expectedCursorStatusSkill = pinnedVersion === null ? null : renderCursorStatusSkill(pinnedVersion);
1809
+ const expectedCursorMigrateSkill = pinnedVersion === null ? null : renderCursorMigrateSkill(pinnedVersion);
1810
+ const expectedCursorUpdateSkill = pinnedVersion === null ? null : renderCursorUpdateSkill(pinnedVersion);
1811
+ const expectedCursorCheckSkill = pinnedVersion === null ? null : renderCursorCheckSkill(pinnedVersion);
1812
+ const expectedCursorPrepareSkill = pinnedVersion === null ? null : renderCursorPrepareSkill(pinnedVersion);
1813
+ const expectedCursorVerifySkill = pinnedVersion === null ? null : renderCursorVerifySkill(pinnedVersion);
1814
+ const expectedCursorScanSkill = pinnedVersion === null ? null : renderCursorScanSkill(pinnedVersion);
1424
1815
  const surfaces = [
1425
1816
  createSurfaceStatus({
1426
1817
  kind: "rule_file",
@@ -1448,21 +1839,27 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp) {
1448
1839
  present: onboardSkillContent !== null,
1449
1840
  state: onboardSkillContent === null
1450
1841
  ? "missing"
1451
- : onboardSkillContent === renderCursorOnboardSkill()
1452
- ? "ready"
1453
- : "misconfigured",
1842
+ : expectedCursorOnboardSkill === null
1843
+ ? "missing"
1844
+ : onboardSkillContent === expectedCursorOnboardSkill
1845
+ ? "ready"
1846
+ : "misconfigured",
1454
1847
  summary: onboardSkillContent === null
1455
1848
  ? "Cursor onboarding handoff is missing and should install `/gdh-onboard` under .cursor/skills/."
1456
- : onboardSkillContent === renderCursorOnboardSkill()
1457
- ? "Cursor can discover the managed `/gdh-onboard` handoff skill."
1458
- : "Cursor onboarding handoff exists but no longer matches the expected managed GDH skill.",
1849
+ : expectedCursorOnboardSkill === null
1850
+ ? "Cursor onboarding handoff cannot be validated yet: no `gdh_version` is pinned (run `gdh setup` or `gdh migrate --apply`)."
1851
+ : onboardSkillContent === expectedCursorOnboardSkill
1852
+ ? "Cursor can discover the managed `/gdh-onboard` handoff skill."
1853
+ : "Cursor onboarding handoff exists but no longer matches the expected managed GDH skill.",
1459
1854
  version: null,
1460
1855
  }),
1461
- ...inspectCursorSkillSurface(targetPath, CURSOR_STATUS_SKILL_RELATIVE_PATH, cursorStatusContent, renderCursorStatusSkill(), "gdh-status"),
1462
- ...inspectCursorSkillSurface(targetPath, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, cursorMigrateContent, renderCursorMigrateSkill(), "gdh-migrate"),
1463
- ...inspectCursorSkillSurface(targetPath, CURSOR_CHECK_SKILL_RELATIVE_PATH, cursorCheckContent, renderCursorCheckSkill(), "gdh-check"),
1464
- ...inspectCursorSkillSurface(targetPath, CURSOR_PREPARE_SKILL_RELATIVE_PATH, cursorPrepareContent, renderCursorPrepareSkill(), "gdh-prepare"),
1465
- ...inspectCursorSkillSurface(targetPath, CURSOR_VERIFY_SKILL_RELATIVE_PATH, cursorVerifyContent, renderCursorVerifySkill(), "gdh-verify"),
1856
+ ...inspectCursorSkillSurface(targetPath, CURSOR_STATUS_SKILL_RELATIVE_PATH, cursorStatusContent, expectedCursorStatusSkill, "gdh-status"),
1857
+ ...inspectCursorSkillSurface(targetPath, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, cursorMigrateContent, expectedCursorMigrateSkill, "gdh-migrate"),
1858
+ ...inspectCursorSkillSurface(targetPath, CURSOR_UPDATE_SKILL_RELATIVE_PATH, cursorUpdateContent, expectedCursorUpdateSkill, "gdh-update"),
1859
+ ...inspectCursorSkillSurface(targetPath, CURSOR_CHECK_SKILL_RELATIVE_PATH, cursorCheckContent, expectedCursorCheckSkill, "gdh-check"),
1860
+ ...inspectCursorSkillSurface(targetPath, CURSOR_PREPARE_SKILL_RELATIVE_PATH, cursorPrepareContent, expectedCursorPrepareSkill, "gdh-prepare"),
1861
+ ...inspectCursorSkillSurface(targetPath, CURSOR_VERIFY_SKILL_RELATIVE_PATH, cursorVerifyContent, expectedCursorVerifySkill, "gdh-verify"),
1862
+ ...inspectCursorSkillSurface(targetPath, CURSOR_SCAN_SKILL_RELATIVE_PATH, cursorScanContent, expectedCursorScanSkill, "gdh-scan"),
1466
1863
  ];
1467
1864
  if (projectMcp.enabled) {
1468
1865
  surfaces.push(createSurfaceStatus({
@@ -1552,6 +1949,7 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
1552
1949
  const projectMcp = await inspectProjectMcpSupport(targetPath, {
1553
1950
  includeUserLocal: true,
1554
1951
  integrationRootPath: options.integrationRootPath,
1952
+ pinnedVersion: options.pinnedVersion,
1555
1953
  });
1556
1954
  const actions = [];
1557
1955
  const effectiveDevRepoPath = options.devRepoPath ?? resolveCurrentGdhInstall(import.meta.url).defaultDevRepoPath;
@@ -1567,15 +1965,15 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
1567
1965
  continue;
1568
1966
  }
1569
1967
  if (adapter.agent === "codex") {
1570
- actions.push(...planCodexRepoInstallActions(targetPath, adapter));
1968
+ actions.push(...planCodexRepoInstallActions(targetPath, adapter, options.pinnedVersion));
1571
1969
  continue;
1572
1970
  }
1573
1971
  if (adapter.agent === "claude") {
1574
- actions.push(...planClaudeInstallActions(targetPath, adapter));
1972
+ actions.push(...planClaudeInstallActions(targetPath, adapter, options.pinnedVersion));
1575
1973
  continue;
1576
1974
  }
1577
1975
  if (adapter.agent === "cursor") {
1578
- actions.push(...planCursorInstallActions(targetPath, adapter));
1976
+ actions.push(...planCursorInstallActions(targetPath, adapter, options.pinnedVersion));
1579
1977
  }
1580
1978
  }
1581
1979
  return dedupeInstallActions(actions);
@@ -1667,7 +2065,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
1667
2065
  }
1668
2066
  return actions;
1669
2067
  }
1670
- function planSkillInstallAction(agent, targetPath, adapter, relativePath, renderFn, skillName) {
2068
+ function planSkillInstallAction(agent, targetPath, adapter, relativePath, renderFn, skillName, pinnedVersion) {
1671
2069
  const surface = adapter.surfaces.find((s) => s.relativePath === relativePath);
1672
2070
  if (!surface || surface.state === "ready") {
1673
2071
  return createInstallAction({
@@ -1679,7 +2077,7 @@ function planSkillInstallAction(agent, targetPath, adapter, relativePath, render
1679
2077
  state: "unchanged",
1680
2078
  mode: "unchanged",
1681
2079
  summary: `The managed ${agentLabel(agent)} \`/${skillName}\` ${agent === "claude" ? "command" : "skill"} already matches the expected GDH content.`,
1682
- content: renderFn(),
2080
+ content: renderFn(pinnedVersion),
1683
2081
  });
1684
2082
  }
1685
2083
  return createInstallAction({
@@ -1693,7 +2091,7 @@ function planSkillInstallAction(agent, targetPath, adapter, relativePath, render
1693
2091
  summary: surface.present
1694
2092
  ? `Replace the existing ${agentLabel(agent)} \`/${skillName}\` ${agent === "claude" ? "command" : "skill"} with the managed GDH ${agent === "claude" ? "command" : "skill"}.`
1695
2093
  : `Create the managed ${agentLabel(agent)} \`/${skillName}\` ${agent === "claude" ? "command" : "skill"}.`,
1696
- content: renderFn(),
2094
+ content: renderFn(pinnedVersion),
1697
2095
  });
1698
2096
  }
1699
2097
  function agentLabel(agent) {
@@ -1706,14 +2104,16 @@ function agentLabel(agent) {
1706
2104
  return "Cursor";
1707
2105
  }
1708
2106
  }
1709
- function planCodexRepoInstallActions(targetPath, adapter) {
2107
+ function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion) {
1710
2108
  return [
1711
- planSkillInstallAction("codex", targetPath, adapter, CODEX_ONBOARD_SKILL_RELATIVE_PATH, renderCodexOnboardSkill, "gdh-onboard"),
1712
- planSkillInstallAction("codex", targetPath, adapter, CODEX_STATUS_SKILL_RELATIVE_PATH, renderCodexStatusSkill, "gdh-status"),
1713
- planSkillInstallAction("codex", targetPath, adapter, CODEX_MIGRATE_SKILL_RELATIVE_PATH, renderCodexMigrateSkill, "gdh-migrate"),
1714
- planSkillInstallAction("codex", targetPath, adapter, CODEX_CHECK_SKILL_RELATIVE_PATH, renderCodexCheckSkill, "gdh-check"),
1715
- planSkillInstallAction("codex", targetPath, adapter, CODEX_PREPARE_SKILL_RELATIVE_PATH, renderCodexPrepareSkill, "gdh-prepare"),
1716
- planSkillInstallAction("codex", targetPath, adapter, CODEX_VERIFY_SKILL_RELATIVE_PATH, renderCodexVerifySkill, "gdh-verify"),
2109
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_ONBOARD_SKILL_RELATIVE_PATH, renderCodexOnboardSkill, "gdh-onboard", pinnedVersion),
2110
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_STATUS_SKILL_RELATIVE_PATH, renderCodexStatusSkill, "gdh-status", pinnedVersion),
2111
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_MIGRATE_SKILL_RELATIVE_PATH, renderCodexMigrateSkill, "gdh-migrate", pinnedVersion),
2112
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_UPDATE_SKILL_RELATIVE_PATH, renderCodexUpdateSkill, "gdh-update", pinnedVersion),
2113
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_CHECK_SKILL_RELATIVE_PATH, renderCodexCheckSkill, "gdh-check", pinnedVersion),
2114
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_PREPARE_SKILL_RELATIVE_PATH, renderCodexPrepareSkill, "gdh-prepare", pinnedVersion),
2115
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_VERIFY_SKILL_RELATIVE_PATH, renderCodexVerifySkill, "gdh-verify", pinnedVersion),
2116
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_SCAN_SKILL_RELATIVE_PATH, renderCodexScanSkill, "gdh-scan", pinnedVersion),
1717
2117
  ];
1718
2118
  }
1719
2119
  function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath) {
@@ -1764,7 +2164,7 @@ function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath
1764
2164
  }));
1765
2165
  return actions;
1766
2166
  }
1767
- function planClaudeInstallActions(targetPath, adapter) {
2167
+ function planClaudeInstallActions(targetPath, adapter, pinnedVersion) {
1768
2168
  const actions = [];
1769
2169
  const claudeSurface = adapter.surfaces.find((surface) => surface.relativePath === CLAUDE_SHIM_RELATIVE_PATH);
1770
2170
  if (!claudeSurface || claudeSurface.state === "ready") {
@@ -1795,10 +2195,58 @@ function planClaudeInstallActions(targetPath, adapter) {
1795
2195
  expectedTarget: "AGENTS.md",
1796
2196
  }));
1797
2197
  }
1798
- actions.push(planSkillInstallAction("claude", targetPath, adapter, CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH, renderClaudeOnboardCommand, "gdh-onboard"), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, renderClaudeStatusCommand, "gdh-status"), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, renderClaudeMigrateCommand, "gdh-migrate"), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, renderClaudeCheckCommand, "gdh-check"), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, renderClaudePrepareCommand, "gdh-prepare"), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, renderClaudeVerifyCommand, "gdh-verify"));
2198
+ actions.push(planSkillInstallAction("claude", targetPath, adapter, CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH, renderClaudeOnboardCommand, "gdh-onboard", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, renderClaudeStatusCommand, "gdh-status", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, renderClaudeMigrateCommand, "gdh-migrate", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_UPDATE_COMMAND_RELATIVE_PATH, renderClaudeUpdateCommand, "gdh-update", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, renderClaudeCheckCommand, "gdh-check", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, renderClaudePrepareCommand, "gdh-prepare", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, renderClaudeVerifyCommand, "gdh-verify", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_SCAN_COMMAND_RELATIVE_PATH, renderClaudeScanCommand, "gdh-scan", pinnedVersion),
2199
+ // UPD-01 managed hook surfaces baked at the pinned version.
2200
+ planSkillInstallAction("claude", targetPath, adapter, CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH, renderClaudeCheckUpdateHook, "gdh-check-update-hook", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_CHECK_UPDATE_WORKER_RELATIVE_PATH, renderClaudeCheckUpdateWorker, "gdh-check-update-worker", pinnedVersion),
2201
+ // UPD-02 managed statusline surface baked at the pinned version. The .js
2202
+ // file is ALWAYS baked, even when settings.json statusLine is not owned
2203
+ // by GDH (write-if-absent below) — users can manually point their config
2204
+ // at gdh-statusline.js later; the baked file is a no-op until wired.
2205
+ planSkillInstallAction("claude", targetPath, adapter, CLAUDE_STATUSLINE_RELATIVE_PATH, renderClaudeUpdateStatusline, "gdh-statusline", pinnedVersion));
2206
+ // UPD-01 + UPD-02 .claude/settings.json composite patch action.
2207
+ // - Plan 02 (Strategy A, planner-lock #1): patch-merge the SessionStart
2208
+ // hook entry by exact command-literal match, preserving siblings.
2209
+ // - Plan 03 (write-if-absent, planner-lock #2 / D-18): add the statusLine
2210
+ // entry ONLY when no existing statusLine is configured (e.g. GSD-owned
2211
+ // or user-owned). Silent no-op for UPD-02 on targets where another
2212
+ // tool already owns the slot; the universal surface is MCP _meta.
2213
+ //
2214
+ // Synchronous read via fsSync is intentional: planClaudeInstallActions is
2215
+ // called without `await`, and making it async would ripple to every sibling
2216
+ // planner (Codex, Cursor). The composed patches are idempotent and
2217
+ // commutative (proven in Task 1 Test J) so application order is irrelevant.
2218
+ let existingSettingsContent = "";
2219
+ try {
2220
+ existingSettingsContent = fsSync.readFileSync(path.join(targetPath, CLAUDE_SETTINGS_RELATIVE_PATH), "utf8");
2221
+ }
2222
+ catch {
2223
+ existingSettingsContent = "";
2224
+ }
2225
+ const patchedSettings = patchClaudeSettingsForGdhStatusline(patchClaudeSettingsForGdhSessionStart(existingSettingsContent));
2226
+ const settingsIsUnchanged = existingSettingsContent === patchedSettings;
2227
+ const settingsExistedOnDisk = existingSettingsContent.length > 0;
2228
+ actions.push(createInstallAction({
2229
+ agent: "claude",
2230
+ kind: "write_file",
2231
+ scope: "repo",
2232
+ targetPath,
2233
+ relativePath: CLAUDE_SETTINGS_RELATIVE_PATH,
2234
+ state: settingsIsUnchanged ? "unchanged" : "planned",
2235
+ mode: settingsIsUnchanged
2236
+ ? "unchanged"
2237
+ : settingsExistedOnDisk
2238
+ ? "replace"
2239
+ : "create",
2240
+ summary: settingsIsUnchanged
2241
+ ? "GDH SessionStart hook + statusline already registered in .claude/settings.json."
2242
+ : settingsExistedOnDisk
2243
+ ? "Register the GDH SessionStart hook and statusline (write-if-absent) in .claude/settings.json while preserving sibling content (patch-merge)."
2244
+ : "Create .claude/settings.json with the GDH SessionStart hook + statusline registration.",
2245
+ content: patchedSettings,
2246
+ }));
1799
2247
  return actions;
1800
2248
  }
1801
- function planCursorInstallActions(targetPath, adapter) {
2249
+ function planCursorInstallActions(targetPath, adapter, pinnedVersion) {
1802
2250
  const actions = [];
1803
2251
  const cursorRuleSurface = adapter.surfaces.find((surface) => surface.relativePath === CURSOR_RULE_RELATIVE_PATH);
1804
2252
  if (!cursorRuleSurface || cursorRuleSurface.state === "ready") {
@@ -1831,7 +2279,7 @@ function planCursorInstallActions(targetPath, adapter) {
1831
2279
  content: renderCursorRule(),
1832
2280
  }));
1833
2281
  }
1834
- actions.push(planSkillInstallAction("cursor", targetPath, adapter, CURSOR_ONBOARD_SKILL_RELATIVE_PATH, renderCursorOnboardSkill, "gdh-onboard"), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_STATUS_SKILL_RELATIVE_PATH, renderCursorStatusSkill, "gdh-status"), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, renderCursorMigrateSkill, "gdh-migrate"), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_CHECK_SKILL_RELATIVE_PATH, renderCursorCheckSkill, "gdh-check"), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_PREPARE_SKILL_RELATIVE_PATH, renderCursorPrepareSkill, "gdh-prepare"), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_VERIFY_SKILL_RELATIVE_PATH, renderCursorVerifySkill, "gdh-verify"));
2282
+ actions.push(planSkillInstallAction("cursor", targetPath, adapter, CURSOR_ONBOARD_SKILL_RELATIVE_PATH, renderCursorOnboardSkill, "gdh-onboard", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_STATUS_SKILL_RELATIVE_PATH, renderCursorStatusSkill, "gdh-status", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, renderCursorMigrateSkill, "gdh-migrate", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_UPDATE_SKILL_RELATIVE_PATH, renderCursorUpdateSkill, "gdh-update", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_CHECK_SKILL_RELATIVE_PATH, renderCursorCheckSkill, "gdh-check", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_PREPARE_SKILL_RELATIVE_PATH, renderCursorPrepareSkill, "gdh-prepare", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_VERIFY_SKILL_RELATIVE_PATH, renderCursorVerifySkill, "gdh-verify", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_SCAN_SKILL_RELATIVE_PATH, renderCursorScanSkill, "gdh-scan", pinnedVersion));
1835
2283
  return actions;
1836
2284
  }
1837
2285
  function dedupeInstallActions(actions) {
@@ -1929,7 +2377,7 @@ function resolveProjectMcpEnabled(projectConfig) {
1929
2377
  function createCodexServerName(projectKeySeed) {
1930
2378
  return `gdh-${projectKeySeed}`;
1931
2379
  }
1932
- function renderManagedMcpLauncher() {
2380
+ export function renderManagedMcpLauncher(pinnedVersion) {
1933
2381
  return [
1934
2382
  "#!/usr/bin/env node",
1935
2383
  `// GDH MCP launcher v${GDH_MCP_LAUNCHER_VERSION}`,
@@ -1968,9 +2416,9 @@ function renderManagedMcpLauncher() {
1968
2416
  " }",
1969
2417
  "}",
1970
2418
  "",
1971
- 'const result = spawnSync("gdh", ["mcp", "serve", "--target", targetPath], { stdio: "inherit" });',
2419
+ `const result = spawnSync("npx", ["-y", "@skillcap/gdh@${pinnedVersion}", "mcp", "serve", "--target", targetPath], { stdio: "inherit", cwd: targetPath });`,
1972
2420
  'if (result.error && result.error.code === "ENOENT") {',
1973
- ' console.error("GDH MCP launcher could not find a usable GDH server. Install `gdh` on PATH or run `gdh adapters install <target> --dev-repo <path-to-gdh>`.");',
2421
+ ` console.error("GDH MCP launcher could not launch npx for @skillcap/gdh@${pinnedVersion}. Ensure Node.js 20+ is installed (npx ships with Node), or configure the contributor dev escape hatch via the GDH_DEV_REPO env var or .gdh-state/local-paths.json gdhDevRepoPath.");`,
1974
2422
  " process.exit(1);",
1975
2423
  "}",
1976
2424
  "process.exit(result.status ?? 1);",
@@ -3050,4 +3498,7 @@ function deriveRepoState(context) {
3050
3498
  function normalizeChangedFiles(files) {
3051
3499
  return [...new Set(files.map((file) => file.trim()).filter((file) => file.length > 0))];
3052
3500
  }
3501
+ export { bumpAndRebakePin, } from "./self-update-mechanics.js";
3502
+ export { CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH } from "./claude-update-hook-render.js";
3503
+ export { CLAUDE_STATUSLINE_RELATIVE_PATH } from "./claude-statusline-render.js";
3053
3504
  //# sourceMappingURL=index.js.map