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
|
@@ -14,9 +14,11 @@ import type { GSDPreferences } from "./preferences.js";
|
|
|
14
14
|
import type { UatType } from "./files.js";
|
|
15
15
|
import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
|
|
16
16
|
import {
|
|
17
|
-
resolveMilestoneFile, resolveSliceFile,
|
|
18
|
-
relSliceFile,
|
|
17
|
+
resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveTaskFile,
|
|
18
|
+
relSliceFile, buildMilestoneFileName,
|
|
19
19
|
} from "./paths.js";
|
|
20
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
20
22
|
import {
|
|
21
23
|
buildResearchMilestonePrompt,
|
|
22
24
|
buildPlanMilestonePrompt,
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
buildExecuteTaskPrompt,
|
|
26
28
|
buildCompleteSlicePrompt,
|
|
27
29
|
buildCompleteMilestonePrompt,
|
|
30
|
+
buildValidateMilestonePrompt,
|
|
28
31
|
buildReplanSlicePrompt,
|
|
29
32
|
buildRunUatPrompt,
|
|
30
33
|
buildReassessRoadmapPrompt,
|
|
@@ -246,6 +249,20 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
246
249
|
const sTitle = state.activeSlice!.title;
|
|
247
250
|
const tid = state.activeTask.id;
|
|
248
251
|
const tTitle = state.activeTask.title;
|
|
252
|
+
|
|
253
|
+
// Guard: refuse to dispatch execute-task when the task plan file is missing.
|
|
254
|
+
// This prevents the agent from running blind after a failed plan-slice that
|
|
255
|
+
// wrote S{sid}-PLAN.md but omitted the individual T{tid}-PLAN.md files.
|
|
256
|
+
// (See issue #739 — missing task plan caused runaway execution and EPIPE crash.)
|
|
257
|
+
const taskPlanPath = resolveTaskFile(basePath, mid, sid, tid, "PLAN");
|
|
258
|
+
if (!taskPlanPath || !existsSync(taskPlanPath)) {
|
|
259
|
+
return {
|
|
260
|
+
action: "stop",
|
|
261
|
+
reason: `Task plan ${tid}-PLAN.md is missing for ${mid}/${sid}/${tid}. Re-run plan-slice to regenerate task plans, or create the file manually and resume.`,
|
|
262
|
+
level: "error",
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
249
266
|
return {
|
|
250
267
|
action: "dispatch",
|
|
251
268
|
unitType: "execute-task",
|
|
@@ -254,6 +271,38 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
254
271
|
};
|
|
255
272
|
},
|
|
256
273
|
},
|
|
274
|
+
{
|
|
275
|
+
name: "validating-milestone → validate-milestone",
|
|
276
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
277
|
+
if (state.phase !== "validating-milestone") return null;
|
|
278
|
+
// Skip preference: write a minimal pass-through VALIDATION file
|
|
279
|
+
if (prefs?.phases?.skip_milestone_validation) {
|
|
280
|
+
const mDir = resolveMilestonePath(basePath, mid);
|
|
281
|
+
if (mDir) {
|
|
282
|
+
if (!existsSync(mDir)) mkdirSync(mDir, { recursive: true });
|
|
283
|
+
const validationPath = join(mDir, buildMilestoneFileName(mid, "VALIDATION"));
|
|
284
|
+
const content = [
|
|
285
|
+
"---",
|
|
286
|
+
"verdict: pass",
|
|
287
|
+
"remediation_round: 0",
|
|
288
|
+
"---",
|
|
289
|
+
"",
|
|
290
|
+
"# Milestone Validation (skipped by preference)",
|
|
291
|
+
"",
|
|
292
|
+
"Milestone validation was skipped via `skip_milestone_validation` preference.",
|
|
293
|
+
].join("\n");
|
|
294
|
+
writeFileSync(validationPath, content, "utf-8");
|
|
295
|
+
}
|
|
296
|
+
return { action: "skip" };
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
action: "dispatch",
|
|
300
|
+
unitType: "validate-milestone",
|
|
301
|
+
unitId: mid,
|
|
302
|
+
prompt: await buildValidateMilestonePrompt(mid, midTitle, basePath),
|
|
303
|
+
};
|
|
304
|
+
},
|
|
305
|
+
},
|
|
257
306
|
{
|
|
258
307
|
name: "completing-milestone → complete-milestone",
|
|
259
308
|
match: async ({ state, mid, midTitle, basePath }) => {
|
|
@@ -637,6 +637,12 @@ export async function buildPlanSlicePrompt(
|
|
|
637
637
|
const executorContextConstraints = formatExecutorConstraints();
|
|
638
638
|
|
|
639
639
|
const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
|
|
640
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
641
|
+
const commitDocsEnabled = prefs?.preferences?.git?.commit_docs !== false;
|
|
642
|
+
const commitInstruction = commitDocsEnabled
|
|
643
|
+
? `Commit: \`docs(${sid}): add slice plan\``
|
|
644
|
+
: "Do not commit — planning docs are not tracked in git for this project.";
|
|
645
|
+
|
|
640
646
|
return loadPrompt("plan-slice", {
|
|
641
647
|
workingDirectory: base,
|
|
642
648
|
milestoneId: mid, sliceId: sid, sliceTitle: sTitle,
|
|
@@ -647,6 +653,7 @@ export async function buildPlanSlicePrompt(
|
|
|
647
653
|
inlinedContext,
|
|
648
654
|
dependencySummaries: depContent,
|
|
649
655
|
executorContextConstraints,
|
|
656
|
+
commitInstruction,
|
|
650
657
|
});
|
|
651
658
|
}
|
|
652
659
|
|
|
@@ -855,6 +862,79 @@ export async function buildCompleteMilestonePrompt(
|
|
|
855
862
|
});
|
|
856
863
|
}
|
|
857
864
|
|
|
865
|
+
export async function buildValidateMilestonePrompt(
|
|
866
|
+
mid: string, midTitle: string, base: string, level?: InlineLevel,
|
|
867
|
+
): Promise<string> {
|
|
868
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
869
|
+
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
870
|
+
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
871
|
+
|
|
872
|
+
const inlined: string[] = [];
|
|
873
|
+
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
874
|
+
|
|
875
|
+
// Inline all slice summaries and UAT results
|
|
876
|
+
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
877
|
+
if (roadmapContent) {
|
|
878
|
+
const roadmap = parseRoadmap(roadmapContent);
|
|
879
|
+
const seenSlices = new Set<string>();
|
|
880
|
+
for (const slice of roadmap.slices) {
|
|
881
|
+
if (seenSlices.has(slice.id)) continue;
|
|
882
|
+
seenSlices.add(slice.id);
|
|
883
|
+
const summaryPath = resolveSliceFile(base, mid, slice.id, "SUMMARY");
|
|
884
|
+
const summaryRel = relSliceFile(base, mid, slice.id, "SUMMARY");
|
|
885
|
+
inlined.push(await inlineFile(summaryPath, summaryRel, `${slice.id} Summary`));
|
|
886
|
+
|
|
887
|
+
const uatPath = resolveSliceFile(base, mid, slice.id, "UAT-RESULT");
|
|
888
|
+
const uatRel = relSliceFile(base, mid, slice.id, "UAT-RESULT");
|
|
889
|
+
const uatInline = await inlineFileOptional(uatPath, uatRel, `${slice.id} UAT Result`);
|
|
890
|
+
if (uatInline) inlined.push(uatInline);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Inline existing VALIDATION file if this is a re-validation round
|
|
895
|
+
const validationPath = resolveMilestoneFile(base, mid, "VALIDATION");
|
|
896
|
+
const validationRel = relMilestoneFile(base, mid, "VALIDATION");
|
|
897
|
+
const validationContent = validationPath ? await loadFile(validationPath) : null;
|
|
898
|
+
let remediationRound = 0;
|
|
899
|
+
if (validationContent) {
|
|
900
|
+
const roundMatch = validationContent.match(/remediation_round:\s*(\d+)/);
|
|
901
|
+
remediationRound = roundMatch ? parseInt(roundMatch[1], 10) + 1 : 1;
|
|
902
|
+
inlined.push(`### Previous Validation (re-validation round ${remediationRound})\nSource: \`${validationRel}\`\n\n${validationContent.trim()}`);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Inline root GSD files
|
|
906
|
+
if (inlineLevel !== "minimal") {
|
|
907
|
+
const requirementsInline = await inlineRequirementsFromDb(base);
|
|
908
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
909
|
+
const decisionsInline = await inlineDecisionsFromDb(base, mid);
|
|
910
|
+
if (decisionsInline) inlined.push(decisionsInline);
|
|
911
|
+
const projectInline = await inlineProjectFromDb(base);
|
|
912
|
+
if (projectInline) inlined.push(projectInline);
|
|
913
|
+
}
|
|
914
|
+
const knowledgeInline = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
915
|
+
if (knowledgeInline) inlined.push(knowledgeInline);
|
|
916
|
+
// Inline milestone context file
|
|
917
|
+
const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
|
|
918
|
+
const contextRel = relMilestoneFile(base, mid, "CONTEXT");
|
|
919
|
+
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
920
|
+
if (contextInline) inlined.push(contextInline);
|
|
921
|
+
|
|
922
|
+
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
923
|
+
|
|
924
|
+
const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
|
|
925
|
+
const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
|
|
926
|
+
|
|
927
|
+
return loadPrompt("validate-milestone", {
|
|
928
|
+
workingDirectory: base,
|
|
929
|
+
milestoneId: mid,
|
|
930
|
+
milestoneTitle: midTitle,
|
|
931
|
+
roadmapPath: roadmapOutputPath,
|
|
932
|
+
inlinedContext,
|
|
933
|
+
validationPath: validationOutputPath,
|
|
934
|
+
remediationRound: String(remediationRound),
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
|
|
858
938
|
export async function buildReplanSlicePrompt(
|
|
859
939
|
mid: string, midTitle: string, sid: string, sTitle: string, base: string,
|
|
860
940
|
): Promise<string> {
|
|
@@ -998,6 +1078,12 @@ export async function buildReassessRoadmapPrompt(
|
|
|
998
1078
|
// Non-fatal — captures module may not be available
|
|
999
1079
|
}
|
|
1000
1080
|
|
|
1081
|
+
const reassessPrefs = loadEffectiveGSDPreferences();
|
|
1082
|
+
const reassessCommitDocsEnabled = reassessPrefs?.preferences?.git?.commit_docs !== false;
|
|
1083
|
+
const reassessCommitInstruction = reassessCommitDocsEnabled
|
|
1084
|
+
? `Commit: \`docs(${mid}): reassess roadmap after ${completedSliceId}\``
|
|
1085
|
+
: "Do not commit — planning docs are not tracked in git for this project.";
|
|
1086
|
+
|
|
1001
1087
|
return loadPrompt("reassess-roadmap", {
|
|
1002
1088
|
workingDirectory: base,
|
|
1003
1089
|
milestoneId: mid,
|
|
@@ -1008,6 +1094,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1008
1094
|
assessmentPath,
|
|
1009
1095
|
inlinedContext,
|
|
1010
1096
|
deferredCaptures,
|
|
1097
|
+
commitInstruction: reassessCommitInstruction,
|
|
1011
1098
|
});
|
|
1012
1099
|
}
|
|
1013
1100
|
|
|
@@ -11,7 +11,7 @@ import type { ExtensionContext } from "@gsd/pi-coding-agent";
|
|
|
11
11
|
import {
|
|
12
12
|
clearUnitRuntimeRecord,
|
|
13
13
|
} from "./unit-runtime.js";
|
|
14
|
-
import { clearParseCache } from "./files.js";
|
|
14
|
+
import { clearParseCache, parseRoadmap, parsePlan } from "./files.js";
|
|
15
15
|
import {
|
|
16
16
|
nativeConflictFiles,
|
|
17
17
|
nativeCommit,
|
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
clearPathCache,
|
|
37
37
|
resolveGsdRootFile,
|
|
38
38
|
} from "./paths.js";
|
|
39
|
-
import { parseRoadmap } from "./files.js";
|
|
40
39
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, renameSync } from "node:fs";
|
|
41
40
|
import { dirname, join } from "node:path";
|
|
42
41
|
|
|
@@ -83,6 +82,10 @@ export function resolveExpectedArtifactPath(unitType: string, unitId: string, ba
|
|
|
83
82
|
const dir = resolveSlicePath(base, mid, sid!);
|
|
84
83
|
return dir ? join(dir, buildSliceFileName(sid!, "SUMMARY")) : null;
|
|
85
84
|
}
|
|
85
|
+
case "validate-milestone": {
|
|
86
|
+
const dir = resolveMilestonePath(base, mid);
|
|
87
|
+
return dir ? join(dir, buildMilestoneFileName(mid, "VALIDATION")) : null;
|
|
88
|
+
}
|
|
86
89
|
case "complete-milestone": {
|
|
87
90
|
const dir = resolveMilestonePath(base, mid);
|
|
88
91
|
return dir ? join(dir, buildMilestoneFileName(mid, "SUMMARY")) : null;
|
|
@@ -157,6 +160,31 @@ export function verifyExpectedArtifact(unitType: string, unitId: string, base: s
|
|
|
157
160
|
}
|
|
158
161
|
}
|
|
159
162
|
|
|
163
|
+
// plan-slice must also produce individual task plan files for every task listed
|
|
164
|
+
// in the slice plan. Without this check, a plan-slice that wrote S{sid}-PLAN.md
|
|
165
|
+
// but omitted T{tid}-PLAN.md files would be marked complete, causing execute-task
|
|
166
|
+
// to dispatch with a missing task plan (see issue #739).
|
|
167
|
+
if (unitType === "plan-slice") {
|
|
168
|
+
const parts = unitId.split("/");
|
|
169
|
+
const mid = parts[0];
|
|
170
|
+
const sid = parts[1];
|
|
171
|
+
if (mid && sid) {
|
|
172
|
+
try {
|
|
173
|
+
const planContent = readFileSync(absPath, "utf-8");
|
|
174
|
+
const plan = parsePlan(planContent);
|
|
175
|
+
const tasksDir = resolveTasksDir(base, mid, sid);
|
|
176
|
+
if (plan.tasks.length > 0 && tasksDir) {
|
|
177
|
+
for (const task of plan.tasks) {
|
|
178
|
+
const taskPlanFile = join(tasksDir, `${task.id}-PLAN.md`);
|
|
179
|
+
if (!existsSync(taskPlanFile)) return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
// Parse failure — don't block; slice plan may have non-standard format
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
160
188
|
// complete-slice must also produce a UAT file AND mark the slice [x] in the roadmap.
|
|
161
189
|
// Without the roadmap check, a crash after writing SUMMARY+UAT but before updating
|
|
162
190
|
// the roadmap causes an infinite skip loop: the idempotency key says "done" but the
|
|
@@ -244,6 +272,8 @@ export function diagnoseExpectedArtifact(unitType: string, unitId: string, base:
|
|
|
244
272
|
return `${relSliceFile(base, mid!, sid!, "ASSESSMENT")} (roadmap reassessment)`;
|
|
245
273
|
case "run-uat":
|
|
246
274
|
return `${relSliceFile(base, mid!, sid!, "UAT-RESULT")} (UAT result)`;
|
|
275
|
+
case "validate-milestone":
|
|
276
|
+
return `${relMilestoneFile(base, mid!, "VALIDATION")} (milestone validation report)`;
|
|
247
277
|
case "complete-milestone":
|
|
248
278
|
return `${relMilestoneFile(base, mid!, "SUMMARY")} (milestone summary)`;
|
|
249
279
|
default:
|
|
@@ -537,6 +567,15 @@ export function buildLoopRemediationSteps(unitType: string, unitId: string, base
|
|
|
537
567
|
` 4. Resume auto-mode`,
|
|
538
568
|
].join("\n");
|
|
539
569
|
}
|
|
570
|
+
case "validate-milestone": {
|
|
571
|
+
if (!mid) break;
|
|
572
|
+
const artifactRel = relMilestoneFile(base, mid, "VALIDATION");
|
|
573
|
+
return [
|
|
574
|
+
` 1. Write ${artifactRel} with verdict: pass`,
|
|
575
|
+
` 2. Run \`gsd doctor\``,
|
|
576
|
+
` 3. Resume auto-mode`,
|
|
577
|
+
].join("\n");
|
|
578
|
+
}
|
|
540
579
|
default:
|
|
541
580
|
break;
|
|
542
581
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* manages create, enter, detect, and teardown for auto-mode worktrees.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { existsSync, cpSync, readFileSync, realpathSync, utimesSync } from "node:fs";
|
|
9
|
+
import { existsSync, cpSync, readFileSync, writeFileSync, readdirSync, mkdirSync, realpathSync, utimesSync } from "node:fs";
|
|
10
10
|
import { isAbsolute, join, resolve } from "node:path";
|
|
11
11
|
import { copyWorktreeDb, reconcileWorktreeDb, isDbAvailable } from "./gsd-db.js";
|
|
12
12
|
import { execSync, execFileSync } from "node:child_process";
|
|
@@ -134,6 +134,112 @@ export function autoWorktreeBranch(milestoneId: string): string {
|
|
|
134
134
|
* Atomic: chdir + originalBase update happen in the same try block
|
|
135
135
|
* to prevent split-brain.
|
|
136
136
|
*/
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Forward-merge plan checkbox state from the project root into a freshly
|
|
140
|
+
* re-attached worktree (#778).
|
|
141
|
+
*
|
|
142
|
+
* When auto-mode stops via crash (not graceful stop), the milestone branch
|
|
143
|
+
* HEAD may be behind the filesystem state at the project root because
|
|
144
|
+
* syncStateToProjectRoot() runs after every task completion but the final
|
|
145
|
+
* git commit may not have happened before the crash. On restart the worktree
|
|
146
|
+
* is re-attached to the branch HEAD, which has [ ] for the crashed task,
|
|
147
|
+
* causing verifyExpectedArtifact() to fail and triggering an infinite
|
|
148
|
+
* dispatch/skip loop.
|
|
149
|
+
*
|
|
150
|
+
* Fix: after re-attaching, read every *.md plan file in the milestone
|
|
151
|
+
* directory at the project root and apply any [x] checkbox states that are
|
|
152
|
+
* ahead of the worktree version (forward-only: never downgrade [x] → [ ]).
|
|
153
|
+
*
|
|
154
|
+
* This is safe because syncStateToProjectRoot() is the authoritative source
|
|
155
|
+
* of post-task state at the project root — it writes the same [x] the LLM
|
|
156
|
+
* produced, then the auto-commit follows. If the commit never happened, the
|
|
157
|
+
* filesystem copy is still valid and correct.
|
|
158
|
+
*/
|
|
159
|
+
function reconcilePlanCheckboxes(projectRoot: string, wtPath: string, milestoneId: string): void {
|
|
160
|
+
const srcMilestone = join(projectRoot, ".gsd", "milestones", milestoneId);
|
|
161
|
+
const dstMilestone = join(wtPath, ".gsd", "milestones", milestoneId);
|
|
162
|
+
if (!existsSync(srcMilestone) || !existsSync(dstMilestone)) return;
|
|
163
|
+
|
|
164
|
+
// Walk all markdown files in the milestone directory (plans, summaries, etc.)
|
|
165
|
+
function walkMd(dir: string): string[] {
|
|
166
|
+
const results: string[] = [];
|
|
167
|
+
try {
|
|
168
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
169
|
+
const full = join(dir, entry.name);
|
|
170
|
+
if (entry.isDirectory()) {
|
|
171
|
+
results.push(...walkMd(full));
|
|
172
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
173
|
+
results.push(full);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
} catch { /* non-fatal */ }
|
|
177
|
+
return results;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const srcFile of walkMd(srcMilestone)) {
|
|
181
|
+
const rel = srcFile.slice(srcMilestone.length);
|
|
182
|
+
const dstFile = dstMilestone + rel;
|
|
183
|
+
if (!existsSync(dstFile)) continue; // only reconcile existing files
|
|
184
|
+
|
|
185
|
+
let srcContent: string;
|
|
186
|
+
let dstContent: string;
|
|
187
|
+
try {
|
|
188
|
+
srcContent = readFileSync(srcFile, "utf-8");
|
|
189
|
+
dstContent = readFileSync(dstFile, "utf-8");
|
|
190
|
+
} catch { continue; }
|
|
191
|
+
|
|
192
|
+
if (srcContent === dstContent) continue;
|
|
193
|
+
|
|
194
|
+
// Extract all checked task IDs from the source (project root)
|
|
195
|
+
// Pattern: - [x] **T<id>: or - [x] **S<id>: (case-insensitive x)
|
|
196
|
+
const checkedRe = /^- \[[xX]\] \*\*([TS]\d+):/gm;
|
|
197
|
+
const srcChecked = new Set<string>();
|
|
198
|
+
for (const m of srcContent.matchAll(checkedRe)) srcChecked.add(m[1]);
|
|
199
|
+
|
|
200
|
+
if (srcChecked.size === 0) continue;
|
|
201
|
+
|
|
202
|
+
// Forward-apply: replace [ ] → [x] for any IDs that are checked in src
|
|
203
|
+
let updated = dstContent;
|
|
204
|
+
let changed = false;
|
|
205
|
+
for (const id of srcChecked) {
|
|
206
|
+
const escapedId = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
207
|
+
const uncheckedRe = new RegExp(`^(- )\\[ \\]( \\*\\*${escapedId}:)`, "gm");
|
|
208
|
+
if (uncheckedRe.test(updated)) {
|
|
209
|
+
updated = updated.replace(
|
|
210
|
+
new RegExp(`^(- )\\[ \\]( \\*\\*${escapedId}:)`, "gm"),
|
|
211
|
+
"$1[x]$2",
|
|
212
|
+
);
|
|
213
|
+
changed = true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (changed) {
|
|
218
|
+
try {
|
|
219
|
+
writeFileSync(dstFile, updated, "utf-8");
|
|
220
|
+
} catch { /* non-fatal */ }
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Also forward-merge completed-units.json (set-union)
|
|
225
|
+
const srcKeys = join(projectRoot, ".gsd", "completed-units.json");
|
|
226
|
+
const dstKeys = join(wtPath, ".gsd", "completed-units.json");
|
|
227
|
+
if (existsSync(srcKeys)) {
|
|
228
|
+
try {
|
|
229
|
+
const src: string[] = JSON.parse(readFileSync(srcKeys, "utf-8"));
|
|
230
|
+
let dst: string[] = [];
|
|
231
|
+
if (existsSync(dstKeys)) {
|
|
232
|
+
try { dst = JSON.parse(readFileSync(dstKeys, "utf-8")); } catch { /* ignore corrupt */ }
|
|
233
|
+
}
|
|
234
|
+
const merged = [...new Set([...dst, ...src])];
|
|
235
|
+
if (merged.length > dst.length) {
|
|
236
|
+
mkdirSync(join(wtPath, ".gsd"), { recursive: true });
|
|
237
|
+
writeFileSync(dstKeys, JSON.stringify(merged), "utf-8");
|
|
238
|
+
}
|
|
239
|
+
} catch { /* non-fatal */ }
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
137
243
|
export function createAutoWorktree(basePath: string, milestoneId: string): string {
|
|
138
244
|
const branch = autoWorktreeBranch(milestoneId);
|
|
139
245
|
|
|
@@ -158,7 +264,27 @@ export function createAutoWorktree(basePath: string, milestoneId: string): strin
|
|
|
158
264
|
// Planning artifacts may be untracked if the project's .gitignore had a
|
|
159
265
|
// blanket .gsd/ rule (pre-v2.14.0). Without this copy, auto-mode loops
|
|
160
266
|
// on plan-slice because the plan file doesn't exist in the worktree.
|
|
161
|
-
|
|
267
|
+
//
|
|
268
|
+
// IMPORTANT: Skip when re-attaching to an existing branch (#759).
|
|
269
|
+
// The branch checkout already has committed artifacts with correct state
|
|
270
|
+
// (e.g. [x] for completed slices). Copying from the project root would
|
|
271
|
+
// overwrite them with stale data ([ ] checkboxes) because the root is
|
|
272
|
+
// not always fully synced.
|
|
273
|
+
if (!branchExists) {
|
|
274
|
+
copyPlanningArtifacts(basePath, info.path);
|
|
275
|
+
} else {
|
|
276
|
+
// Re-attaching to an existing branch: forward-merge any plan checkpoint
|
|
277
|
+
// state from the project root into the worktree (#778).
|
|
278
|
+
//
|
|
279
|
+
// If auto-mode stopped via crash, the milestone branch HEAD may lag behind
|
|
280
|
+
// the project root filesystem because syncStateToProjectRoot() ran after
|
|
281
|
+
// task completion but the auto-commit never fired. On restart the worktree
|
|
282
|
+
// is re-created from the branch HEAD (which has [ ] for the crashed task),
|
|
283
|
+
// causing verifyExpectedArtifact() to return false → stale-key eviction →
|
|
284
|
+
// infinite dispatch/skip loop. Reconciling here ensures the worktree sees
|
|
285
|
+
// the same [x] state that syncStateToProjectRoot() wrote to the root.
|
|
286
|
+
reconcilePlanCheckboxes(basePath, info.path, milestoneId);
|
|
287
|
+
}
|
|
162
288
|
|
|
163
289
|
// Run user-configured post-create hook (#597) — e.g. copy .env, symlink assets
|
|
164
290
|
const hookError = runWorktreePostCreateHook(basePath, info.path);
|
|
@@ -429,8 +555,12 @@ export function mergeMilestoneToMain(
|
|
|
429
555
|
const integrationBranch = readIntegrationBranch(originalBasePath_, milestoneId);
|
|
430
556
|
const mainBranch = integrationBranch ?? prefs.main_branch ?? "main";
|
|
431
557
|
|
|
432
|
-
// 5. Checkout integration branch
|
|
433
|
-
|
|
558
|
+
// 5. Checkout integration branch (skip if already current — avoids git error
|
|
559
|
+
// when main is already checked out in the project-root worktree, #757)
|
|
560
|
+
const currentBranchAtBase = nativeGetCurrentBranch(originalBasePath_);
|
|
561
|
+
if (currentBranchAtBase !== mainBranch) {
|
|
562
|
+
nativeCheckoutBranch(originalBasePath_, mainBranch);
|
|
563
|
+
}
|
|
434
564
|
|
|
435
565
|
// 6. Build rich commit message
|
|
436
566
|
const milestoneTitle = roadmap.title.replace(/^M\d+:\s*/, "").trim() || milestoneId;
|