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.
- package/README.md +2 -1
- package/dist/cli.js +12 -3
- package/dist/headless.d.ts +4 -0
- package/dist/headless.js +118 -10
- package/dist/help-text.js +22 -7
- package/dist/models-resolver.d.ts +0 -11
- package/dist/models-resolver.js +0 -15
- package/dist/resource-loader.d.ts +0 -1
- package/dist/resource-loader.js +64 -18
- package/dist/resources/GSD-WORKFLOW.md +12 -9
- package/dist/resources/extensions/bg-shell/overlay.ts +18 -17
- package/dist/resources/extensions/get-secrets-from-user.ts +5 -23
- package/dist/resources/extensions/gsd/activity-log.ts +5 -3
- package/dist/resources/extensions/gsd/auto-dispatch.ts +51 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +87 -0
- package/dist/resources/extensions/gsd/auto-recovery.ts +41 -2
- package/dist/resources/extensions/gsd/auto-worktree.ts +134 -4
- package/dist/resources/extensions/gsd/auto.ts +307 -77
- package/dist/resources/extensions/gsd/cache.ts +3 -1
- package/dist/resources/extensions/gsd/commands.ts +176 -10
- package/dist/resources/extensions/gsd/complexity.ts +1 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +38 -0
- package/dist/resources/extensions/gsd/doctor.ts +58 -11
- package/dist/resources/extensions/gsd/exit-command.ts +2 -2
- package/dist/resources/extensions/gsd/git-service.ts +74 -14
- package/dist/resources/extensions/gsd/gitignore.ts +1 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +78 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +109 -12
- package/dist/resources/extensions/gsd/index.ts +48 -2
- package/dist/resources/extensions/gsd/memory-extractor.ts +352 -0
- package/dist/resources/extensions/gsd/memory-store.ts +441 -0
- package/dist/resources/extensions/gsd/migrate/command.ts +2 -2
- package/dist/resources/extensions/gsd/parallel-eligibility.ts +233 -0
- package/dist/resources/extensions/gsd/parallel-merge.ts +156 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
- package/dist/resources/extensions/gsd/preferences.ts +65 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +4 -4
- package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
- package/dist/resources/extensions/gsd/provider-error-pause.ts +29 -2
- package/dist/resources/extensions/gsd/session-status-io.ts +197 -0
- package/dist/resources/extensions/gsd/state.ts +72 -30
- package/dist/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
- package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
- package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +256 -2
- package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
- package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
- package/dist/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +70 -4
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
- package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
- package/dist/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
- package/dist/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
- package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
- package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
- package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
- package/dist/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +1 -1
- package/dist/resources/extensions/gsd/types.ts +15 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +291 -10
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +237 -28
- package/dist/resources/extensions/gsd/visualizer-views.ts +462 -48
- package/dist/resources/extensions/gsd/worktree.ts +9 -2
- package/dist/resources/extensions/search-the-web/native-search.ts +15 -5
- package/dist/resources/extensions/subagent/index.ts +5 -0
- package/dist/resources/extensions/subagent/worker-registry.ts +99 -0
- package/dist/update-check.d.ts +9 -0
- package/dist/update-check.js +97 -0
- package/package.json +6 -1
- package/packages/pi-agent-core/dist/agent-loop.js +2 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +55 -7
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -4
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.js +21 -9
- package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/packages/pi-ai/dist/providers/mistral.js +3 -0
- package/packages/pi-ai/dist/providers/mistral.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +12 -4
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +12 -4
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +23 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic.ts +59 -9
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +16 -4
- package/packages/pi-ai/src/providers/google-vertex.ts +32 -17
- package/packages/pi-ai/src/providers/mistral.ts +3 -0
- package/packages/pi-ai/src/providers/openai-completions.ts +16 -4
- package/packages/pi-ai/src/providers/openai-responses.ts +16 -4
- package/packages/pi-ai/src/types.ts +19 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +72 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
- package/packages/pi-coding-agent/src/core/settings-manager.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +84 -0
- package/scripts/postinstall.js +7 -109
- package/src/resources/GSD-WORKFLOW.md +12 -9
- package/src/resources/extensions/bg-shell/overlay.ts +18 -17
- package/src/resources/extensions/get-secrets-from-user.ts +5 -23
- package/src/resources/extensions/gsd/activity-log.ts +5 -3
- package/src/resources/extensions/gsd/auto-dispatch.ts +51 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +87 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +41 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +134 -4
- package/src/resources/extensions/gsd/auto.ts +307 -77
- package/src/resources/extensions/gsd/cache.ts +3 -1
- package/src/resources/extensions/gsd/commands.ts +176 -10
- package/src/resources/extensions/gsd/complexity.ts +1 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +38 -0
- package/src/resources/extensions/gsd/doctor.ts +58 -11
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +74 -14
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/gsd-db.ts +78 -1
- package/src/resources/extensions/gsd/guided-flow.ts +109 -12
- package/src/resources/extensions/gsd/index.ts +48 -2
- package/src/resources/extensions/gsd/memory-extractor.ts +352 -0
- package/src/resources/extensions/gsd/memory-store.ts +441 -0
- package/src/resources/extensions/gsd/migrate/command.ts +2 -2
- package/src/resources/extensions/gsd/parallel-eligibility.ts +233 -0
- package/src/resources/extensions/gsd/parallel-merge.ts +156 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
- package/src/resources/extensions/gsd/preferences.ts +65 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +4 -4
- package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
- package/src/resources/extensions/gsd/provider-error-pause.ts +29 -2
- package/src/resources/extensions/gsd/session-status-io.ts +197 -0
- package/src/resources/extensions/gsd/state.ts +72 -30
- package/src/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +256 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +70 -4
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
- package/src/resources/extensions/gsd/triage-ui.ts +1 -1
- package/src/resources/extensions/gsd/types.ts +15 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +291 -10
- package/src/resources/extensions/gsd/visualizer-overlay.ts +237 -28
- package/src/resources/extensions/gsd/visualizer-views.ts +462 -48
- package/src/resources/extensions/gsd/worktree.ts +9 -2
- package/src/resources/extensions/search-the-web/native-search.ts +15 -5
- package/src/resources/extensions/subagent/index.ts +5 -0
- 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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|