gsd-pi 2.38.0-dev.4d4d14a → 2.38.0-dev.5492881
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/resource-loader.js +34 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto-loop.js +538 -469
- package/dist/resources/extensions/gsd/auto-post-unit.js +9 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +18 -14
- package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
- package/dist/resources/extensions/gsd/commands.js +2 -1
- package/dist/resources/extensions/gsd/doctor.js +20 -1
- package/dist/resources/extensions/gsd/exit-command.js +2 -1
- package/dist/resources/extensions/gsd/files.js +4 -0
- package/dist/resources/extensions/gsd/git-service.js +22 -11
- package/dist/resources/extensions/gsd/guided-flow.js +82 -32
- package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
- package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -0
- package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto-loop.ts +342 -304
- package/src/resources/extensions/gsd/auto-post-unit.ts +10 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +20 -14
- package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
- package/src/resources/extensions/gsd/commands.ts +2 -2
- package/src/resources/extensions/gsd/doctor.ts +22 -1
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/files.ts +3 -1
- package/src/resources/extensions/gsd/git-service.ts +31 -9
- package/src/resources/extensions/gsd/guided-flow.ts +110 -38
- package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +2 -0
- package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +106 -31
- package/src/resources/extensions/mcp-client/index.ts +17 -1
|
@@ -172,13 +172,20 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
172
172
|
ctx.ui.notify(`Post-hook: applied ${report.fixesApplied.length} fix(es).`, "info");
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
// Proactive health tracking
|
|
176
|
-
|
|
175
|
+
// Proactive health tracking — filter to current milestone to avoid
|
|
176
|
+
// cross-milestone stale errors inflating the escalation counter
|
|
177
|
+
const currentMilestoneId = s.currentUnit.id.split("/")[0];
|
|
178
|
+
const milestoneIssues = currentMilestoneId
|
|
179
|
+
? report.issues.filter(i =>
|
|
180
|
+
i.unitId === currentMilestoneId ||
|
|
181
|
+
i.unitId.startsWith(`${currentMilestoneId}/`))
|
|
182
|
+
: report.issues;
|
|
183
|
+
const summary = summarizeDoctorIssues(milestoneIssues);
|
|
177
184
|
recordHealthSnapshot(summary.errors, summary.warnings, report.fixesApplied.length);
|
|
178
185
|
|
|
179
186
|
// Check if we should escalate to LLM-assisted heal
|
|
180
187
|
if (summary.errors > 0) {
|
|
181
|
-
const unresolvedErrors =
|
|
188
|
+
const unresolvedErrors = milestoneIssues
|
|
182
189
|
.filter(i => i.severity === "error" && !i.fixable)
|
|
183
190
|
.map(i => ({ code: i.code, message: i.message, unitId: i.unitId }));
|
|
184
191
|
const escalation = checkHealEscalation(summary.errors, unresolvedErrors);
|
|
@@ -20,9 +20,18 @@ import type { GSDState, InlineLevel } from "./types.js";
|
|
|
20
20
|
import type { GSDPreferences } from "./preferences.js";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { existsSync } from "node:fs";
|
|
23
|
-
import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.js";
|
|
23
|
+
import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
|
|
24
24
|
import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
|
|
25
25
|
|
|
26
|
+
// ─── Preamble Cap ─────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
const MAX_PREAMBLE_CHARS = 30_000;
|
|
29
|
+
|
|
30
|
+
function capPreamble(preamble: string): string {
|
|
31
|
+
if (preamble.length <= MAX_PREAMBLE_CHARS) return preamble;
|
|
32
|
+
return truncateAtSectionBoundary(preamble, MAX_PREAMBLE_CHARS).content;
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
// ─── Executor Constraints ─────────────────────────────────────────────────────
|
|
27
36
|
|
|
28
37
|
/**
|
|
@@ -157,7 +166,6 @@ export async function inlineFileSmart(
|
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
// For large files, truncate at section boundary
|
|
160
|
-
const { truncateAtSectionBoundary } = await import("./context-budget.js");
|
|
161
169
|
const truncated = truncateAtSectionBoundary(content, threshold).content;
|
|
162
170
|
return `### ${label}\nSource: \`${relPath}\`\n\n${truncated}`;
|
|
163
171
|
}
|
|
@@ -193,7 +201,6 @@ export async function inlineDependencySummaries(
|
|
|
193
201
|
|
|
194
202
|
const result = sections.join("\n\n");
|
|
195
203
|
if (budgetChars !== undefined && result.length > budgetChars) {
|
|
196
|
-
const { truncateAtSectionBoundary } = await import("./context-budget.js");
|
|
197
204
|
return truncateAtSectionBoundary(result, budgetChars).content;
|
|
198
205
|
}
|
|
199
206
|
return result;
|
|
@@ -611,7 +618,7 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
611
618
|
if (knowledgeInlineRM) inlined.push(knowledgeInlineRM);
|
|
612
619
|
inlined.push(inlineTemplate("research", "Research"));
|
|
613
620
|
|
|
614
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
621
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
615
622
|
|
|
616
623
|
const outputRelPath = relMilestoneFile(base, mid, "RESEARCH");
|
|
617
624
|
return loadPrompt("research-milestone", {
|
|
@@ -661,7 +668,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
661
668
|
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
662
669
|
}
|
|
663
670
|
|
|
664
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
671
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
665
672
|
|
|
666
673
|
const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
|
|
667
674
|
const researchOutputPath = join(base, relMilestoneFile(base, mid, "RESEARCH"));
|
|
@@ -710,7 +717,7 @@ export async function buildResearchSlicePrompt(
|
|
|
710
717
|
const overridesInline = formatOverridesSection(activeOverrides);
|
|
711
718
|
if (overridesInline) inlined.unshift(overridesInline);
|
|
712
719
|
|
|
713
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
720
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
714
721
|
|
|
715
722
|
const outputRelPath = relSliceFile(base, mid, sid, "RESEARCH");
|
|
716
723
|
return loadPrompt("research-slice", {
|
|
@@ -758,7 +765,7 @@ export async function buildPlanSlicePrompt(
|
|
|
758
765
|
const planOverridesInline = formatOverridesSection(planActiveOverrides);
|
|
759
766
|
if (planOverridesInline) inlined.unshift(planOverridesInline);
|
|
760
767
|
|
|
761
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
768
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
762
769
|
|
|
763
770
|
// Build executor context constraints from the budget engine
|
|
764
771
|
const executorContextConstraints = formatExecutorConstraints();
|
|
@@ -881,7 +888,6 @@ export async function buildExecuteTaskPrompt(
|
|
|
881
888
|
const carryForwardBudget = Math.floor(budgets.inlineContextBudgetChars * 0.4);
|
|
882
889
|
let finalCarryForward = carryForwardSection;
|
|
883
890
|
if (carryForwardSection.length > carryForwardBudget) {
|
|
884
|
-
const { truncateAtSectionBoundary } = await import("./context-budget.js");
|
|
885
891
|
finalCarryForward = truncateAtSectionBoundary(carryForwardSection, carryForwardBudget).content;
|
|
886
892
|
}
|
|
887
893
|
|
|
@@ -945,7 +951,7 @@ export async function buildCompleteSlicePrompt(
|
|
|
945
951
|
const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
|
|
946
952
|
if (completeOverridesInline) inlined.unshift(completeOverridesInline);
|
|
947
953
|
|
|
948
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
954
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
949
955
|
|
|
950
956
|
const sliceRel = relSlicePath(base, mid, sid);
|
|
951
957
|
const sliceSummaryPath = join(base, `${sliceRel}/${sid}-SUMMARY.md`);
|
|
@@ -1004,7 +1010,7 @@ export async function buildCompleteMilestonePrompt(
|
|
|
1004
1010
|
if (contextInline) inlined.push(contextInline);
|
|
1005
1011
|
inlined.push(inlineTemplate("milestone-summary", "Milestone Summary"));
|
|
1006
1012
|
|
|
1007
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1013
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1008
1014
|
|
|
1009
1015
|
const milestoneSummaryPath = join(base, `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md`);
|
|
1010
1016
|
|
|
@@ -1075,7 +1081,7 @@ export async function buildValidateMilestonePrompt(
|
|
|
1075
1081
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
1076
1082
|
if (contextInline) inlined.push(contextInline);
|
|
1077
1083
|
|
|
1078
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1084
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1079
1085
|
|
|
1080
1086
|
const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
|
|
1081
1087
|
const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
|
|
@@ -1129,7 +1135,7 @@ export async function buildReplanSlicePrompt(
|
|
|
1129
1135
|
const replanOverridesInline = formatOverridesSection(replanActiveOverrides);
|
|
1130
1136
|
if (replanOverridesInline) inlined.unshift(replanOverridesInline);
|
|
1131
1137
|
|
|
1132
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1138
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1133
1139
|
|
|
1134
1140
|
const replanPath = join(base, `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md`);
|
|
1135
1141
|
|
|
@@ -1177,7 +1183,7 @@ export async function buildRunUatPrompt(
|
|
|
1177
1183
|
const projectInline = await inlineProjectFromDb(base);
|
|
1178
1184
|
if (projectInline) inlined.push(projectInline);
|
|
1179
1185
|
|
|
1180
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1186
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1181
1187
|
|
|
1182
1188
|
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "UAT-RESULT"));
|
|
1183
1189
|
const uatType = extractUatType(uatContent) ?? "human-experience";
|
|
@@ -1216,7 +1222,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1216
1222
|
const knowledgeInlineRA = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
1217
1223
|
if (knowledgeInlineRA) inlined.push(knowledgeInlineRA);
|
|
1218
1224
|
|
|
1219
|
-
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}
|
|
1225
|
+
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1220
1226
|
|
|
1221
1227
|
const assessmentPath = join(base, relSliceFile(base, mid, completedSliceId, "ASSESSMENT"));
|
|
1222
1228
|
|
|
@@ -37,13 +37,13 @@ import {
|
|
|
37
37
|
resolveGitHeadPath,
|
|
38
38
|
nudgeGitBranchCache,
|
|
39
39
|
} from "./worktree.js";
|
|
40
|
-
import { MergeConflictError, readIntegrationBranch } from "./git-service.js";
|
|
40
|
+
import { MergeConflictError, readIntegrationBranch, RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
|
|
41
41
|
import { parseRoadmap } from "./files.js";
|
|
42
42
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
43
43
|
import {
|
|
44
44
|
nativeGetCurrentBranch,
|
|
45
45
|
nativeWorkingTreeStatus,
|
|
46
|
-
|
|
46
|
+
nativeAddAllWithExclusions,
|
|
47
47
|
nativeCommit,
|
|
48
48
|
nativeCheckoutBranch,
|
|
49
49
|
nativeMergeSquash,
|
|
@@ -768,7 +768,7 @@ function autoCommitDirtyState(cwd: string): boolean {
|
|
|
768
768
|
try {
|
|
769
769
|
const status = nativeWorkingTreeStatus(cwd);
|
|
770
770
|
if (!status) return false;
|
|
771
|
-
|
|
771
|
+
nativeAddAllWithExclusions(cwd, RUNTIME_EXCLUSION_PATHS);
|
|
772
772
|
const result = nativeCommit(
|
|
773
773
|
cwd,
|
|
774
774
|
"chore: auto-commit before milestone merge",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* One command, one wizard. Routes to smart entry or status.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type
|
|
7
|
+
import { importExtensionModule, type ExtensionAPI, type ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
8
8
|
import type { GSDState } from "./types.js";
|
|
9
9
|
import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
10
10
|
import { homedir } from "node:os";
|
|
@@ -585,7 +585,7 @@ export async function handleGSDCommand(
|
|
|
585
585
|
}
|
|
586
586
|
|
|
587
587
|
if (trimmed === "widget" || trimmed.startsWith("widget ")) {
|
|
588
|
-
const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await import("./auto-dashboard.js");
|
|
588
|
+
const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await importExtensionModule<typeof import("./auto-dashboard.js")>(import.meta.url, "./auto-dashboard.js");
|
|
589
589
|
const arg = trimmed.replace(/^widget\s*/, "").trim();
|
|
590
590
|
if (arg === "full" || arg === "small" || arg === "min" || arg === "off") {
|
|
591
591
|
setWidgetMode(arg);
|
|
@@ -280,9 +280,24 @@ async function markSliceDoneInRoadmap(basePath: string, milestoneId: string, sli
|
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
async function markSliceUndoneInRoadmap(basePath: string, milestoneId: string, sliceId: string, fixesApplied: string[]): Promise<void> {
|
|
284
|
+
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
285
|
+
if (!roadmapPath) return;
|
|
286
|
+
const content = await loadFile(roadmapPath);
|
|
287
|
+
if (!content) return;
|
|
288
|
+
const updated = content.replace(
|
|
289
|
+
new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${sliceId}:`, "m"),
|
|
290
|
+
`$1[ ] **${sliceId}:`,
|
|
291
|
+
);
|
|
292
|
+
if (updated !== content) {
|
|
293
|
+
await saveFile(roadmapPath, updated);
|
|
294
|
+
fixesApplied.push(`unmarked ${sliceId} in ${roadmapPath} (premature completion)`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
283
298
|
function matchesScope(unitId: string, scope?: string): boolean {
|
|
284
299
|
if (!scope) return true;
|
|
285
|
-
return unitId === scope || unitId.startsWith(`${scope}/`)
|
|
300
|
+
return unitId === scope || unitId.startsWith(`${scope}/`);
|
|
286
301
|
}
|
|
287
302
|
|
|
288
303
|
function auditRequirements(content: string | null): DoctorIssue[] {
|
|
@@ -863,6 +878,12 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
|
|
|
863
878
|
file: relSliceFile(basePath, milestoneId, slice.id, "SUMMARY"),
|
|
864
879
|
fixable: true,
|
|
865
880
|
});
|
|
881
|
+
if (!allTasksDone) {
|
|
882
|
+
dryRunCanFix("slice_checked_missing_summary", `uncheck ${slice.id} in roadmap (tasks incomplete)`);
|
|
883
|
+
if (shouldFix("slice_checked_missing_summary")) {
|
|
884
|
+
await markSliceUndoneInRoadmap(basePath, milestoneId, slice.id, fixesApplied);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
866
887
|
}
|
|
867
888
|
|
|
868
889
|
if (slice.done && !hasSliceUat) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { importExtensionModule, type ExtensionAPI, type ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
2
2
|
|
|
3
3
|
type StopAutoFn = (ctx: ExtensionCommandContext, pi: ExtensionAPI, reason?: string) => Promise<void>;
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ export function registerExitCommand(
|
|
|
10
10
|
description: "Exit GSD gracefully",
|
|
11
11
|
handler: async (_args: string, ctx: ExtensionCommandContext) => {
|
|
12
12
|
// Stop auto-mode first so locks and activity state are cleaned up before shutdown.
|
|
13
|
-
const stopAuto = deps.stopAuto ?? (await import("./auto.js")).stopAuto;
|
|
13
|
+
const stopAuto = deps.stopAuto ?? (await importExtensionModule<typeof import("./auto.js")>(import.meta.url, "./auto.js")).stopAuto;
|
|
14
14
|
await stopAuto(ctx, pi, "Graceful exit");
|
|
15
15
|
ctx.shutdown();
|
|
16
16
|
},
|
|
@@ -775,7 +775,7 @@ export function parseTaskPlanIO(content: string): { inputFiles: string[]; output
|
|
|
775
775
|
* The four UAT classification types recognised by GSD auto-mode.
|
|
776
776
|
* `undefined` is returned (not this union) when no type can be determined.
|
|
777
777
|
*/
|
|
778
|
-
export type UatType = 'artifact-driven' | 'live-runtime' | 'human-experience' | 'mixed';
|
|
778
|
+
export type UatType = 'artifact-driven' | 'live-runtime' | 'human-experience' | 'mixed' | 'browser-executable' | 'runtime-executable';
|
|
779
779
|
|
|
780
780
|
/**
|
|
781
781
|
* Extract the UAT type from a UAT file's raw content.
|
|
@@ -799,6 +799,8 @@ export function extractUatType(content: string): UatType | undefined {
|
|
|
799
799
|
const rawValue = modeBullet.slice('UAT mode:'.length).trim().toLowerCase();
|
|
800
800
|
|
|
801
801
|
if (rawValue.startsWith('artifact-driven')) return 'artifact-driven';
|
|
802
|
+
if (rawValue.startsWith('browser-executable')) return 'browser-executable';
|
|
803
|
+
if (rawValue.startsWith('runtime-executable')) return 'runtime-executable';
|
|
802
804
|
if (rawValue.startsWith('live-runtime')) return 'live-runtime';
|
|
803
805
|
if (rawValue.startsWith('human-experience')) return 'human-experience';
|
|
804
806
|
if (rawValue.startsWith('mixed')) return 'mixed';
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
nativeDetectMainBranch,
|
|
25
25
|
nativeBranchExists,
|
|
26
26
|
nativeHasChanges,
|
|
27
|
-
|
|
27
|
+
nativeAddAllWithExclusions,
|
|
28
28
|
nativeResetPaths,
|
|
29
29
|
nativeHasStagedChanges,
|
|
30
30
|
nativeCommit,
|
|
@@ -385,7 +385,9 @@ export class GitServiceImpl {
|
|
|
385
385
|
this._runtimeFilesCleanedUp = true;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
// Stage everything
|
|
388
|
+
// Stage everything using pathspec exclusions so excluded paths are never
|
|
389
|
+
// hashed by git. The old approach of `git add -A` followed by unstaging
|
|
390
|
+
// hangs indefinitely on repos with large untracked artifact trees (#1605).
|
|
389
391
|
//
|
|
390
392
|
// Exclude only RUNTIME paths from staging — not the entire .gsd/ directory.
|
|
391
393
|
// When .gsd/milestones/ files are already tracked in the index (projects
|
|
@@ -395,13 +397,9 @@ export class GitServiceImpl {
|
|
|
395
397
|
// the second half of a milestone's artifacts are never committed (#1326).
|
|
396
398
|
//
|
|
397
399
|
// If .gsd/ IS in .gitignore (the default for external state projects),
|
|
398
|
-
// git add -A already skips it and the
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const runtimeExclusions = [...RUNTIME_EXCLUSION_PATHS, ...extraExclusions];
|
|
402
|
-
for (const exclusion of runtimeExclusions) {
|
|
403
|
-
try { nativeResetPaths(this.basePath, [exclusion]); } catch { /* path not staged — ignore */ }
|
|
404
|
-
}
|
|
400
|
+
// git add -A already skips it and the exclusions are harmless no-ops.
|
|
401
|
+
const allExclusions = [...RUNTIME_EXCLUSION_PATHS, ...extraExclusions];
|
|
402
|
+
nativeAddAllWithExclusions(this.basePath, allExclusions);
|
|
405
403
|
}
|
|
406
404
|
|
|
407
405
|
/** Tracks whether runtime file cleanup has run this session. */
|
|
@@ -586,6 +584,30 @@ export class GitServiceImpl {
|
|
|
586
584
|
|
|
587
585
|
}
|
|
588
586
|
|
|
587
|
+
// ─── Draft PR Creation ─────────────────────────────────────────────────────
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Create a draft pull request for a completed milestone using `gh pr create`.
|
|
591
|
+
* Returns the PR URL on success, or null on failure.
|
|
592
|
+
* Non-fatal: callers should treat failure as best-effort.
|
|
593
|
+
*/
|
|
594
|
+
export function createDraftPR(
|
|
595
|
+
basePath: string,
|
|
596
|
+
milestoneId: string,
|
|
597
|
+
title: string,
|
|
598
|
+
body: string,
|
|
599
|
+
): string | null {
|
|
600
|
+
try {
|
|
601
|
+
const result = execSync(
|
|
602
|
+
`gh pr create --draft --title ${JSON.stringify(title)} --body ${JSON.stringify(body)}`,
|
|
603
|
+
{ cwd: basePath, encoding: "utf8", timeout: 30000, env: GIT_NO_PROMPT_ENV },
|
|
604
|
+
);
|
|
605
|
+
return result.trim();
|
|
606
|
+
} catch {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
589
611
|
// ─── Factory ───────────────────────────────────────────────────────────────
|
|
590
612
|
|
|
591
613
|
/** Create a GitServiceImpl with the current effective git preferences. */
|
|
@@ -34,6 +34,7 @@ import { showConfirm } from "../shared/mod.js";
|
|
|
34
34
|
import { debugLog } from "./debug-logger.js";
|
|
35
35
|
import { findMilestoneIds, nextMilestoneId } from "./milestone-ids.js";
|
|
36
36
|
import { parkMilestone, discardMilestone } from "./milestone-actions.js";
|
|
37
|
+
import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
|
|
37
38
|
|
|
38
39
|
// ─── Re-exports (preserve public API for existing importers) ────────────────
|
|
39
40
|
export {
|
|
@@ -190,8 +191,40 @@ type UIContext = ExtensionContext;
|
|
|
190
191
|
/**
|
|
191
192
|
* Read GSD-WORKFLOW.md and dispatch it to the LLM with a contextual note.
|
|
192
193
|
* This is the only way the wizard triggers work — everything else is the LLM's job.
|
|
194
|
+
*
|
|
195
|
+
* When a unitType is provided, resolves the user's model preference for that
|
|
196
|
+
* phase (e.g., models.planning → "plan-milestone") and applies it before
|
|
197
|
+
* dispatching. This ensures guided-flow dispatches respect the same
|
|
198
|
+
* per-phase model preferences that auto-mode uses.
|
|
193
199
|
*/
|
|
194
|
-
function dispatchWorkflow(
|
|
200
|
+
async function dispatchWorkflow(
|
|
201
|
+
pi: ExtensionAPI,
|
|
202
|
+
note: string,
|
|
203
|
+
customType = "gsd-run",
|
|
204
|
+
ctx?: ExtensionContext,
|
|
205
|
+
unitType?: string,
|
|
206
|
+
): Promise<void> {
|
|
207
|
+
// Apply model preference for this unit type (if configured)
|
|
208
|
+
if (ctx && unitType) {
|
|
209
|
+
const modelConfig = resolveModelWithFallbacksForUnit(unitType);
|
|
210
|
+
if (modelConfig) {
|
|
211
|
+
const availableModels = ctx.modelRegistry.getAvailable();
|
|
212
|
+
const modelsToTry = [modelConfig.primary, ...modelConfig.fallbacks];
|
|
213
|
+
|
|
214
|
+
for (const modelId of modelsToTry) {
|
|
215
|
+
// Resolve model from available models (same logic as auto-model-selection)
|
|
216
|
+
const model = resolveAvailableModel(modelId, availableModels, ctx.model?.provider);
|
|
217
|
+
if (!model) continue;
|
|
218
|
+
|
|
219
|
+
const ok = await pi.setModel(model, { persist: false });
|
|
220
|
+
if (ok) {
|
|
221
|
+
debugLog("guided-flow-model-applied", { unitType, model: `${model.provider}/${model.id}` });
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
195
228
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".gsd", "agent", "GSD-WORKFLOW.md");
|
|
196
229
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
197
230
|
|
|
@@ -205,6 +238,45 @@ function dispatchWorkflow(pi: ExtensionAPI, note: string, customType = "gsd-run"
|
|
|
205
238
|
);
|
|
206
239
|
}
|
|
207
240
|
|
|
241
|
+
/**
|
|
242
|
+
* Resolve a model ID string to a model object from available models.
|
|
243
|
+
* Handles "provider/model" and bare ID formats.
|
|
244
|
+
*/
|
|
245
|
+
function resolveAvailableModel<T extends { id: string; provider: string }>(
|
|
246
|
+
modelId: string,
|
|
247
|
+
availableModels: T[],
|
|
248
|
+
currentProvider: string | undefined,
|
|
249
|
+
): T | undefined {
|
|
250
|
+
const slashIdx = modelId.indexOf("/");
|
|
251
|
+
|
|
252
|
+
if (slashIdx !== -1) {
|
|
253
|
+
const maybeProvider = modelId.substring(0, slashIdx);
|
|
254
|
+
const id = modelId.substring(slashIdx + 1);
|
|
255
|
+
|
|
256
|
+
const knownProviders = new Set(availableModels.map(m => m.provider.toLowerCase()));
|
|
257
|
+
if (knownProviders.has(maybeProvider.toLowerCase())) {
|
|
258
|
+
const match = availableModels.find(
|
|
259
|
+
m => m.provider.toLowerCase() === maybeProvider.toLowerCase()
|
|
260
|
+
&& m.id.toLowerCase() === id.toLowerCase(),
|
|
261
|
+
);
|
|
262
|
+
if (match) return match;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Try matching the full string as a model ID (OpenRouter-style)
|
|
266
|
+
const lower = modelId.toLowerCase();
|
|
267
|
+
return availableModels.find(
|
|
268
|
+
m => m.id.toLowerCase() === lower
|
|
269
|
+
|| `${m.provider}/${m.id}`.toLowerCase() === lower,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Bare ID — prefer current provider, then first available
|
|
274
|
+
const exactProviderMatch = availableModels.find(
|
|
275
|
+
m => m.id === modelId && m.provider === currentProvider,
|
|
276
|
+
);
|
|
277
|
+
return exactProviderMatch ?? availableModels.find(m => m.id === modelId);
|
|
278
|
+
}
|
|
279
|
+
|
|
208
280
|
/**
|
|
209
281
|
* Build the discuss-and-plan prompt for a new milestone.
|
|
210
282
|
* Used by all three "new milestone" paths (first ever, no active, all complete).
|
|
@@ -301,8 +373,8 @@ export async function showHeadlessMilestoneCreation(
|
|
|
301
373
|
// Set pending auto start (auto-mode triggers on "Milestone X ready." via checkAutoStartAfterDiscuss)
|
|
302
374
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId };
|
|
303
375
|
|
|
304
|
-
// Dispatch
|
|
305
|
-
dispatchWorkflow(pi, prompt);
|
|
376
|
+
// Dispatch — headless milestone creation is a planning activity
|
|
377
|
+
await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "plan-milestone");
|
|
306
378
|
}
|
|
307
379
|
|
|
308
380
|
|
|
@@ -467,21 +539,21 @@ export async function showDiscuss(
|
|
|
467
539
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
468
540
|
: basePrompt;
|
|
469
541
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: mid, step: false };
|
|
470
|
-
dispatchWorkflow(pi, seed, "gsd-discuss");
|
|
542
|
+
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "plan-milestone");
|
|
471
543
|
} else if (choice === "discuss_fresh") {
|
|
472
544
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
473
545
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
474
546
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: mid, step: false };
|
|
475
|
-
dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
547
|
+
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
476
548
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
477
549
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
478
|
-
}), "gsd-discuss");
|
|
550
|
+
}), "gsd-discuss", ctx, "plan-milestone");
|
|
479
551
|
} else if (choice === "skip_milestone") {
|
|
480
552
|
const milestoneIds = findMilestoneIds(basePath);
|
|
481
553
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
482
554
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
483
555
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: false };
|
|
484
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath));
|
|
556
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "plan-milestone");
|
|
485
557
|
}
|
|
486
558
|
return;
|
|
487
559
|
}
|
|
@@ -580,7 +652,7 @@ export async function showDiscuss(
|
|
|
580
652
|
}
|
|
581
653
|
|
|
582
654
|
const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss });
|
|
583
|
-
dispatchWorkflow(pi, prompt, "gsd-discuss");
|
|
655
|
+
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "plan-slice");
|
|
584
656
|
|
|
585
657
|
// Wait for the discuss session to finish, then loop back to the picker
|
|
586
658
|
await ctx.waitForIdle();
|
|
@@ -722,10 +794,10 @@ async function handleMilestoneActions(
|
|
|
722
794
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
723
795
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
724
796
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
725
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
797
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
726
798
|
`New milestone ${nextId}.`,
|
|
727
799
|
basePath
|
|
728
|
-
));
|
|
800
|
+
), "gsd-run", ctx, "plan-milestone");
|
|
729
801
|
return true;
|
|
730
802
|
}
|
|
731
803
|
|
|
@@ -866,10 +938,10 @@ export async function showSmartEntry(
|
|
|
866
938
|
if (isFirst) {
|
|
867
939
|
// First ever — skip wizard, just ask directly
|
|
868
940
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
869
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
941
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
870
942
|
`New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`,
|
|
871
943
|
basePath
|
|
872
|
-
));
|
|
944
|
+
), "gsd-run", ctx, "plan-milestone");
|
|
873
945
|
} else {
|
|
874
946
|
const choice = await showNextAction(ctx, {
|
|
875
947
|
title: "GSD — Get Shit Done",
|
|
@@ -887,10 +959,10 @@ export async function showSmartEntry(
|
|
|
887
959
|
|
|
888
960
|
if (choice === "new_milestone") {
|
|
889
961
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
890
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
962
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
891
963
|
`New milestone ${nextId}.`,
|
|
892
964
|
basePath
|
|
893
|
-
));
|
|
965
|
+
), "gsd-run", ctx, "plan-milestone");
|
|
894
966
|
}
|
|
895
967
|
}
|
|
896
968
|
return;
|
|
@@ -926,10 +998,10 @@ export async function showSmartEntry(
|
|
|
926
998
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
927
999
|
|
|
928
1000
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
929
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
1001
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
930
1002
|
`New milestone ${nextId}.`,
|
|
931
1003
|
basePath
|
|
932
|
-
));
|
|
1004
|
+
), "gsd-run", ctx, "plan-milestone");
|
|
933
1005
|
} else if (choice === "status") {
|
|
934
1006
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
935
1007
|
await fireStatusViaCommand(ctx);
|
|
@@ -977,24 +1049,24 @@ export async function showSmartEntry(
|
|
|
977
1049
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
978
1050
|
: basePrompt;
|
|
979
1051
|
pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
|
|
980
|
-
dispatchWorkflow(pi, seed, "gsd-discuss");
|
|
1052
|
+
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "plan-milestone");
|
|
981
1053
|
} else if (choice === "discuss_fresh") {
|
|
982
1054
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
983
1055
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
984
1056
|
pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
|
|
985
|
-
dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1057
|
+
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
986
1058
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
987
1059
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
988
|
-
}), "gsd-discuss");
|
|
1060
|
+
}), "gsd-discuss", ctx, "plan-milestone");
|
|
989
1061
|
} else if (choice === "skip_milestone") {
|
|
990
1062
|
const milestoneIds = findMilestoneIds(basePath);
|
|
991
1063
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
992
1064
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
993
1065
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
994
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
1066
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
995
1067
|
`New milestone ${nextId}.`,
|
|
996
1068
|
basePath
|
|
997
|
-
));
|
|
1069
|
+
), "gsd-run", ctx, "plan-milestone");
|
|
998
1070
|
}
|
|
999
1071
|
return;
|
|
1000
1072
|
}
|
|
@@ -1051,25 +1123,25 @@ export async function showSmartEntry(
|
|
|
1051
1123
|
inlineTemplate("secrets-manifest", "Secrets Manifest"),
|
|
1052
1124
|
].join("\n\n---\n\n");
|
|
1053
1125
|
const secretsOutputPath = relMilestoneFile(basePath, milestoneId, "SECRETS");
|
|
1054
|
-
dispatchWorkflow(pi, loadPrompt("guided-plan-milestone", {
|
|
1126
|
+
await dispatchWorkflow(pi, loadPrompt("guided-plan-milestone", {
|
|
1055
1127
|
milestoneId, milestoneTitle, secretsOutputPath, inlinedTemplates: planMilestoneTemplates,
|
|
1056
|
-
}));
|
|
1128
|
+
}), "gsd-run", ctx, "plan-milestone");
|
|
1057
1129
|
} else if (choice === "discuss") {
|
|
1058
1130
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1059
1131
|
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
|
1060
|
-
dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1132
|
+
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1061
1133
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1062
1134
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
1063
|
-
}));
|
|
1135
|
+
}), "gsd-run", ctx, "plan-milestone");
|
|
1064
1136
|
} else if (choice === "skip_milestone") {
|
|
1065
1137
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1066
1138
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1067
1139
|
const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
|
|
1068
1140
|
pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
|
|
1069
|
-
dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
1141
|
+
await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
|
|
1070
1142
|
`New milestone ${nextId}.`,
|
|
1071
1143
|
basePath
|
|
1072
|
-
));
|
|
1144
|
+
), "gsd-run", ctx, "plan-milestone");
|
|
1073
1145
|
} else if (choice === "discard_milestone") {
|
|
1074
1146
|
const confirmed = await showConfirm(ctx, {
|
|
1075
1147
|
title: "Discard milestone?",
|
|
@@ -1181,16 +1253,16 @@ export async function showSmartEntry(
|
|
|
1181
1253
|
inlineTemplate("plan", "Slice Plan"),
|
|
1182
1254
|
inlineTemplate("task-plan", "Task Plan"),
|
|
1183
1255
|
].join("\n\n---\n\n");
|
|
1184
|
-
dispatchWorkflow(pi, loadPrompt("guided-plan-slice", {
|
|
1256
|
+
await dispatchWorkflow(pi, loadPrompt("guided-plan-slice", {
|
|
1185
1257
|
milestoneId, sliceId, sliceTitle, inlinedTemplates: planSliceTemplates,
|
|
1186
|
-
}));
|
|
1258
|
+
}), "gsd-run", ctx, "plan-slice");
|
|
1187
1259
|
} else if (choice === "discuss") {
|
|
1188
|
-
dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext }));
|
|
1260
|
+
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext }), "gsd-run", ctx, "plan-slice");
|
|
1189
1261
|
} else if (choice === "research") {
|
|
1190
1262
|
const researchTemplates = inlineTemplate("research", "Research");
|
|
1191
|
-
dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
|
|
1263
|
+
await dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
|
|
1192
1264
|
milestoneId, sliceId, sliceTitle, inlinedTemplates: researchTemplates,
|
|
1193
|
-
}));
|
|
1265
|
+
}), "gsd-run", ctx, "research-slice");
|
|
1194
1266
|
} else if (choice === "status") {
|
|
1195
1267
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
1196
1268
|
await fireStatusViaCommand(ctx);
|
|
@@ -1232,9 +1304,9 @@ export async function showSmartEntry(
|
|
|
1232
1304
|
inlineTemplate("slice-summary", "Slice Summary"),
|
|
1233
1305
|
inlineTemplate("uat", "UAT"),
|
|
1234
1306
|
].join("\n\n---\n\n");
|
|
1235
|
-
dispatchWorkflow(pi, loadPrompt("guided-complete-slice", {
|
|
1307
|
+
await dispatchWorkflow(pi, loadPrompt("guided-complete-slice", {
|
|
1236
1308
|
workingDirectory: basePath, milestoneId, sliceId, sliceTitle, inlinedTemplates: completeSliceTemplates,
|
|
1237
|
-
}));
|
|
1309
|
+
}), "gsd-run", ctx, "complete-slice");
|
|
1238
1310
|
} else if (choice === "status") {
|
|
1239
1311
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
1240
1312
|
await fireStatusViaCommand(ctx);
|
|
@@ -1297,14 +1369,14 @@ export async function showSmartEntry(
|
|
|
1297
1369
|
|
|
1298
1370
|
if (choice === "execute") {
|
|
1299
1371
|
if (hasInterrupted) {
|
|
1300
|
-
dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
|
|
1372
|
+
await dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
|
|
1301
1373
|
milestoneId, sliceId,
|
|
1302
|
-
}));
|
|
1374
|
+
}), "gsd-run", ctx, "execute-task");
|
|
1303
1375
|
} else {
|
|
1304
1376
|
const executeTaskTemplates = inlineTemplate("task-summary", "Task Summary");
|
|
1305
|
-
dispatchWorkflow(pi, loadPrompt("guided-execute-task", {
|
|
1377
|
+
await dispatchWorkflow(pi, loadPrompt("guided-execute-task", {
|
|
1306
1378
|
milestoneId, sliceId, taskId, taskTitle, inlinedTemplates: executeTaskTemplates,
|
|
1307
|
-
}));
|
|
1379
|
+
}), "gsd-run", ctx, "execute-task");
|
|
1308
1380
|
}
|
|
1309
1381
|
} else if (choice === "status") {
|
|
1310
1382
|
const { fireStatusViaCommand } = await import("./commands.js");
|