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.
Files changed (41) hide show
  1. package/bin/gitignore-patterns.mjs +11 -8
  2. package/bin/rpc-wrapper.mjs +410 -357
  3. package/bin/taskplane.mjs +533 -250
  4. package/extensions/reviewer-extension.ts +17 -11
  5. package/extensions/taskplane/abort.ts +50 -18
  6. package/extensions/taskplane/agent-bridge-extension.ts +232 -105
  7. package/extensions/taskplane/agent-host.ts +224 -97
  8. package/extensions/taskplane/cleanup.ts +71 -42
  9. package/extensions/taskplane/config-loader.ts +142 -58
  10. package/extensions/taskplane/config-schema.ts +6 -13
  11. package/extensions/taskplane/config.ts +10 -2
  12. package/extensions/taskplane/diagnostic-reports.ts +59 -47
  13. package/extensions/taskplane/diagnostics.ts +13 -13
  14. package/extensions/taskplane/discovery.ts +35 -61
  15. package/extensions/taskplane/engine-worker.ts +53 -46
  16. package/extensions/taskplane/engine.ts +1760 -602
  17. package/extensions/taskplane/execution.ts +426 -206
  18. package/extensions/taskplane/extension.ts +1073 -598
  19. package/extensions/taskplane/formatting.ts +136 -124
  20. package/extensions/taskplane/git.ts +0 -2
  21. package/extensions/taskplane/lane-runner.ts +542 -311
  22. package/extensions/taskplane/mailbox.ts +57 -49
  23. package/extensions/taskplane/merge.ts +662 -383
  24. package/extensions/taskplane/messages.ts +109 -51
  25. package/extensions/taskplane/migrations.ts +1 -1
  26. package/extensions/taskplane/path-resolver.ts +8 -9
  27. package/extensions/taskplane/persistence.ts +425 -262
  28. package/extensions/taskplane/process-registry.ts +36 -7
  29. package/extensions/taskplane/quality-gate.ts +107 -55
  30. package/extensions/taskplane/resume.ts +774 -267
  31. package/extensions/taskplane/sessions.ts +1 -1
  32. package/extensions/taskplane/settings-tui.ts +505 -164
  33. package/extensions/taskplane/sidecar-telemetry.ts +25 -10
  34. package/extensions/taskplane/supervisor.ts +477 -270
  35. package/extensions/taskplane/task-executor-core.ts +178 -53
  36. package/extensions/taskplane/types.ts +186 -108
  37. package/extensions/taskplane/verification.ts +27 -22
  38. package/extensions/taskplane/waves.ts +59 -43
  39. package/extensions/taskplane/workspace.ts +14 -12
  40. package/extensions/taskplane/worktree.ts +218 -196
  41. 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 { AbortMode, MergeFailureClassification, MergeRetryCallbacks, MergeRetryDecision, MergeRetryLoopOutcome, MergeRetryPolicy, MergeWaveResult, OrchestratorConfig, RepoMergeOutcome } from "./types.ts";
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: (waveNum: number, succeeded: number, failed: number, skipped: number, elapsedSec: number) =>
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: (batchId: string, succeeded: number, failed: number, skipped: number, blocked: number, elapsedSec: number, orchBranch?: string, baseBranch?: string) => {
41
- const lines = [`\n🏁 Batch ${batchId} complete: ${succeeded} succeeded, ${failed} failed, ${skipped} skipped, ${blocked} blocked (${elapsedSec}s)`];
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: (batchId: string, completed: number, pending: number, failed: number, reconnecting: number, reExecuting: number = 0) =>
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
- ` ${reason}`,
133
- resumeComplete: (batchId: string, succeeded: number, failed: number, skipped: number, blocked: number, elapsedSec: number) =>
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: (batchId: string, graceful: number, forceKilled: number, durationSec: number) =>
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": return "✅";
194
- case "partial": return "⚠️";
195
- case "failed": return "❌";
196
- default: return "";
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 => !lr.error && (lr.result?.status === "SUCCESS" || lr.result?.status === "CONFLICT_RESOLVED"),
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(r => r.result?.status === "CONFLICT_UNRESOLVED" || r.result?.status === "BUILD_FAILURE" || r.error)
332
- .map(r => `lane-${r.laneNumber}`)
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") + "\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(mergeResult: MergeWaveResult): MergeFailureClassification | null {
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: `${classification} retry ${currentRetryCount + 1}/${policy.maxAttempts}` +
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 => lr.laneNumber === failedLaneNum &&
682
- (lr.error || lr.result?.status === "CONFLICT_UNRESOLVED" || lr.result?.status === "BUILD_FAILURE"),
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
- (lastDecision.cooldownMs > 0 ? `Waiting ${lastDecision.cooldownMs}ms before retry...` : "Retrying immediately..."),
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 = currentResult.persistenceErrors && currentResult.persistenceErrors.length > 0;
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.` + persistWarning,
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.` + persistWarning,
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(r =>
911
- r.staleWorktrees.length > 0 ||
912
- r.staleLaneBranches.length > 0 ||
913
- r.staleOrchBranches.length > 0 ||
914
- r.staleAutostashEntries.length > 0 ||
915
- r.nonEmptyWorktreeContainers.length > 0,
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
- `This may indicate a packaging issue with the taskplane package.`,
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
- `(${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)"}`,
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 { /* ignore — process.argv[1] may be undefined in test contexts */ }
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
  }