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.
- package/CHANGELOG.md +194 -0
- package/README.md +81 -33
- package/docs/issue-29-analysis.md +189 -0
- package/docs/superpowers/plans/2026-06-09-fallow-patterns-adoption.md +1268 -0
- package/docs/ui-optimization-plan.md +447 -0
- package/package.json +2 -2
- package/src/config/config.ts +106 -15
- package/src/errors.ts +107 -0
- package/src/extension/async-notifier.ts +6 -2
- package/src/extension/crew-cleanup.ts +8 -5
- package/src/extension/management.ts +464 -109
- package/src/extension/register.ts +213 -35
- package/src/extension/registration/brief-tool-overrides.ts +400 -0
- package/src/extension/registration/commands.ts +27 -2
- package/src/extension/registration/subagent-helpers.ts +2 -2
- package/src/extension/registration/subagent-tools.ts +9 -4
- package/src/extension/registration/team-tool.ts +17 -10
- package/src/extension/registration/viewers.ts +2 -2
- package/src/extension/team-tool/api.ts +3 -3
- package/src/extension/team-tool/cancel.ts +3 -3
- package/src/extension/team-tool/explain.ts +1 -1
- package/src/extension/team-tool/handle-schedule.ts +40 -0
- package/src/extension/team-tool/inspect.ts +3 -3
- package/src/extension/team-tool/lifecycle-actions.ts +4 -4
- package/src/extension/team-tool/respond.ts +2 -2
- package/src/extension/team-tool/run.ts +64 -14
- package/src/extension/team-tool/status.ts +1 -1
- package/src/extension/team-tool-types.ts +2 -0
- package/src/extension/team-tool.ts +173 -46
- package/src/hooks/registry.ts +77 -13
- package/src/hooks/types.ts +9 -0
- package/src/plugins/plugin-define.ts +6 -0
- package/src/plugins/plugin-registry.ts +32 -0
- package/src/plugins/plugins/index.ts +3 -0
- package/src/plugins/plugins/nextjs.ts +19 -0
- package/src/plugins/plugins/vite.ts +14 -0
- package/src/plugins/plugins/vitest.ts +14 -0
- package/src/runtime/async-runner.ts +35 -7
- package/src/runtime/background-runner.ts +529 -144
- package/src/runtime/chain-parser.ts +5 -1
- package/src/runtime/chain-runner.ts +9 -3
- package/src/runtime/checkpoint.ts +262 -180
- package/src/runtime/child-pi.ts +164 -37
- package/src/runtime/crash-recovery.ts +25 -14
- package/src/runtime/crew-agent-records.ts +2 -0
- package/src/runtime/diagnostic-export.ts +1 -1
- package/src/runtime/dynamic-script-runner.ts +10 -7
- package/src/runtime/foreground-watchdog.ts +1 -1
- package/src/runtime/heartbeat-watcher.ts +19 -2
- package/src/runtime/intercom-bridge.ts +7 -0
- package/src/runtime/iteration-hooks.ts +1 -1
- package/src/runtime/manifest-cache.ts +4 -0
- package/src/runtime/orphan-worker-registry.ts +444 -0
- package/src/runtime/parent-guard.ts +70 -16
- package/src/runtime/pi-args.ts +437 -14
- package/src/runtime/pi-spawn.ts +1 -0
- package/src/runtime/post-checks.ts +1 -1
- package/src/runtime/retry-runner.ts +9 -3
- package/src/runtime/run-tracker.ts +38 -10
- package/src/runtime/sandbox.ts +17 -2
- package/src/runtime/scheduler.ts +25 -2
- package/src/runtime/skill-effectiveness.ts +105 -62
- package/src/runtime/skill-instructions.ts +110 -32
- package/src/runtime/stale-reconciler.ts +310 -69
- package/src/runtime/subagent-manager.ts +251 -57
- package/src/runtime/task-health.ts +76 -0
- package/src/runtime/task-id.ts +20 -0
- package/src/runtime/task-runner/live-executor.ts +1 -1
- package/src/runtime/task-runner/progress.ts +1 -1
- package/src/runtime/task-runner/state-helpers.ts +110 -6
- package/src/runtime/task-runner.ts +92 -70
- package/src/runtime/team-runner.ts +186 -20
- package/src/schema/team-tool-schema.ts +27 -9
- package/src/skills/discover-skills.ts +9 -3
- package/src/state/active-run-registry.ts +170 -38
- package/src/state/artifact-store.ts +25 -13
- package/src/state/atomic-write-v2.ts +86 -0
- package/src/state/atomic-write.ts +346 -55
- package/src/state/blob-store.ts +178 -10
- package/src/state/crew-init.ts +161 -28
- package/src/state/decision-ledger.ts +172 -111
- package/src/state/event-log-rotation.ts +82 -52
- package/src/state/event-log.ts +254 -70
- package/src/state/health-store.ts +71 -0
- package/src/state/locks.ts +102 -20
- package/src/state/mailbox.ts +45 -7
- package/src/state/observation-store.ts +4 -1
- package/src/state/run-graph.ts +141 -130
- package/src/state/run-metrics.ts +24 -8
- package/src/state/session-state-map.ts +51 -0
- package/src/state/state-store.ts +330 -43
- package/src/ui/live-run-sidebar.ts +1 -1
- package/src/ui/loaders.ts +4 -0
- package/src/ui/overlays/agent-picker-overlay.ts +1 -1
- package/src/ui/overlays/mailbox-detail-overlay.ts +1 -1
- package/src/ui/powerbar-publisher.ts +1 -1
- package/src/ui/run-action-dispatcher.ts +2 -2
- package/src/ui/run-snapshot-cache.ts +1 -1
- package/src/ui/status-colors.ts +5 -1
- package/src/ui/theme-adapter.ts +80 -1
- package/src/ui/tool-progress-formatter.ts +9 -5
- package/src/ui/tool-render.ts +4 -0
- package/src/ui/tool-renderers/brief-mode.ts +207 -0
- package/src/ui/tool-renderers/index.ts +627 -0
- package/src/ui/widget/index.ts +224 -0
- package/src/ui/widget/widget-formatters.ts +148 -0
- package/src/ui/widget/widget-model.ts +90 -0
- package/src/ui/widget/widget-renderer.ts +130 -0
- package/src/ui/widget/widget-types.ts +37 -0
- package/src/utils/env-filter.ts +66 -0
- package/src/utils/file-coalescer.ts +9 -1
- package/src/utils/guards.ts +110 -0
- package/src/utils/paths.ts +80 -5
- package/src/utils/redaction.ts +6 -1
- package/src/utils/safe-paths.ts +281 -10
- package/src/worktree/cleanup.ts +112 -24
- package/src/worktree/worktree-manager.ts +128 -15
- package/test-bugs-all.mjs +10 -6
- 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
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
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
|
-
|
|
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
|
}
|