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
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
* User-facing message templates (ORCH_MESSAGES)
|
|
3
3
|
* @module orch/messages
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
AbortMode,
|
|
7
|
+
MergeFailureClassification,
|
|
8
|
+
MergeRetryCallbacks,
|
|
9
|
+
MergeRetryDecision,
|
|
10
|
+
MergeRetryLoopOutcome,
|
|
11
|
+
MergeRetryPolicy,
|
|
12
|
+
MergeWaveResult,
|
|
13
|
+
OrchestratorConfig,
|
|
14
|
+
RepoMergeOutcome,
|
|
15
|
+
} from "./types.ts";
|
|
6
16
|
import { MERGE_RETRY_POLICY_MATRIX } from "./types.ts";
|
|
7
17
|
|
|
8
18
|
// ── Message Templates ────────────────────────────────────────────────
|
|
@@ -17,7 +27,13 @@ export const ORCH_MESSAGES = {
|
|
|
17
27
|
`🚀 Starting batch ${batchId}: ${waves} wave(s), ${tasks} task(s)`,
|
|
18
28
|
orchWaveStart: (waveNum: number, totalWaves: number, tasks: number, lanes: number) =>
|
|
19
29
|
`\n🌊 Wave ${waveNum}/${totalWaves}: ${tasks} task(s) across ${lanes} lane(s)`,
|
|
20
|
-
orchWaveComplete: (
|
|
30
|
+
orchWaveComplete: (
|
|
31
|
+
waveNum: number,
|
|
32
|
+
succeeded: number,
|
|
33
|
+
failed: number,
|
|
34
|
+
skipped: number,
|
|
35
|
+
elapsedSec: number,
|
|
36
|
+
) =>
|
|
21
37
|
`✅ Wave ${waveNum} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped (${elapsedSec}s)`,
|
|
22
38
|
orchMergeStart: (waveNum: number, laneCount: number) =>
|
|
23
39
|
`🔀 [Wave ${waveNum}] Merging ${laneCount} lane(s) into target branch...`,
|
|
@@ -31,14 +47,24 @@ export const ORCH_MESSAGES = {
|
|
|
31
47
|
`🔀 [Wave ${waveNum}] Merge complete: ${mergedCount} lane(s) merged (${totalSec}s)`,
|
|
32
48
|
orchMergeFailed: (waveNum: number, laneNum: number, reason: string) =>
|
|
33
49
|
`❌ [Wave ${waveNum}] Merge failed at lane ${laneNum}: ${reason}`,
|
|
34
|
-
orchMergeSkipped: (waveNum: number) =>
|
|
35
|
-
`📝 [Wave ${waveNum}] No successful lanes to merge`,
|
|
50
|
+
orchMergeSkipped: (waveNum: number) => `📝 [Wave ${waveNum}] No successful lanes to merge`,
|
|
36
51
|
orchMergePlaceholder: (waveNum: number) =>
|
|
37
52
|
`🔀 [Wave ${waveNum}] Merge: placeholder — Step 3 (TS-008) will replace with mergeWave()`,
|
|
38
53
|
orchWorktreeReset: (waveNum: number, lanes: number) =>
|
|
39
54
|
`🔄 Resetting ${lanes} worktree(s) to target branch HEAD after wave ${waveNum}`,
|
|
40
|
-
orchBatchComplete: (
|
|
41
|
-
|
|
55
|
+
orchBatchComplete: (
|
|
56
|
+
batchId: string,
|
|
57
|
+
succeeded: number,
|
|
58
|
+
failed: number,
|
|
59
|
+
skipped: number,
|
|
60
|
+
blocked: number,
|
|
61
|
+
elapsedSec: number,
|
|
62
|
+
orchBranch?: string,
|
|
63
|
+
baseBranch?: string,
|
|
64
|
+
) => {
|
|
65
|
+
const lines = [
|
|
66
|
+
`\n🏁 Batch ${batchId} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped, ${blocked} blocked (${elapsedSec}s)`,
|
|
67
|
+
];
|
|
42
68
|
if (failed > 0 || blocked > 0) {
|
|
43
69
|
lines.push("");
|
|
44
70
|
if (blocked > 0) {
|
|
@@ -66,8 +92,7 @@ export const ORCH_MESSAGES = {
|
|
|
66
92
|
}
|
|
67
93
|
return lines.join("\n");
|
|
68
94
|
},
|
|
69
|
-
orchBatchFailed: (batchId: string, reason: string) =>
|
|
70
|
-
`\n❌ Batch ${batchId} failed: ${reason}`,
|
|
95
|
+
orchBatchFailed: (batchId: string, reason: string) => `\n❌ Batch ${batchId} failed: ${reason}`,
|
|
71
96
|
orchBatchStopped: (batchId: string, policy: string) =>
|
|
72
97
|
`\n⛔ Batch ${batchId} stopped by ${policy} policy`,
|
|
73
98
|
|
|
@@ -88,17 +113,22 @@ export const ORCH_MESSAGES = {
|
|
|
88
113
|
orphanDetectionAbort: (sessionCount: number) =>
|
|
89
114
|
`⚠️ Found ${sessionCount} orphan orchestrator session(s) without usable state.\n` +
|
|
90
115
|
` Use /orch-abort to clean up before starting a new batch.`,
|
|
91
|
-
orphanDetectionCleanup: () =>
|
|
92
|
-
`🧹 Cleaned up stale batch state file. Starting fresh.`,
|
|
116
|
+
orphanDetectionCleanup: () => `🧹 Cleaned up stale batch state file. Starting fresh.`,
|
|
93
117
|
|
|
94
118
|
// /orch-resume
|
|
95
119
|
resumeStarting: (batchId: string, phase: string) =>
|
|
96
120
|
`🔄 Resuming batch ${batchId} (was: ${phase})...`,
|
|
97
|
-
resumeReconciled: (
|
|
121
|
+
resumeReconciled: (
|
|
122
|
+
batchId: string,
|
|
123
|
+
completed: number,
|
|
124
|
+
pending: number,
|
|
125
|
+
failed: number,
|
|
126
|
+
reconnecting: number,
|
|
127
|
+
reExecuting: number = 0,
|
|
128
|
+
) =>
|
|
98
129
|
`📊 Batch ${batchId} reconciliation: ${completed} completed, ${pending} pending, ${failed} failed, ${reconnecting} reconnecting` +
|
|
99
130
|
(reExecuting > 0 ? `, ${reExecuting} re-executing` : ""),
|
|
100
|
-
resumeSkippedWaves: (skippedCount: number) =>
|
|
101
|
-
`⏭️ Skipping ${skippedCount} completed wave(s)`,
|
|
131
|
+
resumeSkippedWaves: (skippedCount: number) => `⏭️ Skipping ${skippedCount} completed wave(s)`,
|
|
102
132
|
resumeReconnecting: (sessionCount: number) =>
|
|
103
133
|
`🔗 Reconnecting to ${sessionCount} alive session(s)...`,
|
|
104
134
|
resumeNoState: () =>
|
|
@@ -128,9 +158,15 @@ export const ORCH_MESSAGES = {
|
|
|
128
158
|
` Error: ${error}\n` +
|
|
129
159
|
` Delete .pi/batch-state.json and start a new batch.`,
|
|
130
160
|
resumePhaseNotResumable: (batchId: string, phase: string, reason: string) =>
|
|
131
|
-
`❌ Cannot resume batch ${batchId} (phase: ${phase}).\n` +
|
|
132
|
-
|
|
133
|
-
|
|
161
|
+
`❌ Cannot resume batch ${batchId} (phase: ${phase}).\n` + ` ${reason}`,
|
|
162
|
+
resumeComplete: (
|
|
163
|
+
batchId: string,
|
|
164
|
+
succeeded: number,
|
|
165
|
+
failed: number,
|
|
166
|
+
skipped: number,
|
|
167
|
+
blocked: number,
|
|
168
|
+
elapsedSec: number,
|
|
169
|
+
) =>
|
|
134
170
|
`\n🏁 Resumed batch ${batchId} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped, ${blocked} blocked (${elapsedSec}s total)`,
|
|
135
171
|
|
|
136
172
|
// /orch-resume --force
|
|
@@ -147,7 +183,12 @@ export const ORCH_MESSAGES = {
|
|
|
147
183
|
`⏳ Waiting up to ${graceSec}s for sessions to checkpoint and exit...`,
|
|
148
184
|
abortGracefulForceKill: (count: number) =>
|
|
149
185
|
`⚠️ Force-killing ${count} session(s) that did not exit within timeout`,
|
|
150
|
-
abortGracefulComplete: (
|
|
186
|
+
abortGracefulComplete: (
|
|
187
|
+
batchId: string,
|
|
188
|
+
graceful: number,
|
|
189
|
+
forceKilled: number,
|
|
190
|
+
durationSec: number,
|
|
191
|
+
) =>
|
|
151
192
|
`✅ Graceful abort complete for batch ${batchId}: ${graceful} exited gracefully, ${forceKilled} force-killed (${durationSec}s)`,
|
|
152
193
|
abortHardStarting: (batchId: string, sessionCount: number) =>
|
|
153
194
|
`⚡ Hard abort of batch ${batchId}: killing ${sessionCount} session(s) immediately...`,
|
|
@@ -155,8 +196,7 @@ export const ORCH_MESSAGES = {
|
|
|
155
196
|
`✅ Hard abort complete for batch ${batchId}: ${killed} session(s) killed (${durationSec}s)`,
|
|
156
197
|
abortPartialFailure: (failureCount: number) =>
|
|
157
198
|
`⚠️ ${failureCount} error(s) during abort (see details above)`,
|
|
158
|
-
abortNoBatch: () =>
|
|
159
|
-
`No active batch to abort. Use /orch <areas|all> to start a batch.`,
|
|
199
|
+
abortNoBatch: () => `No active batch to abort. Use /orch <areas|all> to start a batch.`,
|
|
160
200
|
abortComplete: (mode: AbortMode, sessionsKilled: number) =>
|
|
161
201
|
`🏁 Abort (${mode}) complete: ${sessionsKilled} session(s) terminated. Worktrees and branches preserved.`,
|
|
162
202
|
// /orch merge — repo-scoped partial summary (TP-005 Step 1)
|
|
@@ -182,7 +222,6 @@ export const ORCH_MESSAGES = {
|
|
|
182
222
|
},
|
|
183
223
|
} as const;
|
|
184
224
|
|
|
185
|
-
|
|
186
225
|
// ── Repo-Scoped Merge Summary (TP-005) ──────────────────────────────
|
|
187
226
|
|
|
188
227
|
/**
|
|
@@ -190,10 +229,14 @@ export const ORCH_MESSAGES = {
|
|
|
190
229
|
*/
|
|
191
230
|
function repoStatusIcon(status: RepoMergeOutcome["status"]): string {
|
|
192
231
|
switch (status) {
|
|
193
|
-
case "succeeded":
|
|
194
|
-
|
|
195
|
-
case "
|
|
196
|
-
|
|
232
|
+
case "succeeded":
|
|
233
|
+
return "✅";
|
|
234
|
+
case "partial":
|
|
235
|
+
return "⚠️";
|
|
236
|
+
case "failed":
|
|
237
|
+
return "❌";
|
|
238
|
+
default:
|
|
239
|
+
return "❓";
|
|
197
240
|
}
|
|
198
241
|
}
|
|
199
242
|
|
|
@@ -228,7 +271,7 @@ export function formatRepoMergeSummary(mergeResult: MergeWaveResult): string | n
|
|
|
228
271
|
}
|
|
229
272
|
|
|
230
273
|
// Check for actual divergence: are there different statuses across repos?
|
|
231
|
-
const statuses = new Set(repoResults.map(r => r.status));
|
|
274
|
+
const statuses = new Set(repoResults.map((r) => r.status));
|
|
232
275
|
if (statuses.size < 2) {
|
|
233
276
|
// All repos have the same status (e.g., all "partial") —
|
|
234
277
|
// the partial is from within-repo lane failures, not cross-repo divergence
|
|
@@ -236,12 +279,13 @@ export function formatRepoMergeSummary(mergeResult: MergeWaveResult): string | n
|
|
|
236
279
|
}
|
|
237
280
|
|
|
238
281
|
// Build per-repo summary lines (sorted by repoId, which repoResults already is)
|
|
239
|
-
const repoLines = repoResults.map(r => {
|
|
282
|
+
const repoLines = repoResults.map((r) => {
|
|
240
283
|
const repoLabel = r.repoId ?? "(default)";
|
|
241
284
|
const icon = repoStatusIcon(r.status);
|
|
242
285
|
// TP-032 R006-3: Exclude verification_new_failure lanes from success count
|
|
243
286
|
const mergedCount = r.laneResults.filter(
|
|
244
|
-
lr =>
|
|
287
|
+
(lr) =>
|
|
288
|
+
!lr.error && (lr.result?.status === "SUCCESS" || lr.result?.status === "CONFLICT_RESOLVED"),
|
|
245
289
|
).length;
|
|
246
290
|
const totalCount = r.laneResults.length;
|
|
247
291
|
let detail = `${mergedCount}/${totalCount} lane(s) merged`;
|
|
@@ -254,7 +298,6 @@ export function formatRepoMergeSummary(mergeResult: MergeWaveResult): string | n
|
|
|
254
298
|
return ORCH_MESSAGES.orchMergePartialRepoSummary(mergeResult.waveIndex, repoLines);
|
|
255
299
|
}
|
|
256
300
|
|
|
257
|
-
|
|
258
301
|
// ── Merge Failure Policy Application (TP-005 Step 2) ─────────────────
|
|
259
302
|
|
|
260
303
|
/**
|
|
@@ -328,8 +371,11 @@ export function computeMergeFailurePolicy(
|
|
|
328
371
|
// 3. Repo-level: repos with non-succeeded status from repoResults
|
|
329
372
|
// (catches setup failures where failedLane=null and no lane results)
|
|
330
373
|
let failedLaneIds = mergeResult.laneResults
|
|
331
|
-
.filter(
|
|
332
|
-
|
|
374
|
+
.filter(
|
|
375
|
+
(r) =>
|
|
376
|
+
r.result?.status === "CONFLICT_UNRESOLVED" || r.result?.status === "BUILD_FAILURE" || r.error,
|
|
377
|
+
)
|
|
378
|
+
.map((r) => `lane-${r.laneNumber}`)
|
|
333
379
|
.join(", ");
|
|
334
380
|
if (!failedLaneIds && mergeResult.failedLane !== null) {
|
|
335
381
|
failedLaneIds = `lane-${mergeResult.failedLane}`;
|
|
@@ -338,8 +384,8 @@ export function computeMergeFailurePolicy(
|
|
|
338
384
|
// Repo-level fallback for setup failures (no lane results, failedLane=null).
|
|
339
385
|
// Uses sorted repoResults order for determinism.
|
|
340
386
|
failedLaneIds = mergeResult.repoResults
|
|
341
|
-
.filter(r => r.status !== "succeeded")
|
|
342
|
-
.map(r => `repo:${r.repoId ?? "default"}`)
|
|
387
|
+
.filter((r) => r.status !== "succeeded")
|
|
388
|
+
.map((r) => `repo:${r.repoId ?? "default"}`)
|
|
343
389
|
.join(", ");
|
|
344
390
|
}
|
|
345
391
|
|
|
@@ -384,7 +430,6 @@ export function computeMergeFailurePolicy(
|
|
|
384
430
|
};
|
|
385
431
|
}
|
|
386
432
|
|
|
387
|
-
|
|
388
433
|
// ── Cleanup Gate Policy (TP-029 Step 2) ──────────────────────────────
|
|
389
434
|
|
|
390
435
|
/**
|
|
@@ -456,12 +501,12 @@ export function computeCleanupGatePolicy(
|
|
|
456
501
|
const failedRepoCount = failures.length;
|
|
457
502
|
const totalStaleWorktrees = failures.reduce((sum, f) => sum + f.staleWorktrees.length, 0);
|
|
458
503
|
|
|
459
|
-
const repos = failures.map(f => ({
|
|
504
|
+
const repos = failures.map((f) => ({
|
|
460
505
|
repoId: f.repoId ?? "(default)",
|
|
461
506
|
staleCount: f.staleWorktrees.length,
|
|
462
507
|
}));
|
|
463
508
|
|
|
464
|
-
const repoDetail = repos.map(r => `${r.repoId} (${r.staleCount} stale)`).join(", ");
|
|
509
|
+
const repoDetail = repos.map((r) => `${r.repoId} (${r.staleCount} stale)`).join(", ");
|
|
465
510
|
|
|
466
511
|
const errorMessage =
|
|
467
512
|
`Post-merge cleanup failed at wave ${waveNum}: ${totalStaleWorktrees} stale worktree(s) ` +
|
|
@@ -481,7 +526,8 @@ export function computeCleanupGatePolicy(
|
|
|
481
526
|
`⏸️ Batch paused: post-merge cleanup failed at wave ${waveNum}.\n` +
|
|
482
527
|
` ${totalStaleWorktrees} stale worktree(s) in ${failedRepoCount} repo(s): ${repoDetail}\n` +
|
|
483
528
|
` Manual recovery:\n` +
|
|
484
|
-
recoveryLines.join("\n") +
|
|
529
|
+
recoveryLines.join("\n") +
|
|
530
|
+
"\n" +
|
|
485
531
|
` Then: /orch-resume`;
|
|
486
532
|
|
|
487
533
|
return {
|
|
@@ -522,7 +568,9 @@ export function computeCleanupGatePolicy(
|
|
|
522
568
|
* @returns Classification or null if no merge-retry class matches
|
|
523
569
|
* @since TP-033
|
|
524
570
|
*/
|
|
525
|
-
export function classifyMergeFailure(
|
|
571
|
+
export function classifyMergeFailure(
|
|
572
|
+
mergeResult: MergeWaveResult,
|
|
573
|
+
): MergeFailureClassification | null {
|
|
526
574
|
// Check lane-level errors first (most specific)
|
|
527
575
|
for (const lr of mergeResult.laneResults) {
|
|
528
576
|
if (lr.error && lr.error.startsWith("verification_new_failure")) {
|
|
@@ -619,7 +667,8 @@ export function computeMergeRetryDecision(
|
|
|
619
667
|
return {
|
|
620
668
|
shouldRetry: true,
|
|
621
669
|
cooldownMs: policy.cooldownMs,
|
|
622
|
-
reason:
|
|
670
|
+
reason:
|
|
671
|
+
`${classification} retry ${currentRetryCount + 1}/${policy.maxAttempts}` +
|
|
623
672
|
(policy.cooldownMs > 0 ? ` (cooldown: ${policy.cooldownMs}ms)` : ""),
|
|
624
673
|
currentAttempt: currentRetryCount + 1,
|
|
625
674
|
maxAttempts: policy.maxAttempts,
|
|
@@ -678,8 +727,11 @@ export function extractFailedRepoId(mergeResult: MergeWaveResult): string | unde
|
|
|
678
727
|
// 1. Try lane-level extraction
|
|
679
728
|
if (failedLaneNum !== null && failedLaneNum !== undefined) {
|
|
680
729
|
const failedLaneResult = mergeResult.laneResults.find(
|
|
681
|
-
lr =>
|
|
682
|
-
|
|
730
|
+
(lr) =>
|
|
731
|
+
lr.laneNumber === failedLaneNum &&
|
|
732
|
+
(lr.error ||
|
|
733
|
+
lr.result?.status === "CONFLICT_UNRESOLVED" ||
|
|
734
|
+
lr.result?.status === "BUILD_FAILURE"),
|
|
683
735
|
);
|
|
684
736
|
if (failedLaneResult?.repoId) return failedLaneResult.repoId;
|
|
685
737
|
}
|
|
@@ -687,7 +739,7 @@ export function extractFailedRepoId(mergeResult: MergeWaveResult): string | unde
|
|
|
687
739
|
// 2. Repo-level fallback for setup failures (failedLane === null)
|
|
688
740
|
if (mergeResult.repoResults && mergeResult.repoResults.length > 0) {
|
|
689
741
|
const failedRepo = mergeResult.repoResults.find(
|
|
690
|
-
rr => rr.status === "failed" || rr.status === "partial",
|
|
742
|
+
(rr) => rr.status === "failed" || rr.status === "partial",
|
|
691
743
|
);
|
|
692
744
|
if (failedRepo?.repoId) return failedRepo.repoId;
|
|
693
745
|
}
|
|
@@ -783,7 +835,9 @@ export async function applyMergeRetryLoop(
|
|
|
783
835
|
|
|
784
836
|
callbacks.notify(
|
|
785
837
|
`🔄 Merge retry (${lastDecision.reason}) at wave ${waveIdx + 1}. ` +
|
|
786
|
-
|
|
838
|
+
(lastDecision.cooldownMs > 0
|
|
839
|
+
? `Waiting ${lastDecision.cooldownMs}ms before retry...`
|
|
840
|
+
: "Retrying immediately..."),
|
|
787
841
|
"warning",
|
|
788
842
|
);
|
|
789
843
|
|
|
@@ -811,7 +865,8 @@ export async function applyMergeRetryLoop(
|
|
|
811
865
|
|
|
812
866
|
if (currentResult.rollbackFailed) {
|
|
813
867
|
// Safe-stop takes priority
|
|
814
|
-
const hasPersistErrors =
|
|
868
|
+
const hasPersistErrors =
|
|
869
|
+
currentResult.persistenceErrors && currentResult.persistenceErrors.length > 0;
|
|
815
870
|
const persistWarning = hasPersistErrors
|
|
816
871
|
? ` WARNING: ${currentResult.persistenceErrors!.length} transaction record(s) failed to persist.`
|
|
817
872
|
: "";
|
|
@@ -824,10 +879,12 @@ export async function applyMergeRetryLoop(
|
|
|
824
879
|
lastDecision,
|
|
825
880
|
errorMessage:
|
|
826
881
|
`Safe-stop at wave ${waveIdx + 1}: verification rollback failed after retry. ` +
|
|
827
|
-
`Merge worktree and temp branch preserved for recovery.` +
|
|
882
|
+
`Merge worktree and temp branch preserved for recovery.` +
|
|
883
|
+
persistWarning,
|
|
828
884
|
notifyMessage:
|
|
829
885
|
`🛑 Safe-stop: verification rollback failed at wave ${waveIdx + 1} after retry. ` +
|
|
830
|
-
`Batch force-paused.` +
|
|
886
|
+
`Batch force-paused.` +
|
|
887
|
+
persistWarning,
|
|
831
888
|
};
|
|
832
889
|
}
|
|
833
890
|
|
|
@@ -907,12 +964,13 @@ export function computeIntegrateCleanupResult(
|
|
|
907
964
|
repoFindings: IntegrateCleanupRepoFindings[],
|
|
908
965
|
): IntegrateCleanupResult {
|
|
909
966
|
// Filter to repos that have at least one issue
|
|
910
|
-
const dirtyRepos = repoFindings.filter(
|
|
911
|
-
r
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
967
|
+
const dirtyRepos = repoFindings.filter(
|
|
968
|
+
(r) =>
|
|
969
|
+
r.staleWorktrees.length > 0 ||
|
|
970
|
+
r.staleLaneBranches.length > 0 ||
|
|
971
|
+
r.staleOrchBranches.length > 0 ||
|
|
972
|
+
r.staleAutostashEntries.length > 0 ||
|
|
973
|
+
r.nonEmptyWorktreeContainers.length > 0,
|
|
916
974
|
);
|
|
917
975
|
|
|
918
976
|
if (dirtyRepos.length === 0) {
|
|
@@ -180,7 +180,7 @@ export const MIGRATION_REGISTRY: Migration[] = [
|
|
|
180
180
|
if (!existsSync(templatePath)) {
|
|
181
181
|
throw new Error(
|
|
182
182
|
`Migration template not found: ${templatePath}. ` +
|
|
183
|
-
|
|
183
|
+
`This may indicate a packaging issue with the taskplane package.`,
|
|
184
184
|
);
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -169,10 +169,10 @@ export function resolvePiCliPath(): string {
|
|
|
169
169
|
|
|
170
170
|
throw new Error(
|
|
171
171
|
"Cannot find Pi CLI entrypoint (pi-coding-agent/dist/cli.js) under any known npm scope " +
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
`(${PI_PACKAGE_SCOPES.join(" or ")}). ` +
|
|
173
|
+
"Install via 'npm install -g @earendil-works/pi-coding-agent' " +
|
|
174
|
+
"(or, for legacy installs, 'npm install -g @mariozechner/pi-coding-agent'). " +
|
|
175
|
+
`npm root -g returned: ${npmRoot || "(empty — npm may not be on PATH)"}`,
|
|
176
176
|
);
|
|
177
177
|
}
|
|
178
178
|
|
|
@@ -236,7 +236,9 @@ export function resolveTaskplanePackageFile(repoRoot: string, relPath: string):
|
|
|
236
236
|
const piPkgDir = resolve(piPath, "..", ".."); // <npmRoot>/<scope>/pi-coding-agent
|
|
237
237
|
const npmRootFromPi = resolve(piPkgDir, "..", ".."); // <npmRoot>
|
|
238
238
|
candidates.push(join(npmRootFromPi, "taskplane", relPath));
|
|
239
|
-
} catch {
|
|
239
|
+
} catch {
|
|
240
|
+
/* ignore — process.argv[1] may be undefined in test contexts */
|
|
241
|
+
}
|
|
240
242
|
|
|
241
243
|
for (const candidate of candidates) {
|
|
242
244
|
if (existsSync(candidate)) return candidate;
|
|
@@ -269,8 +271,5 @@ export function resolveTaskplanePackageFile(repoRoot: string, relPath: string):
|
|
|
269
271
|
* ```
|
|
270
272
|
*/
|
|
271
273
|
export function resolveTaskplaneAgentTemplate(agentName: string): string {
|
|
272
|
-
return resolveTaskplanePackageFile(
|
|
273
|
-
process.cwd(),
|
|
274
|
-
join("templates", "agents", `${agentName}.md`),
|
|
275
|
-
);
|
|
274
|
+
return resolveTaskplanePackageFile(process.cwd(), join("templates", "agents", `${agentName}.md`));
|
|
276
275
|
}
|