@skillcap/gdh 0.6.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 +23 -5
  3. package/node_modules/@gdh/adapters/dist/index.d.ts +6 -3
  4. package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
  5. package/node_modules/@gdh/adapters/dist/index.js +222 -258
  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 +279 -150
  18. package/node_modules/@gdh/cli/dist/index.js.map +1 -1
  19. package/node_modules/@gdh/cli/dist/migrate.d.ts +5 -1
  20. package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
  21. package/node_modules/@gdh/cli/dist/migrate.js +57 -3
  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 +77 -4
  32. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  33. package/node_modules/@gdh/core/dist/index.js +45 -6
  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
@@ -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_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";
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";
@@ -13,7 +16,7 @@ import { evaluateDonePolicy, exerciseRuntimeCorpusEntry, inspectRuntimeCorpusSta
13
16
  import { migrateProjectLifecycleSurface } from "./migrate.js";
14
17
  import { presentPublicRuntimeTerms } from "./public-terms.js";
15
18
  import { runSelfUpdateCommand } from "./self-update.js";
16
- import { executeSetupCommand, isSetupCanceledError, renderSetupIntro, renderSetupOutro, renderSetupSummary, } from "./setup.js";
19
+ import { executeSetupCommand, isSetupCanceledError, renderDurableTruthCollisionHint, renderSetupIntro, renderSetupOutro, renderSetupSummary, } from "./setup.js";
17
20
  import { emitUpdateBannerIfStale } from "./update-banner.js";
18
21
  export const cliPackage = definePackageBoundary({
19
22
  name: "@gdh/cli",
@@ -41,6 +44,18 @@ export async function runCli(args, io = { stdout: process.stdout, stderr: proces
41
44
  io.stdout.write(`${resolveGdhProductMetadata().version}\n`);
42
45
  return 0;
43
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
+ }
44
59
  if (command === "version") {
45
60
  return runVersionCommand(rest, io);
46
61
  }
@@ -213,8 +228,12 @@ async function runSetupCommand(args, io) {
213
228
  const yes = args.includes("--yes");
214
229
  const allAgents = args.includes("--all-agents");
215
230
  const positionalArgs = extractPositionalArgs(args, new Set(["--agent", "--project", "--integration-root"]));
216
- if (positionalArgs.length > 1) {
217
- 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);
218
237
  return 1;
219
238
  }
220
239
  const requestedAgents = collectAdapterNames(args);
@@ -226,7 +245,6 @@ async function runSetupCommand(args, io) {
226
245
  io.stderr.write("Usage error: choose either --all-agents or one or more --agent values.\n");
227
246
  return 1;
228
247
  }
229
- const targetPath = positionalArgs[0] ?? ".";
230
248
  const explicitTargetPath = positionalArgs.length === 1;
231
249
  const selectedProjectRelativePath = readSingleOptionValue(args, "--project");
232
250
  const integrationRootPath = readSingleOptionValue(args, "--integration-root");
@@ -288,6 +306,22 @@ async function runSetupCommand(args, io) {
288
306
  });
289
307
  return 1;
290
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
+ }
291
325
  await recordSessionEvent(targetPath, {
292
326
  commandStartedAtMs,
293
327
  kind: "onboard",
@@ -318,12 +352,14 @@ async function runOnboardCommand(args, io) {
318
352
  return 1;
319
353
  }
320
354
  const dryRun = args.includes("--dry-run");
321
- const positionalArgs = extractGuidanceResolvePositionalArgs(args);
322
- if (positionalArgs.length > 1) {
323
- 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);
324
361
  return 1;
325
362
  }
326
- const targetPath = positionalArgs[0] ?? ".";
327
363
  const commandStartedAtMs = Date.now();
328
364
  try {
329
365
  const result = await onboardGodotProject(targetPath, { dryRun });
@@ -519,16 +555,18 @@ async function runDocsVersionCommand(args, io) {
519
555
  io.stderr.write(unsupportedOptionsError);
520
556
  return 1;
521
557
  }
522
- const positionalArgs = extractPositionalArgs(args, new Set(["--project"]));
523
- if (positionalArgs.length > 1) {
524
- 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);
525
564
  return 1;
526
565
  }
527
566
  if (args.includes("--project") && readSingleOptionValue(args, "--project") === null) {
528
567
  io.stderr.write("Usage error: --project requires a relative project path.\n");
529
568
  return 1;
530
569
  }
531
- const targetPath = positionalArgs[0] ?? ".";
532
570
  const requestedProjectPath = readSingleOptionValue(args, "--project");
533
571
  try {
534
572
  const context = await buildAuthoringContext(targetPath);
@@ -767,12 +805,14 @@ async function runWarmupCommand(args, io) {
767
805
  return 1;
768
806
  }
769
807
  const dryRun = args.includes("--dry-run");
770
- const positionalArgs = extractGuidanceResolvePositionalArgs(args);
771
- if (positionalArgs.length > 1) {
772
- 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);
773
814
  return 1;
774
815
  }
775
- const targetPath = positionalArgs[0] ?? ".";
776
816
  try {
777
817
  const context = await buildAuthoringContext(targetPath);
778
818
  const result = await runWarmup({
@@ -831,12 +871,14 @@ async function runTargetPrepareCommand(args, io) {
831
871
  }
832
872
  const dryRun = args.includes("--dry-run");
833
873
  const refreshImports = !args.includes("--no-refresh-imports");
834
- const positionalArgs = extractPositionalArgs(args, new Set(["--source-target"]));
835
- if (positionalArgs.length > 1) {
836
- 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);
837
880
  return 1;
838
881
  }
839
- const targetPath = positionalArgs[0] ?? ".";
840
882
  const sourceTargetPath = collectOptionValues(args, "--source-target")[0] ?? null;
841
883
  const commandStartedAtMs = Date.now();
842
884
  try {
@@ -918,12 +960,13 @@ async function runImportsRefreshCommand(args, io) {
918
960
  return 1;
919
961
  }
920
962
  const dryRun = args.includes("--dry-run");
921
- const positionalArgs = extractPositionalArgs(args, new Set());
922
- if (positionalArgs.length > 1) {
923
- 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);
924
968
  return 1;
925
969
  }
926
- const targetPath = positionalArgs[0] ?? ".";
927
970
  const commandStartedAtMs = Date.now();
928
971
  try {
929
972
  const context = await buildAuthoringContext(targetPath);
@@ -1259,7 +1302,7 @@ async function runVerifyCommand(args, io) {
1259
1302
  " corpus <status|record>",
1260
1303
  " Inspect or record runtime corpus release-readiness evidence as JSON.",
1261
1304
  " drift [target]",
1262
- " Check whether baked `@skillcap/gdh@<version>` literals (launcher + skills + commands) match .gdh/project.yaml gdh_version.",
1305
+ " Check whether baked `@skillcap/gdh@<version>` literals (skills + commands) match .gdh/project.yaml gdh_version.",
1263
1306
  " recommend [target] --files <path> [--files <path>...]",
1264
1307
  " Recommend authoring-first validation requirements for the supplied change set.",
1265
1308
  " done [target] --files <path> [--files <path>...] [--performed <kind>]",
@@ -1476,12 +1519,14 @@ async function runMigrateCommand(args, io) {
1476
1519
  io.stderr.write(unsupportedOptionsError);
1477
1520
  return 1;
1478
1521
  }
1479
- const positionalArgs = extractPositionalArgs(args, new Set(["--apply"]));
1480
- if (positionalArgs.length > 1) {
1481
- 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);
1482
1528
  return 1;
1483
1529
  }
1484
- const targetPath = positionalArgs[0] ?? ".";
1485
1530
  const dryRun = !args.includes("--apply");
1486
1531
  try {
1487
1532
  const result = await migrateProjectLifecycleSurface({ targetPath, dryRun });
@@ -1510,12 +1555,13 @@ async function runBridgeStatusCommand(args, io) {
1510
1555
  io.stderr.write(unsupportedOptionsError);
1511
1556
  return 1;
1512
1557
  }
1513
- const positionalArgs = extractPositionalArgs(args, new Set());
1514
- if (positionalArgs.length > 1) {
1515
- 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);
1516
1563
  return 1;
1517
1564
  }
1518
- const targetPath = positionalArgs[0] ?? ".";
1519
1565
  const commandStartedAtMs = Date.now();
1520
1566
  try {
1521
1567
  const projectConfig = await readProjectConfig(targetPath);
@@ -1563,12 +1609,14 @@ async function runBridgeApplyCommand(args, io, operation) {
1563
1609
  io.stderr.write(unsupportedOptionsError);
1564
1610
  return 1;
1565
1611
  }
1566
- const positionalArgs = extractPositionalArgs(args, new Set(["--dry-run"]));
1567
- if (positionalArgs.length > 1) {
1568
- 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);
1569
1618
  return 1;
1570
1619
  }
1571
- const targetPath = positionalArgs[0] ?? ".";
1572
1620
  const dryRun = args.includes("--dry-run");
1573
1621
  const commandStartedAtMs = Date.now();
1574
1622
  try {
@@ -1642,12 +1690,14 @@ async function runRecipeListCommand(args, io) {
1642
1690
  io.stderr.write(unsupportedOptionsError);
1643
1691
  return 1;
1644
1692
  }
1645
- const positionalArgs = extractRecipePositionalArgs(args);
1646
- if (positionalArgs.length > 1) {
1647
- 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);
1648
1699
  return 1;
1649
1700
  }
1650
- const targetPath = positionalArgs[0] ?? ".";
1651
1701
  const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
1652
1702
  const commandStartedAtMs = Date.now();
1653
1703
  try {
@@ -1876,12 +1926,14 @@ async function runScenarioListCommand(args, io) {
1876
1926
  io.stderr.write(unsupportedOptionsError);
1877
1927
  return 1;
1878
1928
  }
1879
- const positionalArgs = extractRecipePositionalArgs(args);
1880
- if (positionalArgs.length > 1) {
1881
- 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);
1882
1935
  return 1;
1883
1936
  }
1884
- const targetPath = positionalArgs[0] ?? ".";
1885
1937
  const effectiveTargetPath = await resolveEffectiveTargetPath(targetPath);
1886
1938
  const commandStartedAtMs = Date.now();
1887
1939
  try {
@@ -1980,12 +2032,14 @@ async function runAdaptersStatusCommand(args, io) {
1980
2032
  io.stderr.write(unsupportedOptionsError);
1981
2033
  return 1;
1982
2034
  }
1983
- const positionalArgs = extractPositionalArgs(args, new Set(["--integration-root"]));
1984
- if (positionalArgs.length > 1) {
1985
- 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);
1986
2041
  return 1;
1987
2042
  }
1988
- const targetPath = positionalArgs[0] ?? ".";
1989
2043
  const integrationRootPath = readSingleOptionValue(args, "--integration-root");
1990
2044
  try {
1991
2045
  const result = await getSupportedAgentAdaptersStatus(targetPath, {
@@ -2022,9 +2076,12 @@ async function runAdaptersInstallCommand(args, io) {
2022
2076
  }
2023
2077
  const dryRun = args.includes("--dry-run");
2024
2078
  const user = args.includes("--user");
2025
- const positionalArgs = extractPositionalArgs(args, new Set(["--agent", "--dev-repo", "--integration-root"]));
2026
- if (positionalArgs.length > 1) {
2027
- 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);
2028
2085
  return 1;
2029
2086
  }
2030
2087
  const agents = collectAdapterNames(args);
@@ -2032,7 +2089,6 @@ async function runAdaptersInstallCommand(args, io) {
2032
2089
  io.stderr.write("Usage error: --agent must be one of codex, claude, or cursor.\n");
2033
2090
  return 1;
2034
2091
  }
2035
- const targetPath = positionalArgs[0] ?? ".";
2036
2092
  const devRepoPath = readSingleOptionValue(args, "--dev-repo");
2037
2093
  const integrationRootPath = readSingleOptionValue(args, "--integration-root");
2038
2094
  try {
@@ -2068,12 +2124,14 @@ async function runGsdSnapshotCommand(args, io) {
2068
2124
  io.stderr.write(unsupportedOptionsError);
2069
2125
  return 1;
2070
2126
  }
2071
- const positionalArgs = extractPositionalArgs(args, new Set(["--files", "--performed"]));
2072
- if (positionalArgs.length > 1) {
2073
- 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);
2074
2133
  return 1;
2075
2134
  }
2076
- const targetPath = positionalArgs[0] ?? ".";
2077
2135
  try {
2078
2136
  const result = await createGsdSnapshot(targetPath, {
2079
2137
  files: collectOptionValues(args, "--files"),
@@ -2098,29 +2156,30 @@ async function runMcpManifestCommand(args, io) {
2098
2156
  async function runMcpServeCommand(args, io) {
2099
2157
  if (args.includes("--help")) {
2100
2158
  io.stdout.write([
2101
- "Usage: gdh mcp serve --target <path>",
2159
+ "Usage: gdh mcp serve [--target <path>]",
2102
2160
  "",
2103
- "Run the real GDH stdio MCP server for one bound target path.",
2161
+ "Run the real GDH stdio MCP server. When --target is omitted, defaults to",
2162
+ "the current working directory and walks up to find the nearest .gdh/project.yaml.",
2104
2163
  ].join("\n") + "\n");
2105
2164
  return 0;
2106
2165
  }
2107
2166
  const unsupportedOptionsError = findUnsupportedOptionsError(args, {
2108
- usage: "Usage: gdh mcp serve --target <path>\n",
2167
+ usage: "Usage: gdh mcp serve [--target <path>]\n",
2109
2168
  optionsWithValues: ["--target"],
2110
2169
  });
2111
2170
  if (unsupportedOptionsError !== null) {
2112
2171
  io.stderr.write(unsupportedOptionsError);
2113
2172
  return 1;
2114
2173
  }
2115
- const targetPath = readSingleOptionValue(args, "--target");
2116
- if (targetPath === null) {
2117
- io.stderr.write("Usage error: gdh mcp serve requires --target <path>.\n");
2118
- return 1;
2119
- }
2120
2174
  if (extractPositionalArgs(args, new Set(["--target"])).length > 0) {
2121
2175
  io.stderr.write("Usage error: gdh mcp serve accepts only --target <path>.\n");
2122
2176
  return 1;
2123
2177
  }
2178
+ const targetPath = readSingleOptionValue(args, "--target") ?? process.cwd();
2179
+ const devRepoExitCode = maybeReexecIntoDevRepo(targetPath);
2180
+ if (devRepoExitCode !== null) {
2181
+ return devRepoExitCode;
2182
+ }
2124
2183
  try {
2125
2184
  await serveMcpOverStdio(targetPath);
2126
2185
  return 0;
@@ -2130,6 +2189,42 @@ async function runMcpServeCommand(args, io) {
2130
2189
  return 1;
2131
2190
  }
2132
2191
  }
2192
+ function maybeReexecIntoDevRepo(targetPath) {
2193
+ if (process.env["GDH_MCP_DEV_REEXEC"] === "1") {
2194
+ // Already running under a dev-repo re-exec; skip to avoid a loop.
2195
+ return null;
2196
+ }
2197
+ const envDevRepoPath = process.env["GDH_DEV_REPO"]?.trim() || null;
2198
+ const hintsPath = path.join(path.resolve(targetPath), ".gdh-state", "local-paths.json");
2199
+ let hintDevRepoPath = null;
2200
+ try {
2201
+ const raw = fsSync.readFileSync(hintsPath, "utf8");
2202
+ const parsed = JSON.parse(raw);
2203
+ if (typeof parsed.gdhDevRepoPath === "string" && parsed.gdhDevRepoPath.length > 0) {
2204
+ hintDevRepoPath = parsed.gdhDevRepoPath;
2205
+ }
2206
+ }
2207
+ catch {
2208
+ // No hints file or unparseable — fall through.
2209
+ }
2210
+ const devRepoPath = envDevRepoPath || hintDevRepoPath;
2211
+ if (devRepoPath === null) {
2212
+ return null;
2213
+ }
2214
+ const devCliEntry = path.join(devRepoPath, "packages/cli/dist/cli.js");
2215
+ if (!fsSync.existsSync(devCliEntry)) {
2216
+ return null;
2217
+ }
2218
+ const currentCliPath = path.resolve(fileURLToPath(import.meta.url));
2219
+ const resolvedDevRepoPath = path.resolve(devRepoPath);
2220
+ // Avoid loop if the currently-running CLI source already lives inside the
2221
+ // dev repo (e.g. contributor running the dev CLI directly via tsx).
2222
+ if (currentCliPath.startsWith(`${resolvedDevRepoPath}${path.sep}`)) {
2223
+ return null;
2224
+ }
2225
+ const result = spawnSync(process.execPath, [devCliEntry, "mcp", "serve", "--target", path.resolve(targetPath)], { stdio: "inherit", env: { ...process.env, GDH_MCP_DEV_REEXEC: "1" } });
2226
+ return result.status ?? 1;
2227
+ }
2133
2228
  async function runMcpInvokeCommand(args, io) {
2134
2229
  if (args.includes("--help")) {
2135
2230
  io.stdout.write([
@@ -2157,12 +2252,14 @@ async function runMcpInvokeCommand(args, io) {
2157
2252
  io.stderr.write(unsupportedOptionsError);
2158
2253
  return 1;
2159
2254
  }
2160
- const positionalArgs = extractPositionalArgs(rest, new Set(["--input-json"]));
2161
- if (positionalArgs.length > 1) {
2162
- 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);
2163
2261
  return 1;
2164
2262
  }
2165
- const targetPath = positionalArgs[0] ?? ".";
2166
2263
  try {
2167
2264
  const result = await invokeMcpTool({
2168
2265
  toolName: toolName,
@@ -2220,9 +2317,12 @@ async function runVerifyRecommendCommand(args, io) {
2220
2317
  io.stderr.write(unsupportedOptionsError);
2221
2318
  return 1;
2222
2319
  }
2223
- const positionalArgs = extractPositionalArgs(args, new Set(["--files"]));
2224
- if (positionalArgs.length > 1) {
2225
- 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);
2226
2326
  return 1;
2227
2327
  }
2228
2328
  const files = collectOptionValues(args, "--files");
@@ -2230,7 +2330,6 @@ async function runVerifyRecommendCommand(args, io) {
2230
2330
  io.stderr.write("Usage error: gdh verify recommend requires at least one --files <path> argument.\n");
2231
2331
  return 1;
2232
2332
  }
2233
- const targetPath = positionalArgs[0] ?? ".";
2234
2333
  const commandStartedAtMs = Date.now();
2235
2334
  try {
2236
2335
  const context = await buildAuthoringContext(targetPath);
@@ -2289,9 +2388,12 @@ async function runVerifyDoneCommand(args, io) {
2289
2388
  io.stderr.write(unsupportedOptionsError);
2290
2389
  return 1;
2291
2390
  }
2292
- const positionalArgs = extractPositionalArgs(args, new Set(["--files", "--performed"]));
2293
- if (positionalArgs.length > 1) {
2294
- 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);
2295
2397
  return 1;
2296
2398
  }
2297
2399
  const files = collectOptionValues(args, "--files");
@@ -2299,7 +2401,6 @@ async function runVerifyDoneCommand(args, io) {
2299
2401
  io.stderr.write("Usage error: gdh verify done requires at least one --files <path> argument.\n");
2300
2402
  return 1;
2301
2403
  }
2302
- const targetPath = positionalArgs[0] ?? ".";
2303
2404
  const commandStartedAtMs = Date.now();
2304
2405
  try {
2305
2406
  const context = await buildAuthoringContext(targetPath);
@@ -2450,12 +2551,14 @@ async function runVerifyInspectCommand(args, io) {
2450
2551
  io.stderr.write(unsupportedOptionsError);
2451
2552
  return 1;
2452
2553
  }
2453
- const positionalArgs = extractPositionalArgs(args, new Set(["--run-record", "--bundle"]));
2454
- if (positionalArgs.length > 1) {
2455
- 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);
2456
2560
  return 1;
2457
2561
  }
2458
- const targetPath = positionalArgs[0] ?? ".";
2459
2562
  const commandStartedAtMs = Date.now();
2460
2563
  try {
2461
2564
  const result = await inspectRuntimeVerificationBundleState({
@@ -2502,12 +2605,13 @@ async function runVerifyReadinessCommand(args, io) {
2502
2605
  io.stderr.write(unsupportedOptionsError);
2503
2606
  return 1;
2504
2607
  }
2505
- const positionalArgs = extractPositionalArgs(args, new Set());
2506
- if (positionalArgs.length > 1) {
2507
- 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);
2508
2613
  return 1;
2509
2614
  }
2510
- const targetPath = positionalArgs[0] ?? ".";
2511
2615
  const commandStartedAtMs = Date.now();
2512
2616
  try {
2513
2617
  const projectConfig = await readProjectConfig(targetPath);
@@ -2542,7 +2646,6 @@ async function runVerifyReadinessCommand(args, io) {
2542
2646
  }
2543
2647
  }
2544
2648
  const VERIFY_DRIFT_SCANNED_FILES = [
2545
- { relativePath: MCP_LAUNCHER_RELATIVE_PATH, description: "managed MCP launcher" },
2546
2649
  { relativePath: CODEX_ONBOARD_SKILL_RELATIVE_PATH, description: "Codex gdh-onboard skill" },
2547
2650
  { relativePath: CODEX_STATUS_SKILL_RELATIVE_PATH, description: "Codex gdh-status skill" },
2548
2651
  { relativePath: CODEX_MIGRATE_SKILL_RELATIVE_PATH, description: "Codex gdh-migrate skill" },
@@ -2611,8 +2714,8 @@ async function runVerifyDriftCommand(args, io) {
2611
2714
  "Usage: gdh verify drift [target]",
2612
2715
  "",
2613
2716
  "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",
2717
+ "surfaces (regenerated Codex/Cursor/Claude skill + command files) against",
2718
+ ".gdh/project.yaml gdh_version. Exits 1 and",
2616
2719
  "emits a structured JSON result when ANY file's baked version diverges from",
2617
2720
  "the pinned value. Covers ROADMAP Success Criterion #3 (one source of truth).",
2618
2721
  "",
@@ -2626,12 +2729,13 @@ async function runVerifyDriftCommand(args, io) {
2626
2729
  io.stderr.write(unsupportedOptionsError);
2627
2730
  return 1;
2628
2731
  }
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");
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);
2632
2737
  return 1;
2633
2738
  }
2634
- const targetPath = positionalArgs[0] ?? ".";
2635
2739
  const commandStartedAtMs = Date.now();
2636
2740
  let pinnedVersion;
2637
2741
  try {
@@ -2902,12 +3006,14 @@ async function runCacheInspectCommand(args, io) {
2902
3006
  io.stderr.write(unsupportedOptionsError);
2903
3007
  return 1;
2904
3008
  }
2905
- const positionalArgs = extractGuidanceResolvePositionalArgs(args);
2906
- if (positionalArgs.length > 1) {
2907
- 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);
2908
3015
  return 1;
2909
3016
  }
2910
- const targetPath = positionalArgs[0] ?? ".";
2911
3017
  try {
2912
3018
  const context = await buildAuthoringContext(targetPath);
2913
3019
  const result = await inspectCacheState({
@@ -2939,12 +3045,13 @@ async function runGuidanceStatusCommand(args, io) {
2939
3045
  io.stderr.write(unsupportedOptionsError);
2940
3046
  return 1;
2941
3047
  }
2942
- const positionalArgs = args.filter((arg) => !arg.startsWith("--"));
2943
- if (positionalArgs.length > 1) {
2944
- 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);
2945
3053
  return 1;
2946
3054
  }
2947
- const targetPath = positionalArgs[0] ?? ".";
2948
3055
  const commandStartedAtMs = Date.now();
2949
3056
  try {
2950
3057
  const context = await buildAuthoringContext(targetPath);
@@ -3006,12 +3113,14 @@ async function runGuidanceResolveCommand(args, io) {
3006
3113
  io.stderr.write(unsupportedOptionsError);
3007
3114
  return 1;
3008
3115
  }
3009
- const positionalArgs = extractGuidanceResolvePositionalArgs(args);
3010
- if (positionalArgs.length > 1) {
3011
- 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);
3012
3122
  return 1;
3013
3123
  }
3014
- const targetPath = positionalArgs[0] ?? ".";
3015
3124
  const commandStartedAtMs = Date.now();
3016
3125
  try {
3017
3126
  const context = await buildAuthoringContext(targetPath);
@@ -3065,12 +3174,13 @@ async function runGuidanceAuditCommand(args, io) {
3065
3174
  io.stderr.write(unsupportedOptionsError);
3066
3175
  return 1;
3067
3176
  }
3068
- const positionalArgs = args.filter((arg) => !arg.startsWith("--"));
3069
- if (positionalArgs.length > 1) {
3070
- 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);
3071
3182
  return 1;
3072
3183
  }
3073
- const targetPath = positionalArgs[0] ?? ".";
3074
3184
  try {
3075
3185
  const result = await inspectGuidanceAudit(targetPath);
3076
3186
  await recordSessionEvent(targetPath, {
@@ -3112,12 +3222,14 @@ async function runObservabilitySessionCommand(args, io) {
3112
3222
  io.stderr.write(unsupportedOptionsError);
3113
3223
  return 1;
3114
3224
  }
3115
- const positionalArgs = extractPositionalArgs(args, new Set(["--session-id"]));
3116
- if (positionalArgs.length > 1) {
3117
- 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);
3118
3231
  return 1;
3119
3232
  }
3120
- const targetPath = positionalArgs[0] ?? ".";
3121
3233
  const sessionId = readSingleOptionValue(args, "--session-id");
3122
3234
  try {
3123
3235
  const result = await inspectAuthoringSessions(targetPath, {
@@ -3150,12 +3262,14 @@ async function runObservabilityEffectivenessCommand(args, io) {
3150
3262
  io.stderr.write(unsupportedOptionsError);
3151
3263
  return 1;
3152
3264
  }
3153
- const positionalArgs = extractPositionalArgs(args, new Set(["--session-id"]));
3154
- if (positionalArgs.length > 1) {
3155
- 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);
3156
3271
  return 1;
3157
3272
  }
3158
- const targetPath = positionalArgs[0] ?? ".";
3159
3273
  const sessionId = readSingleOptionValue(args, "--session-id");
3160
3274
  try {
3161
3275
  const result = await inspectAuthoringEffectiveness(targetPath, {
@@ -3190,17 +3304,19 @@ async function runCachePruneCommand(args, io) {
3190
3304
  return 1;
3191
3305
  }
3192
3306
  const dryRun = args.includes("--dry-run");
3193
- 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
+ }
3194
3315
  const scope = resolveCachePruneScope(args);
3195
3316
  if (scope === null) {
3196
3317
  io.stderr.write("Usage error: --scope must be one of worktree, project, or all.\n");
3197
3318
  return 1;
3198
3319
  }
3199
- if (positionalArgs.length > 1) {
3200
- io.stderr.write("Usage error: gdh cache prune accepts at most one positional target path.\n");
3201
- return 1;
3202
- }
3203
- const targetPath = positionalArgs[0] ?? ".";
3204
3320
  try {
3205
3321
  const context = await buildAuthoringContext(targetPath);
3206
3322
  const result = await pruneCacheState({
@@ -3807,24 +3923,6 @@ function resolveCachePruneScope(args) {
3807
3923
  }
3808
3924
  return null;
3809
3925
  }
3810
- function extractCachePrunePositionalArgs(args) {
3811
- const positionalArgs = [];
3812
- for (let index = 0; index < args.length; index += 1) {
3813
- const arg = args[index];
3814
- if (!arg) {
3815
- continue;
3816
- }
3817
- if (arg === "--scope") {
3818
- index += 1;
3819
- continue;
3820
- }
3821
- if (arg.startsWith("--")) {
3822
- continue;
3823
- }
3824
- positionalArgs.push(arg);
3825
- }
3826
- return positionalArgs;
3827
- }
3828
3926
  function collectOptionValues(args, optionName) {
3829
3927
  const values = [];
3830
3928
  for (let index = 0; index < args.length; index += 1) {
@@ -3873,7 +3971,14 @@ export function findUnsupportedOptionsError(args, options) {
3873
3971
  }
3874
3972
  return `Usage error: unsupported option(s): ${unknownOptions.join(", ")}.\n${options.usage}`;
3875
3973
  }
3876
- 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) {
3877
3982
  const allowTargetOption = options.allowTargetOption ?? false;
3878
3983
  const unsupportedOptionsError = findUnsupportedOptionsError(args, {
3879
3984
  usage: options.usage,
@@ -3881,7 +3986,7 @@ function parseSingleTargetPathArg(args, options) {
3881
3986
  });
3882
3987
  if (unsupportedOptionsError !== null) {
3883
3988
  return {
3884
- targetPath: ".",
3989
+ targetPath: path.resolve("."),
3885
3990
  error: unsupportedOptionsError,
3886
3991
  };
3887
3992
  }
@@ -3889,24 +3994,51 @@ function parseSingleTargetPathArg(args, options) {
3889
3994
  const positionalArgs = extractPositionalArgs(args, new Set(allowTargetOption ? ["--target"] : []));
3890
3995
  if (allowTargetOption && args.includes("--target") && targetOptionValue === null) {
3891
3996
  return {
3892
- targetPath: ".",
3997
+ targetPath: path.resolve("."),
3893
3998
  error: `Usage error: --target requires a path value.\n${options.usage}`,
3894
3999
  };
3895
4000
  }
3896
4001
  if (positionalArgs.length > 1) {
3897
4002
  return {
3898
- targetPath: ".",
4003
+ targetPath: path.resolve("."),
3899
4004
  error: `Usage error: command accepts at most one target path.\n${options.usage}`,
3900
4005
  };
3901
4006
  }
3902
4007
  if (allowTargetOption && targetOptionValue !== null && positionalArgs.length > 0) {
3903
4008
  return {
3904
- targetPath: ".",
4009
+ targetPath: path.resolve("."),
3905
4010
  error: `Usage error: choose either a positional target path or --target <path>.\n${options.usage}`,
3906
4011
  };
3907
4012
  }
3908
4013
  return {
3909
- 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] ?? "."),
3910
4042
  error: null,
3911
4043
  };
3912
4044
  }
@@ -3993,9 +4125,6 @@ function collectPerformedValidationKinds(args) {
3993
4125
  }
3994
4126
  });
3995
4127
  }
3996
- function extractGuidanceResolvePositionalArgs(args) {
3997
- return extractPositionalArgs(args, new Set(["--task-type", "--capability", "--repo-state", "--keyword"]));
3998
- }
3999
4128
  function extractRecipePositionalArgs(args) {
4000
4129
  return extractPositionalArgs(args, new Set(["--provider", "--param", "--feature", "--no-feature", "--env"]));
4001
4130
  }