gsd-pi 2.33.1-dev.ee47f1b → 2.34.0-dev.bbb5216
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/bundled-resource-path.d.ts +8 -0
- package/dist/bundled-resource-path.js +14 -0
- package/dist/headless-query.js +6 -6
- package/dist/resources/extensions/gsd/auto/session.js +27 -32
- package/dist/resources/extensions/gsd/auto-dashboard.js +29 -109
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +6 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +52 -81
- package/dist/resources/extensions/gsd/auto-loop.js +956 -0
- package/dist/resources/extensions/gsd/auto-observability.js +4 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +75 -185
- package/dist/resources/extensions/gsd/auto-prompts.js +133 -101
- package/dist/resources/extensions/gsd/auto-recovery.js +59 -97
- package/dist/resources/extensions/gsd/auto-start.js +330 -309
- package/dist/resources/extensions/gsd/auto-supervisor.js +5 -11
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +7 -7
- package/dist/resources/extensions/gsd/auto-timers.js +3 -4
- package/dist/resources/extensions/gsd/auto-verification.js +35 -73
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +167 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +291 -126
- package/dist/resources/extensions/gsd/auto.js +283 -1013
- package/dist/resources/extensions/gsd/captures.js +10 -4
- package/dist/resources/extensions/gsd/dispatch-guard.js +7 -8
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +25 -18
- package/dist/resources/extensions/gsd/doctor-checks.js +3 -4
- package/dist/resources/extensions/gsd/git-service.js +1 -1
- package/dist/resources/extensions/gsd/gsd-db.js +296 -151
- package/dist/resources/extensions/gsd/index.js +92 -228
- package/dist/resources/extensions/gsd/post-unit-hooks.js +13 -13
- package/dist/resources/extensions/gsd/progress-score.js +61 -156
- package/dist/resources/extensions/gsd/quick.js +98 -122
- package/dist/resources/extensions/gsd/session-lock.js +13 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
- package/dist/resources/extensions/gsd/undo.js +43 -48
- package/dist/resources/extensions/gsd/unit-runtime.js +16 -15
- package/dist/resources/extensions/gsd/verification-evidence.js +0 -1
- package/dist/resources/extensions/gsd/verification-gate.js +6 -35
- package/dist/resources/extensions/gsd/worktree-command.js +30 -24
- package/dist/resources/extensions/gsd/worktree-manager.js +2 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +344 -0
- package/dist/resources/extensions/gsd/worktree.js +7 -44
- package/dist/tool-bootstrap.js +59 -11
- package/dist/worktree-cli.js +7 -7
- package/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +3630 -5483
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +735 -2588
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/src/models.generated.ts +1039 -2892
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/session.ts +47 -30
- package/src/resources/extensions/gsd/auto-dashboard.ts +28 -131
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +6 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +135 -91
- package/src/resources/extensions/gsd/auto-loop.ts +1665 -0
- package/src/resources/extensions/gsd/auto-observability.ts +4 -2
- package/src/resources/extensions/gsd/auto-post-unit.ts +85 -228
- package/src/resources/extensions/gsd/auto-prompts.ts +138 -109
- package/src/resources/extensions/gsd/auto-recovery.ts +124 -118
- package/src/resources/extensions/gsd/auto-start.ts +440 -354
- package/src/resources/extensions/gsd/auto-supervisor.ts +5 -12
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +8 -8
- package/src/resources/extensions/gsd/auto-timers.ts +3 -4
- package/src/resources/extensions/gsd/auto-verification.ts +76 -90
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +204 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +389 -141
- package/src/resources/extensions/gsd/auto.ts +515 -1199
- package/src/resources/extensions/gsd/captures.ts +10 -4
- package/src/resources/extensions/gsd/dispatch-guard.ts +13 -9
- package/src/resources/extensions/gsd/docs/preferences-reference.md +25 -18
- package/src/resources/extensions/gsd/doctor-checks.ts +3 -4
- package/src/resources/extensions/gsd/git-service.ts +8 -1
- package/src/resources/extensions/gsd/gitignore.ts +4 -2
- package/src/resources/extensions/gsd/gsd-db.ts +375 -180
- package/src/resources/extensions/gsd/index.ts +104 -263
- package/src/resources/extensions/gsd/post-unit-hooks.ts +13 -13
- package/src/resources/extensions/gsd/progress-score.ts +65 -200
- package/src/resources/extensions/gsd/quick.ts +121 -125
- package/src/resources/extensions/gsd/session-lock.ts +11 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +32 -59
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +75 -27
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +1458 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +8 -162
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -108
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +1 -3
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -55
- package/src/resources/extensions/gsd/tests/headless-query.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +8 -11
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +4 -6
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/undo.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +24 -26
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +7 -201
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +705 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +57 -106
- package/src/resources/extensions/gsd/tests/worktree.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +43 -132
- package/src/resources/extensions/gsd/types.ts +90 -81
- package/src/resources/extensions/gsd/undo.ts +42 -46
- package/src/resources/extensions/gsd/unit-runtime.ts +14 -18
- package/src/resources/extensions/gsd/verification-evidence.ts +1 -3
- package/src/resources/extensions/gsd/verification-gate.ts +6 -39
- package/src/resources/extensions/gsd/worktree-command.ts +36 -24
- package/src/resources/extensions/gsd/worktree-manager.ts +2 -3
- package/src/resources/extensions/gsd/worktree-resolver.ts +485 -0
- package/src/resources/extensions/gsd/worktree.ts +7 -44
- package/dist/resources/extensions/gsd/auto-constants.js +0 -5
- package/dist/resources/extensions/gsd/auto-idempotency.js +0 -106
- package/dist/resources/extensions/gsd/auto-stuck-detection.js +0 -165
- package/dist/resources/extensions/gsd/mechanical-completion.js +0 -351
- package/src/resources/extensions/gsd/auto-constants.ts +0 -6
- package/src/resources/extensions/gsd/auto-idempotency.ts +0 -151
- package/src/resources/extensions/gsd/auto-stuck-detection.ts +0 -221
- package/src/resources/extensions/gsd/mechanical-completion.ts +0 -430
- package/src/resources/extensions/gsd/tests/auto-dispatch-loop.test.ts +0 -691
- package/src/resources/extensions/gsd/tests/auto-reentrancy-guard.test.ts +0 -127
- package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +0 -123
- package/src/resources/extensions/gsd/tests/dispatch-stall-guard.test.ts +0 -126
- package/src/resources/extensions/gsd/tests/loop-regression.test.ts +0 -874
- package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +0 -356
- package/src/resources/extensions/gsd/tests/progress-score.test.ts +0 -206
- package/src/resources/extensions/gsd/tests/session-lock.test.ts +0 -434
package/pkg/package.json
CHANGED
|
@@ -52,16 +52,28 @@ export interface PendingVerificationRetry {
|
|
|
52
52
|
attempt: number;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* A typed item enqueued by postUnitPostVerification for the main loop to
|
|
57
|
+
* drain via the standard runUnit path. Replaces inline dispatch
|
|
58
|
+
* (pi.sendMessage / s.cmdCtx.newSession()) for hooks, triage, and quick-tasks.
|
|
59
|
+
*/
|
|
60
|
+
export interface SidecarItem {
|
|
61
|
+
kind: "hook" | "triage" | "quick-task";
|
|
62
|
+
unitType: string;
|
|
63
|
+
unitId: string;
|
|
64
|
+
prompt: string;
|
|
65
|
+
/** Model override for hook units (e.g. "anthropic/claude-3-5-sonnet"). */
|
|
66
|
+
model?: string;
|
|
67
|
+
/** Capture ID for quick-task items (already marked executed at enqueue time). */
|
|
68
|
+
captureId?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
56
72
|
|
|
57
73
|
export const MAX_UNIT_DISPATCHES = 3;
|
|
58
74
|
export const STUB_RECOVERY_THRESHOLD = 2;
|
|
59
75
|
export const MAX_LIFETIME_DISPATCHES = 6;
|
|
60
|
-
export const MAX_CONSECUTIVE_SKIPS = 3;
|
|
61
|
-
export const DISPATCH_GAP_TIMEOUT_MS = 5_000;
|
|
62
|
-
export const MAX_SKIP_DEPTH = 20;
|
|
63
76
|
export const NEW_SESSION_TIMEOUT_MS = 30_000;
|
|
64
|
-
export const DISPATCH_HANG_TIMEOUT_MS = 60_000;
|
|
65
77
|
|
|
66
78
|
// ─── AutoSession ─────────────────────────────────────────────────────────────
|
|
67
79
|
|
|
@@ -69,7 +81,6 @@ export class AutoSession {
|
|
|
69
81
|
// ── Lifecycle ────────────────────────────────────────────────────────────
|
|
70
82
|
active = false;
|
|
71
83
|
paused = false;
|
|
72
|
-
pausedForSecrets = false;
|
|
73
84
|
stepMode = false;
|
|
74
85
|
verbose = false;
|
|
75
86
|
cmdCtx: ExtensionCommandContext | null = null;
|
|
@@ -83,15 +94,12 @@ export class AutoSession {
|
|
|
83
94
|
readonly unitDispatchCount = new Map<string, number>();
|
|
84
95
|
readonly unitLifetimeDispatches = new Map<string, number>();
|
|
85
96
|
readonly unitRecoveryCount = new Map<string, number>();
|
|
86
|
-
readonly unitConsecutiveSkips = new Map<string, number>();
|
|
87
|
-
readonly completedKeySet = new Set<string>();
|
|
88
97
|
|
|
89
98
|
// ── Timers ───────────────────────────────────────────────────────────────
|
|
90
99
|
unitTimeoutHandle: ReturnType<typeof setTimeout> | null = null;
|
|
91
100
|
wrapupWarningHandle: ReturnType<typeof setTimeout> | null = null;
|
|
92
101
|
idleWatchdogHandle: ReturnType<typeof setInterval> | null = null;
|
|
93
102
|
continueHereHandle: ReturnType<typeof setInterval> | null = null;
|
|
94
|
-
dispatchGapHandle: ReturnType<typeof setTimeout> | null = null;
|
|
95
103
|
|
|
96
104
|
// ── Current unit ─────────────────────────────────────────────────────────
|
|
97
105
|
currentUnit: CurrentUnit | null = null;
|
|
@@ -113,12 +121,8 @@ export class AutoSession {
|
|
|
113
121
|
resourceVersionOnStart: string | null = null;
|
|
114
122
|
lastStateRebuildAt = 0;
|
|
115
123
|
|
|
116
|
-
// ──
|
|
117
|
-
|
|
118
|
-
pendingAgentEndRetry = false;
|
|
119
|
-
dispatching = false;
|
|
120
|
-
skipDepth = 0;
|
|
121
|
-
readonly recentlyEvictedKeys = new Set<string>();
|
|
124
|
+
// ── Sidecar queue ─────────────────────────────────────────────────────
|
|
125
|
+
sidecarQueue: SidecarItem[] = [];
|
|
122
126
|
|
|
123
127
|
// ── Metrics ──────────────────────────────────────────────────────────────
|
|
124
128
|
autoStartTime = 0;
|
|
@@ -129,6 +133,29 @@ export class AutoSession {
|
|
|
129
133
|
// ── Signal handler ───────────────────────────────────────────────────────
|
|
130
134
|
sigtermHandler: (() => void) | null = null;
|
|
131
135
|
|
|
136
|
+
// ── Loop promise state ──────────────────────────────────────────────────
|
|
137
|
+
/**
|
|
138
|
+
* True only while runUnit is rotating into a fresh session. agent_end events
|
|
139
|
+
* emitted from the previous session's abort during this window must be
|
|
140
|
+
* ignored; they do not belong to the new unit.
|
|
141
|
+
*/
|
|
142
|
+
sessionSwitchInFlight = false;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* One-shot resolver for the current unit's agent_end promise.
|
|
146
|
+
* Non-null only while a unit is in-flight (between sendMessage and agent_end).
|
|
147
|
+
* Scoped to the session to prevent concurrent session corruption.
|
|
148
|
+
*/
|
|
149
|
+
pendingResolve: ((result: { status: "completed" | "cancelled" | "error"; event?: { messages: unknown[] } }) => void) | null = null;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Queue for agent_end events that arrive when no pendingResolve exists.
|
|
153
|
+
* This happens when error-recovery sendMessage retries produce agent_end
|
|
154
|
+
* events between loop iterations. The next runUnit drains this queue
|
|
155
|
+
* instead of waiting for a new event.
|
|
156
|
+
*/
|
|
157
|
+
pendingAgentEndQueue: Array<{ messages: unknown[] }> = [];
|
|
158
|
+
|
|
132
159
|
// ── Methods ──────────────────────────────────────────────────────────────
|
|
133
160
|
|
|
134
161
|
clearTimers(): void {
|
|
@@ -136,13 +163,11 @@ export class AutoSession {
|
|
|
136
163
|
if (this.wrapupWarningHandle) { clearTimeout(this.wrapupWarningHandle); this.wrapupWarningHandle = null; }
|
|
137
164
|
if (this.idleWatchdogHandle) { clearInterval(this.idleWatchdogHandle); this.idleWatchdogHandle = null; }
|
|
138
165
|
if (this.continueHereHandle) { clearInterval(this.continueHereHandle); this.continueHereHandle = null; }
|
|
139
|
-
if (this.dispatchGapHandle) { clearTimeout(this.dispatchGapHandle); this.dispatchGapHandle = null; }
|
|
140
166
|
}
|
|
141
167
|
|
|
142
168
|
resetDispatchCounters(): void {
|
|
143
169
|
this.unitDispatchCount.clear();
|
|
144
170
|
this.unitLifetimeDispatches.clear();
|
|
145
|
-
this.unitConsecutiveSkips.clear();
|
|
146
171
|
}
|
|
147
172
|
|
|
148
173
|
get lockBasePath(): string {
|
|
@@ -163,7 +188,6 @@ export class AutoSession {
|
|
|
163
188
|
// Lifecycle
|
|
164
189
|
this.active = false;
|
|
165
190
|
this.paused = false;
|
|
166
|
-
this.pausedForSecrets = false;
|
|
167
191
|
this.stepMode = false;
|
|
168
192
|
this.verbose = false;
|
|
169
193
|
this.cmdCtx = null;
|
|
@@ -177,9 +201,6 @@ export class AutoSession {
|
|
|
177
201
|
this.unitDispatchCount.clear();
|
|
178
202
|
this.unitLifetimeDispatches.clear();
|
|
179
203
|
this.unitRecoveryCount.clear();
|
|
180
|
-
this.unitConsecutiveSkips.clear();
|
|
181
|
-
// Note: completedKeySet is intentionally NOT cleared — it persists
|
|
182
|
-
// across restarts to prevent re-dispatching completed units.
|
|
183
204
|
|
|
184
205
|
// Unit
|
|
185
206
|
this.currentUnit = null;
|
|
@@ -201,21 +222,20 @@ export class AutoSession {
|
|
|
201
222
|
this.resourceVersionOnStart = null;
|
|
202
223
|
this.lastStateRebuildAt = 0;
|
|
203
224
|
|
|
204
|
-
// Guards
|
|
205
|
-
this.handlingAgentEnd = false;
|
|
206
|
-
this.pendingAgentEndRetry = false;
|
|
207
|
-
this.dispatching = false;
|
|
208
|
-
this.skipDepth = 0;
|
|
209
|
-
this.recentlyEvictedKeys.clear();
|
|
210
|
-
|
|
211
225
|
// Metrics
|
|
212
226
|
this.autoStartTime = 0;
|
|
213
227
|
this.lastPromptCharCount = undefined;
|
|
214
228
|
this.lastBaselineCharCount = undefined;
|
|
215
229
|
this.pendingQuickTasks = [];
|
|
230
|
+
this.sidecarQueue = [];
|
|
216
231
|
|
|
217
232
|
// Signal handler
|
|
218
233
|
this.sigtermHandler = null;
|
|
234
|
+
|
|
235
|
+
// Loop promise state
|
|
236
|
+
this.sessionSwitchInFlight = false;
|
|
237
|
+
this.pendingResolve = null;
|
|
238
|
+
this.pendingAgentEndQueue = [];
|
|
219
239
|
}
|
|
220
240
|
|
|
221
241
|
toJSON(): Record<string, unknown> {
|
|
@@ -227,10 +247,7 @@ export class AutoSession {
|
|
|
227
247
|
currentMilestoneId: this.currentMilestoneId,
|
|
228
248
|
currentUnit: this.currentUnit,
|
|
229
249
|
completedUnits: this.completedUnits.length,
|
|
230
|
-
completedKeySet: this.completedKeySet.size,
|
|
231
250
|
unitDispatchCount: Object.fromEntries(this.unitDispatchCount),
|
|
232
|
-
dispatching: this.dispatching,
|
|
233
|
-
skipDepth: this.skipDepth,
|
|
234
251
|
};
|
|
235
252
|
}
|
|
236
253
|
}
|
|
@@ -11,7 +11,6 @@ import type { GSDState } from "./types.js";
|
|
|
11
11
|
import { getCurrentBranch } from "./worktree.js";
|
|
12
12
|
import { getActiveHook } from "./post-unit-hooks.js";
|
|
13
13
|
import { getLedger, getProjectTotals, formatCost, formatTokenCount, formatTierSavings } from "./metrics.js";
|
|
14
|
-
import { getHealthTrend, getConsecutiveErrorUnits } from "./doctor-proactive.js";
|
|
15
14
|
import {
|
|
16
15
|
resolveMilestoneFile,
|
|
17
16
|
resolveSliceFile,
|
|
@@ -20,7 +19,6 @@ import { parseRoadmap, parsePlan } from "./files.js";
|
|
|
20
19
|
import { readFileSync, existsSync } from "node:fs";
|
|
21
20
|
import { truncateToWidth, visibleWidth } from "@gsd/pi-tui";
|
|
22
21
|
import { makeUI, GLYPH, INDENT } from "../shared/mod.js";
|
|
23
|
-
import { parseUnitId } from "./unit-id.js";
|
|
24
22
|
|
|
25
23
|
// ─── Dashboard Data ───────────────────────────────────────────────────────────
|
|
26
24
|
|
|
@@ -49,34 +47,40 @@ export interface AutoDashboardData {
|
|
|
49
47
|
|
|
50
48
|
// ─── Unit Description Helpers ─────────────────────────────────────────────────
|
|
51
49
|
|
|
52
|
-
/** Canonical verb and phase label for each known unit type. */
|
|
53
|
-
const UNIT_TYPE_INFO: Record<string, { verb: string; phaseLabel: string }> = {
|
|
54
|
-
"research-milestone": { verb: "researching", phaseLabel: "RESEARCH" },
|
|
55
|
-
"research-slice": { verb: "researching", phaseLabel: "RESEARCH" },
|
|
56
|
-
"plan-milestone": { verb: "planning", phaseLabel: "PLAN" },
|
|
57
|
-
"plan-slice": { verb: "planning", phaseLabel: "PLAN" },
|
|
58
|
-
"execute-task": { verb: "executing", phaseLabel: "EXECUTE" },
|
|
59
|
-
"complete-slice": { verb: "completing", phaseLabel: "COMPLETE" },
|
|
60
|
-
"replan-slice": { verb: "replanning", phaseLabel: "REPLAN" },
|
|
61
|
-
"rewrite-docs": { verb: "rewriting", phaseLabel: "REWRITE" },
|
|
62
|
-
"reassess-roadmap": { verb: "reassessing", phaseLabel: "REASSESS" },
|
|
63
|
-
"run-uat": { verb: "running UAT", phaseLabel: "UAT" },
|
|
64
|
-
};
|
|
65
|
-
|
|
66
50
|
export function unitVerb(unitType: string): string {
|
|
67
51
|
if (unitType.startsWith("hook/")) return `hook: ${unitType.slice(5)}`;
|
|
68
|
-
|
|
52
|
+
switch (unitType) {
|
|
53
|
+
case "research-milestone":
|
|
54
|
+
case "research-slice": return "researching";
|
|
55
|
+
case "plan-milestone":
|
|
56
|
+
case "plan-slice": return "planning";
|
|
57
|
+
case "execute-task": return "executing";
|
|
58
|
+
case "complete-slice": return "completing";
|
|
59
|
+
case "replan-slice": return "replanning";
|
|
60
|
+
case "rewrite-docs": return "rewriting";
|
|
61
|
+
case "reassess-roadmap": return "reassessing";
|
|
62
|
+
case "run-uat": return "running UAT";
|
|
63
|
+
default: return unitType;
|
|
64
|
+
}
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
export function unitPhaseLabel(unitType: string): string {
|
|
72
68
|
if (unitType.startsWith("hook/")) return "HOOK";
|
|
73
|
-
|
|
69
|
+
switch (unitType) {
|
|
70
|
+
case "research-milestone": return "RESEARCH";
|
|
71
|
+
case "research-slice": return "RESEARCH";
|
|
72
|
+
case "plan-milestone": return "PLAN";
|
|
73
|
+
case "plan-slice": return "PLAN";
|
|
74
|
+
case "execute-task": return "EXECUTE";
|
|
75
|
+
case "complete-slice": return "COMPLETE";
|
|
76
|
+
case "replan-slice": return "REPLAN";
|
|
77
|
+
case "rewrite-docs": return "REWRITE";
|
|
78
|
+
case "reassess-roadmap": return "REASSESS";
|
|
79
|
+
case "run-uat": return "UAT";
|
|
80
|
+
default: return unitType.toUpperCase();
|
|
81
|
+
}
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
/**
|
|
77
|
-
* Describe the expected next step after the current unit completes.
|
|
78
|
-
* Unit types here mirror the keys in UNIT_TYPE_INFO above.
|
|
79
|
-
*/
|
|
80
84
|
function peekNext(unitType: string, state: GSDState): string {
|
|
81
85
|
// Show active hook info in progress display
|
|
82
86
|
const activeHookState = getActiveHook();
|
|
@@ -305,16 +309,6 @@ export function updateProgressWidget(
|
|
|
305
309
|
}
|
|
306
310
|
if (cachedBranch) widgetPwd = `${widgetPwd} (${cachedBranch})`;
|
|
307
311
|
|
|
308
|
-
// Set a string-array fallback first — this is the only version RPC mode will
|
|
309
|
-
// see, since the factory widget set below is not supported in RPC mode.
|
|
310
|
-
const progressText = buildProgressTextLines(
|
|
311
|
-
verb, phaseLabel, unitId, mid, slice, task, next,
|
|
312
|
-
accessors, tierBadge, widgetPwd,
|
|
313
|
-
);
|
|
314
|
-
ctx.ui.setWidget("gsd-progress", progressText);
|
|
315
|
-
|
|
316
|
-
// Set the factory-based widget — in TUI mode this replaces the string-array
|
|
317
|
-
// version with a dynamic, animated widget. In RPC mode this call is a no-op.
|
|
318
312
|
ctx.ui.setWidget("gsd-progress", (tui, theme) => {
|
|
319
313
|
let pulseBright = true;
|
|
320
314
|
let cachedLines: string[] | undefined;
|
|
@@ -372,11 +366,7 @@ export function updateProgressWidget(
|
|
|
372
366
|
|
|
373
367
|
lines.push("");
|
|
374
368
|
|
|
375
|
-
const
|
|
376
|
-
const hookParsed = isHook ? parseUnitId(unitId) : undefined;
|
|
377
|
-
const target = isHook
|
|
378
|
-
? (hookParsed!.task ?? hookParsed!.slice ?? unitId)
|
|
379
|
-
: (task ? `${task.id}: ${task.title}` : unitId);
|
|
369
|
+
const target = task ? `${task.id}: ${task.title}` : unitId;
|
|
380
370
|
const actionLeft = `${pad}${theme.fg("accent", "▸")} ${theme.fg("accent", verb)} ${theme.fg("text", target)}`;
|
|
381
371
|
const tierTag = tierBadge ? theme.fg("dim", `[${tierBadge}] `) : "";
|
|
382
372
|
const phaseBadge = `${tierTag}${theme.fg("dim", phaseLabel)}`;
|
|
@@ -396,10 +386,7 @@ export function updateProgressWidget(
|
|
|
396
386
|
let meta = theme.fg("dim", `${done}/${total} slices`);
|
|
397
387
|
|
|
398
388
|
if (activeSliceTasks && activeSliceTasks.total > 0) {
|
|
399
|
-
|
|
400
|
-
const taskNum = isHook
|
|
401
|
-
? Math.max(activeSliceTasks.done, 1)
|
|
402
|
-
: Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
|
|
389
|
+
const taskNum = Math.min(activeSliceTasks.done + 1, activeSliceTasks.total);
|
|
403
390
|
meta += theme.fg("dim", ` · task ${taskNum}/${activeSliceTasks.total}`);
|
|
404
391
|
}
|
|
405
392
|
|
|
@@ -467,7 +454,6 @@ export function updateProgressWidget(
|
|
|
467
454
|
sp.push(`\u26A1${hitRate}%`);
|
|
468
455
|
}
|
|
469
456
|
if (cumulativeCost) sp.push(`$${cumulativeCost.toFixed(3)}`);
|
|
470
|
-
else if (autoTotals?.apiRequests) sp.push(`${autoTotals.apiRequests} reqs`);
|
|
471
457
|
|
|
472
458
|
const cxDisplay = cxPct === "?"
|
|
473
459
|
? `?/${formatWidgetTokens(cxWindow)}`
|
|
@@ -526,95 +512,6 @@ export function updateProgressWidget(
|
|
|
526
512
|
});
|
|
527
513
|
}
|
|
528
514
|
|
|
529
|
-
// ─── Text Fallback for RPC Mode ───────────────────────────────────────────
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Build a compact string-array representation of the progress widget.
|
|
533
|
-
* Used as a fallback when the factory-based widget cannot render (RPC mode).
|
|
534
|
-
*/
|
|
535
|
-
// ─── Model Health Indicator ───────────────────────────────────────────────────
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Compute a traffic-light health indicator from observable signals.
|
|
539
|
-
* 🟢 progressing well — no errors, trend stable/improving
|
|
540
|
-
* 🟡 struggling — some errors or degrading trend
|
|
541
|
-
* 🔴 stuck — consecutive errors, likely needs attention
|
|
542
|
-
*/
|
|
543
|
-
export function getModelHealthIndicator(): { emoji: string; label: string } {
|
|
544
|
-
const trend = getHealthTrend();
|
|
545
|
-
const consecutiveErrors = getConsecutiveErrorUnits();
|
|
546
|
-
|
|
547
|
-
if (consecutiveErrors >= 3) {
|
|
548
|
-
return { emoji: "🔴", label: "stuck" };
|
|
549
|
-
}
|
|
550
|
-
if (consecutiveErrors >= 1 || trend === "degrading") {
|
|
551
|
-
return { emoji: "🟡", label: "struggling" };
|
|
552
|
-
}
|
|
553
|
-
if (trend === "improving") {
|
|
554
|
-
return { emoji: "🟢", label: "progressing well" };
|
|
555
|
-
}
|
|
556
|
-
// stable or unknown
|
|
557
|
-
return { emoji: "🟢", label: "progressing" };
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
function buildProgressTextLines(
|
|
561
|
-
verb: string,
|
|
562
|
-
phaseLabel: string,
|
|
563
|
-
unitId: string,
|
|
564
|
-
mid: { id: string; title: string } | null,
|
|
565
|
-
slice: { id: string; title: string } | null,
|
|
566
|
-
task: { id: string; title: string } | null,
|
|
567
|
-
next: string,
|
|
568
|
-
accessors: WidgetStateAccessors,
|
|
569
|
-
tierBadge: string | undefined,
|
|
570
|
-
widgetPwd: string,
|
|
571
|
-
): string[] {
|
|
572
|
-
const mode = accessors.isStepMode() ? "step" : "auto";
|
|
573
|
-
const elapsed = formatAutoElapsed(accessors.getAutoStartTime());
|
|
574
|
-
const tierStr = tierBadge ? ` [${tierBadge}]` : "";
|
|
575
|
-
|
|
576
|
-
const lines: string[] = [];
|
|
577
|
-
lines.push(`[GSD ${mode}] ${verb} ${unitId}${tierStr}${elapsed ? ` — ${elapsed}` : ""}`);
|
|
578
|
-
|
|
579
|
-
if (mid) lines.push(` Milestone: ${mid.id} — ${mid.title}`);
|
|
580
|
-
if (slice) lines.push(` Slice: ${slice.id} — ${slice.title}`);
|
|
581
|
-
if (task) lines.push(` Task: ${task.id} — ${task.title}`);
|
|
582
|
-
|
|
583
|
-
// Progress bar
|
|
584
|
-
const sp = cachedSliceProgress;
|
|
585
|
-
if (sp && sp.total > 0) {
|
|
586
|
-
const pct = Math.round((sp.done / sp.total) * 100);
|
|
587
|
-
const taskInfo = sp.activeSliceTasks
|
|
588
|
-
? ` (tasks: ${sp.activeSliceTasks.done}/${sp.activeSliceTasks.total})`
|
|
589
|
-
: "";
|
|
590
|
-
lines.push(` Progress: ${sp.done}/${sp.total} slices (${pct}%)${taskInfo}`);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
// Cost / tokens
|
|
594
|
-
const ledger = getLedger();
|
|
595
|
-
const totals = ledger ? getProjectTotals(ledger.units) : null;
|
|
596
|
-
if (totals) {
|
|
597
|
-
const parts: string[] = [];
|
|
598
|
-
if (totals.tokens.input || totals.tokens.output) {
|
|
599
|
-
parts.push(`tokens: ${formatWidgetTokens(totals.tokens.input)}↑ ${formatWidgetTokens(totals.tokens.output)}↓`);
|
|
600
|
-
}
|
|
601
|
-
if (totals.cost > 0) {
|
|
602
|
-
parts.push(`cost: ${formatCost(totals.cost)}`);
|
|
603
|
-
}
|
|
604
|
-
if (parts.length > 0) lines.push(` ${parts.join(" — ")}`);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
if (next) lines.push(` Next: ${next}`);
|
|
608
|
-
|
|
609
|
-
// Model health indicator
|
|
610
|
-
const health = getModelHealthIndicator();
|
|
611
|
-
lines.push(` Health: ${health.emoji} ${health.label}`);
|
|
612
|
-
|
|
613
|
-
lines.push(` ${widgetPwd}`);
|
|
614
|
-
|
|
615
|
-
return lines;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
515
|
// ─── Right-align Helper ───────────────────────────────────────────────────────
|
|
619
516
|
|
|
620
517
|
/** Right-align helper: build a line with left content and right content. */
|
|
@@ -182,10 +182,15 @@ 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
|
+
}
|
|
185
190
|
const uatPath = relSliceFile(base, mid, sid, "UAT");
|
|
186
191
|
unitType = "run-uat";
|
|
187
192
|
unitId = `${mid}/${sid}`;
|
|
188
|
-
prompt = await buildRunUatPrompt(mid, sid, uatPath, base);
|
|
193
|
+
prompt = await buildRunUatPrompt(mid, sid, uatPath, uatContent, base);
|
|
189
194
|
break;
|
|
190
195
|
}
|
|
191
196
|
|