pi-crew 0.6.1 → 0.6.4

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 (119) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +81 -33
  3. package/docs/issue-29-analysis.md +189 -0
  4. package/docs/superpowers/plans/2026-06-09-fallow-patterns-adoption.md +1268 -0
  5. package/docs/ui-optimization-plan.md +447 -0
  6. package/package.json +2 -2
  7. package/src/config/config.ts +106 -15
  8. package/src/errors.ts +107 -0
  9. package/src/extension/async-notifier.ts +6 -2
  10. package/src/extension/crew-cleanup.ts +8 -5
  11. package/src/extension/management.ts +464 -109
  12. package/src/extension/register.ts +213 -35
  13. package/src/extension/registration/brief-tool-overrides.ts +400 -0
  14. package/src/extension/registration/commands.ts +27 -2
  15. package/src/extension/registration/subagent-helpers.ts +2 -2
  16. package/src/extension/registration/subagent-tools.ts +9 -4
  17. package/src/extension/registration/team-tool.ts +17 -10
  18. package/src/extension/registration/viewers.ts +2 -2
  19. package/src/extension/team-tool/api.ts +3 -3
  20. package/src/extension/team-tool/cancel.ts +3 -3
  21. package/src/extension/team-tool/explain.ts +1 -1
  22. package/src/extension/team-tool/handle-schedule.ts +40 -0
  23. package/src/extension/team-tool/inspect.ts +3 -3
  24. package/src/extension/team-tool/lifecycle-actions.ts +4 -4
  25. package/src/extension/team-tool/respond.ts +2 -2
  26. package/src/extension/team-tool/run.ts +64 -14
  27. package/src/extension/team-tool/status.ts +1 -1
  28. package/src/extension/team-tool-types.ts +2 -0
  29. package/src/extension/team-tool.ts +173 -46
  30. package/src/hooks/registry.ts +77 -13
  31. package/src/hooks/types.ts +9 -0
  32. package/src/plugins/plugin-define.ts +6 -0
  33. package/src/plugins/plugin-registry.ts +32 -0
  34. package/src/plugins/plugins/index.ts +3 -0
  35. package/src/plugins/plugins/nextjs.ts +19 -0
  36. package/src/plugins/plugins/vite.ts +14 -0
  37. package/src/plugins/plugins/vitest.ts +14 -0
  38. package/src/runtime/async-runner.ts +35 -7
  39. package/src/runtime/background-runner.ts +529 -144
  40. package/src/runtime/chain-parser.ts +5 -1
  41. package/src/runtime/chain-runner.ts +9 -3
  42. package/src/runtime/checkpoint.ts +262 -180
  43. package/src/runtime/child-pi.ts +164 -37
  44. package/src/runtime/crash-recovery.ts +25 -14
  45. package/src/runtime/crew-agent-records.ts +2 -0
  46. package/src/runtime/diagnostic-export.ts +1 -1
  47. package/src/runtime/dynamic-script-runner.ts +10 -7
  48. package/src/runtime/foreground-watchdog.ts +1 -1
  49. package/src/runtime/heartbeat-watcher.ts +19 -2
  50. package/src/runtime/intercom-bridge.ts +7 -0
  51. package/src/runtime/iteration-hooks.ts +1 -1
  52. package/src/runtime/manifest-cache.ts +4 -0
  53. package/src/runtime/orphan-worker-registry.ts +444 -0
  54. package/src/runtime/parent-guard.ts +70 -16
  55. package/src/runtime/pi-args.ts +437 -14
  56. package/src/runtime/pi-spawn.ts +1 -0
  57. package/src/runtime/post-checks.ts +1 -1
  58. package/src/runtime/retry-runner.ts +9 -3
  59. package/src/runtime/run-tracker.ts +38 -10
  60. package/src/runtime/sandbox.ts +17 -2
  61. package/src/runtime/scheduler.ts +25 -2
  62. package/src/runtime/skill-effectiveness.ts +105 -62
  63. package/src/runtime/skill-instructions.ts +110 -32
  64. package/src/runtime/stale-reconciler.ts +310 -69
  65. package/src/runtime/subagent-manager.ts +251 -57
  66. package/src/runtime/task-health.ts +76 -0
  67. package/src/runtime/task-id.ts +20 -0
  68. package/src/runtime/task-runner/live-executor.ts +1 -1
  69. package/src/runtime/task-runner/progress.ts +1 -1
  70. package/src/runtime/task-runner/state-helpers.ts +110 -6
  71. package/src/runtime/task-runner.ts +92 -70
  72. package/src/runtime/team-runner.ts +186 -20
  73. package/src/schema/team-tool-schema.ts +27 -9
  74. package/src/skills/discover-skills.ts +9 -3
  75. package/src/state/active-run-registry.ts +170 -38
  76. package/src/state/artifact-store.ts +25 -13
  77. package/src/state/atomic-write-v2.ts +86 -0
  78. package/src/state/atomic-write.ts +346 -55
  79. package/src/state/blob-store.ts +178 -10
  80. package/src/state/crew-init.ts +161 -28
  81. package/src/state/decision-ledger.ts +172 -111
  82. package/src/state/event-log-rotation.ts +82 -52
  83. package/src/state/event-log.ts +254 -70
  84. package/src/state/health-store.ts +71 -0
  85. package/src/state/locks.ts +102 -20
  86. package/src/state/mailbox.ts +45 -7
  87. package/src/state/observation-store.ts +4 -1
  88. package/src/state/run-graph.ts +141 -130
  89. package/src/state/run-metrics.ts +24 -8
  90. package/src/state/session-state-map.ts +51 -0
  91. package/src/state/state-store.ts +330 -43
  92. package/src/ui/live-run-sidebar.ts +1 -1
  93. package/src/ui/loaders.ts +4 -0
  94. package/src/ui/overlays/agent-picker-overlay.ts +1 -1
  95. package/src/ui/overlays/mailbox-detail-overlay.ts +1 -1
  96. package/src/ui/powerbar-publisher.ts +1 -1
  97. package/src/ui/run-action-dispatcher.ts +2 -2
  98. package/src/ui/run-snapshot-cache.ts +1 -1
  99. package/src/ui/status-colors.ts +5 -1
  100. package/src/ui/theme-adapter.ts +80 -1
  101. package/src/ui/tool-progress-formatter.ts +9 -5
  102. package/src/ui/tool-render.ts +4 -0
  103. package/src/ui/tool-renderers/brief-mode.ts +207 -0
  104. package/src/ui/tool-renderers/index.ts +627 -0
  105. package/src/ui/widget/index.ts +224 -0
  106. package/src/ui/widget/widget-formatters.ts +148 -0
  107. package/src/ui/widget/widget-model.ts +90 -0
  108. package/src/ui/widget/widget-renderer.ts +130 -0
  109. package/src/ui/widget/widget-types.ts +37 -0
  110. package/src/utils/env-filter.ts +66 -0
  111. package/src/utils/file-coalescer.ts +9 -1
  112. package/src/utils/guards.ts +110 -0
  113. package/src/utils/paths.ts +80 -5
  114. package/src/utils/redaction.ts +6 -1
  115. package/src/utils/safe-paths.ts +281 -10
  116. package/src/worktree/cleanup.ts +112 -24
  117. package/src/worktree/worktree-manager.ts +128 -15
  118. package/test-bugs-all.mjs +10 -6
  119. package/test-integration-check.ts +4 -0
package/src/errors.ts ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Error code taxonomy for pi-crew.
3
+ * Maps to semantic categories matching fallow's E001-E004 pattern.
4
+ */
5
+ // Implemented as const object + type alias (not `enum`) so that Node's
6
+ // `--experimental-strip-types` can load this module. TypeScript `enum`
7
+ // syntax is not supported in strip-only mode.
8
+ export const ErrorCode = {
9
+ FileReadError: "E001", // Cannot read a file
10
+ FileWriteError: "E002", // Cannot write a file
11
+ TaskNotFound: "E003", // Referenced task ID does not exist
12
+ InvalidStatusTransition: "E004", // Run/task status cannot legally transition
13
+ ConfigError: "E005", // Malformed config or missing required field
14
+ ResourceNotFound: "E006", // Agent/team/workflow not found in discovery paths
15
+ } as const;
16
+
17
+ export type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode];
18
+
19
+ const DEFAULT_HELP: Record<ErrorCode, string | undefined> = {
20
+ [ErrorCode.FileReadError]: "Check that the file exists and that the process has read permission.",
21
+ [ErrorCode.FileWriteError]: "Check that the disk is not full and that the process has write permission.",
22
+ [ErrorCode.TaskNotFound]: "The task may have been removed or the run may be in an inconsistent state. Use `team status` to verify.",
23
+ [ErrorCode.InvalidStatusTransition]: "Verify the run status using `team status` before retrying.",
24
+ [ErrorCode.ConfigError]: "Check the configuration file for syntax errors or missing required fields.",
25
+ [ErrorCode.ResourceNotFound]: "Use `team list` to see available agents, teams, and workflows.",
26
+ };
27
+
28
+ /**
29
+ * Structured error type for pi-crew.
30
+ * Display format:
31
+ * error[E001]: Failed to read manifest.json: not found
32
+ * context: while loading run state
33
+ * help: Check that the file exists and that the process has read permission.
34
+ */
35
+ export class CrewError extends Error {
36
+ readonly code: ErrorCode;
37
+ help?: string;
38
+ private _context?: string;
39
+
40
+ constructor(code: ErrorCode, message: string, help?: string) {
41
+ super(message);
42
+ this.name = "CrewError";
43
+ this.code = code;
44
+ this.help = help ?? DEFAULT_HELP[code];
45
+ Object.defineProperty(this, "message", { enumerable: true });
46
+ Object.defineProperty(this, "code", { enumerable: true });
47
+ }
48
+
49
+ withContext(context: string): this {
50
+ this._context = context;
51
+ return this;
52
+ }
53
+
54
+ withHelp(help: string): this {
55
+ this.help = help;
56
+ return this;
57
+ }
58
+
59
+ toString(): string {
60
+ let out = `error[${this.code}]: ${this.message}`;
61
+ if (this._context) out += `\n context: ${this._context}`;
62
+ if (this.help) out += `\n help: ${this.help}`;
63
+ return out;
64
+ }
65
+ }
66
+
67
+ export const errors = {
68
+ fileRead(path: string, source: NodeJS.ErrnoException): CrewError {
69
+ return new CrewError(
70
+ ErrorCode.FileReadError,
71
+ `Failed to read ${path}: ${source.code?.toLowerCase() ?? "unknown"}`,
72
+ ).withContext("file system read operation");
73
+ },
74
+
75
+ fileWrite(path: string, source: NodeJS.ErrnoException): CrewError {
76
+ return new CrewError(
77
+ ErrorCode.FileWriteError,
78
+ `Failed to write ${path}: ${source.code?.toLowerCase() ?? "unknown"}`,
79
+ ).withContext("file system write operation");
80
+ },
81
+
82
+ taskNotFound(taskId: string, runId?: string): CrewError {
83
+ const msg = runId
84
+ ? `Task '${taskId}' not found in run '${runId}'`
85
+ : `Task '${taskId}' not found`;
86
+ return new CrewError(ErrorCode.TaskNotFound, msg);
87
+ },
88
+
89
+ invalidStatusTransition(from: string, to: string): CrewError {
90
+ return new CrewError(
91
+ ErrorCode.InvalidStatusTransition,
92
+ `Invalid run status transition: ${from} -> ${to}`,
93
+ );
94
+ },
95
+
96
+ config(message: string): CrewError {
97
+ return new CrewError(ErrorCode.ConfigError, message)
98
+ .withContext("configuration loading");
99
+ },
100
+
101
+ resourceNotFound(type: string, name: string): CrewError {
102
+ return new CrewError(
103
+ ErrorCode.ResourceNotFound,
104
+ `${type} '${name}' not found in any discovery path`,
105
+ );
106
+ },
107
+ } as const;
@@ -47,7 +47,7 @@ function isTaskActive(task: TeamTaskState): boolean {
47
47
  }
48
48
 
49
49
  function markActiveTasksAndAgentsFailed(run: TeamRunManifest, message: string): void {
50
- const loaded = loadRunManifestById(run.cwd, run.runId);
50
+ const loaded = loadRunManifestById(run.cwd, run.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency;
51
51
  const tasks = loaded?.tasks ?? [];
52
52
  const failedAt = new Date().toISOString();
53
53
  if (tasks.some(isTaskActive)) {
@@ -73,7 +73,7 @@ export function markDeadAsyncRunIfNeeded(run: TeamRunManifest, now = Date.now(),
73
73
  const asyncPid = run.async.pid;
74
74
  const message = `Background runner died unexpectedly; check background.log (${liveness.detail}).`;
75
75
  return withRunLockSync(run, () => {
76
- const fresh = loadRunManifestById(run.cwd, run.runId);
76
+ const fresh = loadRunManifestById(run.cwd, run.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency;
77
77
  if (!fresh || !isActiveRunStatus(fresh.manifest.status)) return undefined;
78
78
  const failed = updateRunStatus(fresh.manifest, "failed", message);
79
79
  markActiveTasksAndAgentsFailed(failed, message);
@@ -125,6 +125,10 @@ export function startAsyncRunNotifier(ctx: ExtensionContext, state: AsyncNotifie
125
125
  logInternalError("async-notifier", error, `interval=${intervalMs}`);
126
126
  }
127
127
  }, intervalMs);
128
+ // Defense-in-depth: never let the notifier timer keep the event loop alive.
129
+ // If stopAsyncRunNotifier is missed (session switch race), the next run of
130
+ // this interval is harmless, but the timer must not block process exit.
131
+ if (typeof state.interval.unref === "function") state.interval.unref();
128
132
  }
129
133
 
130
134
  export function stopAsyncRunNotifier(state: AsyncNotifierState): void {
@@ -1,5 +1,6 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { logInternalError } from "../utils/internal-error.ts";
3
+ import { cleanupAllTrackedTempDirs } from "../runtime/pi-args.ts";
3
4
  // NOTE: globalProgressTracker import kept for documentation but not directly used
4
5
  // since we don't have agent IDs to untrack. Actual progress clearing should be
5
6
  // handled by the progress tracker itself on shutdown.
@@ -112,12 +113,14 @@ async function cleanupChildProcesses(): Promise<void> {
112
113
  }
113
114
 
114
115
  async function cleanupTempDirectories(): Promise<void> {
115
- // NOTE: getTempDir is not available in paths.ts.
116
- // For now, just log that cleanup is pending.
117
- // Actual temp directory cleanup should be implemented by the run-graph
118
- // or the specific code that creates temporary workspaces.
116
+ // Clean up every temp dir created in this process. Previously this was
117
+ // a stub that just logged; it caused /tmp/pi-crew-* dirs to accumulate
118
+ // from killed test runs and child-pi invocations. See issue #<n>.
119
119
  try {
120
- console.log(`[pi-crew] Temp directory cleanup deferred to run-graph`);
120
+ const result = cleanupAllTrackedTempDirs();
121
+ if (result.cleaned > 0) {
122
+ console.log(`[pi-crew] Cleaned ${result.cleaned} tracked temp dirs (${result.failed} failed)`);
123
+ }
121
124
  } catch (error) {
122
125
  logInternalError("crew-cleanup.temp", error);
123
126
  }