gsd-pi 2.23.0 → 2.25.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 (212) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.js +12 -3
  3. package/dist/headless.d.ts +4 -0
  4. package/dist/headless.js +118 -10
  5. package/dist/help-text.js +22 -7
  6. package/dist/models-resolver.d.ts +0 -11
  7. package/dist/models-resolver.js +0 -15
  8. package/dist/resource-loader.d.ts +0 -1
  9. package/dist/resource-loader.js +64 -18
  10. package/dist/resources/GSD-WORKFLOW.md +12 -9
  11. package/dist/resources/extensions/bg-shell/overlay.ts +18 -17
  12. package/dist/resources/extensions/get-secrets-from-user.ts +5 -23
  13. package/dist/resources/extensions/gsd/activity-log.ts +5 -3
  14. package/dist/resources/extensions/gsd/auto-dispatch.ts +51 -2
  15. package/dist/resources/extensions/gsd/auto-prompts.ts +87 -0
  16. package/dist/resources/extensions/gsd/auto-recovery.ts +41 -2
  17. package/dist/resources/extensions/gsd/auto-worktree.ts +134 -4
  18. package/dist/resources/extensions/gsd/auto.ts +307 -77
  19. package/dist/resources/extensions/gsd/cache.ts +3 -1
  20. package/dist/resources/extensions/gsd/commands.ts +176 -10
  21. package/dist/resources/extensions/gsd/complexity.ts +1 -0
  22. package/dist/resources/extensions/gsd/dashboard-overlay.ts +38 -0
  23. package/dist/resources/extensions/gsd/doctor.ts +58 -11
  24. package/dist/resources/extensions/gsd/exit-command.ts +2 -2
  25. package/dist/resources/extensions/gsd/git-service.ts +74 -14
  26. package/dist/resources/extensions/gsd/gitignore.ts +1 -0
  27. package/dist/resources/extensions/gsd/gsd-db.ts +78 -1
  28. package/dist/resources/extensions/gsd/guided-flow.ts +109 -12
  29. package/dist/resources/extensions/gsd/index.ts +48 -2
  30. package/dist/resources/extensions/gsd/memory-extractor.ts +352 -0
  31. package/dist/resources/extensions/gsd/memory-store.ts +441 -0
  32. package/dist/resources/extensions/gsd/migrate/command.ts +2 -2
  33. package/dist/resources/extensions/gsd/parallel-eligibility.ts +233 -0
  34. package/dist/resources/extensions/gsd/parallel-merge.ts +156 -0
  35. package/dist/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
  36. package/dist/resources/extensions/gsd/preferences.ts +65 -1
  37. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  38. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
  39. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -4
  40. package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
  41. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  42. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  43. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  47. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
  48. package/dist/resources/extensions/gsd/provider-error-pause.ts +29 -2
  49. package/dist/resources/extensions/gsd/session-status-io.ts +197 -0
  50. package/dist/resources/extensions/gsd/state.ts +72 -30
  51. package/dist/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
  52. package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
  53. package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
  54. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +256 -2
  55. package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
  56. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
  57. package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
  58. package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
  59. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
  60. package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
  61. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
  62. package/dist/resources/extensions/gsd/tests/git-service.test.ts +70 -4
  63. package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
  64. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
  65. package/dist/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
  66. package/dist/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
  67. package/dist/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
  68. package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
  69. package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
  70. package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
  71. package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
  72. package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
  73. package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
  74. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
  75. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
  76. package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
  77. package/dist/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
  78. package/dist/resources/extensions/gsd/triage-ui.ts +1 -1
  79. package/dist/resources/extensions/gsd/types.ts +15 -1
  80. package/dist/resources/extensions/gsd/visualizer-data.ts +291 -10
  81. package/dist/resources/extensions/gsd/visualizer-overlay.ts +237 -28
  82. package/dist/resources/extensions/gsd/visualizer-views.ts +462 -48
  83. package/dist/resources/extensions/gsd/worktree.ts +9 -2
  84. package/dist/resources/extensions/search-the-web/native-search.ts +15 -5
  85. package/dist/resources/extensions/subagent/index.ts +5 -0
  86. package/dist/resources/extensions/subagent/worker-registry.ts +99 -0
  87. package/dist/update-check.d.ts +9 -0
  88. package/dist/update-check.js +97 -0
  89. package/package.json +6 -1
  90. package/packages/pi-agent-core/dist/agent-loop.js +2 -0
  91. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  92. package/packages/pi-agent-core/src/agent-loop.ts +2 -0
  93. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  94. package/packages/pi-ai/dist/providers/anthropic.js +55 -7
  95. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  96. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  97. package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -4
  98. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
  100. package/packages/pi-ai/dist/providers/google-vertex.js +21 -9
  101. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  102. package/packages/pi-ai/dist/providers/mistral.js +3 -0
  103. package/packages/pi-ai/dist/providers/mistral.js.map +1 -1
  104. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  105. package/packages/pi-ai/dist/providers/openai-completions.js +12 -4
  106. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  107. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  108. package/packages/pi-ai/dist/providers/openai-responses.js +12 -4
  109. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  110. package/packages/pi-ai/dist/types.d.ts +23 -1
  111. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  112. package/packages/pi-ai/dist/types.js.map +1 -1
  113. package/packages/pi-ai/src/providers/anthropic.ts +59 -9
  114. package/packages/pi-ai/src/providers/azure-openai-responses.ts +16 -4
  115. package/packages/pi-ai/src/providers/google-vertex.ts +32 -17
  116. package/packages/pi-ai/src/providers/mistral.ts +3 -0
  117. package/packages/pi-ai/src/providers/openai-completions.ts +16 -4
  118. package/packages/pi-ai/src/providers/openai-responses.ts +16 -4
  119. package/packages/pi-ai/src/types.ts +19 -1
  120. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  121. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  123. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -0
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +72 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  131. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  132. package/packages/pi-coding-agent/src/core/settings-manager.ts +2 -2
  133. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -0
  134. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +84 -0
  135. package/scripts/postinstall.js +7 -109
  136. package/src/resources/GSD-WORKFLOW.md +12 -9
  137. package/src/resources/extensions/bg-shell/overlay.ts +18 -17
  138. package/src/resources/extensions/get-secrets-from-user.ts +5 -23
  139. package/src/resources/extensions/gsd/activity-log.ts +5 -3
  140. package/src/resources/extensions/gsd/auto-dispatch.ts +51 -2
  141. package/src/resources/extensions/gsd/auto-prompts.ts +87 -0
  142. package/src/resources/extensions/gsd/auto-recovery.ts +41 -2
  143. package/src/resources/extensions/gsd/auto-worktree.ts +134 -4
  144. package/src/resources/extensions/gsd/auto.ts +307 -77
  145. package/src/resources/extensions/gsd/cache.ts +3 -1
  146. package/src/resources/extensions/gsd/commands.ts +176 -10
  147. package/src/resources/extensions/gsd/complexity.ts +1 -0
  148. package/src/resources/extensions/gsd/dashboard-overlay.ts +38 -0
  149. package/src/resources/extensions/gsd/doctor.ts +58 -11
  150. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  151. package/src/resources/extensions/gsd/git-service.ts +74 -14
  152. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  153. package/src/resources/extensions/gsd/gsd-db.ts +78 -1
  154. package/src/resources/extensions/gsd/guided-flow.ts +109 -12
  155. package/src/resources/extensions/gsd/index.ts +48 -2
  156. package/src/resources/extensions/gsd/memory-extractor.ts +352 -0
  157. package/src/resources/extensions/gsd/memory-store.ts +441 -0
  158. package/src/resources/extensions/gsd/migrate/command.ts +2 -2
  159. package/src/resources/extensions/gsd/parallel-eligibility.ts +233 -0
  160. package/src/resources/extensions/gsd/parallel-merge.ts +156 -0
  161. package/src/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
  162. package/src/resources/extensions/gsd/preferences.ts +65 -1
  163. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  164. package/src/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
  165. package/src/resources/extensions/gsd/prompts/discuss.md +4 -4
  166. package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
  167. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  168. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  169. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  170. package/src/resources/extensions/gsd/prompts/queue.md +1 -1
  171. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  172. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  173. package/src/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
  174. package/src/resources/extensions/gsd/provider-error-pause.ts +29 -2
  175. package/src/resources/extensions/gsd/session-status-io.ts +197 -0
  176. package/src/resources/extensions/gsd/state.ts +72 -30
  177. package/src/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
  178. package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
  179. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
  180. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +256 -2
  181. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
  182. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
  183. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
  184. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
  185. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
  186. package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
  187. package/src/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
  188. package/src/resources/extensions/gsd/tests/git-service.test.ts +70 -4
  189. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
  190. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
  191. package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
  192. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
  193. package/src/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
  194. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
  195. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
  196. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
  197. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
  198. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
  199. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
  200. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
  201. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
  202. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
  203. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
  204. package/src/resources/extensions/gsd/triage-ui.ts +1 -1
  205. package/src/resources/extensions/gsd/types.ts +15 -1
  206. package/src/resources/extensions/gsd/visualizer-data.ts +291 -10
  207. package/src/resources/extensions/gsd/visualizer-overlay.ts +237 -28
  208. package/src/resources/extensions/gsd/visualizer-views.ts +462 -48
  209. package/src/resources/extensions/gsd/worktree.ts +9 -2
  210. package/src/resources/extensions/search-the-web/native-search.ts +15 -5
  211. package/src/resources/extensions/subagent/index.ts +5 -0
  212. package/src/resources/extensions/subagent/worker-registry.ts +99 -0
@@ -161,7 +161,7 @@ function openRawDb(path: string): unknown {
161
161
 
162
162
  // ─── Schema ────────────────────────────────────────────────────────────────
163
163
 
164
- const SCHEMA_VERSION = 2;
164
+ const SCHEMA_VERSION = 3;
165
165
 
166
166
  function initSchema(db: DbAdapter, fileBacked: boolean): void {
167
167
  // WAL mode for file-backed databases (must be outside transaction)
@@ -221,9 +221,36 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
221
221
  )
222
222
  `);
223
223
 
224
+ db.exec(`
225
+ CREATE TABLE IF NOT EXISTS memories (
226
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
227
+ id TEXT NOT NULL UNIQUE,
228
+ category TEXT NOT NULL,
229
+ content TEXT NOT NULL,
230
+ confidence REAL NOT NULL DEFAULT 0.8,
231
+ source_unit_type TEXT,
232
+ source_unit_id TEXT,
233
+ created_at TEXT NOT NULL,
234
+ updated_at TEXT NOT NULL,
235
+ superseded_by TEXT DEFAULT NULL,
236
+ hit_count INTEGER NOT NULL DEFAULT 0
237
+ )
238
+ `);
239
+
240
+ db.exec(`
241
+ CREATE TABLE IF NOT EXISTS memory_processed_units (
242
+ unit_key TEXT PRIMARY KEY,
243
+ activity_file TEXT,
244
+ processed_at TEXT NOT NULL
245
+ )
246
+ `);
247
+
248
+ db.exec('CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)');
249
+
224
250
  // Views — DROP + CREATE since CREATE VIEW IF NOT EXISTS doesn't update definitions
225
251
  db.exec(`CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`);
226
252
  db.exec(`CREATE VIEW IF NOT EXISTS active_requirements AS SELECT * FROM requirements WHERE superseded_by IS NULL`);
253
+ db.exec(`CREATE VIEW IF NOT EXISTS active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL`);
227
254
 
228
255
  // Insert schema version if not already present
229
256
  const existing = db.prepare('SELECT count(*) as cnt FROM schema_version').get();
@@ -274,6 +301,41 @@ function migrateSchema(db: DbAdapter): void {
274
301
  );
275
302
  }
276
303
 
304
+ // v2 → v3: add memories + memory_processed_units tables
305
+ if (currentVersion < 3) {
306
+ db.exec(`
307
+ CREATE TABLE IF NOT EXISTS memories (
308
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,
309
+ id TEXT NOT NULL UNIQUE,
310
+ category TEXT NOT NULL,
311
+ content TEXT NOT NULL,
312
+ confidence REAL NOT NULL DEFAULT 0.8,
313
+ source_unit_type TEXT,
314
+ source_unit_id TEXT,
315
+ created_at TEXT NOT NULL,
316
+ updated_at TEXT NOT NULL,
317
+ superseded_by TEXT DEFAULT NULL,
318
+ hit_count INTEGER NOT NULL DEFAULT 0
319
+ )
320
+ `);
321
+
322
+ db.exec(`
323
+ CREATE TABLE IF NOT EXISTS memory_processed_units (
324
+ unit_key TEXT PRIMARY KEY,
325
+ activity_file TEXT,
326
+ processed_at TEXT NOT NULL
327
+ )
328
+ `);
329
+
330
+ db.exec('CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)');
331
+ db.exec('DROP VIEW IF EXISTS active_memories');
332
+ db.exec('CREATE VIEW active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL');
333
+
334
+ db.prepare('INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)').run(
335
+ { ':version': 3, ':applied_at': new Date().toISOString() },
336
+ );
337
+ }
338
+
277
339
  db.exec('COMMIT');
278
340
  } catch (err) {
279
341
  db.exec('ROLLBACK');
@@ -728,6 +790,21 @@ export function upsertRequirement(r: Requirement): void {
728
790
  /**
729
791
  * Insert or replace an artifact. Uses the `path` PK for idempotency.
730
792
  */
793
+ /**
794
+ * Delete all rows from the artifacts table.
795
+ * The artifacts table is a read cache — clearing it forces the next
796
+ * deriveState() to fall through to disk reads (native Rust batch parse).
797
+ * Safe to call when no database is open (no-op).
798
+ */
799
+ export function clearArtifacts(): void {
800
+ if (!currentDb) return;
801
+ try {
802
+ currentDb.exec('DELETE FROM artifacts');
803
+ } catch {
804
+ // Clearing a cache should never be fatal
805
+ }
806
+ }
807
+
731
808
  export function insertArtifact(a: {
732
809
  path: string;
733
810
  artifact_type: string;
@@ -29,6 +29,17 @@ import { loadEffectiveGSDPreferences } from "./preferences.js";
29
29
  import { showConfirm } from "../shared/confirm-ui.js";
30
30
  import { loadQueueOrder, sortByQueueOrder, saveQueueOrder } from "./queue-order.js";
31
31
 
32
+ // ─── Commit Instruction Helpers ──────────────────────────────────────────────
33
+
34
+ /** Build conditional commit instruction for planning prompts based on commit_docs preference. */
35
+ function buildDocsCommitInstruction(message: string): string {
36
+ const prefs = loadEffectiveGSDPreferences();
37
+ const commitDocsEnabled = prefs?.preferences?.git?.commit_docs !== false;
38
+ return commitDocsEnabled
39
+ ? `Commit: \`${message}\``
40
+ : "Do not commit — planning docs are not tracked in git for this project.";
41
+ }
42
+
32
43
  // ─── Auto-start after discuss ─────────────────────────────────────────────────
33
44
 
34
45
  /** Stashed context + flag for auto-starting after discuss phase completes */
@@ -198,9 +209,88 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string)
198
209
  contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
199
210
  roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
200
211
  inlinedTemplates,
212
+ commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
213
+ multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
214
+ });
215
+ }
216
+
217
+ /**
218
+ * Build the discuss prompt for headless milestone creation.
219
+ * Uses the discuss-headless prompt template with seed context injected.
220
+ */
221
+ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePath: string): string {
222
+ const milestoneRel = `.gsd/milestones/${nextId}`;
223
+ const inlinedTemplates = [
224
+ inlineTemplate("project", "Project"),
225
+ inlineTemplate("requirements", "Requirements"),
226
+ inlineTemplate("context", "Context"),
227
+ inlineTemplate("roadmap", "Roadmap"),
228
+ inlineTemplate("decisions", "Decisions"),
229
+ ].join("\n\n---\n\n");
230
+ return loadPrompt("discuss-headless", {
231
+ milestoneId: nextId,
232
+ seedContext,
233
+ contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
234
+ roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
235
+ inlinedTemplates,
236
+ commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
237
+ multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
201
238
  });
202
239
  }
203
240
 
241
+ /**
242
+ * Bootstrap a .gsd/ project from scratch for headless use.
243
+ * Ensures git repo, .gsd/ structure, gitignore, and preferences all exist.
244
+ */
245
+ function bootstrapGsdProject(basePath: string): void {
246
+ if (!nativeIsRepo(basePath)) {
247
+ const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main";
248
+ nativeInit(basePath, mainBranch);
249
+ }
250
+
251
+ const root = gsdRoot(basePath);
252
+ mkdirSync(join(root, "milestones"), { recursive: true });
253
+ mkdirSync(join(root, "runtime"), { recursive: true });
254
+
255
+ const commitDocs = loadEffectiveGSDPreferences()?.preferences?.git?.commit_docs;
256
+ ensureGitignore(basePath, { commitDocs });
257
+ ensurePreferences(basePath);
258
+ untrackRuntimeFiles(basePath);
259
+ }
260
+
261
+ /**
262
+ * Headless milestone creation from a seed specification document.
263
+ * Bootstraps the project if needed, generates the next milestone ID,
264
+ * and dispatches the headless discuss prompt (no Q&A rounds).
265
+ */
266
+ export async function showHeadlessMilestoneCreation(
267
+ ctx: ExtensionCommandContext,
268
+ pi: ExtensionAPI,
269
+ basePath: string,
270
+ seedContext: string,
271
+ ): Promise<void> {
272
+ // Ensure .gsd/ is bootstrapped
273
+ bootstrapGsdProject(basePath);
274
+
275
+ // Generate next milestone ID
276
+ const existingIds = findMilestoneIds(basePath);
277
+ const prefs = loadEffectiveGSDPreferences();
278
+ const nextId = nextMilestoneId(existingIds, prefs?.preferences?.unique_milestone_ids ?? false);
279
+
280
+ // Create milestone directory
281
+ const milestoneDir = join(basePath, ".gsd", "milestones", nextId, "slices");
282
+ mkdirSync(milestoneDir, { recursive: true });
283
+
284
+ // Build and dispatch the headless discuss prompt
285
+ const prompt = buildHeadlessDiscussPrompt(nextId, seedContext, basePath);
286
+
287
+ // Set pending auto start (auto-mode triggers on "Milestone X ready." via checkAutoStartAfterDiscuss)
288
+ pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId };
289
+
290
+ // Dispatch
291
+ dispatchWorkflow(pi, prompt);
292
+ }
293
+
204
294
  export function findMilestoneIds(basePath: string): string[] {
205
295
  const dir = milestonesDir(basePath);
206
296
  try {
@@ -573,6 +663,7 @@ async function showQueueAdd(
573
663
  nextIdPlus1,
574
664
  existingMilestonesContext: existingContext,
575
665
  inlinedTemplates: queueInlinedTemplates,
666
+ commitInstruction: buildDocsCommitInstruction("docs: queue <milestone list>"),
576
667
  });
577
668
 
578
669
  pi.sendMessage(
@@ -759,6 +850,7 @@ async function buildDiscussSlicePrompt(
759
850
  contextPath: sliceContextPath,
760
851
  projectRoot: base,
761
852
  inlinedTemplates,
853
+ commitInstruction: buildDocsCommitInstruction(`docs(${mid}/${sid}): slice context from discuss`),
762
854
  });
763
855
  }
764
856
 
@@ -795,7 +887,7 @@ export async function showDiscuss(
795
887
  const draftFile = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
796
888
  const draftContent = draftFile ? await loadFile(draftFile) : null;
797
889
 
798
- const choice = await showNextAction(ctx as any, {
890
+ const choice = await showNextAction(ctx, {
799
891
  title: `GSD — ${mid}: ${milestoneTitle}`,
800
892
  summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
801
893
  actions: [
@@ -824,6 +916,7 @@ export async function showDiscuss(
824
916
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
825
917
  const basePrompt = loadPrompt("guided-discuss-milestone", {
826
918
  milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
919
+ commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
827
920
  });
828
921
  const seed = draftContent
829
922
  ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
@@ -836,6 +929,7 @@ export async function showDiscuss(
836
929
  pendingAutoStart = { ctx, pi, basePath, milestoneId: mid, step: false };
837
930
  dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
838
931
  milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
932
+ commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
839
933
  }), "gsd-discuss");
840
934
  } else if (choice === "skip_milestone") {
841
935
  const milestoneIds = findMilestoneIds(basePath);
@@ -872,7 +966,7 @@ export async function showDiscuss(
872
966
  recommended: i === 0,
873
967
  }));
874
968
 
875
- const choice = await showNextAction(ctx as any, {
969
+ const choice = await showNextAction(ctx, {
876
970
  title: "GSD — Discuss a slice",
877
971
  summary: [
878
972
  `${mid}: ${milestoneTitle}`,
@@ -981,7 +1075,7 @@ export async function showSmartEntry(
981
1075
  const crashLock = readCrashLock(basePath);
982
1076
  if (crashLock) {
983
1077
  clearLock(basePath);
984
- const resume = await showNextAction(ctx as any, {
1078
+ const resume = await showNextAction(ctx, {
985
1079
  title: "GSD — Interrupted Session Detected",
986
1080
  summary: [formatCrashInfo(crashLock)],
987
1081
  actions: [
@@ -1041,7 +1135,7 @@ export async function showSmartEntry(
1041
1135
  basePath
1042
1136
  ));
1043
1137
  } else {
1044
- const choice = await showNextAction(ctx as any, {
1138
+ const choice = await showNextAction(ctx, {
1045
1139
  title: "GSD — Get Shit Done",
1046
1140
  summary: ["No active milestone."],
1047
1141
  actions: [
@@ -1071,7 +1165,7 @@ export async function showSmartEntry(
1071
1165
 
1072
1166
  // ── All milestones complete → New milestone ──────────────────────────
1073
1167
  if (state.phase === "complete") {
1074
- const choice = await showNextAction(ctx as any, {
1168
+ const choice = await showNextAction(ctx, {
1075
1169
  title: `GSD — ${milestoneId}: ${milestoneTitle}`,
1076
1170
  summary: ["All milestones complete."],
1077
1171
  actions: [
@@ -1112,7 +1206,7 @@ export async function showSmartEntry(
1112
1206
  const draftFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT-DRAFT");
1113
1207
  const draftContent = draftFile ? await loadFile(draftFile) : null;
1114
1208
 
1115
- const choice = await showNextAction(ctx as any, {
1209
+ const choice = await showNextAction(ctx, {
1116
1210
  title: `GSD — ${milestoneId}: ${milestoneTitle}`,
1117
1211
  summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
1118
1212
  actions: [
@@ -1141,6 +1235,7 @@ export async function showSmartEntry(
1141
1235
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
1142
1236
  const basePrompt = loadPrompt("guided-discuss-milestone", {
1143
1237
  milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1238
+ commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
1144
1239
  });
1145
1240
  const seed = draftContent
1146
1241
  ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
@@ -1153,6 +1248,7 @@ export async function showSmartEntry(
1153
1248
  pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
1154
1249
  dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1155
1250
  milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1251
+ commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
1156
1252
  }), "gsd-discuss");
1157
1253
  } else if (choice === "skip_milestone") {
1158
1254
  const milestoneIds = findMilestoneIds(basePath);
@@ -1203,7 +1299,7 @@ export async function showSmartEntry(
1203
1299
  },
1204
1300
  ];
1205
1301
 
1206
- const choice = await showNextAction(ctx as any, {
1302
+ const choice = await showNextAction(ctx, {
1207
1303
  title: `GSD — ${milestoneId}: ${milestoneTitle}`,
1208
1304
  summary: [hasContext ? "Context captured. Ready to create roadmap." : "New milestone — no roadmap yet."],
1209
1305
  actions,
@@ -1227,6 +1323,7 @@ export async function showSmartEntry(
1227
1323
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
1228
1324
  dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1229
1325
  milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1326
+ commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
1230
1327
  }));
1231
1328
  } else if (choice === "skip_milestone") {
1232
1329
  const milestoneIds = findMilestoneIds(basePath);
@@ -1240,7 +1337,7 @@ export async function showSmartEntry(
1240
1337
  } else if (choice === "discard_milestone") {
1241
1338
  const mDir = resolveMilestonePath(basePath, milestoneId);
1242
1339
  if (!mDir) return;
1243
- const confirmed = await showConfirm(ctx as any, {
1340
+ const confirmed = await showConfirm(ctx, {
1244
1341
  title: "Discard milestone?",
1245
1342
  message: `This will permanently delete ${milestoneId} and all its contents.`,
1246
1343
  confirmLabel: "Discard",
@@ -1267,7 +1364,7 @@ export async function showSmartEntry(
1267
1364
  },
1268
1365
  ];
1269
1366
 
1270
- const choice = await showNextAction(ctx as any, {
1367
+ const choice = await showNextAction(ctx, {
1271
1368
  title: `GSD — ${milestoneId}: ${milestoneTitle}`,
1272
1369
  summary: ["Roadmap exists. Ready to execute."],
1273
1370
  actions,
@@ -1325,7 +1422,7 @@ export async function showSmartEntry(
1325
1422
  ? `${sliceId}: ${sliceTitle} (${summaryParts.join(", ")})`
1326
1423
  : `${sliceId}: ${sliceTitle} — ready for planning.`;
1327
1424
 
1328
- const choice = await showNextAction(ctx as any, {
1425
+ const choice = await showNextAction(ctx, {
1329
1426
  title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
1330
1427
  summary: [summaryLine],
1331
1428
  actions,
@@ -1356,7 +1453,7 @@ export async function showSmartEntry(
1356
1453
 
1357
1454
  // ── All tasks done → Complete slice ──────────────────────────────────
1358
1455
  if (state.phase === "summarizing") {
1359
- const choice = await showNextAction(ctx as any, {
1456
+ const choice = await showNextAction(ctx, {
1360
1457
  title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
1361
1458
  summary: ["All tasks complete. Ready for slice summary."],
1362
1459
  actions: [
@@ -1400,7 +1497,7 @@ export async function showSmartEntry(
1400
1497
  const hasInterrupted = !!(continueFile && await loadFile(continueFile)) ||
1401
1498
  !!(sDir && await loadFile(join(sDir, "continue.md")));
1402
1499
 
1403
- const choice = await showNextAction(ctx as any, {
1500
+ const choice = await showNextAction(ctx, {
1404
1501
  title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
1405
1502
  summary: [
1406
1503
  hasInterrupted
@@ -129,6 +129,23 @@ export default function (pi: ExtensionAPI) {
129
129
  registerWorktreeCommand(pi);
130
130
  registerExitCommand(pi);
131
131
 
132
+ // ── EPIPE guard — prevent crash when stdout/stderr pipe closes unexpectedly ──
133
+ // Node.js throws a fatal `Error: write EPIPE` when the parent process closes
134
+ // its end of the stdio pipe (e.g. during shell/IPC teardown) while auto-mode
135
+ // is still writing diagnostics. Catching this here gives auto-mode a clean
136
+ // chance to persist state and pause instead of crashing (see issue #739).
137
+ if (!process.listeners("uncaughtException").some(l => l.name === "_gsdEpipeGuard")) {
138
+ const _gsdEpipeGuard = (err: Error): void => {
139
+ if ((err as NodeJS.ErrnoException).code === "EPIPE") {
140
+ // Pipe closed — nothing we can write; just exit cleanly
141
+ process.exit(0);
142
+ }
143
+ // Re-throw anything that isn't EPIPE so real crashes still surface
144
+ throw err;
145
+ };
146
+ process.on("uncaughtException", _gsdEpipeGuard);
147
+ }
148
+
132
149
  // ── /kill — immediate exit (bypass cleanup) ─────────────────────────────
133
150
  pi.registerCommand("kill", {
134
151
  description: "Exit GSD immediately (no cleanup)",
@@ -549,6 +566,19 @@ export default function (pi: ExtensionAPI) {
549
566
  }
550
567
  }
551
568
 
569
+ // Inject auto-learned project memories
570
+ let memoryBlock = "";
571
+ try {
572
+ const { getActiveMemoriesRanked, formatMemoriesForPrompt } = await import("./memory-store.js");
573
+ const memories = getActiveMemoriesRanked(30);
574
+ if (memories.length > 0) {
575
+ const formatted = formatMemoriesForPrompt(memories, 2000);
576
+ if (formatted) {
577
+ memoryBlock = `\n\n${formatted}`;
578
+ }
579
+ }
580
+ } catch { /* non-fatal */ }
581
+
552
582
  // Detect skills installed during this auto-mode session
553
583
  let newSkillsBlock = "";
554
584
  if (hasSkillSnapshot()) {
@@ -608,7 +638,7 @@ export default function (pi: ExtensionAPI) {
608
638
  ].join("\n");
609
639
  }
610
640
 
611
- const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${agentInstructionsBlock}${knowledgeBlock}${newSkillsBlock}${worktreeBlock}`;
641
+ const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${agentInstructionsBlock}${knowledgeBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}`;
612
642
  stopContextTimer({
613
643
  systemPromptSize: fullSystem.length,
614
644
  injectionSize: injection?.length ?? 0,
@@ -699,7 +729,23 @@ export default function (pi: ExtensionAPI) {
699
729
  }
700
730
  }
701
731
 
702
- await pauseAutoForProviderError(ctx.ui, errorDetail, () => pauseAuto(ctx, pi));
732
+ // Detect rate-limit errors and extract retry delay for auto-resume
733
+ const errorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
734
+ const isRateLimit = /rate.?limit|too many requests|429/i.test(errorMsg);
735
+ const retryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number")
736
+ ? lastMsg.retryAfterMs
737
+ : (() => { const m = errorMsg.match(/reset in (\d+)s/i); return m ? Number(m[1]) * 1000 : undefined; })();
738
+
739
+ await pauseAutoForProviderError(ctx.ui, errorDetail, () => pauseAuto(ctx, pi), {
740
+ isRateLimit,
741
+ retryAfterMs,
742
+ resume: () => {
743
+ pi.sendMessage(
744
+ { customType: "gsd-auto-timeout-recovery", content: "Continue execution \u2014 rate limit window elapsed.", display: false },
745
+ { triggerTurn: true },
746
+ );
747
+ },
748
+ });
703
749
  return;
704
750
  }
705
751