taskplane 0.29.2 → 0.30.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/bin/gitignore-patterns.mjs +11 -8
- package/bin/rpc-wrapper.mjs +410 -357
- package/bin/taskplane.mjs +533 -250
- package/extensions/reviewer-extension.ts +17 -11
- package/extensions/taskplane/abort.ts +50 -18
- package/extensions/taskplane/agent-bridge-extension.ts +232 -105
- package/extensions/taskplane/agent-host.ts +224 -97
- package/extensions/taskplane/cleanup.ts +71 -42
- package/extensions/taskplane/config-loader.ts +142 -58
- package/extensions/taskplane/config-schema.ts +6 -13
- package/extensions/taskplane/config.ts +10 -2
- package/extensions/taskplane/diagnostic-reports.ts +59 -47
- package/extensions/taskplane/diagnostics.ts +13 -13
- package/extensions/taskplane/discovery.ts +35 -61
- package/extensions/taskplane/engine-worker.ts +53 -46
- package/extensions/taskplane/engine.ts +1760 -602
- package/extensions/taskplane/execution.ts +426 -206
- package/extensions/taskplane/extension.ts +1073 -598
- package/extensions/taskplane/formatting.ts +136 -124
- package/extensions/taskplane/git.ts +0 -2
- package/extensions/taskplane/lane-runner.ts +542 -311
- package/extensions/taskplane/mailbox.ts +57 -49
- package/extensions/taskplane/merge.ts +662 -383
- package/extensions/taskplane/messages.ts +109 -51
- package/extensions/taskplane/migrations.ts +1 -1
- package/extensions/taskplane/path-resolver.ts +8 -9
- package/extensions/taskplane/persistence.ts +425 -262
- package/extensions/taskplane/process-registry.ts +36 -7
- package/extensions/taskplane/quality-gate.ts +107 -55
- package/extensions/taskplane/resume.ts +774 -267
- package/extensions/taskplane/sessions.ts +1 -1
- package/extensions/taskplane/settings-tui.ts +505 -164
- package/extensions/taskplane/sidecar-telemetry.ts +25 -10
- package/extensions/taskplane/supervisor.ts +477 -270
- package/extensions/taskplane/task-executor-core.ts +178 -53
- package/extensions/taskplane/types.ts +186 -108
- package/extensions/taskplane/verification.ts +27 -22
- package/extensions/taskplane/waves.ts +59 -43
- package/extensions/taskplane/workspace.ts +14 -12
- package/extensions/taskplane/worktree.ts +218 -196
- package/package.json +14 -2
|
@@ -6,7 +6,16 @@ import { join } from "path";
|
|
|
6
6
|
import { truncateToWidth } from "@mariozechner/pi-tui";
|
|
7
7
|
|
|
8
8
|
import { parseDependencyReference } from "./discovery.ts";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
LaneAssignment,
|
|
11
|
+
MonitorState,
|
|
12
|
+
OrchBatchRuntimeState,
|
|
13
|
+
OrchDashboardViewModel,
|
|
14
|
+
OrchLaneCardData,
|
|
15
|
+
OrchSummaryCounts,
|
|
16
|
+
ParsedTask,
|
|
17
|
+
WaveComputationResult,
|
|
18
|
+
} from "./types.ts";
|
|
10
19
|
import { getTaskDurationMinutes, SIZE_DURATION_MINUTES } from "./types.ts";
|
|
11
20
|
|
|
12
21
|
// ── Wave Output Formatting ───────────────────────────────────────────
|
|
@@ -30,9 +39,7 @@ export function formatDependencyGraph(
|
|
|
30
39
|
const lines: string[] = [];
|
|
31
40
|
|
|
32
41
|
// Sort tasks deterministically by ID
|
|
33
|
-
const sortedTasks = [...pending.values()].sort((a, b) =>
|
|
34
|
-
a.taskId.localeCompare(b.taskId),
|
|
35
|
-
);
|
|
42
|
+
const sortedTasks = [...pending.values()].sort((a, b) => a.taskId.localeCompare(b.taskId));
|
|
36
43
|
|
|
37
44
|
// Build downstream index: taskID → tasks that depend on it
|
|
38
45
|
const downstream = new Map<string, string[]>();
|
|
@@ -130,14 +137,8 @@ export function formatDependencyGraph(
|
|
|
130
137
|
const dependents = (downstream.get(target) || []).sort();
|
|
131
138
|
if (dependents.length > 0) {
|
|
132
139
|
hasDownstream = true;
|
|
133
|
-
const status = completed.has(target)
|
|
134
|
-
|
|
135
|
-
: pending.has(target)
|
|
136
|
-
? "⏳"
|
|
137
|
-
: "❓";
|
|
138
|
-
lines.push(
|
|
139
|
-
` ${target} ${status} ← ${dependents.join(", ")}`,
|
|
140
|
-
);
|
|
140
|
+
const status = completed.has(target) ? "✅" : pending.has(target) ? "⏳" : "❓";
|
|
141
|
+
lines.push(` ${target} ${status} ← ${dependents.join(", ")}`);
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
if (!hasDownstream) {
|
|
@@ -146,9 +147,7 @@ export function formatDependencyGraph(
|
|
|
146
147
|
|
|
147
148
|
// Section 3: Independent tasks (no deps, nothing depends on them)
|
|
148
149
|
const independentTasks = sortedTasks.filter(
|
|
149
|
-
(t) =>
|
|
150
|
-
t.dependencies.length === 0 &&
|
|
151
|
-
!(downstream.get(t.taskId)?.length),
|
|
150
|
+
(t) => t.dependencies.length === 0 && !downstream.get(t.taskId)?.length,
|
|
152
151
|
);
|
|
153
152
|
if (independentTasks.length > 0) {
|
|
154
153
|
lines.push("");
|
|
@@ -206,7 +205,7 @@ export function formatWavePlan(
|
|
|
206
205
|
|
|
207
206
|
lines.push(
|
|
208
207
|
`🌊 Execution Plan: ${result.waves.length} wave(s), ` +
|
|
209
|
-
|
|
208
|
+
`${totalTasks} task(s), up to ${maxLanesUsed} lane(s)`,
|
|
210
209
|
);
|
|
211
210
|
lines.push("");
|
|
212
211
|
|
|
@@ -225,46 +224,31 @@ export function formatWavePlan(
|
|
|
225
224
|
const parallel = laneCount > 1 ? "parallel" : "serial";
|
|
226
225
|
|
|
227
226
|
lines.push(
|
|
228
|
-
` Wave ${wave.waveNumber}: ${taskCount} task(s) across ` +
|
|
229
|
-
`${laneCount} lane(s) [${parallel}]`,
|
|
227
|
+
` Wave ${wave.waveNumber}: ${taskCount} task(s) across ` + `${laneCount} lane(s) [${parallel}]`,
|
|
230
228
|
);
|
|
231
229
|
|
|
232
230
|
// Calculate wave duration: critical path = max lane duration
|
|
233
231
|
let maxLaneDuration = 0;
|
|
234
232
|
|
|
235
233
|
// Sort lanes deterministically by lane number
|
|
236
|
-
const sortedLanes = [...laneGroups.entries()].sort(
|
|
237
|
-
(a, b) => a[0] - b[0],
|
|
238
|
-
);
|
|
234
|
+
const sortedLanes = [...laneGroups.entries()].sort((a, b) => a[0] - b[0]);
|
|
239
235
|
|
|
240
236
|
for (const [lane, assignments] of sortedLanes) {
|
|
241
237
|
// Sort tasks within lane by task ID for deterministic output
|
|
242
|
-
const sortedAssignments = [...assignments].sort((a, b) =>
|
|
243
|
-
|
|
244
|
-
);
|
|
245
|
-
const taskList = sortedAssignments
|
|
246
|
-
.map((a) => `${a.taskId} [${a.task.size}]`)
|
|
247
|
-
.join(", ");
|
|
238
|
+
const sortedAssignments = [...assignments].sort((a, b) => a.taskId.localeCompare(b.taskId));
|
|
239
|
+
const taskList = sortedAssignments.map((a) => `${a.taskId} [${a.task.size}]`).join(", ");
|
|
248
240
|
const laneDuration = sortedAssignments.reduce(
|
|
249
|
-
(sum, a) =>
|
|
250
|
-
sum + getTaskDurationMinutes(a.task.size, sizeWeights),
|
|
241
|
+
(sum, a) => sum + getTaskDurationMinutes(a.task.size, sizeWeights),
|
|
251
242
|
0,
|
|
252
243
|
);
|
|
253
244
|
if (laneDuration > maxLaneDuration) maxLaneDuration = laneDuration;
|
|
254
|
-
const serialNote =
|
|
255
|
-
|
|
256
|
-
lines.push(
|
|
257
|
-
` Lane ${lane}: ${taskList}${serialNote} ` +
|
|
258
|
-
`[est. ${laneDuration} min]`,
|
|
259
|
-
);
|
|
245
|
+
const serialNote = sortedAssignments.length > 1 ? " (serial)" : "";
|
|
246
|
+
lines.push(` Lane ${lane}: ${taskList}${serialNote} ` + `[est. ${laneDuration} min]`);
|
|
260
247
|
}
|
|
261
248
|
|
|
262
249
|
// Critical path for this wave
|
|
263
250
|
totalEstimate += maxLaneDuration;
|
|
264
|
-
lines.push(
|
|
265
|
-
` ⏱ Wave duration: ${maxLaneDuration} min ` +
|
|
266
|
-
`(critical path: longest lane)`,
|
|
267
|
-
);
|
|
251
|
+
lines.push(` ⏱ Wave duration: ${maxLaneDuration} min ` + `(critical path: longest lane)`);
|
|
268
252
|
lines.push("");
|
|
269
253
|
}
|
|
270
254
|
|
|
@@ -273,17 +257,15 @@ export function formatWavePlan(
|
|
|
273
257
|
lines.push(`📊 Total estimated duration: ${totalEstimate} min (~${totalHours} hours)`);
|
|
274
258
|
lines.push(
|
|
275
259
|
` Duration model: S=${SIZE_DURATION_MINUTES["S"]}m, ` +
|
|
276
|
-
|
|
260
|
+
`M=${SIZE_DURATION_MINUTES["M"]}m, L=${SIZE_DURATION_MINUTES["L"]}m`,
|
|
277
261
|
);
|
|
278
262
|
lines.push(
|
|
279
|
-
" Critical path: sum of per-wave bottleneck lanes " +
|
|
280
|
-
"(waves sequential, lanes parallel)",
|
|
263
|
+
" Critical path: sum of per-wave bottleneck lanes " + "(waves sequential, lanes parallel)",
|
|
281
264
|
);
|
|
282
265
|
|
|
283
266
|
return lines.join("\n");
|
|
284
267
|
}
|
|
285
268
|
|
|
286
|
-
|
|
287
269
|
// ── Summary Helpers ──────────────────────────────────────────────────
|
|
288
270
|
|
|
289
271
|
/**
|
|
@@ -315,7 +297,10 @@ export function computeOrchSummaryCounts(
|
|
|
315
297
|
const failed = batchState.failedTasks;
|
|
316
298
|
const blocked = batchState.blockedTasks;
|
|
317
299
|
const total = batchState.totalTasks;
|
|
318
|
-
const queued = Math.max(
|
|
300
|
+
const queued = Math.max(
|
|
301
|
+
0,
|
|
302
|
+
total - completed - failed - blocked - stalled - running - batchState.skippedTasks,
|
|
303
|
+
);
|
|
319
304
|
|
|
320
305
|
return { completed, running, queued, failed, blocked, stalled, total };
|
|
321
306
|
}
|
|
@@ -360,9 +345,10 @@ export function buildDashboardViewModel(
|
|
|
360
345
|
const summary = computeOrchSummaryCounts(batchState, monitorState);
|
|
361
346
|
const elapsed = formatElapsedTime(batchState.startedAt, batchState.endedAt);
|
|
362
347
|
|
|
363
|
-
const waveProgress =
|
|
364
|
-
|
|
365
|
-
|
|
348
|
+
const waveProgress =
|
|
349
|
+
batchState.totalWaves > 0
|
|
350
|
+
? `${Math.max(0, batchState.currentWaveIndex + 1)}/${batchState.totalWaves}`
|
|
351
|
+
: "0/0";
|
|
366
352
|
|
|
367
353
|
// Build lane cards from monitor state (if available) or current lanes
|
|
368
354
|
const laneCards: OrchLaneCardData[] = [];
|
|
@@ -372,15 +358,16 @@ export function buildDashboardViewModel(
|
|
|
372
358
|
// lanes, but monitorState may still hold wave N's data until the first
|
|
373
359
|
// poll of wave N+1's monitor. Detect this mismatch by checking whether
|
|
374
360
|
// the monitor's lane numbers match the current allocation.
|
|
375
|
-
const monitorIsFresh =
|
|
361
|
+
const monitorIsFresh =
|
|
362
|
+
monitorState &&
|
|
363
|
+
monitorState.lanes.length > 0 &&
|
|
376
364
|
// If no current allocation, monitor data is the best we have
|
|
377
365
|
// (covers terminal phases like completed/failed/stopped)
|
|
378
|
-
batchState.currentLanes.length === 0 ||
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
);
|
|
366
|
+
(batchState.currentLanes.length === 0 ||
|
|
367
|
+
// If allocated lanes exist, verify monitor lanes match them
|
|
368
|
+
monitorState.lanes.some((ml) =>
|
|
369
|
+
batchState.currentLanes.some((cl) => cl.laneNumber === ml.laneNumber),
|
|
370
|
+
));
|
|
384
371
|
|
|
385
372
|
// TP-170: Build a laneNumber → AllocatedLane index for identity reconciliation.
|
|
386
373
|
// In workspace mode, the monitor’s sessionName (e.g., "orch-henry-api-lane-1")
|
|
@@ -438,7 +425,11 @@ export function buildDashboardViewModel(
|
|
|
438
425
|
totalChecked: snap?.totalChecked || 0,
|
|
439
426
|
totalItems: snap?.totalItems || 0,
|
|
440
427
|
completedTasks: lane.completedTasks.length,
|
|
441
|
-
totalLaneTasks:
|
|
428
|
+
totalLaneTasks:
|
|
429
|
+
lane.completedTasks.length +
|
|
430
|
+
lane.failedTasks.length +
|
|
431
|
+
lane.remainingTasks.length +
|
|
432
|
+
(lane.currentTaskId ? 1 : 0),
|
|
442
433
|
status,
|
|
443
434
|
stallReason: snap?.stallReason || null,
|
|
444
435
|
});
|
|
@@ -468,7 +459,7 @@ export function buildDashboardViewModel(
|
|
|
468
459
|
|
|
469
460
|
// Determine attach hint
|
|
470
461
|
let attachHint = "";
|
|
471
|
-
const aliveLane = laneCards.find(l => l.sessionAlive && l.status === "running");
|
|
462
|
+
const aliveLane = laneCards.find((l) => l.sessionAlive && l.status === "running");
|
|
472
463
|
if (aliveLane) {
|
|
473
464
|
attachHint = `Use /orch-sessions to inspect active lane sessions (${aliveLane.sessionName})`;
|
|
474
465
|
} else if (laneCards.length > 0) {
|
|
@@ -513,19 +504,29 @@ export function buildDashboardViewModel(
|
|
|
513
504
|
*/
|
|
514
505
|
export function renderLaneCard(card: OrchLaneCardData, colWidth: number, theme: any): string[] {
|
|
515
506
|
const w = colWidth - 2; // inner width (excluding │ borders)
|
|
516
|
-
const trunc = (s: string, max: number) => s.length > max ? s.slice(0, max - 3) + "..." : s;
|
|
507
|
+
const trunc = (s: string, max: number) => (s.length > max ? s.slice(0, max - 3) + "..." : s);
|
|
517
508
|
|
|
518
509
|
// Status icon and color
|
|
519
|
-
const statusIcon =
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
510
|
+
const statusIcon =
|
|
511
|
+
card.status === "succeeded"
|
|
512
|
+
? "✓"
|
|
513
|
+
: card.status === "running"
|
|
514
|
+
? "●"
|
|
515
|
+
: card.status === "failed"
|
|
516
|
+
? "✗"
|
|
517
|
+
: card.status === "stalled"
|
|
518
|
+
? "⚠"
|
|
519
|
+
: "○";
|
|
520
|
+
const statusColor =
|
|
521
|
+
card.status === "succeeded"
|
|
522
|
+
? "success"
|
|
523
|
+
: card.status === "running"
|
|
524
|
+
? "accent"
|
|
525
|
+
: card.status === "failed"
|
|
526
|
+
? "error"
|
|
527
|
+
: card.status === "stalled"
|
|
528
|
+
? "warning"
|
|
529
|
+
: "dim";
|
|
529
530
|
|
|
530
531
|
// Line 1: Session name (e.g., "⎡orch-lane-1⎤")
|
|
531
532
|
const sessionLabel = `⎡${card.sessionName}⎤`;
|
|
@@ -535,9 +536,11 @@ export function renderLaneCard(card: OrchLaneCardData, colWidth: number, theme:
|
|
|
535
536
|
// Line 2: Status + current task
|
|
536
537
|
const taskInfo = card.currentTaskId
|
|
537
538
|
? `${statusIcon} ${card.currentTaskId}`
|
|
538
|
-
: card.status === "succeeded"
|
|
539
|
-
|
|
540
|
-
|
|
539
|
+
: card.status === "succeeded"
|
|
540
|
+
? `${statusIcon} done`
|
|
541
|
+
: card.status === "failed"
|
|
542
|
+
? `${statusIcon} failed`
|
|
543
|
+
: `${statusIcon} idle`;
|
|
541
544
|
const taskStr = theme.fg(statusColor, trunc(taskInfo, w));
|
|
542
545
|
const taskVis = Math.min(taskInfo.length, w);
|
|
543
546
|
|
|
@@ -627,22 +630,35 @@ export function createOrchWidget(
|
|
|
627
630
|
|
|
628
631
|
// ── Phase-specific rendering ──────────────────
|
|
629
632
|
const phaseIcon =
|
|
630
|
-
vm.phase === "launching"
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
633
|
+
vm.phase === "launching"
|
|
634
|
+
? "◌"
|
|
635
|
+
: vm.phase === "planning"
|
|
636
|
+
? "◌"
|
|
637
|
+
: vm.phase === "executing"
|
|
638
|
+
? "●"
|
|
639
|
+
: vm.phase === "merging"
|
|
640
|
+
? "🔀"
|
|
641
|
+
: vm.phase === "paused"
|
|
642
|
+
? "⏸"
|
|
643
|
+
: vm.phase === "stopped"
|
|
644
|
+
? "⛔"
|
|
645
|
+
: vm.phase === "completed"
|
|
646
|
+
? "✓"
|
|
647
|
+
: vm.phase === "failed"
|
|
648
|
+
? "✗"
|
|
649
|
+
: "○";
|
|
639
650
|
const phaseColor =
|
|
640
|
-
vm.phase === "executing"
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
651
|
+
vm.phase === "executing"
|
|
652
|
+
? "accent"
|
|
653
|
+
: vm.phase === "merging"
|
|
654
|
+
? "accent"
|
|
655
|
+
: vm.phase === "completed"
|
|
656
|
+
? "success"
|
|
657
|
+
: vm.phase === "failed" || vm.phase === "stopped"
|
|
658
|
+
? "error"
|
|
659
|
+
: vm.phase === "paused"
|
|
660
|
+
? "warning"
|
|
661
|
+
: "dim";
|
|
646
662
|
|
|
647
663
|
// Header: phase icon + batch ID + wave + elapsed
|
|
648
664
|
const header =
|
|
@@ -656,10 +672,7 @@ export function createOrchWidget(
|
|
|
656
672
|
|
|
657
673
|
// ── Planning state ────────────────────────────
|
|
658
674
|
if (vm.phase === "planning") {
|
|
659
|
-
lines.push(truncateToWidth(
|
|
660
|
-
theme.fg("dim", " ◌ Planning batch..."),
|
|
661
|
-
width,
|
|
662
|
-
));
|
|
675
|
+
lines.push(truncateToWidth(theme.fg("dim", " ◌ Planning batch..."), width));
|
|
663
676
|
return lines;
|
|
664
677
|
}
|
|
665
678
|
|
|
@@ -683,18 +696,24 @@ export function createOrchWidget(
|
|
|
683
696
|
// ── Summary counts line ───────────────────────
|
|
684
697
|
const countParts: string[] = [];
|
|
685
698
|
if (vm.summary.completed > 0) countParts.push(theme.fg("success", `${vm.summary.completed} ✓`));
|
|
686
|
-
if (vm.summary.running > 0)
|
|
699
|
+
if (vm.summary.running > 0)
|
|
700
|
+
countParts.push(theme.fg("accent", `${vm.summary.running} running`));
|
|
687
701
|
if (vm.summary.queued > 0) countParts.push(theme.fg("dim", `${vm.summary.queued} queued`));
|
|
688
702
|
if (vm.summary.failed > 0) countParts.push(theme.fg("error", `${vm.summary.failed} ✗`));
|
|
689
|
-
if (vm.summary.blocked > 0)
|
|
690
|
-
|
|
703
|
+
if (vm.summary.blocked > 0)
|
|
704
|
+
countParts.push(theme.fg("warning", `${vm.summary.blocked} blocked`));
|
|
705
|
+
if (vm.summary.stalled > 0)
|
|
706
|
+
countParts.push(theme.fg("warning", `${vm.summary.stalled} stalled`));
|
|
691
707
|
if (countParts.length > 0) {
|
|
692
708
|
lines.push(truncateToWidth(" " + countParts.join(theme.fg("dim", " · ")), width));
|
|
693
709
|
}
|
|
694
710
|
lines.push("");
|
|
695
711
|
|
|
696
712
|
// ── Lane cards ─────────────────────────────────
|
|
697
|
-
if (
|
|
713
|
+
if (
|
|
714
|
+
vm.laneCards.length > 0 &&
|
|
715
|
+
(vm.phase === "executing" || vm.phase === "merging" || vm.phase === "paused")
|
|
716
|
+
) {
|
|
698
717
|
const arrowWidth = 3;
|
|
699
718
|
const minCardWidth = 18;
|
|
700
719
|
const maxCols = Math.max(1, Math.floor((width + arrowWidth) / (minCardWidth + arrowWidth)));
|
|
@@ -703,7 +722,7 @@ export function createOrchWidget(
|
|
|
703
722
|
|
|
704
723
|
for (let rowStart = 0; rowStart < vm.laneCards.length; rowStart += cols) {
|
|
705
724
|
const rowCards = vm.laneCards.slice(rowStart, rowStart + cols);
|
|
706
|
-
const rendered = rowCards.map(c => renderLaneCard(c, colWidth, theme));
|
|
725
|
+
const rendered = rowCards.map((c) => renderLaneCard(c, colWidth, theme));
|
|
707
726
|
|
|
708
727
|
if (rendered.length > 0) {
|
|
709
728
|
const cardHeight = rendered[0].length;
|
|
@@ -721,47 +740,41 @@ export function createOrchWidget(
|
|
|
721
740
|
|
|
722
741
|
// ── Terminal states (completed/failed/stopped) ──
|
|
723
742
|
if (vm.phase === "completed") {
|
|
724
|
-
lines.push(truncateToWidth(
|
|
725
|
-
theme.fg("success", " ✅ Batch complete"),
|
|
726
|
-
width,
|
|
727
|
-
));
|
|
743
|
+
lines.push(truncateToWidth(theme.fg("success", " ✅ Batch complete"), width));
|
|
728
744
|
} else if (vm.phase === "failed") {
|
|
729
|
-
lines.push(truncateToWidth(
|
|
730
|
-
theme.fg("error", " ❌ Batch failed"),
|
|
731
|
-
width,
|
|
732
|
-
));
|
|
745
|
+
lines.push(truncateToWidth(theme.fg("error", " ❌ Batch failed"), width));
|
|
733
746
|
for (const err of vm.errors.slice(0, 3)) {
|
|
734
|
-
lines.push(truncateToWidth(
|
|
735
|
-
theme.fg("error", ` ${err.slice(0, 80)}`),
|
|
736
|
-
width,
|
|
737
|
-
));
|
|
747
|
+
lines.push(truncateToWidth(theme.fg("error", ` ${err.slice(0, 80)}`), width));
|
|
738
748
|
}
|
|
739
749
|
} else if (vm.phase === "stopped") {
|
|
740
|
-
lines.push(
|
|
741
|
-
theme.fg("error", ` ⛔ Stopped by ${vm.failurePolicy || "policy"}`),
|
|
742
|
-
|
|
743
|
-
));
|
|
750
|
+
lines.push(
|
|
751
|
+
truncateToWidth(theme.fg("error", ` ⛔ Stopped by ${vm.failurePolicy || "policy"}`), width),
|
|
752
|
+
);
|
|
744
753
|
} else if (vm.phase === "merging") {
|
|
745
754
|
lines.push("");
|
|
746
|
-
lines.push(
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
755
|
+
lines.push(
|
|
756
|
+
truncateToWidth(
|
|
757
|
+
theme.fg("accent", ` 🔀 Merging lane branches into ${vm.orchBranch || "orch branch"}...`),
|
|
758
|
+
width,
|
|
759
|
+
),
|
|
760
|
+
);
|
|
750
761
|
} else if (vm.phase === "paused") {
|
|
751
762
|
lines.push("");
|
|
752
|
-
lines.push(
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
763
|
+
lines.push(
|
|
764
|
+
truncateToWidth(
|
|
765
|
+
theme.fg("warning", " ⏸ Batch paused — lanes will stop after current tasks"),
|
|
766
|
+
width,
|
|
767
|
+
),
|
|
768
|
+
);
|
|
756
769
|
}
|
|
757
770
|
|
|
758
771
|
// ── Footer: attach hint ───────────────────────
|
|
759
|
-
if (
|
|
772
|
+
if (
|
|
773
|
+
vm.attachHint &&
|
|
774
|
+
(vm.phase === "executing" || vm.phase === "merging" || vm.phase === "paused")
|
|
775
|
+
) {
|
|
760
776
|
lines.push("");
|
|
761
|
-
lines.push(truncateToWidth(
|
|
762
|
-
theme.fg("dim", ` 💡 ${vm.attachHint}`),
|
|
763
|
-
width,
|
|
764
|
-
));
|
|
777
|
+
lines.push(truncateToWidth(theme.fg("dim", ` 💡 ${vm.attachHint}`), width));
|
|
765
778
|
}
|
|
766
779
|
|
|
767
780
|
return lines;
|
|
@@ -770,4 +783,3 @@ export function createOrchWidget(
|
|
|
770
783
|
};
|
|
771
784
|
};
|
|
772
785
|
}
|
|
773
|
-
|