@skillcap/gdh 0.7.0 → 0.8.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 (69) hide show
  1. package/INSTALL-BUNDLE.json +1 -1
  2. package/README.md +4 -4
  3. package/node_modules/@gdh/adapters/dist/index.d.ts +3 -1
  4. package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
  5. package/node_modules/@gdh/adapters/dist/index.js +65 -17
  6. package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
  7. package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts.map +1 -1
  8. package/node_modules/@gdh/adapters/dist/self-update-mechanics.js +9 -1
  9. package/node_modules/@gdh/adapters/dist/self-update-mechanics.js.map +1 -1
  10. package/node_modules/@gdh/adapters/package.json +8 -8
  11. package/node_modules/@gdh/authoring/dist/prepare.d.ts.map +1 -1
  12. package/node_modules/@gdh/authoring/dist/prepare.js +5 -1
  13. package/node_modules/@gdh/authoring/dist/prepare.js.map +1 -1
  14. package/node_modules/@gdh/authoring/package.json +2 -2
  15. package/node_modules/@gdh/cli/dist/index.d.ts +35 -0
  16. package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
  17. package/node_modules/@gdh/cli/dist/index.js +227 -137
  18. package/node_modules/@gdh/cli/dist/index.js.map +1 -1
  19. package/node_modules/@gdh/cli/dist/migrate.d.ts +4 -0
  20. package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
  21. package/node_modules/@gdh/cli/dist/migrate.js +13 -0
  22. package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
  23. package/node_modules/@gdh/cli/dist/self-update.d.ts.map +1 -1
  24. package/node_modules/@gdh/cli/dist/self-update.js +52 -3
  25. package/node_modules/@gdh/cli/dist/self-update.js.map +1 -1
  26. package/node_modules/@gdh/cli/dist/setup.d.ts +41 -0
  27. package/node_modules/@gdh/cli/dist/setup.d.ts.map +1 -1
  28. package/node_modules/@gdh/cli/dist/setup.js +181 -26
  29. package/node_modules/@gdh/cli/dist/setup.js.map +1 -1
  30. package/node_modules/@gdh/cli/package.json +10 -10
  31. package/node_modules/@gdh/core/dist/index.d.ts +75 -1
  32. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  33. package/node_modules/@gdh/core/dist/index.js +45 -1
  34. package/node_modules/@gdh/core/dist/index.js.map +1 -1
  35. package/node_modules/@gdh/core/dist/update-cache.d.ts +12 -0
  36. package/node_modules/@gdh/core/dist/update-cache.d.ts.map +1 -1
  37. package/node_modules/@gdh/core/dist/update-cache.js +20 -0
  38. package/node_modules/@gdh/core/dist/update-cache.js.map +1 -1
  39. package/node_modules/@gdh/core/dist/update-probe.d.ts.map +1 -1
  40. package/node_modules/@gdh/core/dist/update-probe.js +7 -1
  41. package/node_modules/@gdh/core/dist/update-probe.js.map +1 -1
  42. package/node_modules/@gdh/core/package.json +1 -1
  43. package/node_modules/@gdh/docs/dist/index.d.ts +2 -0
  44. package/node_modules/@gdh/docs/dist/index.d.ts.map +1 -1
  45. package/node_modules/@gdh/docs/dist/index.js +1 -0
  46. package/node_modules/@gdh/docs/dist/index.js.map +1 -1
  47. package/node_modules/@gdh/docs/dist/recovery-hints.d.ts +43 -0
  48. package/node_modules/@gdh/docs/dist/recovery-hints.d.ts.map +1 -0
  49. package/node_modules/@gdh/docs/dist/recovery-hints.js +203 -0
  50. package/node_modules/@gdh/docs/dist/recovery-hints.js.map +1 -0
  51. package/node_modules/@gdh/docs/package.json +2 -2
  52. package/node_modules/@gdh/mcp/package.json +8 -8
  53. package/node_modules/@gdh/observability/package.json +2 -2
  54. package/node_modules/@gdh/runtime/package.json +2 -2
  55. package/node_modules/@gdh/scan/dist/detect-existing-onboarding.d.ts +19 -0
  56. package/node_modules/@gdh/scan/dist/detect-existing-onboarding.d.ts.map +1 -0
  57. package/node_modules/@gdh/scan/dist/detect-existing-onboarding.js +58 -0
  58. package/node_modules/@gdh/scan/dist/detect-existing-onboarding.js.map +1 -0
  59. package/node_modules/@gdh/scan/dist/index.d.ts +1 -0
  60. package/node_modules/@gdh/scan/dist/index.d.ts.map +1 -1
  61. package/node_modules/@gdh/scan/dist/index.js +1 -0
  62. package/node_modules/@gdh/scan/dist/index.js.map +1 -1
  63. package/node_modules/@gdh/scan/dist/onboard.d.ts +2 -0
  64. package/node_modules/@gdh/scan/dist/onboard.d.ts.map +1 -1
  65. package/node_modules/@gdh/scan/dist/onboard.js +12 -4
  66. package/node_modules/@gdh/scan/dist/onboard.js.map +1 -1
  67. package/node_modules/@gdh/scan/package.json +3 -3
  68. package/node_modules/@gdh/verify/package.json +7 -7
  69. package/package.json +11 -11
@@ -16,7 +16,7 @@ import { evaluateDonePolicy, exerciseRuntimeCorpusEntry, inspectRuntimeCorpusSta
16
16
  import { migrateProjectLifecycleSurface } from "./migrate.js";
17
17
  import { presentPublicRuntimeTerms } from "./public-terms.js";
18
18
  import { runSelfUpdateCommand } from "./self-update.js";
19
- import { executeSetupCommand, isSetupCanceledError, renderSetupIntro, renderSetupOutro, renderSetupSummary, } from "./setup.js";
19
+ import { executeSetupCommand, isSetupCanceledError, renderDurableTruthCollisionHint, renderSetupIntro, renderSetupOutro, renderSetupSummary, } from "./setup.js";
20
20
  import { emitUpdateBannerIfStale } from "./update-banner.js";
21
21
  export const cliPackage = definePackageBoundary({
22
22
  name: "@gdh/cli",
@@ -44,6 +44,18 @@ export async function runCli(args, io = { stdout: process.stdout, stderr: proces
44
44
  io.stdout.write(`${resolveGdhProductMetadata().version}\n`);
45
45
  return 0;
46
46
  }
47
+ // Internal diagnostic for Plan 15-05 symlink-shape test; not a user-facing feature.
48
+ if (command === "--print-target-path") {
49
+ const { targetPath, error } = parseOptionalPositionalTargetPath(rest, {
50
+ usage: "Usage: gdh --print-target-path [<target-path>]\n",
51
+ });
52
+ if (error !== null) {
53
+ io.stderr.write(error);
54
+ return 1;
55
+ }
56
+ io.stdout.write(`${JSON.stringify({ targetPath })}\n`);
57
+ return 0;
58
+ }
47
59
  if (command === "version") {
48
60
  return runVersionCommand(rest, io);
49
61
  }
@@ -216,8 +228,12 @@ async function runSetupCommand(args, io) {
216
228
  const yes = args.includes("--yes");
217
229
  const allAgents = args.includes("--all-agents");
218
230
  const positionalArgs = extractPositionalArgs(args, new Set(["--agent", "--project", "--integration-root"]));
219
- if (positionalArgs.length > 1) {
220
- io.stderr.write("Usage error: gdh setup accepts at most one positional target path.\n");
231
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
232
+ usage: "Usage: gdh setup [<target-path>] [--agent <name>] [--all-agents] [--project <path>] [--integration-root <path>] [--yes]\n",
233
+ additionalOptionsWithValues: new Set(["--agent", "--project", "--integration-root"]),
234
+ });
235
+ if (targetPathError !== null) {
236
+ io.stderr.write(targetPathError);
221
237
  return 1;
222
238
  }
223
239
  const requestedAgents = collectAdapterNames(args);
@@ -229,7 +245,6 @@ async function runSetupCommand(args, io) {
229
245
  io.stderr.write("Usage error: choose either --all-agents or one or more --agent values.\n");
230
246
  return 1;
231
247
  }
232
- const targetPath = positionalArgs[0] ?? ".";
233
248
  const explicitTargetPath = positionalArgs.length === 1;
234
249
  const selectedProjectRelativePath = readSingleOptionValue(args, "--project");
235
250
  const integrationRootPath = readSingleOptionValue(args, "--integration-root");
@@ -291,6 +306,22 @@ async function runSetupCommand(args, io) {
291
306
  });
292
307
  return 1;
293
308
  }
309
+ // Phase 18 plan 03 (UX-02): typed recoveryHint render before the generic
310
+ // failure branch. Detects DurableTruthCollisionError via the dual-package-
311
+ // hazard-safe predicate exported by setup.ts, renders the structured
312
+ // recoveryHint (Suggested fix / Run / Skill) to the CLI stderr sink, and
313
+ // records a blocked session (mirrors the SetupCanceledError arm shape).
314
+ if (renderDurableTruthCollisionHint(error, io.stderr)) {
315
+ await recordSessionEvent(targetPath, {
316
+ commandStartedAtMs,
317
+ kind: "onboard",
318
+ command: "gdh setup",
319
+ state: "blocked",
320
+ summary: "GDH setup blocked on durable-truth collision; see recoveryHint.",
321
+ errorMessage: formatCliError(error),
322
+ });
323
+ return 1;
324
+ }
294
325
  await recordSessionEvent(targetPath, {
295
326
  commandStartedAtMs,
296
327
  kind: "onboard",
@@ -321,12 +352,14 @@ async function runOnboardCommand(args, io) {
321
352
  return 1;
322
353
  }
323
354
  const dryRun = args.includes("--dry-run");
324
- const positionalArgs = extractGuidanceResolvePositionalArgs(args);
325
- if (positionalArgs.length > 1) {
326
- io.stderr.write("Usage error: gdh onboard accepts at most one positional target path.\n");
355
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
356
+ usage: "Usage: gdh onboard [target] [--dry-run]\n",
357
+ additionalOptionsWithValues: new Set(["--task-type", "--capability", "--repo-state", "--keyword"]),
358
+ });
359
+ if (targetPathError !== null) {
360
+ io.stderr.write(targetPathError);
327
361
  return 1;
328
362
  }
329
- const targetPath = positionalArgs[0] ?? ".";
330
363
  const commandStartedAtMs = Date.now();
331
364
  try {
332
365
  const result = await onboardGodotProject(targetPath, { dryRun });
@@ -522,16 +555,18 @@ async function runDocsVersionCommand(args, io) {
522
555
  io.stderr.write(unsupportedOptionsError);
523
556
  return 1;
524
557
  }
525
- const positionalArgs = extractPositionalArgs(args, new Set(["--project"]));
526
- if (positionalArgs.length > 1) {
527
- io.stderr.write("Usage error: gdh docs version accepts at most one positional target path.\n");
558
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
559
+ usage: "Usage: gdh docs version [target] [--project <relative-path>]\n",
560
+ additionalOptionsWithValues: new Set(["--project"]),
561
+ });
562
+ if (targetPathError !== null) {
563
+ io.stderr.write(targetPathError);
528
564
  return 1;
529
565
  }
530
566
  if (args.includes("--project") && readSingleOptionValue(args, "--project") === null) {
531
567
  io.stderr.write("Usage error: --project requires a relative project path.\n");
532
568
  return 1;
533
569
  }
534
- const targetPath = positionalArgs[0] ?? ".";
535
570
  const requestedProjectPath = readSingleOptionValue(args, "--project");
536
571
  try {
537
572
  const context = await buildAuthoringContext(targetPath);
@@ -770,12 +805,14 @@ async function runWarmupCommand(args, io) {
770
805
  return 1;
771
806
  }
772
807
  const dryRun = args.includes("--dry-run");
773
- const positionalArgs = extractGuidanceResolvePositionalArgs(args);
774
- if (positionalArgs.length > 1) {
775
- io.stderr.write("Usage error: gdh warmup accepts at most one positional target path.\n");
808
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
809
+ usage: "Usage: gdh warmup [target] [--dry-run]\n",
810
+ additionalOptionsWithValues: new Set(["--task-type", "--capability", "--repo-state", "--keyword"]),
811
+ });
812
+ if (targetPathError !== null) {
813
+ io.stderr.write(targetPathError);
776
814
  return 1;
777
815
  }
778
- const targetPath = positionalArgs[0] ?? ".";
779
816
  try {
780
817
  const context = await buildAuthoringContext(targetPath);
781
818
  const result = await runWarmup({
@@ -834,12 +871,14 @@ async function runTargetPrepareCommand(args, io) {
834
871
  }
835
872
  const dryRun = args.includes("--dry-run");
836
873
  const refreshImports = !args.includes("--no-refresh-imports");
837
- const positionalArgs = extractPositionalArgs(args, new Set(["--source-target"]));
838
- if (positionalArgs.length > 1) {
839
- io.stderr.write("Usage error: gdh target prepare accepts at most one positional target path.\n");
874
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
875
+ usage: "Usage: gdh target prepare [target] [--source-target <path>] [--dry-run] [--no-refresh-imports]\n",
876
+ additionalOptionsWithValues: new Set(["--source-target"]),
877
+ });
878
+ if (targetPathError !== null) {
879
+ io.stderr.write(targetPathError);
840
880
  return 1;
841
881
  }
842
- const targetPath = positionalArgs[0] ?? ".";
843
882
  const sourceTargetPath = collectOptionValues(args, "--source-target")[0] ?? null;
844
883
  const commandStartedAtMs = Date.now();
845
884
  try {
@@ -921,12 +960,13 @@ async function runImportsRefreshCommand(args, io) {
921
960
  return 1;
922
961
  }
923
962
  const dryRun = args.includes("--dry-run");
924
- const positionalArgs = extractPositionalArgs(args, new Set());
925
- if (positionalArgs.length > 1) {
926
- io.stderr.write("Usage error: gdh imports refresh accepts at most one positional target path.\n");
963
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
964
+ usage: "Usage: gdh imports refresh [target] [--dry-run]\n",
965
+ });
966
+ if (targetPathError !== null) {
967
+ io.stderr.write(targetPathError);
927
968
  return 1;
928
969
  }
929
- const targetPath = positionalArgs[0] ?? ".";
930
970
  const commandStartedAtMs = Date.now();
931
971
  try {
932
972
  const context = await buildAuthoringContext(targetPath);
@@ -1479,12 +1519,14 @@ async function runMigrateCommand(args, io) {
1479
1519
  io.stderr.write(unsupportedOptionsError);
1480
1520
  return 1;
1481
1521
  }
1482
- const positionalArgs = extractPositionalArgs(args, new Set(["--apply"]));
1483
- if (positionalArgs.length > 1) {
1484
- io.stderr.write("Usage error: gdh migrate accepts at most one positional target path.\n");
1522
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
1523
+ usage: "Usage: gdh migrate [target] [--apply]\n",
1524
+ additionalOptionsWithValues: new Set(["--apply"]),
1525
+ });
1526
+ if (targetPathError !== null) {
1527
+ io.stderr.write(targetPathError);
1485
1528
  return 1;
1486
1529
  }
1487
- const targetPath = positionalArgs[0] ?? ".";
1488
1530
  const dryRun = !args.includes("--apply");
1489
1531
  try {
1490
1532
  const result = await migrateProjectLifecycleSurface({ targetPath, dryRun });
@@ -1513,12 +1555,13 @@ async function runBridgeStatusCommand(args, io) {
1513
1555
  io.stderr.write(unsupportedOptionsError);
1514
1556
  return 1;
1515
1557
  }
1516
- const positionalArgs = extractPositionalArgs(args, new Set());
1517
- if (positionalArgs.length > 1) {
1518
- io.stderr.write("Usage error: gdh bridge status accepts at most one positional target path.\n");
1558
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
1559
+ usage: "Usage: gdh bridge status [target]\nUsage: gdh bridge doctor [target]\n",
1560
+ });
1561
+ if (targetPathError !== null) {
1562
+ io.stderr.write(targetPathError);
1519
1563
  return 1;
1520
1564
  }
1521
- const targetPath = positionalArgs[0] ?? ".";
1522
1565
  const commandStartedAtMs = Date.now();
1523
1566
  try {
1524
1567
  const projectConfig = await readProjectConfig(targetPath);
@@ -1566,12 +1609,14 @@ async function runBridgeApplyCommand(args, io, operation) {
1566
1609
  io.stderr.write(unsupportedOptionsError);
1567
1610
  return 1;
1568
1611
  }
1569
- const positionalArgs = extractPositionalArgs(args, new Set(["--dry-run"]));
1570
- if (positionalArgs.length > 1) {
1571
- io.stderr.write(`Usage error: gdh bridge ${operation} accepts at most one positional target path.\n`);
1612
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
1613
+ usage: `Usage: gdh bridge ${operation} [target] [--dry-run]\n`,
1614
+ additionalOptionsWithValues: new Set(["--dry-run"]),
1615
+ });
1616
+ if (targetPathError !== null) {
1617
+ io.stderr.write(targetPathError);
1572
1618
  return 1;
1573
1619
  }
1574
- const targetPath = positionalArgs[0] ?? ".";
1575
1620
  const dryRun = args.includes("--dry-run");
1576
1621
  const commandStartedAtMs = Date.now();
1577
1622
  try {
@@ -1645,12 +1690,14 @@ async function runRecipeListCommand(args, io) {
1645
1690
  io.stderr.write(unsupportedOptionsError);
1646
1691
  return 1;
1647
1692
  }
1648
- const positionalArgs = extractRecipePositionalArgs(args);
1649
- if (positionalArgs.length > 1) {
1650
- io.stderr.write("Usage error: gdh run-config list accepts at most one positional target path.\n");
1693
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
1694
+ usage: "Usage: gdh run-config list [target]\n",
1695
+ additionalOptionsWithValues: new Set(["--provider", "--param", "--feature", "--no-feature", "--env"]),
1696
+ });
1697
+ if (targetPathError !== null) {
1698
+ io.stderr.write(targetPathError);
1651
1699
  return 1;
1652
1700
  }
1653
- const targetPath = positionalArgs[0] ?? ".";
1654
1701
  const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
1655
1702
  const commandStartedAtMs = Date.now();
1656
1703
  try {
@@ -1879,12 +1926,14 @@ async function runScenarioListCommand(args, io) {
1879
1926
  io.stderr.write(unsupportedOptionsError);
1880
1927
  return 1;
1881
1928
  }
1882
- const positionalArgs = extractRecipePositionalArgs(args);
1883
- if (positionalArgs.length > 1) {
1884
- io.stderr.write("Usage error: gdh verification-scenario list accepts at most one positional target path.\n");
1929
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
1930
+ usage: "Usage: gdh verification-scenario list [target]\n",
1931
+ additionalOptionsWithValues: new Set(["--provider", "--param", "--feature", "--no-feature", "--env"]),
1932
+ });
1933
+ if (targetPathError !== null) {
1934
+ io.stderr.write(targetPathError);
1885
1935
  return 1;
1886
1936
  }
1887
- const targetPath = positionalArgs[0] ?? ".";
1888
1937
  const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
1889
1938
  const commandStartedAtMs = Date.now();
1890
1939
  try {
@@ -1983,12 +2032,14 @@ async function runAdaptersStatusCommand(args, io) {
1983
2032
  io.stderr.write(unsupportedOptionsError);
1984
2033
  return 1;
1985
2034
  }
1986
- const positionalArgs = extractPositionalArgs(args, new Set(["--integration-root"]));
1987
- if (positionalArgs.length > 1) {
1988
- io.stderr.write("Usage error: gdh adapters status accepts at most one positional target path.\n");
2035
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
2036
+ usage: "Usage: gdh adapters status [target] [--integration-root <path>]\n",
2037
+ additionalOptionsWithValues: new Set(["--integration-root"]),
2038
+ });
2039
+ if (targetPathError !== null) {
2040
+ io.stderr.write(targetPathError);
1989
2041
  return 1;
1990
2042
  }
1991
- const targetPath = positionalArgs[0] ?? ".";
1992
2043
  const integrationRootPath = readSingleOptionValue(args, "--integration-root");
1993
2044
  try {
1994
2045
  const result = await getSupportedAgentAdaptersStatus(targetPath, {
@@ -2025,9 +2076,12 @@ async function runAdaptersInstallCommand(args, io) {
2025
2076
  }
2026
2077
  const dryRun = args.includes("--dry-run");
2027
2078
  const user = args.includes("--user");
2028
- const positionalArgs = extractPositionalArgs(args, new Set(["--agent", "--dev-repo", "--integration-root"]));
2029
- if (positionalArgs.length > 1) {
2030
- io.stderr.write("Usage error: gdh adapters install accepts at most one positional target path.\n");
2079
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
2080
+ usage: "Usage: gdh adapters install [target] [--dry-run] [--agent <name>] [--user] [--dev-repo <path>] [--integration-root <path>]\n",
2081
+ additionalOptionsWithValues: new Set(["--agent", "--dev-repo", "--integration-root"]),
2082
+ });
2083
+ if (targetPathError !== null) {
2084
+ io.stderr.write(targetPathError);
2031
2085
  return 1;
2032
2086
  }
2033
2087
  const agents = collectAdapterNames(args);
@@ -2035,7 +2089,6 @@ async function runAdaptersInstallCommand(args, io) {
2035
2089
  io.stderr.write("Usage error: --agent must be one of codex, claude, or cursor.\n");
2036
2090
  return 1;
2037
2091
  }
2038
- const targetPath = positionalArgs[0] ?? ".";
2039
2092
  const devRepoPath = readSingleOptionValue(args, "--dev-repo");
2040
2093
  const integrationRootPath = readSingleOptionValue(args, "--integration-root");
2041
2094
  try {
@@ -2071,12 +2124,14 @@ async function runGsdSnapshotCommand(args, io) {
2071
2124
  io.stderr.write(unsupportedOptionsError);
2072
2125
  return 1;
2073
2126
  }
2074
- const positionalArgs = extractPositionalArgs(args, new Set(["--files", "--performed"]));
2075
- if (positionalArgs.length > 1) {
2076
- io.stderr.write("Usage error: gdh adapters gsd snapshot accepts at most one positional target path.\n");
2127
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
2128
+ usage: "Usage: gdh adapters gsd snapshot [target] [--files <path>] [--performed <kind>]\n",
2129
+ additionalOptionsWithValues: new Set(["--files", "--performed"]),
2130
+ });
2131
+ if (targetPathError !== null) {
2132
+ io.stderr.write(targetPathError);
2077
2133
  return 1;
2078
2134
  }
2079
- const targetPath = positionalArgs[0] ?? ".";
2080
2135
  try {
2081
2136
  const result = await createGsdSnapshot(targetPath, {
2082
2137
  files: collectOptionValues(args, "--files"),
@@ -2197,12 +2252,14 @@ async function runMcpInvokeCommand(args, io) {
2197
2252
  io.stderr.write(unsupportedOptionsError);
2198
2253
  return 1;
2199
2254
  }
2200
- const positionalArgs = extractPositionalArgs(rest, new Set(["--input-json"]));
2201
- if (positionalArgs.length > 1) {
2202
- io.stderr.write("Usage error: gdh mcp invoke accepts at most one positional target path.\n");
2255
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(rest, {
2256
+ usage: "Usage: gdh mcp invoke <tool> [target] [--input-json <json>]\n",
2257
+ additionalOptionsWithValues: new Set(["--input-json"]),
2258
+ });
2259
+ if (targetPathError !== null) {
2260
+ io.stderr.write(targetPathError);
2203
2261
  return 1;
2204
2262
  }
2205
- const targetPath = positionalArgs[0] ?? ".";
2206
2263
  try {
2207
2264
  const result = await invokeMcpTool({
2208
2265
  toolName: toolName,
@@ -2260,9 +2317,12 @@ async function runVerifyRecommendCommand(args, io) {
2260
2317
  io.stderr.write(unsupportedOptionsError);
2261
2318
  return 1;
2262
2319
  }
2263
- const positionalArgs = extractPositionalArgs(args, new Set(["--files"]));
2264
- if (positionalArgs.length > 1) {
2265
- io.stderr.write("Usage error: gdh verify recommend accepts at most one positional target path.\n");
2320
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
2321
+ usage: "Usage: gdh verify recommend [target] --files <path> [--files <path>...]\n",
2322
+ additionalOptionsWithValues: new Set(["--files"]),
2323
+ });
2324
+ if (targetPathError !== null) {
2325
+ io.stderr.write(targetPathError);
2266
2326
  return 1;
2267
2327
  }
2268
2328
  const files = collectOptionValues(args, "--files");
@@ -2270,7 +2330,6 @@ async function runVerifyRecommendCommand(args, io) {
2270
2330
  io.stderr.write("Usage error: gdh verify recommend requires at least one --files <path> argument.\n");
2271
2331
  return 1;
2272
2332
  }
2273
- const targetPath = positionalArgs[0] ?? ".";
2274
2333
  const commandStartedAtMs = Date.now();
2275
2334
  try {
2276
2335
  const context = await buildAuthoringContext(targetPath);
@@ -2329,9 +2388,12 @@ async function runVerifyDoneCommand(args, io) {
2329
2388
  io.stderr.write(unsupportedOptionsError);
2330
2389
  return 1;
2331
2390
  }
2332
- const positionalArgs = extractPositionalArgs(args, new Set(["--files", "--performed"]));
2333
- if (positionalArgs.length > 1) {
2334
- io.stderr.write("Usage error: gdh verify done accepts at most one positional target path.\n");
2391
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
2392
+ usage: "Usage: gdh verify done [target] --files <path> [--files <path>...] [--performed <kind>]\n",
2393
+ additionalOptionsWithValues: new Set(["--files", "--performed"]),
2394
+ });
2395
+ if (targetPathError !== null) {
2396
+ io.stderr.write(targetPathError);
2335
2397
  return 1;
2336
2398
  }
2337
2399
  const files = collectOptionValues(args, "--files");
@@ -2339,7 +2401,6 @@ async function runVerifyDoneCommand(args, io) {
2339
2401
  io.stderr.write("Usage error: gdh verify done requires at least one --files <path> argument.\n");
2340
2402
  return 1;
2341
2403
  }
2342
- const targetPath = positionalArgs[0] ?? ".";
2343
2404
  const commandStartedAtMs = Date.now();
2344
2405
  try {
2345
2406
  const context = await buildAuthoringContext(targetPath);
@@ -2490,12 +2551,14 @@ async function runVerifyInspectCommand(args, io) {
2490
2551
  io.stderr.write(unsupportedOptionsError);
2491
2552
  return 1;
2492
2553
  }
2493
- const positionalArgs = extractPositionalArgs(args, new Set(["--run-record", "--bundle"]));
2494
- if (positionalArgs.length > 1) {
2495
- io.stderr.write("Usage error: gdh verify inspect accepts at most one positional target path.\n");
2554
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
2555
+ usage: "Usage: gdh verify inspect [target] [--run-record <run-record-id>]\n",
2556
+ additionalOptionsWithValues: new Set(["--run-record", "--bundle"]),
2557
+ });
2558
+ if (targetPathError !== null) {
2559
+ io.stderr.write(targetPathError);
2496
2560
  return 1;
2497
2561
  }
2498
- const targetPath = positionalArgs[0] ?? ".";
2499
2562
  const commandStartedAtMs = Date.now();
2500
2563
  try {
2501
2564
  const result = await inspectRuntimeVerificationBundleState({
@@ -2542,12 +2605,13 @@ async function runVerifyReadinessCommand(args, io) {
2542
2605
  io.stderr.write(unsupportedOptionsError);
2543
2606
  return 1;
2544
2607
  }
2545
- const positionalArgs = extractPositionalArgs(args, new Set());
2546
- if (positionalArgs.length > 1) {
2547
- io.stderr.write("Usage error: gdh verify readiness accepts at most one positional target path.\n");
2608
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
2609
+ usage: "Usage: gdh verify readiness [target]\n",
2610
+ });
2611
+ if (targetPathError !== null) {
2612
+ io.stderr.write(targetPathError);
2548
2613
  return 1;
2549
2614
  }
2550
- const targetPath = positionalArgs[0] ?? ".";
2551
2615
  const commandStartedAtMs = Date.now();
2552
2616
  try {
2553
2617
  const projectConfig = await readProjectConfig(targetPath);
@@ -2665,12 +2729,13 @@ async function runVerifyDriftCommand(args, io) {
2665
2729
  io.stderr.write(unsupportedOptionsError);
2666
2730
  return 1;
2667
2731
  }
2668
- const positionalArgs = extractPositionalArgs(args, new Set());
2669
- if (positionalArgs.length > 1) {
2670
- io.stderr.write("Usage error: gdh verify drift accepts at most one positional target path.\n");
2732
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
2733
+ usage: "Usage: gdh verify drift [target]\n",
2734
+ });
2735
+ if (targetPathError !== null) {
2736
+ io.stderr.write(targetPathError);
2671
2737
  return 1;
2672
2738
  }
2673
- const targetPath = positionalArgs[0] ?? ".";
2674
2739
  const commandStartedAtMs = Date.now();
2675
2740
  let pinnedVersion;
2676
2741
  try {
@@ -2941,12 +3006,14 @@ async function runCacheInspectCommand(args, io) {
2941
3006
  io.stderr.write(unsupportedOptionsError);
2942
3007
  return 1;
2943
3008
  }
2944
- const positionalArgs = extractGuidanceResolvePositionalArgs(args);
2945
- if (positionalArgs.length > 1) {
2946
- io.stderr.write("Usage error: gdh cache inspect accepts at most one positional target path.\n");
3009
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
3010
+ usage: "Usage: gdh cache inspect [target]\n",
3011
+ additionalOptionsWithValues: new Set(["--task-type", "--capability", "--repo-state", "--keyword"]),
3012
+ });
3013
+ if (targetPathError !== null) {
3014
+ io.stderr.write(targetPathError);
2947
3015
  return 1;
2948
3016
  }
2949
- const targetPath = positionalArgs[0] ?? ".";
2950
3017
  try {
2951
3018
  const context = await buildAuthoringContext(targetPath);
2952
3019
  const result = await inspectCacheState({
@@ -2978,12 +3045,13 @@ async function runGuidanceStatusCommand(args, io) {
2978
3045
  io.stderr.write(unsupportedOptionsError);
2979
3046
  return 1;
2980
3047
  }
2981
- const positionalArgs = args.filter((arg) => !arg.startsWith("--"));
2982
- if (positionalArgs.length > 1) {
2983
- io.stderr.write("Usage error: gdh guidance status accepts at most one positional target path.\n");
3048
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
3049
+ usage: "Usage: gdh guidance status [target]\n",
3050
+ });
3051
+ if (targetPathError !== null) {
3052
+ io.stderr.write(targetPathError);
2984
3053
  return 1;
2985
3054
  }
2986
- const targetPath = positionalArgs[0] ?? ".";
2987
3055
  const commandStartedAtMs = Date.now();
2988
3056
  try {
2989
3057
  const context = await buildAuthoringContext(targetPath);
@@ -3045,12 +3113,14 @@ async function runGuidanceResolveCommand(args, io) {
3045
3113
  io.stderr.write(unsupportedOptionsError);
3046
3114
  return 1;
3047
3115
  }
3048
- const positionalArgs = extractGuidanceResolvePositionalArgs(args);
3049
- if (positionalArgs.length > 1) {
3050
- io.stderr.write("Usage error: gdh guidance resolve accepts at most one positional target path.\n");
3116
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
3117
+ usage: "Usage: gdh guidance resolve [target] [--task-type <type>] [--capability <id>] [--repo-state <state>] [--keyword <term>]\n",
3118
+ additionalOptionsWithValues: new Set(["--task-type", "--capability", "--repo-state", "--keyword"]),
3119
+ });
3120
+ if (targetPathError !== null) {
3121
+ io.stderr.write(targetPathError);
3051
3122
  return 1;
3052
3123
  }
3053
- const targetPath = positionalArgs[0] ?? ".";
3054
3124
  const commandStartedAtMs = Date.now();
3055
3125
  try {
3056
3126
  const context = await buildAuthoringContext(targetPath);
@@ -3104,12 +3174,13 @@ async function runGuidanceAuditCommand(args, io) {
3104
3174
  io.stderr.write(unsupportedOptionsError);
3105
3175
  return 1;
3106
3176
  }
3107
- const positionalArgs = args.filter((arg) => !arg.startsWith("--"));
3108
- if (positionalArgs.length > 1) {
3109
- io.stderr.write("Usage error: gdh guidance audit accepts at most one positional target path.\n");
3177
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
3178
+ usage: "Usage: gdh guidance audit [target]\n",
3179
+ });
3180
+ if (targetPathError !== null) {
3181
+ io.stderr.write(targetPathError);
3110
3182
  return 1;
3111
3183
  }
3112
- const targetPath = positionalArgs[0] ?? ".";
3113
3184
  try {
3114
3185
  const result = await inspectGuidanceAudit(targetPath);
3115
3186
  await recordSessionEvent(targetPath, {
@@ -3151,12 +3222,14 @@ async function runObservabilitySessionCommand(args, io) {
3151
3222
  io.stderr.write(unsupportedOptionsError);
3152
3223
  return 1;
3153
3224
  }
3154
- const positionalArgs = extractPositionalArgs(args, new Set(["--session-id"]));
3155
- if (positionalArgs.length > 1) {
3156
- io.stderr.write("Usage error: gdh observability session accepts at most one positional target path.\n");
3225
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
3226
+ usage: "Usage: gdh observability session [target] [--latest] [--session-id <id>]\n",
3227
+ additionalOptionsWithValues: new Set(["--session-id"]),
3228
+ });
3229
+ if (targetPathError !== null) {
3230
+ io.stderr.write(targetPathError);
3157
3231
  return 1;
3158
3232
  }
3159
- const targetPath = positionalArgs[0] ?? ".";
3160
3233
  const sessionId = readSingleOptionValue(args, "--session-id");
3161
3234
  try {
3162
3235
  const result = await inspectAuthoringSessions(targetPath, {
@@ -3189,12 +3262,14 @@ async function runObservabilityEffectivenessCommand(args, io) {
3189
3262
  io.stderr.write(unsupportedOptionsError);
3190
3263
  return 1;
3191
3264
  }
3192
- const positionalArgs = extractPositionalArgs(args, new Set(["--session-id"]));
3193
- if (positionalArgs.length > 1) {
3194
- io.stderr.write("Usage error: gdh observability effectiveness accepts at most one positional target path.\n");
3265
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
3266
+ usage: "Usage: gdh observability effectiveness [target] [--latest] [--session-id <id>]\n",
3267
+ additionalOptionsWithValues: new Set(["--session-id"]),
3268
+ });
3269
+ if (targetPathError !== null) {
3270
+ io.stderr.write(targetPathError);
3195
3271
  return 1;
3196
3272
  }
3197
- const targetPath = positionalArgs[0] ?? ".";
3198
3273
  const sessionId = readSingleOptionValue(args, "--session-id");
3199
3274
  try {
3200
3275
  const result = await inspectAuthoringEffectiveness(targetPath, {
@@ -3229,17 +3304,19 @@ async function runCachePruneCommand(args, io) {
3229
3304
  return 1;
3230
3305
  }
3231
3306
  const dryRun = args.includes("--dry-run");
3232
- const positionalArgs = extractCachePrunePositionalArgs(args);
3307
+ const { targetPath, error: targetPathError } = parseOptionalPositionalTargetPath(args, {
3308
+ usage: "Usage: gdh cache prune [target] [--scope <scope>] [--dry-run]\n",
3309
+ additionalOptionsWithValues: new Set(["--scope"]),
3310
+ });
3311
+ if (targetPathError !== null) {
3312
+ io.stderr.write(targetPathError);
3313
+ return 1;
3314
+ }
3233
3315
  const scope = resolveCachePruneScope(args);
3234
3316
  if (scope === null) {
3235
3317
  io.stderr.write("Usage error: --scope must be one of worktree, project, or all.\n");
3236
3318
  return 1;
3237
3319
  }
3238
- if (positionalArgs.length > 1) {
3239
- io.stderr.write("Usage error: gdh cache prune accepts at most one positional target path.\n");
3240
- return 1;
3241
- }
3242
- const targetPath = positionalArgs[0] ?? ".";
3243
3320
  try {
3244
3321
  const context = await buildAuthoringContext(targetPath);
3245
3322
  const result = await pruneCacheState({
@@ -3846,24 +3923,6 @@ function resolveCachePruneScope(args) {
3846
3923
  }
3847
3924
  return null;
3848
3925
  }
3849
- function extractCachePrunePositionalArgs(args) {
3850
- const positionalArgs = [];
3851
- for (let index = 0; index < args.length; index += 1) {
3852
- const arg = args[index];
3853
- if (!arg) {
3854
- continue;
3855
- }
3856
- if (arg === "--scope") {
3857
- index += 1;
3858
- continue;
3859
- }
3860
- if (arg.startsWith("--")) {
3861
- continue;
3862
- }
3863
- positionalArgs.push(arg);
3864
- }
3865
- return positionalArgs;
3866
- }
3867
3926
  function collectOptionValues(args, optionName) {
3868
3927
  const values = [];
3869
3928
  for (let index = 0; index < args.length; index += 1) {
@@ -3912,7 +3971,14 @@ export function findUnsupportedOptionsError(args, options) {
3912
3971
  }
3913
3972
  return `Usage error: unsupported option(s): ${unknownOptions.join(", ")}.\n${options.usage}`;
3914
3973
  }
3915
- function parseSingleTargetPathArg(args, options) {
3974
+ /**
3975
+ * CLI ingress helper. Returns an ABSOLUTE `targetPath` on both success and error paths.
3976
+ *
3977
+ * Part of the "targetPath is absolute post-ingress" invariant committed in
3978
+ * `docs/architecture/package-boundaries.md#path-contracts`. Uses `path.resolve` — NEVER
3979
+ * `fs.realpath` — to preserve caller-supplied symlink path shapes (worktree invocations).
3980
+ */
3981
+ export function parseSingleTargetPathArg(args, options) {
3916
3982
  const allowTargetOption = options.allowTargetOption ?? false;
3917
3983
  const unsupportedOptionsError = findUnsupportedOptionsError(args, {
3918
3984
  usage: options.usage,
@@ -3920,7 +3986,7 @@ function parseSingleTargetPathArg(args, options) {
3920
3986
  });
3921
3987
  if (unsupportedOptionsError !== null) {
3922
3988
  return {
3923
- targetPath: ".",
3989
+ targetPath: path.resolve("."),
3924
3990
  error: unsupportedOptionsError,
3925
3991
  };
3926
3992
  }
@@ -3928,24 +3994,51 @@ function parseSingleTargetPathArg(args, options) {
3928
3994
  const positionalArgs = extractPositionalArgs(args, new Set(allowTargetOption ? ["--target"] : []));
3929
3995
  if (allowTargetOption && args.includes("--target") && targetOptionValue === null) {
3930
3996
  return {
3931
- targetPath: ".",
3997
+ targetPath: path.resolve("."),
3932
3998
  error: `Usage error: --target requires a path value.\n${options.usage}`,
3933
3999
  };
3934
4000
  }
3935
4001
  if (positionalArgs.length > 1) {
3936
4002
  return {
3937
- targetPath: ".",
4003
+ targetPath: path.resolve("."),
3938
4004
  error: `Usage error: command accepts at most one target path.\n${options.usage}`,
3939
4005
  };
3940
4006
  }
3941
4007
  if (allowTargetOption && targetOptionValue !== null && positionalArgs.length > 0) {
3942
4008
  return {
3943
- targetPath: ".",
4009
+ targetPath: path.resolve("."),
3944
4010
  error: `Usage error: choose either a positional target path or --target <path>.\n${options.usage}`,
3945
4011
  };
3946
4012
  }
3947
4013
  return {
3948
- targetPath: targetOptionValue ?? positionalArgs[0] ?? ".",
4014
+ targetPath: path.resolve(targetOptionValue ?? positionalArgs[0] ?? "."),
4015
+ error: null,
4016
+ };
4017
+ }
4018
+ /**
4019
+ * CLI ingress helper for subcommands that accept at most one optional positional
4020
+ * target-path argument (no `--target` option). Returns an ABSOLUTE `targetPath` on both
4021
+ * success and error paths.
4022
+ *
4023
+ * Part of the "targetPath is absolute post-ingress" invariant committed in
4024
+ * `docs/architecture/package-boundaries.md#path-contracts`. Uses `path.resolve` — NEVER
4025
+ * `fs.realpath` — to preserve caller-supplied symlink path shapes (worktree invocations).
4026
+ *
4027
+ * @param args - argv slice starting after the subcommand name.
4028
+ * @param options.usage - usage string embedded in error messages.
4029
+ * @param options.additionalOptionsWithValues - options the subcommand consumes with values
4030
+ * so they are not misinterpreted as positionals (forwarded to `extractPositionalArgs`).
4031
+ */
4032
+ export function parseOptionalPositionalTargetPath(args, options) {
4033
+ const positionalArgs = extractPositionalArgs(args, options.additionalOptionsWithValues ?? new Set());
4034
+ if (positionalArgs.length > 1) {
4035
+ return {
4036
+ targetPath: path.resolve("."),
4037
+ error: `Usage error: command accepts at most one positional target path.\n${options.usage}`,
4038
+ };
4039
+ }
4040
+ return {
4041
+ targetPath: path.resolve(positionalArgs[0] ?? "."),
3949
4042
  error: null,
3950
4043
  };
3951
4044
  }
@@ -4032,9 +4125,6 @@ function collectPerformedValidationKinds(args) {
4032
4125
  }
4033
4126
  });
4034
4127
  }
4035
- function extractGuidanceResolvePositionalArgs(args) {
4036
- return extractPositionalArgs(args, new Set(["--task-type", "--capability", "--repo-state", "--keyword"]));
4037
- }
4038
4128
  function extractRecipePositionalArgs(args) {
4039
4129
  return extractPositionalArgs(args, new Set(["--provider", "--param", "--feature", "--no-feature", "--env"]));
4040
4130
  }