pi-crew 0.1.51 → 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 +56 -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 +13 -1
- 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 -94
- package/src/extension/project-init.ts +21 -1
- package/src/extension/register.ts +45 -14
- 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-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/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 +11 -1
- 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/crash-recovery.ts +33 -0
- package/src/runtime/delta-conflict.ts +360 -0
- package/src/runtime/direct-run.ts +35 -35
- 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 -11
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/pi-spawn.ts +57 -7
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/post-checks.ts +122 -0
- 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 +2 -2
- 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-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 +117 -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 +11 -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 -13
- 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 +5 -2
- 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/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/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 -43
- 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,94 +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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
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,6 +2,7 @@ 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 {
|
|
@@ -21,6 +22,8 @@ export interface ProjectInitResult {
|
|
|
21
22
|
configScope: "global" | "project" | "none";
|
|
22
23
|
configCreated: boolean;
|
|
23
24
|
configSkipped: boolean;
|
|
25
|
+
guidancePath: string;
|
|
26
|
+
guidanceModified: boolean;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
function ensureDir(dir: string, createdDirs: string[]): void {
|
|
@@ -144,5 +147,22 @@ export function initializeProject(cwd: string, options: ProjectInitOptions = {})
|
|
|
144
147
|
gitignoreUpdated = true;
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
|
|
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
|
+
}
|
|
148
168
|
}
|
|
@@ -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";
|
|
@@ -415,26 +416,56 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
415
416
|
|
|
416
417
|
// Auto-cancel orphaned runs from dead sessions
|
|
417
418
|
const currentSessionId = (typeof ctx === "object" && ctx !== null && "sessionId" in ctx ? (ctx as Record<string, unknown>).sessionId : undefined) as string | undefined;
|
|
418
|
-
|
|
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
|
|
419
439
|
try {
|
|
420
|
-
const {
|
|
421
|
-
if (
|
|
422
|
-
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` });
|
|
423
443
|
}
|
|
424
444
|
} catch (error) {
|
|
425
|
-
logInternalError("register.sessionStart.
|
|
445
|
+
logInternalError("register.sessionStart.globalIndexPurge", error);
|
|
426
446
|
}
|
|
427
|
-
}
|
|
428
447
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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);
|
|
434
456
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
+
|
|
438
469
|
|
|
439
470
|
const loadedConfig = loadConfig(ctx.cwd);
|
|
440
471
|
autoRecoveryLast.clear();
|
|
@@ -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
|
|
|
@@ -1,34 +1,48 @@
|
|
|
1
|
-
import type { ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import { loadRunManifestById } from "../../state/state-store.ts";
|
|
3
|
-
import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
|
|
4
|
-
import { loadConfig } from "../../config/config.ts";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (!
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
import type { ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { loadRunManifestById } from "../../state/state-store.ts";
|
|
3
|
+
import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
|
|
4
|
+
import { loadConfig } from "../../config/config.ts";
|
|
5
|
+
// Lazy-loaded: DurableTranscriptViewer is 658ms — only needed for /crew transcript command
|
|
6
|
+
import type { DurableTranscriptViewer as DurableTranscriptViewerType } from "../../ui/transcript-viewer.ts";
|
|
7
|
+
let _cachedViewer: typeof DurableTranscriptViewerType | undefined;
|
|
8
|
+
let _viewerPromise: Promise<typeof DurableTranscriptViewerType> | undefined;
|
|
9
|
+
async function getViewer(): Promise<typeof DurableTranscriptViewerType> {
|
|
10
|
+
if (_cachedViewer) return _cachedViewer;
|
|
11
|
+
if (!_viewerPromise) {
|
|
12
|
+
_viewerPromise = import("../../ui/transcript-viewer.ts").then((mod) => {
|
|
13
|
+
_cachedViewer = mod.DurableTranscriptViewer;
|
|
14
|
+
return mod.DurableTranscriptViewer;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return _viewerPromise;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function selectAgentTask(ctx: ExtensionCommandContext, runId: string | undefined, taskId?: string): Promise<{ runId: string; taskId?: string } | undefined> {
|
|
21
|
+
if (!runId) return undefined;
|
|
22
|
+
if (taskId) return { runId, taskId };
|
|
23
|
+
const loaded = loadRunManifestById(ctx.cwd, runId);
|
|
24
|
+
if (!loaded) return { runId };
|
|
25
|
+
const agents = readCrewAgents(loaded.manifest);
|
|
26
|
+
if (ctx.hasUI && agents.length > 1) {
|
|
27
|
+
const choice = await ctx.ui.select("Select pi-crew agent", agents.map((agent) => `${agent.taskId} ${agent.role}→${agent.agent} [${agent.status}]`));
|
|
28
|
+
return { runId, taskId: choice?.split(" ")[0] };
|
|
29
|
+
}
|
|
30
|
+
return { runId, taskId: agents[0]?.taskId };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function openTranscriptViewer(ctx: ExtensionCommandContext, initialRunId: string | undefined, initialTaskId?: string): Promise<boolean> {
|
|
34
|
+
const selected = await selectAgentTask(ctx, initialRunId, initialTaskId);
|
|
35
|
+
if (!selected) return false;
|
|
36
|
+
const runId = selected.runId;
|
|
37
|
+
const taskId = selected.taskId;
|
|
38
|
+
if (!runId || !ctx.hasUI) return false;
|
|
39
|
+
const loaded = loadRunManifestById(ctx.cwd, runId);
|
|
40
|
+
if (!loaded) return false;
|
|
41
|
+
const uiConfig = loadConfig(ctx.cwd).config.ui;
|
|
42
|
+
const DurableTranscriptViewer = await getViewer();
|
|
43
|
+
await ctx.ui.custom<undefined>((_tui, theme, _keybindings, done) => new DurableTranscriptViewer(loaded.manifest, theme, done, taskId, { maxTailBytes: uiConfig?.transcriptTailBytes }), {
|
|
44
|
+
overlay: true,
|
|
45
|
+
overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" },
|
|
46
|
+
});
|
|
47
|
+
return true;
|
|
48
|
+
}
|