pi-crew 0.1.49 → 0.2.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/CHANGELOG.md +74 -1
- package/README.md +176 -781
- package/agents/analyst.md +11 -11
- package/agents/critic.md +11 -11
- package/agents/executor.md +11 -11
- package/agents/explorer.md +11 -11
- package/agents/planner.md +11 -11
- package/agents/reviewer.md +11 -11
- package/agents/security-reviewer.md +11 -11
- package/agents/test-engineer.md +11 -11
- package/agents/verifier.md +70 -11
- package/agents/writer.md +11 -11
- package/docs/actions-reference.md +595 -0
- package/docs/commands-reference.md +347 -0
- package/docs/runtime-flow.md +148 -148
- package/index.ts +6 -6
- package/package.json +99 -99
- package/skills/async-worker-recovery/SKILL.md +42 -42
- package/skills/context-artifact-hygiene/SKILL.md +52 -52
- package/skills/delegation-patterns/SKILL.md +54 -54
- package/skills/mailbox-interactive/SKILL.md +40 -40
- package/skills/model-routing-context/SKILL.md +39 -39
- package/skills/multi-perspective-review/SKILL.md +58 -58
- package/skills/observability-reliability/SKILL.md +41 -41
- package/skills/orchestration/SKILL.md +157 -157
- package/skills/ownership-session-security/SKILL.md +41 -41
- package/skills/pi-extension-lifecycle/SKILL.md +39 -39
- package/skills/requirements-to-task-packet/SKILL.md +63 -63
- package/skills/resource-discovery-config/SKILL.md +41 -41
- package/skills/runtime-state-reader/SKILL.md +44 -44
- package/skills/secure-agent-orchestration-review/SKILL.md +45 -45
- package/skills/state-mutation-locking/SKILL.md +42 -42
- package/skills/systematic-debugging/SKILL.md +67 -67
- package/skills/ui-render-performance/SKILL.md +39 -39
- package/skills/verification-before-done/SKILL.md +57 -57
- package/skills/worktree-isolation/SKILL.md +39 -39
- package/src/adapters/claude-adapter.ts +25 -0
- package/src/adapters/codex-adapter.ts +21 -0
- package/src/adapters/cursor-adapter.ts +17 -0
- package/src/adapters/export-util.ts +137 -0
- package/src/adapters/index.ts +15 -0
- package/src/adapters/registry.ts +18 -0
- package/src/adapters/types.ts +23 -0
- package/src/agents/agent-config.ts +2 -0
- package/src/agents/agent-search.ts +98 -98
- package/src/agents/discover-agents.ts +2 -1
- package/src/config/config.ts +14 -1
- package/src/config/defaults.ts +5 -5
- package/src/config/drift-detector.ts +211 -0
- package/src/config/markers.ts +327 -0
- package/src/config/resilient-parser.ts +108 -0
- package/src/config/suggestions.ts +74 -0
- package/src/extension/cross-extension-rpc.ts +103 -82
- package/src/extension/project-init.ts +36 -4
- package/src/extension/register.ts +67 -22
- package/src/extension/registration/commands.ts +77 -8
- package/src/extension/registration/subagent-tools.ts +10 -1
- package/src/extension/registration/team-tool.ts +10 -1
- package/src/extension/registration/viewers.ts +48 -34
- package/src/extension/run-bundle-schema.ts +89 -89
- package/src/extension/run-export.ts +26 -12
- package/src/extension/run-import.ts +25 -1
- package/src/extension/run-index.ts +5 -1
- package/src/extension/run-maintenance.ts +142 -68
- package/src/extension/team-manager-command.ts +10 -1
- package/src/extension/team-tool/context.ts +1 -1
- package/src/extension/team-tool/doctor.ts +28 -3
- package/src/extension/team-tool/handle-settings.ts +195 -188
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/intent-policy.ts +42 -42
- package/src/extension/team-tool/lifecycle-actions.ts +27 -8
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/run.ts +12 -1
- package/src/extension/team-tool.ts +14 -3
- package/src/i18n.ts +184 -184
- package/src/observability/exporters/otlp-exporter.ts +92 -77
- package/src/prompt/prompt-runtime.ts +72 -72
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -114
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/auto-resume.ts +100 -0
- package/src/runtime/background-runner.ts +11 -1
- package/src/runtime/cancellation-token.ts +89 -89
- package/src/runtime/cancellation.ts +61 -61
- package/src/runtime/capability-inventory.ts +116 -116
- package/src/runtime/child-pi.ts +7 -2
- package/src/runtime/compaction-summary.ts +271 -0
- package/src/runtime/completion-guard.ts +190 -190
- package/src/runtime/concurrency.ts +3 -1
- package/src/runtime/crash-recovery.ts +33 -0
- package/src/runtime/delta-conflict.ts +360 -0
- package/src/runtime/diagnostic-export.ts +3 -1
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/event-stream-bridge.ts +3 -1
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +106 -106
- package/src/runtime/heartbeat-gradient.ts +28 -28
- package/src/runtime/heartbeat-watcher.ts +124 -124
- package/src/runtime/iteration-hooks.ts +262 -0
- package/src/runtime/live-agent-control.ts +88 -88
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-extension-bridge.ts +150 -150
- package/src/runtime/live-irc.ts +92 -92
- package/src/runtime/live-session-health.ts +100 -100
- package/src/runtime/loop-gates.ts +129 -0
- package/src/runtime/metric-parser.ts +40 -0
- package/src/runtime/notebook-helpers.ts +90 -90
- package/src/runtime/orphan-sentinel.ts +7 -7
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/phase-progress.ts +217 -0
- package/src/runtime/pi-args.ts +38 -2
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/pi-spawn.ts +74 -6
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/post-checks.ts +122 -0
- package/src/runtime/process-status.ts +14 -1
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/prose-compressor.ts +164 -164
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/result-extractor.ts +121 -121
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/sensitive-paths.ts +3 -3
- package/src/runtime/session-resources.ts +25 -25
- package/src/runtime/session-snapshot.ts +59 -59
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/stream-preview.ts +177 -177
- package/src/runtime/supervisor-contact.ts +59 -59
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-graph.ts +207 -0
- package/src/runtime/task-quality.ts +207 -0
- package/src/runtime/task-runner/capabilities.ts +78 -78
- package/src/runtime/task-runner/live-executor.ts +7 -1
- package/src/runtime/task-runner/progress.ts +119 -119
- package/src/runtime/task-runner/prompt-builder.ts +1 -1
- package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/run-projection.ts +103 -103
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/team-runner.ts +126 -7
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/runtime/workflow-state.ts +187 -0
- package/src/runtime/workspace-tree.ts +298 -298
- package/src/schema/config-schema.ts +12 -0
- package/src/schema/validation-types.ts +148 -0
- package/src/skills/skill-templates.ts +374 -0
- package/src/state/active-run-registry.ts +35 -11
- package/src/state/atomic-write.ts +33 -26
- package/src/state/contracts.ts +1 -0
- package/src/state/event-reconstructor.ts +217 -0
- package/src/state/locks.ts +2 -11
- package/src/state/mailbox.ts +4 -3
- package/src/state/state-store.ts +32 -14
- package/src/state/task-claims.ts +44 -44
- package/src/state/types.ts +9 -0
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/crew-footer.ts +101 -101
- package/src/ui/crew-select-list.ts +111 -111
- package/src/ui/crew-widget.ts +9 -4
- package/src/ui/dashboard-panes/cancellation-pane.ts +42 -42
- package/src/ui/dashboard-panes/capability-pane.ts +59 -59
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -35
- package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
- package/src/ui/dashboard-panes/progress-pane.ts +11 -0
- package/src/ui/dynamic-border.ts +25 -25
- package/src/ui/layout-primitives.ts +106 -106
- package/src/ui/loaders.ts +158 -158
- package/src/ui/powerbar-publisher.ts +6 -0
- package/src/ui/render-coalescer.ts +51 -51
- package/src/ui/render-diff.ts +119 -119
- package/src/ui/render-scheduler.ts +143 -143
- package/src/ui/run-action-dispatcher.ts +10 -1
- package/src/ui/spinner.ts +17 -17
- package/src/ui/status-colors.ts +58 -58
- package/src/ui/syntax-highlight.ts +116 -116
- package/src/ui/transcript-entries.ts +258 -258
- package/src/utils/completion-dedupe.ts +63 -63
- package/src/utils/frontmatter.ts +68 -68
- package/src/utils/git.ts +262 -262
- package/src/utils/ids.ts +17 -17
- package/src/utils/incremental-reader.ts +104 -104
- package/src/utils/names.ts +27 -27
- package/src/utils/redaction.ts +44 -44
- package/src/utils/safe-paths.ts +47 -47
- package/src/utils/scan-cache.ts +136 -136
- package/src/utils/sleep.ts +40 -26
- package/src/utils/task-name-generator.ts +337 -337
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/worktree-manager.ts +11 -3
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/workflows/default.workflow.md +30 -29
- package/workflows/fast-fix.workflow.md +23 -22
- package/workflows/implementation.workflow.md +43 -38
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
- package/docs/refactor-tasks-phase3.md +0 -394
- package/docs/refactor-tasks-phase4.md +0 -564
- package/docs/refactor-tasks-phase5.md +0 -402
- package/docs/refactor-tasks-phase6.md +0 -662
- package/docs/refactor-tasks.md +0 -1484
- package/docs/research/AGENT-EXECUTION-ARCHITECTURE.md +0 -261
- package/docs/research/AGENT-LIFECYCLE-COMPARISON.md +0 -111
- package/docs/research/AUDIT_OH_MY_PI.md +0 -261
- package/docs/research/AUDIT_PI_CREW.md +0 -457
- package/docs/research/CAVEMAN-DEEP-RESEARCH.md +0 -281
- package/docs/research/COMPARISON_OH_MY_PI_VS_PI_CREW.md +0 -264
- package/docs/research/DEEP-RESEARCH-PI-POWERBAR.md +0 -343
- package/docs/research/DEEP_RESEARCH_SUBAGENT_ARCHITECTURE.md +0 -480
- package/docs/research/GAP_CLOSURE_IMPLEMENTATION_PLAN.md +0 -354
- package/docs/research/IMPLEMENTATION_PLAN.md +0 -385
- package/docs/research/LIVE-SESSION-PRODUCTION-READY-PLAN.md +0 -502
- package/docs/research/OH-MY-PI-DEEP-RESEARCH-v14.7.6.md +0 -266
- package/docs/research/REMAINING-GAPS-PLAN.md +0 -363
- package/docs/research/SESSION-SUMMARY-2026-05-08.md +0 -146
- package/docs/research/UI-RESPONSIVENESS-AUDIT.md +0 -173
- package/docs/research-awesome-agent-skills-distillation.md +0 -100
- package/docs/research-extension-examples.md +0 -297
- package/docs/research-extension-system.md +0 -324
- package/docs/research-oh-my-pi-distillation.md +0 -369
- package/docs/research-optimization-plan.md +0 -548
- package/docs/research-phase10-distillation.md +0 -199
- package/docs/research-phase11-distillation.md +0 -201
- package/docs/research-phase8-operator-experience-plan.md +0 -819
- package/docs/research-phase9-observability-reliability-plan.md +0 -1190
- package/docs/research-pi-coding-agent.md +0 -357
- package/docs/research-source-pi-crew-reference.md +0 -174
- package/docs/research-ui-optimization-plan.md +0 -480
- package/docs/source-runtime-refactor-map.md +0 -107
- package/src/utils/atomic-write.ts +0 -33
|
@@ -1,82 +1,103 @@
|
|
|
1
|
-
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
1
|
+
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
|
|
3
|
+
// Lazy-loaded to avoid pulling team-tool.ts (and its entire runtime chain) into module load.
|
|
4
|
+
import type { handleTeamTool as HandleTeamToolFn } from "./team-tool.ts";
|
|
5
|
+
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
6
|
+
async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
|
|
7
|
+
if (!_cachedHandleTeamTool) {
|
|
8
|
+
const mod = await import("./team-tool.ts");
|
|
9
|
+
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
10
|
+
}
|
|
11
|
+
return _cachedHandleTeamTool(params, ctx);
|
|
12
|
+
}
|
|
13
|
+
import { parseLiveControlRealtimeMessage, publishLiveControlRealtime } from "../runtime/live-control-realtime.ts";
|
|
14
|
+
|
|
15
|
+
export interface EventBusLike {
|
|
16
|
+
on(event: string, handler: (data: unknown) => void): (() => void) | void;
|
|
17
|
+
emit(event: string, data: unknown): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type RpcReply<T = unknown> = { success: true; data?: T } | { success: false; error: string };
|
|
21
|
+
export const PI_CREW_RPC_VERSION = 1;
|
|
22
|
+
|
|
23
|
+
export interface PiCrewRpcHandle {
|
|
24
|
+
unsubscribe(): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function requestId(raw: unknown): string | undefined {
|
|
28
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) && typeof (raw as { requestId?: unknown }).requestId === "string" ? (raw as { requestId: string }).requestId : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function reply(events: EventBusLike, channel: string, id: string | undefined, payload: RpcReply): void {
|
|
32
|
+
if (!id) return;
|
|
33
|
+
events.emit(`${channel}:reply:${id}`, payload);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function textOf(result: Awaited<ReturnType<typeof handleTeamTool>>): string {
|
|
37
|
+
return result.content?.map((item) => item.type === "text" ? item.text : "").join("\n") ?? "";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function on(events: EventBusLike, channel: string, handler: (raw: unknown) => void): () => void {
|
|
41
|
+
const unsub = events.on(channel, handler);
|
|
42
|
+
return typeof unsub === "function" ? unsub : () => {};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function registerPiCrewRpc(events: EventBusLike | undefined, getCtx: () => ExtensionContext | undefined): PiCrewRpcHandle | undefined {
|
|
46
|
+
if (!events) return undefined;
|
|
47
|
+
const unsubs = [
|
|
48
|
+
on(events, "pi-crew:rpc:ping", (raw) => reply(events, "pi-crew:rpc:ping", requestId(raw), { success: true, data: { version: PI_CREW_RPC_VERSION } })),
|
|
49
|
+
on(events, "pi-crew:rpc:run", async (raw) => {
|
|
50
|
+
const id = requestId(raw);
|
|
51
|
+
try {
|
|
52
|
+
const ctx = getCtx();
|
|
53
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
54
|
+
// Validate payload: only allow known fields from TeamToolParamsValue
|
|
55
|
+
const ALLOWED_RPC_RUN_KEYS = new Set(["goal", "team", "workflow", "async", "cwd", "config", "skill", "model"]);
|
|
56
|
+
let params: TeamToolParamsValue;
|
|
57
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
58
|
+
const filtered: Record<string, unknown> = { ...(raw as object) };
|
|
59
|
+
// Strip any keys not in the allowlist to prevent injection of unexpected fields
|
|
60
|
+
for (const key of Object.keys(filtered)) {
|
|
61
|
+
if (!ALLOWED_RPC_RUN_KEYS.has(key)) delete filtered[key];
|
|
62
|
+
}
|
|
63
|
+
params = { ...filtered, action: "run" } as TeamToolParamsValue;
|
|
64
|
+
} else {
|
|
65
|
+
params = { action: "run" };
|
|
66
|
+
}
|
|
67
|
+
const result = await handleTeamTool(params, ctx);
|
|
68
|
+
reply(events, "pi-crew:rpc:run", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: result.details });
|
|
69
|
+
} catch (error) {
|
|
70
|
+
reply(events, "pi-crew:rpc:run", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
71
|
+
}
|
|
72
|
+
}),
|
|
73
|
+
on(events, "pi-crew:rpc:status", async (raw) => {
|
|
74
|
+
const id = requestId(raw);
|
|
75
|
+
try {
|
|
76
|
+
const ctx = getCtx();
|
|
77
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
78
|
+
const runId = raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as { runId?: string }).runId : undefined;
|
|
79
|
+
const result = await handleTeamTool({ action: "status", runId }, ctx);
|
|
80
|
+
reply(events, "pi-crew:rpc:status", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
|
|
81
|
+
} catch (error) {
|
|
82
|
+
reply(events, "pi-crew:rpc:status", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
83
|
+
}
|
|
84
|
+
}),
|
|
85
|
+
on(events, "pi-crew:live-control", (raw) => {
|
|
86
|
+
const request = parseLiveControlRealtimeMessage(raw);
|
|
87
|
+
if (request) publishLiveControlRealtime(request);
|
|
88
|
+
}),
|
|
89
|
+
on(events, "pi-crew:rpc:live-control", async (raw) => {
|
|
90
|
+
const id = requestId(raw);
|
|
91
|
+
try {
|
|
92
|
+
const ctx = getCtx();
|
|
93
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
94
|
+
const obj = raw && typeof raw === "object" && !Array.isArray(raw) ? raw as Record<string, unknown> : {};
|
|
95
|
+
const result = await handleTeamTool({ action: "api", runId: typeof obj.runId === "string" ? obj.runId : undefined, config: { operation: typeof obj.operation === "string" ? obj.operation : "steer-agent", agentId: obj.agentId, message: obj.message, prompt: obj.prompt } }, ctx);
|
|
96
|
+
reply(events, "pi-crew:rpc:live-control", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
|
|
97
|
+
} catch (error) {
|
|
98
|
+
reply(events, "pi-crew:rpc:live-control", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
99
|
+
}
|
|
100
|
+
}),
|
|
101
|
+
];
|
|
102
|
+
return { unsubscribe: () => unsubs.forEach((unsub) => unsub()) };
|
|
103
|
+
}
|
|
@@ -2,12 +2,14 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { configPath as globalConfigPath } from "../config/config.ts";
|
|
4
4
|
import { DEFAULT_UI } from "../config/defaults.ts";
|
|
5
|
+
import { injectGuidance, standardGuidanceBlocks } from "../config/markers.ts";
|
|
5
6
|
import { packageRoot, projectCrewRoot, projectPiRoot } from "../utils/paths.ts";
|
|
6
7
|
|
|
7
8
|
export interface ProjectInitOptions {
|
|
8
9
|
copyBuiltins?: boolean;
|
|
9
10
|
overwrite?: boolean;
|
|
10
11
|
configScope?: "global" | "project" | "none";
|
|
12
|
+
ignoreMethod?: "gitignore" | "exclude";
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
export interface ProjectInitResult {
|
|
@@ -20,6 +22,8 @@ export interface ProjectInitResult {
|
|
|
20
22
|
configScope: "global" | "project" | "none";
|
|
21
23
|
configCreated: boolean;
|
|
22
24
|
configSkipped: boolean;
|
|
25
|
+
guidancePath: string;
|
|
26
|
+
guidanceModified: boolean;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
function ensureDir(dir: string, createdDirs: string[]): void {
|
|
@@ -121,16 +125,44 @@ export function initializeProject(cwd: string, options: ProjectInitOptions = {})
|
|
|
121
125
|
copyBuiltinDir("workflows", workflowsDir, options.overwrite === true, copiedFiles, skippedFiles);
|
|
122
126
|
}
|
|
123
127
|
|
|
124
|
-
const
|
|
128
|
+
const ignoreMethod = options.ignoreMethod ?? "gitignore";
|
|
125
129
|
const desired = [`${ignorePrefix}/state/`, `${ignorePrefix}/artifacts/`, `${ignorePrefix}/worktrees/`, `${ignorePrefix}/imports/`];
|
|
130
|
+
const gitignorePath = ignoreMethod === "exclude"
|
|
131
|
+
? path.join(cwd, ".git", "info", "exclude")
|
|
132
|
+
: path.join(cwd, ".gitignore");
|
|
133
|
+
let gitignoreUpdated = false;
|
|
134
|
+
if (ignoreMethod === "exclude") {
|
|
135
|
+
// Ensure .git/info/ directory exists
|
|
136
|
+
const infoDir = path.dirname(gitignorePath);
|
|
137
|
+
if (!fs.existsSync(infoDir)) {
|
|
138
|
+
fs.mkdirSync(infoDir, { recursive: true });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
126
141
|
const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, "utf-8") : "";
|
|
127
142
|
const missing = desired.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
128
|
-
let gitignoreUpdated = false;
|
|
129
143
|
if (missing.length > 0) {
|
|
130
144
|
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
131
|
-
|
|
145
|
+
const comment = "# pi-crew runtime state";
|
|
146
|
+
fs.writeFileSync(gitignorePath, `${existing}${prefix}\n${comment}\n${missing.join("\n")}\n`, "utf-8");
|
|
132
147
|
gitignoreUpdated = true;
|
|
133
148
|
}
|
|
134
149
|
|
|
135
|
-
|
|
150
|
+
// Inject guidance into project AGENTS.md (or similar) using marker-based injection.
|
|
151
|
+
const guidancePath = path.join(cwd, "AGENTS.md");
|
|
152
|
+
const version = getPackageVersion();
|
|
153
|
+
const guidanceResult = injectGuidance(guidancePath, standardGuidanceBlocks(version));
|
|
154
|
+
|
|
155
|
+
return { createdDirs, copiedFiles, skippedFiles, gitignorePath, gitignoreUpdated, configPath, configScope, configCreated, configSkipped, guidancePath, guidanceModified: guidanceResult.modified };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Read the current package version from the nearest package.json. */
|
|
159
|
+
function getPackageVersion(): string {
|
|
160
|
+
try {
|
|
161
|
+
const pkgPath = path.join(packageRoot(), "package.json");
|
|
162
|
+
const raw = fs.readFileSync(pkgPath, "utf-8");
|
|
163
|
+
const parsed: { version?: string } = JSON.parse(raw) as { version?: string };
|
|
164
|
+
return parsed.version ?? "0.0.0";
|
|
165
|
+
} catch {
|
|
166
|
+
return "0.0.0";
|
|
167
|
+
}
|
|
136
168
|
}
|
|
@@ -9,7 +9,7 @@ import { notifyActiveRuns } from "./session-summary.ts";
|
|
|
9
9
|
import { LiveRunSidebar } from "../ui/live-run-sidebar.ts";
|
|
10
10
|
import { registerPiCrewRpc, type PiCrewRpcHandle } from "./cross-extension-rpc.ts";
|
|
11
11
|
import { stopCrewWidget, updateCrewWidget, type CrewWidgetState } from "../ui/crew-widget.ts";
|
|
12
|
-
import { clearPiCrewPowerbar, disposePowerbarCoalescer, registerPiCrewPowerbarSegments, requestPowerbarUpdate, updatePiCrewPowerbar } from "../ui/powerbar-publisher.ts";
|
|
12
|
+
import { clearPiCrewPowerbar, disposePowerbarCoalescer, registerPiCrewPowerbarSegments, requestPowerbarUpdate, resetPowerbarDedupState, updatePiCrewPowerbar } from "../ui/powerbar-publisher.ts";
|
|
13
13
|
import { loadRunManifestById, updateRunStatus } from "../state/state-store.ts";
|
|
14
14
|
import type { TeamRunManifest } from "../state/types.ts";
|
|
15
15
|
import { terminateActiveChildPiProcesses } from "../subagents/spawn.ts";
|
|
@@ -38,6 +38,7 @@ import { OTLPExporter } from "../observability/exporters/otlp-exporter.ts";
|
|
|
38
38
|
import { HeartbeatWatcher } from "../runtime/heartbeat-watcher.ts";
|
|
39
39
|
import { appendDeadletter } from "../runtime/deadletter.ts";
|
|
40
40
|
import { cancelOrphanedRuns, detectInterruptedRuns, purgeStaleActiveRunIndex } from "../runtime/crash-recovery.ts";
|
|
41
|
+
import { pruneFinishedRuns, pruneUserLevelRuns } from "../extension/run-maintenance.ts";
|
|
41
42
|
import { DeliveryCoordinator } from "../runtime/delivery-coordinator.ts";
|
|
42
43
|
import { OverflowRecoveryTracker } from "../runtime/overflow-recovery.ts";
|
|
43
44
|
import { tryRegisterSessionCleanup } from "../runtime/session-resources.ts";
|
|
@@ -301,9 +302,9 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
301
302
|
.finally(() => {
|
|
302
303
|
foregroundControllers.delete(key);
|
|
303
304
|
const ownerCurrent = isContextCurrent(ctx, ownerGeneration);
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
ctx.ui.setWorkingMessage();
|
|
305
|
+
if (ctx.hasUI) {
|
|
306
|
+
// Always clear working message/spinner — stale spinners for completed runs are confusing.
|
|
307
|
+
try { setWorkingIndicator(ctx); ctx.ui.setWorkingMessage(); } catch { /* ignore */ }
|
|
307
308
|
}
|
|
308
309
|
if (ownerCurrent && runId) {
|
|
309
310
|
const loaded = loadRunManifestById(ctx.cwd, runId);
|
|
@@ -344,7 +345,11 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
344
345
|
time("register.policy");
|
|
345
346
|
registerAutonomousPolicy(pi);
|
|
346
347
|
time("register.rpc");
|
|
347
|
-
|
|
348
|
+
function getPiEvents(): Parameters<typeof registerPiCrewRpc>[0] | undefined {
|
|
349
|
+
if (pi && typeof pi === "object" && "events" in pi) return (pi as unknown as Record<string, unknown>).events as Parameters<typeof registerPiCrewRpc>[0];
|
|
350
|
+
return undefined;
|
|
351
|
+
}
|
|
352
|
+
rpcHandle = registerPiCrewRpc(getPiEvents(), () => currentCtx);
|
|
348
353
|
|
|
349
354
|
const cleanupRuntime = (): void => {
|
|
350
355
|
if (cleanedUp) return;
|
|
@@ -410,34 +415,64 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
410
415
|
notifyActiveRuns(ctx);
|
|
411
416
|
|
|
412
417
|
// Auto-cancel orphaned runs from dead sessions
|
|
413
|
-
const currentSessionId = (ctx
|
|
414
|
-
|
|
418
|
+
const currentSessionId = (typeof ctx === "object" && ctx !== null && "sessionId" in ctx ? (ctx as Record<string, unknown>).sessionId : undefined) as string | undefined;
|
|
419
|
+
|
|
420
|
+
// Defer ALL heavy cleanup to after the session_start handler returns.
|
|
421
|
+
// These operations involve synchronous directory scanning (readdirSync, readFileSync)
|
|
422
|
+
// which can take 100ms–1s+ on Windows. They MUST NOT block the session_start event.
|
|
423
|
+
setTimeout(() => {
|
|
424
|
+
if (cleanedUp || sessionGeneration !== ownerGeneration) return; // session switched while we waited
|
|
425
|
+
|
|
426
|
+
// Auto-cancel orphaned runs
|
|
427
|
+
if (currentSessionId) {
|
|
428
|
+
try {
|
|
429
|
+
const { cancelled } = cancelOrphanedRuns(ctx.cwd, getManifestCache(ctx.cwd), currentSessionId);
|
|
430
|
+
if (cancelled.length > 0) {
|
|
431
|
+
notifyOperator({ id: `orphan_cleanup`, severity: "info", source: "crash-recovery", title: `Cleaned up ${cancelled.length} orphaned run(s)`, body: `Runs from previous sessions were auto-cancelled: ${cancelled.join(", ")}` });
|
|
432
|
+
}
|
|
433
|
+
} catch (error) {
|
|
434
|
+
logInternalError("register.sessionStart.orphanCleanup", error);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Global purge of stale active-run-index entries
|
|
415
439
|
try {
|
|
416
|
-
const {
|
|
417
|
-
if (
|
|
418
|
-
notifyOperator({ id: `
|
|
440
|
+
const { purged } = purgeStaleActiveRunIndex();
|
|
441
|
+
if (purged.length > 0) {
|
|
442
|
+
notifyOperator({ id: `active_index_purge`, severity: "info", source: "crash-recovery", title: `Purged ${purged.length} stale active-run-index entr${purged.length === 1 ? "y" : "ies"}`, body: `Cleaned up global active run index` });
|
|
419
443
|
}
|
|
420
444
|
} catch (error) {
|
|
421
|
-
logInternalError("register.sessionStart.
|
|
445
|
+
logInternalError("register.sessionStart.globalIndexPurge", error);
|
|
422
446
|
}
|
|
423
|
-
}
|
|
424
447
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
448
|
+
// Auto-prune finished project-level run directories (keep 10 most recent)
|
|
449
|
+
try {
|
|
450
|
+
const { removed } = pruneFinishedRuns(ctx.cwd, 10);
|
|
451
|
+
if (removed.length > 0) {
|
|
452
|
+
notifyOperator({ id: `auto_prune_project`, severity: "info", source: "run-maintenance", title: `Auto-pruned ${removed.length} finished project run(s)`, body: `Removed old finished runs: ${removed.join(", ")}` });
|
|
453
|
+
}
|
|
454
|
+
} catch (error) {
|
|
455
|
+
logInternalError("register.sessionStart.autoPruneProject", error);
|
|
430
456
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
457
|
+
|
|
458
|
+
// Auto-prune finished user-level run directories (keep 10 most recent)
|
|
459
|
+
try {
|
|
460
|
+
const { removed } = pruneUserLevelRuns(10);
|
|
461
|
+
if (removed.length > 0) {
|
|
462
|
+
notifyOperator({ id: `auto_prune_user`, severity: "info", source: "run-maintenance", title: `Auto-pruned ${removed.length} finished user-level run(s)`, body: `Removed old finished runs: ${removed.join(", ")}` });
|
|
463
|
+
}
|
|
464
|
+
} catch (error) {
|
|
465
|
+
logInternalError("register.sessionStart.autoPruneUser", error);
|
|
466
|
+
}
|
|
467
|
+
}, 0);
|
|
468
|
+
|
|
434
469
|
|
|
435
470
|
const loadedConfig = loadConfig(ctx.cwd);
|
|
436
471
|
autoRecoveryLast.clear();
|
|
437
472
|
configureNotifications(ctx);
|
|
438
473
|
configureObservability(ctx);
|
|
439
474
|
configureDeliveryCoordinator();
|
|
440
|
-
const sessionId = ctx.sessionManager?.getSessionId?.() ?? (ctx
|
|
475
|
+
const sessionId = ctx.sessionManager?.getSessionId?.() ?? (typeof ctx === "object" && ctx !== null && "sessionId" in ctx ? (ctx as Record<string, unknown>).sessionId : undefined);
|
|
441
476
|
if (typeof sessionId === "string" && sessionId) deliveryCoordinator?.activate(sessionId);
|
|
442
477
|
tryRegisterSessionCleanup(pi, () => { terminateActiveChildPiProcesses(); cleanupRuntime(); });
|
|
443
478
|
registerPiCrewPowerbarSegments(pi.events, loadedConfig.config.ui);
|
|
@@ -542,7 +577,16 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
542
577
|
const fallbackMs = loadedConfig.config.ui?.dashboardLiveRefreshMs ?? DEFAULT_UI.refreshMs;
|
|
543
578
|
renderScheduler = new RenderScheduler(pi.events, renderTick, {
|
|
544
579
|
fallbackMs,
|
|
545
|
-
onInvalidate: () =>
|
|
580
|
+
onInvalidate: (payload: unknown) => {
|
|
581
|
+
// Invalidate only the specific run, not the entire cache.
|
|
582
|
+
// Full cache.clear() causes widget flicker — the widget component's
|
|
583
|
+
// render() may run before renderTick rebuilds the preloaded frame,
|
|
584
|
+
// seeing an empty cache and returning no agents.
|
|
585
|
+
const runId = typeof payload === "object" && payload !== null && "runId" in payload && typeof (payload as { runId: unknown }).runId === "string"
|
|
586
|
+
? (payload as { runId: string }).runId
|
|
587
|
+
: undefined;
|
|
588
|
+
getRunSnapshotCache(ctx.cwd).invalidate(runId);
|
|
589
|
+
},
|
|
546
590
|
});
|
|
547
591
|
// Start async preload loop — refreshes snapshot cache in background
|
|
548
592
|
startPreloadLoop(fallbackMs);
|
|
@@ -561,6 +605,7 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
561
605
|
logInternalError("register.session-before-switch", `Switching session with ${pendingCount} pending deliveries`);
|
|
562
606
|
}
|
|
563
607
|
deliveryCoordinator?.deactivate();
|
|
608
|
+
resetPowerbarDedupState();
|
|
564
609
|
stopAsyncRunNotifier(notifierState);
|
|
565
610
|
stopSessionBoundSubagents();
|
|
566
611
|
});
|
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { loadConfig } from "../../config/config.ts";
|
|
3
|
-
|
|
3
|
+
// Lazy-loaded: team-tool.ts pulls in entire runtime chain (1.4s+).
|
|
4
|
+
import type { handleTeamTool as HandleTeamToolFn } from "../team-tool.ts";
|
|
5
|
+
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
6
|
+
let _handleTeamToolPromise: Promise<typeof HandleTeamToolFn> | undefined;
|
|
7
|
+
async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
|
|
8
|
+
if (!_cachedHandleTeamTool) {
|
|
9
|
+
if (!_handleTeamToolPromise) {
|
|
10
|
+
_handleTeamToolPromise = import("../team-tool.ts").then((mod) => {
|
|
11
|
+
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
12
|
+
return mod.handleTeamTool;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const fn = await _handleTeamToolPromise;
|
|
16
|
+
return fn(params, ctx);
|
|
17
|
+
}
|
|
18
|
+
return _cachedHandleTeamTool(params, ctx);
|
|
19
|
+
}
|
|
4
20
|
import { withSessionId } from "../team-tool/context.ts";
|
|
5
21
|
import { piTeamsHelp } from "../help.ts";
|
|
6
22
|
import { handleTeamManagerCommand } from "../team-manager-command.ts";
|
|
7
23
|
import { loadRunManifestById } from "../../state/state-store.ts";
|
|
8
24
|
import type { TeamRunManifest } from "../../state/types.ts";
|
|
9
25
|
import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
|
|
10
|
-
import { AnimatedMascot } from "../../ui/mascot.ts";
|
|
11
26
|
import * as path from "node:path";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
27
|
+
// Heavy UI modules — lazy-loaded because they're only used in /crew commands.
|
|
28
|
+
// RunDashboard (288ms), DurableTextViewer (658ms), Overlays are unnecessary at Pi startup.
|
|
29
|
+
import type { RunDashboard as RunDashboardType, RunDashboardSelection } from "../../ui/run-dashboard.ts";
|
|
30
|
+
import type { DurableTextViewer as DurableTextViewerType } from "../../ui/transcript-viewer.ts";
|
|
31
|
+
import type { ConfirmOverlay as ConfirmOverlayType, ConfirmOptions } from "../../ui/overlays/confirm-overlay.ts";
|
|
32
|
+
import type { MailboxDetailOverlay as MailboxDetailOverlayType, MailboxAction } from "../../ui/overlays/mailbox-detail-overlay.ts";
|
|
33
|
+
import type { MailboxComposeOverlay as MailboxComposeOverlayType, MailboxComposeResult } from "../../ui/overlays/mailbox-compose-overlay.ts";
|
|
34
|
+
import type { AgentPickerOverlay as AgentPickerOverlayType } from "../../ui/overlays/agent-picker-overlay.ts";
|
|
35
|
+
import type { AnimatedMascot as AnimatedMascotType } from "../../ui/mascot.ts";
|
|
36
|
+
// Eagerly import lightweight modules
|
|
18
37
|
import { dispatchDiagnosticExport, dispatchHealthRecovery, dispatchKillStaleWorkers, dispatchMailboxAck, dispatchMailboxAckAll, dispatchMailboxCompose, dispatchMailboxNudge } from "../../ui/run-action-dispatcher.ts";
|
|
19
38
|
import { DEFAULT_UI } from "../../config/defaults.ts";
|
|
20
39
|
import { listRecentDiagnostic } from "../../runtime/diagnostic-export.ts";
|
|
@@ -35,13 +54,58 @@ export interface RegisterTeamCommandsDeps {
|
|
|
35
54
|
dismissNotifications?: () => void;
|
|
36
55
|
}
|
|
37
56
|
|
|
57
|
+
// Lazy-loaded UI module cache — avoids importing 900ms+ of UI at Pi startup.
|
|
58
|
+
// These modules are only needed when user invokes /crew commands.
|
|
59
|
+
let _uiCache: {
|
|
60
|
+
RunDashboard: typeof RunDashboardType;
|
|
61
|
+
DurableTextViewer: typeof DurableTextViewerType;
|
|
62
|
+
ConfirmOverlay: typeof ConfirmOverlayType;
|
|
63
|
+
MailboxDetailOverlay: typeof MailboxDetailOverlayType;
|
|
64
|
+
MailboxComposeOverlay: typeof MailboxComposeOverlayType;
|
|
65
|
+
AgentPickerOverlay: typeof AgentPickerOverlayType;
|
|
66
|
+
AnimatedMascot: typeof AnimatedMascotType;
|
|
67
|
+
} | undefined;
|
|
68
|
+
let _uiCachePromise: Promise<NonNullable<typeof _uiCache>> | undefined;
|
|
69
|
+
async function ui(): Promise<NonNullable<typeof _uiCache>> {
|
|
70
|
+
if (!_uiCache) {
|
|
71
|
+
if (!_uiCachePromise) {
|
|
72
|
+
_uiCachePromise = (async () => {
|
|
73
|
+
const [rd, tv, co, md, mc, ap, ma] = await Promise.all([
|
|
74
|
+
import("../../ui/run-dashboard.ts"),
|
|
75
|
+
import("../../ui/transcript-viewer.ts"),
|
|
76
|
+
import("../../ui/overlays/confirm-overlay.ts"),
|
|
77
|
+
import("../../ui/overlays/mailbox-detail-overlay.ts"),
|
|
78
|
+
import("../../ui/overlays/mailbox-compose-overlay.ts"),
|
|
79
|
+
import("../../ui/overlays/agent-picker-overlay.ts"),
|
|
80
|
+
import("../../ui/mascot.ts"),
|
|
81
|
+
]);
|
|
82
|
+
const cache = {
|
|
83
|
+
RunDashboard: rd.RunDashboard,
|
|
84
|
+
DurableTextViewer: tv.DurableTextViewer,
|
|
85
|
+
ConfirmOverlay: co.ConfirmOverlay,
|
|
86
|
+
MailboxDetailOverlay: md.MailboxDetailOverlay,
|
|
87
|
+
MailboxComposeOverlay: mc.MailboxComposeOverlay,
|
|
88
|
+
AgentPickerOverlay: ap.AgentPickerOverlay,
|
|
89
|
+
AnimatedMascot: ma.AnimatedMascot,
|
|
90
|
+
};
|
|
91
|
+
_uiCache = cache;
|
|
92
|
+
return cache;
|
|
93
|
+
})();
|
|
94
|
+
}
|
|
95
|
+
return _uiCachePromise;
|
|
96
|
+
}
|
|
97
|
+
return _uiCache;
|
|
98
|
+
}
|
|
99
|
+
|
|
38
100
|
async function openConfirm(ctx: ExtensionCommandContext, options: ConfirmOptions): Promise<boolean> {
|
|
39
101
|
if (!ctx.hasUI) return false;
|
|
102
|
+
const { ConfirmOverlay } = await ui();
|
|
40
103
|
return await ctx.ui.custom<boolean>((_tui, theme, _keybindings, done) => new ConfirmOverlay(options, done, theme), { overlay: true, overlayOptions: { width: 64, maxHeight: "70%", anchor: "center" } });
|
|
41
104
|
}
|
|
42
105
|
|
|
43
106
|
async function handleMailboxDashboardAction(ctx: ExtensionCommandContext, runId: string): Promise<void> {
|
|
44
107
|
if (!ctx.hasUI) return;
|
|
108
|
+
const { MailboxDetailOverlay } = await ui();
|
|
45
109
|
const action = await ctx.ui.custom<MailboxAction | undefined>((_tui, theme, _keybindings, done) => new MailboxDetailOverlay({ runId, cwd: ctx.cwd, done, theme }), { overlay: true, overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" } });
|
|
46
110
|
if (!action || action.type === "close") return;
|
|
47
111
|
let resultMessage: string | undefined;
|
|
@@ -57,6 +121,7 @@ async function handleMailboxDashboardAction(ctx: ExtensionCommandContext, runId:
|
|
|
57
121
|
ok = result.ok;
|
|
58
122
|
resultMessage = result.message;
|
|
59
123
|
} else if (action.type === "compose") {
|
|
124
|
+
const { MailboxComposeOverlay } = await ui();
|
|
60
125
|
const compose = await ctx.ui.custom<MailboxComposeResult>((_tui, theme, _keybindings, done) => new MailboxComposeOverlay({ done, theme }), { overlay: true, overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" } });
|
|
61
126
|
if (compose.type === "cancel") return;
|
|
62
127
|
const result = await dispatchMailboxCompose(ctx as ExtensionContext, runId, compose.payload);
|
|
@@ -65,6 +130,7 @@ async function handleMailboxDashboardAction(ctx: ExtensionCommandContext, runId:
|
|
|
65
130
|
} else if (action.type === "nudge") {
|
|
66
131
|
let agentId = action.agentId;
|
|
67
132
|
if (!agentId) {
|
|
133
|
+
const { AgentPickerOverlay } = await ui();
|
|
68
134
|
const picked = await ctx.ui.custom<{ agentId: string } | undefined>((_tui, theme, _keybindings, done) => new AgentPickerOverlay({ cwd: ctx.cwd, runId, done, theme }), { overlay: true, overlayOptions: { width: 72, maxHeight: "75%", anchor: "center" } });
|
|
69
135
|
agentId = picked?.agentId;
|
|
70
136
|
}
|
|
@@ -272,6 +338,7 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
|
|
|
272
338
|
if (ctx.hasUI && loaded) {
|
|
273
339
|
const agent = readCrewAgents(loaded.manifest).find((item) => item.taskId === selected?.taskId || item.id === selected?.taskId) ?? readCrewAgents(loaded.manifest)[0];
|
|
274
340
|
const resultText = agent?.resultArtifactPath ? commandText(await handleTeamTool({ action: "api", runId: selected?.runId ?? "", config: { operation: "read-agent-output", agentId: agent.taskId, maxBytes: 64_000 } }, teamCommandContext(ctx))) : "(no result)";
|
|
341
|
+
const { DurableTextViewer } = await ui();
|
|
275
342
|
await ctx.ui.custom<undefined>((_tui, theme, _keybindings, done) => new DurableTextViewer("pi-crew result", `${selected?.runId ?? ""}:${agent?.taskId ?? "unknown"}`, resultText.split(/\r?\n/), theme, done), { overlay: true, overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" } });
|
|
276
343
|
return;
|
|
277
344
|
}
|
|
@@ -292,6 +359,7 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
|
|
|
292
359
|
const uiConfig = loadConfig(ctx.cwd).config.ui;
|
|
293
360
|
const rightPanel = (uiConfig?.dashboardPlacement ?? DEFAULT_UI.dashboardPlacement) === "right";
|
|
294
361
|
const width = rightPanel ? Math.min(90, Math.max(40, uiConfig?.dashboardWidth ?? DEFAULT_UI.dashboardWidth)) : "90%";
|
|
362
|
+
const { RunDashboard } = await ui();
|
|
295
363
|
const selection = await ctx.ui.custom<RunDashboardSelection | undefined>((_tui, theme, _keybindings, done) => new RunDashboard(runs, done, theme, { placement: rightPanel ? "right" : "center", showModel: uiConfig?.showModel, showTokens: uiConfig?.showTokens, showTools: uiConfig?.showTools, snapshotCache: deps.getRunSnapshotCache?.(ctx.cwd), runProvider: () => deps.getManifestCache(ctx.cwd).list(50), registry: deps.getMetricRegistry?.() }), { overlay: true, overlayOptions: rightPanel ? { width, minWidth: 40, maxHeight: "100%", anchor: "top-right", offsetX: 0, offsetY: 0, margin: { top: 0, right: 0, bottom: 0, left: 0 } } : { width, maxHeight: "90%", anchor: "center", margin: 2 } });
|
|
296
364
|
if (!selection) return;
|
|
297
365
|
if (selection.action === "reload") continue;
|
|
@@ -325,6 +393,7 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
|
|
|
325
393
|
const effectArg = tokens.find((t) => ["random", "none", "typewriter", "scanline", "rain", "fade", "crt", "glitch", "dissolve"].includes(t));
|
|
326
394
|
const style = (styleArg as "cat" | "armin" | undefined) ?? uiConfig?.mascotStyle ?? DEFAULT_UI.mascotStyle;
|
|
327
395
|
const effect = (effectArg as "random" | "none" | "typewriter" | "scanline" | "rain" | "fade" | "crt" | "glitch" | "dissolve" | undefined) ?? uiConfig?.mascotEffect ?? DEFAULT_UI.mascotEffect;
|
|
396
|
+
const { AnimatedMascot } = await ui();
|
|
328
397
|
await ctx.ui.custom<undefined>((tui, theme, _keybindings, done) => new AnimatedMascot(theme, () => done(undefined), { frameIntervalMs: style === "armin" ? 33 : 180, autoCloseMs: 7000, requestRender: () => requestRenderTarget(tui), style, effect }), { overlay: true, overlayOptions: { width: style === "armin" ? 48 : 62, maxHeight: "85%", anchor: "center" } });
|
|
329
398
|
} });
|
|
330
399
|
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import type { ExtensionAPI, ToolDefinition } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
|
|
4
|
-
|
|
4
|
+
// Lazy-loaded: team-tool.ts pulls in entire runtime chain.
|
|
5
|
+
import type { handleTeamTool as HandleTeamToolFn } from "../team-tool.ts";
|
|
6
|
+
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
7
|
+
async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
|
|
8
|
+
if (!_cachedHandleTeamTool) {
|
|
9
|
+
const mod = await import("../team-tool.ts");
|
|
10
|
+
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
11
|
+
}
|
|
12
|
+
return _cachedHandleTeamTool(params, ctx);
|
|
13
|
+
}
|
|
5
14
|
import { checkSubagentSpawnPermission, currentCrewRole } from "../../runtime/role-permission.ts";
|
|
6
15
|
import { readPersistedSubagentRecord, savePersistedSubagentRecord, type SubagentManager, type SubagentSpawnOptions } from "../../subagents/manager.ts";
|
|
7
16
|
import { loadConfig } from "../../config/config.ts";
|
|
@@ -9,7 +9,16 @@ import type { createManifestCache } from "../../runtime/manifest-cache.ts";
|
|
|
9
9
|
import type { createRunSnapshotCache } from "../../ui/run-snapshot-cache.ts";
|
|
10
10
|
import type { MetricRegistry } from "../../observability/metric-registry.ts";
|
|
11
11
|
import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
12
|
-
|
|
12
|
+
// Team tool handler — lazy-loaded because team-tool.ts imports many modules
|
|
13
|
+
import type { handleTeamTool as HandleTeamToolFn } from "../team-tool.ts";
|
|
14
|
+
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
15
|
+
async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<ReturnType<typeof HandleTeamToolFn>> {
|
|
16
|
+
if (!_cachedHandleTeamTool) {
|
|
17
|
+
const mod = await import("../team-tool.ts");
|
|
18
|
+
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
19
|
+
}
|
|
20
|
+
return _cachedHandleTeamTool(params, ctx);
|
|
21
|
+
}
|
|
13
22
|
import { withSessionId } from "../team-tool/context.ts";
|
|
14
23
|
import { toolResult } from "../tool-result.ts";
|
|
15
24
|
|