gsd-pi 2.16.0 → 2.18.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 (225) hide show
  1. package/README.md +39 -0
  2. package/dist/onboarding.js +2 -2
  3. package/dist/remote-questions-config.d.ts +10 -0
  4. package/dist/remote-questions-config.js +36 -0
  5. package/dist/resources/extensions/gsd/activity-log.ts +37 -7
  6. package/dist/resources/extensions/gsd/auto-dashboard.ts +4 -0
  7. package/dist/resources/extensions/gsd/auto-dispatch.ts +9 -3
  8. package/dist/resources/extensions/gsd/auto-prompts.ts +91 -42
  9. package/dist/resources/extensions/gsd/auto-recovery.ts +7 -2
  10. package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
  11. package/dist/resources/extensions/gsd/auto.ts +177 -25
  12. package/dist/resources/extensions/gsd/commands.ts +264 -23
  13. package/dist/resources/extensions/gsd/complexity.ts +236 -0
  14. package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
  15. package/dist/resources/extensions/gsd/docs/preferences-reference.md +202 -2
  16. package/dist/resources/extensions/gsd/files.ts +129 -3
  17. package/dist/resources/extensions/gsd/git-service.ts +19 -8
  18. package/dist/resources/extensions/gsd/gitignore.ts +41 -2
  19. package/dist/resources/extensions/gsd/guided-flow.ts +247 -10
  20. package/dist/resources/extensions/gsd/index.ts +47 -3
  21. package/dist/resources/extensions/gsd/metrics.ts +44 -0
  22. package/dist/resources/extensions/gsd/native-git-bridge.ts +5 -0
  23. package/dist/resources/extensions/gsd/native-parser-bridge.ts +5 -0
  24. package/dist/resources/extensions/gsd/paths.ts +9 -0
  25. package/dist/resources/extensions/gsd/preferences.ts +181 -2
  26. package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
  27. package/dist/resources/extensions/gsd/prompts/system.md +2 -0
  28. package/dist/resources/extensions/gsd/queue-order.ts +231 -0
  29. package/dist/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
  30. package/dist/resources/extensions/gsd/routing-history.ts +290 -0
  31. package/dist/resources/extensions/gsd/state.ts +15 -3
  32. package/dist/resources/extensions/gsd/templates/knowledge.md +19 -0
  33. package/dist/resources/extensions/gsd/templates/preferences.md +14 -0
  34. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
  35. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
  36. package/dist/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
  37. package/dist/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
  38. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
  39. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
  40. package/dist/resources/extensions/gsd/tests/git-service.test.ts +132 -0
  41. package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
  42. package/dist/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
  43. package/dist/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
  44. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
  45. package/dist/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
  46. package/dist/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
  47. package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
  48. package/dist/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
  49. package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
  50. package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
  51. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
  52. package/dist/resources/extensions/gsd/types.ts +28 -0
  53. package/dist/resources/extensions/gsd/worktree-manager.ts +8 -5
  54. package/dist/resources/extensions/gsd/worktree.ts +24 -2
  55. package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
  56. package/package.json +1 -1
  57. package/packages/pi-ai/dist/models.generated.d.ts +493 -13
  58. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  59. package/packages/pi-ai/dist/models.generated.js +422 -62
  60. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  61. package/packages/pi-ai/dist/providers/google-shared.d.ts +12 -0
  62. package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
  63. package/packages/pi-ai/dist/providers/google-shared.js +9 -22
  64. package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
  65. package/packages/pi-ai/dist/providers/google-shared.test.d.ts +2 -0
  66. package/packages/pi-ai/dist/providers/google-shared.test.d.ts.map +1 -0
  67. package/packages/pi-ai/dist/providers/google-shared.test.js +125 -0
  68. package/packages/pi-ai/dist/providers/google-shared.test.js.map +1 -0
  69. package/packages/pi-ai/src/models.generated.ts +422 -62
  70. package/packages/pi-ai/src/providers/google-shared.test.ts +137 -0
  71. package/packages/pi-ai/src/providers/google-shared.ts +10 -19
  72. package/packages/pi-coding-agent/dist/cli/args.d.ts +5 -0
  73. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  74. package/packages/pi-coding-agent/dist/cli/args.js +21 -0
  75. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  76. package/packages/pi-coding-agent/dist/cli/list-models.d.ts +14 -3
  77. package/packages/pi-coding-agent/dist/cli/list-models.d.ts.map +1 -1
  78. package/packages/pi-coding-agent/dist/cli/list-models.js +52 -17
  79. package/packages/pi-coding-agent/dist/cli/list-models.js.map +1 -1
  80. package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts +27 -0
  81. package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -0
  82. package/packages/pi-coding-agent/dist/core/discovery-cache.js +79 -0
  83. package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -0
  84. package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts +2 -0
  85. package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts.map +1 -0
  86. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +140 -0
  87. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -0
  88. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +35 -0
  89. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -0
  90. package/packages/pi-coding-agent/dist/core/model-discovery.js +162 -0
  91. package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -0
  92. package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts +2 -0
  93. package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts.map +1 -0
  94. package/packages/pi-coding-agent/dist/core/model-discovery.test.js +100 -0
  95. package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -0
  96. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts +2 -0
  97. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts.map +1 -0
  98. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +113 -0
  99. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -0
  100. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +26 -0
  101. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/model-registry.js +98 -0
  103. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts +62 -0
  105. package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts.map +1 -0
  106. package/packages/pi-coding-agent/dist/core/models-json-writer.js +145 -0
  107. package/packages/pi-coding-agent/dist/core/models-json-writer.js.map +1 -0
  108. package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts +2 -0
  109. package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts.map +1 -0
  110. package/packages/pi-coding-agent/dist/core/models-json-writer.test.js +118 -0
  111. package/packages/pi-coding-agent/dist/core/models-json-writer.test.js.map +1 -0
  112. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
  113. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  114. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  115. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  118. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +7 -7
  120. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +209 -13
  122. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts +2 -0
  124. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts.map +1 -0
  125. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +67 -0
  126. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -0
  127. package/packages/pi-coding-agent/dist/index.d.ts +5 -1
  128. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  129. package/packages/pi-coding-agent/dist/index.js +4 -1
  130. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/main.js +17 -2
  133. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +1 -0
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +25 -0
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -0
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +121 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -0
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +32 -0
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
  150. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  151. package/packages/pi-coding-agent/src/cli/args.ts +21 -0
  152. package/packages/pi-coding-agent/src/cli/list-models.ts +70 -17
  153. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +170 -0
  154. package/packages/pi-coding-agent/src/core/discovery-cache.ts +97 -0
  155. package/packages/pi-coding-agent/src/core/model-discovery.test.ts +125 -0
  156. package/packages/pi-coding-agent/src/core/model-discovery.ts +231 -0
  157. package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +135 -0
  158. package/packages/pi-coding-agent/src/core/model-registry.ts +107 -0
  159. package/packages/pi-coding-agent/src/core/models-json-writer.test.ts +145 -0
  160. package/packages/pi-coding-agent/src/core/models-json-writer.ts +188 -0
  161. package/packages/pi-coding-agent/src/core/settings-manager.ts +21 -0
  162. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  163. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +85 -0
  164. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +245 -17
  165. package/packages/pi-coding-agent/src/index.ts +5 -0
  166. package/packages/pi-coding-agent/src/main.ts +19 -2
  167. package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +1 -0
  168. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +1 -1
  169. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +163 -0
  170. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +37 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +13 -0
  172. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  173. package/pkg/dist/modes/interactive/theme/theme.js +10 -0
  174. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  175. package/src/resources/extensions/gsd/activity-log.ts +37 -7
  176. package/src/resources/extensions/gsd/auto-dashboard.ts +4 -0
  177. package/src/resources/extensions/gsd/auto-dispatch.ts +9 -3
  178. package/src/resources/extensions/gsd/auto-prompts.ts +91 -42
  179. package/src/resources/extensions/gsd/auto-recovery.ts +7 -2
  180. package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
  181. package/src/resources/extensions/gsd/auto.ts +177 -25
  182. package/src/resources/extensions/gsd/commands.ts +264 -23
  183. package/src/resources/extensions/gsd/complexity.ts +236 -0
  184. package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
  185. package/src/resources/extensions/gsd/docs/preferences-reference.md +202 -2
  186. package/src/resources/extensions/gsd/files.ts +129 -3
  187. package/src/resources/extensions/gsd/git-service.ts +19 -8
  188. package/src/resources/extensions/gsd/gitignore.ts +41 -2
  189. package/src/resources/extensions/gsd/guided-flow.ts +247 -10
  190. package/src/resources/extensions/gsd/index.ts +47 -3
  191. package/src/resources/extensions/gsd/metrics.ts +44 -0
  192. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -0
  193. package/src/resources/extensions/gsd/native-parser-bridge.ts +5 -0
  194. package/src/resources/extensions/gsd/paths.ts +9 -0
  195. package/src/resources/extensions/gsd/preferences.ts +181 -2
  196. package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
  197. package/src/resources/extensions/gsd/prompts/system.md +2 -0
  198. package/src/resources/extensions/gsd/queue-order.ts +231 -0
  199. package/src/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
  200. package/src/resources/extensions/gsd/routing-history.ts +290 -0
  201. package/src/resources/extensions/gsd/state.ts +15 -3
  202. package/src/resources/extensions/gsd/templates/knowledge.md +19 -0
  203. package/src/resources/extensions/gsd/templates/preferences.md +14 -0
  204. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
  205. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
  206. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
  207. package/src/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
  208. package/src/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
  209. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
  210. package/src/resources/extensions/gsd/tests/git-service.test.ts +132 -0
  211. package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
  212. package/src/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
  213. package/src/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
  214. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
  215. package/src/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
  216. package/src/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
  217. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
  218. package/src/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
  219. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
  220. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
  221. package/src/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
  222. package/src/resources/extensions/gsd/types.ts +28 -0
  223. package/src/resources/extensions/gsd/worktree-manager.ts +8 -5
  224. package/src/resources/extensions/gsd/worktree.ts +24 -2
  225. package/src/resources/extensions/shared/next-action-ui.ts +16 -1
@@ -29,7 +29,7 @@ import {
29
29
  buildMilestoneFileName, buildSliceFileName, buildTaskFileName,
30
30
  } from "./paths.js";
31
31
  import { invalidateAllCaches } from "./cache.js";
32
- import { saveActivityLog } from "./activity-log.js";
32
+ import { saveActivityLog, clearActivityLogState } from "./activity-log.js";
33
33
  import { synthesizeCrashRecovery, getDeepDiagnostic } from "./session-forensics.js";
34
34
  import { writeLock, clearLock, readCrashLock, formatCrashInfo, isLockProcessAlive } from "./crash-recovery.js";
35
35
  import {
@@ -92,7 +92,9 @@ import {
92
92
  getAutoWorktreePath,
93
93
  getAutoWorktreeOriginalBase,
94
94
  mergeMilestoneToMain,
95
+ autoWorktreeBranch,
95
96
  } from "./auto-worktree.js";
97
+ import { pruneQueueOrder } from "./queue-order.js";
96
98
  import { showNextAction } from "../shared/next-action-ui.js";
97
99
  import {
98
100
  resolveExpectedArtifactPath,
@@ -196,6 +198,33 @@ function shouldUseWorktreeIsolation(): boolean {
196
198
  return true; // default: worktree
197
199
  }
198
200
 
201
+ /**
202
+ * Detect and escape a stale worktree cwd (#608).
203
+ *
204
+ * After milestone completion + merge, the worktree directory is removed but
205
+ * the process cwd may still point inside `.gsd/worktrees/<MID>/`.
206
+ * When a new session starts, `process.cwd()` is passed as `base` to startAuto
207
+ * and all subsequent writes land in the wrong directory. This function detects
208
+ * that scenario and chdir back to the project root.
209
+ *
210
+ * Returns the corrected base path.
211
+ */
212
+ function escapeStaleWorktree(base: string): string {
213
+ const marker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
214
+ const idx = base.indexOf(marker);
215
+ if (idx === -1) return base;
216
+
217
+ // base is inside .gsd/worktrees/<something> — extract the project root
218
+ const projectRoot = base.slice(0, idx);
219
+ try {
220
+ process.chdir(projectRoot);
221
+ } catch {
222
+ // If chdir fails, return the original — caller will handle errors downstream
223
+ return base;
224
+ }
225
+ return projectRoot;
226
+ }
227
+
199
228
  /** Crash recovery prompt — set by startAuto, consumed by first dispatchNextUnit */
200
229
  let pendingCrashRecovery: string | null = null;
201
230
 
@@ -228,6 +257,9 @@ const DISPATCH_GAP_TIMEOUT_MS = 5_000; // 5 seconds
228
257
  /** SIGTERM handler registered while auto-mode is active — cleared on stop/pause. */
229
258
  let _sigtermHandler: (() => void) | null = null;
230
259
 
260
+ /** Tool calls currently being executed — prevents false idle detection during long-running tools. */
261
+ const inFlightTools = new Set<string>();
262
+
231
263
  type BudgetAlertLevel = 0 | 75 | 90 | 100;
232
264
 
233
265
  export function getBudgetAlertLevel(budgetPct: number): BudgetAlertLevel {
@@ -293,6 +325,57 @@ export function isAutoPaused(): boolean {
293
325
  return paused;
294
326
  }
295
327
 
328
+ /**
329
+ * Mark a tool execution as in-flight. Called from index.ts on tool_execution_start.
330
+ * Prevents the idle watchdog from declaring the agent idle while tools are executing.
331
+ */
332
+ export function markToolStart(toolCallId: string): void {
333
+ if (!active) return;
334
+ inFlightTools.add(toolCallId);
335
+ }
336
+
337
+ /**
338
+ * Mark a tool execution as completed. Called from index.ts on tool_execution_end.
339
+ */
340
+ export function markToolEnd(toolCallId: string): void {
341
+ inFlightTools.delete(toolCallId);
342
+ }
343
+
344
+ /**
345
+ * Return the base path to use for the auto.lock file.
346
+ * Always uses the original project root (not the worktree) so that
347
+ * a second terminal can discover and stop a running auto-mode session.
348
+ */
349
+ function lockBase(): string {
350
+ return originalBasePath || basePath;
351
+ }
352
+
353
+ /**
354
+ * Attempt to stop a running auto-mode session from a different process.
355
+ * Reads the lock file at the project root, checks if the PID is alive,
356
+ * and sends SIGTERM to gracefully stop it.
357
+ *
358
+ * Returns true if a remote session was found and signaled, false otherwise.
359
+ */
360
+ export function stopAutoRemote(projectRoot: string): { found: boolean; pid?: number; error?: string } {
361
+ const lock = readCrashLock(projectRoot);
362
+ if (!lock) return { found: false };
363
+
364
+ if (!isLockProcessAlive(lock)) {
365
+ // Stale lock — clean it up
366
+ clearLock(projectRoot);
367
+ return { found: false };
368
+ }
369
+
370
+ // Send SIGTERM — the auto-mode process has a handler that clears the lock and exits
371
+ try {
372
+ process.kill(lock.pid, "SIGTERM");
373
+ return { found: true, pid: lock.pid };
374
+ } catch (err) {
375
+ return { found: false, error: (err as Error).message };
376
+ }
377
+ }
378
+
296
379
  export function isStepMode(): boolean {
297
380
  return stepMode;
298
381
  }
@@ -310,6 +393,7 @@ function clearUnitTimeout(): void {
310
393
  clearInterval(idleWatchdogHandle);
311
394
  idleWatchdogHandle = null;
312
395
  }
396
+ inFlightTools.clear();
313
397
  clearDispatchGapWatchdog();
314
398
  }
315
399
 
@@ -371,7 +455,7 @@ function startDispatchGapWatchdog(ctx: ExtensionContext, pi: ExtensionAPI): void
371
455
  export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI): Promise<void> {
372
456
  if (!active && !paused) return;
373
457
  clearUnitTimeout();
374
- if (basePath) clearLock(basePath);
458
+ if (lockBase()) clearLock(lockBase());
375
459
  clearSkillSnapshot();
376
460
  _dispatching = false;
377
461
  _skipDepth = 0;
@@ -391,14 +475,18 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI): Promi
391
475
  `Auto-worktree teardown failed: ${err instanceof Error ? err.message : String(err)}`,
392
476
  "warning",
393
477
  );
394
- // Force basePath back to original even if teardown failed
395
- if (originalBasePath) {
396
- basePath = originalBasePath;
397
- try { process.chdir(basePath); } catch { /* best-effort */ }
398
- }
399
478
  }
400
479
  }
401
480
 
481
+ // Always restore cwd to project root on stop (#608).
482
+ // Even if isInAutoWorktree returned false (e.g., module state was already
483
+ // cleared by mergeMilestoneToMain), the process cwd may still be inside
484
+ // the worktree directory. Force it back to originalBasePath.
485
+ if (originalBasePath) {
486
+ basePath = originalBasePath;
487
+ try { process.chdir(basePath); } catch { /* best-effort */ }
488
+ }
489
+
402
490
  const ledger = getLedger();
403
491
  if (ledger && ledger.units.length > 0) {
404
492
  const totals = getProjectTotals(ledger.units);
@@ -423,12 +511,15 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI): Promi
423
511
  stepMode = false;
424
512
  unitDispatchCount.clear();
425
513
  unitRecoveryCount.clear();
514
+ inFlightTools.clear();
426
515
  lastBudgetAlertLevel = 0;
427
516
  unitLifetimeDispatches.clear();
428
517
  currentUnit = null;
429
518
  currentMilestoneId = null;
430
519
  originalBasePath = "";
520
+ completedUnits = [];
431
521
  clearSliceProgressCache();
522
+ clearActivityLogState();
432
523
  pendingCrashRecovery = null;
433
524
  _handlingAgentEnd = false;
434
525
  ctx?.ui.setStatus("gsd-auto", undefined);
@@ -454,7 +545,7 @@ export async function stopAuto(ctx?: ExtensionContext, pi?: ExtensionAPI): Promi
454
545
  export async function pauseAuto(ctx?: ExtensionContext, _pi?: ExtensionAPI): Promise<void> {
455
546
  if (!active) return;
456
547
  clearUnitTimeout();
457
- if (basePath) clearLock(basePath);
548
+ if (lockBase()) clearLock(lockBase());
458
549
 
459
550
  // Remove SIGTERM handler registered at auto-mode start
460
551
  deregisterSigtermHandler();
@@ -484,6 +575,11 @@ export async function startAuto(
484
575
  ): Promise<void> {
485
576
  const requestedStepMode = options?.step ?? false;
486
577
 
578
+ // Escape stale worktree cwd from a previous milestone (#608).
579
+ // After milestone merge + worktree removal, the process cwd may still point
580
+ // inside .gsd/worktrees/<MID>/ — detect and chdir back to project root.
581
+ base = escapeStaleWorktree(base);
582
+
487
583
  // If resuming from paused state, just re-activate and dispatch next unit.
488
584
  // The conversation is still intact — no need to reinitialize everything.
489
585
  if (paused) {
@@ -527,24 +623,24 @@ export async function startAuto(
527
623
  }
528
624
  }
529
625
 
530
- // Re-register SIGTERM handler for the resumed session
531
- registerSigtermHandler(basePath);
626
+ // Re-register SIGTERM handler for the resumed session (use original base for lock)
627
+ registerSigtermHandler(lockBase());
532
628
 
533
629
  ctx.ui.setStatus("gsd-auto", stepMode ? "next" : "auto");
534
630
  ctx.ui.setFooter(hideFooter);
535
631
  ctx.ui.notify(stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
536
632
  // Restore hook state from disk in case session was interrupted
537
- restoreHookState(base);
633
+ restoreHookState(basePath);
538
634
  // Rebuild disk state before resuming — user interaction during pause may have changed files
539
- try { await rebuildState(base); } catch { /* non-fatal */ }
635
+ try { await rebuildState(basePath); } catch { /* non-fatal */ }
540
636
  try {
541
- const report = await runGSDDoctor(base, { fix: true });
637
+ const report = await runGSDDoctor(basePath, { fix: true });
542
638
  if (report.fixesApplied.length > 0) {
543
639
  ctx.ui.notify(`Resume: applied ${report.fixesApplied.length} fix(es) to state.`, "info");
544
640
  }
545
641
  } catch { /* non-fatal */ }
546
642
  // Self-heal: clear stale runtime records where artifacts already exist
547
- await selfHealRuntimeRecords(base, ctx, completedKeySet);
643
+ await selfHealRuntimeRecords(basePath, ctx, completedKeySet);
548
644
  invalidateAllCaches();
549
645
  await dispatchNextUnit(ctx, pi);
550
646
  return;
@@ -557,17 +653,21 @@ export async function startAuto(
557
653
  }
558
654
 
559
655
  // Ensure .gitignore has baseline patterns
560
- ensureGitignore(base);
656
+ const commitDocs = loadEffectiveGSDPreferences()?.preferences?.git?.commit_docs;
657
+ ensureGitignore(base, { commitDocs });
561
658
  untrackRuntimeFiles(base);
562
659
 
563
660
  // Bootstrap .gsd/ if it doesn't exist
564
661
  const gsdDir = join(base, ".gsd");
565
662
  if (!existsSync(gsdDir)) {
566
663
  mkdirSync(join(gsdDir, "milestones"), { recursive: true });
567
- try {
568
- nativeAddPaths(base, [".gsd", ".gitignore"]);
569
- nativeCommit(base, "chore: init gsd");
570
- } catch { /* nothing to commit */ }
664
+ // Only commit .gsd/ init when commit_docs is not explicitly false
665
+ if (commitDocs !== false) {
666
+ try {
667
+ nativeAddPaths(base, [".gsd", ".gitignore"]);
668
+ nativeCommit(base, "chore: init gsd");
669
+ } catch { /* nothing to commit */ }
670
+ }
571
671
  }
572
672
 
573
673
  // Initialize GitServiceImpl — basePath is set and git repo confirmed
@@ -658,7 +758,7 @@ export async function startAuto(
658
758
  // of the repo's default (main/master). Idempotent when the branch is the
659
759
  // same; updates the record when started from a different branch (#300).
660
760
  if (currentMilestoneId) {
661
- captureIntegrationBranch(base, currentMilestoneId);
761
+ captureIntegrationBranch(base, currentMilestoneId, { commitDocs });
662
762
  setActiveMilestoneId(base, currentMilestoneId);
663
763
  }
664
764
 
@@ -695,8 +795,8 @@ export async function startAuto(
695
795
  gitService = new GitServiceImpl(basePath, loadEffectiveGSDPreferences()?.preferences?.git ?? {});
696
796
  ctx.ui.notify(`Created auto-worktree at ${wtPath}`, "info");
697
797
  }
698
- // Re-register SIGTERM handler with the new basePath
699
- registerSigtermHandler(basePath);
798
+ // Re-register SIGTERM handler with the original basePath (lock lives there)
799
+ registerSigtermHandler(originalBasePath);
700
800
  } catch (err) {
701
801
  // Worktree creation is non-fatal — continue in the project root.
702
802
  ctx.ui.notify(
@@ -952,7 +1052,7 @@ export async function handleAgentEnd(
952
1052
  return;
953
1053
  }
954
1054
  const sessionFile = ctx.sessionManager.getSessionFile();
955
- writeLock(basePath, hookUnit.unitType, hookUnit.unitId, completedUnits.length, sessionFile);
1055
+ writeLock(lockBase(), hookUnit.unitType, hookUnit.unitId, completedUnits.length, sessionFile);
956
1056
  // Persist hook state so cycle counts survive crashes
957
1057
  persistHookState(basePath);
958
1058
 
@@ -1211,7 +1311,12 @@ async function dispatchNextUnit(
1211
1311
  unitRecoveryCount.clear();
1212
1312
  unitLifetimeDispatches.clear();
1213
1313
  // Capture integration branch for the new milestone and update git service
1214
- captureIntegrationBranch(originalBasePath || basePath, mid);
1314
+ captureIntegrationBranch(originalBasePath || basePath, mid, { commitDocs: loadEffectiveGSDPreferences()?.preferences?.git?.commit_docs });
1315
+ // Prune completed milestone from queue order file
1316
+ const pendingIds = state.registry
1317
+ .filter(m => m.status !== "complete")
1318
+ .map(m => m.id);
1319
+ pruneQueueOrder(basePath, pendingIds);
1215
1320
  }
1216
1321
  if (mid) {
1217
1322
  currentMilestoneId = mid;
@@ -1292,6 +1397,39 @@ async function dispatchNextUnit(
1292
1397
  `Milestone merge failed: ${err instanceof Error ? err.message : String(err)}`,
1293
1398
  "warning",
1294
1399
  );
1400
+ // Ensure cwd is restored even if merge failed partway through (#608).
1401
+ // mergeMilestoneToMain may have chdir'd but then thrown, leaving us
1402
+ // in an indeterminate location.
1403
+ if (originalBasePath) {
1404
+ basePath = originalBasePath;
1405
+ try { process.chdir(basePath); } catch { /* best-effort */ }
1406
+ }
1407
+ }
1408
+ } else if (currentMilestoneId && !isInAutoWorktree(basePath)) {
1409
+ // Branch isolation mode (#603): no worktree, but we may be on a milestone/* branch.
1410
+ // Squash-merge back to the integration branch (or main) before stopping.
1411
+ try {
1412
+ const currentBranch = getCurrentBranch(basePath);
1413
+ const milestoneBranch = autoWorktreeBranch(currentMilestoneId);
1414
+ if (currentBranch === milestoneBranch) {
1415
+ const roadmapPath = resolveMilestoneFile(basePath, currentMilestoneId, "ROADMAP");
1416
+ if (roadmapPath) {
1417
+ const roadmapContent = readFileSync(roadmapPath, "utf-8");
1418
+ // mergeMilestoneToMain handles: auto-commit, checkout integration branch,
1419
+ // squash merge, commit, optional push, branch deletion.
1420
+ const mergeResult = mergeMilestoneToMain(basePath, currentMilestoneId, roadmapContent);
1421
+ gitService = new GitServiceImpl(basePath, loadEffectiveGSDPreferences()?.preferences?.git ?? {});
1422
+ ctx.ui.notify(
1423
+ `Milestone ${currentMilestoneId} merged (branch mode).${mergeResult.pushed ? " Pushed to remote." : ""}`,
1424
+ "info",
1425
+ );
1426
+ }
1427
+ }
1428
+ } catch (err) {
1429
+ ctx.ui.notify(
1430
+ `Milestone merge failed (branch mode): ${err instanceof Error ? err.message : String(err)}`,
1431
+ "warning",
1432
+ );
1295
1433
  }
1296
1434
  }
1297
1435
  sendDesktopNotification("GSD", `Milestone ${mid} complete!`, "success", "milestone");
@@ -1718,6 +1856,10 @@ async function dispatchNextUnit(
1718
1856
  startedAt: currentUnit.startedAt,
1719
1857
  finishedAt: Date.now(),
1720
1858
  });
1859
+ // Cap to last 200 entries to prevent unbounded growth (#611)
1860
+ if (completedUnits.length > 200) {
1861
+ completedUnits = completedUnits.slice(-200);
1862
+ }
1721
1863
  clearUnitRuntimeRecord(basePath, currentUnit.type, currentUnit.id);
1722
1864
  unitDispatchCount.delete(`${currentUnit.type}/${currentUnit.id}`);
1723
1865
  unitRecoveryCount.delete(`${currentUnit.type}/${currentUnit.id}`);
@@ -1758,7 +1900,7 @@ async function dispatchNextUnit(
1758
1900
  // Pi appends entries incrementally via appendFileSync, so on crash the
1759
1901
  // session file survives with every tool call up to the crash point.
1760
1902
  const sessionFile = ctx.sessionManager.getSessionFile();
1761
- writeLock(basePath, unitType, unitId, completedUnits.length, sessionFile);
1903
+ writeLock(lockBase(), unitType, unitId, completedUnits.length, sessionFile);
1762
1904
 
1763
1905
  // On crash recovery, prepend the full recovery briefing
1764
1906
  // On retry (stuck detection), prepend deep diagnostic from last attempt
@@ -1918,6 +2060,16 @@ async function dispatchNextUnit(
1918
2060
  if (!runtime) return;
1919
2061
  if (Date.now() - runtime.lastProgressAt < idleTimeoutMs) return;
1920
2062
 
2063
+ // Agent has tool calls currently executing (await_job, long bash, etc.) —
2064
+ // not idle, just waiting for tool completion.
2065
+ if (inFlightTools.size > 0) {
2066
+ writeUnitRuntimeRecord(basePath, unitType, unitId, currentUnit.startedAt, {
2067
+ lastProgressAt: Date.now(),
2068
+ lastProgressKind: "tool-in-flight",
2069
+ });
2070
+ return;
2071
+ }
2072
+
1921
2073
  // Before triggering recovery, check if the agent is actually producing
1922
2074
  // work on disk. `git status --porcelain` is cheap and catches any
1923
2075
  // staged/unstaged/untracked changes the agent made since lastProgressAt.