@skillcap/gdh 0.5.0 → 0.7.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 (83) hide show
  1. package/INSTALL-BUNDLE.json +1 -1
  2. package/README.md +66 -85
  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 +12 -2
  20. package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
  21. package/node_modules/@gdh/adapters/dist/index.js +382 -244
  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 +1 -0
  29. package/node_modules/@gdh/authoring/dist/index.d.ts.map +1 -1
  30. package/node_modules/@gdh/authoring/dist/index.js +1 -0
  31. package/node_modules/@gdh/authoring/dist/index.js.map +1 -1
  32. package/node_modules/@gdh/authoring/dist/writePinnedVersion.d.ts +17 -0
  33. package/node_modules/@gdh/authoring/dist/writePinnedVersion.d.ts.map +1 -0
  34. package/node_modules/@gdh/authoring/dist/writePinnedVersion.js +50 -0
  35. package/node_modules/@gdh/authoring/dist/writePinnedVersion.js.map +1 -0
  36. package/node_modules/@gdh/authoring/package.json +5 -2
  37. package/node_modules/@gdh/cli/dist/index.d.ts +15 -0
  38. package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
  39. package/node_modules/@gdh/cli/dist/index.js +119 -20
  40. package/node_modules/@gdh/cli/dist/index.js.map +1 -1
  41. package/node_modules/@gdh/cli/dist/migrate.d.ts +1 -1
  42. package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
  43. package/node_modules/@gdh/cli/dist/migrate.js +44 -3
  44. package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
  45. package/node_modules/@gdh/cli/dist/self-update.d.ts +3 -0
  46. package/node_modules/@gdh/cli/dist/self-update.d.ts.map +1 -0
  47. package/node_modules/@gdh/cli/dist/self-update.js +235 -0
  48. package/node_modules/@gdh/cli/dist/self-update.js.map +1 -0
  49. package/node_modules/@gdh/cli/dist/update-banner.d.ts +42 -0
  50. package/node_modules/@gdh/cli/dist/update-banner.d.ts.map +1 -0
  51. package/node_modules/@gdh/cli/dist/update-banner.js +49 -0
  52. package/node_modules/@gdh/cli/dist/update-banner.js.map +1 -0
  53. package/node_modules/@gdh/cli/package.json +10 -10
  54. package/node_modules/@gdh/core/dist/dev-mode.d.ts +13 -0
  55. package/node_modules/@gdh/core/dist/dev-mode.d.ts.map +1 -0
  56. package/node_modules/@gdh/core/dist/dev-mode.js +21 -0
  57. package/node_modules/@gdh/core/dist/dev-mode.js.map +1 -0
  58. package/node_modules/@gdh/core/dist/index.d.ts +9 -4
  59. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  60. package/node_modules/@gdh/core/dist/index.js +8 -5
  61. package/node_modules/@gdh/core/dist/index.js.map +1 -1
  62. package/node_modules/@gdh/core/dist/update-cache.d.ts +46 -0
  63. package/node_modules/@gdh/core/dist/update-cache.d.ts.map +1 -0
  64. package/node_modules/@gdh/core/dist/update-cache.js +90 -0
  65. package/node_modules/@gdh/core/dist/update-cache.js.map +1 -0
  66. package/node_modules/@gdh/core/dist/update-probe.d.ts +102 -0
  67. package/node_modules/@gdh/core/dist/update-probe.d.ts.map +1 -0
  68. package/node_modules/@gdh/core/dist/update-probe.js +195 -0
  69. package/node_modules/@gdh/core/dist/update-probe.js.map +1 -0
  70. package/node_modules/@gdh/core/package.json +1 -1
  71. package/node_modules/@gdh/docs/package.json +2 -2
  72. package/node_modules/@gdh/mcp/dist/index.d.ts +20 -0
  73. package/node_modules/@gdh/mcp/dist/index.d.ts.map +1 -1
  74. package/node_modules/@gdh/mcp/dist/index.js +39 -2
  75. package/node_modules/@gdh/mcp/dist/index.js.map +1 -1
  76. package/node_modules/@gdh/mcp/package.json +8 -8
  77. package/node_modules/@gdh/observability/dist/guidance-audit.js +2 -1
  78. package/node_modules/@gdh/observability/dist/guidance-audit.js.map +1 -1
  79. package/node_modules/@gdh/observability/package.json +2 -2
  80. package/node_modules/@gdh/runtime/package.json +2 -2
  81. package/node_modules/@gdh/scan/package.json +3 -3
  82. package/node_modules/@gdh/verify/package.json +7 -7
  83. package/package.json +11 -11
@@ -0,0 +1,155 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { resolvePinnedVersion, resolvePinnedVersionOrNull, writePinnedVersion, } from "@gdh/authoring";
4
+ import { resolveEffectiveDevRepoPath } from "@gdh/core";
5
+ // Dynamic import breaks the circular dependency with ./index.ts.
6
+ //
7
+ // self-update-mechanics.ts is re-exported from adapters/index.ts (the barrel).
8
+ // installSupportedAgentAdapters is defined in index.ts. A static import of
9
+ // index.ts from here creates a circular reference that Vite SSR resolves with
10
+ // a partial module object, making some exports undefined at call time.
11
+ //
12
+ // All imports from @gdh/authoring and @gdh/core remain static: the circular dep
13
+ // only applies to ./index.js within the same package. Vite's tsconfig path
14
+ // mapping correctly resolves cross-package static imports to source files.
15
+ // The dynamic import() for ./index.js is the only exception: it defers the
16
+ // intra-package circular resolution to call time, after all modules initialize.
17
+ async function loadInstallSupportedAgentAdapters() {
18
+ const { installSupportedAgentAdapters } = await import("./index.js");
19
+ return installSupportedAgentAdapters;
20
+ }
21
+ /**
22
+ * Phase 12 MIG-01 + MIG-02 orchestrator. Atomically bumps the .gdh/project.yaml
23
+ * `gdh_version` pin and re-bakes every managed surface at the new pin.
24
+ *
25
+ * Sequencing invariant (MIG-02): writePinnedVersion is called BEFORE
26
+ * installSupportedAgentAdapters. Because installSupportedAgentAdapters reads
27
+ * the pin from disk once per top-level call (adapters/index.ts:173), pinning
28
+ * first guarantees every renderer bakes the new value.
29
+ *
30
+ * Transactional invariant (MIG-02): on re-bake failure, every captured original
31
+ * is restored. Partial state is not acceptable.
32
+ *
33
+ * SELF-03 leak barrier: dev-mode check gates at the mechanics layer, not only at
34
+ * Phase 13 entry points. Single source of truth.
35
+ */
36
+ export async function bumpAndRebakePin(targetPath, options) {
37
+ // Step 1 (SELF-03 leak barrier): dev-mode short-circuit.
38
+ const devRepoPath = await resolveEffectiveDevRepoPath(targetPath);
39
+ if (devRepoPath !== null) {
40
+ return {
41
+ state: "skipped_dev_mode",
42
+ devRepoPath,
43
+ currentVersion: await resolvePinnedVersionOrNull(targetPath),
44
+ };
45
+ }
46
+ // Step 2: Read current pin (throws with gdh-migrate-apply hint if unonboarded).
47
+ const fromVersion = await resolvePinnedVersion(targetPath);
48
+ if (fromVersion === options.targetVersion) {
49
+ return { state: "noop", currentVersion: fromVersion };
50
+ }
51
+ // Step 3: Plan — dry-run to collect actions. File SET is determined by adapter
52
+ // status + guidance readiness, not by pin value; planning against current pin
53
+ // tells us which files to capture. Per research recommendation (c).
54
+ const installSupportedAgentAdapters = await loadInstallSupportedAgentAdapters();
55
+ const plan = await installSupportedAgentAdapters(targetPath, {
56
+ dryRun: true,
57
+ allowBootstrap: true,
58
+ });
59
+ // Step 4: Capture originals (including .gdh/project.yaml) before any mutation.
60
+ const configPath = path.join(targetPath, ".gdh/project.yaml");
61
+ const originals = await captureOriginals(plan.actions, configPath);
62
+ if (options.dryRun === true) {
63
+ return {
64
+ state: "updated",
65
+ fromVersion,
66
+ toVersion: options.targetVersion,
67
+ actions: plan.actions,
68
+ };
69
+ }
70
+ // Step 5: COMMIT POINT — write new pin first (MIG-02 sequencing).
71
+ // Phase 12 Check 43 asserts writePinnedVersion call textually precedes the
72
+ // final installSupportedAgentAdapters call in this source file. Both calls
73
+ // are inside bumpAndRebakePin; the grep/awk check targets the function body.
74
+ try {
75
+ await writePinnedVersion(targetPath, options.targetVersion);
76
+ }
77
+ catch (error) {
78
+ // Nothing else mutated yet — no rollback needed.
79
+ return { state: "blocked", reason: `Pin write failed: ${formatError(error)}` };
80
+ }
81
+ // Step 6: Re-bake — installSupportedAgentAdapters reads the new pin from disk.
82
+ try {
83
+ const rebake = await installSupportedAgentAdapters(targetPath, {
84
+ allowBootstrap: true,
85
+ });
86
+ const blocked = rebake.actions.filter((a) => a.state === "blocked");
87
+ if (blocked.length > 0) {
88
+ throw new Error(`Re-bake had ${blocked.length} blocked action(s): ${blocked
89
+ .map((a) => a.summary)
90
+ .join("; ")}`);
91
+ }
92
+ return {
93
+ state: "updated",
94
+ fromVersion,
95
+ toVersion: options.targetVersion,
96
+ actions: rebake.actions,
97
+ };
98
+ }
99
+ catch (error) {
100
+ const restoreFailures = await rollbackOriginals(originals);
101
+ return {
102
+ state: "rolled_back",
103
+ fromVersion,
104
+ attemptedToVersion: options.targetVersion,
105
+ failureReason: formatError(error),
106
+ restoreFailures,
107
+ };
108
+ }
109
+ }
110
+ /**
111
+ * Capture current content of every file the planned actions will touch, plus the
112
+ * pin config. A `null` value means the file did not exist — rollback is `fs.rm`.
113
+ * Skips actions with null absolutePath (run_command, write_local_hints abstract actions).
114
+ */
115
+ async function captureOriginals(plannedActions, pinConfigPath) {
116
+ const originals = new Map();
117
+ originals.set(pinConfigPath, await fs.readFile(pinConfigPath, "utf8"));
118
+ for (const action of plannedActions) {
119
+ if (action.absolutePath === null)
120
+ continue;
121
+ if (originals.has(action.absolutePath))
122
+ continue;
123
+ const existing = await fs
124
+ .readFile(action.absolutePath, "utf8")
125
+ .catch(() => null);
126
+ originals.set(action.absolutePath, existing);
127
+ }
128
+ return originals;
129
+ }
130
+ /**
131
+ * Best-effort restore. Iterates captured originals; restores content or rm's
132
+ * previously-absent files. Collects failure messages but continues (rollback-soldier
133
+ * pattern: partial recovery > aborting mid-rollback).
134
+ */
135
+ async function rollbackOriginals(originals) {
136
+ const failures = [];
137
+ for (const [absolutePath, content] of originals) {
138
+ try {
139
+ if (content === null) {
140
+ await fs.rm(absolutePath, { force: true });
141
+ }
142
+ else {
143
+ await fs.writeFile(absolutePath, content, "utf8");
144
+ }
145
+ }
146
+ catch (error) {
147
+ failures.push(`${absolutePath}: ${error instanceof Error ? error.message : String(error)}`);
148
+ }
149
+ }
150
+ return failures;
151
+ }
152
+ function formatError(error) {
153
+ return error instanceof Error ? error.message : String(error);
154
+ }
155
+ //# sourceMappingURL=self-update-mechanics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-update-mechanics.js","sourceRoot":"","sources":["../src/self-update-mechanics.ts"],"names":[],"mappings":"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"}
@@ -11,13 +11,13 @@
11
11
  }
12
12
  },
13
13
  "dependencies": {
14
- "@gdh/authoring": "0.5.0",
15
- "@gdh/core": "0.5.0",
16
- "@gdh/docs": "0.5.0",
17
- "@gdh/observability": "0.5.0",
18
- "@gdh/runtime": "0.5.0",
19
- "@gdh/scan": "0.5.0",
20
- "@gdh/verify": "0.5.0"
14
+ "@gdh/authoring": "0.7.0",
15
+ "@gdh/core": "0.7.0",
16
+ "@gdh/docs": "0.7.0",
17
+ "@gdh/observability": "0.7.0",
18
+ "@gdh/runtime": "0.7.0",
19
+ "@gdh/scan": "0.7.0",
20
+ "@gdh/verify": "0.7.0"
21
21
  },
22
- "version": "0.5.0"
22
+ "version": "0.7.0"
23
23
  }
@@ -6,6 +6,7 @@ export declare function runAuthoringCheck(input: {
6
6
  readonly projectConfig: GdhProjectConfig | null;
7
7
  }): Promise<GdhAuthoringCheckResult>;
8
8
  export { hasCompleteOnboardingSurface, parseProjectConfigYaml, readProjectConfig, resolvePinnedVersion, resolvePinnedVersionOrNull, resolveProjectRoot, readWorktreeState, resolveAuthoringStatus, resolveConfiguredCapabilities, resolveTargetGodotDocsVersion, } from "./project.js";
9
+ export { writePinnedVersion } from "./writePinnedVersion.js";
9
10
  export { getManagedLspStatus, resetManagedLspTestState, runManagedAuthoringCheck, } from "./lsp.js";
10
11
  export { inspectCacheState, pruneCacheState, resolveCacheNamespace } from "./cache.js";
11
12
  export { runImportRefresh, runTargetPrepare } from "./prepare.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAOnB,eAAO,MAAM,gBAAgB,wCAM3B,CAAC;AAEH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACjD,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAqBnC;AAED,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAOnB,eAAO,MAAM,gBAAgB,wCAM3B,CAAC;AAEH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,wBAAwB,CAAC;IAC1C,QAAQ,CAAC,aAAa,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACjD,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAqBnC;AAED,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
@@ -30,6 +30,7 @@ export async function runAuthoringCheck(input) {
30
30
  });
31
31
  }
32
32
  export { hasCompleteOnboardingSurface, parseProjectConfigYaml, readProjectConfig, resolvePinnedVersion, resolvePinnedVersionOrNull, resolveProjectRoot, readWorktreeState, resolveAuthoringStatus, resolveConfiguredCapabilities, resolveTargetGodotDocsVersion, } from "./project.js";
33
+ export { writePinnedVersion } from "./writePinnedVersion.js";
33
34
  export { getManagedLspStatus, resetManagedLspTestState, runManagedAuthoringCheck, } from "./lsp.js";
34
35
  export { inspectCacheState, pruneCacheState, resolveCacheNamespace } from "./cache.js";
35
36
  export { runImportRefresh, runTargetPrepare } from "./prepare.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAItB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;IACpD,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,YAAY;IACnB,cAAc,EACZ,kGAAkG;IACpG,2BAA2B,EAAE,CAAC,WAAW,CAAC;CAC3C,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAIvC;IACC,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC;QAC1C,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,KAAK,EAAE,qBAAqB;KAC7B,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,MAAM,wBAAwB,CAAC;QAChD,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,SAAS;KACV,CAAC,CAAC;IAEH,OAAO,0BAA0B,CAAC;QAChC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,MAAM,EAAE,UAAU;KACnB,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAItB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;IACpD,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,YAAY;IACnB,cAAc,EACZ,kGAAkG;IACpG,2BAA2B,EAAE,CAAC,WAAW,CAAC;CAC3C,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAIvC;IACC,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC;QAC1C,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,KAAK,EAAE,qBAAqB;KAC7B,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,MAAM,wBAAwB,CAAC;QAChD,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,SAAS;KACV,CAAC,CAAC;IAEH,OAAO,0BAA0B,CAAC;QAChC,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,MAAM,EAAE,UAAU;KACnB,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Replaces the EXISTING `gdh_version` line in `.gdh/project.yaml` with a new pin.
3
+ * Distinct from `rewriteProjectConfig` (packages/cli/src/migrate.ts:843), which
4
+ * ONLY adds the field when missing. Phase 12 MIG-01: self-update needs the
5
+ * update-existing path; `gdh migrate` stays pin-preserving.
6
+ *
7
+ * Atomicity: writes temp file + fs.rename. POSIX guarantees rename atomicity
8
+ * within a single filesystem. Keep `.tmp-<pid>` sibling to the final path so
9
+ * both live in `.gdh/` — crossing filesystems fails with EXDEV.
10
+ *
11
+ * Throws:
12
+ * - Error (/exact SemVer/) when newVersion does not match EXACT_SEMVER_PATTERN
13
+ * - Error (/gdh migrate --apply/) when .gdh/project.yaml is missing gdh_version
14
+ * - underlying fs errors (ENOENT on missing .gdh/project.yaml, EACCES, etc.)
15
+ */
16
+ export declare function writePinnedVersion(targetPath: string, newVersion: string): Promise<void>;
17
+ //# sourceMappingURL=writePinnedVersion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writePinnedVersion.d.ts","sourceRoot":"","sources":["../src/writePinnedVersion.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyC9F"}
@@ -0,0 +1,50 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { EXACT_SEMVER_PATTERN } from "./project.js";
4
+ /**
5
+ * Replaces the EXISTING `gdh_version` line in `.gdh/project.yaml` with a new pin.
6
+ * Distinct from `rewriteProjectConfig` (packages/cli/src/migrate.ts:843), which
7
+ * ONLY adds the field when missing. Phase 12 MIG-01: self-update needs the
8
+ * update-existing path; `gdh migrate` stays pin-preserving.
9
+ *
10
+ * Atomicity: writes temp file + fs.rename. POSIX guarantees rename atomicity
11
+ * within a single filesystem. Keep `.tmp-<pid>` sibling to the final path so
12
+ * both live in `.gdh/` — crossing filesystems fails with EXDEV.
13
+ *
14
+ * Throws:
15
+ * - Error (/exact SemVer/) when newVersion does not match EXACT_SEMVER_PATTERN
16
+ * - Error (/gdh migrate --apply/) when .gdh/project.yaml is missing gdh_version
17
+ * - underlying fs errors (ENOENT on missing .gdh/project.yaml, EACCES, etc.)
18
+ */
19
+ export async function writePinnedVersion(targetPath, newVersion) {
20
+ if (!EXACT_SEMVER_PATTERN.test(newVersion)) {
21
+ throw new Error(`Cannot pin \`gdh_version\`: ${JSON.stringify(newVersion)} is not an exact SemVer. ` +
22
+ "Ranges (^, ~), dist-tags (latest, next), and local paths are rejected.");
23
+ }
24
+ const configPath = path.join(targetPath, ".gdh/project.yaml");
25
+ const original = await fs.readFile(configPath, "utf8");
26
+ // Mirror the EXACT regex migrate.ts:843 uses to DETECT the pin; inverted guard here.
27
+ if (!/^gdh_version:/m.test(original)) {
28
+ throw new Error(".gdh/project.yaml is missing the `gdh_version` field. " +
29
+ "Run `gdh migrate --apply` to bootstrap the pin before invoking self-update.");
30
+ }
31
+ // JSON.stringify produces "<v>" (double-quoted) — matches migrate.ts:845 wire format
32
+ // and parseProjectConfigYaml expectations (project.ts:461 parseYamlStringValue).
33
+ const updated = original.replace(/^gdh_version:.*\n/m, `gdh_version: ${JSON.stringify(newVersion)}\n`);
34
+ if (updated === original) {
35
+ // Defense-in-depth no-op: caller (bumpAndRebakePin in Plan 02) must detect
36
+ // this upstream via resolvePinnedVersion comparison to emit state: "noop".
37
+ return;
38
+ }
39
+ const tempPath = `${configPath}.tmp-${process.pid}`;
40
+ await fs.writeFile(tempPath, updated, "utf8");
41
+ try {
42
+ await fs.rename(tempPath, configPath);
43
+ }
44
+ catch (error) {
45
+ // Best-effort temp-file cleanup on rename failure; swallow secondary errors.
46
+ await fs.rm(tempPath, { force: true }).catch(() => { });
47
+ throw error;
48
+ }
49
+ }
50
+ //# sourceMappingURL=writePinnedVersion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writePinnedVersion.js","sourceRoot":"","sources":["../src/writePinnedVersion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAkB,EAAE,UAAkB;IAC7E,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,+BAA+B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,2BAA2B;YAClF,wEAAwE,CAC3E,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAEvD,qFAAqF;IACrF,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,wDAAwD;YACtD,6EAA6E,CAChF,CAAC;IACJ,CAAC;IAED,qFAAqF;IACrF,iFAAiF;IACjF,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAC9B,oBAAoB,EACpB,gBAAgB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,CAC/C,CAAC;IAEF,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,2EAA2E;QAC3E,2EAA2E;QAC3E,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,UAAU,QAAQ,OAAO,CAAC,GAAG,EAAE,CAAC;IACpD,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,6EAA6E;QAC7E,MAAM,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACvD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -10,8 +10,11 @@
10
10
  "import": "./dist/index.js"
11
11
  }
12
12
  },
13
+ "scripts": {
14
+ "test": "vitest run"
15
+ },
13
16
  "dependencies": {
14
- "@gdh/core": "0.5.0"
17
+ "@gdh/core": "0.7.0"
15
18
  },
16
- "version": "0.5.0"
19
+ "version": "0.7.0"
17
20
  }
@@ -1,7 +1,22 @@
1
+ import { recordAuthoringSessionEvent } from "@gdh/observability";
1
2
  export declare const cliPackage: import("@gdh/core").GdhPackageBoundary;
2
3
  export interface CliIo {
3
4
  readonly stdout: Pick<NodeJS.WriteStream, "write">;
4
5
  readonly stderr: Pick<NodeJS.WriteStream, "write">;
5
6
  }
6
7
  export declare function runCli(args: readonly string[], io?: CliIo): Promise<number>;
8
+ export declare function writeJsonResult(io: CliIo, value: unknown, options?: {
9
+ readonly presentRuntimeTerms?: boolean;
10
+ }): void;
11
+ export declare function formatCliError(error: unknown): string;
12
+ export declare function readSingleOptionValue(args: readonly string[], optionName: string): string | null;
13
+ export declare function findUnsupportedOptionsError(args: readonly string[], options: {
14
+ readonly usage: string;
15
+ readonly optionsWithValues?: readonly string[];
16
+ readonly booleanOptions?: readonly string[];
17
+ }): string | null;
18
+ export declare function recordSessionEvent(targetPath: string, input: Parameters<typeof recordAuthoringSessionEvent>[1] & {
19
+ readonly commandStartedAtMs?: number;
20
+ }): Promise<void>;
21
+ export declare function extractPositionalArgs(args: readonly string[], optionsWithValues: ReadonlySet<string>): readonly string[];
7
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAsHA,eAAO,MAAM,UAAU,wCAerB,CAAC;AAEH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CACpD;AAED,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,GAAE,KAA0D,GAC7D,OAAO,CAAC,MAAM,CAAC,CA2GjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2EA,OAAO,EAIL,2BAA2B,EAC5B,MAAM,oBAAoB,CAAC;AA2C5B,eAAO,MAAM,UAAU,wCAerB,CAAC;AAEH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CACpD;AAED,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,GAAE,KAA0D,GAC7D,OAAO,CAAC,MAAM,CAAC,CA+GjB;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,KAAK,EACT,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IAAE,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAO,GACvD,IAAI,CAGN;AAy+ID,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAErD;AAkFD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAchG;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE;IACP,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C,GACA,MAAM,GAAG,IAAI,CAcf;AA8ED,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC,CAAC,CAAC,GAAG;IACzD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC,GACA,OAAO,CAAC,IAAI,CAAC,CAef;AAwFD,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,GACrC,SAAS,MAAM,EAAE,CAuBnB"}
@@ -1,7 +1,10 @@
1
+ import { spawnSync } from "node:child_process";
1
2
  import fs from "node:fs/promises";
3
+ import fsSync from "node:fs";
2
4
  import os from "node:os";
3
5
  import path from "node:path";
4
- import { buildGdhStatusResult, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, CODEX_CHECK_SKILL_RELATIVE_PATH, CODEX_MIGRATE_SKILL_RELATIVE_PATH, CODEX_ONBOARD_SKILL_RELATIVE_PATH, CODEX_PREPARE_SKILL_RELATIVE_PATH, CODEX_STATUS_SKILL_RELATIVE_PATH, CODEX_VERIFY_SKILL_RELATIVE_PATH, createGsdSnapshot, CURSOR_CHECK_SKILL_RELATIVE_PATH, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, CURSOR_ONBOARD_SKILL_RELATIVE_PATH, CURSOR_PREPARE_SKILL_RELATIVE_PATH, CURSOR_STATUS_SKILL_RELATIVE_PATH, CURSOR_VERIFY_SKILL_RELATIVE_PATH, getSupportedAgentAdaptersStatus, installSupportedAgentAdapters, MCP_LAUNCHER_RELATIVE_PATH, } from "@gdh/adapters";
6
+ import { fileURLToPath } from "node:url";
7
+ import { buildGdhStatusResult, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, CODEX_CHECK_SKILL_RELATIVE_PATH, CODEX_MIGRATE_SKILL_RELATIVE_PATH, CODEX_ONBOARD_SKILL_RELATIVE_PATH, CODEX_PREPARE_SKILL_RELATIVE_PATH, CODEX_STATUS_SKILL_RELATIVE_PATH, CODEX_VERIFY_SKILL_RELATIVE_PATH, createGsdSnapshot, CURSOR_CHECK_SKILL_RELATIVE_PATH, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, CURSOR_ONBOARD_SKILL_RELATIVE_PATH, CURSOR_PREPARE_SKILL_RELATIVE_PATH, CURSOR_STATUS_SKILL_RELATIVE_PATH, CURSOR_VERIFY_SKILL_RELATIVE_PATH, getSupportedAgentAdaptersStatus, installSupportedAgentAdapters, } from "@gdh/adapters";
5
8
  import { getManagedLspStatus, hasCompleteOnboardingSurface, inspectCacheState, pruneCacheState, readProjectConfig, resolvePinnedVersion, resolveProjectRoot, readWorktreeState, resolveAuthoringStatus, resolveTargetGodotDocsVersion, runAuthoringCheck, runImportRefresh, runTargetPrepare, runWarmup, } from "@gdh/authoring";
6
9
  import { definePackageBoundary, GDH_AUTHORING_DOGFOOD_VERSION, GDH_AUTHORING_SLICE_REPORT_VERSION, GDH_PRODUCT_NAME, resolveCurrentGdhInstall, resolveGdhProductMetadata, } from "@gdh/core";
7
10
  import { fetchOfficialGodotDoc, getGuidanceStatus, resolveGuidanceQuery, searchOfficialGodotDocs, } from "@gdh/docs";
@@ -12,7 +15,9 @@ import { applyRepairableOnboardingWrites, onboardGodotProject, persistInventoryF
12
15
  import { evaluateDonePolicy, exerciseRuntimeCorpusEntry, inspectRuntimeCorpusStatus, inspectRuntimeVerificationBundleState, inspectRuntimeVerificationReadiness, listRuntimeScenarios, materializeRuntimeCorpusEntry, recommendValidationForChange, recordRuntimeCorpusValidation, runRuntimeVerificationScenario, showRuntimeScenario, } from "@gdh/verify";
13
16
  import { migrateProjectLifecycleSurface } from "./migrate.js";
14
17
  import { presentPublicRuntimeTerms } from "./public-terms.js";
18
+ import { runSelfUpdateCommand } from "./self-update.js";
15
19
  import { executeSetupCommand, isSetupCanceledError, renderSetupIntro, renderSetupOutro, renderSetupSummary, } from "./setup.js";
20
+ import { emitUpdateBannerIfStale } from "./update-banner.js";
16
21
  export const cliPackage = definePackageBoundary({
17
22
  name: "@gdh/cli",
18
23
  layer: "surface",
@@ -102,6 +107,9 @@ export async function runCli(args, io = { stdout: process.stdout, stderr: proces
102
107
  if (command === "migrate") {
103
108
  return runMigrateCommand(rest, io);
104
109
  }
110
+ if (command === "self-update") {
111
+ return runSelfUpdateCommand(rest, io);
112
+ }
105
113
  if (command === "cache") {
106
114
  return runCacheCommand(rest, io);
107
115
  }
@@ -111,7 +119,7 @@ export async function runCli(args, io = { stdout: process.stdout, stderr: proces
111
119
  io.stderr.write(`Unknown command: ${command}\n\n${renderHelp()}`);
112
120
  return 1;
113
121
  }
114
- function writeJsonResult(io, value, options = {}) {
122
+ export function writeJsonResult(io, value, options = {}) {
115
123
  const output = options.presentRuntimeTerms ? presentPublicRuntimeTerms(value) : value;
116
124
  io.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
117
125
  }
@@ -386,6 +394,11 @@ async function runStatusCommand(args, io) {
386
394
  }
387
395
  const targetPath = parsedTarget.targetPath;
388
396
  const commandStartedAtMs = Date.now();
397
+ // UPD-04: emit the update banner to stderr BEFORE primary work so the
398
+ // stderr prelude precedes any stdout JSON payload. D-13 scopes the banner
399
+ // to `gdh status` and `gdh verify` only. Silent when cache is null /
400
+ // current / offline / dev-mode (D-06).
401
+ await emitUpdateBannerIfStale(targetPath, io);
389
402
  try {
390
403
  const status = await buildGdhStatusResult(targetPath);
391
404
  await recordSessionEvent(targetPath, {
@@ -1249,7 +1262,7 @@ async function runVerifyCommand(args, io) {
1249
1262
  " corpus <status|record>",
1250
1263
  " Inspect or record runtime corpus release-readiness evidence as JSON.",
1251
1264
  " drift [target]",
1252
- " Check whether baked `@skillcap/gdh@<version>` literals (launcher + skills + commands) match .gdh/project.yaml gdh_version.",
1265
+ " Check whether baked `@skillcap/gdh@<version>` literals (skills + commands) match .gdh/project.yaml gdh_version.",
1253
1266
  " recommend [target] --files <path> [--files <path>...]",
1254
1267
  " Recommend authoring-first validation requirements for the supplied change set.",
1255
1268
  " done [target] --files <path> [--files <path>...] [--performed <kind>]",
@@ -1257,6 +1270,16 @@ async function runVerifyCommand(args, io) {
1257
1270
  ].join("\n") + "\n");
1258
1271
  return 0;
1259
1272
  }
1273
+ // UPD-04: emit the update banner to stderr BEFORE dispatching to verify
1274
+ // subcommands so the stderr prelude precedes any stdout JSON payload. D-13
1275
+ // scopes the banner to `gdh status` and `gdh verify` only. Target for the
1276
+ // banner's pre-onboard re-check is the first non-flag positional arg
1277
+ // after the subcommand (falling back to "."). The cache itself is per-user,
1278
+ // not per-target, so any resolvable path suffices for the dev-mode check.
1279
+ {
1280
+ const verifyTargetPath = rest.find((arg) => !arg.startsWith("--")) ?? ".";
1281
+ await emitUpdateBannerIfStale(verifyTargetPath, io);
1282
+ }
1260
1283
  if (subcommand === "recommend") {
1261
1284
  return runVerifyRecommendCommand(rest, io);
1262
1285
  }
@@ -2078,29 +2101,30 @@ async function runMcpManifestCommand(args, io) {
2078
2101
  async function runMcpServeCommand(args, io) {
2079
2102
  if (args.includes("--help")) {
2080
2103
  io.stdout.write([
2081
- "Usage: gdh mcp serve --target <path>",
2104
+ "Usage: gdh mcp serve [--target <path>]",
2082
2105
  "",
2083
- "Run the real GDH stdio MCP server for one bound target path.",
2106
+ "Run the real GDH stdio MCP server. When --target is omitted, defaults to",
2107
+ "the current working directory and walks up to find the nearest .gdh/project.yaml.",
2084
2108
  ].join("\n") + "\n");
2085
2109
  return 0;
2086
2110
  }
2087
2111
  const unsupportedOptionsError = findUnsupportedOptionsError(args, {
2088
- usage: "Usage: gdh mcp serve --target <path>\n",
2112
+ usage: "Usage: gdh mcp serve [--target <path>]\n",
2089
2113
  optionsWithValues: ["--target"],
2090
2114
  });
2091
2115
  if (unsupportedOptionsError !== null) {
2092
2116
  io.stderr.write(unsupportedOptionsError);
2093
2117
  return 1;
2094
2118
  }
2095
- const targetPath = readSingleOptionValue(args, "--target");
2096
- if (targetPath === null) {
2097
- io.stderr.write("Usage error: gdh mcp serve requires --target <path>.\n");
2098
- return 1;
2099
- }
2100
2119
  if (extractPositionalArgs(args, new Set(["--target"])).length > 0) {
2101
2120
  io.stderr.write("Usage error: gdh mcp serve accepts only --target <path>.\n");
2102
2121
  return 1;
2103
2122
  }
2123
+ const targetPath = readSingleOptionValue(args, "--target") ?? process.cwd();
2124
+ const devRepoExitCode = maybeReexecIntoDevRepo(targetPath);
2125
+ if (devRepoExitCode !== null) {
2126
+ return devRepoExitCode;
2127
+ }
2104
2128
  try {
2105
2129
  await serveMcpOverStdio(targetPath);
2106
2130
  return 0;
@@ -2110,6 +2134,42 @@ async function runMcpServeCommand(args, io) {
2110
2134
  return 1;
2111
2135
  }
2112
2136
  }
2137
+ function maybeReexecIntoDevRepo(targetPath) {
2138
+ if (process.env["GDH_MCP_DEV_REEXEC"] === "1") {
2139
+ // Already running under a dev-repo re-exec; skip to avoid a loop.
2140
+ return null;
2141
+ }
2142
+ const envDevRepoPath = process.env["GDH_DEV_REPO"]?.trim() || null;
2143
+ const hintsPath = path.join(path.resolve(targetPath), ".gdh-state", "local-paths.json");
2144
+ let hintDevRepoPath = null;
2145
+ try {
2146
+ const raw = fsSync.readFileSync(hintsPath, "utf8");
2147
+ const parsed = JSON.parse(raw);
2148
+ if (typeof parsed.gdhDevRepoPath === "string" && parsed.gdhDevRepoPath.length > 0) {
2149
+ hintDevRepoPath = parsed.gdhDevRepoPath;
2150
+ }
2151
+ }
2152
+ catch {
2153
+ // No hints file or unparseable — fall through.
2154
+ }
2155
+ const devRepoPath = envDevRepoPath || hintDevRepoPath;
2156
+ if (devRepoPath === null) {
2157
+ return null;
2158
+ }
2159
+ const devCliEntry = path.join(devRepoPath, "packages/cli/dist/cli.js");
2160
+ if (!fsSync.existsSync(devCliEntry)) {
2161
+ return null;
2162
+ }
2163
+ const currentCliPath = path.resolve(fileURLToPath(import.meta.url));
2164
+ const resolvedDevRepoPath = path.resolve(devRepoPath);
2165
+ // Avoid loop if the currently-running CLI source already lives inside the
2166
+ // dev repo (e.g. contributor running the dev CLI directly via tsx).
2167
+ if (currentCliPath.startsWith(`${resolvedDevRepoPath}${path.sep}`)) {
2168
+ return null;
2169
+ }
2170
+ const result = spawnSync(process.execPath, [devCliEntry, "mcp", "serve", "--target", path.resolve(targetPath)], { stdio: "inherit", env: { ...process.env, GDH_MCP_DEV_REEXEC: "1" } });
2171
+ return result.status ?? 1;
2172
+ }
2113
2173
  async function runMcpInvokeCommand(args, io) {
2114
2174
  if (args.includes("--help")) {
2115
2175
  io.stdout.write([
@@ -2522,7 +2582,6 @@ async function runVerifyReadinessCommand(args, io) {
2522
2582
  }
2523
2583
  }
2524
2584
  const VERIFY_DRIFT_SCANNED_FILES = [
2525
- { relativePath: MCP_LAUNCHER_RELATIVE_PATH, description: "managed MCP launcher" },
2526
2585
  { relativePath: CODEX_ONBOARD_SKILL_RELATIVE_PATH, description: "Codex gdh-onboard skill" },
2527
2586
  { relativePath: CODEX_STATUS_SKILL_RELATIVE_PATH, description: "Codex gdh-status skill" },
2528
2587
  { relativePath: CODEX_MIGRATE_SKILL_RELATIVE_PATH, description: "Codex gdh-migrate skill" },
@@ -2541,6 +2600,46 @@ const VERIFY_DRIFT_SCANNED_FILES = [
2541
2600
  { relativePath: CLAUDE_CHECK_COMMAND_RELATIVE_PATH, description: "Claude gdh/check command" },
2542
2601
  { relativePath: CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, description: "Claude gdh/prepare command" },
2543
2602
  { relativePath: CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, description: "Claude gdh/verify command" },
2603
+ // Phase 12 extension — close the Phase 11 coverage gap (research §Baked Version Surface Inventory rows 26-28).
2604
+ // These files are re-baked at pin time and must be audited by MIG-02's post-condition invariant.
2605
+ //
2606
+ // SessionStart hook: bakes `GDH_PINNED_VERSION: "<semver>"` (not an npx literal). Uses a
2607
+ // dedicated versionRegex to extract the pin from that env-injection pattern.
2608
+ {
2609
+ relativePath: CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH,
2610
+ description: "Claude SessionStart update hook",
2611
+ // The hook body sets GDH_PINNED_VERSION: "<semver>" in the spawned worker's env.
2612
+ // VERIFY_DRIFT_BAKED_VERSION_REGEX matches `@skillcap/gdh@<v>` npx literals; this
2613
+ // supplementary regex extracts the pin from the env-injection literal instead.
2614
+ versionRegex: /GDH_PINNED_VERSION:\s*"(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)"/,
2615
+ },
2616
+ // Intentionally excluded: the Claude background worker (gdh-check-update-worker.js) — the
2617
+ // worker script body reads GDH_PINNED_VERSION from process.env at runtime and does NOT contain
2618
+ // an `@skillcap/gdh@<v>` literal or any other baked-version pattern. Adding it would cause
2619
+ // VERIFY_DRIFT_BAKED_VERSION_REGEX to report `no_baked_version` → false positive. Worker drift
2620
+ // is audited via the `// gdh-hook-version: N` marker through the renderer-layer contract, not
2621
+ // via this version-literal scanner. Covered in integration tests at
2622
+ // packages/adapters/src/claude-update-install-integration.test.ts.
2623
+ //
2624
+ // Intentionally excluded: the Claude statusline (gdh-statusline.js) — the statusline renderer
2625
+ // produces a version-agnostic script (reads the update cache at runtime; no semver literal baked
2626
+ // in the output). Adding it would permanently report `no_baked_version` → false positive. The
2627
+ // statusline IS re-baked on every `gdh adapters install` via the renderer pipeline, which ensures
2628
+ // it stays current with the hook version; pin-literal drift detection is not applicable here.
2629
+ //
2630
+ // Intentionally excluded: the Claude /gdh-update command (.claude/commands/gdh/update.md) —
2631
+ // the rendered command shells out to `npx -y @skillcap/gdh@latest self-update` (LITERAL @latest,
2632
+ // not @<pinnedVersion>). The shellout MUST bake @latest so the NEW CLI performs the update,
2633
+ // not the OLD (potentially buggy) pinned one. Because @latest is not a semver literal, the
2634
+ // VERIFY_DRIFT_BAKED_VERSION_REGEX finds no pinned-version match and would permanently report
2635
+ // `no_baked_version` → false positive. Phase 13 D-13 locks this exclusion; Check 44 in
2636
+ // scripts/validate-docs.mjs enforces both the @latest invariant and the exclusion.
2637
+ //
2638
+ // Intentionally excluded: the Codex gdh-update skill (.codex/skills/gdh-update/SKILL.md) —
2639
+ // same rationale: renders @skillcap/gdh@latest, not @<pinnedVersion>. Phase 13 D-13.
2640
+ //
2641
+ // Intentionally excluded: the Cursor gdh-update skill (.cursor/skills/gdh-update/SKILL.md) —
2642
+ // same rationale: renders @skillcap/gdh@latest, not @<pinnedVersion>. Phase 13 D-13.
2544
2643
  ];
2545
2644
  // Mirrors EXACT_SEMVER_PATTERN in @gdh/authoring so only well-formed SemVer
2546
2645
  // values match — drift check does not pick up unrelated doc prose.
@@ -2551,8 +2650,8 @@ async function runVerifyDriftCommand(args, io) {
2551
2650
  "Usage: gdh verify drift [target]",
2552
2651
  "",
2553
2652
  "Compares every baked `@skillcap/gdh@<version>` literal across the managed",
2554
- "surfaces (.gdh/bin/gdh-mcp.mjs launcher + regenerated Codex/Cursor/Claude",
2555
- "skill + command files) against .gdh/project.yaml gdh_version. Exits 1 and",
2653
+ "surfaces (regenerated Codex/Cursor/Claude skill + command files) against",
2654
+ ".gdh/project.yaml gdh_version. Exits 1 and",
2556
2655
  "emits a structured JSON result when ANY file's baked version diverges from",
2557
2656
  "the pinned value. Covers ROADMAP Success Criterion #3 (one source of truth).",
2558
2657
  "",
@@ -2608,7 +2707,7 @@ async function runVerifyDriftCommand(args, io) {
2608
2707
  reason: "file_missing",
2609
2708
  };
2610
2709
  }
2611
- const match = content.match(VERIFY_DRIFT_BAKED_VERSION_REGEX);
2710
+ const match = content.match(file.versionRegex ?? VERIFY_DRIFT_BAKED_VERSION_REGEX);
2612
2711
  const baked = match?.[1] ?? null;
2613
2712
  const reason = baked === null
2614
2713
  ? "no_baked_version"
@@ -3733,7 +3832,7 @@ function renderHelp() {
3733
3832
  "Run `gdh <command> --help` for command-specific usage.",
3734
3833
  ].join("\n") + "\n");
3735
3834
  }
3736
- function formatCliError(error) {
3835
+ export function formatCliError(error) {
3737
3836
  return error instanceof Error ? error.message : String(error);
3738
3837
  }
3739
3838
  function resolveCachePruneScope(args) {
@@ -3791,7 +3890,7 @@ function collectAssignmentOptionValues(args, optionName) {
3791
3890
  }
3792
3891
  return assignments;
3793
3892
  }
3794
- function readSingleOptionValue(args, optionName) {
3893
+ export function readSingleOptionValue(args, optionName) {
3795
3894
  const index = args.indexOf(optionName);
3796
3895
  if (index === -1) {
3797
3896
  return null;
@@ -3802,7 +3901,7 @@ function readSingleOptionValue(args, optionName) {
3802
3901
  }
3803
3902
  return value;
3804
3903
  }
3805
- function findUnsupportedOptionsError(args, options) {
3904
+ export function findUnsupportedOptionsError(args, options) {
3806
3905
  const supportedOptions = new Set([
3807
3906
  ...(options.optionsWithValues ?? []),
3808
3907
  ...(options.booleanOptions ?? []),
@@ -3864,7 +3963,7 @@ function buildGuidanceSnapshot(result) {
3864
3963
  recommendedUnitIds: result.recommendedUnits.map((unit) => unit.id),
3865
3964
  };
3866
3965
  }
3867
- async function recordSessionEvent(targetPath, input) {
3966
+ export async function recordSessionEvent(targetPath, input) {
3868
3967
  try {
3869
3968
  const { commandStartedAtMs, ...eventInput } = input;
3870
3969
  const startedAt = typeof commandStartedAtMs === "number" && Number.isFinite(commandStartedAtMs)
@@ -3939,7 +4038,7 @@ function extractGuidanceResolvePositionalArgs(args) {
3939
4038
  function extractRecipePositionalArgs(args) {
3940
4039
  return extractPositionalArgs(args, new Set(["--provider", "--param", "--feature", "--no-feature", "--env"]));
3941
4040
  }
3942
- function extractPositionalArgs(args, optionsWithValues) {
4041
+ export function extractPositionalArgs(args, optionsWithValues) {
3943
4042
  const positionalArgs = [];
3944
4043
  for (let index = 0; index < args.length; index += 1) {
3945
4044
  const arg = args[index];