gsd-pi 2.26.0 → 2.27.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 +43 -6
- package/dist/cli.js +4 -2
- package/dist/headless.d.ts +3 -0
- package/dist/headless.js +136 -8
- package/dist/help-text.js +3 -0
- package/dist/loader.js +33 -4
- package/dist/resources/extensions/bg-shell/index.ts +19 -2
- package/dist/resources/extensions/bg-shell/process-manager.ts +45 -0
- package/dist/resources/extensions/bg-shell/types.ts +21 -1
- package/dist/resources/extensions/gsd/auto/session.ts +224 -0
- package/dist/resources/extensions/gsd/auto-budget.ts +32 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +63 -10
- package/dist/resources/extensions/gsd/auto-direct-dispatch.ts +229 -0
- package/dist/resources/extensions/gsd/auto-dispatch.ts +23 -10
- package/dist/resources/extensions/gsd/auto-model-selection.ts +179 -0
- package/dist/resources/extensions/gsd/auto-observability.ts +74 -0
- package/dist/resources/extensions/gsd/auto-prompts.ts +0 -1
- package/dist/resources/extensions/gsd/auto-timeout-recovery.ts +262 -0
- package/dist/resources/extensions/gsd/auto-tool-tracking.ts +54 -0
- package/dist/resources/extensions/gsd/auto-unit-closeout.ts +46 -0
- package/dist/resources/extensions/gsd/auto-worktree-sync.ts +207 -0
- package/dist/resources/extensions/gsd/auto.ts +977 -1551
- package/dist/resources/extensions/gsd/commands.ts +3 -3
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +47 -72
- package/dist/resources/extensions/gsd/doctor-proactive.ts +9 -4
- package/dist/resources/extensions/gsd/export-html.ts +1001 -0
- package/dist/resources/extensions/gsd/export.ts +49 -1
- package/dist/resources/extensions/gsd/git-service.ts +6 -0
- package/dist/resources/extensions/gsd/gitignore.ts +4 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +24 -5
- package/dist/resources/extensions/gsd/index.ts +54 -1
- package/dist/resources/extensions/gsd/native-git-bridge.ts +30 -2
- package/dist/resources/extensions/gsd/observability-validator.ts +21 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +231 -20
- package/dist/resources/extensions/gsd/preferences.ts +62 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -3
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/reports.ts +510 -0
- package/dist/resources/extensions/gsd/roadmap-slices.ts +1 -1
- package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +178 -0
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +54 -0
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +59 -0
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +185 -0
- package/dist/resources/extensions/gsd/state.ts +30 -0
- package/dist/resources/extensions/gsd/templates/task-summary.md +9 -0
- package/dist/resources/extensions/gsd/tests/auto-dashboard.test.ts +13 -0
- package/dist/resources/extensions/gsd/tests/continue-here.test.ts +81 -0
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +5 -0
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/derive-state.test.ts +10 -1
- package/dist/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +132 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +14 -0
- package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/native-has-changes-cache.test.ts +61 -0
- package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +51 -1
- package/dist/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +331 -0
- package/dist/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +298 -0
- package/dist/resources/extensions/gsd/tests/parallel-merge.test.ts +465 -0
- package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +39 -10
- package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +71 -0
- package/dist/resources/extensions/gsd/tests/replan-slice.test.ts +42 -0
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +9 -9
- package/dist/resources/extensions/gsd/tests/verification-evidence.test.ts +743 -0
- package/dist/resources/extensions/gsd/tests/verification-gate.test.ts +965 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +44 -10
- package/dist/resources/extensions/gsd/tests/worktree.test.ts +3 -1
- package/dist/resources/extensions/gsd/types.ts +38 -0
- package/dist/resources/extensions/gsd/verification-evidence.ts +183 -0
- package/dist/resources/extensions/gsd/verification-gate.ts +567 -0
- package/dist/resources/extensions/gsd/visualizer-data.ts +25 -3
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +31 -21
- package/dist/resources/extensions/gsd/visualizer-views.ts +15 -66
- package/dist/resources/extensions/search-the-web/tool-search.ts +26 -0
- package/dist/resources/extensions/shared/format-utils.ts +85 -0
- package/dist/resources/extensions/shared/tests/format-utils.test.ts +153 -0
- package/dist/resources/extensions/subagent/index.ts +46 -1
- package/dist/resources/extensions/subagent/isolation.ts +9 -6
- package/package.json +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +7 -4
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-completions.ts +7 -4
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +7 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +9 -2
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/src/core/lsp/client.ts +8 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +9 -2
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +1 -1
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +3 -1
- package/scripts/link-workspace-packages.cjs +22 -6
- package/src/resources/extensions/bg-shell/index.ts +19 -2
- package/src/resources/extensions/bg-shell/process-manager.ts +45 -0
- package/src/resources/extensions/bg-shell/types.ts +21 -1
- package/src/resources/extensions/gsd/auto/session.ts +224 -0
- package/src/resources/extensions/gsd/auto-budget.ts +32 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +63 -10
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +229 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +23 -10
- package/src/resources/extensions/gsd/auto-model-selection.ts +179 -0
- package/src/resources/extensions/gsd/auto-observability.ts +74 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +0 -1
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +262 -0
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +54 -0
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +46 -0
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +207 -0
- package/src/resources/extensions/gsd/auto.ts +977 -1551
- package/src/resources/extensions/gsd/commands.ts +3 -3
- package/src/resources/extensions/gsd/dashboard-overlay.ts +47 -72
- package/src/resources/extensions/gsd/doctor-proactive.ts +9 -4
- package/src/resources/extensions/gsd/export-html.ts +1001 -0
- package/src/resources/extensions/gsd/export.ts +49 -1
- package/src/resources/extensions/gsd/git-service.ts +6 -0
- package/src/resources/extensions/gsd/gitignore.ts +4 -1
- package/src/resources/extensions/gsd/guided-flow.ts +24 -5
- package/src/resources/extensions/gsd/index.ts +54 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +30 -2
- package/src/resources/extensions/gsd/observability-validator.ts +21 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +231 -20
- package/src/resources/extensions/gsd/preferences.ts +62 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -3
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/reports.ts +510 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +1 -1
- package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +178 -0
- package/src/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +54 -0
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +59 -0
- package/src/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +185 -0
- package/src/resources/extensions/gsd/state.ts +30 -0
- package/src/resources/extensions/gsd/templates/task-summary.md +9 -0
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/native-has-changes-cache.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +51 -1
- package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +331 -0
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +298 -0
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +465 -0
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +39 -10
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +9 -9
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +743 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +965 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +44 -10
- package/src/resources/extensions/gsd/tests/worktree.test.ts +3 -1
- package/src/resources/extensions/gsd/types.ts +38 -0
- package/src/resources/extensions/gsd/verification-evidence.ts +183 -0
- package/src/resources/extensions/gsd/verification-gate.ts +567 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +25 -3
- package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -21
- package/src/resources/extensions/gsd/visualizer-views.ts +15 -66
- package/src/resources/extensions/search-the-web/tool-search.ts +26 -0
- package/src/resources/extensions/shared/format-utils.ts +85 -0
- package/src/resources/extensions/shared/tests/format-utils.test.ts +153 -0
- package/src/resources/extensions/subagent/index.ts +46 -1
- package/src/resources/extensions/subagent/isolation.ts +9 -6
|
@@ -53,6 +53,10 @@ export interface BgProcess {
|
|
|
53
53
|
label: string;
|
|
54
54
|
command: string;
|
|
55
55
|
cwd: string;
|
|
56
|
+
/** Session file that created this process (used for per-session cleanup) */
|
|
57
|
+
ownerSessionFile: string | null;
|
|
58
|
+
/** Whether this process should survive a new-session boundary */
|
|
59
|
+
persistAcrossSessions: boolean;
|
|
56
60
|
startedAt: number;
|
|
57
61
|
proc: import("node:child_process").ChildProcess;
|
|
58
62
|
/** Unified chronologically-interleaved output buffer */
|
|
@@ -103,7 +107,17 @@ export interface BgProcess {
|
|
|
103
107
|
/** Restart count */
|
|
104
108
|
restartCount: number;
|
|
105
109
|
/** Original start config for restart */
|
|
106
|
-
startConfig: {
|
|
110
|
+
startConfig: {
|
|
111
|
+
command: string;
|
|
112
|
+
cwd: string;
|
|
113
|
+
label: string;
|
|
114
|
+
processType: ProcessType;
|
|
115
|
+
ownerSessionFile: string | null;
|
|
116
|
+
persistAcrossSessions: boolean;
|
|
117
|
+
readyPattern: string | null;
|
|
118
|
+
readyPort: number | null;
|
|
119
|
+
group: string | null;
|
|
120
|
+
};
|
|
107
121
|
}
|
|
108
122
|
|
|
109
123
|
export interface BgProcessInfo {
|
|
@@ -111,6 +125,8 @@ export interface BgProcessInfo {
|
|
|
111
125
|
label: string;
|
|
112
126
|
command: string;
|
|
113
127
|
cwd: string;
|
|
128
|
+
ownerSessionFile: string | null;
|
|
129
|
+
persistAcrossSessions: boolean;
|
|
114
130
|
startedAt: number;
|
|
115
131
|
alive: boolean;
|
|
116
132
|
exitCode: number | null;
|
|
@@ -133,6 +149,8 @@ export interface BgProcessInfo {
|
|
|
133
149
|
export interface StartOptions {
|
|
134
150
|
command: string;
|
|
135
151
|
cwd: string;
|
|
152
|
+
ownerSessionFile?: string | null;
|
|
153
|
+
persistAcrossSessions?: boolean;
|
|
136
154
|
label?: string;
|
|
137
155
|
type?: ProcessType;
|
|
138
156
|
readyPattern?: string;
|
|
@@ -154,6 +172,8 @@ export interface ProcessManifest {
|
|
|
154
172
|
label: string;
|
|
155
173
|
command: string;
|
|
156
174
|
cwd: string;
|
|
175
|
+
ownerSessionFile: string | null;
|
|
176
|
+
persistAcrossSessions: boolean;
|
|
157
177
|
startedAt: number;
|
|
158
178
|
processType: ProcessType;
|
|
159
179
|
group: string | null;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutoSession — encapsulates all mutable auto-mode state into a single instance.
|
|
3
|
+
*
|
|
4
|
+
* Replaces ~40 module-level variables scattered across auto.ts with typed
|
|
5
|
+
* properties on a class instance. Benefits:
|
|
6
|
+
*
|
|
7
|
+
* - reset() clears everything in one call (was 25+ manual resets in stopAuto)
|
|
8
|
+
* - toJSON() provides diagnostic snapshots
|
|
9
|
+
* - grep `s.` shows every state access
|
|
10
|
+
* - Constructable for testing
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
14
|
+
import type { GitServiceImpl } from "../git-service.js";
|
|
15
|
+
import type { CaptureEntry } from "../captures.js";
|
|
16
|
+
import type { BudgetAlertLevel } from "../auto-budget.js";
|
|
17
|
+
|
|
18
|
+
// ─── Exported Types ──────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export interface CompletedUnit {
|
|
21
|
+
type: string;
|
|
22
|
+
id: string;
|
|
23
|
+
startedAt: number;
|
|
24
|
+
finishedAt: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface CurrentUnit {
|
|
28
|
+
type: string;
|
|
29
|
+
id: string;
|
|
30
|
+
startedAt: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UnitRouting {
|
|
34
|
+
tier: string;
|
|
35
|
+
modelDowngraded: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface StartModel {
|
|
39
|
+
provider: string;
|
|
40
|
+
id: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface PendingVerificationRetry {
|
|
44
|
+
unitId: string;
|
|
45
|
+
failureContext: string;
|
|
46
|
+
attempt: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
export const MAX_UNIT_DISPATCHES = 3;
|
|
52
|
+
export const STUB_RECOVERY_THRESHOLD = 2;
|
|
53
|
+
export const MAX_LIFETIME_DISPATCHES = 6;
|
|
54
|
+
export const MAX_CONSECUTIVE_SKIPS = 3;
|
|
55
|
+
export const DISPATCH_GAP_TIMEOUT_MS = 5_000;
|
|
56
|
+
export const MAX_SKIP_DEPTH = 20;
|
|
57
|
+
|
|
58
|
+
// ─── AutoSession ─────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
export class AutoSession {
|
|
61
|
+
// ── Lifecycle ────────────────────────────────────────────────────────────
|
|
62
|
+
active = false;
|
|
63
|
+
paused = false;
|
|
64
|
+
stepMode = false;
|
|
65
|
+
verbose = false;
|
|
66
|
+
cmdCtx: ExtensionCommandContext | null = null;
|
|
67
|
+
|
|
68
|
+
// ── Paths ────────────────────────────────────────────────────────────────
|
|
69
|
+
basePath = "";
|
|
70
|
+
originalBasePath = "";
|
|
71
|
+
gitService: GitServiceImpl | null = null;
|
|
72
|
+
|
|
73
|
+
// ── Dispatch counters ────────────────────────────────────────────────────
|
|
74
|
+
readonly unitDispatchCount = new Map<string, number>();
|
|
75
|
+
readonly unitLifetimeDispatches = new Map<string, number>();
|
|
76
|
+
readonly unitRecoveryCount = new Map<string, number>();
|
|
77
|
+
readonly unitConsecutiveSkips = new Map<string, number>();
|
|
78
|
+
readonly completedKeySet = new Set<string>();
|
|
79
|
+
|
|
80
|
+
// ── Timers ───────────────────────────────────────────────────────────────
|
|
81
|
+
unitTimeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
82
|
+
wrapupWarningHandle: ReturnType<typeof setTimeout> | null = null;
|
|
83
|
+
idleWatchdogHandle: ReturnType<typeof setInterval> | null = null;
|
|
84
|
+
continueHereHandle: ReturnType<typeof setInterval> | null = null;
|
|
85
|
+
dispatchGapHandle: ReturnType<typeof setTimeout> | null = null;
|
|
86
|
+
|
|
87
|
+
// ── Current unit ─────────────────────────────────────────────────────────
|
|
88
|
+
currentUnit: CurrentUnit | null = null;
|
|
89
|
+
currentUnitRouting: UnitRouting | null = null;
|
|
90
|
+
completedUnits: CompletedUnit[] = [];
|
|
91
|
+
currentMilestoneId: string | null = null;
|
|
92
|
+
|
|
93
|
+
// ── Model state ──────────────────────────────────────────────────────────
|
|
94
|
+
autoModeStartModel: StartModel | null = null;
|
|
95
|
+
originalModelId: string | null = null;
|
|
96
|
+
originalModelProvider: string | null = null;
|
|
97
|
+
lastBudgetAlertLevel: BudgetAlertLevel = 0;
|
|
98
|
+
|
|
99
|
+
// ── Recovery ─────────────────────────────────────────────────────────────
|
|
100
|
+
pendingCrashRecovery: string | null = null;
|
|
101
|
+
pendingVerificationRetry: PendingVerificationRetry | null = null;
|
|
102
|
+
readonly verificationRetryCount = new Map<string, number>();
|
|
103
|
+
pausedSessionFile: string | null = null;
|
|
104
|
+
resourceVersionOnStart: string | null = null;
|
|
105
|
+
lastStateRebuildAt = 0;
|
|
106
|
+
|
|
107
|
+
// ── Guards ───────────────────────────────────────────────────────────────
|
|
108
|
+
handlingAgentEnd = false;
|
|
109
|
+
dispatching = false;
|
|
110
|
+
skipDepth = 0;
|
|
111
|
+
readonly recentlyEvictedKeys = new Set<string>();
|
|
112
|
+
|
|
113
|
+
// ── Metrics ──────────────────────────────────────────────────────────────
|
|
114
|
+
autoStartTime = 0;
|
|
115
|
+
lastPromptCharCount: number | undefined;
|
|
116
|
+
lastBaselineCharCount: number | undefined;
|
|
117
|
+
pendingQuickTasks: CaptureEntry[] = [];
|
|
118
|
+
|
|
119
|
+
// ── Signal handler ───────────────────────────────────────────────────────
|
|
120
|
+
sigtermHandler: (() => void) | null = null;
|
|
121
|
+
|
|
122
|
+
// ── Methods ──────────────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
clearTimers(): void {
|
|
125
|
+
if (this.unitTimeoutHandle) { clearTimeout(this.unitTimeoutHandle); this.unitTimeoutHandle = null; }
|
|
126
|
+
if (this.wrapupWarningHandle) { clearTimeout(this.wrapupWarningHandle); this.wrapupWarningHandle = null; }
|
|
127
|
+
if (this.idleWatchdogHandle) { clearInterval(this.idleWatchdogHandle); this.idleWatchdogHandle = null; }
|
|
128
|
+
if (this.continueHereHandle) { clearInterval(this.continueHereHandle); this.continueHereHandle = null; }
|
|
129
|
+
if (this.dispatchGapHandle) { clearTimeout(this.dispatchGapHandle); this.dispatchGapHandle = null; }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
resetDispatchCounters(): void {
|
|
133
|
+
this.unitDispatchCount.clear();
|
|
134
|
+
this.unitLifetimeDispatches.clear();
|
|
135
|
+
this.unitConsecutiveSkips.clear();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get lockBasePath(): string {
|
|
139
|
+
return this.originalBasePath || this.basePath;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
completeCurrentUnit(): CompletedUnit | null {
|
|
143
|
+
if (!this.currentUnit) return null;
|
|
144
|
+
const done: CompletedUnit = { ...this.currentUnit, finishedAt: Date.now() };
|
|
145
|
+
this.completedUnits.push(done);
|
|
146
|
+
this.currentUnit = null;
|
|
147
|
+
return done;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
reset(): void {
|
|
151
|
+
this.clearTimers();
|
|
152
|
+
|
|
153
|
+
// Lifecycle
|
|
154
|
+
this.active = false;
|
|
155
|
+
this.paused = false;
|
|
156
|
+
this.stepMode = false;
|
|
157
|
+
this.verbose = false;
|
|
158
|
+
this.cmdCtx = null;
|
|
159
|
+
|
|
160
|
+
// Paths
|
|
161
|
+
this.basePath = "";
|
|
162
|
+
this.originalBasePath = "";
|
|
163
|
+
this.gitService = null;
|
|
164
|
+
|
|
165
|
+
// Dispatch
|
|
166
|
+
this.unitDispatchCount.clear();
|
|
167
|
+
this.unitLifetimeDispatches.clear();
|
|
168
|
+
this.unitRecoveryCount.clear();
|
|
169
|
+
this.unitConsecutiveSkips.clear();
|
|
170
|
+
// Note: completedKeySet is intentionally NOT cleared — it persists
|
|
171
|
+
// across restarts to prevent re-dispatching completed units.
|
|
172
|
+
|
|
173
|
+
// Unit
|
|
174
|
+
this.currentUnit = null;
|
|
175
|
+
this.currentUnitRouting = null;
|
|
176
|
+
this.completedUnits = [];
|
|
177
|
+
this.currentMilestoneId = null;
|
|
178
|
+
|
|
179
|
+
// Model
|
|
180
|
+
this.autoModeStartModel = null;
|
|
181
|
+
this.originalModelId = null;
|
|
182
|
+
this.originalModelProvider = null;
|
|
183
|
+
this.lastBudgetAlertLevel = 0;
|
|
184
|
+
|
|
185
|
+
// Recovery
|
|
186
|
+
this.pendingCrashRecovery = null;
|
|
187
|
+
this.pendingVerificationRetry = null;
|
|
188
|
+
this.verificationRetryCount.clear();
|
|
189
|
+
this.pausedSessionFile = null;
|
|
190
|
+
this.resourceVersionOnStart = null;
|
|
191
|
+
this.lastStateRebuildAt = 0;
|
|
192
|
+
|
|
193
|
+
// Guards
|
|
194
|
+
this.handlingAgentEnd = false;
|
|
195
|
+
this.dispatching = false;
|
|
196
|
+
this.skipDepth = 0;
|
|
197
|
+
this.recentlyEvictedKeys.clear();
|
|
198
|
+
|
|
199
|
+
// Metrics
|
|
200
|
+
this.autoStartTime = 0;
|
|
201
|
+
this.lastPromptCharCount = undefined;
|
|
202
|
+
this.lastBaselineCharCount = undefined;
|
|
203
|
+
this.pendingQuickTasks = [];
|
|
204
|
+
|
|
205
|
+
// Signal handler
|
|
206
|
+
this.sigtermHandler = null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
toJSON(): Record<string, unknown> {
|
|
210
|
+
return {
|
|
211
|
+
active: this.active,
|
|
212
|
+
paused: this.paused,
|
|
213
|
+
stepMode: this.stepMode,
|
|
214
|
+
basePath: this.basePath,
|
|
215
|
+
currentMilestoneId: this.currentMilestoneId,
|
|
216
|
+
currentUnit: this.currentUnit,
|
|
217
|
+
completedUnits: this.completedUnits.length,
|
|
218
|
+
completedKeySet: this.completedKeySet.size,
|
|
219
|
+
unitDispatchCount: Object.fromEntries(this.unitDispatchCount),
|
|
220
|
+
dispatching: this.dispatching,
|
|
221
|
+
skipDepth: this.skipDepth,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget alert level tracking and enforcement for auto-mode.
|
|
3
|
+
* Pure functions — no module state or side effects.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { BudgetEnforcementMode } from "./types.js";
|
|
7
|
+
|
|
8
|
+
export type BudgetAlertLevel = 0 | 75 | 80 | 90 | 100;
|
|
9
|
+
|
|
10
|
+
export function getBudgetAlertLevel(budgetPct: number): BudgetAlertLevel {
|
|
11
|
+
if (budgetPct >= 1.0) return 100;
|
|
12
|
+
if (budgetPct >= 0.90) return 90;
|
|
13
|
+
if (budgetPct >= 0.80) return 80;
|
|
14
|
+
if (budgetPct >= 0.75) return 75;
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getNewBudgetAlertLevel(previousLevel: BudgetAlertLevel, budgetPct: number): BudgetAlertLevel | null {
|
|
19
|
+
const currentLevel = getBudgetAlertLevel(budgetPct);
|
|
20
|
+
if (currentLevel === 0 || currentLevel <= previousLevel) return null;
|
|
21
|
+
return currentLevel;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getBudgetEnforcementAction(
|
|
25
|
+
enforcement: BudgetEnforcementMode,
|
|
26
|
+
budgetPct: number,
|
|
27
|
+
): "none" | "warn" | "pause" | "halt" {
|
|
28
|
+
if (budgetPct < 1.0) return "none";
|
|
29
|
+
if (enforcement === "halt") return "halt";
|
|
30
|
+
if (enforcement === "pause") return "pause";
|
|
31
|
+
return "warn";
|
|
32
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* or AutoContext dependency. State accessors are passed as callbacks.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { ExtensionContext, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
9
|
+
import type { ExtensionContext, ExtensionCommandContext, SessionMessageEntry } from "@gsd/pi-coding-agent";
|
|
10
10
|
import type { GSDState } from "./types.js";
|
|
11
11
|
import { getCurrentBranch } from "./worktree.js";
|
|
12
12
|
import { getActiveHook } from "./post-unit-hooks.js";
|
|
@@ -159,6 +159,49 @@ export function formatWidgetTokens(count: number): string {
|
|
|
159
159
|
return `${Math.round(count / 1000000)}M`;
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
// ─── ETA Estimation ──────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Estimate remaining time based on average unit duration from the metrics ledger.
|
|
166
|
+
* Returns a formatted string like "~12m remaining" or null if insufficient data.
|
|
167
|
+
*/
|
|
168
|
+
export function estimateTimeRemaining(): string | null {
|
|
169
|
+
const ledger = getLedger();
|
|
170
|
+
if (!ledger || ledger.units.length < 2) return null;
|
|
171
|
+
|
|
172
|
+
const sliceProgress = getRoadmapSlicesSync();
|
|
173
|
+
if (!sliceProgress || sliceProgress.total === 0) return null;
|
|
174
|
+
|
|
175
|
+
const remainingSlices = sliceProgress.total - sliceProgress.done;
|
|
176
|
+
if (remainingSlices <= 0) return null;
|
|
177
|
+
|
|
178
|
+
// Compute average duration per completed slice from the ledger
|
|
179
|
+
const completedSliceUnits = ledger.units.filter(
|
|
180
|
+
u => u.finishedAt > 0 && u.startedAt > 0,
|
|
181
|
+
);
|
|
182
|
+
if (completedSliceUnits.length < 2) return null;
|
|
183
|
+
|
|
184
|
+
const totalDuration = completedSliceUnits.reduce(
|
|
185
|
+
(sum, u) => sum + (u.finishedAt - u.startedAt), 0,
|
|
186
|
+
);
|
|
187
|
+
const avgDuration = totalDuration / completedSliceUnits.length;
|
|
188
|
+
|
|
189
|
+
// Rough estimate: remaining slices × average units per slice × avg duration
|
|
190
|
+
const completedSlices = sliceProgress.done || 1;
|
|
191
|
+
const unitsPerSlice = completedSliceUnits.length / completedSlices;
|
|
192
|
+
const estimatedMs = remainingSlices * unitsPerSlice * avgDuration;
|
|
193
|
+
|
|
194
|
+
if (estimatedMs < 5_000) return null; // Too small to display
|
|
195
|
+
|
|
196
|
+
const s = Math.floor(estimatedMs / 1000);
|
|
197
|
+
if (s < 60) return `~${s}s remaining`;
|
|
198
|
+
const m = Math.floor(s / 60);
|
|
199
|
+
if (m < 60) return `~${m}m remaining`;
|
|
200
|
+
const h = Math.floor(m / 60);
|
|
201
|
+
const rm = m % 60;
|
|
202
|
+
return rm > 0 ? `~${h}h ${rm}m remaining` : `~${h}h remaining`;
|
|
203
|
+
}
|
|
204
|
+
|
|
162
205
|
// ─── Slice Progress Cache ─────────────────────────────────────────────────────
|
|
163
206
|
|
|
164
207
|
/** Cached slice progress for the widget — avoid async in render */
|
|
@@ -277,15 +320,16 @@ export function updateProgressWidget(
|
|
|
277
320
|
tui.requestRender();
|
|
278
321
|
}, 800);
|
|
279
322
|
|
|
280
|
-
// Refresh progress cache from disk every
|
|
323
|
+
// Refresh progress cache from disk every 15s so the widget reflects
|
|
281
324
|
// task/slice completion mid-unit. Without this, the progress bar only
|
|
282
325
|
// updates at dispatch time, appearing frozen during long-running units.
|
|
326
|
+
// 15s (vs 5s) reduces synchronous file I/O on the hot path.
|
|
283
327
|
const progressRefreshTimer = mid ? setInterval(() => {
|
|
284
328
|
try {
|
|
285
329
|
updateSliceProgressCache(accessors.getBasePath(), mid.id, slice?.id);
|
|
286
330
|
cachedLines = undefined;
|
|
287
331
|
} catch { /* non-fatal */ }
|
|
288
|
-
},
|
|
332
|
+
}, 15_000) : null;
|
|
289
333
|
|
|
290
334
|
return {
|
|
291
335
|
render(width: number): string[] {
|
|
@@ -346,6 +390,12 @@ export function updateProgressWidget(
|
|
|
346
390
|
meta += theme.fg("dim", ` · task ${taskNum}/${activeSliceTasks.total}`);
|
|
347
391
|
}
|
|
348
392
|
|
|
393
|
+
// ETA estimate
|
|
394
|
+
const eta = estimateTimeRemaining();
|
|
395
|
+
if (eta) {
|
|
396
|
+
meta += theme.fg("dim", ` · ${eta}`);
|
|
397
|
+
}
|
|
398
|
+
|
|
349
399
|
lines.push(truncateToWidth(`${pad}${bar} ${meta}`, width));
|
|
350
400
|
}
|
|
351
401
|
}
|
|
@@ -370,13 +420,16 @@ export function updateProgressWidget(
|
|
|
370
420
|
let totalCacheRead = 0, totalCacheWrite = 0;
|
|
371
421
|
if (cmdCtx) {
|
|
372
422
|
for (const entry of cmdCtx.sessionManager.getEntries()) {
|
|
373
|
-
if (entry.type === "message"
|
|
374
|
-
const
|
|
375
|
-
if (
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
423
|
+
if (entry.type === "message") {
|
|
424
|
+
const msgEntry = entry as SessionMessageEntry;
|
|
425
|
+
if (msgEntry.message?.role === "assistant") {
|
|
426
|
+
const u = (msgEntry.message as any).usage;
|
|
427
|
+
if (u) {
|
|
428
|
+
totalInput += u.input || 0;
|
|
429
|
+
totalOutput += u.output || 0;
|
|
430
|
+
totalCacheRead += u.cacheRead || 0;
|
|
431
|
+
totalCacheWrite += u.cacheWrite || 0;
|
|
432
|
+
}
|
|
380
433
|
}
|
|
381
434
|
}
|
|
382
435
|
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct phase dispatch — handles manual /gsd dispatch commands.
|
|
3
|
+
* Resolves phase name → unit type + prompt, creates a session, and sends the message.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ExtensionAPI,
|
|
8
|
+
ExtensionCommandContext,
|
|
9
|
+
} from "@gsd/pi-coding-agent";
|
|
10
|
+
|
|
11
|
+
import { deriveState } from "./state.js";
|
|
12
|
+
import { loadFile, parseRoadmap } from "./files.js";
|
|
13
|
+
import {
|
|
14
|
+
resolveMilestoneFile, resolveSliceFile, relSliceFile,
|
|
15
|
+
} from "./paths.js";
|
|
16
|
+
import {
|
|
17
|
+
buildResearchSlicePrompt,
|
|
18
|
+
buildResearchMilestonePrompt,
|
|
19
|
+
buildPlanSlicePrompt,
|
|
20
|
+
buildPlanMilestonePrompt,
|
|
21
|
+
buildExecuteTaskPrompt,
|
|
22
|
+
buildCompleteSlicePrompt,
|
|
23
|
+
buildCompleteMilestonePrompt,
|
|
24
|
+
buildReassessRoadmapPrompt,
|
|
25
|
+
buildRunUatPrompt,
|
|
26
|
+
buildReplanSlicePrompt,
|
|
27
|
+
} from "./auto-prompts.js";
|
|
28
|
+
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
29
|
+
import { pauseAuto } from "./auto.js";
|
|
30
|
+
|
|
31
|
+
export async function dispatchDirectPhase(
|
|
32
|
+
ctx: ExtensionCommandContext,
|
|
33
|
+
pi: ExtensionAPI,
|
|
34
|
+
phase: string,
|
|
35
|
+
base: string,
|
|
36
|
+
): Promise<void> {
|
|
37
|
+
const state = await deriveState(base);
|
|
38
|
+
const mid = state.activeMilestone?.id;
|
|
39
|
+
const midTitle = state.activeMilestone?.title ?? "";
|
|
40
|
+
|
|
41
|
+
if (!mid) {
|
|
42
|
+
ctx.ui.notify("Cannot dispatch: no active milestone.", "warning");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const normalized = phase.toLowerCase();
|
|
47
|
+
let unitType: string;
|
|
48
|
+
let unitId: string;
|
|
49
|
+
let prompt: string;
|
|
50
|
+
|
|
51
|
+
switch (normalized) {
|
|
52
|
+
case "research":
|
|
53
|
+
case "research-milestone":
|
|
54
|
+
case "research-slice": {
|
|
55
|
+
const isSlice = normalized === "research-slice" || (normalized === "research" && state.phase !== "pre-planning");
|
|
56
|
+
if (isSlice) {
|
|
57
|
+
const sid = state.activeSlice?.id;
|
|
58
|
+
const sTitle = state.activeSlice?.title ?? "";
|
|
59
|
+
if (!sid) {
|
|
60
|
+
ctx.ui.notify("Cannot dispatch research-slice: no active slice.", "warning");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// When require_slice_discussion is enabled, pause auto-mode before
|
|
65
|
+
// each new slice so the user can discuss requirements first (#789).
|
|
66
|
+
const sliceContextFile = resolveSliceFile(base, mid, sid, "CONTEXT");
|
|
67
|
+
const requireDiscussion = loadEffectiveGSDPreferences()?.preferences?.phases?.require_slice_discussion;
|
|
68
|
+
if (requireDiscussion && !sliceContextFile) {
|
|
69
|
+
ctx.ui.notify(
|
|
70
|
+
`Slice ${sid} requires discussion before planning. Run /gsd discuss to discuss this slice, then /gsd auto to resume.`,
|
|
71
|
+
"info",
|
|
72
|
+
);
|
|
73
|
+
await pauseAuto(ctx, pi);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
unitType = "research-slice";
|
|
78
|
+
unitId = `${mid}/${sid}`;
|
|
79
|
+
prompt = await buildResearchSlicePrompt(mid, midTitle, sid, sTitle, base);
|
|
80
|
+
} else {
|
|
81
|
+
unitType = "research-milestone";
|
|
82
|
+
unitId = mid;
|
|
83
|
+
prompt = await buildResearchMilestonePrompt(mid, midTitle, base);
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
case "plan":
|
|
89
|
+
case "plan-milestone":
|
|
90
|
+
case "plan-slice": {
|
|
91
|
+
const isSlice = normalized === "plan-slice" || (normalized === "plan" && state.phase !== "pre-planning");
|
|
92
|
+
if (isSlice) {
|
|
93
|
+
const sid = state.activeSlice?.id;
|
|
94
|
+
const sTitle = state.activeSlice?.title ?? "";
|
|
95
|
+
if (!sid) {
|
|
96
|
+
ctx.ui.notify("Cannot dispatch plan-slice: no active slice.", "warning");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
unitType = "plan-slice";
|
|
100
|
+
unitId = `${mid}/${sid}`;
|
|
101
|
+
prompt = await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, base);
|
|
102
|
+
} else {
|
|
103
|
+
unitType = "plan-milestone";
|
|
104
|
+
unitId = mid;
|
|
105
|
+
prompt = await buildPlanMilestonePrompt(mid, midTitle, base);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
case "execute":
|
|
111
|
+
case "execute-task": {
|
|
112
|
+
const sid = state.activeSlice?.id;
|
|
113
|
+
const sTitle = state.activeSlice?.title ?? "";
|
|
114
|
+
const tid = state.activeTask?.id;
|
|
115
|
+
const tTitle = state.activeTask?.title ?? "";
|
|
116
|
+
if (!sid) {
|
|
117
|
+
ctx.ui.notify("Cannot dispatch execute-task: no active slice.", "warning");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!tid) {
|
|
121
|
+
ctx.ui.notify("Cannot dispatch execute-task: no active task.", "warning");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
unitType = "execute-task";
|
|
125
|
+
unitId = `${mid}/${sid}/${tid}`;
|
|
126
|
+
prompt = await buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
case "complete":
|
|
131
|
+
case "complete-slice":
|
|
132
|
+
case "complete-milestone": {
|
|
133
|
+
const isSlice = normalized === "complete-slice" || (normalized === "complete" && state.phase === "summarizing");
|
|
134
|
+
if (isSlice) {
|
|
135
|
+
const sid = state.activeSlice?.id;
|
|
136
|
+
const sTitle = state.activeSlice?.title ?? "";
|
|
137
|
+
if (!sid) {
|
|
138
|
+
ctx.ui.notify("Cannot dispatch complete-slice: no active slice.", "warning");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
unitType = "complete-slice";
|
|
142
|
+
unitId = `${mid}/${sid}`;
|
|
143
|
+
prompt = await buildCompleteSlicePrompt(mid, midTitle, sid, sTitle, base);
|
|
144
|
+
} else {
|
|
145
|
+
unitType = "complete-milestone";
|
|
146
|
+
unitId = mid;
|
|
147
|
+
prompt = await buildCompleteMilestonePrompt(mid, midTitle, base);
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case "reassess":
|
|
153
|
+
case "reassess-roadmap": {
|
|
154
|
+
const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
155
|
+
const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
|
|
156
|
+
if (!roadmapContent) {
|
|
157
|
+
ctx.ui.notify("Cannot dispatch reassess-roadmap: no roadmap found.", "warning");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const roadmap = parseRoadmap(roadmapContent);
|
|
161
|
+
const completedSlices = roadmap.slices.filter(s => s.done);
|
|
162
|
+
if (completedSlices.length === 0) {
|
|
163
|
+
ctx.ui.notify("Cannot dispatch reassess-roadmap: no completed slices.", "warning");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const completedSliceId = completedSlices[completedSlices.length - 1].id;
|
|
167
|
+
unitType = "reassess-roadmap";
|
|
168
|
+
unitId = `${mid}/${completedSliceId}`;
|
|
169
|
+
prompt = await buildReassessRoadmapPrompt(mid, midTitle, completedSliceId, base);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
case "uat":
|
|
174
|
+
case "run-uat": {
|
|
175
|
+
const sid = state.activeSlice?.id;
|
|
176
|
+
if (!sid) {
|
|
177
|
+
ctx.ui.notify("Cannot dispatch run-uat: no active slice.", "warning");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const uatFile = resolveSliceFile(base, mid, sid, "UAT");
|
|
181
|
+
if (!uatFile) {
|
|
182
|
+
ctx.ui.notify("Cannot dispatch run-uat: no UAT file found.", "warning");
|
|
183
|
+
return;
|
|
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
|
+
const uatPath = relSliceFile(base, mid, sid, "UAT");
|
|
191
|
+
unitType = "run-uat";
|
|
192
|
+
unitId = `${mid}/${sid}`;
|
|
193
|
+
prompt = await buildRunUatPrompt(mid, sid, uatPath, uatContent, base);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
case "replan":
|
|
198
|
+
case "replan-slice": {
|
|
199
|
+
const sid = state.activeSlice?.id;
|
|
200
|
+
const sTitle = state.activeSlice?.title ?? "";
|
|
201
|
+
if (!sid) {
|
|
202
|
+
ctx.ui.notify("Cannot dispatch replan-slice: no active slice.", "warning");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
unitType = "replan-slice";
|
|
206
|
+
unitId = `${mid}/${sid}`;
|
|
207
|
+
prompt = await buildReplanSlicePrompt(mid, midTitle, sid, sTitle, base);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
default:
|
|
212
|
+
ctx.ui.notify(
|
|
213
|
+
`Unknown phase "${phase}". Valid phases: research, plan, execute, complete, reassess, uat, replan.`,
|
|
214
|
+
"warning",
|
|
215
|
+
);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
ctx.ui.notify(`Dispatching ${unitType} for ${unitId}...`, "info");
|
|
220
|
+
const result = await ctx.newSession();
|
|
221
|
+
if (result.cancelled) {
|
|
222
|
+
ctx.ui.notify("Session creation cancelled.", "warning");
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
pi.sendMessage(
|
|
226
|
+
{ customType: "gsd-dispatch", content: prompt, display: false },
|
|
227
|
+
{ triggerTurn: true },
|
|
228
|
+
);
|
|
229
|
+
}
|