@skillcap/gdh 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL-BUNDLE.json +1 -1
- package/README.md +54 -92
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.d.ts +74 -0
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.js +158 -0
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.d.ts +51 -0
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.js +80 -0
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.d.ts +35 -0
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js +76 -0
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.d.ts +28 -0
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js +99 -0
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/index.d.ts +34 -18
- package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/index.js +596 -145
- package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts +51 -0
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.js +155 -0
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.js.map +1 -0
- package/node_modules/@gdh/adapters/package.json +8 -8
- package/node_modules/@gdh/authoring/dist/index.d.ts +2 -1
- package/node_modules/@gdh/authoring/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/authoring/dist/index.js +2 -1
- package/node_modules/@gdh/authoring/dist/index.js.map +1 -1
- package/node_modules/@gdh/authoring/dist/project.d.ts +24 -0
- package/node_modules/@gdh/authoring/dist/project.d.ts.map +1 -1
- package/node_modules/@gdh/authoring/dist/project.js +51 -1
- package/node_modules/@gdh/authoring/dist/project.js.map +1 -1
- package/node_modules/@gdh/authoring/dist/writePinnedVersion.d.ts +17 -0
- package/node_modules/@gdh/authoring/dist/writePinnedVersion.d.ts.map +1 -0
- package/node_modules/@gdh/authoring/dist/writePinnedVersion.js +50 -0
- package/node_modules/@gdh/authoring/dist/writePinnedVersion.js.map +1 -0
- package/node_modules/@gdh/authoring/package.json +5 -2
- package/node_modules/@gdh/cli/dist/index.d.ts +15 -0
- package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/index.js +292 -40
- package/node_modules/@gdh/cli/dist/index.js.map +1 -1
- package/node_modules/@gdh/cli/dist/migrate.d.ts +1 -0
- package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/migrate.js +180 -72
- package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
- package/node_modules/@gdh/cli/dist/self-update.d.ts +3 -0
- package/node_modules/@gdh/cli/dist/self-update.d.ts.map +1 -0
- package/node_modules/@gdh/cli/dist/self-update.js +235 -0
- package/node_modules/@gdh/cli/dist/self-update.js.map +1 -0
- package/node_modules/@gdh/cli/dist/setup.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/setup.js +49 -1
- package/node_modules/@gdh/cli/dist/setup.js.map +1 -1
- package/node_modules/@gdh/cli/dist/update-banner.d.ts +42 -0
- package/node_modules/@gdh/cli/dist/update-banner.d.ts.map +1 -0
- package/node_modules/@gdh/cli/dist/update-banner.js +49 -0
- package/node_modules/@gdh/cli/dist/update-banner.js.map +1 -0
- package/node_modules/@gdh/cli/package.json +10 -10
- package/node_modules/@gdh/core/dist/dev-mode.d.ts +13 -0
- package/node_modules/@gdh/core/dist/dev-mode.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/dev-mode.js +21 -0
- package/node_modules/@gdh/core/dist/dev-mode.js.map +1 -0
- package/node_modules/@gdh/core/dist/index.d.ts +12 -5
- package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/index.js +10 -2
- package/node_modules/@gdh/core/dist/index.js.map +1 -1
- package/node_modules/@gdh/core/dist/update-cache.d.ts +46 -0
- package/node_modules/@gdh/core/dist/update-cache.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/update-cache.js +90 -0
- package/node_modules/@gdh/core/dist/update-cache.js.map +1 -0
- package/node_modules/@gdh/core/dist/update-probe.d.ts +102 -0
- package/node_modules/@gdh/core/dist/update-probe.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/update-probe.js +195 -0
- package/node_modules/@gdh/core/dist/update-probe.js.map +1 -0
- package/node_modules/@gdh/core/package.json +1 -1
- package/node_modules/@gdh/docs/dist/guidance.d.ts.map +1 -1
- package/node_modules/@gdh/docs/dist/guidance.js +47 -0
- package/node_modules/@gdh/docs/dist/guidance.js.map +1 -1
- package/node_modules/@gdh/docs/package.json +2 -2
- package/node_modules/@gdh/mcp/dist/index.d.ts +20 -0
- package/node_modules/@gdh/mcp/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/mcp/dist/index.js +45 -4
- package/node_modules/@gdh/mcp/dist/index.js.map +1 -1
- package/node_modules/@gdh/mcp/package.json +8 -8
- package/node_modules/@gdh/observability/dist/guidance-audit.js +3 -1
- package/node_modules/@gdh/observability/dist/guidance-audit.js.map +1 -1
- package/node_modules/@gdh/observability/package.json +2 -2
- package/node_modules/@gdh/runtime/package.json +2 -2
- package/node_modules/@gdh/scan/dist/index.d.ts +2 -0
- package/node_modules/@gdh/scan/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/scan/dist/index.js +1 -0
- package/node_modules/@gdh/scan/dist/index.js.map +1 -1
- package/node_modules/@gdh/scan/dist/inventory-cache.d.ts +15 -0
- package/node_modules/@gdh/scan/dist/inventory-cache.d.ts.map +1 -0
- package/node_modules/@gdh/scan/dist/inventory-cache.js +53 -0
- package/node_modules/@gdh/scan/dist/inventory-cache.js.map +1 -0
- package/node_modules/@gdh/scan/dist/onboard.d.ts +7 -0
- package/node_modules/@gdh/scan/dist/onboard.d.ts.map +1 -1
- package/node_modules/@gdh/scan/dist/onboard.js +7 -1
- package/node_modules/@gdh/scan/dist/onboard.js.map +1 -1
- package/node_modules/@gdh/scan/package.json +3 -3
- package/node_modules/@gdh/verify/package.json +7 -7
- package/package.json +11 -11
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { buildGdhStatusResult, createGsdSnapshot, getSupportedAgentAdaptersStatus, installSupportedAgentAdapters, } from "@gdh/adapters";
|
|
5
|
-
import { getManagedLspStatus, hasCompleteOnboardingSurface, inspectCacheState, pruneCacheState, readProjectConfig, resolveProjectRoot, readWorktreeState, resolveAuthoringStatus, resolveTargetGodotDocsVersion, runAuthoringCheck, runImportRefresh, runTargetPrepare, runWarmup, } from "@gdh/authoring";
|
|
4
|
+
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, MCP_LAUNCHER_RELATIVE_PATH, } from "@gdh/adapters";
|
|
5
|
+
import { getManagedLspStatus, hasCompleteOnboardingSurface, inspectCacheState, pruneCacheState, readProjectConfig, resolvePinnedVersion, resolveProjectRoot, readWorktreeState, resolveAuthoringStatus, resolveTargetGodotDocsVersion, runAuthoringCheck, runImportRefresh, runTargetPrepare, runWarmup, } from "@gdh/authoring";
|
|
6
6
|
import { definePackageBoundary, GDH_AUTHORING_DOGFOOD_VERSION, GDH_AUTHORING_SLICE_REPORT_VERSION, GDH_PRODUCT_NAME, resolveCurrentGdhInstall, resolveGdhProductMetadata, } from "@gdh/core";
|
|
7
7
|
import { fetchOfficialGodotDoc, getGuidanceStatus, resolveGuidanceQuery, searchOfficialGodotDocs, } from "@gdh/docs";
|
|
8
8
|
import { createMcpManifest, invokeMcpTool, serveMcpOverStdio } from "@gdh/mcp";
|
|
9
9
|
import { inspectAuthoringEffectiveness, inspectAuthoringSessions, inspectGuidanceAudit, recordAuthoringSessionEvent, } from "@gdh/observability";
|
|
10
10
|
import { checkRuntimeRecipe, inspectRuntimeBridgeSurface, installRuntimeBridgeSurface, listRuntimeRecipes, removeRuntimeBridgeSurface, repairRuntimeBridgeSurface, runRuntimeRecipe, showRuntimeRecipe, } from "@gdh/runtime";
|
|
11
|
-
import { applyRepairableOnboardingWrites, onboardGodotProject, scanGodotProjectInventory, } from "@gdh/scan";
|
|
11
|
+
import { applyRepairableOnboardingWrites, onboardGodotProject, persistInventoryForTarget, readInventoryCacheOrScan, scanGodotProjectInventory, } from "@gdh/scan";
|
|
12
12
|
import { evaluateDonePolicy, exerciseRuntimeCorpusEntry, inspectRuntimeCorpusStatus, inspectRuntimeVerificationBundleState, inspectRuntimeVerificationReadiness, listRuntimeScenarios, materializeRuntimeCorpusEntry, recommendValidationForChange, recordRuntimeCorpusValidation, runRuntimeVerificationScenario, showRuntimeScenario, } from "@gdh/verify";
|
|
13
13
|
import { migrateProjectLifecycleSurface } from "./migrate.js";
|
|
14
14
|
import { presentPublicRuntimeTerms } from "./public-terms.js";
|
|
15
|
+
import { runSelfUpdateCommand } from "./self-update.js";
|
|
15
16
|
import { executeSetupCommand, isSetupCanceledError, renderSetupIntro, renderSetupOutro, renderSetupSummary, } from "./setup.js";
|
|
17
|
+
import { emitUpdateBannerIfStale } from "./update-banner.js";
|
|
16
18
|
export const cliPackage = definePackageBoundary({
|
|
17
19
|
name: "@gdh/cli",
|
|
18
20
|
layer: "surface",
|
|
@@ -102,6 +104,9 @@ export async function runCli(args, io = { stdout: process.stdout, stderr: proces
|
|
|
102
104
|
if (command === "migrate") {
|
|
103
105
|
return runMigrateCommand(rest, io);
|
|
104
106
|
}
|
|
107
|
+
if (command === "self-update") {
|
|
108
|
+
return runSelfUpdateCommand(rest, io);
|
|
109
|
+
}
|
|
105
110
|
if (command === "cache") {
|
|
106
111
|
return runCacheCommand(rest, io);
|
|
107
112
|
}
|
|
@@ -111,7 +116,7 @@ export async function runCli(args, io = { stdout: process.stdout, stderr: proces
|
|
|
111
116
|
io.stderr.write(`Unknown command: ${command}\n\n${renderHelp()}`);
|
|
112
117
|
return 1;
|
|
113
118
|
}
|
|
114
|
-
function writeJsonResult(io, value, options = {}) {
|
|
119
|
+
export function writeJsonResult(io, value, options = {}) {
|
|
115
120
|
const output = options.presentRuntimeTerms ? presentPublicRuntimeTerms(value) : value;
|
|
116
121
|
io.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
117
122
|
}
|
|
@@ -134,12 +139,51 @@ async function runScanCommand(args, io) {
|
|
|
134
139
|
return 1;
|
|
135
140
|
}
|
|
136
141
|
const targetPath = parsedTarget.targetPath;
|
|
142
|
+
const commandStartedAtMs = Date.now();
|
|
143
|
+
let onboardedRoot = null;
|
|
137
144
|
try {
|
|
138
|
-
|
|
139
|
-
|
|
145
|
+
onboardedRoot = await resolveProjectRoot(targetPath);
|
|
146
|
+
// Use readInventoryCacheOrScan so that a re-scan of an already-persisted, unchanged
|
|
147
|
+
// filesystem returns the cached inventory byte-for-byte identical to what is on disk.
|
|
148
|
+
// This lets persistInventoryForTarget detect mode: "unchanged" on repeat invocations,
|
|
149
|
+
// keeping the semantics idempotent for unmodified projects (REFR-01 TBD-04).
|
|
150
|
+
// For non-onboarded targets (onboardedRoot === null), fall back to a plain live scan.
|
|
151
|
+
const inventoryRoot = onboardedRoot ?? targetPath;
|
|
152
|
+
const inventoryResult = await readInventoryCacheOrScan(inventoryRoot);
|
|
153
|
+
const inventory = inventoryResult.inventory;
|
|
154
|
+
if (onboardedRoot !== null) {
|
|
155
|
+
const persisted = await persistInventoryForTarget(inventory, onboardedRoot);
|
|
156
|
+
await recordSessionEvent(onboardedRoot, {
|
|
157
|
+
commandStartedAtMs,
|
|
158
|
+
kind: "scan",
|
|
159
|
+
command: "gdh scan",
|
|
160
|
+
state: "succeeded",
|
|
161
|
+
summary: `Scanned target and persisted inventory to ${persisted.relativePath} (${persisted.mode}).`,
|
|
162
|
+
producedArtifacts: persisted.mode !== "unchanged" ? [persisted.relativePath] : [],
|
|
163
|
+
});
|
|
164
|
+
io.stdout.write(`${JSON.stringify({ inventory, persisted }, null, 2)}\n`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
io.stdout.write(`${JSON.stringify({ inventory, persisted: null }, null, 2)}\n`);
|
|
168
|
+
}
|
|
140
169
|
return 0;
|
|
141
170
|
}
|
|
142
171
|
catch (error) {
|
|
172
|
+
// Best-effort: if walk-up hasn't run yet, try it once in the catch path so we can still
|
|
173
|
+
// emit the session event when an onboarded root exists but the live scan threw.
|
|
174
|
+
if (onboardedRoot === null) {
|
|
175
|
+
onboardedRoot = await resolveProjectRoot(targetPath).catch(() => null);
|
|
176
|
+
}
|
|
177
|
+
if (onboardedRoot !== null) {
|
|
178
|
+
await recordSessionEvent(onboardedRoot, {
|
|
179
|
+
commandStartedAtMs,
|
|
180
|
+
kind: "scan",
|
|
181
|
+
command: "gdh scan",
|
|
182
|
+
state: "failed",
|
|
183
|
+
summary: "gdh scan failed before completing inventory write.",
|
|
184
|
+
errorMessage: formatCliError(error),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
143
187
|
io.stderr.write(`Failed to scan target "${targetPath}": ${formatCliError(error)}\n`);
|
|
144
188
|
return 1;
|
|
145
189
|
}
|
|
@@ -347,6 +391,11 @@ async function runStatusCommand(args, io) {
|
|
|
347
391
|
}
|
|
348
392
|
const targetPath = parsedTarget.targetPath;
|
|
349
393
|
const commandStartedAtMs = Date.now();
|
|
394
|
+
// UPD-04: emit the update banner to stderr BEFORE primary work so the
|
|
395
|
+
// stderr prelude precedes any stdout JSON payload. D-13 scopes the banner
|
|
396
|
+
// to `gdh status` and `gdh verify` only. Silent when cache is null /
|
|
397
|
+
// current / offline / dev-mode (D-06).
|
|
398
|
+
await emitUpdateBannerIfStale(targetPath, io);
|
|
350
399
|
try {
|
|
351
400
|
const status = await buildGdhStatusResult(targetPath);
|
|
352
401
|
await recordSessionEvent(targetPath, {
|
|
@@ -1209,6 +1258,8 @@ async function runVerifyCommand(args, io) {
|
|
|
1209
1258
|
" Summarize runtime verification readiness, stale evidence, and recurring runtime feedback for one target.",
|
|
1210
1259
|
" corpus <status|record>",
|
|
1211
1260
|
" Inspect or record runtime corpus release-readiness evidence as JSON.",
|
|
1261
|
+
" drift [target]",
|
|
1262
|
+
" Check whether baked `@skillcap/gdh@<version>` literals (launcher + skills + commands) match .gdh/project.yaml gdh_version.",
|
|
1212
1263
|
" recommend [target] --files <path> [--files <path>...]",
|
|
1213
1264
|
" Recommend authoring-first validation requirements for the supplied change set.",
|
|
1214
1265
|
" done [target] --files <path> [--files <path>...] [--performed <kind>]",
|
|
@@ -1216,6 +1267,16 @@ async function runVerifyCommand(args, io) {
|
|
|
1216
1267
|
].join("\n") + "\n");
|
|
1217
1268
|
return 0;
|
|
1218
1269
|
}
|
|
1270
|
+
// UPD-04: emit the update banner to stderr BEFORE dispatching to verify
|
|
1271
|
+
// subcommands so the stderr prelude precedes any stdout JSON payload. D-13
|
|
1272
|
+
// scopes the banner to `gdh status` and `gdh verify` only. Target for the
|
|
1273
|
+
// banner's pre-onboard re-check is the first non-flag positional arg
|
|
1274
|
+
// after the subcommand (falling back to "."). The cache itself is per-user,
|
|
1275
|
+
// not per-target, so any resolvable path suffices for the dev-mode check.
|
|
1276
|
+
{
|
|
1277
|
+
const verifyTargetPath = rest.find((arg) => !arg.startsWith("--")) ?? ".";
|
|
1278
|
+
await emitUpdateBannerIfStale(verifyTargetPath, io);
|
|
1279
|
+
}
|
|
1219
1280
|
if (subcommand === "recommend") {
|
|
1220
1281
|
return runVerifyRecommendCommand(rest, io);
|
|
1221
1282
|
}
|
|
@@ -1234,6 +1295,9 @@ async function runVerifyCommand(args, io) {
|
|
|
1234
1295
|
if (subcommand === "corpus") {
|
|
1235
1296
|
return runVerifyCorpusCommand(rest, io);
|
|
1236
1297
|
}
|
|
1298
|
+
if (subcommand === "drift") {
|
|
1299
|
+
return runVerifyDriftCommand(rest, io);
|
|
1300
|
+
}
|
|
1237
1301
|
io.stderr.write([
|
|
1238
1302
|
`Unknown verify command: ${subcommand}`,
|
|
1239
1303
|
"",
|
|
@@ -1244,6 +1308,7 @@ async function runVerifyCommand(args, io) {
|
|
|
1244
1308
|
"Usage: gdh verify corpus materialize --entry <id> [--manifest <path>] [--workspace-root <path>]",
|
|
1245
1309
|
"Usage: gdh verify corpus exercise --entry <id> [--manifest <path>] [--workspace-root <path>] [--no-prepare]",
|
|
1246
1310
|
"Usage: gdh verify corpus record --entry <id> --target <path> [--manifest <path>] [--workspace-root <path>]",
|
|
1311
|
+
"Usage: gdh verify drift [target]",
|
|
1247
1312
|
"Usage: gdh verify recommend [target] --files <path> [--files <path>...]",
|
|
1248
1313
|
"Usage: gdh verify done [target] --files <path> [--files <path>...] [--performed <kind>]",
|
|
1249
1314
|
].join("\n") + "\n");
|
|
@@ -1583,14 +1648,15 @@ async function runRecipeListCommand(args, io) {
|
|
|
1583
1648
|
return 1;
|
|
1584
1649
|
}
|
|
1585
1650
|
const targetPath = positionalArgs[0] ?? ".";
|
|
1651
|
+
const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
|
|
1586
1652
|
const commandStartedAtMs = Date.now();
|
|
1587
1653
|
try {
|
|
1588
|
-
const projectConfig = await readProjectConfig(
|
|
1654
|
+
const projectConfig = await readProjectConfig(effectiveTargetPath);
|
|
1589
1655
|
const result = await listRuntimeRecipes({
|
|
1590
|
-
targetPath,
|
|
1656
|
+
targetPath: effectiveTargetPath,
|
|
1591
1657
|
projectConfig,
|
|
1592
1658
|
});
|
|
1593
|
-
await recordSessionEvent(
|
|
1659
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1594
1660
|
commandStartedAtMs,
|
|
1595
1661
|
kind: "recipe_list",
|
|
1596
1662
|
command: "gdh run-config list",
|
|
@@ -1601,7 +1667,7 @@ async function runRecipeListCommand(args, io) {
|
|
|
1601
1667
|
return 0;
|
|
1602
1668
|
}
|
|
1603
1669
|
catch (error) {
|
|
1604
|
-
await recordSessionEvent(
|
|
1670
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1605
1671
|
commandStartedAtMs,
|
|
1606
1672
|
kind: "recipe_list",
|
|
1607
1673
|
command: "gdh run-config list",
|
|
@@ -1636,15 +1702,16 @@ async function runRecipeShowCommand(args, io) {
|
|
|
1636
1702
|
}
|
|
1637
1703
|
const targetPath = positionalArgs.length === 1 ? "." : positionalArgs[0];
|
|
1638
1704
|
const recipeId = positionalArgs.length === 1 ? positionalArgs[0] : positionalArgs[1];
|
|
1705
|
+
const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
|
|
1639
1706
|
const commandStartedAtMs = Date.now();
|
|
1640
1707
|
try {
|
|
1641
|
-
const projectConfig = await readProjectConfig(
|
|
1708
|
+
const projectConfig = await readProjectConfig(effectiveTargetPath);
|
|
1642
1709
|
const result = await showRuntimeRecipe({
|
|
1643
|
-
targetPath,
|
|
1710
|
+
targetPath: effectiveTargetPath,
|
|
1644
1711
|
projectConfig,
|
|
1645
1712
|
recipeId,
|
|
1646
1713
|
});
|
|
1647
|
-
await recordSessionEvent(
|
|
1714
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1648
1715
|
commandStartedAtMs,
|
|
1649
1716
|
kind: "recipe_show",
|
|
1650
1717
|
command: "gdh run-config show",
|
|
@@ -1655,7 +1722,7 @@ async function runRecipeShowCommand(args, io) {
|
|
|
1655
1722
|
return result.state === "ready" ? 0 : 1;
|
|
1656
1723
|
}
|
|
1657
1724
|
catch (error) {
|
|
1658
|
-
await recordSessionEvent(
|
|
1725
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1659
1726
|
commandStartedAtMs,
|
|
1660
1727
|
kind: "recipe_show",
|
|
1661
1728
|
command: "gdh run-config show",
|
|
@@ -1691,11 +1758,12 @@ async function runRecipeCheckCommand(args, io) {
|
|
|
1691
1758
|
}
|
|
1692
1759
|
const targetPath = positionalArgs.length === 1 ? "." : positionalArgs[0];
|
|
1693
1760
|
const recipeId = positionalArgs.length === 1 ? positionalArgs[0] : positionalArgs[1];
|
|
1761
|
+
const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
|
|
1694
1762
|
const commandStartedAtMs = Date.now();
|
|
1695
1763
|
try {
|
|
1696
|
-
const projectConfig = await readProjectConfig(
|
|
1764
|
+
const projectConfig = await readProjectConfig(effectiveTargetPath);
|
|
1697
1765
|
const result = await checkRuntimeRecipe({
|
|
1698
|
-
targetPath,
|
|
1766
|
+
targetPath: effectiveTargetPath,
|
|
1699
1767
|
projectConfig,
|
|
1700
1768
|
recipeId,
|
|
1701
1769
|
provider: readSingleOptionValue(args, "--provider"),
|
|
@@ -1705,7 +1773,7 @@ async function runRecipeCheckCommand(args, io) {
|
|
|
1705
1773
|
environment: collectAssignmentOptionValues(args, "--env"),
|
|
1706
1774
|
workspaceMode: "isolated_copy",
|
|
1707
1775
|
});
|
|
1708
|
-
await recordSessionEvent(
|
|
1776
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1709
1777
|
commandStartedAtMs,
|
|
1710
1778
|
kind: "recipe_check",
|
|
1711
1779
|
command: "gdh run-config check",
|
|
@@ -1716,7 +1784,7 @@ async function runRecipeCheckCommand(args, io) {
|
|
|
1716
1784
|
return result.state === "runnable" ? 0 : 1;
|
|
1717
1785
|
}
|
|
1718
1786
|
catch (error) {
|
|
1719
|
-
await recordSessionEvent(
|
|
1787
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1720
1788
|
commandStartedAtMs,
|
|
1721
1789
|
kind: "recipe_check",
|
|
1722
1790
|
command: "gdh run-config check",
|
|
@@ -1753,11 +1821,12 @@ async function runRecipeRunCommand(args, io) {
|
|
|
1753
1821
|
}
|
|
1754
1822
|
const targetPath = positionalArgs.length === 1 ? "." : positionalArgs[0];
|
|
1755
1823
|
const recipeId = positionalArgs.length === 1 ? positionalArgs[0] : positionalArgs[1];
|
|
1824
|
+
const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
|
|
1756
1825
|
const commandStartedAtMs = Date.now();
|
|
1757
1826
|
try {
|
|
1758
|
-
const projectConfig = await readProjectConfig(
|
|
1827
|
+
const projectConfig = await readProjectConfig(effectiveTargetPath);
|
|
1759
1828
|
const result = await runRuntimeRecipe({
|
|
1760
|
-
targetPath,
|
|
1829
|
+
targetPath: effectiveTargetPath,
|
|
1761
1830
|
projectConfig,
|
|
1762
1831
|
recipeId,
|
|
1763
1832
|
provider: readSingleOptionValue(args, "--provider"),
|
|
@@ -1767,7 +1836,7 @@ async function runRecipeRunCommand(args, io) {
|
|
|
1767
1836
|
environment: collectAssignmentOptionValues(args, "--env"),
|
|
1768
1837
|
workspaceMode: args.includes("--live-workspace") ? "live_workspace" : "isolated_copy",
|
|
1769
1838
|
});
|
|
1770
|
-
await recordSessionEvent(
|
|
1839
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1771
1840
|
commandStartedAtMs,
|
|
1772
1841
|
kind: "recipe_run",
|
|
1773
1842
|
command: "gdh run-config run",
|
|
@@ -1779,7 +1848,7 @@ async function runRecipeRunCommand(args, io) {
|
|
|
1779
1848
|
return result.state === "passed" ? 0 : 1;
|
|
1780
1849
|
}
|
|
1781
1850
|
catch (error) {
|
|
1782
|
-
await recordSessionEvent(
|
|
1851
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1783
1852
|
commandStartedAtMs,
|
|
1784
1853
|
kind: "recipe_run",
|
|
1785
1854
|
command: "gdh run-config run",
|
|
@@ -1813,10 +1882,11 @@ async function runScenarioListCommand(args, io) {
|
|
|
1813
1882
|
return 1;
|
|
1814
1883
|
}
|
|
1815
1884
|
const targetPath = positionalArgs[0] ?? ".";
|
|
1885
|
+
const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
|
|
1816
1886
|
const commandStartedAtMs = Date.now();
|
|
1817
1887
|
try {
|
|
1818
|
-
const result = await listRuntimeScenarios({ targetPath });
|
|
1819
|
-
await recordSessionEvent(
|
|
1888
|
+
const result = await listRuntimeScenarios({ targetPath: effectiveTargetPath });
|
|
1889
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1820
1890
|
commandStartedAtMs,
|
|
1821
1891
|
kind: "scenario_list",
|
|
1822
1892
|
command: "gdh verification-scenario list",
|
|
@@ -1827,7 +1897,7 @@ async function runScenarioListCommand(args, io) {
|
|
|
1827
1897
|
return result.state === "ready" ? 0 : 1;
|
|
1828
1898
|
}
|
|
1829
1899
|
catch (error) {
|
|
1830
|
-
await recordSessionEvent(
|
|
1900
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1831
1901
|
commandStartedAtMs,
|
|
1832
1902
|
kind: "scenario_list",
|
|
1833
1903
|
command: "gdh verification-scenario list",
|
|
@@ -1862,13 +1932,14 @@ async function runScenarioShowCommand(args, io) {
|
|
|
1862
1932
|
}
|
|
1863
1933
|
const targetPath = positionalArgs.length === 1 ? "." : positionalArgs[0];
|
|
1864
1934
|
const scenarioId = positionalArgs.length === 1 ? positionalArgs[0] : positionalArgs[1];
|
|
1935
|
+
const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
|
|
1865
1936
|
const commandStartedAtMs = Date.now();
|
|
1866
1937
|
try {
|
|
1867
1938
|
const result = await showRuntimeScenario({
|
|
1868
|
-
targetPath,
|
|
1939
|
+
targetPath: effectiveTargetPath,
|
|
1869
1940
|
scenarioId,
|
|
1870
1941
|
});
|
|
1871
|
-
await recordSessionEvent(
|
|
1942
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1872
1943
|
commandStartedAtMs,
|
|
1873
1944
|
kind: "scenario_show",
|
|
1874
1945
|
command: "gdh verification-scenario show",
|
|
@@ -1879,7 +1950,7 @@ async function runScenarioShowCommand(args, io) {
|
|
|
1879
1950
|
return result.state === "ready" ? 0 : 1;
|
|
1880
1951
|
}
|
|
1881
1952
|
catch (error) {
|
|
1882
|
-
await recordSessionEvent(
|
|
1953
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
1883
1954
|
commandStartedAtMs,
|
|
1884
1955
|
kind: "scenario_show",
|
|
1885
1956
|
command: "gdh verification-scenario show",
|
|
@@ -2106,10 +2177,17 @@ async function runMcpInvokeCommand(args, io) {
|
|
|
2106
2177
|
return 1;
|
|
2107
2178
|
}
|
|
2108
2179
|
}
|
|
2109
|
-
async function
|
|
2180
|
+
async function resolveEffectiveTargetPath(targetPath) {
|
|
2110
2181
|
const resolvedRoot = await resolveProjectRoot(targetPath);
|
|
2111
|
-
|
|
2112
|
-
|
|
2182
|
+
return resolvedRoot ?? targetPath;
|
|
2183
|
+
}
|
|
2184
|
+
async function buildAuthoringContext(targetPath, io) {
|
|
2185
|
+
const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
|
|
2186
|
+
const inventoryResult = await readInventoryCacheOrScan(effectiveTargetPath);
|
|
2187
|
+
if (inventoryResult.degraded && io !== undefined) {
|
|
2188
|
+
io.stderr.write(`Warning: .gdh-state/inventory.json is corrupt or unreadable; fell back to live scan.\n`);
|
|
2189
|
+
}
|
|
2190
|
+
const inventory = inventoryResult.inventory;
|
|
2113
2191
|
const projectConfig = await readProjectConfig(effectiveTargetPath);
|
|
2114
2192
|
const worktreeState = await readWorktreeState(effectiveTargetPath);
|
|
2115
2193
|
const status = resolveAuthoringStatus({
|
|
@@ -2311,11 +2389,12 @@ async function runVerifyRunCommand(args, io) {
|
|
|
2311
2389
|
}
|
|
2312
2390
|
const targetPath = positionalArgs.length === 1 ? "." : positionalArgs[0];
|
|
2313
2391
|
const recipeId = positionalArgs.length === 1 ? positionalArgs[0] : positionalArgs[1];
|
|
2392
|
+
const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
|
|
2314
2393
|
const commandStartedAtMs = Date.now();
|
|
2315
2394
|
try {
|
|
2316
|
-
const projectConfig = await readProjectConfig(
|
|
2395
|
+
const projectConfig = await readProjectConfig(effectiveTargetPath);
|
|
2317
2396
|
const result = await runRuntimeVerificationScenario({
|
|
2318
|
-
targetPath,
|
|
2397
|
+
targetPath: effectiveTargetPath,
|
|
2319
2398
|
projectConfig,
|
|
2320
2399
|
recipeId,
|
|
2321
2400
|
scenarioId,
|
|
@@ -2326,7 +2405,7 @@ async function runVerifyRunCommand(args, io) {
|
|
|
2326
2405
|
environment: collectAssignmentOptionValues(args, "--env"),
|
|
2327
2406
|
workspaceMode: args.includes("--live-workspace") ? "live_workspace" : "isolated_copy",
|
|
2328
2407
|
});
|
|
2329
|
-
await recordSessionEvent(
|
|
2408
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
2330
2409
|
commandStartedAtMs,
|
|
2331
2410
|
kind: "verify_run",
|
|
2332
2411
|
command: "gdh verify run",
|
|
@@ -2342,7 +2421,7 @@ async function runVerifyRunCommand(args, io) {
|
|
|
2342
2421
|
return result.outcome === "passed" || result.outcome === "flaky" ? 0 : 1;
|
|
2343
2422
|
}
|
|
2344
2423
|
catch (error) {
|
|
2345
|
-
await recordSessionEvent(
|
|
2424
|
+
await recordSessionEvent(effectiveTargetPath, {
|
|
2346
2425
|
commandStartedAtMs,
|
|
2347
2426
|
kind: "verify_run",
|
|
2348
2427
|
command: "gdh verify run",
|
|
@@ -2462,6 +2541,179 @@ async function runVerifyReadinessCommand(args, io) {
|
|
|
2462
2541
|
return 1;
|
|
2463
2542
|
}
|
|
2464
2543
|
}
|
|
2544
|
+
const VERIFY_DRIFT_SCANNED_FILES = [
|
|
2545
|
+
{ relativePath: MCP_LAUNCHER_RELATIVE_PATH, description: "managed MCP launcher" },
|
|
2546
|
+
{ relativePath: CODEX_ONBOARD_SKILL_RELATIVE_PATH, description: "Codex gdh-onboard skill" },
|
|
2547
|
+
{ relativePath: CODEX_STATUS_SKILL_RELATIVE_PATH, description: "Codex gdh-status skill" },
|
|
2548
|
+
{ relativePath: CODEX_MIGRATE_SKILL_RELATIVE_PATH, description: "Codex gdh-migrate skill" },
|
|
2549
|
+
{ relativePath: CODEX_CHECK_SKILL_RELATIVE_PATH, description: "Codex gdh-check skill" },
|
|
2550
|
+
{ relativePath: CODEX_PREPARE_SKILL_RELATIVE_PATH, description: "Codex gdh-prepare skill" },
|
|
2551
|
+
{ relativePath: CODEX_VERIFY_SKILL_RELATIVE_PATH, description: "Codex gdh-verify skill" },
|
|
2552
|
+
{ relativePath: CURSOR_ONBOARD_SKILL_RELATIVE_PATH, description: "Cursor gdh-onboard skill" },
|
|
2553
|
+
{ relativePath: CURSOR_STATUS_SKILL_RELATIVE_PATH, description: "Cursor gdh-status skill" },
|
|
2554
|
+
{ relativePath: CURSOR_MIGRATE_SKILL_RELATIVE_PATH, description: "Cursor gdh-migrate skill" },
|
|
2555
|
+
{ relativePath: CURSOR_CHECK_SKILL_RELATIVE_PATH, description: "Cursor gdh-check skill" },
|
|
2556
|
+
{ relativePath: CURSOR_PREPARE_SKILL_RELATIVE_PATH, description: "Cursor gdh-prepare skill" },
|
|
2557
|
+
{ relativePath: CURSOR_VERIFY_SKILL_RELATIVE_PATH, description: "Cursor gdh-verify skill" },
|
|
2558
|
+
{ relativePath: CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH, description: "Claude gdh/onboard command" },
|
|
2559
|
+
{ relativePath: CLAUDE_STATUS_COMMAND_RELATIVE_PATH, description: "Claude gdh/status command" },
|
|
2560
|
+
{ relativePath: CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, description: "Claude gdh/migrate command" },
|
|
2561
|
+
{ relativePath: CLAUDE_CHECK_COMMAND_RELATIVE_PATH, description: "Claude gdh/check command" },
|
|
2562
|
+
{ relativePath: CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, description: "Claude gdh/prepare command" },
|
|
2563
|
+
{ relativePath: CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, description: "Claude gdh/verify command" },
|
|
2564
|
+
// Phase 12 extension — close the Phase 11 coverage gap (research §Baked Version Surface Inventory rows 26-28).
|
|
2565
|
+
// These files are re-baked at pin time and must be audited by MIG-02's post-condition invariant.
|
|
2566
|
+
//
|
|
2567
|
+
// SessionStart hook: bakes `GDH_PINNED_VERSION: "<semver>"` (not an npx literal). Uses a
|
|
2568
|
+
// dedicated versionRegex to extract the pin from that env-injection pattern.
|
|
2569
|
+
{
|
|
2570
|
+
relativePath: CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH,
|
|
2571
|
+
description: "Claude SessionStart update hook",
|
|
2572
|
+
// The hook body sets GDH_PINNED_VERSION: "<semver>" in the spawned worker's env.
|
|
2573
|
+
// VERIFY_DRIFT_BAKED_VERSION_REGEX matches `@skillcap/gdh@<v>` npx literals; this
|
|
2574
|
+
// supplementary regex extracts the pin from the env-injection literal instead.
|
|
2575
|
+
versionRegex: /GDH_PINNED_VERSION:\s*"(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)"/,
|
|
2576
|
+
},
|
|
2577
|
+
// Intentionally excluded: the Claude background worker (gdh-check-update-worker.js) — the
|
|
2578
|
+
// worker script body reads GDH_PINNED_VERSION from process.env at runtime and does NOT contain
|
|
2579
|
+
// an `@skillcap/gdh@<v>` literal or any other baked-version pattern. Adding it would cause
|
|
2580
|
+
// VERIFY_DRIFT_BAKED_VERSION_REGEX to report `no_baked_version` → false positive. Worker drift
|
|
2581
|
+
// is audited via the `// gdh-hook-version: N` marker through the renderer-layer contract, not
|
|
2582
|
+
// via this version-literal scanner. Covered in integration tests at
|
|
2583
|
+
// packages/adapters/src/claude-update-install-integration.test.ts.
|
|
2584
|
+
//
|
|
2585
|
+
// Intentionally excluded: the Claude statusline (gdh-statusline.js) — the statusline renderer
|
|
2586
|
+
// produces a version-agnostic script (reads the update cache at runtime; no semver literal baked
|
|
2587
|
+
// in the output). Adding it would permanently report `no_baked_version` → false positive. The
|
|
2588
|
+
// statusline IS re-baked on every `gdh adapters install` via the renderer pipeline, which ensures
|
|
2589
|
+
// it stays current with the hook version; pin-literal drift detection is not applicable here.
|
|
2590
|
+
//
|
|
2591
|
+
// Intentionally excluded: the Claude /gdh-update command (.claude/commands/gdh/update.md) —
|
|
2592
|
+
// the rendered command shells out to `npx -y @skillcap/gdh@latest self-update` (LITERAL @latest,
|
|
2593
|
+
// not @<pinnedVersion>). The shellout MUST bake @latest so the NEW CLI performs the update,
|
|
2594
|
+
// not the OLD (potentially buggy) pinned one. Because @latest is not a semver literal, the
|
|
2595
|
+
// VERIFY_DRIFT_BAKED_VERSION_REGEX finds no pinned-version match and would permanently report
|
|
2596
|
+
// `no_baked_version` → false positive. Phase 13 D-13 locks this exclusion; Check 44 in
|
|
2597
|
+
// scripts/validate-docs.mjs enforces both the @latest invariant and the exclusion.
|
|
2598
|
+
//
|
|
2599
|
+
// Intentionally excluded: the Codex gdh-update skill (.codex/skills/gdh-update/SKILL.md) —
|
|
2600
|
+
// same rationale: renders @skillcap/gdh@latest, not @<pinnedVersion>. Phase 13 D-13.
|
|
2601
|
+
//
|
|
2602
|
+
// Intentionally excluded: the Cursor gdh-update skill (.cursor/skills/gdh-update/SKILL.md) —
|
|
2603
|
+
// same rationale: renders @skillcap/gdh@latest, not @<pinnedVersion>. Phase 13 D-13.
|
|
2604
|
+
];
|
|
2605
|
+
// Mirrors EXACT_SEMVER_PATTERN in @gdh/authoring so only well-formed SemVer
|
|
2606
|
+
// values match — drift check does not pick up unrelated doc prose.
|
|
2607
|
+
const VERIFY_DRIFT_BAKED_VERSION_REGEX = /@skillcap\/gdh@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)/;
|
|
2608
|
+
async function runVerifyDriftCommand(args, io) {
|
|
2609
|
+
if (args.includes("--help") || args.includes("help")) {
|
|
2610
|
+
io.stdout.write([
|
|
2611
|
+
"Usage: gdh verify drift [target]",
|
|
2612
|
+
"",
|
|
2613
|
+
"Compares every baked `@skillcap/gdh@<version>` literal across the managed",
|
|
2614
|
+
"surfaces (.gdh/bin/gdh-mcp.mjs launcher + regenerated Codex/Cursor/Claude",
|
|
2615
|
+
"skill + command files) against .gdh/project.yaml gdh_version. Exits 1 and",
|
|
2616
|
+
"emits a structured JSON result when ANY file's baked version diverges from",
|
|
2617
|
+
"the pinned value. Covers ROADMAP Success Criterion #3 (one source of truth).",
|
|
2618
|
+
"",
|
|
2619
|
+
].join("\n"));
|
|
2620
|
+
return 0;
|
|
2621
|
+
}
|
|
2622
|
+
const unsupportedOptionsError = findUnsupportedOptionsError(args, {
|
|
2623
|
+
usage: "Usage: gdh verify drift [target]\n",
|
|
2624
|
+
});
|
|
2625
|
+
if (unsupportedOptionsError !== null) {
|
|
2626
|
+
io.stderr.write(unsupportedOptionsError);
|
|
2627
|
+
return 1;
|
|
2628
|
+
}
|
|
2629
|
+
const positionalArgs = extractPositionalArgs(args, new Set());
|
|
2630
|
+
if (positionalArgs.length > 1) {
|
|
2631
|
+
io.stderr.write("Usage error: gdh verify drift accepts at most one positional target path.\n");
|
|
2632
|
+
return 1;
|
|
2633
|
+
}
|
|
2634
|
+
const targetPath = positionalArgs[0] ?? ".";
|
|
2635
|
+
const commandStartedAtMs = Date.now();
|
|
2636
|
+
let pinnedVersion;
|
|
2637
|
+
try {
|
|
2638
|
+
pinnedVersion = await resolvePinnedVersion(targetPath);
|
|
2639
|
+
}
|
|
2640
|
+
catch (error) {
|
|
2641
|
+
await recordSessionEvent(targetPath, {
|
|
2642
|
+
commandStartedAtMs,
|
|
2643
|
+
kind: "verify_drift",
|
|
2644
|
+
command: "gdh verify drift",
|
|
2645
|
+
state: "failed",
|
|
2646
|
+
summary: "Drift check could not resolve the pinned GDH version for this target.",
|
|
2647
|
+
errorMessage: formatCliError(error),
|
|
2648
|
+
});
|
|
2649
|
+
// D-07: surface the concrete `gdh migrate --apply` guidance that the
|
|
2650
|
+
// resolvePinnedVersion throw already names — write the full error
|
|
2651
|
+
// message to stdout so agents parsing JSON get the fix command and
|
|
2652
|
+
// stderr so operators get an unmistakable failure line.
|
|
2653
|
+
const message = formatCliError(error);
|
|
2654
|
+
io.stdout.write(`${message}\n`);
|
|
2655
|
+
io.stderr.write(`Failed to check managed-surface drift for "${targetPath}": ${message}\n`);
|
|
2656
|
+
return 1;
|
|
2657
|
+
}
|
|
2658
|
+
const fileResults = await Promise.all(VERIFY_DRIFT_SCANNED_FILES.map(async (file) => {
|
|
2659
|
+
const absolutePath = path.join(targetPath, file.relativePath);
|
|
2660
|
+
const content = await fs.readFile(absolutePath, "utf8").catch(() => null);
|
|
2661
|
+
if (content === null) {
|
|
2662
|
+
return {
|
|
2663
|
+
relativePath: file.relativePath,
|
|
2664
|
+
description: file.description,
|
|
2665
|
+
present: false,
|
|
2666
|
+
baked: null,
|
|
2667
|
+
drift: true,
|
|
2668
|
+
reason: "file_missing",
|
|
2669
|
+
};
|
|
2670
|
+
}
|
|
2671
|
+
const match = content.match(file.versionRegex ?? VERIFY_DRIFT_BAKED_VERSION_REGEX);
|
|
2672
|
+
const baked = match?.[1] ?? null;
|
|
2673
|
+
const reason = baked === null
|
|
2674
|
+
? "no_baked_version"
|
|
2675
|
+
: baked !== pinnedVersion
|
|
2676
|
+
? "version_mismatch"
|
|
2677
|
+
: "ok";
|
|
2678
|
+
return {
|
|
2679
|
+
relativePath: file.relativePath,
|
|
2680
|
+
description: file.description,
|
|
2681
|
+
present: true,
|
|
2682
|
+
baked,
|
|
2683
|
+
drift: reason !== "ok",
|
|
2684
|
+
reason,
|
|
2685
|
+
};
|
|
2686
|
+
}));
|
|
2687
|
+
const driftFiles = fileResults.filter((file) => file.drift);
|
|
2688
|
+
const overallDrift = driftFiles.length > 0;
|
|
2689
|
+
const result = {
|
|
2690
|
+
ok: !overallDrift,
|
|
2691
|
+
drift: overallDrift,
|
|
2692
|
+
pinned: pinnedVersion,
|
|
2693
|
+
scannedFileCount: fileResults.length,
|
|
2694
|
+
driftFileCount: driftFiles.length,
|
|
2695
|
+
files: fileResults,
|
|
2696
|
+
action: overallDrift
|
|
2697
|
+
? {
|
|
2698
|
+
kind: "run_repair",
|
|
2699
|
+
summary: `${driftFiles.length} managed-surface file(s) have a baked version that does not match .gdh/project.yaml gdh_version. Run \`gdh adapters install\` to regenerate them.`,
|
|
2700
|
+
command: ["gdh", "adapters", "install", targetPath],
|
|
2701
|
+
autoApplicable: true,
|
|
2702
|
+
}
|
|
2703
|
+
: null,
|
|
2704
|
+
};
|
|
2705
|
+
await recordSessionEvent(targetPath, {
|
|
2706
|
+
commandStartedAtMs,
|
|
2707
|
+
kind: "verify_drift",
|
|
2708
|
+
command: "gdh verify drift",
|
|
2709
|
+
state: overallDrift ? "failed" : "succeeded",
|
|
2710
|
+
summary: overallDrift
|
|
2711
|
+
? `Detected ${driftFiles.length} managed-surface file(s) with a baked version drifted from gdh_version.`
|
|
2712
|
+
: `All ${fileResults.length} managed-surface file(s) match .gdh/project.yaml gdh_version.`,
|
|
2713
|
+
});
|
|
2714
|
+
writeJsonResult(io, result);
|
|
2715
|
+
return overallDrift ? 1 : 0;
|
|
2716
|
+
}
|
|
2465
2717
|
async function runVerifyCorpusStatusCommand(args, io) {
|
|
2466
2718
|
if (args.includes("--help")) {
|
|
2467
2719
|
io.stdout.write([
|
|
@@ -3541,7 +3793,7 @@ function renderHelp() {
|
|
|
3541
3793
|
"Run `gdh <command> --help` for command-specific usage.",
|
|
3542
3794
|
].join("\n") + "\n");
|
|
3543
3795
|
}
|
|
3544
|
-
function formatCliError(error) {
|
|
3796
|
+
export function formatCliError(error) {
|
|
3545
3797
|
return error instanceof Error ? error.message : String(error);
|
|
3546
3798
|
}
|
|
3547
3799
|
function resolveCachePruneScope(args) {
|
|
@@ -3599,7 +3851,7 @@ function collectAssignmentOptionValues(args, optionName) {
|
|
|
3599
3851
|
}
|
|
3600
3852
|
return assignments;
|
|
3601
3853
|
}
|
|
3602
|
-
function readSingleOptionValue(args, optionName) {
|
|
3854
|
+
export function readSingleOptionValue(args, optionName) {
|
|
3603
3855
|
const index = args.indexOf(optionName);
|
|
3604
3856
|
if (index === -1) {
|
|
3605
3857
|
return null;
|
|
@@ -3610,7 +3862,7 @@ function readSingleOptionValue(args, optionName) {
|
|
|
3610
3862
|
}
|
|
3611
3863
|
return value;
|
|
3612
3864
|
}
|
|
3613
|
-
function findUnsupportedOptionsError(args, options) {
|
|
3865
|
+
export function findUnsupportedOptionsError(args, options) {
|
|
3614
3866
|
const supportedOptions = new Set([
|
|
3615
3867
|
...(options.optionsWithValues ?? []),
|
|
3616
3868
|
...(options.booleanOptions ?? []),
|
|
@@ -3672,7 +3924,7 @@ function buildGuidanceSnapshot(result) {
|
|
|
3672
3924
|
recommendedUnitIds: result.recommendedUnits.map((unit) => unit.id),
|
|
3673
3925
|
};
|
|
3674
3926
|
}
|
|
3675
|
-
async function recordSessionEvent(targetPath, input) {
|
|
3927
|
+
export async function recordSessionEvent(targetPath, input) {
|
|
3676
3928
|
try {
|
|
3677
3929
|
const { commandStartedAtMs, ...eventInput } = input;
|
|
3678
3930
|
const startedAt = typeof commandStartedAtMs === "number" && Number.isFinite(commandStartedAtMs)
|
|
@@ -3747,7 +3999,7 @@ function extractGuidanceResolvePositionalArgs(args) {
|
|
|
3747
3999
|
function extractRecipePositionalArgs(args) {
|
|
3748
4000
|
return extractPositionalArgs(args, new Set(["--provider", "--param", "--feature", "--no-feature", "--env"]));
|
|
3749
4001
|
}
|
|
3750
|
-
function extractPositionalArgs(args, optionsWithValues) {
|
|
4002
|
+
export function extractPositionalArgs(args, optionsWithValues) {
|
|
3751
4003
|
const positionalArgs = [];
|
|
3752
4004
|
for (let index = 0; index < args.length; index += 1) {
|
|
3753
4005
|
const arg = args[index];
|