gsd-pi 2.31.1 → 2.31.2-dev.2453512
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/dist/resources/extensions/gsd/auto-constants.ts +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +20 -26
- package/dist/resources/extensions/gsd/auto-direct-dispatch.ts +1 -6
- package/dist/resources/extensions/gsd/auto-dispatch.ts +18 -22
- package/dist/resources/extensions/gsd/auto-post-unit.ts +27 -32
- package/dist/resources/extensions/gsd/auto-prompts.ts +43 -46
- package/dist/resources/extensions/gsd/auto-start.ts +4 -4
- package/dist/resources/extensions/gsd/auto.ts +54 -33
- package/dist/resources/extensions/gsd/commands-workflow-templates.ts +3 -5
- package/dist/resources/extensions/gsd/git-service.ts +9 -0
- package/dist/resources/extensions/gsd/guided-flow-queue.ts +1 -8
- package/dist/resources/extensions/gsd/preferences-types.ts +8 -0
- package/dist/resources/extensions/gsd/preferences-validation.ts +3 -10
- package/dist/resources/extensions/gsd/prompts/run-uat.md +1 -42
- package/dist/resources/extensions/gsd/quick.ts +3 -5
- package/dist/resources/extensions/gsd/tests/auto-reentrancy-guard.test.ts +127 -0
- package/dist/resources/extensions/gsd/tests/run-uat.test.ts +101 -2
- package/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto-constants.ts +6 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +20 -26
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -6
- package/src/resources/extensions/gsd/auto-dispatch.ts +18 -22
- package/src/resources/extensions/gsd/auto-post-unit.ts +27 -32
- package/src/resources/extensions/gsd/auto-prompts.ts +43 -46
- package/src/resources/extensions/gsd/auto-start.ts +4 -4
- package/src/resources/extensions/gsd/auto.ts +54 -33
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -5
- package/src/resources/extensions/gsd/git-service.ts +9 -0
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -8
- package/src/resources/extensions/gsd/preferences-types.ts +8 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +3 -10
- package/src/resources/extensions/gsd/prompts/run-uat.md +1 -42
- package/src/resources/extensions/gsd/quick.ts +3 -5
- package/src/resources/extensions/gsd/tests/auto-reentrancy-guard.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +101 -2
|
@@ -48,40 +48,34 @@ export interface AutoDashboardData {
|
|
|
48
48
|
|
|
49
49
|
// ─── Unit Description Helpers ─────────────────────────────────────────────────
|
|
50
50
|
|
|
51
|
+
/** Canonical verb and phase label for each known unit type. */
|
|
52
|
+
const UNIT_TYPE_INFO: Record<string, { verb: string; phaseLabel: string }> = {
|
|
53
|
+
"research-milestone": { verb: "researching", phaseLabel: "RESEARCH" },
|
|
54
|
+
"research-slice": { verb: "researching", phaseLabel: "RESEARCH" },
|
|
55
|
+
"plan-milestone": { verb: "planning", phaseLabel: "PLAN" },
|
|
56
|
+
"plan-slice": { verb: "planning", phaseLabel: "PLAN" },
|
|
57
|
+
"execute-task": { verb: "executing", phaseLabel: "EXECUTE" },
|
|
58
|
+
"complete-slice": { verb: "completing", phaseLabel: "COMPLETE" },
|
|
59
|
+
"replan-slice": { verb: "replanning", phaseLabel: "REPLAN" },
|
|
60
|
+
"rewrite-docs": { verb: "rewriting", phaseLabel: "REWRITE" },
|
|
61
|
+
"reassess-roadmap": { verb: "reassessing", phaseLabel: "REASSESS" },
|
|
62
|
+
"run-uat": { verb: "running UAT", phaseLabel: "UAT" },
|
|
63
|
+
};
|
|
64
|
+
|
|
51
65
|
export function unitVerb(unitType: string): string {
|
|
52
66
|
if (unitType.startsWith("hook/")) return `hook: ${unitType.slice(5)}`;
|
|
53
|
-
|
|
54
|
-
case "research-milestone":
|
|
55
|
-
case "research-slice": return "researching";
|
|
56
|
-
case "plan-milestone":
|
|
57
|
-
case "plan-slice": return "planning";
|
|
58
|
-
case "execute-task": return "executing";
|
|
59
|
-
case "complete-slice": return "completing";
|
|
60
|
-
case "replan-slice": return "replanning";
|
|
61
|
-
case "rewrite-docs": return "rewriting";
|
|
62
|
-
case "reassess-roadmap": return "reassessing";
|
|
63
|
-
case "run-uat": return "running UAT";
|
|
64
|
-
default: return unitType;
|
|
65
|
-
}
|
|
67
|
+
return UNIT_TYPE_INFO[unitType]?.verb ?? unitType;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
export function unitPhaseLabel(unitType: string): string {
|
|
69
71
|
if (unitType.startsWith("hook/")) return "HOOK";
|
|
70
|
-
|
|
71
|
-
case "research-milestone": return "RESEARCH";
|
|
72
|
-
case "research-slice": return "RESEARCH";
|
|
73
|
-
case "plan-milestone": return "PLAN";
|
|
74
|
-
case "plan-slice": return "PLAN";
|
|
75
|
-
case "execute-task": return "EXECUTE";
|
|
76
|
-
case "complete-slice": return "COMPLETE";
|
|
77
|
-
case "replan-slice": return "REPLAN";
|
|
78
|
-
case "rewrite-docs": return "REWRITE";
|
|
79
|
-
case "reassess-roadmap": return "REASSESS";
|
|
80
|
-
case "run-uat": return "UAT";
|
|
81
|
-
default: return unitType.toUpperCase();
|
|
82
|
-
}
|
|
72
|
+
return UNIT_TYPE_INFO[unitType]?.phaseLabel ?? unitType.toUpperCase();
|
|
83
73
|
}
|
|
84
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Describe the expected next step after the current unit completes.
|
|
77
|
+
* Unit types here mirror the keys in UNIT_TYPE_INFO above.
|
|
78
|
+
*/
|
|
85
79
|
function peekNext(unitType: string, state: GSDState): string {
|
|
86
80
|
// Show active hook info in progress display
|
|
87
81
|
const activeHookState = getActiveHook();
|
|
@@ -182,15 +182,10 @@ export async function dispatchDirectPhase(
|
|
|
182
182
|
ctx.ui.notify("Cannot dispatch run-uat: no UAT file found.", "warning");
|
|
183
183
|
return;
|
|
184
184
|
}
|
|
185
|
-
const uatContent = await loadFile(uatFile);
|
|
186
|
-
if (!uatContent) {
|
|
187
|
-
ctx.ui.notify("Cannot dispatch run-uat: UAT file is empty.", "warning");
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
185
|
const uatPath = relSliceFile(base, mid, sid, "UAT");
|
|
191
186
|
unitType = "run-uat";
|
|
192
187
|
unitId = `${mid}/${sid}`;
|
|
193
|
-
prompt = await buildRunUatPrompt(mid, sid, uatPath,
|
|
188
|
+
prompt = await buildRunUatPrompt(mid, sid, uatPath, base);
|
|
194
189
|
break;
|
|
195
190
|
}
|
|
196
191
|
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { GSDState } from "./types.js";
|
|
13
13
|
import type { GSDPreferences } from "./preferences.js";
|
|
14
|
-
import
|
|
15
|
-
import { loadFile, extractUatType, loadActiveOverrides, parseRoadmap } from "./files.js";
|
|
14
|
+
import { loadFile, loadActiveOverrides, parseRoadmap } from "./files.js";
|
|
16
15
|
import {
|
|
17
16
|
resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveTaskFile,
|
|
18
17
|
relSliceFile, buildMilestoneFileName,
|
|
@@ -39,7 +38,7 @@ import {
|
|
|
39
38
|
// ─── Types ────────────────────────────────────────────────────────────────
|
|
40
39
|
|
|
41
40
|
export type DispatchAction =
|
|
42
|
-
| { action: "dispatch"; unitType: string; unitId: string; prompt: string
|
|
41
|
+
| { action: "dispatch"; unitType: string; unitId: string; prompt: string }
|
|
43
42
|
| { action: "stop"; reason: string; level: "info" | "warning" | "error" }
|
|
44
43
|
| { action: "skip" };
|
|
45
44
|
|
|
@@ -104,25 +103,6 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
104
103
|
};
|
|
105
104
|
},
|
|
106
105
|
},
|
|
107
|
-
{
|
|
108
|
-
name: "run-uat (post-completion)",
|
|
109
|
-
match: async ({ state, mid, basePath, prefs }) => {
|
|
110
|
-
const needsRunUat = await checkNeedsRunUat(basePath, mid, state, prefs);
|
|
111
|
-
if (!needsRunUat) return null;
|
|
112
|
-
const { sliceId, uatType } = needsRunUat;
|
|
113
|
-
const uatFile = resolveSliceFile(basePath, mid, sliceId, "UAT")!;
|
|
114
|
-
const uatContent = await loadFile(uatFile);
|
|
115
|
-
return {
|
|
116
|
-
action: "dispatch",
|
|
117
|
-
unitType: "run-uat",
|
|
118
|
-
unitId: `${mid}/${sliceId}`,
|
|
119
|
-
prompt: await buildRunUatPrompt(
|
|
120
|
-
mid, sliceId, relSliceFile(basePath, mid, sliceId, "UAT"), uatContent ?? "", basePath,
|
|
121
|
-
),
|
|
122
|
-
pauseAfterDispatch: uatType !== "artifact-driven",
|
|
123
|
-
};
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
106
|
{
|
|
127
107
|
name: "uat-verdict-gate (non-PASS blocks progression)",
|
|
128
108
|
match: async ({ mid, basePath, prefs }) => {
|
|
@@ -152,6 +132,22 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
152
132
|
return null;
|
|
153
133
|
},
|
|
154
134
|
},
|
|
135
|
+
{
|
|
136
|
+
name: "run-uat (post-completion)",
|
|
137
|
+
match: async ({ state, mid, basePath, prefs }) => {
|
|
138
|
+
const needsRunUat = await checkNeedsRunUat(basePath, mid, state, prefs);
|
|
139
|
+
if (!needsRunUat) return null;
|
|
140
|
+
const { sliceId } = needsRunUat;
|
|
141
|
+
return {
|
|
142
|
+
action: "dispatch",
|
|
143
|
+
unitType: "run-uat",
|
|
144
|
+
unitId: `${mid}/${sliceId}`,
|
|
145
|
+
prompt: await buildRunUatPrompt(
|
|
146
|
+
mid, sliceId, relSliceFile(basePath, mid, sliceId, "UAT"), basePath,
|
|
147
|
+
),
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
},
|
|
155
151
|
{
|
|
156
152
|
name: "reassess-roadmap (post-completion)",
|
|
157
153
|
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
@@ -60,9 +60,31 @@ import {
|
|
|
60
60
|
hideFooter,
|
|
61
61
|
} from "./auto-dashboard.js";
|
|
62
62
|
import { join } from "node:path";
|
|
63
|
+
import { STATE_REBUILD_MIN_INTERVAL_MS } from "./auto-constants.js";
|
|
63
64
|
|
|
64
|
-
/**
|
|
65
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Initialize a unit dispatch: stamp the current time, set `s.currentUnit`,
|
|
67
|
+
* and persist the initial runtime record. Returns `startedAt` for callers
|
|
68
|
+
* that need the timestamp.
|
|
69
|
+
*/
|
|
70
|
+
function dispatchUnit(
|
|
71
|
+
s: AutoSession,
|
|
72
|
+
basePath: string,
|
|
73
|
+
unitType: string,
|
|
74
|
+
unitId: string,
|
|
75
|
+
): number {
|
|
76
|
+
const startedAt = Date.now();
|
|
77
|
+
s.currentUnit = { type: unitType, id: unitId, startedAt };
|
|
78
|
+
writeUnitRuntimeRecord(basePath, unitType, unitId, startedAt, {
|
|
79
|
+
phase: "dispatched",
|
|
80
|
+
wrapupWarningSent: false,
|
|
81
|
+
timeoutAt: null,
|
|
82
|
+
lastProgressAt: startedAt,
|
|
83
|
+
progressCount: 0,
|
|
84
|
+
lastProgressKind: "dispatch",
|
|
85
|
+
});
|
|
86
|
+
return startedAt;
|
|
87
|
+
}
|
|
66
88
|
|
|
67
89
|
export interface PostUnitContext {
|
|
68
90
|
s: AutoSession;
|
|
@@ -364,19 +386,10 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
364
386
|
if (s.currentUnit && !s.stepMode) {
|
|
365
387
|
const hookUnit = checkPostUnitHooks(s.currentUnit.type, s.currentUnit.id, s.basePath);
|
|
366
388
|
if (hookUnit) {
|
|
367
|
-
const hookStartedAt = Date.now();
|
|
368
389
|
if (s.currentUnit) {
|
|
369
390
|
await closeoutUnit(ctx, s.basePath, s.currentUnit.type, s.currentUnit.id, s.currentUnit.startedAt, buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id));
|
|
370
391
|
}
|
|
371
|
-
s.
|
|
372
|
-
writeUnitRuntimeRecord(s.basePath, hookUnit.unitType, hookUnit.unitId, hookStartedAt, {
|
|
373
|
-
phase: "dispatched",
|
|
374
|
-
wrapupWarningSent: false,
|
|
375
|
-
timeoutAt: null,
|
|
376
|
-
lastProgressAt: hookStartedAt,
|
|
377
|
-
progressCount: 0,
|
|
378
|
-
lastProgressKind: "dispatch",
|
|
379
|
-
});
|
|
392
|
+
dispatchUnit(s, s.basePath, hookUnit.unitType, hookUnit.unitId);
|
|
380
393
|
|
|
381
394
|
const state = await deriveState(s.basePath);
|
|
382
395
|
updateProgressWidget(ctx, hookUnit.unitType, hookUnit.unitId, state);
|
|
@@ -498,16 +511,7 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
498
511
|
|
|
499
512
|
const triageUnitType = "triage-captures";
|
|
500
513
|
const triageUnitId = `${mid}/${sid}/triage`;
|
|
501
|
-
|
|
502
|
-
s.currentUnit = { type: triageUnitType, id: triageUnitId, startedAt: triageStartedAt };
|
|
503
|
-
writeUnitRuntimeRecord(s.basePath, triageUnitType, triageUnitId, triageStartedAt, {
|
|
504
|
-
phase: "dispatched",
|
|
505
|
-
wrapupWarningSent: false,
|
|
506
|
-
timeoutAt: null,
|
|
507
|
-
lastProgressAt: triageStartedAt,
|
|
508
|
-
progressCount: 0,
|
|
509
|
-
lastProgressKind: "dispatch",
|
|
510
|
-
});
|
|
514
|
+
dispatchUnit(s, s.basePath, triageUnitType, triageUnitId);
|
|
511
515
|
updateProgressWidget(ctx, triageUnitType, triageUnitId, state);
|
|
512
516
|
|
|
513
517
|
const result = await s.cmdCtx!.newSession();
|
|
@@ -568,16 +572,7 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
568
572
|
|
|
569
573
|
const qtUnitType = "quick-task";
|
|
570
574
|
const qtUnitId = `${s.currentMilestoneId}/${capture.id}`;
|
|
571
|
-
|
|
572
|
-
s.currentUnit = { type: qtUnitType, id: qtUnitId, startedAt: qtStartedAt };
|
|
573
|
-
writeUnitRuntimeRecord(s.basePath, qtUnitType, qtUnitId, qtStartedAt, {
|
|
574
|
-
phase: "dispatched",
|
|
575
|
-
wrapupWarningSent: false,
|
|
576
|
-
timeoutAt: null,
|
|
577
|
-
lastProgressAt: qtStartedAt,
|
|
578
|
-
progressCount: 0,
|
|
579
|
-
lastProgressKind: "dispatch",
|
|
580
|
-
});
|
|
575
|
+
dispatchUnit(s, s.basePath, qtUnitType, qtUnitId);
|
|
581
576
|
const state = await deriveState(s.basePath);
|
|
582
577
|
updateProgressWidget(ctx, qtUnitType, qtUnitId, state);
|
|
583
578
|
|
|
@@ -324,6 +324,27 @@ function oneLine(text: string): string {
|
|
|
324
324
|
return text.replace(/\s+/g, " ").trim();
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
/** Build the standard inlined-context section used by all prompt builders. */
|
|
328
|
+
function buildInlinedContextSection(inlined: string[]): string {
|
|
329
|
+
return `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Build the formatted list of available GSD source files for planners to read on demand. */
|
|
333
|
+
function buildSourceFileList(base: string, opts?: { includeProject?: boolean }): string {
|
|
334
|
+
const paths: string[] = [];
|
|
335
|
+
if (opts?.includeProject && existsSync(resolveGsdRootFile(base, "PROJECT")))
|
|
336
|
+
paths.push(`- **Project**: \`${relGsdRootFile("PROJECT")}\``);
|
|
337
|
+
if (existsSync(resolveGsdRootFile(base, "REQUIREMENTS")))
|
|
338
|
+
paths.push(`- **Requirements**: \`${relGsdRootFile("REQUIREMENTS")}\``);
|
|
339
|
+
if (existsSync(resolveGsdRootFile(base, "DECISIONS")))
|
|
340
|
+
paths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
|
|
341
|
+
if (paths.length === 0) {
|
|
342
|
+
const types = opts?.includeProject ? "project/requirements/decisions" : "requirements/decisions";
|
|
343
|
+
return `_No ${types} files found._`;
|
|
344
|
+
}
|
|
345
|
+
return paths.join("\n");
|
|
346
|
+
}
|
|
347
|
+
|
|
327
348
|
// ─── Section Builders ──────────────────────────────────────────────────────
|
|
328
349
|
|
|
329
350
|
export function buildResumeSection(
|
|
@@ -530,25 +551,21 @@ export async function checkNeedsRunUat(
|
|
|
530
551
|
const uatContent = await loadFile(uatFile);
|
|
531
552
|
if (!uatContent) return null;
|
|
532
553
|
|
|
533
|
-
// If UAT result already exists
|
|
534
|
-
//
|
|
535
|
-
//
|
|
554
|
+
// If a UAT result already exists, the UAT unit has already run and must not
|
|
555
|
+
// be re-dispatched. PASS means progression can continue; any non-PASS verdict
|
|
556
|
+
// must be handled by the dispatch table's verdict gate, which stops progression
|
|
557
|
+
// with a human-action message instead of replaying the same run-uat unit.
|
|
536
558
|
const uatResultFile = resolveSliceFile(base, mid, sid, "UAT-RESULT");
|
|
537
559
|
if (uatResultFile) {
|
|
538
560
|
const resultContent = await loadFile(uatResultFile);
|
|
539
|
-
if (resultContent)
|
|
540
|
-
const verdictMatch = resultContent.match(/verdict:\s*([\w-]+)/i);
|
|
541
|
-
const verdict = verdictMatch?.[1]?.toLowerCase();
|
|
542
|
-
if (verdict === "pass" || verdict === "passed") return null; // PASS — skip
|
|
543
|
-
// Non-PASS verdict exists — don't re-run UAT, but don't advance either.
|
|
544
|
-
// Return null here since the UAT already ran; the dispatch table's
|
|
545
|
-
// complete-slice rule should check the verdict before advancing.
|
|
546
|
-
// For now, returning the slice signals it still needs attention.
|
|
547
|
-
}
|
|
561
|
+
if (resultContent) return null;
|
|
548
562
|
}
|
|
549
563
|
|
|
550
|
-
// Classify UAT type;
|
|
564
|
+
// Classify UAT type; skip non-artifact-driven types — auto-mode can only
|
|
565
|
+
// execute mechanical checks. Non-artifact UATs are tracked in the dashboard
|
|
566
|
+
// but don't block auto-mode progression.
|
|
551
567
|
const uatType = extractUatType(uatContent) ?? "human-experience";
|
|
568
|
+
if (uatType !== "artifact-driven") return null;
|
|
552
569
|
|
|
553
570
|
return { sliceId: sid, uatType };
|
|
554
571
|
}
|
|
@@ -571,7 +588,7 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
571
588
|
if (knowledgeInlineRM) inlined.push(knowledgeInlineRM);
|
|
572
589
|
inlined.push(inlineTemplate("research", "Research"));
|
|
573
590
|
|
|
574
|
-
const inlinedContext =
|
|
591
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
575
592
|
|
|
576
593
|
const outputRelPath = relMilestoneFile(base, mid, "RESEARCH");
|
|
577
594
|
return loadPrompt("research-milestone", {
|
|
@@ -599,17 +616,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
599
616
|
const { inlinePriorMilestoneSummary } = await import("./files.js");
|
|
600
617
|
const priorSummaryInline = await inlinePriorMilestoneSummary(mid, base);
|
|
601
618
|
if (priorSummaryInline) inlined.push(priorSummaryInline);
|
|
602
|
-
|
|
603
|
-
const sourcePaths: string[] = [];
|
|
604
|
-
if (existsSync(resolveGsdRootFile(base, "PROJECT")))
|
|
605
|
-
sourcePaths.push(`- **Project**: \`${relGsdRootFile("PROJECT")}\``);
|
|
606
|
-
if (existsSync(resolveGsdRootFile(base, "REQUIREMENTS")))
|
|
607
|
-
sourcePaths.push(`- **Requirements**: \`${relGsdRootFile("REQUIREMENTS")}\``);
|
|
608
|
-
if (existsSync(resolveGsdRootFile(base, "DECISIONS")))
|
|
609
|
-
sourcePaths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
|
|
610
|
-
const sourceFilePaths = sourcePaths.length > 0
|
|
611
|
-
? sourcePaths.join("\n")
|
|
612
|
-
: "_No project/requirements/decisions files found._";
|
|
619
|
+
const sourceFilePaths = buildSourceFileList(base, { includeProject: true });
|
|
613
620
|
|
|
614
621
|
const knowledgeInlinePM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
615
622
|
if (knowledgeInlinePM) inlined.push(knowledgeInlinePM);
|
|
@@ -625,7 +632,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
625
632
|
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
626
633
|
}
|
|
627
634
|
|
|
628
|
-
const inlinedContext =
|
|
635
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
629
636
|
|
|
630
637
|
const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
|
|
631
638
|
const secretsOutputPath = join(base, relMilestoneFile(base, mid, "SECRETS"));
|
|
@@ -674,7 +681,7 @@ export async function buildResearchSlicePrompt(
|
|
|
674
681
|
const overridesInline = formatOverridesSection(activeOverrides);
|
|
675
682
|
if (overridesInline) inlined.unshift(overridesInline);
|
|
676
683
|
|
|
677
|
-
const inlinedContext =
|
|
684
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
678
685
|
|
|
679
686
|
const outputRelPath = relSliceFile(base, mid, sid, "RESEARCH");
|
|
680
687
|
return loadPrompt("research-slice", {
|
|
@@ -704,15 +711,7 @@ export async function buildPlanSlicePrompt(
|
|
|
704
711
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
705
712
|
const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
|
|
706
713
|
if (researchInline) inlined.push(researchInline);
|
|
707
|
-
|
|
708
|
-
const sliceSourcePaths: string[] = [];
|
|
709
|
-
if (existsSync(resolveGsdRootFile(base, "REQUIREMENTS")))
|
|
710
|
-
sliceSourcePaths.push(`- **Requirements**: \`${relGsdRootFile("REQUIREMENTS")}\``);
|
|
711
|
-
if (existsSync(resolveGsdRootFile(base, "DECISIONS")))
|
|
712
|
-
sliceSourcePaths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
|
|
713
|
-
const sliceSourceFilePaths = sliceSourcePaths.length > 0
|
|
714
|
-
? sliceSourcePaths.join("\n")
|
|
715
|
-
: "_No requirements/decisions files found._";
|
|
714
|
+
const sliceSourceFilePaths = buildSourceFileList(base);
|
|
716
715
|
|
|
717
716
|
const knowledgeInlinePS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
718
717
|
if (knowledgeInlinePS) inlined.push(knowledgeInlinePS);
|
|
@@ -726,7 +725,7 @@ export async function buildPlanSlicePrompt(
|
|
|
726
725
|
const planOverridesInline = formatOverridesSection(planActiveOverrides);
|
|
727
726
|
if (planOverridesInline) inlined.unshift(planOverridesInline);
|
|
728
727
|
|
|
729
|
-
const inlinedContext =
|
|
728
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
730
729
|
|
|
731
730
|
// Build executor context constraints from the budget engine
|
|
732
731
|
const executorContextConstraints = formatExecutorConstraints();
|
|
@@ -901,7 +900,7 @@ export async function buildCompleteSlicePrompt(
|
|
|
901
900
|
const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
|
|
902
901
|
if (completeOverridesInline) inlined.unshift(completeOverridesInline);
|
|
903
902
|
|
|
904
|
-
const inlinedContext =
|
|
903
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
905
904
|
|
|
906
905
|
const sliceRel = relSlicePath(base, mid, sid);
|
|
907
906
|
const sliceSummaryPath = join(base, `${sliceRel}/${sid}-SUMMARY.md`);
|
|
@@ -960,7 +959,7 @@ export async function buildCompleteMilestonePrompt(
|
|
|
960
959
|
if (contextInline) inlined.push(contextInline);
|
|
961
960
|
inlined.push(inlineTemplate("milestone-summary", "Milestone Summary"));
|
|
962
961
|
|
|
963
|
-
const inlinedContext =
|
|
962
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
964
963
|
|
|
965
964
|
const milestoneSummaryPath = join(base, `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md`);
|
|
966
965
|
|
|
@@ -1031,7 +1030,7 @@ export async function buildValidateMilestonePrompt(
|
|
|
1031
1030
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
1032
1031
|
if (contextInline) inlined.push(contextInline);
|
|
1033
1032
|
|
|
1034
|
-
const inlinedContext =
|
|
1033
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
1035
1034
|
|
|
1036
1035
|
const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
|
|
1037
1036
|
const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
|
|
@@ -1085,7 +1084,7 @@ export async function buildReplanSlicePrompt(
|
|
|
1085
1084
|
const replanOverridesInline = formatOverridesSection(replanActiveOverrides);
|
|
1086
1085
|
if (replanOverridesInline) inlined.unshift(replanOverridesInline);
|
|
1087
1086
|
|
|
1088
|
-
const inlinedContext =
|
|
1087
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
1089
1088
|
|
|
1090
1089
|
const replanPath = join(base, `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md`);
|
|
1091
1090
|
|
|
@@ -1118,7 +1117,7 @@ export async function buildReplanSlicePrompt(
|
|
|
1118
1117
|
}
|
|
1119
1118
|
|
|
1120
1119
|
export async function buildRunUatPrompt(
|
|
1121
|
-
mid: string, sliceId: string, uatPath: string,
|
|
1120
|
+
mid: string, sliceId: string, uatPath: string, base: string,
|
|
1122
1121
|
): Promise<string> {
|
|
1123
1122
|
const inlined: string[] = [];
|
|
1124
1123
|
inlined.push(await inlineFile(resolveSliceFile(base, mid, sliceId, "UAT"), uatPath, `${sliceId} UAT`));
|
|
@@ -1133,10 +1132,9 @@ export async function buildRunUatPrompt(
|
|
|
1133
1132
|
const projectInline = await inlineProjectFromDb(base);
|
|
1134
1133
|
if (projectInline) inlined.push(projectInline);
|
|
1135
1134
|
|
|
1136
|
-
const inlinedContext =
|
|
1135
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
1137
1136
|
|
|
1138
1137
|
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "UAT-RESULT"));
|
|
1139
|
-
const uatType = extractUatType(uatContent) ?? "human-experience";
|
|
1140
1138
|
|
|
1141
1139
|
return loadPrompt("run-uat", {
|
|
1142
1140
|
workingDirectory: base,
|
|
@@ -1144,7 +1142,6 @@ export async function buildRunUatPrompt(
|
|
|
1144
1142
|
sliceId,
|
|
1145
1143
|
uatPath,
|
|
1146
1144
|
uatResultPath,
|
|
1147
|
-
uatType,
|
|
1148
1145
|
inlinedContext,
|
|
1149
1146
|
});
|
|
1150
1147
|
}
|
|
@@ -1172,7 +1169,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1172
1169
|
const knowledgeInlineRA = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
1173
1170
|
if (knowledgeInlineRA) inlined.push(knowledgeInlineRA);
|
|
1174
1171
|
|
|
1175
|
-
const inlinedContext =
|
|
1172
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
1176
1173
|
|
|
1177
1174
|
const assessmentPath = join(base, relSliceFile(base, mid, completedSliceId, "ASSESSMENT"));
|
|
1178
1175
|
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
import { selfHealRuntimeRecords } from "./auto-recovery.js";
|
|
39
39
|
import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
|
|
40
40
|
import { nativeIsRepo, nativeInit } from "./native-git-bridge.js";
|
|
41
|
-
import {
|
|
41
|
+
import { createGitService } from "./git-service.js";
|
|
42
42
|
import {
|
|
43
43
|
captureIntegrationBranch,
|
|
44
44
|
detectWorktreeName,
|
|
@@ -129,7 +129,7 @@ export async function bootstrapAutoSession(
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// Initialize GitServiceImpl
|
|
132
|
-
s.gitService =
|
|
132
|
+
s.gitService = createGitService(s.basePath);
|
|
133
133
|
|
|
134
134
|
// Check for crash from previous session (use both old and new lock data)
|
|
135
135
|
const crashLock = readCrashLock(base);
|
|
@@ -330,12 +330,12 @@ export async function bootstrapAutoSession(
|
|
|
330
330
|
if (existingWtPath) {
|
|
331
331
|
const wtPath = enterAutoWorktree(base, s.currentMilestoneId);
|
|
332
332
|
s.basePath = wtPath;
|
|
333
|
-
s.gitService =
|
|
333
|
+
s.gitService = createGitService(s.basePath);
|
|
334
334
|
ctx.ui.notify(`Entered auto-worktree at ${wtPath}`, "info");
|
|
335
335
|
} else {
|
|
336
336
|
const wtPath = createAutoWorktree(base, s.currentMilestoneId);
|
|
337
337
|
s.basePath = wtPath;
|
|
338
|
-
s.gitService =
|
|
338
|
+
s.gitService = createGitService(s.basePath);
|
|
339
339
|
ctx.ui.notify(`Created auto-worktree at ${wtPath}`, "info");
|
|
340
340
|
}
|
|
341
341
|
registerSigtermHandler(s.originalBasePath);
|