gsd-pi 2.60.0-dev.d9052f5 → 2.61.0-dev.7aed0bf

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 (198) hide show
  1. package/dist/resources/extensions/ask-user-questions.js +7 -4
  2. package/dist/resources/extensions/gsd/auto/phases.js +15 -7
  3. package/dist/resources/extensions/gsd/auto-dashboard.js +21 -8
  4. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -3
  5. package/dist/resources/extensions/gsd/auto-model-selection.js +58 -9
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +3 -2
  7. package/dist/resources/extensions/gsd/auto-prompts.js +36 -20
  8. package/dist/resources/extensions/gsd/auto-recovery.js +37 -18
  9. package/dist/resources/extensions/gsd/auto-start.js +9 -5
  10. package/dist/resources/extensions/gsd/auto-timers.js +11 -5
  11. package/dist/resources/extensions/gsd/auto-unit-closeout.js +5 -3
  12. package/dist/resources/extensions/gsd/auto-verification.js +3 -2
  13. package/dist/resources/extensions/gsd/auto-worktree.js +120 -55
  14. package/dist/resources/extensions/gsd/auto.js +39 -17
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +6 -3
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
  17. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +4 -10
  18. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +2 -1
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
  20. package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
  21. package/dist/resources/extensions/gsd/commands/catalog.js +2 -0
  22. package/dist/resources/extensions/gsd/commands-codebase.js +48 -21
  23. package/dist/resources/extensions/gsd/commands-inspect.js +2 -1
  24. package/dist/resources/extensions/gsd/commands-maintenance.js +32 -19
  25. package/dist/resources/extensions/gsd/complexity-classifier.js +8 -4
  26. package/dist/resources/extensions/gsd/custom-verification.js +3 -2
  27. package/dist/resources/extensions/gsd/gsd-db.js +33 -13
  28. package/dist/resources/extensions/gsd/guided-flow.js +19 -9
  29. package/dist/resources/extensions/gsd/init-wizard.js +12 -0
  30. package/dist/resources/extensions/gsd/markdown-renderer.js +11 -9
  31. package/dist/resources/extensions/gsd/md-importer.js +5 -4
  32. package/dist/resources/extensions/gsd/milestone-actions.js +3 -2
  33. package/dist/resources/extensions/gsd/milestone-ids.js +2 -1
  34. package/dist/resources/extensions/gsd/model-router.js +156 -121
  35. package/dist/resources/extensions/gsd/parallel-merge.js +5 -3
  36. package/dist/resources/extensions/gsd/parallel-orchestrator.js +26 -14
  37. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  38. package/dist/resources/extensions/gsd/preferences-validation.js +45 -0
  39. package/dist/resources/extensions/gsd/preferences.js +15 -3
  40. package/dist/resources/extensions/gsd/prompt-loader.js +3 -2
  41. package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
  42. package/dist/resources/extensions/gsd/rule-registry.js +7 -6
  43. package/dist/resources/extensions/gsd/safe-fs.js +6 -8
  44. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  45. package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -2
  46. package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
  47. package/dist/resources/extensions/gsd/tools/plan-milestone.js +3 -2
  48. package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -2
  49. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -1
  50. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
  51. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -1
  52. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -1
  53. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -1
  54. package/dist/resources/extensions/gsd/tools/validate-milestone.js +2 -1
  55. package/dist/resources/extensions/gsd/triage-resolution.js +11 -4
  56. package/dist/resources/extensions/gsd/workflow-events.js +2 -1
  57. package/dist/resources/extensions/gsd/workflow-logger.js +37 -4
  58. package/dist/resources/extensions/gsd/workflow-migration.js +14 -12
  59. package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
  60. package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
  61. package/dist/resources/extensions/gsd/worktree-manager.js +26 -14
  62. package/dist/resources/extensions/shared/interview-ui.js +3 -1
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  65. package/dist/web/standalone/.next/build-manifest.json +2 -2
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  68. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.html +1 -1
  84. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  91. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  92. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  93. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  94. package/package.json +1 -1
  95. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
  97. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
  99. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  100. package/packages/pi-coding-agent/dist/core/extensions/runner.js +16 -0
  101. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +26 -0
  103. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  106. package/packages/pi-coding-agent/dist/core/lsp/config.js +6 -1
  107. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
  109. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +2 -0
  110. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +1 -0
  111. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +47 -0
  112. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +1 -0
  113. package/packages/pi-coding-agent/package.json +1 -1
  114. package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
  115. package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -0
  116. package/packages/pi-coding-agent/src/core/extensions/types.ts +26 -0
  117. package/packages/pi-coding-agent/src/core/lsp/config.ts +7 -1
  118. package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
  119. package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +70 -0
  120. package/pkg/package.json +1 -1
  121. package/src/resources/extensions/ask-user-questions.ts +7 -3
  122. package/src/resources/extensions/gsd/auto/phases.ts +17 -7
  123. package/src/resources/extensions/gsd/auto-dashboard.ts +22 -8
  124. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -3
  125. package/src/resources/extensions/gsd/auto-model-selection.ts +77 -15
  126. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
  127. package/src/resources/extensions/gsd/auto-prompts.ts +37 -20
  128. package/src/resources/extensions/gsd/auto-recovery.ts +38 -18
  129. package/src/resources/extensions/gsd/auto-start.ts +10 -9
  130. package/src/resources/extensions/gsd/auto-timers.ts +12 -5
  131. package/src/resources/extensions/gsd/auto-unit-closeout.ts +6 -2
  132. package/src/resources/extensions/gsd/auto-verification.ts +3 -6
  133. package/src/resources/extensions/gsd/auto-worktree.ts +121 -55
  134. package/src/resources/extensions/gsd/auto.ts +40 -17
  135. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -3
  136. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
  137. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +4 -16
  138. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +2 -1
  139. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
  140. package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
  141. package/src/resources/extensions/gsd/commands/catalog.ts +2 -0
  142. package/src/resources/extensions/gsd/commands-codebase.ts +52 -20
  143. package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
  144. package/src/resources/extensions/gsd/commands-maintenance.ts +28 -19
  145. package/src/resources/extensions/gsd/complexity-classifier.ts +9 -4
  146. package/src/resources/extensions/gsd/custom-verification.ts +3 -2
  147. package/src/resources/extensions/gsd/gsd-db.ts +12 -14
  148. package/src/resources/extensions/gsd/guided-flow.ts +9 -8
  149. package/src/resources/extensions/gsd/init-wizard.ts +12 -0
  150. package/src/resources/extensions/gsd/markdown-renderer.ts +11 -17
  151. package/src/resources/extensions/gsd/md-importer.ts +5 -4
  152. package/src/resources/extensions/gsd/milestone-actions.ts +3 -2
  153. package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
  154. package/src/resources/extensions/gsd/model-router.ts +199 -173
  155. package/src/resources/extensions/gsd/parallel-merge.ts +5 -3
  156. package/src/resources/extensions/gsd/parallel-orchestrator.ts +18 -14
  157. package/src/resources/extensions/gsd/preferences-types.ts +13 -0
  158. package/src/resources/extensions/gsd/preferences-validation.ts +45 -0
  159. package/src/resources/extensions/gsd/preferences.ts +16 -3
  160. package/src/resources/extensions/gsd/prompt-loader.ts +3 -2
  161. package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
  162. package/src/resources/extensions/gsd/rule-registry.ts +7 -6
  163. package/src/resources/extensions/gsd/safe-fs.ts +6 -5
  164. package/src/resources/extensions/gsd/tests/capability-router.test.ts +347 -0
  165. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +63 -0
  166. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +27 -2
  167. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  168. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1188 -0
  169. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +841 -0
  170. package/src/resources/extensions/gsd/tests/model-router.test.ts +403 -3
  171. package/src/resources/extensions/gsd/tests/preferences.test.ts +62 -0
  172. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +21 -0
  173. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +284 -0
  174. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +120 -0
  175. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
  176. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -6
  177. package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -6
  178. package/src/resources/extensions/gsd/tools/complete-task.ts +3 -6
  179. package/src/resources/extensions/gsd/tools/plan-milestone.ts +3 -6
  180. package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -6
  181. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -3
  182. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +4 -6
  183. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -3
  184. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -3
  185. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -3
  186. package/src/resources/extensions/gsd/tools/validate-milestone.ts +2 -3
  187. package/src/resources/extensions/gsd/triage-resolution.ts +11 -4
  188. package/src/resources/extensions/gsd/types.ts +1 -0
  189. package/src/resources/extensions/gsd/workflow-events.ts +2 -1
  190. package/src/resources/extensions/gsd/workflow-logger.ts +52 -5
  191. package/src/resources/extensions/gsd/workflow-migration.ts +14 -12
  192. package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
  193. package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
  194. package/src/resources/extensions/gsd/worktree-manager.ts +16 -14
  195. package/src/resources/extensions/shared/interview-ui.ts +3 -1
  196. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +144 -0
  197. /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → b7FOoMHaUb3FPoLNbxar4}/_buildManifest.js +0 -0
  198. /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → b7FOoMHaUb3FPoLNbxar4}/_ssgManifest.js +0 -0
@@ -115,7 +115,7 @@ import {
115
115
  formatCost,
116
116
  formatTokenCount,
117
117
  } from "./metrics.js";
118
- import { setLogBasePath } from "./workflow-logger.js";
118
+ import { setLogBasePath, logWarning, logError } from "./workflow-logger.js";
119
119
  import { join } from "node:path";
120
120
  import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
121
121
  import { atomicWriteSync } from "./atomic-write.js";
@@ -316,8 +316,9 @@ export function getAutoDashboardData(): AutoDashboardData {
316
316
  if (s.basePath) {
317
317
  pendingCaptureCount = countPendingCaptures(s.basePath);
318
318
  }
319
- } catch {
319
+ } catch (err) {
320
320
  // Non-fatal — captures module may not be loaded
321
+ logWarning("engine", `capture count failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
321
322
  }
322
323
  return {
323
324
  active: s.active,
@@ -565,8 +566,9 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
565
566
  try {
566
567
  if (lockBase()) clearLock(lockBase());
567
568
  if (lockBase()) releaseSessionLock(lockBase());
568
- } catch {
569
+ } catch (err) {
569
570
  /* best-effort — mirror stopAuto cleanup */
571
+ logWarning("session", `lock cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
570
572
  }
571
573
 
572
574
  ctx.ui.setStatus("gsd-auto", undefined);
@@ -578,8 +580,9 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
578
580
  s.basePath = s.originalBasePath;
579
581
  try {
580
582
  process.chdir(s.basePath);
581
- } catch {
583
+ } catch (err) {
582
584
  /* best-effort */
585
+ logWarning("engine", `chdir failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
583
586
  }
584
587
  }
585
588
  }
@@ -651,8 +654,9 @@ export async function stopAuto(
651
654
  } else {
652
655
  milestoneComplete = true;
653
656
  }
654
- } catch {
657
+ } catch (err) {
655
658
  // Non-fatal — fall through to preserveBranch path
659
+ logWarning("engine", `milestone summary check failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
656
660
  }
657
661
 
658
662
  if (milestoneComplete) {
@@ -687,8 +691,9 @@ export async function stopAuto(
687
691
  s.basePath = s.originalBasePath;
688
692
  try {
689
693
  process.chdir(s.basePath);
690
- } catch {
694
+ } catch (err) {
691
695
  /* best-effort */
696
+ logWarning("engine", `chdir failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
692
697
  }
693
698
  }
694
699
  } catch (e) {
@@ -760,7 +765,9 @@ export async function stopAuto(
760
765
  try {
761
766
  const pausedPath = join(gsdRoot(s.originalBasePath || s.basePath), "runtime", "paused-session.json");
762
767
  if (existsSync(pausedPath)) unlinkSync(pausedPath);
763
- } catch { /* non-fatal */ }
768
+ } catch (err) { /* non-fatal */
769
+ logWarning("engine", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
770
+ }
764
771
 
765
772
  // ── Step 13: Restore original model (before reset clears IDs) ──
766
773
  try {
@@ -794,7 +801,9 @@ export async function stopAuto(
794
801
  const { closeBrowser } = await import("../browser-tools/lifecycle.js");
795
802
  await closeBrowser();
796
803
  }
797
- } catch { /* non-fatal: browser-tools may not be loaded */ }
804
+ } catch (err) { /* non-fatal: browser-tools may not be loaded */
805
+ logWarning("engine", `browser teardown failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
806
+ }
798
807
 
799
808
  // External cleanup (not covered by session reset)
800
809
  clearInFlightTools();
@@ -852,16 +861,18 @@ export async function pauseAuto(
852
861
  JSON.stringify(pausedMeta, null, 2),
853
862
  "utf-8",
854
863
  );
855
- } catch {
864
+ } catch (err) {
856
865
  // Non-fatal — resume will still work via full bootstrap, just without worktree context
866
+ logWarning("engine", `paused-session file write failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
857
867
  }
858
868
 
859
869
  // Close out the current unit so its runtime record doesn't stay at "dispatched"
860
870
  if (s.currentUnit && ctx) {
861
871
  try {
862
872
  await closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt);
863
- } catch {
873
+ } catch (err) {
864
874
  // Non-fatal — best-effort closeout on pause
875
+ logWarning("engine", `unit closeout on pause failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
865
876
  }
866
877
  s.currentUnit = null;
867
878
  }
@@ -1085,7 +1096,9 @@ export async function startAuto(
1085
1096
  s.originalBasePath = meta.originalBasePath || base;
1086
1097
  s.stepMode = meta.stepMode ?? requestedStepMode;
1087
1098
  s.paused = true;
1088
- try { unlinkSync(pausedPath); } catch { /* non-fatal */ }
1099
+ try { unlinkSync(pausedPath); } catch (err) { /* non-fatal */
1100
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1101
+ }
1089
1102
  ctx.ui.notify(
1090
1103
  `Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`,
1091
1104
  "info",
@@ -1096,7 +1109,9 @@ export async function startAuto(
1096
1109
  const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
1097
1110
  if (!mDir || summaryFile) {
1098
1111
  // Stale milestone — clean up and fall through to fresh bootstrap
1099
- try { unlinkSync(pausedPath); } catch { /* non-fatal */ }
1112
+ try { unlinkSync(pausedPath); } catch (err) { /* non-fatal */
1113
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1114
+ }
1100
1115
  ctx.ui.notify(
1101
1116
  `Paused milestone ${meta.milestoneId} is ${!mDir ? "missing" : "already complete"}. Starting fresh.`,
1102
1117
  "info",
@@ -1107,7 +1122,9 @@ export async function startAuto(
1107
1122
  s.stepMode = meta.stepMode ?? requestedStepMode;
1108
1123
  s.paused = true;
1109
1124
  // Clean up the persisted file — we're consuming it
1110
- try { unlinkSync(pausedPath); } catch { /* non-fatal */ }
1125
+ try { unlinkSync(pausedPath); } catch (err) { /* non-fatal */
1126
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1127
+ }
1111
1128
  ctx.ui.notify(
1112
1129
  `Resuming paused session for ${meta.milestoneId}${meta.worktreePath ? ` (worktree)` : ""}.`,
1113
1130
  "info",
@@ -1115,8 +1132,9 @@ export async function startAuto(
1115
1132
  }
1116
1133
  }
1117
1134
  }
1118
- } catch {
1135
+ } catch (err) {
1119
1136
  // Malformed or missing — proceed with fresh bootstrap
1137
+ logWarning("session", `paused-session restore failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1120
1138
  }
1121
1139
  }
1122
1140
 
@@ -1242,8 +1260,9 @@ export async function startAuto(
1242
1260
 
1243
1261
  try {
1244
1262
  syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
1245
- } catch {
1263
+ } catch (err) {
1246
1264
  // Best-effort only — sidebar sync must never block auto-mode startup
1265
+ logWarning("engine", `cmux sync failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1247
1266
  }
1248
1267
  logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
1249
1268
 
@@ -1415,8 +1434,9 @@ export async function dispatchHookUnit(
1415
1434
  if (match) {
1416
1435
  try {
1417
1436
  await pi.setModel(match);
1418
- } catch {
1437
+ } catch (err) {
1419
1438
  /* non-fatal */
1439
+ logWarning("dispatch", `hook model set failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1420
1440
  }
1421
1441
  } else {
1422
1442
  ctx.ui.notify(
@@ -1453,7 +1473,9 @@ export async function dispatchHookUnit(
1453
1473
  ctx.ui.notify(`Running post-unit hook: ${hookName}`, "info");
1454
1474
 
1455
1475
  // Ensure cwd matches basePath before hook dispatch (#1389)
1456
- try { if (process.cwd() !== s.basePath) process.chdir(s.basePath); } catch {}
1476
+ try { if (process.cwd() !== s.basePath) process.chdir(s.basePath); } catch (err) {
1477
+ logWarning("engine", `chdir failed before hook dispatch: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1478
+ }
1457
1479
 
1458
1480
  debugLog("dispatchHookUnit", {
1459
1481
  phase: "send-message",
@@ -1475,3 +1497,4 @@ export {
1475
1497
  buildLoopRemediationSteps,
1476
1498
  } from "./auto-recovery.js";
1477
1499
  export { resolveExpectedArtifactPath } from "./auto-artifact-paths.js";
1500
+
@@ -1,5 +1,6 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
2
2
 
3
+ import { logWarning } from "../workflow-logger.js";
3
4
  import { checkAutoStartAfterDiscuss } from "../guided-flow.js";
4
5
  import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto } from "../auto.js";
5
6
  import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
@@ -85,7 +86,7 @@ export async function handleAgentEnd(
85
86
  } catch (err) {
86
87
  const message = err instanceof Error ? err.message : String(err);
87
88
  ctx.ui.notify(`Auto-mode error after empty-content abort: ${message}. Stopping auto-mode.`, "error");
88
- try { await pauseAuto(ctx, pi); } catch { /* best-effort */ }
89
+ try { await pauseAuto(ctx, pi); } catch (e) { logWarning("bootstrap", `pauseAuto failed after empty-content abort: ${(e as Error).message}`); }
89
90
  }
90
91
  return;
91
92
  }
@@ -212,8 +213,8 @@ export async function handleAgentEnd(
212
213
  ctx.ui.notify(`Auto-mode error in agent_end handler: ${message}. Stopping auto-mode.`, "error");
213
214
  try {
214
215
  await pauseAuto(ctx, pi);
215
- } catch {
216
- // best-effort
216
+ } catch (e) {
217
+ logWarning("bootstrap", `pauseAuto failed in agent_end handler: ${(e as Error).message}`);
217
218
  }
218
219
  }
219
220
  }
@@ -413,8 +413,8 @@ export function registerDbTools(pi: ExtensionAPI): void {
413
413
  try {
414
414
  const { insertMilestone } = await import("../gsd-db.js");
415
415
  insertMilestone({ id: milestoneId, status: "queued" });
416
- } catch {
417
- // Non-fatal the safety-net in deriveStateFromDb will catch this
416
+ } catch (e) {
417
+ logError("tool", `insertMilestone failed for ${milestoneId}: ${(e as Error).message}`);
418
418
  }
419
419
  }
420
420
 
@@ -5,7 +5,7 @@ import type { ExtensionAPI } from "@gsd/pi-coding-agent";
5
5
  import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@gsd/pi-coding-agent";
6
6
 
7
7
  import { DEFAULT_BASH_TIMEOUT_SECS } from "../constants.js";
8
- import { setLogBasePath } from "../workflow-logger.js";
8
+ import { setLogBasePath, logWarning } from "../workflow-logger.js";
9
9
 
10
10
  /**
11
11
  * Resolve the correct DB path for the current working directory.
@@ -92,9 +92,7 @@ export async function ensureDbOpen(): Promise<boolean> {
92
92
  const { migrateFromMarkdown } = await import("../md-importer.js");
93
93
  migrateFromMarkdown(basePath);
94
94
  } catch (err) {
95
- process.stderr.write(
96
- `gsd-db: ensureDbOpen auto-migration failed: ${(err as Error).message}\n`,
97
- );
95
+ logWarning("bootstrap", `ensureDbOpen auto-migration failed: ${(err as Error).message}`);
98
96
  }
99
97
  }
100
98
  return opened;
@@ -106,20 +104,10 @@ export async function ensureDbOpen(): Promise<boolean> {
106
104
  return opened;
107
105
  }
108
106
 
109
- process.stderr.write(
110
- `gsd-db: ensureDbOpen failed — no .gsd directory found (resolvedPath=${resolveProjectRootDbPath(basePath)}, cwd=${basePath})\n`,
111
- );
107
+ logWarning("bootstrap", "ensureDbOpen failed — no .gsd directory found");
112
108
  return false;
113
109
  } catch (err) {
114
- const basePath = process.cwd();
115
- const diagnostic = {
116
- resolvedPath: resolveProjectRootDbPath(basePath),
117
- cwd: basePath,
118
- error: (err as Error).message ?? String(err),
119
- };
120
- process.stderr.write(
121
- `gsd-db: ensureDbOpen failed — ${JSON.stringify(diagnostic)}\n`,
122
- );
110
+ logWarning("bootstrap", `ensureDbOpen failed: ${(err as Error).message ?? String(err)}`);
123
111
  return false;
124
112
  }
125
113
  }
@@ -2,6 +2,7 @@ import { Type } from "@sinclair/typebox";
2
2
  import type { ExtensionAPI } from "@gsd/pi-coding-agent";
3
3
 
4
4
  import { queryJournal } from "../journal.js";
5
+ import { logWarning } from "../workflow-logger.js";
5
6
 
6
7
  export function registerJournalTools(pi: ExtensionAPI): void {
7
8
  pi.registerTool({
@@ -51,7 +52,7 @@ export function registerJournalTools(pi: ExtensionAPI): void {
51
52
  };
52
53
  } catch (err) {
53
54
  const msg = err instanceof Error ? err.message : String(err);
54
- process.stderr.write(`gsd-journal: gsd_journal_query tool failed: ${msg}\n`);
55
+ logWarning("tool", `gsd_journal_query tool failed: ${msg}`);
55
56
  return {
56
57
  content: [{ type: "text" as const, text: `Error querying journal: ${msg}` }],
57
58
  details: { operation: "journal_query", error: msg } as any,
@@ -322,4 +322,12 @@ export function registerHooks(pi: ExtensionAPI): void {
322
322
  payload.service_tier = tier;
323
323
  return payload;
324
324
  });
325
+
326
+ // Capability-aware model routing hook (ADR-004)
327
+ // Extensions can override model selection by returning { modelId: "..." }
328
+ // Return undefined to let the built-in capability scoring proceed.
329
+ pi.on("before_model_select", async (_event) => {
330
+ // Default: no override — let capability scoring handle selection
331
+ return undefined;
332
+ });
325
333
  }
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
 
5
5
  import type { ExtensionContext } from "@gsd/pi-coding-agent";
6
6
 
7
+ import { logWarning } from "../workflow-logger.js";
7
8
  import { debugTime } from "../debug-logger.js";
8
9
  import { loadPrompt } from "../prompt-loader.js";
9
10
  import { readForensicsMarker } from "../forensics.js";
@@ -83,8 +84,8 @@ export async function buildBeforeAgentStartResult(
83
84
  memoryBlock = `\n\n${formatted}`;
84
85
  }
85
86
  }
86
- } catch {
87
- // non-fatal
87
+ } catch (e) {
88
+ logWarning("bootstrap", `memory block fetch failed: ${(e as Error).message}`);
88
89
  }
89
90
 
90
91
  let newSkillsBlock = "";
@@ -111,8 +112,8 @@ export async function buildBeforeAgentStartResult(
111
112
  : rawContent;
112
113
  codebaseBlock = `\n\n[PROJECT CODEBASE — File structure and descriptions (generated ${generatedAt}, may be stale — run /gsd codebase update to refresh)]\n\n${content}`;
113
114
  }
114
- } catch {
115
- // skip
115
+ } catch (e) {
116
+ logWarning("bootstrap", `CODEBASE file read failed: ${(e as Error).message}`);
116
117
  }
117
118
  }
118
119
 
@@ -158,8 +159,8 @@ export function loadKnowledgeBlock(gsdHomeDir: string, cwd: string): { block: st
158
159
  globalSizeKb = Buffer.byteLength(content, "utf-8") / 1024;
159
160
  globalKnowledge = content;
160
161
  }
161
- } catch {
162
- // skip
162
+ } catch (e) {
163
+ logWarning("bootstrap", `global knowledge file read failed: ${(e as Error).message}`);
163
164
  }
164
165
  }
165
166
 
@@ -170,8 +171,8 @@ export function loadKnowledgeBlock(gsdHomeDir: string, cwd: string): { block: st
170
171
  try {
171
172
  const content = readFileSync(knowledgePath, "utf-8").trim();
172
173
  if (content) projectKnowledge = content;
173
- } catch {
174
- // skip
174
+ } catch (e) {
175
+ logWarning("bootstrap", `project knowledge file read failed: ${(e as Error).message}`);
175
176
  }
176
177
  }
177
178
 
@@ -429,8 +430,8 @@ export function clearForensicsMarker(basePath: string): void {
429
430
  if (existsSync(markerPath)) {
430
431
  try {
431
432
  unlinkSync(markerPath);
432
- } catch {
433
- // non-fatal
433
+ } catch (e) {
434
+ logWarning("bootstrap", `unlinkSync forensics marker failed: ${(e as Error).message}`);
434
435
  }
435
436
  }
436
437
  }
@@ -229,8 +229,10 @@ const NESTED_COMPLETIONS: CompletionMap = {
229
229
  codebase: [
230
230
  { cmd: "generate", desc: "Generate or regenerate CODEBASE.md" },
231
231
  { cmd: "generate --max-files", desc: "Generate with custom file limit (default: 500)" },
232
+ { cmd: "generate --collapse-threshold", desc: "Generate with custom collapse threshold (default: 20)" },
232
233
  { cmd: "update", desc: "Incremental update (preserves descriptions)" },
233
234
  { cmd: "update --max-files", desc: "Update with custom file limit" },
235
+ { cmd: "update --collapse-threshold", desc: "Update with custom collapse threshold" },
234
236
  { cmd: "stats", desc: "Show file count, description coverage, and generation time" },
235
237
  { cmd: "help", desc: "Show usage and available subcommands" },
236
238
  ],
@@ -14,14 +14,21 @@ import {
14
14
  getCodebaseMapStats,
15
15
  readCodebaseMap,
16
16
  } from "./codebase-generator.js";
17
+ import { loadEffectiveGSDPreferences } from "./preferences.js";
18
+ import type { CodebaseMapOptions } from "./codebase-generator.js";
17
19
 
18
20
  const USAGE =
19
21
  "Usage: /gsd codebase [generate|update|stats]\n\n" +
20
- " generate [--max-files N] — Generate or regenerate CODEBASE.md\n" +
21
- " update — Incremental update (preserves descriptions)\n" +
22
- " stats — Show file count, coverage, and generation time\n" +
23
- " help — Show this help\n\n" +
24
- "With no subcommand, shows stats if a map exists or help if not.";
22
+ " generate [--max-files N] [--collapse-threshold N] — Generate or regenerate CODEBASE.md\n" +
23
+ " update [--max-files N] [--collapse-threshold N] — Incremental update (preserves descriptions)\n" +
24
+ " stats — Show file count, coverage, and generation time\n" +
25
+ " help — Show this help\n\n" +
26
+ "With no subcommand, shows stats if a map exists or help if not.\n\n" +
27
+ "Configure defaults via preferences.md:\n" +
28
+ " codebase:\n" +
29
+ " exclude_patterns: [\"docs/\", \"fixtures/\"]\n" +
30
+ " max_files: 1000\n" +
31
+ " collapse_threshold: 15";
25
32
 
26
33
  export async function handleCodebase(
27
34
  args: string,
@@ -34,15 +41,15 @@ export async function handleCodebase(
34
41
 
35
42
  switch (sub) {
36
43
  case "generate": {
37
- const maxFiles = parseMaxFiles(args, ctx);
38
- if (maxFiles === false) return; // validation failed, message already shown
44
+ const options = resolveCodebaseOptions(args, ctx);
45
+ if (options === false) return; // validation failed, message already shown
39
46
 
40
47
  const existing = readCodebaseMap(basePath);
41
48
  const existingDescriptions = existing
42
49
  ? (await import("./codebase-generator.js")).parseCodebaseMap(existing)
43
50
  : undefined;
44
51
 
45
- const result = generateCodebaseMap(basePath, { maxFiles: maxFiles ?? undefined }, existingDescriptions);
52
+ const result = generateCodebaseMap(basePath, options, existingDescriptions);
46
53
 
47
54
  if (result.fileCount === 0) {
48
55
  ctx.ui.notify(
@@ -73,10 +80,10 @@ export async function handleCodebase(
73
80
  return;
74
81
  }
75
82
 
76
- const maxFiles = parseMaxFiles(args, ctx);
77
- if (maxFiles === false) return;
83
+ const options = resolveCodebaseOptions(args, ctx);
84
+ if (options === false) return;
78
85
 
79
- const result = updateCodebaseMap(basePath, { maxFiles: maxFiles ?? undefined });
86
+ const result = updateCodebaseMap(basePath, options);
80
87
  writeCodebaseMap(basePath, result.content);
81
88
 
82
89
  ctx.ui.notify(
@@ -141,19 +148,44 @@ function showStats(basePath: string, ctx: ExtensionCommandContext): void {
141
148
  }
142
149
 
143
150
  /**
144
- * Parse and validate --max-files flag.
145
- * Returns the parsed number, undefined if flag not present, or false if invalid.
151
+ * Resolve codebase map options by merging preferences with CLI flags.
152
+ * CLI flags override preferences; preferences override built-in defaults.
153
+ * Returns false if validation failed (error already shown to user).
146
154
  */
147
- function parseMaxFiles(args: string, ctx: ExtensionCommandContext): number | undefined | false {
155
+ function resolveCodebaseOptions(args: string, ctx: ExtensionCommandContext): CodebaseMapOptions | false {
156
+ // Load preferences defaults
157
+ const prefs = loadEffectiveGSDPreferences()?.preferences?.codebase;
158
+
159
+ // Parse CLI flags
148
160
  const maxFilesStr = extractFlag(args, "--max-files");
149
- if (!maxFilesStr) return undefined;
161
+ const collapseStr = extractFlag(args, "--collapse-threshold");
162
+
163
+ // Validate --max-files
164
+ let maxFiles: number | undefined;
165
+ if (maxFilesStr) {
166
+ maxFiles = parseInt(maxFilesStr, 10);
167
+ if (isNaN(maxFiles) || maxFiles < 1) {
168
+ ctx.ui.notify("--max-files must be a positive integer (e.g. --max-files 200).", "warning");
169
+ return false;
170
+ }
171
+ }
150
172
 
151
- const maxFiles = parseInt(maxFilesStr, 10);
152
- if (isNaN(maxFiles) || maxFiles < 1) {
153
- ctx.ui.notify("--max-files must be a positive integer (e.g. --max-files 200).", "warning");
154
- return false;
173
+ // Validate --collapse-threshold
174
+ let collapseThreshold: number | undefined;
175
+ if (collapseStr) {
176
+ collapseThreshold = parseInt(collapseStr, 10);
177
+ if (isNaN(collapseThreshold) || collapseThreshold < 1) {
178
+ ctx.ui.notify("--collapse-threshold must be a positive integer (e.g. --collapse-threshold 15).", "warning");
179
+ return false;
180
+ }
155
181
  }
156
- return maxFiles;
182
+
183
+ return {
184
+ // CLI flags override preferences
185
+ maxFiles: maxFiles ?? prefs?.max_files,
186
+ collapseThreshold: collapseThreshold ?? prefs?.collapse_threshold,
187
+ excludePatterns: prefs?.exclude_patterns,
188
+ };
157
189
  }
158
190
 
159
191
  function extractFlag(args: string, flag: string): string | undefined {
@@ -8,6 +8,7 @@ import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
8
8
  import { existsSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
  import { gsdRoot } from "./paths.js";
11
+ import { logWarning } from "./workflow-logger.js";
11
12
  import { getErrorMessage } from "./error-utils.js";
12
13
 
13
14
  export interface InspectData {
@@ -92,7 +93,7 @@ export async function handleInspect(ctx: ExtensionCommandContext): Promise<void>
92
93
 
93
94
  ctx.ui.notify(formatInspectOutput(data), "info");
94
95
  } catch (err) {
95
- process.stderr.write(`gsd-db: /gsd inspect failed: ${getErrorMessage(err)}\n`);
96
+ logWarning("command", `/gsd inspect failed: ${getErrorMessage(err)}`);
96
97
  ctx.ui.notify("Failed to inspect GSD database. Check stderr for details.", "error");
97
98
  }
98
99
  }
@@ -7,12 +7,14 @@
7
7
  import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
8
8
  import { deriveState } from "./state.js";
9
9
  import { nativeBranchList, nativeDetectMainBranch, nativeBranchListMerged, nativeBranchDelete, nativeForEachRef, nativeUpdateRef } from "./native-git-bridge.js";
10
+ import { logWarning } from "./workflow-logger.js";
10
11
 
11
12
  export async function handleCleanupBranches(ctx: ExtensionCommandContext, basePath: string): Promise<void> {
12
13
  let branches: string[];
13
14
  try {
14
15
  branches = nativeBranchList(basePath, "gsd/*");
15
- } catch {
16
+ } catch (e) {
17
+ logWarning("command", `branch list failed: ${(e as Error).message}`);
16
18
  ctx.ui.notify("No GSD branches to clean up.", "info");
17
19
  return;
18
20
  }
@@ -23,7 +25,8 @@ export async function handleCleanupBranches(ctx: ExtensionCommandContext, basePa
23
25
  let merged: string[];
24
26
  try {
25
27
  merged = nativeBranchListMerged(basePath, mainBranch, "gsd/*");
26
- } catch {
28
+ } catch (e) {
29
+ logWarning("command", `merged branch list failed: ${(e as Error).message}`);
27
30
  merged = [];
28
31
  }
29
32
 
@@ -33,8 +36,8 @@ export async function handleCleanupBranches(ctx: ExtensionCommandContext, basePa
33
36
  try {
34
37
  nativeBranchDelete(basePath, branch, false);
35
38
  deletedMerged++;
36
- } catch {
37
- /* skip branches that cannot be deleted */
39
+ } catch (e) {
40
+ logWarning("command", `branch delete failed for ${branch}: ${(e as Error).message}`);
38
41
  }
39
42
  }
40
43
 
@@ -66,7 +69,7 @@ export async function handleCleanupBranches(ctx: ExtensionCommandContext, basePa
66
69
  try {
67
70
  nativeBranchDelete(basePath, branch, true);
68
71
  deletedStaleMilestones++;
69
- } catch { /* non-fatal */ }
72
+ } catch (e) { logWarning("command", `stale milestone branch delete failed for ${branch}: ${(e as Error).message}`); }
70
73
  continue;
71
74
  }
72
75
  }
@@ -77,7 +80,8 @@ export async function handleCleanupBranches(ctx: ExtensionCommandContext, basePa
77
80
  let roadmapContent: string | null = null;
78
81
  try {
79
82
  roadmapContent = await loadFile(roadmapPath);
80
- } catch {
83
+ } catch (e) {
84
+ logWarning("command", `loadFile failed for ${roadmapPath}: ${(e as Error).message}`);
81
85
  roadmapContent = null;
82
86
  }
83
87
  if (!roadmapContent) continue;
@@ -85,12 +89,12 @@ export async function handleCleanupBranches(ctx: ExtensionCommandContext, basePa
85
89
  try {
86
90
  nativeBranchDelete(basePath, branch, true);
87
91
  deletedStaleMilestones++;
88
- } catch {
89
- /* non-fatal */
92
+ } catch (e) {
93
+ logWarning("command", `milestone branch delete failed for ${branch}: ${(e as Error).message}`);
90
94
  }
91
95
  }
92
- } catch {
93
- /* non-fatal */
96
+ } catch (e) {
97
+ logWarning("command", `stale milestone cleanup failed: ${(e as Error).message}`);
94
98
  }
95
99
 
96
100
  const summary: string[] = [];
@@ -122,7 +126,8 @@ export async function handleCleanupSnapshots(ctx: ExtensionCommandContext, baseP
122
126
  let refs: string[];
123
127
  try {
124
128
  refs = nativeForEachRef(basePath, "refs/gsd/snapshots/");
125
- } catch {
129
+ } catch (e) {
130
+ logWarning("command", `snapshot ref list failed: ${(e as Error).message}`);
126
131
  ctx.ui.notify("No snapshot refs to clean up.", "info");
127
132
  return;
128
133
  }
@@ -147,8 +152,8 @@ export async function handleCleanupSnapshots(ctx: ExtensionCommandContext, baseP
147
152
  try {
148
153
  nativeUpdateRef(basePath, old);
149
154
  pruned++;
150
- } catch {
151
- /* skip individual failures */
155
+ } catch (e) {
156
+ logWarning("command", `snapshot ref update failed for ${old}: ${(e as Error).message}`);
152
157
  }
153
158
  }
154
159
  }
@@ -164,7 +169,8 @@ export async function handleCleanupWorktrees(ctx: ExtensionCommandContext, baseP
164
169
  let statuses;
165
170
  try {
166
171
  statuses = getAllWorktreeHealth(basePath);
167
- } catch {
172
+ } catch (e) {
173
+ logWarning("command", `worktree health inspection failed: ${(e as Error).message}`);
168
174
  ctx.ui.notify("Failed to inspect worktrees.", "error");
169
175
  return;
170
176
  }
@@ -197,7 +203,8 @@ export async function handleCleanupWorktrees(ctx: ExtensionCommandContext, baseP
197
203
  removeWorktree(basePath, wt.name, { deleteBranch: true });
198
204
  lines.push(` ✓ ${wt.name} removed (branch ${wt.branch} deleted)`);
199
205
  removed++;
200
- } catch {
206
+ } catch (e) {
207
+ logWarning("command", `worktree removal failed for ${wt.name}: ${(e as Error).message}`);
201
208
  lines.push(` ✗ ${wt.name} failed to remove`);
202
209
  }
203
210
  }
@@ -246,7 +253,7 @@ export async function handleSkip(unitArg: string, ctx: ExtensionCommandContext,
246
253
  if (fileExists(completedKeysFile)) {
247
254
  keys = JSON.parse(readFile(completedKeysFile, "utf-8"));
248
255
  }
249
- } catch { /* start fresh */ }
256
+ } catch (e) { logWarning("command", `completed-units.json parse failed: ${(e as Error).message}`); }
250
257
 
251
258
  // Normalize: accept "execute-task/M001/S01/T03", "M001/S01/T03", or just "T03"
252
259
  let skipKey = unitArg;
@@ -371,7 +378,8 @@ export async function handleCleanupProjects(args: string, ctx: ExtensionCommandC
371
378
  hashList = readdirSync(projectsDir, { withFileTypes: true })
372
379
  .filter(e => e.isDirectory())
373
380
  .map(e => e.name);
374
- } catch {
381
+ } catch (e) {
382
+ logWarning("command", `readdir failed for project-state directory: ${(e as Error).message}`);
375
383
  ctx.ui.notify(`Failed to read project-state directory at ${projectsDir}.`, "error");
376
384
  return;
377
385
  }
@@ -454,7 +462,8 @@ export async function handleCleanupProjects(args: string, ctx: ExtensionCommandC
454
462
  try {
455
463
  fsRmSync(pathJoin(projectsDir, e.hash), { recursive: true, force: true });
456
464
  removed++;
457
- } catch {
465
+ } catch (err) {
466
+ logWarning("command", `project cleanup rm failed for ${e.hash}: ${(err as Error).message}`);
458
467
  failed.push(e.hash);
459
468
  }
460
469
  }
@@ -529,7 +538,7 @@ export async function handleRecover(ctx: ExtensionCommandContext, basePath: stri
529
538
  ctx.ui.notify(lines.join("\n"), "success");
530
539
  } catch (err) {
531
540
  const msg = err instanceof Error ? err.message : String(err);
532
- process.stderr.write(`gsd-recover: failed: ${msg}\n`);
541
+ logWarning("command", `recover failed: ${msg}`);
533
542
  ctx.ui.notify(`gsd recover failed: ${msg}`, "error");
534
543
  }
535
544
  }