@skillcap/gdh 0.4.0 → 0.5.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 (58) hide show
  1. package/INSTALL-BUNDLE.json +1 -1
  2. package/README.md +9 -10
  3. package/node_modules/@gdh/adapters/dist/index.d.ts +25 -18
  4. package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
  5. package/node_modules/@gdh/adapters/dist/index.js +378 -149
  6. package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
  7. package/node_modules/@gdh/adapters/package.json +8 -8
  8. package/node_modules/@gdh/authoring/dist/index.d.ts +1 -1
  9. package/node_modules/@gdh/authoring/dist/index.d.ts.map +1 -1
  10. package/node_modules/@gdh/authoring/dist/index.js +1 -1
  11. package/node_modules/@gdh/authoring/dist/index.js.map +1 -1
  12. package/node_modules/@gdh/authoring/dist/project.d.ts +24 -0
  13. package/node_modules/@gdh/authoring/dist/project.d.ts.map +1 -1
  14. package/node_modules/@gdh/authoring/dist/project.js +51 -1
  15. package/node_modules/@gdh/authoring/dist/project.js.map +1 -1
  16. package/node_modules/@gdh/authoring/package.json +2 -2
  17. package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
  18. package/node_modules/@gdh/cli/dist/index.js +226 -34
  19. package/node_modules/@gdh/cli/dist/index.js.map +1 -1
  20. package/node_modules/@gdh/cli/dist/migrate.d.ts +1 -0
  21. package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
  22. package/node_modules/@gdh/cli/dist/migrate.js +180 -72
  23. package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
  24. package/node_modules/@gdh/cli/dist/setup.d.ts.map +1 -1
  25. package/node_modules/@gdh/cli/dist/setup.js +49 -1
  26. package/node_modules/@gdh/cli/dist/setup.js.map +1 -1
  27. package/node_modules/@gdh/cli/package.json +10 -10
  28. package/node_modules/@gdh/core/dist/index.d.ts +5 -4
  29. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  30. package/node_modules/@gdh/core/dist/index.js +2 -2
  31. package/node_modules/@gdh/core/dist/index.js.map +1 -1
  32. package/node_modules/@gdh/core/package.json +1 -1
  33. package/node_modules/@gdh/docs/dist/guidance.d.ts.map +1 -1
  34. package/node_modules/@gdh/docs/dist/guidance.js +47 -0
  35. package/node_modules/@gdh/docs/dist/guidance.js.map +1 -1
  36. package/node_modules/@gdh/docs/package.json +2 -2
  37. package/node_modules/@gdh/mcp/dist/index.js +6 -2
  38. package/node_modules/@gdh/mcp/dist/index.js.map +1 -1
  39. package/node_modules/@gdh/mcp/package.json +8 -8
  40. package/node_modules/@gdh/observability/dist/guidance-audit.js +1 -0
  41. package/node_modules/@gdh/observability/dist/guidance-audit.js.map +1 -1
  42. package/node_modules/@gdh/observability/package.json +2 -2
  43. package/node_modules/@gdh/runtime/package.json +2 -2
  44. package/node_modules/@gdh/scan/dist/index.d.ts +2 -0
  45. package/node_modules/@gdh/scan/dist/index.d.ts.map +1 -1
  46. package/node_modules/@gdh/scan/dist/index.js +1 -0
  47. package/node_modules/@gdh/scan/dist/index.js.map +1 -1
  48. package/node_modules/@gdh/scan/dist/inventory-cache.d.ts +15 -0
  49. package/node_modules/@gdh/scan/dist/inventory-cache.d.ts.map +1 -0
  50. package/node_modules/@gdh/scan/dist/inventory-cache.js +53 -0
  51. package/node_modules/@gdh/scan/dist/inventory-cache.js.map +1 -0
  52. package/node_modules/@gdh/scan/dist/onboard.d.ts +7 -0
  53. package/node_modules/@gdh/scan/dist/onboard.d.ts.map +1 -1
  54. package/node_modules/@gdh/scan/dist/onboard.js +7 -1
  55. package/node_modules/@gdh/scan/dist/onboard.js.map +1 -1
  56. package/node_modules/@gdh/scan/package.json +3 -3
  57. package/node_modules/@gdh/verify/package.json +7 -7
  58. package/package.json +11 -11
@@ -4,7 +4,7 @@ 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
8
  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
9
  import { createDefaultGuidanceUnits, getGuidanceStatus, resolveGuidanceQuery, } from "@gdh/docs";
10
10
  import { inspectGuidanceAudit } from "@gdh/observability";
@@ -48,6 +48,9 @@ export const CURSOR_MIGRATE_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-migrate/SK
48
48
  export const CURSOR_CHECK_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-check/SKILL.md";
49
49
  export const CURSOR_PREPARE_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-prepare/SKILL.md";
50
50
  export const CURSOR_VERIFY_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-verify/SKILL.md";
51
+ export const CLAUDE_SCAN_COMMAND_RELATIVE_PATH = ".claude/commands/gdh/scan.md";
52
+ export const CODEX_SCAN_SKILL_RELATIVE_PATH = ".codex/skills/gdh-scan/SKILL.md";
53
+ export const CURSOR_SCAN_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-scan/SKILL.md";
51
54
  export const LOCAL_PATH_HINTS_RELATIVE_PATH = ".gdh-state/local-paths.json";
52
55
  export const MCP_LAUNCHER_RELATIVE_PATH = ".gdh/bin/gdh-mcp.mjs";
53
56
  export const GDH_MCP_SERVER_NAME = "gdh";
@@ -56,16 +59,18 @@ export async function getSupportedAgentAdaptersStatus(targetPath, options = {})
56
59
  const includeUserLocal = options.includeUserLocal ?? true;
57
60
  const integrationRootPath = resolveIntegrationRootPath(targetPath, options.integrationRootPath);
58
61
  const guidance = await getGuidanceStatus(targetPath);
62
+ const pinnedVersion = await resolvePinnedVersionOrNull(targetPath);
59
63
  const projectMcp = await inspectProjectMcpSupport(targetPath, {
60
64
  includeUserLocal,
61
65
  integrationRootPath,
66
+ pinnedVersion,
62
67
  });
63
68
  const [codex, claude, cursor] = await Promise.all([
64
- inspectCodexAdapter(targetPath, guidance, projectMcp, {
69
+ inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersion, {
65
70
  includeUserLocal,
66
71
  }),
67
- inspectClaudeAdapter(targetPath, guidance, projectMcp),
68
- inspectCursorAdapter(targetPath, guidance, projectMcp),
72
+ inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVersion),
73
+ inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVersion),
69
74
  ]);
70
75
  return {
71
76
  targetPath,
@@ -80,6 +85,7 @@ export async function installSupportedAgentAdapters(targetPath, options = {}) {
80
85
  const status = await getSupportedAgentAdaptersStatus(targetPath, {
81
86
  integrationRootPath,
82
87
  });
88
+ const pinnedVersion = await resolvePinnedVersion(targetPath);
83
89
  if (!hasReadyVisibilityChain(status.guidance) && options.allowBootstrap !== true) {
84
90
  return {
85
91
  targetPath,
@@ -96,26 +102,41 @@ export async function installSupportedAgentAdapters(targetPath, options = {}) {
96
102
  user: options.user ?? false,
97
103
  devRepoPath: options.devRepoPath ?? null,
98
104
  integrationRootPath,
105
+ pinnedVersion,
99
106
  });
107
+ let appliedActions = actions;
100
108
  if (!dryRun) {
109
+ const newActions = [];
101
110
  for (const action of actions) {
102
111
  if (action.state === "unchanged") {
112
+ newActions.push(action);
103
113
  continue;
104
114
  }
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;
115
+ try {
116
+ if (action.kind === "ensure_symlink") {
117
+ await applySymlinkAction(action);
118
+ }
119
+ else if (action.kind === "run_command") {
120
+ await applyRunCommandAction(action);
121
+ }
122
+ else if (action.command?.[0] === "write_local_hints") {
123
+ await applyLocalHintsAction(action);
124
+ }
125
+ else {
126
+ await applyWriteFileAction(action);
127
+ }
128
+ newActions.push({ ...action, state: "applied" });
112
129
  }
113
- if (action.command?.[0] === "write_local_hints") {
114
- await applyLocalHintsAction(action);
115
- continue;
130
+ catch (error) {
131
+ const errorMessage = error instanceof Error ? error.message : String(error);
132
+ newActions.push({
133
+ ...action,
134
+ state: "blocked",
135
+ summary: `${action.summary} (failed: ${errorMessage})`,
136
+ });
116
137
  }
117
- await applyWriteFileAction(action);
118
138
  }
139
+ appliedActions = newActions;
119
140
  }
120
141
  return {
121
142
  targetPath,
@@ -129,7 +150,7 @@ export async function installSupportedAgentAdapters(targetPath, options = {}) {
129
150
  requestedAgents,
130
151
  guidance: status.guidance,
131
152
  adapters: status.adapters,
132
- actions,
153
+ actions: appliedActions,
133
154
  };
134
155
  }
135
156
  export async function inspectProjectLifecycleCompatibility(targetPath) {
@@ -285,7 +306,7 @@ export function renderCursorRule() {
285
306
  "",
286
307
  ].join("\n");
287
308
  }
288
- export function renderClaudeOnboardCommand() {
309
+ export function renderClaudeOnboardCommand(pinnedVersion) {
289
310
  return [
290
311
  "---",
291
312
  "name: gdh:onboard",
@@ -298,17 +319,17 @@ export function renderClaudeOnboardCommand() {
298
319
  " - AskUserQuestion",
299
320
  "---",
300
321
  "<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.",
322
+ `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
323
  "</objective>",
303
324
  "",
304
325
  "<process>",
305
326
  "Follow this order:",
306
327
  "",
307
- "1. Confirm the selected target and current GDH state with `gdh status`.",
328
+ `1. Confirm the selected target and current GDH state with \`npx -y @skillcap/gdh@${pinnedVersion} status\`.`,
308
329
  "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.",
330
+ `3. Check immediate readiness with \`npx -y @skillcap/gdh@${pinnedVersion} guidance status\` and \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\`.`,
331
+ `4. If authoring is available, run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\`; if LSP matters, run \`npx -y @skillcap/gdh@${pinnedVersion} lsp status\`.`,
332
+ `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
333
  "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
334
  "7. Ask the human only for narrow unresolved environment or target facts.",
314
335
  "8. End with a concise readiness summary and the exact next development step.",
@@ -318,11 +339,12 @@ export function renderClaudeOnboardCommand() {
318
339
  "- Do not start editing code during `/gdh-onboard` unless the human explicitly asks.",
319
340
  "- Prefer GDH structured surfaces over repo guesswork.",
320
341
  "- Keep the output short, operational, and specific.",
342
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
321
343
  "</rules>",
322
344
  "",
323
345
  ].join("\n");
324
346
  }
325
- export function renderCodexOnboardSkill() {
347
+ export function renderCodexOnboardSkill(pinnedVersion) {
326
348
  return [
327
349
  "---",
328
350
  'name: "gdh-onboard"',
@@ -342,20 +364,20 @@ export function renderCodexOnboardSkill() {
342
364
  "</codex_skill_adapter>",
343
365
  "",
344
366
  "<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.",
367
+ `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
368
  "</objective>",
347
369
  "",
348
370
  "<process>",
349
371
  "Follow this order:",
350
372
  "",
351
- "- run `gdh status`",
373
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} status\``,
352
374
  "- 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`",
375
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} guidance status\``,
376
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\``,
377
+ `- if authoring is available, run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\``,
378
+ `- if LSP matters, run \`npx -y @skillcap/gdh@${pinnedVersion} lsp status\``,
379
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} run-config list\``,
380
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verification-scenario list\``,
359
381
  "- 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
382
  "- ask only for narrow unresolved environment or target facts",
361
383
  "- finish with a concise readiness summary and the exact next development step",
@@ -365,11 +387,12 @@ export function renderCodexOnboardSkill() {
365
387
  "- Do not start editing code during `/gdh-onboard` unless the human explicitly asks.",
366
388
  "- Prefer GDH structured surfaces over repo guesswork.",
367
389
  "- Keep output short, operational, and specific.",
390
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
368
391
  "</rules>",
369
392
  "",
370
393
  ].join("\n");
371
394
  }
372
- export function renderCursorOnboardSkill() {
395
+ export function renderCursorOnboardSkill(pinnedVersion) {
373
396
  return [
374
397
  "---",
375
398
  "name: gdh-onboard",
@@ -387,20 +410,20 @@ export function renderCursorOnboardSkill() {
387
410
  "</cursor_skill_adapter>",
388
411
  "",
389
412
  "<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.",
413
+ `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
414
  "</objective>",
392
415
  "",
393
416
  "<process>",
394
417
  "Follow this order:",
395
418
  "",
396
- "- run `gdh status`",
419
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} status\``,
397
420
  "- 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`",
421
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} guidance status\``,
422
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\``,
423
+ `- if authoring is available, run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\``,
424
+ `- if LSP matters, run \`npx -y @skillcap/gdh@${pinnedVersion} lsp status\``,
425
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} run-config list\``,
426
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verification-scenario list\``,
404
427
  "- 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
428
  "- ask only for narrow unresolved environment or target facts",
406
429
  "- finish with a concise readiness summary and the exact next development step",
@@ -410,12 +433,13 @@ export function renderCursorOnboardSkill() {
410
433
  "- Do not start editing code during `/gdh-onboard` unless the human explicitly asks.",
411
434
  "- Prefer GDH structured surfaces over repo guesswork.",
412
435
  "- Keep output short, operational, and specific.",
436
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
413
437
  "</rules>",
414
438
  "",
415
439
  ].join("\n");
416
440
  }
417
441
  // --- gdh-status skill renders ---
418
- export function renderClaudeStatusCommand() {
442
+ export function renderClaudeStatusCommand(pinnedVersion) {
419
443
  return [
420
444
  "---",
421
445
  "name: gdh:status",
@@ -433,8 +457,8 @@ export function renderClaudeStatusCommand() {
433
457
  "<process>",
434
458
  "Follow this order:",
435
459
  "",
436
- "1. Run `gdh status` and explain each readiness field.",
437
- "2. Check if migration is needed with `gdh migrate`.",
460
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field.`,
461
+ `2. Check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\`.`,
438
462
  "3. Surface any degraded or unavailable capabilities.",
439
463
  "4. Suggest the most productive next step based on current state.",
440
464
  "</process>",
@@ -443,11 +467,12 @@ export function renderClaudeStatusCommand() {
443
467
  "- Do not start editing code.",
444
468
  "- Prefer structured GDH surfaces over repo guesswork.",
445
469
  "- Keep output short and operational.",
470
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
446
471
  "</rules>",
447
472
  "",
448
473
  ].join("\n");
449
474
  }
450
- export function renderCodexStatusSkill() {
475
+ export function renderCodexStatusSkill(pinnedVersion) {
451
476
  return [
452
477
  "---",
453
478
  'name: "gdh-status"',
@@ -473,8 +498,8 @@ export function renderCodexStatusSkill() {
473
498
  "<process>",
474
499
  "Follow this order:",
475
500
  "",
476
- "- run `gdh status` and explain each readiness field",
477
- "- check if migration is needed with `gdh migrate`",
501
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field`,
502
+ `- check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\``,
478
503
  "- surface any degraded or unavailable capabilities",
479
504
  "- suggest the most productive next step based on current state",
480
505
  "</process>",
@@ -483,11 +508,12 @@ export function renderCodexStatusSkill() {
483
508
  "- Do not start editing code.",
484
509
  "- Prefer structured GDH surfaces over repo guesswork.",
485
510
  "- Keep output short and operational.",
511
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
486
512
  "</rules>",
487
513
  "",
488
514
  ].join("\n");
489
515
  }
490
- export function renderCursorStatusSkill() {
516
+ export function renderCursorStatusSkill(pinnedVersion) {
491
517
  return [
492
518
  "---",
493
519
  "name: gdh-status",
@@ -511,8 +537,8 @@ export function renderCursorStatusSkill() {
511
537
  "<process>",
512
538
  "Follow this order:",
513
539
  "",
514
- "- run `gdh status` and explain each readiness field",
515
- "- check if migration is needed with `gdh migrate`",
540
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field`,
541
+ `- check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\``,
516
542
  "- surface any degraded or unavailable capabilities",
517
543
  "- suggest the most productive next step based on current state",
518
544
  "</process>",
@@ -521,12 +547,123 @@ export function renderCursorStatusSkill() {
521
547
  "- Do not start editing code.",
522
548
  "- Prefer structured GDH surfaces over repo guesswork.",
523
549
  "- Keep output short and operational.",
550
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
551
+ "</rules>",
552
+ "",
553
+ ].join("\n");
554
+ }
555
+ // --- gdh-scan skill renders ---
556
+ export function renderClaudeScanCommand(pinnedVersion) {
557
+ return [
558
+ "---",
559
+ "name: gdh:scan",
560
+ "description: Refresh inventory cache and explain persistence effect",
561
+ "allowed-tools:",
562
+ " - Read",
563
+ " - Grep",
564
+ " - Glob",
565
+ " - Bash",
566
+ "---",
567
+ "<objective>",
568
+ "Refresh the inventory cache and explain the persistence effect of the scan.",
569
+ "</objective>",
570
+ "",
571
+ "<process>",
572
+ "Follow this order:",
573
+ "",
574
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} scan\` and parse the \`persisted\` field from the envelope.`,
575
+ "2. Check the `mode` field: `create`, `unchanged`, `overwrite`, or `null`.",
576
+ "3. Explain the persistence effect; if `null`, the target is not onboarded and no disk write occurred.",
577
+ "</process>",
578
+ "",
579
+ "<rules>",
580
+ "- Do not edit the persisted inventory directly.",
581
+ "- Prefer the cached inventory for status reads; re-run scan only when freshness is in question.",
582
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
583
+ "</rules>",
584
+ "",
585
+ ].join("\n");
586
+ }
587
+ export function renderCodexScanSkill(pinnedVersion) {
588
+ return [
589
+ "---",
590
+ 'name: "gdh-scan"',
591
+ 'description: "Refresh inventory cache and explain persistence effect"',
592
+ "metadata:",
593
+ ' short-description: "Refresh inventory cache and explain persistence effect"',
594
+ "---",
595
+ "",
596
+ "<codex_skill_adapter>",
597
+ "## Invocation",
598
+ "- This skill is invoked when the user says `/gdh-scan` or mentions `$gdh-scan`.",
599
+ "- Treat any extra user text as additional scan context.",
600
+ "",
601
+ "## User questions",
602
+ "- Ask only narrow follow-up questions when GDH cannot infer the missing fact safely.",
603
+ "- If structured user-input tooling is unavailable, ask concise plain-text questions instead.",
604
+ "</codex_skill_adapter>",
605
+ "",
606
+ "<objective>",
607
+ "Refresh the inventory cache and explain the persistence effect of the scan.",
608
+ "</objective>",
609
+ "",
610
+ "<process>",
611
+ "Follow this order:",
612
+ "",
613
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} scan\``,
614
+ "- parse the `persisted` field from the envelope",
615
+ "- check `mode`: `create`, `unchanged`, `overwrite`, or `null`",
616
+ "- explain the persistence effect; if `null`, explain the target is not onboarded",
617
+ "</process>",
618
+ "",
619
+ "<rules>",
620
+ "- Do not edit the persisted inventory directly.",
621
+ "- Prefer the cached inventory for status reads; re-run scan only when freshness is in question.",
622
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
623
+ "</rules>",
624
+ "",
625
+ ].join("\n");
626
+ }
627
+ export function renderCursorScanSkill(pinnedVersion) {
628
+ return [
629
+ "---",
630
+ "name: gdh-scan",
631
+ 'description: "Refresh inventory cache and explain persistence effect"',
632
+ "---",
633
+ "",
634
+ "<cursor_skill_adapter>",
635
+ "## Invocation",
636
+ "- This skill is invoked when the user says `/gdh-scan` or mentions `gdh-scan`.",
637
+ "- Treat any extra user text as additional scan context.",
638
+ "",
639
+ "## User questions",
640
+ "- Ask only narrow follow-up questions when GDH cannot infer the missing fact safely.",
641
+ "- Keep questions conversational and concise.",
642
+ "</cursor_skill_adapter>",
643
+ "",
644
+ "<objective>",
645
+ "Refresh the inventory cache and explain the persistence effect of the scan.",
646
+ "</objective>",
647
+ "",
648
+ "<process>",
649
+ "Follow this order:",
650
+ "",
651
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} scan\``,
652
+ "- parse the `persisted` field from the envelope",
653
+ "- check `mode`: `create`, `unchanged`, `overwrite`, or `null`",
654
+ "- explain the persistence effect; if `null`, explain the target is not onboarded",
655
+ "</process>",
656
+ "",
657
+ "<rules>",
658
+ "- Do not edit the persisted inventory directly.",
659
+ "- Prefer the cached inventory for status reads; re-run scan only when freshness is in question.",
660
+ "- For cache/persistence behavior of scan, status, onboard: see `docs/agent/persistence-semantics.md`.",
524
661
  "</rules>",
525
662
  "",
526
663
  ].join("\n");
527
664
  }
528
665
  // --- gdh-migrate skill renders ---
529
- export function renderClaudeMigrateCommand() {
666
+ export function renderClaudeMigrateCommand(pinnedVersion) {
530
667
  return [
531
668
  "---",
532
669
  "name: gdh:migrate",
@@ -545,10 +682,10 @@ export function renderClaudeMigrateCommand() {
545
682
  "<process>",
546
683
  "Follow this order:",
547
684
  "",
548
- "1. Run `gdh migrate` to preview pending migrations.",
685
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations.`,
549
686
  "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.",
687
+ `3. Offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves.`,
688
+ `4. After apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded.`,
552
689
  "</process>",
553
690
  "",
554
691
  "<rules>",
@@ -559,7 +696,7 @@ export function renderClaudeMigrateCommand() {
559
696
  "",
560
697
  ].join("\n");
561
698
  }
562
- export function renderCodexMigrateSkill() {
699
+ export function renderCodexMigrateSkill(pinnedVersion) {
563
700
  return [
564
701
  "---",
565
702
  'name: "gdh-migrate"',
@@ -585,10 +722,10 @@ export function renderCodexMigrateSkill() {
585
722
  "<process>",
586
723
  "Follow this order:",
587
724
  "",
588
- "- run `gdh migrate` to preview pending migrations",
725
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations`,
589
726
  "- 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",
727
+ `- offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves`,
728
+ `- after apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded`,
592
729
  "</process>",
593
730
  "",
594
731
  "<rules>",
@@ -599,7 +736,7 @@ export function renderCodexMigrateSkill() {
599
736
  "",
600
737
  ].join("\n");
601
738
  }
602
- export function renderCursorMigrateSkill() {
739
+ export function renderCursorMigrateSkill(pinnedVersion) {
603
740
  return [
604
741
  "---",
605
742
  "name: gdh-migrate",
@@ -623,10 +760,10 @@ export function renderCursorMigrateSkill() {
623
760
  "<process>",
624
761
  "Follow this order:",
625
762
  "",
626
- "- run `gdh migrate` to preview pending migrations",
763
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations`,
627
764
  "- 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",
765
+ `- offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves`,
766
+ `- after apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded`,
630
767
  "</process>",
631
768
  "",
632
769
  "<rules>",
@@ -638,7 +775,7 @@ export function renderCursorMigrateSkill() {
638
775
  ].join("\n");
639
776
  }
640
777
  // --- gdh-check skill renders ---
641
- export function renderClaudeCheckCommand() {
778
+ export function renderClaudeCheckCommand(pinnedVersion) {
642
779
  return [
643
780
  "---",
644
781
  "name: gdh:check",
@@ -656,7 +793,7 @@ export function renderClaudeCheckCommand() {
656
793
  "<process>",
657
794
  "Follow this order:",
658
795
  "",
659
- "1. Run `gdh authoring check`.",
796
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\`.`,
660
797
  "2. Explain each diagnostic finding with severity and provenance.",
661
798
  "3. Surface any import-state caveats or editor-side warnings.",
662
799
  "4. If issues found, suggest concrete remediation steps.",
@@ -670,7 +807,7 @@ export function renderClaudeCheckCommand() {
670
807
  "",
671
808
  ].join("\n");
672
809
  }
673
- export function renderCodexCheckSkill() {
810
+ export function renderCodexCheckSkill(pinnedVersion) {
674
811
  return [
675
812
  "---",
676
813
  'name: "gdh-check"',
@@ -696,7 +833,7 @@ export function renderCodexCheckSkill() {
696
833
  "<process>",
697
834
  "Follow this order:",
698
835
  "",
699
- "- run `gdh authoring check`",
836
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\``,
700
837
  "- explain each diagnostic finding with severity and provenance",
701
838
  "- surface any import-state caveats or editor-side warnings",
702
839
  "- if issues found, suggest concrete remediation steps",
@@ -710,7 +847,7 @@ export function renderCodexCheckSkill() {
710
847
  "",
711
848
  ].join("\n");
712
849
  }
713
- export function renderCursorCheckSkill() {
850
+ export function renderCursorCheckSkill(pinnedVersion) {
714
851
  return [
715
852
  "---",
716
853
  "name: gdh-check",
@@ -734,7 +871,7 @@ export function renderCursorCheckSkill() {
734
871
  "<process>",
735
872
  "Follow this order:",
736
873
  "",
737
- "- run `gdh authoring check`",
874
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} authoring check\``,
738
875
  "- explain each diagnostic finding with severity and provenance",
739
876
  "- surface any import-state caveats or editor-side warnings",
740
877
  "- if issues found, suggest concrete remediation steps",
@@ -749,7 +886,7 @@ export function renderCursorCheckSkill() {
749
886
  ].join("\n");
750
887
  }
751
888
  // --- gdh-prepare skill renders ---
752
- export function renderClaudePrepareCommand() {
889
+ export function renderClaudePrepareCommand(pinnedVersion) {
753
890
  return [
754
891
  "---",
755
892
  "name: gdh:prepare",
@@ -768,11 +905,11 @@ export function renderClaudePrepareCommand() {
768
905
  "<process>",
769
906
  "Follow this order:",
770
907
  "",
771
- "1. Run `gdh target prepare --dry-run` first to preview planned actions.",
908
+ `1. Run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\` first to preview planned actions.`,
772
909
  "2. Explain what hydration and import refresh will do.",
773
- "3. If the user approves, run `gdh target prepare`.",
910
+ `3. If the user approves, run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare\`.`,
774
911
  "4. If a `--source-target` is relevant, explain when and why to use it.",
775
- "5. Verify preparation succeeded with `gdh status`.",
912
+ `5. Verify preparation succeeded with \`npx -y @skillcap/gdh@${pinnedVersion} status\`.`,
776
913
  "</process>",
777
914
  "",
778
915
  "<rules>",
@@ -783,7 +920,7 @@ export function renderClaudePrepareCommand() {
783
920
  "",
784
921
  ].join("\n");
785
922
  }
786
- export function renderCodexPrepareSkill() {
923
+ export function renderCodexPrepareSkill(pinnedVersion) {
787
924
  return [
788
925
  "---",
789
926
  'name: "gdh-prepare"',
@@ -809,11 +946,11 @@ export function renderCodexPrepareSkill() {
809
946
  "<process>",
810
947
  "Follow this order:",
811
948
  "",
812
- "- run `gdh target prepare --dry-run` first to preview planned actions",
949
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\` first to preview planned actions`,
813
950
  "- explain what hydration and import refresh will do",
814
- "- if the user approves, run `gdh target prepare`",
951
+ `- if the user approves, run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare\``,
815
952
  "- if a `--source-target` is relevant, explain when and why to use it",
816
- "- verify preparation succeeded with `gdh status`",
953
+ `- verify preparation succeeded with \`npx -y @skillcap/gdh@${pinnedVersion} status\``,
817
954
  "</process>",
818
955
  "",
819
956
  "<rules>",
@@ -824,7 +961,7 @@ export function renderCodexPrepareSkill() {
824
961
  "",
825
962
  ].join("\n");
826
963
  }
827
- export function renderCursorPrepareSkill() {
964
+ export function renderCursorPrepareSkill(pinnedVersion) {
828
965
  return [
829
966
  "---",
830
967
  "name: gdh-prepare",
@@ -848,11 +985,11 @@ export function renderCursorPrepareSkill() {
848
985
  "<process>",
849
986
  "Follow this order:",
850
987
  "",
851
- "- run `gdh target prepare --dry-run` first to preview planned actions",
988
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare --dry-run\` first to preview planned actions`,
852
989
  "- explain what hydration and import refresh will do",
853
- "- if the user approves, run `gdh target prepare`",
990
+ `- if the user approves, run \`npx -y @skillcap/gdh@${pinnedVersion} target prepare\``,
854
991
  "- if a `--source-target` is relevant, explain when and why to use it",
855
- "- verify preparation succeeded with `gdh status`",
992
+ `- verify preparation succeeded with \`npx -y @skillcap/gdh@${pinnedVersion} status\``,
856
993
  "</process>",
857
994
  "",
858
995
  "<rules>",
@@ -864,7 +1001,7 @@ export function renderCursorPrepareSkill() {
864
1001
  ].join("\n");
865
1002
  }
866
1003
  // --- gdh-verify skill renders ---
867
- export function renderClaudeVerifyCommand() {
1004
+ export function renderClaudeVerifyCommand(pinnedVersion) {
868
1005
  return [
869
1006
  "---",
870
1007
  "name: gdh:verify",
@@ -883,8 +1020,8 @@ export function renderClaudeVerifyCommand() {
883
1020
  "Follow this order:",
884
1021
  "",
885
1022
  "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.",
1023
+ `2. Run \`npx -y @skillcap/gdh@${pinnedVersion} verify recommend\` with those files to get recommended validation kinds.`,
1024
+ `3. Run \`npx -y @skillcap/gdh@${pinnedVersion} verify done\` with performed validations to check done eligibility.`,
888
1025
  "4. Summarize gaps between recommended and performed validation.",
889
1026
  "5. Suggest specific next verification steps.",
890
1027
  "</process>",
@@ -897,7 +1034,7 @@ export function renderClaudeVerifyCommand() {
897
1034
  "",
898
1035
  ].join("\n");
899
1036
  }
900
- export function renderCodexVerifySkill() {
1037
+ export function renderCodexVerifySkill(pinnedVersion) {
901
1038
  return [
902
1039
  "---",
903
1040
  'name: "gdh-verify"',
@@ -924,8 +1061,8 @@ export function renderCodexVerifySkill() {
924
1061
  "Follow this order:",
925
1062
  "",
926
1063
  "- 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",
1064
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verify recommend\` with those files to get recommended validation kinds`,
1065
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verify done\` with performed validations to check done eligibility`,
929
1066
  "- summarize gaps between recommended and performed validation",
930
1067
  "- suggest specific next verification steps",
931
1068
  "</process>",
@@ -938,7 +1075,7 @@ export function renderCodexVerifySkill() {
938
1075
  "",
939
1076
  ].join("\n");
940
1077
  }
941
- export function renderCursorVerifySkill() {
1078
+ export function renderCursorVerifySkill(pinnedVersion) {
942
1079
  return [
943
1080
  "---",
944
1081
  "name: gdh-verify",
@@ -963,8 +1100,8 @@ export function renderCursorVerifySkill() {
963
1100
  "Follow this order:",
964
1101
  "",
965
1102
  "- 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",
1103
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verify recommend\` with those files to get recommended validation kinds`,
1104
+ `- run \`npx -y @skillcap/gdh@${pinnedVersion} verify done\` with performed validations to check done eligibility`,
968
1105
  "- summarize gaps between recommended and performed validation",
969
1106
  "- suggest specific next verification steps",
970
1107
  "</process>",
@@ -980,11 +1117,13 @@ export function renderCursorVerifySkill() {
980
1117
  async function inspectProjectMcpSupport(targetPath, options) {
981
1118
  const projectConfig = await readProjectConfig(targetPath);
982
1119
  const enabled = resolveProjectMcpEnabled(projectConfig);
983
- const launcherContent = renderManagedMcpLauncher();
1120
+ const launcherContent = options.pinnedVersion === null
1121
+ ? null
1122
+ : renderManagedMcpLauncher(options.pinnedVersion);
984
1123
  const managedMcpEntry = buildManagedMcpServerEntry({
985
1124
  targetPath,
986
1125
  integrationRootPath: options.integrationRootPath,
987
- launcherPathForConfig: `./${normalizePathForJson(path.relative(options.integrationRootPath, path.join(options.integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH)))}`,
1126
+ launcherPathForConfig: path.resolve(options.integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
988
1127
  });
989
1128
  const [projectFile, cursorFile, launcherSource, localPathHints] = await Promise.all([
990
1129
  inspectJsonFile(path.join(options.integrationRootPath, PROJECT_MCP_RELATIVE_PATH)),
@@ -1090,6 +1229,13 @@ function inspectLauncherFile(launcherSource, expectedContent, enabled, bootstrap
1090
1229
  summary: "The managed GDH MCP launcher is disabled for this target.",
1091
1230
  };
1092
1231
  }
1232
+ if (expectedContent === null) {
1233
+ return {
1234
+ present: launcherSource !== null,
1235
+ state: "missing",
1236
+ 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.",
1237
+ };
1238
+ }
1093
1239
  if (launcherSource === null) {
1094
1240
  return {
1095
1241
  present: false,
@@ -1182,6 +1328,20 @@ async function inspectCodexRegistration(targetPath, enabled, codexServerName, co
1182
1328
  };
1183
1329
  }
1184
1330
  function inspectCodexSkillSurface(targetPath, relativePath, content, expectedContent, skillName) {
1331
+ if (expectedContent === null) {
1332
+ return [
1333
+ createSurfaceStatus({
1334
+ kind: "skill_file",
1335
+ scope: "repo",
1336
+ targetPath,
1337
+ relativePath,
1338
+ present: content !== null,
1339
+ state: "missing",
1340
+ summary: `Codex ${skillName} skill cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
1341
+ version: null,
1342
+ }),
1343
+ ];
1344
+ }
1185
1345
  return [
1186
1346
  createSurfaceStatus({
1187
1347
  kind: "skill_file",
@@ -1204,6 +1364,20 @@ function inspectCodexSkillSurface(targetPath, relativePath, content, expectedCon
1204
1364
  ];
1205
1365
  }
1206
1366
  function inspectClaudeCommandSurface(targetPath, relativePath, content, expectedContent, commandName) {
1367
+ if (expectedContent === null) {
1368
+ return [
1369
+ createSurfaceStatus({
1370
+ kind: "command_file",
1371
+ scope: "repo",
1372
+ targetPath,
1373
+ relativePath,
1374
+ present: content !== null,
1375
+ state: "missing",
1376
+ summary: `Claude ${commandName} command cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
1377
+ version: null,
1378
+ }),
1379
+ ];
1380
+ }
1207
1381
  return [
1208
1382
  createSurfaceStatus({
1209
1383
  kind: "command_file",
@@ -1226,6 +1400,20 @@ function inspectClaudeCommandSurface(targetPath, relativePath, content, expected
1226
1400
  ];
1227
1401
  }
1228
1402
  function inspectCursorSkillSurface(targetPath, relativePath, content, expectedContent, skillName) {
1403
+ if (expectedContent === null) {
1404
+ return [
1405
+ createSurfaceStatus({
1406
+ kind: "skill_file",
1407
+ scope: "repo",
1408
+ targetPath,
1409
+ relativePath,
1410
+ present: content !== null,
1411
+ state: "missing",
1412
+ summary: `Cursor ${skillName} skill cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
1413
+ version: null,
1414
+ }),
1415
+ ];
1416
+ }
1229
1417
  return [
1230
1418
  createSurfaceStatus({
1231
1419
  kind: "skill_file",
@@ -1247,7 +1435,7 @@ function inspectCursorSkillSurface(targetPath, relativePath, content, expectedCo
1247
1435
  }),
1248
1436
  ];
1249
1437
  }
1250
- async function inspectCodexAdapter(targetPath, guidance, projectMcp, options) {
1438
+ async function inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersion, options) {
1251
1439
  const codexSkillPath = path.join(targetPath, CODEX_ONBOARD_SKILL_RELATIVE_PATH);
1252
1440
  const codexSkillContent = await fs.readFile(codexSkillPath, "utf8").catch(() => null);
1253
1441
  const codexStatusContent = await fs.readFile(path.join(targetPath, CODEX_STATUS_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
@@ -1255,6 +1443,14 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, options) {
1255
1443
  const codexCheckContent = await fs.readFile(path.join(targetPath, CODEX_CHECK_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1256
1444
  const codexPrepareContent = await fs.readFile(path.join(targetPath, CODEX_PREPARE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1257
1445
  const codexVerifyContent = await fs.readFile(path.join(targetPath, CODEX_VERIFY_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1446
+ const codexScanContent = await fs.readFile(path.join(targetPath, CODEX_SCAN_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1447
+ const expectedCodexOnboardSkill = pinnedVersion === null ? null : renderCodexOnboardSkill(pinnedVersion);
1448
+ const expectedCodexStatusSkill = pinnedVersion === null ? null : renderCodexStatusSkill(pinnedVersion);
1449
+ const expectedCodexMigrateSkill = pinnedVersion === null ? null : renderCodexMigrateSkill(pinnedVersion);
1450
+ const expectedCodexCheckSkill = pinnedVersion === null ? null : renderCodexCheckSkill(pinnedVersion);
1451
+ const expectedCodexPrepareSkill = pinnedVersion === null ? null : renderCodexPrepareSkill(pinnedVersion);
1452
+ const expectedCodexVerifySkill = pinnedVersion === null ? null : renderCodexVerifySkill(pinnedVersion);
1453
+ const expectedCodexScanSkill = pinnedVersion === null ? null : renderCodexScanSkill(pinnedVersion);
1258
1454
  const surfaces = [
1259
1455
  createSurfaceStatus({
1260
1456
  kind: "canonical_entrypoint",
@@ -1282,21 +1478,26 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, options) {
1282
1478
  present: codexSkillContent !== null,
1283
1479
  state: codexSkillContent === null
1284
1480
  ? "missing"
1285
- : codexSkillContent === renderCodexOnboardSkill()
1286
- ? "ready"
1287
- : "misconfigured",
1481
+ : expectedCodexOnboardSkill === null
1482
+ ? "missing"
1483
+ : codexSkillContent === expectedCodexOnboardSkill
1484
+ ? "ready"
1485
+ : "misconfigured",
1288
1486
  summary: codexSkillContent === null
1289
1487
  ? "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.",
1488
+ : expectedCodexOnboardSkill === null
1489
+ ? "Codex onboarding handoff cannot be validated yet: no `gdh_version` is pinned (run `gdh setup` or `gdh migrate --apply`)."
1490
+ : codexSkillContent === expectedCodexOnboardSkill
1491
+ ? "Codex can discover the managed `/gdh-onboard` handoff skill."
1492
+ : "Codex onboarding handoff exists but no longer matches the expected managed GDH skill.",
1293
1493
  version: null,
1294
1494
  }),
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"),
1495
+ ...inspectCodexSkillSurface(targetPath, CODEX_STATUS_SKILL_RELATIVE_PATH, codexStatusContent, expectedCodexStatusSkill, "gdh-status"),
1496
+ ...inspectCodexSkillSurface(targetPath, CODEX_MIGRATE_SKILL_RELATIVE_PATH, codexMigrateContent, expectedCodexMigrateSkill, "gdh-migrate"),
1497
+ ...inspectCodexSkillSurface(targetPath, CODEX_CHECK_SKILL_RELATIVE_PATH, codexCheckContent, expectedCodexCheckSkill, "gdh-check"),
1498
+ ...inspectCodexSkillSurface(targetPath, CODEX_PREPARE_SKILL_RELATIVE_PATH, codexPrepareContent, expectedCodexPrepareSkill, "gdh-prepare"),
1499
+ ...inspectCodexSkillSurface(targetPath, CODEX_VERIFY_SKILL_RELATIVE_PATH, codexVerifyContent, expectedCodexVerifySkill, "gdh-verify"),
1500
+ ...inspectCodexSkillSurface(targetPath, CODEX_SCAN_SKILL_RELATIVE_PATH, codexScanContent, expectedCodexScanSkill, "gdh-scan"),
1300
1501
  ];
1301
1502
  if (projectMcp.enabled) {
1302
1503
  surfaces.push(createSurfaceStatus({
@@ -1325,7 +1526,7 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, options) {
1325
1526
  }
1326
1527
  return createAgentStatus("codex", guidance, surfaces);
1327
1528
  }
1328
- async function inspectClaudeAdapter(targetPath, guidance, projectMcp) {
1529
+ async function inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVersion) {
1329
1530
  const absolutePath = path.join(targetPath, CLAUDE_SHIM_RELATIVE_PATH);
1330
1531
  const onboardCommandPath = path.join(targetPath, CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH);
1331
1532
  const lstat = await fs.lstat(absolutePath).catch(() => null);
@@ -1335,10 +1536,18 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp) {
1335
1536
  const claudeCheckContent = await fs.readFile(path.join(targetPath, CLAUDE_CHECK_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1336
1537
  const claudePrepareContent = await fs.readFile(path.join(targetPath, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1337
1538
  const claudeVerifyContent = await fs.readFile(path.join(targetPath, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1539
+ const claudeScanContent = await fs.readFile(path.join(targetPath, CLAUDE_SCAN_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
1338
1540
  let detectedTarget = null;
1339
1541
  if (lstat?.isSymbolicLink()) {
1340
1542
  detectedTarget = await fs.readlink(absolutePath).catch(() => null);
1341
1543
  }
1544
+ const expectedClaudeOnboardCommand = pinnedVersion === null ? null : renderClaudeOnboardCommand(pinnedVersion);
1545
+ const expectedClaudeStatusCommand = pinnedVersion === null ? null : renderClaudeStatusCommand(pinnedVersion);
1546
+ const expectedClaudeMigrateCommand = pinnedVersion === null ? null : renderClaudeMigrateCommand(pinnedVersion);
1547
+ const expectedClaudeCheckCommand = pinnedVersion === null ? null : renderClaudeCheckCommand(pinnedVersion);
1548
+ const expectedClaudePrepareCommand = pinnedVersion === null ? null : renderClaudePrepareCommand(pinnedVersion);
1549
+ const expectedClaudeVerifyCommand = pinnedVersion === null ? null : renderClaudeVerifyCommand(pinnedVersion);
1550
+ const expectedClaudeScanCommand = pinnedVersion === null ? null : renderClaudeScanCommand(pinnedVersion);
1342
1551
  const surfaces = [
1343
1552
  createSurfaceStatus({
1344
1553
  kind: "symlink",
@@ -1368,21 +1577,26 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp) {
1368
1577
  present: onboardCommandContent !== null,
1369
1578
  state: onboardCommandContent === null
1370
1579
  ? "missing"
1371
- : onboardCommandContent === renderClaudeOnboardCommand()
1372
- ? "ready"
1373
- : "misconfigured",
1580
+ : expectedClaudeOnboardCommand === null
1581
+ ? "missing"
1582
+ : onboardCommandContent === expectedClaudeOnboardCommand
1583
+ ? "ready"
1584
+ : "misconfigured",
1374
1585
  summary: onboardCommandContent === null
1375
1586
  ? "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.",
1587
+ : expectedClaudeOnboardCommand === null
1588
+ ? "Claude onboarding handoff cannot be validated yet: no `gdh_version` is pinned (run `gdh setup` or `gdh migrate --apply`)."
1589
+ : onboardCommandContent === expectedClaudeOnboardCommand
1590
+ ? "Claude can discover the managed `/gdh-onboard` handoff command."
1591
+ : "Claude onboarding handoff exists but no longer matches the expected managed GDH command.",
1379
1592
  version: null,
1380
1593
  }),
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"),
1594
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, claudeStatusContent, expectedClaudeStatusCommand, "gdh-status"),
1595
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, claudeMigrateContent, expectedClaudeMigrateCommand, "gdh-migrate"),
1596
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, claudeCheckContent, expectedClaudeCheckCommand, "gdh-check"),
1597
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, claudePrepareContent, expectedClaudePrepareCommand, "gdh-prepare"),
1598
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, claudeVerifyContent, expectedClaudeVerifyCommand, "gdh-verify"),
1599
+ ...inspectClaudeCommandSurface(targetPath, CLAUDE_SCAN_COMMAND_RELATIVE_PATH, claudeScanContent, expectedClaudeScanCommand, "gdh-scan"),
1386
1600
  ];
1387
1601
  if (projectMcp.enabled) {
1388
1602
  surfaces.push(createSurfaceStatus({
@@ -1409,7 +1623,7 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp) {
1409
1623
  }
1410
1624
  return createAgentStatus("claude", guidance, surfaces);
1411
1625
  }
1412
- async function inspectCursorAdapter(targetPath, guidance, projectMcp) {
1626
+ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVersion) {
1413
1627
  const absolutePath = path.join(targetPath, CURSOR_RULE_RELATIVE_PATH);
1414
1628
  const onboardSkillPath = path.join(targetPath, CURSOR_ONBOARD_SKILL_RELATIVE_PATH);
1415
1629
  const content = await fs.readFile(absolutePath, "utf8").catch(() => null);
@@ -1419,8 +1633,16 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp) {
1419
1633
  const cursorCheckContent = await fs.readFile(path.join(targetPath, CURSOR_CHECK_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1420
1634
  const cursorPrepareContent = await fs.readFile(path.join(targetPath, CURSOR_PREPARE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1421
1635
  const cursorVerifyContent = await fs.readFile(path.join(targetPath, CURSOR_VERIFY_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1636
+ const cursorScanContent = await fs.readFile(path.join(targetPath, CURSOR_SCAN_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
1422
1637
  const expectedContent = renderCursorRule();
1423
1638
  const version = readCursorRuleVersion(content);
1639
+ const expectedCursorOnboardSkill = pinnedVersion === null ? null : renderCursorOnboardSkill(pinnedVersion);
1640
+ const expectedCursorStatusSkill = pinnedVersion === null ? null : renderCursorStatusSkill(pinnedVersion);
1641
+ const expectedCursorMigrateSkill = pinnedVersion === null ? null : renderCursorMigrateSkill(pinnedVersion);
1642
+ const expectedCursorCheckSkill = pinnedVersion === null ? null : renderCursorCheckSkill(pinnedVersion);
1643
+ const expectedCursorPrepareSkill = pinnedVersion === null ? null : renderCursorPrepareSkill(pinnedVersion);
1644
+ const expectedCursorVerifySkill = pinnedVersion === null ? null : renderCursorVerifySkill(pinnedVersion);
1645
+ const expectedCursorScanSkill = pinnedVersion === null ? null : renderCursorScanSkill(pinnedVersion);
1424
1646
  const surfaces = [
1425
1647
  createSurfaceStatus({
1426
1648
  kind: "rule_file",
@@ -1448,21 +1670,26 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp) {
1448
1670
  present: onboardSkillContent !== null,
1449
1671
  state: onboardSkillContent === null
1450
1672
  ? "missing"
1451
- : onboardSkillContent === renderCursorOnboardSkill()
1452
- ? "ready"
1453
- : "misconfigured",
1673
+ : expectedCursorOnboardSkill === null
1674
+ ? "missing"
1675
+ : onboardSkillContent === expectedCursorOnboardSkill
1676
+ ? "ready"
1677
+ : "misconfigured",
1454
1678
  summary: onboardSkillContent === null
1455
1679
  ? "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.",
1680
+ : expectedCursorOnboardSkill === null
1681
+ ? "Cursor onboarding handoff cannot be validated yet: no `gdh_version` is pinned (run `gdh setup` or `gdh migrate --apply`)."
1682
+ : onboardSkillContent === expectedCursorOnboardSkill
1683
+ ? "Cursor can discover the managed `/gdh-onboard` handoff skill."
1684
+ : "Cursor onboarding handoff exists but no longer matches the expected managed GDH skill.",
1459
1685
  version: null,
1460
1686
  }),
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"),
1687
+ ...inspectCursorSkillSurface(targetPath, CURSOR_STATUS_SKILL_RELATIVE_PATH, cursorStatusContent, expectedCursorStatusSkill, "gdh-status"),
1688
+ ...inspectCursorSkillSurface(targetPath, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, cursorMigrateContent, expectedCursorMigrateSkill, "gdh-migrate"),
1689
+ ...inspectCursorSkillSurface(targetPath, CURSOR_CHECK_SKILL_RELATIVE_PATH, cursorCheckContent, expectedCursorCheckSkill, "gdh-check"),
1690
+ ...inspectCursorSkillSurface(targetPath, CURSOR_PREPARE_SKILL_RELATIVE_PATH, cursorPrepareContent, expectedCursorPrepareSkill, "gdh-prepare"),
1691
+ ...inspectCursorSkillSurface(targetPath, CURSOR_VERIFY_SKILL_RELATIVE_PATH, cursorVerifyContent, expectedCursorVerifySkill, "gdh-verify"),
1692
+ ...inspectCursorSkillSurface(targetPath, CURSOR_SCAN_SKILL_RELATIVE_PATH, cursorScanContent, expectedCursorScanSkill, "gdh-scan"),
1466
1693
  ];
1467
1694
  if (projectMcp.enabled) {
1468
1695
  surfaces.push(createSurfaceStatus({
@@ -1552,6 +1779,7 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
1552
1779
  const projectMcp = await inspectProjectMcpSupport(targetPath, {
1553
1780
  includeUserLocal: true,
1554
1781
  integrationRootPath: options.integrationRootPath,
1782
+ pinnedVersion: options.pinnedVersion,
1555
1783
  });
1556
1784
  const actions = [];
1557
1785
  const effectiveDevRepoPath = options.devRepoPath ?? resolveCurrentGdhInstall(import.meta.url).defaultDevRepoPath;
@@ -1567,15 +1795,15 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
1567
1795
  continue;
1568
1796
  }
1569
1797
  if (adapter.agent === "codex") {
1570
- actions.push(...planCodexRepoInstallActions(targetPath, adapter));
1798
+ actions.push(...planCodexRepoInstallActions(targetPath, adapter, options.pinnedVersion));
1571
1799
  continue;
1572
1800
  }
1573
1801
  if (adapter.agent === "claude") {
1574
- actions.push(...planClaudeInstallActions(targetPath, adapter));
1802
+ actions.push(...planClaudeInstallActions(targetPath, adapter, options.pinnedVersion));
1575
1803
  continue;
1576
1804
  }
1577
1805
  if (adapter.agent === "cursor") {
1578
- actions.push(...planCursorInstallActions(targetPath, adapter));
1806
+ actions.push(...planCursorInstallActions(targetPath, adapter, options.pinnedVersion));
1579
1807
  }
1580
1808
  }
1581
1809
  return dedupeInstallActions(actions);
@@ -1616,7 +1844,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
1616
1844
  content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({
1617
1845
  targetPath,
1618
1846
  integrationRootPath,
1619
- launcherPathForConfig: `./${normalizePathForJson(MCP_LAUNCHER_RELATIVE_PATH)}`,
1847
+ launcherPathForConfig: path.resolve(integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
1620
1848
  })),
1621
1849
  }));
1622
1850
  }
@@ -1635,7 +1863,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
1635
1863
  content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({
1636
1864
  targetPath,
1637
1865
  integrationRootPath,
1638
- launcherPathForConfig: `./${normalizePathForJson(MCP_LAUNCHER_RELATIVE_PATH)}`,
1866
+ launcherPathForConfig: path.resolve(integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
1639
1867
  })),
1640
1868
  }));
1641
1869
  }
@@ -1667,7 +1895,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
1667
1895
  }
1668
1896
  return actions;
1669
1897
  }
1670
- function planSkillInstallAction(agent, targetPath, adapter, relativePath, renderFn, skillName) {
1898
+ function planSkillInstallAction(agent, targetPath, adapter, relativePath, renderFn, skillName, pinnedVersion) {
1671
1899
  const surface = adapter.surfaces.find((s) => s.relativePath === relativePath);
1672
1900
  if (!surface || surface.state === "ready") {
1673
1901
  return createInstallAction({
@@ -1679,7 +1907,7 @@ function planSkillInstallAction(agent, targetPath, adapter, relativePath, render
1679
1907
  state: "unchanged",
1680
1908
  mode: "unchanged",
1681
1909
  summary: `The managed ${agentLabel(agent)} \`/${skillName}\` ${agent === "claude" ? "command" : "skill"} already matches the expected GDH content.`,
1682
- content: renderFn(),
1910
+ content: renderFn(pinnedVersion),
1683
1911
  });
1684
1912
  }
1685
1913
  return createInstallAction({
@@ -1693,7 +1921,7 @@ function planSkillInstallAction(agent, targetPath, adapter, relativePath, render
1693
1921
  summary: surface.present
1694
1922
  ? `Replace the existing ${agentLabel(agent)} \`/${skillName}\` ${agent === "claude" ? "command" : "skill"} with the managed GDH ${agent === "claude" ? "command" : "skill"}.`
1695
1923
  : `Create the managed ${agentLabel(agent)} \`/${skillName}\` ${agent === "claude" ? "command" : "skill"}.`,
1696
- content: renderFn(),
1924
+ content: renderFn(pinnedVersion),
1697
1925
  });
1698
1926
  }
1699
1927
  function agentLabel(agent) {
@@ -1706,14 +1934,15 @@ function agentLabel(agent) {
1706
1934
  return "Cursor";
1707
1935
  }
1708
1936
  }
1709
- function planCodexRepoInstallActions(targetPath, adapter) {
1937
+ function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion) {
1710
1938
  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"),
1939
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_ONBOARD_SKILL_RELATIVE_PATH, renderCodexOnboardSkill, "gdh-onboard", pinnedVersion),
1940
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_STATUS_SKILL_RELATIVE_PATH, renderCodexStatusSkill, "gdh-status", pinnedVersion),
1941
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_MIGRATE_SKILL_RELATIVE_PATH, renderCodexMigrateSkill, "gdh-migrate", pinnedVersion),
1942
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_CHECK_SKILL_RELATIVE_PATH, renderCodexCheckSkill, "gdh-check", pinnedVersion),
1943
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_PREPARE_SKILL_RELATIVE_PATH, renderCodexPrepareSkill, "gdh-prepare", pinnedVersion),
1944
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_VERIFY_SKILL_RELATIVE_PATH, renderCodexVerifySkill, "gdh-verify", pinnedVersion),
1945
+ planSkillInstallAction("codex", targetPath, adapter, CODEX_SCAN_SKILL_RELATIVE_PATH, renderCodexScanSkill, "gdh-scan", pinnedVersion),
1717
1946
  ];
1718
1947
  }
1719
1948
  function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath) {
@@ -1764,7 +1993,7 @@ function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath
1764
1993
  }));
1765
1994
  return actions;
1766
1995
  }
1767
- function planClaudeInstallActions(targetPath, adapter) {
1996
+ function planClaudeInstallActions(targetPath, adapter, pinnedVersion) {
1768
1997
  const actions = [];
1769
1998
  const claudeSurface = adapter.surfaces.find((surface) => surface.relativePath === CLAUDE_SHIM_RELATIVE_PATH);
1770
1999
  if (!claudeSurface || claudeSurface.state === "ready") {
@@ -1795,10 +2024,10 @@ function planClaudeInstallActions(targetPath, adapter) {
1795
2024
  expectedTarget: "AGENTS.md",
1796
2025
  }));
1797
2026
  }
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"));
2027
+ 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_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));
1799
2028
  return actions;
1800
2029
  }
1801
- function planCursorInstallActions(targetPath, adapter) {
2030
+ function planCursorInstallActions(targetPath, adapter, pinnedVersion) {
1802
2031
  const actions = [];
1803
2032
  const cursorRuleSurface = adapter.surfaces.find((surface) => surface.relativePath === CURSOR_RULE_RELATIVE_PATH);
1804
2033
  if (!cursorRuleSurface || cursorRuleSurface.state === "ready") {
@@ -1831,7 +2060,7 @@ function planCursorInstallActions(targetPath, adapter) {
1831
2060
  content: renderCursorRule(),
1832
2061
  }));
1833
2062
  }
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"));
2063
+ 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_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
2064
  return actions;
1836
2065
  }
1837
2066
  function dedupeInstallActions(actions) {
@@ -1929,7 +2158,7 @@ function resolveProjectMcpEnabled(projectConfig) {
1929
2158
  function createCodexServerName(projectKeySeed) {
1930
2159
  return `gdh-${projectKeySeed}`;
1931
2160
  }
1932
- function renderManagedMcpLauncher() {
2161
+ export function renderManagedMcpLauncher(pinnedVersion) {
1933
2162
  return [
1934
2163
  "#!/usr/bin/env node",
1935
2164
  `// GDH MCP launcher v${GDH_MCP_LAUNCHER_VERSION}`,
@@ -1968,9 +2197,9 @@ function renderManagedMcpLauncher() {
1968
2197
  " }",
1969
2198
  "}",
1970
2199
  "",
1971
- 'const result = spawnSync("gdh", ["mcp", "serve", "--target", targetPath], { stdio: "inherit" });',
2200
+ `const result = spawnSync("npx", ["-y", "@skillcap/gdh@${pinnedVersion}", "mcp", "serve", "--target", targetPath], { stdio: "inherit", cwd: targetPath });`,
1972
2201
  '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>`.");',
2202
+ ` 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
2203
  " process.exit(1);",
1975
2204
  "}",
1976
2205
  "process.exit(result.status ?? 1);",
@@ -1985,7 +2214,7 @@ function buildManagedMcpServerEntry(input) {
1985
2214
  targetPath: input.targetPath,
1986
2215
  integrationRootPath: input.integrationRootPath,
1987
2216
  launcherPath: input.launcherPathForConfig,
1988
- useAbsoluteTargetPath: false,
2217
+ useAbsoluteTargetPath: true,
1989
2218
  }),
1990
2219
  };
1991
2220
  }