gsd-pi 2.65.0-dev.d0517ff → 2.66.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 (188) hide show
  1. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +2 -0
  2. package/dist/resources/extensions/gsd/auto/loop.js +2 -2
  3. package/dist/resources/extensions/gsd/auto/phases.js +48 -5
  4. package/dist/resources/extensions/gsd/auto/types.js +2 -0
  5. package/dist/resources/extensions/gsd/auto-dashboard.js +2 -1
  6. package/dist/resources/extensions/gsd/auto-start.js +134 -2
  7. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -1
  8. package/dist/resources/extensions/gsd/commands/handlers/core.js +3 -2
  9. package/dist/resources/extensions/gsd/files.js +17 -0
  10. package/dist/resources/extensions/gsd/notification-overlay.js +1 -1
  11. package/dist/resources/extensions/gsd/notification-widget.js +2 -1
  12. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +1 -1
  13. package/dist/resources/extensions/gsd/pre-execution-checks.js +16 -2
  14. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  15. package/dist/resources/extensions/subagent/agents.js +19 -5
  16. package/dist/web/standalone/.next/BUILD_ID +1 -1
  17. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  18. package/dist/web/standalone/.next/build-manifest.json +3 -3
  19. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  20. package/dist/web/standalone/.next/required-server-files.json +3 -3
  21. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  22. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  24. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  32. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  42. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  43. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  44. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  48. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  60. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  80. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  90. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  96. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  110. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  112. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  114. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  116. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/index.html +1 -1
  126. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  127. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  128. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  129. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  131. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/page.js +2 -2
  133. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  135. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  136. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  137. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/middleware.js +2 -2
  139. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  141. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  142. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  143. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  144. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
  145. package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
  146. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +1 -0
  147. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
  148. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
  149. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  150. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  151. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  152. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  153. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  154. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  155. package/dist/web/standalone/server.js +1 -1
  156. package/package.json +1 -1
  157. package/packages/pi-coding-agent/package.json +1 -1
  158. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  159. package/packages/pi-tui/dist/tui.js +3 -1
  160. package/packages/pi-tui/dist/tui.js.map +1 -1
  161. package/packages/pi-tui/src/tui.ts +3 -1
  162. package/pkg/package.json +1 -1
  163. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +3 -0
  164. package/src/resources/extensions/gsd/auto/loop.ts +2 -2
  165. package/src/resources/extensions/gsd/auto/phases.ts +68 -3
  166. package/src/resources/extensions/gsd/auto/types.ts +5 -0
  167. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
  168. package/src/resources/extensions/gsd/auto-start.ts +143 -0
  169. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -1
  170. package/src/resources/extensions/gsd/commands/handlers/core.ts +3 -2
  171. package/src/resources/extensions/gsd/files.ts +19 -0
  172. package/src/resources/extensions/gsd/notification-overlay.ts +1 -1
  173. package/src/resources/extensions/gsd/notification-widget.ts +2 -1
  174. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +1 -1
  175. package/src/resources/extensions/gsd/pre-execution-checks.ts +19 -2
  176. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  177. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +125 -0
  178. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +69 -0
  179. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +11 -10
  180. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +189 -0
  181. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +66 -0
  182. package/src/resources/extensions/gsd/tests/subagent-agent-discovery.test.ts +47 -0
  183. package/src/resources/extensions/subagent/agents.ts +30 -6
  184. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +0 -1
  185. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
  186. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
  187. /package/dist/web/standalone/.next/static/{JwdBI3y1H8vtBKiYvWfEK → Bdk1mnQugYZh7ZxuXUYvc}/_buildManifest.js +0 -0
  188. /package/dist/web/standalone/.next/static/{JwdBI3y1H8vtBKiYvWfEK → Bdk1mnQugYZh7ZxuXUYvc}/_ssgManifest.js +0 -0
@@ -6,6 +6,8 @@
6
6
  *
7
7
  * Leaf module — no imports from auto/ to avoid circular dependencies.
8
8
  */
9
+ /** Timeout for postUnitPreVerification in runFinalize (ms). */
10
+ export const FINALIZE_PRE_TIMEOUT_MS = 60_000;
9
11
  /** Timeout for postUnitPostVerification in runFinalize (ms). */
10
12
  export const FINALIZE_POST_TIMEOUT_MS = 60_000;
11
13
  /**
@@ -24,7 +24,7 @@ import { resolveEngine } from "../engine-resolver.js";
24
24
  export async function autoLoop(ctx, pi, s, deps) {
25
25
  debugLog("autoLoop", { phase: "enter" });
26
26
  let iteration = 0;
27
- const loopState = { recentUnits: [], stuckRecoveryAttempts: 0 };
27
+ const loopState = { recentUnits: [], stuckRecoveryAttempts: 0, consecutiveFinalizeTimeouts: 0 };
28
28
  let consecutiveErrors = 0;
29
29
  const recentErrorMessages = [];
30
30
  while (s.active) {
@@ -202,7 +202,7 @@ export async function autoLoop(ctx, pi, s, deps) {
202
202
  if (unitPhaseResult.action === "break")
203
203
  break;
204
204
  // ── Phase 5: Finalize ───────────────────────────────────────────────
205
- const finalizeResult = await runFinalize(ic, iterData, sidecarItem);
205
+ const finalizeResult = await runFinalize(ic, iterData, loopState, sidecarItem);
206
206
  if (finalizeResult.action === "break")
207
207
  break;
208
208
  if (finalizeResult.action === "continue")
@@ -7,7 +7,7 @@
7
7
  * Imports from: auto/types, auto/detect-stuck, auto/run-unit, auto/loop-deps
8
8
  */
9
9
  import { importExtensionModule } from "@gsd/pi-coding-agent";
10
- import { MAX_RECOVERY_CHARS, BUDGET_THRESHOLDS, } from "./types.js";
10
+ import { MAX_RECOVERY_CHARS, BUDGET_THRESHOLDS, MAX_FINALIZE_TIMEOUTS, } from "./types.js";
11
11
  import { detectStuck } from "./detect-stuck.js";
12
12
  import { runUnit } from "./run-unit.js";
13
13
  import { debugLog } from "../debug-logger.js";
@@ -20,7 +20,7 @@ import { gsdRoot } from "../paths.js";
20
20
  import { atomicWriteSync } from "../atomic-write.js";
21
21
  import { verifyExpectedArtifact, diagnoseExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.js";
22
22
  import { writeUnitRuntimeRecord } from "../unit-runtime.js";
23
- import { withTimeout, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
23
+ import { withTimeout, FINALIZE_PRE_TIMEOUT_MS, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
24
24
  import { getEligibleSlices } from "../slice-parallel-eligibility.js";
25
25
  import { startSliceParallel } from "../slice-parallel-orchestrator.js";
26
26
  import { isDbAvailable, getMilestoneSlices } from "../gsd-db.js";
@@ -1040,7 +1040,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1040
1040
  * Phase 5: Post-unit finalize — pre/post verification, UAT pause, step-wizard.
1041
1041
  * Returns break/continue/next to control the outer loop.
1042
1042
  */
1043
- export async function runFinalize(ic, iterData, sidecarItem) {
1043
+ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
1044
1044
  const { ctx, pi, s, deps } = ic;
1045
1045
  const { pauseAfterUatDispatch } = iterData;
1046
1046
  debugLog("autoLoop", { phase: "finalize", iteration: ic.iteration });
@@ -1058,13 +1058,44 @@ export async function runFinalize(ic, iterData, sidecarItem) {
1058
1058
  updateProgressWidget: deps.updateProgressWidget,
1059
1059
  };
1060
1060
  // Pre-verification processing (commit, doctor, state rebuild, etc.)
1061
+ // Timeout guard: if postUnitPreVerification hangs (e.g., safety harness
1062
+ // deadlock, browser teardown hang, worktree sync stall), force-continue
1063
+ // after timeout so the auto-loop is not permanently frozen (#3757).
1064
+ //
1065
+ // On timeout, null out s.currentUnit so the timed-out task's late async
1066
+ // mutations are harmless — postUnitPreVerification guards all side effects
1067
+ // behind `if (s.currentUnit)`. The next iteration sets a fresh currentUnit.
1061
1068
  // Sidecar items use lightweight pre-verification opts
1062
1069
  const preVerificationOpts = sidecarItem
1063
1070
  ? sidecarItem.kind === "hook"
1064
1071
  ? { skipSettleDelay: true, skipWorktreeSync: true }
1065
1072
  : { skipSettleDelay: true }
1066
1073
  : undefined;
1067
- const preResult = await deps.postUnitPreVerification(postUnitCtx, preVerificationOpts);
1074
+ const preUnitSnapshot = s.currentUnit
1075
+ ? { type: s.currentUnit.type, id: s.currentUnit.id, startedAt: s.currentUnit.startedAt }
1076
+ : null;
1077
+ const preResultGuard = await withTimeout(deps.postUnitPreVerification(postUnitCtx, preVerificationOpts), FINALIZE_PRE_TIMEOUT_MS, "postUnitPreVerification");
1078
+ if (preResultGuard.timedOut) {
1079
+ // Detach session from the timed-out unit so late async completions
1080
+ // cannot mutate state for the next unit (#3757).
1081
+ s.currentUnit = null;
1082
+ loopState.consecutiveFinalizeTimeouts++;
1083
+ debugLog("autoLoop", {
1084
+ phase: "pre-verification-timeout",
1085
+ iteration: ic.iteration,
1086
+ unitType: iterData.unitType,
1087
+ unitId: iterData.unitId,
1088
+ consecutiveTimeouts: loopState.consecutiveFinalizeTimeouts,
1089
+ });
1090
+ if (loopState.consecutiveFinalizeTimeouts >= MAX_FINALIZE_TIMEOUTS) {
1091
+ ctx.ui.notify(`postUnitPreVerification timed out ${loopState.consecutiveFinalizeTimeouts} consecutive times — stopping auto-mode to prevent budget waste`, "error");
1092
+ await deps.stopAuto(ctx, pi, `${loopState.consecutiveFinalizeTimeouts} consecutive finalize timeouts`);
1093
+ return { action: "break", reason: "finalize-timeout-escalation" };
1094
+ }
1095
+ ctx.ui.notify(`postUnitPreVerification timed out after ${FINALIZE_PRE_TIMEOUT_MS / 1000}s for ${iterData.unitType} ${iterData.unitId} (${loopState.consecutiveFinalizeTimeouts}/${MAX_FINALIZE_TIMEOUTS}) — continuing to next iteration`, "warning");
1096
+ return { action: "next", data: undefined };
1097
+ }
1098
+ const preResult = preResultGuard.value;
1068
1099
  if (preResult === "dispatched") {
1069
1100
  debugLog("autoLoop", {
1070
1101
  phase: "exit",
@@ -1119,13 +1150,23 @@ export async function runFinalize(ic, iterData, sidecarItem) {
1119
1150
  // auto-loop is not permanently frozen (#2344).
1120
1151
  const postResultGuard = await withTimeout(deps.postUnitPostVerification(postUnitCtx), FINALIZE_POST_TIMEOUT_MS, "postUnitPostVerification");
1121
1152
  if (postResultGuard.timedOut) {
1153
+ // Detach session from the timed-out unit so late async completions
1154
+ // cannot mutate state for the next unit (#3757).
1155
+ s.currentUnit = null;
1156
+ loopState.consecutiveFinalizeTimeouts++;
1122
1157
  debugLog("autoLoop", {
1123
1158
  phase: "post-verification-timeout",
1124
1159
  iteration: ic.iteration,
1125
1160
  unitType: iterData.unitType,
1126
1161
  unitId: iterData.unitId,
1162
+ consecutiveTimeouts: loopState.consecutiveFinalizeTimeouts,
1127
1163
  });
1128
- ctx.ui.notify(`postUnitPostVerification timed out after ${FINALIZE_POST_TIMEOUT_MS / 1000}s for ${iterData.unitType} ${iterData.unitId} — continuing to next iteration`, "warning");
1164
+ if (loopState.consecutiveFinalizeTimeouts >= MAX_FINALIZE_TIMEOUTS) {
1165
+ ctx.ui.notify(`postUnitPostVerification timed out ${loopState.consecutiveFinalizeTimeouts} consecutive times — stopping auto-mode to prevent budget waste`, "error");
1166
+ await deps.stopAuto(ctx, pi, `${loopState.consecutiveFinalizeTimeouts} consecutive finalize timeouts`);
1167
+ return { action: "break", reason: "finalize-timeout-escalation" };
1168
+ }
1169
+ ctx.ui.notify(`postUnitPostVerification timed out after ${FINALIZE_POST_TIMEOUT_MS / 1000}s for ${iterData.unitType} ${iterData.unitId} (${loopState.consecutiveFinalizeTimeouts}/${MAX_FINALIZE_TIMEOUTS}) — continuing to next iteration`, "warning");
1129
1170
  return { action: "next", data: undefined };
1130
1171
  }
1131
1172
  const postResult = postResultGuard.value;
@@ -1141,5 +1182,7 @@ export async function runFinalize(ic, iterData, sidecarItem) {
1141
1182
  debugLog("autoLoop", { phase: "exit", reason: "step-wizard" });
1142
1183
  return { action: "break", reason: "step-wizard" };
1143
1184
  }
1185
+ // Both pre and post verification completed without timeout — reset counter
1186
+ loopState.consecutiveFinalizeTimeouts = 0;
1144
1187
  return { action: "next", data: undefined };
1145
1188
  }
@@ -21,3 +21,5 @@ export const BUDGET_THRESHOLDS = [
21
21
  { pct: 80, label: "Approaching budget ceiling — 80%", notifyLevel: "warning", cmuxLevel: "warning" },
22
22
  { pct: 75, label: "Budget 75%", notifyLevel: "info", cmuxLevel: "progress" },
23
23
  ];
24
+ /** Max consecutive finalize timeouts before hard-stopping auto-mode. */
25
+ export const MAX_FINALIZE_TIMEOUTS = 3;
@@ -9,6 +9,7 @@ import { getCurrentBranch } from "./worktree.js";
9
9
  import { getActiveHook } from "./post-unit-hooks.js";
10
10
  import { getLedger, getProjectTotals } from "./metrics.js";
11
11
  import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
12
+ import { formatShortcut } from "./files.js";
12
13
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
13
14
  import { execFileSync } from "node:child_process";
14
15
  import { truncateToWidth, visibleWidth } from "@gsd/pi-tui";
@@ -725,7 +726,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
725
726
  // Hints line
726
727
  const hintParts = [];
727
728
  hintParts.push("esc pause");
728
- hintParts.push(process.platform === "darwin" ? "⌃⌥G dashboard" : "Ctrl+Alt+G dashboard");
729
+ hintParts.push(`${formatShortcut("Ctrl+Alt+G")} dashboard`);
729
730
  const hintStr = theme.fg("dim", hintParts.join(" | "));
730
731
  const commitStr = lastCommit
731
732
  ? theme.fg("dim", `${lastCommit.timeAgo} ago: ${commitMsg}`)
@@ -20,11 +20,12 @@ import { synthesizeCrashRecovery } from "./session-forensics.js";
20
20
  import { writeLock, clearLock, readCrashLock, formatCrashInfo, isLockProcessAlive, } from "./crash-recovery.js";
21
21
  import { acquireSessionLock, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
22
22
  import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
23
- import { nativeIsRepo, nativeInit, nativeAddAll, nativeCommit, nativeGetCurrentBranch, nativeDetectMainBranch, nativeCheckoutBranch, } from "./native-git-bridge.js";
23
+ import { nativeIsRepo, nativeInit, nativeAddAll, nativeCommit, nativeGetCurrentBranch, nativeDetectMainBranch, nativeCheckoutBranch, nativeBranchList, nativeBranchListMerged, nativeBranchDelete, nativeWorktreeRemove, } from "./native-git-bridge.js";
24
24
  import { GitServiceImpl } from "./git-service.js";
25
25
  import { captureIntegrationBranch, detectWorktreeName, setActiveMilestoneId, } from "./worktree.js";
26
26
  import { getAutoWorktreePath } from "./auto-worktree.js";
27
27
  import { readResourceVersion, cleanStaleRuntimeUnits } from "./auto-worktree.js";
28
+ import { worktreePath as getWorktreeDir, isInsideWorktreesDir } from "./worktree-manager.js";
28
29
  import { initMetrics } from "./metrics.js";
29
30
  import { initRoutingHistory } from "./routing-history.js";
30
31
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
@@ -35,7 +36,7 @@ import { hideFooter } from "./auto-dashboard.js";
35
36
  import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath, } from "./debug-logger.js";
36
37
  import { logWarning, logError } from "./workflow-logger.js";
37
38
  import { parseUnitId } from "./unit-id.js";
38
- import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync, } from "node:fs";
39
+ import { existsSync, mkdirSync, readdirSync, rmSync, statSync, unlinkSync, } from "node:fs";
39
40
  import { join } from "node:path";
40
41
  import { sep as pathSep } from "node:path";
41
42
  import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
@@ -62,6 +63,117 @@ export async function openProjectDbIfPresent(basePath) {
62
63
  logWarning("engine", `gsd-db: failed to open existing database: ${err instanceof Error ? err.message : String(err)}`);
63
64
  }
64
65
  }
66
+ /**
67
+ * Audit for orphaned milestone branches at bootstrap.
68
+ *
69
+ * After a milestone completes, the teardown step (merge branch → main,
70
+ * delete branch, remove worktree) runs as a post-completion engine step.
71
+ * If the session ends between completion and teardown, the branch and
72
+ * worktree are orphaned — the DB says "complete" so auto-mode won't
73
+ * re-enter the milestone, and the teardown is never retried.
74
+ *
75
+ * This audit runs on every fresh bootstrap to catch that gap:
76
+ * 1. Lists all local `milestone/*` branches.
77
+ * 2. For each, checks if the milestone's DB status is "complete".
78
+ * 3. If the branch is already merged into main → deletes the branch
79
+ * and cleans up any orphaned worktree directory (safe, no data loss).
80
+ * 4. If the branch is NOT merged → preserves it and warns the user
81
+ * so they can merge manually (data safety first).
82
+ *
83
+ * Returns a summary of actions taken for the caller to surface via notify.
84
+ */
85
+ export function auditOrphanedMilestoneBranches(basePath, isolationMode) {
86
+ const recovered = [];
87
+ const warnings = [];
88
+ // Skip in none mode — no milestone branches are created
89
+ if (isolationMode === "none")
90
+ return { recovered, warnings };
91
+ // Skip if DB not available — can't determine completion status
92
+ if (!isDbAvailable())
93
+ return { recovered, warnings };
94
+ let milestoneBranches;
95
+ try {
96
+ milestoneBranches = nativeBranchList(basePath, "milestone/*");
97
+ }
98
+ catch {
99
+ // git branch list failed — skip audit
100
+ return { recovered, warnings };
101
+ }
102
+ if (milestoneBranches.length === 0)
103
+ return { recovered, warnings };
104
+ // Detect main branch for merge-check
105
+ let mainBranch;
106
+ try {
107
+ mainBranch = nativeDetectMainBranch(basePath);
108
+ }
109
+ catch {
110
+ mainBranch = "main";
111
+ }
112
+ // Get branches already merged into main
113
+ let mergedBranches;
114
+ try {
115
+ mergedBranches = new Set(nativeBranchListMerged(basePath, mainBranch, "milestone/*"));
116
+ }
117
+ catch {
118
+ mergedBranches = new Set();
119
+ }
120
+ for (const branch of milestoneBranches) {
121
+ const milestoneId = branch.replace(/^milestone\//, "");
122
+ const milestone = getMilestone(milestoneId);
123
+ // Only audit completed milestones
124
+ if (!milestone || milestone.status !== "complete")
125
+ continue;
126
+ const isMerged = mergedBranches.has(branch);
127
+ if (isMerged) {
128
+ // Branch is merged — safe to delete branch and clean up worktree dir
129
+ try {
130
+ nativeBranchDelete(basePath, branch, true);
131
+ recovered.push(`Deleted merged branch ${branch} for completed milestone ${milestoneId}.`);
132
+ }
133
+ catch (err) {
134
+ warnings.push(`Failed to delete merged branch ${branch}: ${err instanceof Error ? err.message : String(err)}`);
135
+ }
136
+ // Clean up orphaned worktree directory if it exists
137
+ const wtDir = getWorktreeDir(basePath, milestoneId);
138
+ if (existsSync(wtDir)) {
139
+ // Try git worktree remove first (handles registered worktrees)
140
+ try {
141
+ nativeWorktreeRemove(basePath, wtDir, true);
142
+ }
143
+ catch (e) {
144
+ // Not a registered worktree — expected for orphaned dirs
145
+ logWarning("engine", `worktree remove failed (expected for orphaned dirs): ${e instanceof Error ? e.message : String(e)}`);
146
+ }
147
+ // If the directory still exists after git worktree remove (either it
148
+ // wasn't registered or the remove was a noop), fall back to direct
149
+ // filesystem removal — but only inside .gsd/worktrees/ for safety (#2365).
150
+ if (existsSync(wtDir)) {
151
+ if (isInsideWorktreesDir(basePath, wtDir)) {
152
+ try {
153
+ rmSync(wtDir, { recursive: true, force: true });
154
+ recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
155
+ }
156
+ catch (err2) {
157
+ warnings.push(`Failed to remove worktree directory for ${milestoneId}: ${err2 instanceof Error ? err2.message : String(err2)}`);
158
+ }
159
+ }
160
+ else {
161
+ warnings.push(`Orphaned worktree directory for ${milestoneId} is outside .gsd/worktrees/ — skipping removal for safety.`);
162
+ }
163
+ }
164
+ else {
165
+ recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
166
+ }
167
+ }
168
+ }
169
+ else {
170
+ // Branch is NOT merged — preserve for safety, warn the user
171
+ warnings.push(`Branch ${branch} exists for completed milestone ${milestoneId} but is NOT merged into ${mainBranch}. ` +
172
+ `This may contain unmerged work. Merge manually or run \`/gsd health --fix\` to resolve.`);
173
+ }
174
+ }
175
+ return { recovered, warnings };
176
+ }
65
177
  export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, requestedStepMode, deps) {
66
178
  const { shouldUseWorktreeIsolation, registerSigtermHandler, lockBase, buildResolver, } = deps;
67
179
  const lockResult = acquireSessionLock(base);
@@ -191,6 +303,26 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
191
303
  // Open the project-root DB before deriveState so DB-backed state
192
304
  // derivation (queue-order, task status) works on a cold start (#2841).
193
305
  await openProjectDbIfPresent(base);
306
+ // ── Orphaned milestone branch audit ──
307
+ // Catches completed milestones whose teardown (merge + branch delete)
308
+ // was lost due to session ending between completion and teardown.
309
+ // Must run after DB open and before worktree entry.
310
+ try {
311
+ const auditResult = auditOrphanedMilestoneBranches(base, getIsolationMode());
312
+ for (const msg of auditResult.recovered) {
313
+ ctx.ui.notify(`Orphan audit: ${msg}`, "info");
314
+ }
315
+ for (const msg of auditResult.warnings) {
316
+ ctx.ui.notify(`Orphan audit: ${msg}`, "warning");
317
+ }
318
+ if (auditResult.recovered.length > 0) {
319
+ debugLog("orphan-audit", { recovered: auditResult.recovered, warnings: auditResult.warnings });
320
+ }
321
+ }
322
+ catch (err) {
323
+ // Non-fatal — the audit is defensive, never block bootstrap
324
+ logWarning("bootstrap", `orphaned milestone branch audit failed: ${err instanceof Error ? err.message : String(err)}`);
325
+ }
194
326
  let state = await deriveState(base);
195
327
  // Stale worktree state recovery (#654)
196
328
  if (state.activeMilestone &&
@@ -12,7 +12,7 @@ import { hasSkillSnapshot, detectNewSkills, formatSkillsXml } from "../skill-dis
12
12
  import { getActiveAutoWorktreeContext } from "../auto-worktree.js";
13
13
  import { getActiveWorktreeName, getWorktreeOriginalCwd } from "../worktree-command.js";
14
14
  import { deriveState } from "../state.js";
15
- import { formatOverridesSection, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
15
+ import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
16
16
  import { toPosixPath } from "../../shared/mod.js";
17
17
  import { markCmuxPromptShown, shouldPromptToEnableCmux } from "../../cmux/index.js";
18
18
  const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
@@ -60,6 +60,8 @@ export async function buildBeforeAgentStartResult(event, ctx) {
60
60
  const systemContent = loadPrompt("system", {
61
61
  bundledSkillsTable: buildBundledSkillsTable(),
62
62
  templatesDir: getTemplatesDir(),
63
+ shortcutDashboard: formatShortcut("Ctrl+Alt+G"),
64
+ shortcutShell: formatShortcut("Ctrl+Alt+B"),
63
65
  });
64
66
  const loadedPreferences = loadEffectiveGSDPreferences();
65
67
  if (shouldPromptToEnableCmux(loadedPreferences?.preferences)) {
@@ -5,6 +5,7 @@ import { runEnvironmentChecks } from "../../doctor-environment.js";
5
5
  import { deriveState } from "../../state.js";
6
6
  import { handleCmux } from "../../commands-cmux.js";
7
7
  import { projectRoot } from "../context.js";
8
+ import { formatShortcut } from "../../files.js";
8
9
  export function showHelp(ctx) {
9
10
  const lines = [
10
11
  "GSD — Get Shit Done\n",
@@ -20,12 +21,12 @@ export function showHelp(ctx) {
20
21
  " /gsd new-milestone Create milestone from headless context (used by gsd headless)",
21
22
  "",
22
23
  "VISIBILITY",
23
- " /gsd status Show progress dashboard (Ctrl+Alt+G)",
24
+ ` /gsd status Show progress dashboard (${formatShortcut("Ctrl+Alt+G")})`,
24
25
  " /gsd visualize Interactive 10-tab TUI (progress, timeline, deps, metrics, health, agent, changes, knowledge, captures, export)",
25
26
  " /gsd queue Show queued/dispatched units and execution order",
26
27
  " /gsd history View execution history [--cost] [--phase] [--model] [N]",
27
28
  " /gsd changelog Show categorized release notes [version]",
28
- " /gsd notifications View persistent notification history [clear|tail|filter] (Ctrl+Alt+N)",
29
+ ` /gsd notifications View persistent notification history [clear|tail|filter] (${formatShortcut("Ctrl+Alt+N")})`,
29
30
  "",
30
31
  "COURSE CORRECTION",
31
32
  " /gsd steer <desc> Apply user override to active work",
@@ -52,6 +52,23 @@ export function clearParseCache() {
52
52
  for (const cb of _cacheClearCallbacks)
53
53
  cb();
54
54
  }
55
+ // ─── Platform shortcuts ───────────────────────────────────────────────────
56
+ const IS_MAC = process.platform === "darwin";
57
+ /**
58
+ * Format a keyboard shortcut for the current OS.
59
+ * Input: modifier key combo like "Ctrl+Alt+G"
60
+ * Output: "⌃⌥G" on macOS, "Ctrl+Alt+G" on Windows/Linux.
61
+ */
62
+ export function formatShortcut(combo) {
63
+ if (!IS_MAC)
64
+ return combo;
65
+ return combo
66
+ .replace(/Ctrl\+Alt\+/i, "⌃⌥")
67
+ .replace(/Ctrl\+/i, "⌃")
68
+ .replace(/Alt\+/i, "⌥")
69
+ .replace(/Shift\+/i, "⇧")
70
+ .replace(/Cmd\+/i, "⌘");
71
+ }
55
72
  // ─── Helpers ───────────────────────────────────────────────────────────────
56
73
  /** Extract the text after a heading at a given level, up to the next heading of same or higher level. */
57
74
  export function extractSection(body, heading, level = 2) {
@@ -1,6 +1,6 @@
1
1
  // GSD Extension — Notification History Overlay
2
2
  // Scrollable panel showing all persisted notifications with severity filtering.
3
- // Toggled with Ctrl+Alt+N or opened from /gsd notifications.
3
+ // Toggled with Ctrl+Alt+N (⌃⌥N on macOS) or opened from /gsd notifications.
4
4
  import { truncateToWidth, visibleWidth, matchesKey, Key } from "@gsd/pi-tui";
5
5
  import { readNotifications, markAllRead, clearNotifications, } from "./notification-store.js";
6
6
  import { padRight, joinColumns } from "../shared/mod.js";
@@ -3,6 +3,7 @@
3
3
  // the most recent notification message. Refreshes every 5 seconds.
4
4
  // Widget key: "gsd-notifications", placement: "belowEditor"
5
5
  import { getUnreadCount, readNotifications } from "./notification-store.js";
6
+ import { formatShortcut } from "./files.js";
6
7
  // ─── Pure rendering ──���────────────────────────���─────────────────────────
7
8
  export function buildNotificationWidgetLines() {
8
9
  const unread = getUnreadCount();
@@ -18,7 +19,7 @@ export function buildNotificationWidgetLines() {
18
19
  const truncated = latest.message.length > msgMax
19
20
  ? latest.message.slice(0, msgMax - 1) + "…"
20
21
  : latest.message;
21
- return [` ${icon} [${badge}] ${truncated} (Ctrl+Alt+N to view)`];
22
+ return [` ${icon} [${badge}] ${truncated} (${formatShortcut("Ctrl+Alt+N")} to view)`];
22
23
  }
23
24
  // ─── Widget init ────────────────────────────────────────────────────────
24
25
  const REFRESH_INTERVAL_MS = 5_000;
@@ -2,7 +2,7 @@
2
2
  * GSD Parallel Monitor Overlay
3
3
  *
4
4
  * Full-screen TUI overlay showing real-time parallel worker progress.
5
- * Opened via `/gsd parallel watch` or Ctrl+Alt+P.
5
+ * Opened via `/gsd parallel watch` or Ctrl+Alt+P (⌃⌥P on macOS).
6
6
  * Reads the same data sources as `scripts/parallel-monitor.mjs` but
7
7
  * renders as a native pi-tui overlay with theme integration.
8
8
  */
@@ -197,8 +197,7 @@ export async function checkPackageExistence(tasks, _basePath) {
197
197
  export function normalizeFilePath(filePath) {
198
198
  if (!filePath)
199
199
  return filePath;
200
- // Strip backtick wrapping from LLM-generated paths (#3649)
201
- let normalized = filePath.replace(/`/g, "");
200
+ let normalized = extractPathFromAnnotation(filePath);
202
201
  // Normalize path separators to forward slashes
203
202
  normalized = normalized.replace(/\\/g, "/");
204
203
  // Remove leading ./
@@ -213,6 +212,21 @@ export function normalizeFilePath(filePath) {
213
212
  }
214
213
  return normalized;
215
214
  }
215
+ function extractPathFromAnnotation(raw) {
216
+ const trimmed = raw.trim();
217
+ if (!trimmed)
218
+ return trimmed;
219
+ const backtickMatch = trimmed.match(/^`([^`]+)`(?:\s+[—–-]\s+.*)?$/);
220
+ if (backtickMatch) {
221
+ return backtickMatch[1].trim();
222
+ }
223
+ const annotatedMatch = trimmed.match(/^(.+?)\s+[—–-]\s+.+$/);
224
+ if (annotatedMatch) {
225
+ return annotatedMatch[1].trim();
226
+ }
227
+ // Fall back to the original behavior for already-plain paths.
228
+ return trimmed.replace(/`/g, "");
229
+ }
216
230
  /**
217
231
  * Build a set of files that will be created by tasks up to (but not including) taskIndex.
218
232
  * All paths are normalized for consistent comparison.
@@ -131,8 +131,8 @@ Templates showing the expected format for each artifact type are in:
131
131
  - `/gsd status` - progress dashboard overlay
132
132
  - `/gsd queue` - queue future milestones (safe while auto-mode is running)
133
133
  - `/gsd quick <task>` - quick task with GSD guarantees (atomic commits, state tracking) but no milestone ceremony
134
- - `Ctrl+Alt+G` - toggle dashboard overlay
135
- - `Ctrl+Alt+B` - show shell processes
134
+ - `{{shortcutDashboard}}` - toggle dashboard overlay
135
+ - `{{shortcutShell}}` - show shell processes
136
136
 
137
137
  ## Execution Heuristics
138
138
 
@@ -5,6 +5,23 @@ import * as fs from "node:fs";
5
5
  import * as path from "node:path";
6
6
  import { getAgentDir, parseFrontmatter } from "@gsd/pi-coding-agent";
7
7
  const PROJECT_AGENT_DIR_CANDIDATES = [".gsd", ".pi"];
8
+ function parseAgentTools(value) {
9
+ if (typeof value === "string") {
10
+ const tools = value
11
+ .split(",")
12
+ .map((tool) => tool.trim())
13
+ .filter(Boolean);
14
+ return tools.length > 0 ? tools : undefined;
15
+ }
16
+ if (Array.isArray(value)) {
17
+ const tools = value
18
+ .flatMap((tool) => typeof tool === "string" ? tool.split(",") : [])
19
+ .map((tool) => tool.trim())
20
+ .filter(Boolean);
21
+ return tools.length > 0 ? tools : undefined;
22
+ }
23
+ return undefined;
24
+ }
8
25
  function loadAgentsFromDir(dir, source) {
9
26
  const agents = [];
10
27
  if (!fs.existsSync(dir)) {
@@ -31,13 +48,10 @@ function loadAgentsFromDir(dir, source) {
31
48
  continue;
32
49
  }
33
50
  const { frontmatter, body } = parseFrontmatter(content);
34
- if (!frontmatter.name || !frontmatter.description) {
51
+ if (typeof frontmatter.name !== "string" || typeof frontmatter.description !== "string") {
35
52
  continue;
36
53
  }
37
- const tools = frontmatter.tools
38
- ?.split(",")
39
- .map((t) => t.trim())
40
- .filter(Boolean);
54
+ const tools = parseAgentTools(frontmatter.tools);
41
55
  agents.push({
42
56
  name: frontmatter.name,
43
57
  description: frontmatter.description,
@@ -1 +1 @@
1
- JwdBI3y1H8vtBKiYvWfEK
1
+ Bdk1mnQugYZh7ZxuXUYvc
@@ -2,46 +2,46 @@
2
2
  "/_not-found/page": "/_not-found",
3
3
  "/_global-error/page": "/_global-error",
4
4
  "/api/bridge-terminal/input/route": "/api/bridge-terminal/input",
5
- "/api/captures/route": "/api/captures",
6
- "/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
7
5
  "/api/bridge-terminal/resize/route": "/api/bridge-terminal/resize",
8
- "/api/browse-directories/route": "/api/browse-directories",
6
+ "/api/boot/route": "/api/boot",
7
+ "/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
9
8
  "/api/cleanup/route": "/api/cleanup",
9
+ "/api/dev-mode/route": "/api/dev-mode",
10
+ "/api/captures/route": "/api/captures",
10
11
  "/api/export-data/route": "/api/export-data",
11
- "/api/forensics/route": "/api/forensics",
12
+ "/api/browse-directories/route": "/api/browse-directories",
12
13
  "/api/doctor/route": "/api/doctor",
14
+ "/api/forensics/route": "/api/forensics",
13
15
  "/api/git/route": "/api/git",
14
16
  "/api/history/route": "/api/history",
15
- "/api/hooks/route": "/api/hooks",
16
17
  "/api/inspect/route": "/api/inspect",
18
+ "/api/hooks/route": "/api/hooks",
17
19
  "/api/knowledge/route": "/api/knowledge",
20
+ "/api/notifications/route": "/api/notifications",
18
21
  "/api/live-state/route": "/api/live-state",
19
22
  "/api/experimental/route": "/api/experimental",
20
- "/api/notifications/route": "/api/notifications",
21
- "/api/boot/route": "/api/boot",
22
23
  "/api/preferences/route": "/api/preferences",
23
24
  "/api/recovery/route": "/api/recovery",
24
- "/api/onboarding/route": "/api/onboarding",
25
25
  "/api/projects/route": "/api/projects",
26
+ "/api/onboarding/route": "/api/onboarding",
26
27
  "/api/session/browser/route": "/api/session/browser",
27
28
  "/api/session/command/route": "/api/session/command",
28
- "/api/settings-data/route": "/api/settings-data",
29
29
  "/api/session/events/route": "/api/session/events",
30
+ "/api/files/route": "/api/files",
30
31
  "/api/shutdown/route": "/api/shutdown",
32
+ "/api/settings-data/route": "/api/settings-data",
31
33
  "/api/skill-health/route": "/api/skill-health",
32
34
  "/api/session/manage/route": "/api/session/manage",
33
35
  "/api/steer/route": "/api/steer",
34
36
  "/api/terminal/input/route": "/api/terminal/input",
35
37
  "/api/switch-root/route": "/api/switch-root",
36
- "/api/files/route": "/api/files",
37
38
  "/api/terminal/resize/route": "/api/terminal/resize",
38
- "/api/dev-mode/route": "/api/dev-mode",
39
39
  "/api/terminal/sessions/route": "/api/terminal/sessions",
40
- "/api/visualizer/route": "/api/visualizer",
41
40
  "/api/terminal/stream/route": "/api/terminal/stream",
42
41
  "/api/undo/route": "/api/undo",
42
+ "/api/remote-questions/route": "/api/remote-questions",
43
43
  "/api/update/route": "/api/update",
44
44
  "/api/terminal/upload/route": "/api/terminal/upload",
45
- "/api/remote-questions/route": "/api/remote-questions",
45
+ "/api/visualizer/route": "/api/visualizer",
46
46
  "/page": "/"
47
47
  }
@@ -4,14 +4,14 @@
4
4
  ],
5
5
  "devFiles": [],
6
6
  "lowPriorityFiles": [
7
- "static/JwdBI3y1H8vtBKiYvWfEK/_buildManifest.js",
8
- "static/JwdBI3y1H8vtBKiYvWfEK/_ssgManifest.js"
7
+ "static/Bdk1mnQugYZh7ZxuXUYvc/_buildManifest.js",
8
+ "static/Bdk1mnQugYZh7ZxuXUYvc/_ssgManifest.js"
9
9
  ],
10
10
  "rootMainFiles": [
11
11
  "static/chunks/webpack-9fed74684e1c5bb1.js",
12
12
  "static/chunks/4bd1b696-e5d7c65570c947b7.js",
13
13
  "static/chunks/3794-337d1ca25ad99a89.js",
14
- "static/chunks/main-app-fdab67f7802d7832.js"
14
+ "static/chunks/main-app-d3d4c336195465f9.js"
15
15
  ],
16
16
  "rootMainFilesTree": {},
17
17
  "pages": {
@@ -78,8 +78,8 @@
78
78
  "dynamicRoutes": {},
79
79
  "notFoundRoutes": [],
80
80
  "preview": {
81
- "previewModeId": "8379caa4380c703999a60ef15e76a9d0",
82
- "previewModeSigningKey": "491b8d5b0ef9c4d8c76ffb2633d7a866066482a8f622253f8c23c4fcdaf27361",
83
- "previewModeEncryptionKey": "2b563578eea8905c6f25a2710af046923a5d5ef4b8f5f013bfedab4610c613ed"
81
+ "previewModeId": "2697103c5c2033fa3fb96ea39381aa27",
82
+ "previewModeSigningKey": "7fb58f7c9e9468171e803f97f5aab784faf710fee65187d22a9e05891a0249db",
83
+ "previewModeEncryptionKey": "453122158de8d4b03492b1ac0c1966d6b2f562c6746d5a66bffc5c6199aec895"
84
84
  }
85
85
  }
@@ -100,7 +100,7 @@
100
100
  "transform": "lodash/{{member}}"
101
101
  }
102
102
  },
103
- "outputFileTracingRoot": "/__w/gsd-2/gsd-2",
103
+ "outputFileTracingRoot": "/home/runner/_work/gsd-2/gsd-2",
104
104
  "cacheComponents": false,
105
105
  "cacheLife": {
106
106
  "default": {
@@ -297,11 +297,11 @@
297
297
  "node-pty"
298
298
  ],
299
299
  "turbopack": {
300
- "root": "/__w/gsd-2/gsd-2"
300
+ "root": "/home/runner/_work/gsd-2/gsd-2"
301
301
  },
302
302
  "distDirRoot": ".next"
303
303
  },
304
- "appDir": "/__w/gsd-2/gsd-2/web",
304
+ "appDir": "/home/runner/_work/gsd-2/gsd-2/web",
305
305
  "relativeAppDir": "web",
306
306
  "files": [
307
307
  ".next/routes-manifest.json",