pi-crew 0.9.53 → 0.9.55
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 +45 -0
- package/package.json +6 -3
- package/scripts/clean-strip-types.mjs +60 -0
- package/src/adapters/claude-adapter.ts +23 -0
- package/src/adapters/codex-adapter.ts +21 -0
- package/src/adapters/cursor-adapter.ts +17 -0
- package/src/adapters/export-util.ts +143 -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 +194 -0
- package/src/agents/agent-search.ts +98 -0
- package/src/agents/agent-serializer.ts +38 -0
- package/src/agents/discover-agents.ts +623 -0
- package/src/benchmark/benchmark-runner.ts +313 -0
- package/src/benchmark/feedback-loop.ts +73 -0
- package/src/config/config.ts +1270 -0
- package/src/config/defaults.ts +193 -0
- package/src/config/drift-detector.ts +290 -0
- package/src/config/markers.ts +330 -0
- package/src/config/resilient-parser.ts +117 -0
- package/src/config/role-tools.ts +118 -0
- package/src/config/suggestions.ts +75 -0
- package/src/config/types.ts +280 -0
- package/src/errors.ts +194 -0
- package/src/extension/action-suggestions.ts +117 -0
- package/src/extension/async-notifier.ts +179 -0
- package/src/extension/autonomous-policy.ts +209 -0
- package/src/extension/command-completions.ts +120 -0
- package/src/extension/context-status-injection.ts +185 -0
- package/src/extension/crew-autocomplete.ts +131 -0
- package/src/extension/crew-cleanup.ts +168 -0
- package/src/extension/crew-input-router.ts +93 -0
- package/src/extension/crew-shortcuts.ts +87 -0
- package/src/extension/crew-vibes/cat-frames.ts +18 -0
- package/src/extension/crew-vibes/config.ts +195 -0
- package/src/extension/crew-vibes/figures.ts +101 -0
- package/src/extension/crew-vibes/font-detect.ts +71 -0
- package/src/extension/crew-vibes/footer.ts +292 -0
- package/src/extension/crew-vibes/index.ts +432 -0
- package/src/extension/crew-vibes/provider-usage.ts +396 -0
- package/src/extension/crew-vibes/render.ts +206 -0
- package/src/extension/crew-vibes/speed.ts +286 -0
- package/src/extension/cross-extension-rpc.ts +350 -0
- package/src/extension/help.ts +62 -0
- package/src/extension/import-index.ts +74 -0
- package/src/extension/knowledge-injection.ts +402 -0
- package/src/extension/management.ts +571 -0
- package/src/extension/message-renderers.ts +108 -0
- package/src/extension/notification-router.ts +152 -0
- package/src/extension/notification-sink.ts +54 -0
- package/src/extension/pi-api.ts +56 -0
- package/src/extension/plan-orchestrate.ts +302 -0
- package/src/extension/project-init.ts +170 -0
- package/src/extension/register.ts +113 -0
- package/src/extension/registration/artifact-cleanup.ts +20 -0
- package/src/extension/registration/command-registration.ts +58 -0
- package/src/extension/registration/command-utils.ts +58 -0
- package/src/extension/registration/commands.ts +1228 -0
- package/src/extension/registration/compaction-guard.ts +333 -0
- package/src/extension/registration/context-builder.ts +135 -0
- package/src/extension/registration/crash-recovery-cache.ts +54 -0
- package/src/extension/registration/foreground-run-controller.ts +238 -0
- package/src/extension/registration/hook-registration.ts +119 -0
- package/src/extension/registration/lazy-configurers.ts +124 -0
- package/src/extension/registration/lifecycle-handlers.ts +923 -0
- package/src/extension/registration/lifecycle.ts +259 -0
- package/src/extension/registration/observability.ts +322 -0
- package/src/extension/registration/registration-types.ts +158 -0
- package/src/extension/registration/runtime-cleanup.ts +230 -0
- package/src/extension/registration/subagent-helpers.ts +125 -0
- package/src/extension/registration/subagent-manager-setup.ts +330 -0
- package/src/extension/registration/subagent-tools.ts +456 -0
- package/src/extension/registration/team-tool.ts +225 -0
- package/src/extension/registration/tool-registration.ts +50 -0
- package/src/extension/registration/ui.ts +172 -0
- package/src/extension/registration/viewers.ts +121 -0
- package/src/extension/registration/wire-cross-extension.ts +37 -0
- package/src/extension/result-watcher.ts +139 -0
- package/src/extension/rpc-hmac.ts +256 -0
- package/src/extension/run-bundle-schema.ts +104 -0
- package/src/extension/run-export.ts +97 -0
- package/src/extension/run-import.ts +168 -0
- package/src/extension/run-index.ts +131 -0
- package/src/extension/run-maintenance.ts +190 -0
- package/src/extension/session-summary.ts +18 -0
- package/src/extension/team-manager-command.ts +153 -0
- package/src/extension/team-onboard.ts +180 -0
- package/src/extension/team-recommendation.ts +288 -0
- package/src/extension/team-tool/anchor.ts +172 -0
- package/src/extension/team-tool/api.ts +1244 -0
- package/src/extension/team-tool/auto-summarize.ts +142 -0
- package/src/extension/team-tool/cache-control.ts +19 -0
- package/src/extension/team-tool/cancel.ts +337 -0
- package/src/extension/team-tool/chain-dispatch.ts +95 -0
- package/src/extension/team-tool/chain-executor.ts +379 -0
- package/src/extension/team-tool/config-patch.ts +50 -0
- package/src/extension/team-tool/context.ts +149 -0
- package/src/extension/team-tool/destructive-gate.ts +52 -0
- package/src/extension/team-tool/dispatch/automate.ts +80 -0
- package/src/extension/team-tool/dispatch/control.ts +42 -0
- package/src/extension/team-tool/dispatch/index.ts +95 -0
- package/src/extension/team-tool/dispatch/manage.ts +161 -0
- package/src/extension/team-tool/dispatch/run.ts +53 -0
- package/src/extension/team-tool/dispatch/status.ts +187 -0
- package/src/extension/team-tool/doctor.ts +431 -0
- package/src/extension/team-tool/explain.ts +282 -0
- package/src/extension/team-tool/failure-patterns.ts +118 -0
- package/src/extension/team-tool/goal-wrap.ts +330 -0
- package/src/extension/team-tool/goal.ts +556 -0
- package/src/extension/team-tool/handle-schedule.ts +332 -0
- package/src/extension/team-tool/handle-settings.ts +520 -0
- package/src/extension/team-tool/health-monitor.ts +481 -0
- package/src/extension/team-tool/inspect.ts +88 -0
- package/src/extension/team-tool/intent-policy.ts +45 -0
- package/src/extension/team-tool/lifecycle-actions.ts +615 -0
- package/src/extension/team-tool/orchestrate.ts +98 -0
- package/src/extension/team-tool/parallel-dispatch.ts +183 -0
- package/src/extension/team-tool/plan.ts +50 -0
- package/src/extension/team-tool/respond.ts +150 -0
- package/src/extension/team-tool/run-deadline.ts +66 -0
- package/src/extension/team-tool/run-not-found.ts +49 -0
- package/src/extension/team-tool/run.ts +1051 -0
- package/src/extension/team-tool/status.ts +260 -0
- package/src/extension/team-tool/workflow-manage.ts +261 -0
- package/src/extension/team-tool-types.ts +26 -0
- package/src/extension/team-tool.ts +750 -0
- package/src/extension/tool-result.ts +16 -0
- package/src/extension/validate-resources.ts +108 -0
- package/src/hooks/registry.ts +200 -0
- package/src/hooks/types.ts +69 -0
- package/src/i18n.ts +212 -0
- package/src/observability/correlation.ts +50 -0
- package/src/observability/event-bus.ts +86 -0
- package/src/observability/event-to-metric.ts +170 -0
- package/src/observability/exporters/adapter.ts +27 -0
- package/src/observability/exporters/otlp-exporter.ts +356 -0
- package/src/observability/exporters/prometheus-exporter.ts +54 -0
- package/src/observability/metric-registry.ts +100 -0
- package/src/observability/metric-retention.ts +64 -0
- package/src/observability/metric-sink.ts +99 -0
- package/src/observability/metrics-primitives.ts +219 -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 +10 -0
- package/src/plugins/plugins/vitest.ts +9 -0
- package/src/prompt/prompt-runtime.ts +380 -0
- package/src/runtime/adaptive-plan.ts +563 -0
- package/src/runtime/agent-control.ts +215 -0
- package/src/runtime/agent-memory.ts +81 -0
- package/src/runtime/agent-observability.ts +122 -0
- package/src/runtime/anchor-manager.ts +475 -0
- package/src/runtime/async-marker.ts +32 -0
- package/src/runtime/async-runner.ts +381 -0
- package/src/runtime/attention-events.ts +28 -0
- package/src/runtime/auto-summarize.ts +344 -0
- package/src/runtime/background-runner.ts +840 -0
- package/src/runtime/batch-barrier.ts +147 -0
- package/src/runtime/broker-issuer.ts +39 -0
- package/src/runtime/cancellation-token.ts +99 -0
- package/src/runtime/cancellation.ts +99 -0
- package/src/runtime/capability-inventory.ts +137 -0
- package/src/runtime/chain-parser.ts +237 -0
- package/src/runtime/chain-runner.ts +606 -0
- package/src/runtime/checkpoint.ts +291 -0
- package/src/runtime/child-pi-constants.ts +42 -0
- package/src/runtime/child-pi-kill.ts +180 -0
- package/src/runtime/child-pi-pool.ts +68 -0
- package/src/runtime/child-pi-spawn.ts +287 -0
- package/src/runtime/child-pi-steering.ts +128 -0
- package/src/runtime/child-pi-streams.ts +296 -0
- package/src/runtime/child-pi-transcript.ts +169 -0
- package/src/runtime/child-pi.ts +1105 -0
- package/src/runtime/coalesce-tasks.ts +268 -0
- package/src/runtime/code-summary.ts +292 -0
- package/src/runtime/command-trace.ts +105 -0
- package/src/runtime/compact-pipeline.ts +56 -0
- package/src/runtime/compact-stages/ansi-strip-stage.ts +25 -0
- package/src/runtime/compact-stages/blank-collapse-stage.ts +31 -0
- package/src/runtime/compact-stages/deduplicate-stage.ts +34 -0
- package/src/runtime/compact-stages/head-snap-stage.ts +57 -0
- package/src/runtime/compact-stages/index.ts +23 -0
- package/src/runtime/compact-stages/tail-capture-stage.ts +77 -0
- package/src/runtime/compact-stages/truncation-stage.ts +74 -0
- package/src/runtime/compaction-summary.ts +278 -0
- package/src/runtime/completion-guard.ts +207 -0
- package/src/runtime/concurrency.ts +58 -0
- package/src/runtime/crash-classification.ts +229 -0
- package/src/runtime/crash-recovery.ts +594 -0
- package/src/runtime/crew-agent-records.ts +595 -0
- package/src/runtime/crew-agent-runtime.ts +62 -0
- package/src/runtime/crew-broker-child.ts +88 -0
- package/src/runtime/crew-broker-client.ts +673 -0
- package/src/runtime/crew-broker-tokens.ts +159 -0
- package/src/runtime/crew-broker.ts +1304 -0
- package/src/runtime/crew-hooks.ts +227 -0
- package/src/runtime/cross-extension-rpc.ts +143 -0
- package/src/runtime/custom-tools/irc-tool.ts +282 -0
- package/src/runtime/custom-tools/submit-result-tool.ts +102 -0
- package/src/runtime/deadletter.ts +47 -0
- package/src/runtime/delivery-coordinator.ts +211 -0
- package/src/runtime/delta-conflict.ts +348 -0
- package/src/runtime/deterministic-ast.ts +161 -0
- package/src/runtime/diagnostic-export.ts +171 -0
- package/src/runtime/direct-run.ts +45 -0
- package/src/runtime/dwf-state-store.ts +102 -0
- package/src/runtime/dynamic-workflow-context.ts +1013 -0
- package/src/runtime/dynamic-workflow-runner.ts +325 -0
- package/src/runtime/effectiveness.ts +90 -0
- package/src/runtime/errors/crew-errors.ts +162 -0
- package/src/runtime/event-stream-bridge.ts +98 -0
- package/src/runtime/foreground-control.ts +193 -0
- package/src/runtime/foreground-watchdog.ts +132 -0
- package/src/runtime/global-worker-cap.ts +96 -0
- package/src/runtime/goal-achievement.ts +148 -0
- package/src/runtime/goal-evaluator.ts +365 -0
- package/src/runtime/goal-loop-runner.ts +825 -0
- package/src/runtime/goal-state-store.ts +219 -0
- package/src/runtime/green-contract.ts +55 -0
- package/src/runtime/group-join.ts +267 -0
- package/src/runtime/handoff-manager.ts +598 -0
- package/src/runtime/heartbeat-gradient.ts +36 -0
- package/src/runtime/heartbeat-watcher.ts +239 -0
- package/src/runtime/hidden-handoff.ts +407 -0
- package/src/runtime/important-line-classifier.ts +140 -0
- package/src/runtime/intercom-bridge.ts +187 -0
- package/src/runtime/iteration-hooks.ts +305 -0
- package/src/runtime/live-agent-control.ts +136 -0
- package/src/runtime/live-agent-manager.ts +704 -0
- package/src/runtime/live-control-realtime.ts +56 -0
- package/src/runtime/live-extension-bridge.ts +148 -0
- package/src/runtime/live-irc.ts +97 -0
- package/src/runtime/live-session-health.ts +108 -0
- package/src/runtime/live-session-runtime.ts +1146 -0
- package/src/runtime/loop-gates.ts +128 -0
- package/src/runtime/manifest-cache.ts +375 -0
- package/src/runtime/mcp-proxy.ts +105 -0
- package/src/runtime/metric-parser.ts +36 -0
- package/src/runtime/model-fallback.ts +409 -0
- package/src/runtime/model-resolver.ts +126 -0
- package/src/runtime/model-scope.ts +158 -0
- package/src/runtime/orphan-worker-registry.ts +465 -0
- package/src/runtime/output-validator.ts +202 -0
- package/src/runtime/overflow-recovery.ts +206 -0
- package/src/runtime/parallel-research.ts +68 -0
- package/src/runtime/parallel-utils.ts +160 -0
- package/src/runtime/parent-guard.ts +134 -0
- package/src/runtime/path-overlap.ts +150 -0
- package/src/runtime/peer-dep.ts +292 -0
- package/src/runtime/per-write-validator.ts +181 -0
- package/src/runtime/phase-progress.ts +217 -0
- package/src/runtime/phase-tracker.ts +385 -0
- package/src/runtime/pi-args.ts +655 -0
- package/src/runtime/pi-json-output.ts +170 -0
- package/src/runtime/pi-spawn.ts +273 -0
- package/src/runtime/pipeline-runner.ts +523 -0
- package/src/runtime/plan-templates.ts +201 -0
- package/src/runtime/policy-engine.ts +115 -0
- package/src/runtime/post-checks.ts +142 -0
- package/src/runtime/post-exit-stdio-guard.ts +109 -0
- package/src/runtime/process-lifecycle.ts +491 -0
- package/src/runtime/process-status.ts +154 -0
- package/src/runtime/progress-event-coalescer.ts +44 -0
- package/src/runtime/progress-tracker.ts +124 -0
- package/src/runtime/prose-compressor.ts +162 -0
- package/src/runtime/recovery-recipes.ts +203 -0
- package/src/runtime/replace.ts +570 -0
- package/src/runtime/resilient-edit.ts +153 -0
- package/src/runtime/result-extractor.ts +217 -0
- package/src/runtime/retry-executor.ts +109 -0
- package/src/runtime/retry-runner.ts +336 -0
- package/src/runtime/role-permission.ts +59 -0
- package/src/runtime/run-coalesced-task-group.ts +308 -0
- package/src/runtime/run-drift.ts +219 -0
- package/src/runtime/run-tracker.ts +116 -0
- package/src/runtime/run-worker.ts +78 -0
- package/src/runtime/runtime-policy.ts +29 -0
- package/src/runtime/runtime-resolver.ts +172 -0
- package/src/runtime/runtime-warmup.ts +160 -0
- package/src/runtime/scheduler.ts +353 -0
- package/src/runtime/semaphore.ts +141 -0
- package/src/runtime/sensitive-paths.ts +93 -0
- package/src/runtime/session-resources.ts +25 -0
- package/src/runtime/session-snapshot.ts +59 -0
- package/src/runtime/session-usage.ts +79 -0
- package/src/runtime/settings-store.ts +155 -0
- package/src/runtime/sidechain-output.ts +35 -0
- package/src/runtime/single-agent-compose.ts +84 -0
- package/src/runtime/skill-effectiveness.ts +524 -0
- package/src/runtime/skill-instructions.ts +394 -0
- package/src/runtime/stale-reconciler.ts +680 -0
- package/src/runtime/stream-preview.ts +184 -0
- package/src/runtime/streaming-output.ts +50 -0
- package/src/runtime/subagent-manager.ts +561 -0
- package/src/runtime/subprocess-tool-registry.ts +70 -0
- package/src/runtime/supervisor-contact.ts +64 -0
- package/src/runtime/task-display.ts +52 -0
- package/src/runtime/task-graph-scheduler.ts +227 -0
- package/src/runtime/task-graph.ts +290 -0
- package/src/runtime/task-health.ts +83 -0
- package/src/runtime/task-id.ts +161 -0
- package/src/runtime/task-output-context.ts +602 -0
- package/src/runtime/task-packet.ts +268 -0
- package/src/runtime/task-quality.ts +199 -0
- package/src/runtime/task-runner/capabilities.ts +78 -0
- package/src/runtime/task-runner/child-executor.ts +790 -0
- package/src/runtime/task-runner/context-retrieval.ts +163 -0
- package/src/runtime/task-runner/live-executor.ts +227 -0
- package/src/runtime/task-runner/output-splitter.ts +152 -0
- package/src/runtime/task-runner/post-execution.ts +480 -0
- package/src/runtime/task-runner/pre-execution.ts +365 -0
- package/src/runtime/task-runner/progress.ts +157 -0
- package/src/runtime/task-runner/prompt-builder.ts +278 -0
- package/src/runtime/task-runner/prompt-pipeline.ts +88 -0
- package/src/runtime/task-runner/result-utils.ts +16 -0
- package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
- package/src/runtime/task-runner/run-projection.ts +125 -0
- package/src/runtime/task-runner/scaffold-executor.ts +25 -0
- package/src/runtime/task-runner/state-helpers.ts +176 -0
- package/src/runtime/task-runner/tail-read.ts +34 -0
- package/src/runtime/task-runner.ts +216 -0
- package/src/runtime/team-runner-artifacts.ts +13 -0
- package/src/runtime/team-runner.ts +2396 -0
- package/src/runtime/tool-output-pruner.ts +333 -0
- package/src/runtime/tool-progress.ts +278 -0
- package/src/runtime/usage-tracker.ts +73 -0
- package/src/runtime/verification-gates.ts +579 -0
- package/src/runtime/verification-integrity.ts +108 -0
- package/src/runtime/verification-worktree.ts +186 -0
- package/src/runtime/worker-heartbeat.ts +25 -0
- package/src/runtime/worker-startup.ts +83 -0
- package/src/runtime/workflow-state.ts +195 -0
- package/src/runtime/workspace-lock.ts +443 -0
- package/src/runtime/workspace-tree.ts +312 -0
- package/src/runtime/yield-handler.ts +224 -0
- package/src/runtime/zombie-scanner.ts +298 -0
- package/src/schema/config-schema.ts +327 -0
- package/src/schema/team-tool-schema.ts +492 -0
- package/src/schema/validation-types.ts +159 -0
- package/src/skills/discover-skills.ts +203 -0
- package/src/skills/skill-templates.ts +456 -0
- package/src/skills/validate.ts +285 -0
- package/src/state/active-run-registry.ts +439 -0
- package/src/state/artifact-store.ts +176 -0
- package/src/state/atomic-write.ts +970 -0
- package/src/state/blob-store.ts +308 -0
- package/src/state/contracts.ts +160 -0
- package/src/state/crew-init.ts +269 -0
- package/src/state/decision-ledger.ts +372 -0
- package/src/state/event-log-rotation.ts +399 -0
- package/src/state/event-log.ts +1465 -0
- package/src/state/event-reconstructor.ts +229 -0
- package/src/state/gitignore-manager.ts +46 -0
- package/src/state/health-store.ts +79 -0
- package/src/state/hook-instinct-bridge.ts +94 -0
- package/src/state/hook-integrations.ts +51 -0
- package/src/state/instinct-store.ts +275 -0
- package/src/state/jsonl-writer.ts +107 -0
- package/src/state/locks.ts +562 -0
- package/src/state/mailbox.ts +916 -0
- package/src/state/observation-store.ts +176 -0
- package/src/state/run-cache.ts +193 -0
- package/src/state/run-graph.ts +183 -0
- package/src/state/run-metrics.ts +165 -0
- package/src/state/schedule.ts +166 -0
- package/src/state/session-state-map.ts +51 -0
- package/src/state/state-store.ts +973 -0
- package/src/state/task-claims.ts +61 -0
- package/src/state/tiered-eval.ts +480 -0
- package/src/state/types-eval.ts +58 -0
- package/src/state/types.ts +447 -0
- package/src/state/usage.ts +138 -0
- package/src/state/worker-atomic-writer.ts +198 -0
- package/src/subagents/async-entry.ts +1 -0
- package/src/subagents/index.ts +3 -0
- package/src/subagents/live/control.ts +1 -0
- package/src/subagents/live/manager.ts +1 -0
- package/src/subagents/live/realtime.ts +1 -0
- package/src/subagents/live/session-runtime.ts +1 -0
- package/src/subagents/manager.ts +1 -0
- package/src/subagents/spawn.ts +1 -0
- package/src/teams/discover-teams.ts +187 -0
- package/src/teams/team-config.ts +27 -0
- package/src/teams/team-serializer.ts +38 -0
- package/src/tools/safe-bash-extension.ts +54 -0
- package/src/tools/safe-bash.ts +505 -0
- package/src/types/diff.d.ts +18 -0
- package/src/types/new-api-types.ts +35 -0
- package/src/ui/agent-management-overlay.ts +160 -0
- package/src/ui/card-colors.ts +139 -0
- package/src/ui/crew-footer.ts +102 -0
- package/src/ui/crew-select-list.ts +112 -0
- package/src/ui/dashboard-panes/agents-pane.ts +164 -0
- package/src/ui/dashboard-panes/cancellation-pane.ts +66 -0
- package/src/ui/dashboard-panes/capability-pane.ts +77 -0
- package/src/ui/dashboard-panes/health-pane.ts +31 -0
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -0
- package/src/ui/dashboard-panes/metrics-pane.ts +37 -0
- package/src/ui/dashboard-panes/progress-pane.ts +38 -0
- package/src/ui/dashboard-panes/transcript-pane.ts +10 -0
- package/src/ui/deploy-bundled-themes.ts +71 -0
- package/src/ui/dwf-phase-display.ts +151 -0
- package/src/ui/dynamic-border.ts +35 -0
- package/src/ui/format-helpers.ts +31 -0
- package/src/ui/heartbeat-aggregator.ts +74 -0
- package/src/ui/key-utils.ts +42 -0
- package/src/ui/keybinding-map.ts +248 -0
- package/src/ui/layout-primitives.ts +106 -0
- package/src/ui/live-conversation-overlay.ts +183 -0
- package/src/ui/live-duration.ts +55 -0
- package/src/ui/live-run-sidebar.ts +293 -0
- package/src/ui/loaders.ts +6 -0
- package/src/ui/mascot.ts +449 -0
- package/src/ui/overlays/agent-picker-overlay.ts +66 -0
- package/src/ui/overlays/confirm-overlay.ts +60 -0
- package/src/ui/overlays/help-overlay.ts +177 -0
- package/src/ui/overlays/mailbox-compose-overlay.ts +181 -0
- package/src/ui/overlays/mailbox-compose-preview.ts +78 -0
- package/src/ui/overlays/mailbox-detail-overlay.ts +155 -0
- package/src/ui/pi-ui-compat.ts +68 -0
- package/src/ui/powerbar-publisher.ts +459 -0
- package/src/ui/render-coalescer.ts +101 -0
- package/src/ui/render-diff.ts +160 -0
- package/src/ui/render-scheduler.ts +278 -0
- package/src/ui/run-action-dispatcher.ts +182 -0
- package/src/ui/run-dashboard.ts +910 -0
- package/src/ui/run-event-bus.ts +381 -0
- package/src/ui/run-snapshot-cache.ts +1057 -0
- package/src/ui/settings-overlay.ts +1067 -0
- package/src/ui/shared-overlay-scheduler.ts +96 -0
- package/src/ui/snapshot-types.ts +90 -0
- package/src/ui/spinner.ts +17 -0
- package/src/ui/status-colors.ts +143 -0
- package/src/ui/syntax-highlight.ts +136 -0
- package/src/ui/terminal-status.ts +269 -0
- package/src/ui/theme-adapter.ts +271 -0
- package/src/ui/theme-discovery.ts +199 -0
- package/src/ui/tool-progress-formatter.ts +93 -0
- package/src/ui/tool-renderers/brief-mode.ts +225 -0
- package/src/ui/tool-renderers/index.ts +726 -0
- package/src/ui/transcript-cache.ts +128 -0
- package/src/ui/transcript-entries.ts +256 -0
- package/src/ui/transcript-viewer.ts +424 -0
- package/src/ui/widget/index.ts +418 -0
- package/src/ui/widget/widget-formatters.ts +182 -0
- package/src/ui/widget/widget-model.ts +110 -0
- package/src/ui/widget/widget-renderer.ts +182 -0
- package/src/ui/widget/widget-types.ts +36 -0
- package/src/utils/bm25-search.ts +235 -0
- package/src/utils/completion-dedupe.ts +63 -0
- package/src/utils/conflict-detect.ts +668 -0
- package/src/utils/env-allowlist.ts +30 -0
- package/src/utils/env-filter.ts +155 -0
- package/src/utils/file-coalescer.ts +98 -0
- package/src/utils/fingerprint.ts +180 -0
- package/src/utils/frontmatter.ts +70 -0
- package/src/utils/fs-watch.ts +37 -0
- package/src/utils/gh-protocol.ts +556 -0
- package/src/utils/git.ts +260 -0
- package/src/utils/guards.ts +107 -0
- package/src/utils/ids.ts +24 -0
- package/src/utils/incremental-reader.ts +220 -0
- package/src/utils/internal-error.ts +10 -0
- package/src/utils/names.ts +36 -0
- package/src/utils/ndjson.ts +115 -0
- package/src/utils/paths.ts +227 -0
- package/src/utils/project-detector.ts +160 -0
- package/src/utils/redaction.ts +370 -0
- package/src/utils/resolve-shell.ts +36 -0
- package/src/utils/run-watcher-registry.ts +152 -0
- package/src/utils/safe-paths.ts +393 -0
- package/src/utils/scan-cache.ts +144 -0
- package/src/utils/session-utils.ts +110 -0
- package/src/utils/sleep.ts +54 -0
- package/src/utils/socket-path.ts +164 -0
- package/src/utils/sse-parser.ts +131 -0
- package/src/utils/task-name-generator.ts +337 -0
- package/src/utils/timings.ts +33 -0
- package/src/utils/token-counter.ts +200 -0
- package/src/utils/visual.ts +207 -0
- package/src/workflows/cost-estimator.ts +34 -0
- package/src/workflows/discover-workflows.ts +308 -0
- package/src/workflows/intermediate-store.ts +166 -0
- package/src/workflows/preflight-validator.ts +168 -0
- package/src/workflows/topology-analyzer.ts +203 -0
- package/src/workflows/validate-workflow.ts +50 -0
- package/src/workflows/workflow-config.ts +75 -0
- package/src/workflows/workflow-serializer.ts +32 -0
- package/src/worktree/branch-freshness.ts +112 -0
- package/src/worktree/cleanup.ts +341 -0
- package/src/worktree/worktree-manager.ts +1204 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { DEFAULT_PATHS } from "../config/defaults.ts";
|
|
5
|
+
import { createCancellationToken } from "../runtime/cancellation-token.ts";
|
|
6
|
+
import { activeRunEntries } from "../state/active-run-registry.ts";
|
|
7
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
8
|
+
import { findRepoRoot, projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
|
|
9
|
+
import { isSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
10
|
+
import { sharedScanCache } from "../utils/scan-cache.ts";
|
|
11
|
+
|
|
12
|
+
function readManifest(filePath: string): TeamRunManifest | undefined {
|
|
13
|
+
const cached = sharedScanCache.readAndCache("manifests", filePath, filePath);
|
|
14
|
+
if (cached) return cached.raw as TeamRunManifest;
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8")) as TeamRunManifest;
|
|
17
|
+
} catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function collectRuns(root: string, maxEntries?: number, signal?: AbortSignal): TeamRunManifest[] {
|
|
23
|
+
const runsRoot = path.join(root, DEFAULT_PATHS.state.runsSubdir);
|
|
24
|
+
if (!fs.existsSync(runsRoot)) return [];
|
|
25
|
+
if (signal?.aborted) return [];
|
|
26
|
+
// 2.19 — skip temp directories to avoid phantom runs from test/isolated workspaces.
|
|
27
|
+
// Runs in /tmp/ are typically from tests or isolated worktrees and should not
|
|
28
|
+
// appear in production dashboards unless the process is actually alive.
|
|
29
|
+
const tempDirs = [os.tmpdir(), "/var/tmp", "/tmp"];
|
|
30
|
+
const isTempRoot = tempDirs.some((t) => root.startsWith(t + path.sep));
|
|
31
|
+
const token = createCancellationToken({ signal });
|
|
32
|
+
const entries = fs
|
|
33
|
+
.readdirSync(runsRoot, { withFileTypes: true })
|
|
34
|
+
.filter((entry) => entry.isDirectory() && isSafePathId(entry.name))
|
|
35
|
+
.map((entry) => entry.name)
|
|
36
|
+
.sort((a, b) => (b ?? "").localeCompare(a ?? ""));
|
|
37
|
+
const selected = maxEntries !== undefined ? entries.slice(0, Math.max(0, maxEntries)) : entries;
|
|
38
|
+
const results: TeamRunManifest[] = [];
|
|
39
|
+
for (let i = 0; i < selected.length; i++) {
|
|
40
|
+
if (i % 10 === 0) token.heartbeat(`collectRuns:${i}/${selected.length}`);
|
|
41
|
+
try {
|
|
42
|
+
const manifest = readManifest(path.join(resolveRealContainedPath(runsRoot, selected[i]), DEFAULT_PATHS.state.manifestFile));
|
|
43
|
+
if (!manifest) continue;
|
|
44
|
+
// Filter out ghost runs: active status but CWD no longer exists.
|
|
45
|
+
// These are deadletter/replay/temp runs whose temp dirs were cleaned up.
|
|
46
|
+
if (
|
|
47
|
+
(manifest.status === "queued" || manifest.status === "running" || manifest.status === "planning") &&
|
|
48
|
+
manifest.cwd &&
|
|
49
|
+
!fs.existsSync(manifest.cwd)
|
|
50
|
+
)
|
|
51
|
+
continue;
|
|
52
|
+
// 2.19 — for runs in temp directories, verify the background process is alive.
|
|
53
|
+
// Without this, runs that completed/crashed but left stale manifests will still show.
|
|
54
|
+
if (isTempRoot && (manifest.status === "running" || manifest.status === "queued" || manifest.status === "planning")) {
|
|
55
|
+
const asyncPidPath = path.join(path.dirname(manifest.stateRoot), "async.pid");
|
|
56
|
+
try {
|
|
57
|
+
const pidData = JSON.parse(fs.readFileSync(asyncPidPath, "utf-8"));
|
|
58
|
+
const pid = pidData?.pid;
|
|
59
|
+
if (pid && typeof pid === "number") {
|
|
60
|
+
try {
|
|
61
|
+
process.kill(pid, 0);
|
|
62
|
+
} catch {
|
|
63
|
+
continue;
|
|
64
|
+
} // PID dead, skip this run
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
/* no async.pid or parse error — keep the run */
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
results.push(manifest);
|
|
71
|
+
} catch {
|
|
72
|
+
/* skip unreadable manifests */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return results;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function mergeRuns(runSets: TeamRunManifest[][], max?: number): TeamRunManifest[] {
|
|
79
|
+
const byId = new Map<string, TeamRunManifest>();
|
|
80
|
+
for (const runs of runSets) for (const run of runs) byId.set(run.runId, run);
|
|
81
|
+
const sorted = [...byId.values()].sort((a, b) => (b.createdAt ?? "").localeCompare(a.createdAt ?? ""));
|
|
82
|
+
return max !== undefined ? sorted.slice(0, Math.max(0, max)) : sorted;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function scopedRunRoots(cwd: string): string[] {
|
|
86
|
+
const roots = new Set<string>();
|
|
87
|
+
roots.add(userCrewRoot());
|
|
88
|
+
const projectRoot = findRepoRoot(cwd);
|
|
89
|
+
if (projectRoot) roots.add(projectCrewRoot(cwd));
|
|
90
|
+
return [...roots];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function collectActiveRuns(cwd?: string): TeamRunManifest[] {
|
|
94
|
+
return activeRunEntries()
|
|
95
|
+
.map((entry) => readManifest(entry.manifestPath))
|
|
96
|
+
.filter((manifest): manifest is TeamRunManifest => manifest !== undefined);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function listRuns(cwd: string, signal?: AbortSignal): TeamRunManifest[] {
|
|
100
|
+
const roots = scopedRunRoots(cwd);
|
|
101
|
+
return mergeRuns([...roots.map((root) => collectRuns(root, undefined, signal)), collectActiveRuns()]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function listRecentRuns(cwd: string, max = 20, signal?: AbortSignal): TeamRunManifest[] {
|
|
105
|
+
const roots = scopedRunRoots(cwd);
|
|
106
|
+
return mergeRuns([...roots.map((root) => collectRuns(root, max, signal)), collectActiveRuns()], max);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* List runs filtered to a specific scope.
|
|
111
|
+
* - "project": only runs in the project crew root
|
|
112
|
+
* - "user": only runs in the user crew root
|
|
113
|
+
* - "all" (default): merge both scopes (current behavior)
|
|
114
|
+
*/
|
|
115
|
+
export function listRunsByScope(
|
|
116
|
+
cwd: string,
|
|
117
|
+
scope: "project" | "user" | "all" = "all",
|
|
118
|
+
max?: number,
|
|
119
|
+
signal?: AbortSignal,
|
|
120
|
+
): TeamRunManifest[] {
|
|
121
|
+
const projectRoot = findRepoRoot(cwd);
|
|
122
|
+
switch (scope) {
|
|
123
|
+
case "project":
|
|
124
|
+
return projectRoot ? collectRuns(projectCrewRoot(cwd), max, signal) : [];
|
|
125
|
+
case "user":
|
|
126
|
+
return collectRuns(userCrewRoot(), max, signal);
|
|
127
|
+
case "all":
|
|
128
|
+
default:
|
|
129
|
+
return max !== undefined ? listRecentRuns(cwd, max, signal) : listRuns(cwd, signal);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { DEFAULT_PATHS } from "../config/defaults.ts";
|
|
4
|
+
import { createCancellationToken } from "../runtime/cancellation-token.ts";
|
|
5
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
6
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
7
|
+
import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
|
|
8
|
+
import { redactSecrets } from "../utils/redaction.ts";
|
|
9
|
+
import { isSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
10
|
+
import { cleanupRunWorktrees } from "../worktree/cleanup.ts";
|
|
11
|
+
import { listRuns } from "./run-index.ts";
|
|
12
|
+
|
|
13
|
+
export interface PruneRunsResult {
|
|
14
|
+
kept: string[];
|
|
15
|
+
removed: string[];
|
|
16
|
+
auditPath?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PruneRunsOptions {
|
|
20
|
+
intent?: string;
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isFinished(run: TeamRunManifest): boolean {
|
|
25
|
+
// P3: "blocked" is NOT a terminal status — the run is waiting on something
|
|
26
|
+
// (plan approval, mailbox reply, scheduler stall) and can transition to
|
|
27
|
+
// running/cancelled/failed (per TEAM_RUN_STATUS_TRANSITIONS). Pruning a
|
|
28
|
+
// blocked run destroys recoverable state (e.g. a plan the user needs to
|
|
29
|
+
// approve, or a mailbox wait the user needs to answer). Truly stuck
|
|
30
|
+
// blocked runs are handled by the stale-reconciler/crash-recovery, not
|
|
31
|
+
// by prune.
|
|
32
|
+
return run.status === "completed" || run.status === "failed" || run.status === "cancelled";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isSafeToPrune(cwd: string, run: TeamRunManifest): boolean {
|
|
36
|
+
try {
|
|
37
|
+
const crewRoot = run.stateRoot.startsWith(userCrewRoot() + path.sep) ? userCrewRoot() : projectCrewRoot(cwd);
|
|
38
|
+
resolveRealContainedPath(crewRoot, run.stateRoot);
|
|
39
|
+
resolveRealContainedPath(crewRoot, run.artifactsRoot);
|
|
40
|
+
return true;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function appendPruneAudit(cwd: string, payload: Record<string, unknown>): string | undefined {
|
|
47
|
+
try {
|
|
48
|
+
const filePath = path.join(projectCrewRoot(cwd), "audit", "prune.jsonl");
|
|
49
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
50
|
+
fs.appendFileSync(filePath, `${JSON.stringify(redactSecrets({ ...payload, auditedAt: new Date().toISOString() }))}\n`, "utf-8");
|
|
51
|
+
return filePath;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logInternalError("prune.audit-write", error, `cwd=${cwd}`);
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function pruneFinishedRuns(cwd: string, keep: number, options: PruneRunsOptions = {}): PruneRunsResult {
|
|
59
|
+
const token = createCancellationToken({ signal: options.signal });
|
|
60
|
+
const finished = listRuns(cwd, options.signal)
|
|
61
|
+
.filter((run) => run.cwd === cwd && isFinished(run))
|
|
62
|
+
.sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
|
|
63
|
+
const kept = finished.slice(0, keep).map((run) => run.runId);
|
|
64
|
+
const removed: string[] = [];
|
|
65
|
+
const toRemove = finished.slice(keep);
|
|
66
|
+
for (let i = 0; i < toRemove.length; i++) {
|
|
67
|
+
if (i % 5 === 0) token.heartbeat(`prune:${i}/${toRemove.length}`);
|
|
68
|
+
const run = toRemove[i];
|
|
69
|
+
if (!isSafeToPrune(cwd, run)) {
|
|
70
|
+
logInternalError(
|
|
71
|
+
"prune.path-unsafe",
|
|
72
|
+
new Error(`Skipping unsafe prune: stateRoot=${run.stateRoot}, artifactsRoot=${run.artifactsRoot}`),
|
|
73
|
+
`runId=${run.runId}`,
|
|
74
|
+
);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// P2: clean up git worktrees BEFORE deleting state. Worktrees live at
|
|
78
|
+
// <crewRoot>/state/worktrees/<runId>/ — a separate path from stateRoot
|
|
79
|
+
// (<crewRoot>/state/runs/<runId>/) and artifactsRoot, so fs.rmSync below
|
|
80
|
+
// does NOT touch them. Without this, every pruned worktree-using run
|
|
81
|
+
// leaks its worktree dir + git branch. cleanupRunWorktrees (no force)
|
|
82
|
+
// preserves dirty worktrees by writing a diff artifact and skipping
|
|
83
|
+
// the removal — in that case we skip the whole prune of this run so
|
|
84
|
+
// the user can recover (no silent data loss). Compare handleForget
|
|
85
|
+
// which calls cleanupRunWorktrees.
|
|
86
|
+
const worktreeCleanup = cleanupRunWorktrees(run, { signal: options.signal });
|
|
87
|
+
if (worktreeCleanup.preserved.length > 0) {
|
|
88
|
+
logInternalError(
|
|
89
|
+
"prune.worktree-preserved",
|
|
90
|
+
new Error(
|
|
91
|
+
`Skipping prune: ${worktreeCleanup.preserved.length} dirty worktree(s) preserved for recovery: ${worktreeCleanup.preserved.map((p) => p.path).join(", ")}`,
|
|
92
|
+
),
|
|
93
|
+
`runId=${run.runId}`,
|
|
94
|
+
);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
fs.rmSync(run.stateRoot, { recursive: true, force: true });
|
|
98
|
+
fs.rmSync(run.artifactsRoot, { recursive: true, force: true });
|
|
99
|
+
removed.push(run.runId);
|
|
100
|
+
}
|
|
101
|
+
const auditPath = appendPruneAudit(cwd, {
|
|
102
|
+
action: "prune",
|
|
103
|
+
keep,
|
|
104
|
+
intent: options.intent,
|
|
105
|
+
kept,
|
|
106
|
+
removed,
|
|
107
|
+
});
|
|
108
|
+
return { kept, removed, auditPath };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Prune finished run directories at the user level (~/.pi/agent/extensions/pi-crew/state/runs/).
|
|
113
|
+
*
|
|
114
|
+
* This handles runs created without a project root (e.g. `team action='run'` from home directory)
|
|
115
|
+
* that would otherwise accumulate forever.
|
|
116
|
+
*
|
|
117
|
+
* @param keep Number of most recent finished runs to retain
|
|
118
|
+
* @returns kept and removed run IDs
|
|
119
|
+
*/
|
|
120
|
+
export function pruneUserLevelRuns(keep: number): PruneRunsResult {
|
|
121
|
+
const crewRoot = userCrewRoot();
|
|
122
|
+
const runsRoot = path.join(crewRoot, DEFAULT_PATHS.state.runsSubdir);
|
|
123
|
+
if (!fs.existsSync(runsRoot)) return { kept: [], removed: [] };
|
|
124
|
+
|
|
125
|
+
// Read all run directories, parse manifests, filter to finished
|
|
126
|
+
const MAX_DIRS = 500;
|
|
127
|
+
const finished: Array<{
|
|
128
|
+
runId: string;
|
|
129
|
+
updatedAt: string;
|
|
130
|
+
stateRoot: string;
|
|
131
|
+
artifactsRoot: string;
|
|
132
|
+
}> = [];
|
|
133
|
+
const ghostRemoved: string[] = [];
|
|
134
|
+
const dirs = fs
|
|
135
|
+
.readdirSync(runsRoot, { withFileTypes: true })
|
|
136
|
+
.filter((entry) => entry.isDirectory() && isSafePathId(entry.name))
|
|
137
|
+
.slice(0, MAX_DIRS)
|
|
138
|
+
.map((entry) => entry.name);
|
|
139
|
+
|
|
140
|
+
for (const dir of dirs) {
|
|
141
|
+
const manifestPath = path.join(runsRoot, dir, DEFAULT_PATHS.state.manifestFile);
|
|
142
|
+
let manifest: TeamRunManifest | undefined;
|
|
143
|
+
try {
|
|
144
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as TeamRunManifest;
|
|
145
|
+
} catch {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Ghost run cleanup: active status but CWD no longer exists.
|
|
150
|
+
// These are deadletter/replay/temp runs from dead Pi sessions.
|
|
151
|
+
const isActive = manifest.status === "queued" || manifest.status === "running" || manifest.status === "planning";
|
|
152
|
+
if (isActive && manifest.cwd && !fs.existsSync(manifest.cwd)) {
|
|
153
|
+
fs.rmSync(path.join(runsRoot, dir), {
|
|
154
|
+
recursive: true,
|
|
155
|
+
force: true,
|
|
156
|
+
});
|
|
157
|
+
ghostRemoved.push(manifest.runId);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!isFinished(manifest)) continue;
|
|
162
|
+
|
|
163
|
+
// Safety check: ensure stateRoot and artifactsRoot are contained within user crew root
|
|
164
|
+
try {
|
|
165
|
+
resolveRealContainedPath(crewRoot, manifest.stateRoot);
|
|
166
|
+
resolveRealContainedPath(crewRoot, manifest.artifactsRoot);
|
|
167
|
+
} catch {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
finished.push({
|
|
172
|
+
runId: manifest.runId,
|
|
173
|
+
updatedAt: manifest.updatedAt,
|
|
174
|
+
stateRoot: manifest.stateRoot,
|
|
175
|
+
artifactsRoot: manifest.artifactsRoot,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Sort newest first, keep top N, remove the rest
|
|
180
|
+
finished.sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
|
|
181
|
+
const kept = finished.slice(0, keep).map((r) => r.runId);
|
|
182
|
+
const removed: string[] = [];
|
|
183
|
+
for (const run of finished.slice(keep)) {
|
|
184
|
+
fs.rmSync(run.stateRoot, { recursive: true, force: true });
|
|
185
|
+
fs.rmSync(run.artifactsRoot, { recursive: true, force: true });
|
|
186
|
+
removed.push(run.runId);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return { kept, removed: [...removed, ...ghostRemoved] };
|
|
190
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { readCrewAgents } from "../runtime/crew-agent-records.ts";
|
|
3
|
+
import { isDisplayActiveRun } from "../runtime/process-status.ts";
|
|
4
|
+
import { listRuns } from "./run-index.ts";
|
|
5
|
+
|
|
6
|
+
export function notifyActiveRuns(ctx: ExtensionContext): void {
|
|
7
|
+
const active = listRuns(ctx.cwd)
|
|
8
|
+
.filter((run) => {
|
|
9
|
+
if (run.status !== "queued" && run.status !== "planning" && run.status !== "running") return false;
|
|
10
|
+
// Use the same display filter as the widget/powerbar — runs without
|
|
11
|
+
// real agent evidence (e.g. integration test fixtures) must not appear.
|
|
12
|
+
const agents = readCrewAgents(run);
|
|
13
|
+
return isDisplayActiveRun(run, agents);
|
|
14
|
+
})
|
|
15
|
+
.slice(0, 5);
|
|
16
|
+
if (active.length === 0) return;
|
|
17
|
+
ctx.ui.notify(`pi-crew active runs: ${active.map((run) => `${run.runId} [${run.status}]`).join(", ")}`, "info");
|
|
18
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { listRuns } from "./run-index.ts";
|
|
3
|
+
// Lazy-loaded: team-tool.ts pulls in entire runtime chain.
|
|
4
|
+
import type { handleTeamTool as HandleTeamToolFn } from "./team-tool.ts";
|
|
5
|
+
|
|
6
|
+
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
7
|
+
async function handleTeamTool(
|
|
8
|
+
params: Parameters<typeof HandleTeamToolFn>[0],
|
|
9
|
+
ctx: Parameters<typeof HandleTeamToolFn>[1],
|
|
10
|
+
): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
|
|
11
|
+
if (!_cachedHandleTeamTool) {
|
|
12
|
+
// LAZY: team-tool.ts pulls in the entire runtime chain.
|
|
13
|
+
const mod = await import("./team-tool.ts");
|
|
14
|
+
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
15
|
+
}
|
|
16
|
+
return _cachedHandleTeamTool(params, ctx);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
import { isToolError, textFromToolResult } from "./tool-result.ts";
|
|
20
|
+
|
|
21
|
+
async function notifyResult(ctx: ExtensionCommandContext, result: Awaited<ReturnType<typeof handleTeamTool>>): Promise<void> {
|
|
22
|
+
const text = textFromToolResult(result);
|
|
23
|
+
ctx.ui.notify(text.length > 1000 ? `${text.slice(0, 997)}...` : text, isToolError(result) ? "error" : "info");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function handleTeamManagerCommand(_args: string, ctx: ExtensionCommandContext): Promise<void> {
|
|
27
|
+
const action = await ctx.ui.select("pi-crew", [
|
|
28
|
+
"List teams/workflows/agents/runs",
|
|
29
|
+
"Run team",
|
|
30
|
+
"Show run status",
|
|
31
|
+
"Cleanup run worktrees",
|
|
32
|
+
"Create routed resource",
|
|
33
|
+
"Update routed resource",
|
|
34
|
+
"Doctor",
|
|
35
|
+
]);
|
|
36
|
+
if (!action) return;
|
|
37
|
+
|
|
38
|
+
if (action.startsWith("List")) {
|
|
39
|
+
await notifyResult(ctx, await handleTeamTool({ action: "list" }, ctx));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (action === "Doctor") {
|
|
44
|
+
await notifyResult(ctx, await handleTeamTool({ action: "doctor" }, ctx));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (action === "Create routed resource" || action === "Update routed resource") {
|
|
49
|
+
const isUpdate = action === "Update routed resource";
|
|
50
|
+
const resource = await ctx.ui.select("Resource type", ["agent", "team"]);
|
|
51
|
+
if (resource !== "agent" && resource !== "team") return;
|
|
52
|
+
const name = await ctx.ui.input("Name", resource === "agent" ? "custom-agent" : "custom-team");
|
|
53
|
+
if (!name) return;
|
|
54
|
+
const description = await ctx.ui.input("Description", "When to use this resource");
|
|
55
|
+
if (!description) return;
|
|
56
|
+
const triggers = await ctx.ui.input("Triggers (comma-separated)", "");
|
|
57
|
+
const useWhen = await ctx.ui.input("Use when (comma-separated)", "");
|
|
58
|
+
const avoidWhen = await ctx.ui.input("Avoid when (comma-separated)", "");
|
|
59
|
+
const cost = await ctx.ui.select("Cost", ["cheap", "free", "expensive"]);
|
|
60
|
+
const category = await ctx.ui.input("Category", "custom");
|
|
61
|
+
const baseConfig = {
|
|
62
|
+
name,
|
|
63
|
+
description,
|
|
64
|
+
scope: "project",
|
|
65
|
+
triggers,
|
|
66
|
+
useWhen,
|
|
67
|
+
avoidWhen,
|
|
68
|
+
cost,
|
|
69
|
+
category,
|
|
70
|
+
};
|
|
71
|
+
if (resource === "agent") {
|
|
72
|
+
const systemPrompt = isUpdate ? undefined : `You are ${name}.`;
|
|
73
|
+
await notifyResult(
|
|
74
|
+
ctx,
|
|
75
|
+
await handleTeamTool(
|
|
76
|
+
{
|
|
77
|
+
action: isUpdate ? "update" : "create",
|
|
78
|
+
resource,
|
|
79
|
+
agent: name,
|
|
80
|
+
config: { ...baseConfig, systemPrompt },
|
|
81
|
+
},
|
|
82
|
+
ctx,
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const agent = await ctx.ui.input("Role agent", "executor");
|
|
88
|
+
await notifyResult(
|
|
89
|
+
ctx,
|
|
90
|
+
await handleTeamTool(
|
|
91
|
+
{
|
|
92
|
+
action: isUpdate ? "update" : "create",
|
|
93
|
+
resource,
|
|
94
|
+
team: name,
|
|
95
|
+
config: {
|
|
96
|
+
...baseConfig,
|
|
97
|
+
roles: [{ name: "executor", agent: agent || "executor" }],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
ctx,
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (action === "Run team") {
|
|
107
|
+
const team = await ctx.ui.input("Team name", "default");
|
|
108
|
+
if (team === undefined) return;
|
|
109
|
+
const goal = await ctx.ui.input("Goal", "Describe the team objective");
|
|
110
|
+
if (!goal) return;
|
|
111
|
+
const asyncRun = await ctx.ui.confirm("Async run?", "Run in detached background mode?");
|
|
112
|
+
const worktree = await ctx.ui.confirm("Worktree mode?", "Use git worktrees for task workspaces? Requires a clean repo by default.");
|
|
113
|
+
await notifyResult(
|
|
114
|
+
ctx,
|
|
115
|
+
await handleTeamTool(
|
|
116
|
+
{
|
|
117
|
+
action: "run",
|
|
118
|
+
team: team || "default",
|
|
119
|
+
goal,
|
|
120
|
+
async: asyncRun,
|
|
121
|
+
workspaceMode: worktree ? "worktree" : "single",
|
|
122
|
+
},
|
|
123
|
+
ctx,
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const runs = listRuns(ctx.cwd).slice(0, 20);
|
|
130
|
+
if (runs.length === 0) {
|
|
131
|
+
ctx.ui.notify("No pi-crew runs found.", "info");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const selected = await ctx.ui.select(
|
|
135
|
+
"Select run",
|
|
136
|
+
runs.map((run) => `${run.runId} [${run.status}] ${run.team}/${run.workflow ?? "none"}`),
|
|
137
|
+
);
|
|
138
|
+
if (!selected) return;
|
|
139
|
+
const runId = selected.split(" ")[0];
|
|
140
|
+
if (!runId) return;
|
|
141
|
+
|
|
142
|
+
if (action === "Show run status") {
|
|
143
|
+
await notifyResult(ctx, await handleTeamTool({ action: "status", runId }, ctx));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (action === "Cleanup run worktrees") {
|
|
147
|
+
const force = await ctx.ui.confirm(
|
|
148
|
+
"Force cleanup?",
|
|
149
|
+
"Force may remove dirty worktrees. Choose false to preserve dirty worktrees and capture cleanup diffs.",
|
|
150
|
+
);
|
|
151
|
+
await notifyResult(ctx, await handleTeamTool({ action: "cleanup", runId, force }, ctx));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
|
+
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
5
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
6
|
+
import { projectCrewRoot } from "../utils/paths.ts";
|
|
7
|
+
|
|
8
|
+
export interface OnboardingOptions {
|
|
9
|
+
team?: string;
|
|
10
|
+
limit?: number;
|
|
11
|
+
cwd?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface RunSummary {
|
|
15
|
+
runId: string;
|
|
16
|
+
status: string;
|
|
17
|
+
goal: string;
|
|
18
|
+
team: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
completedAt?: string;
|
|
21
|
+
taskCount: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Load run summaries from the state directory.
|
|
26
|
+
*/
|
|
27
|
+
function loadRunSummaries(cwd: string, options: OnboardingOptions = {}): RunSummary[] {
|
|
28
|
+
const crewRoot = projectCrewRoot(cwd);
|
|
29
|
+
const runsRoot = path.join(crewRoot, "state", "runs");
|
|
30
|
+
|
|
31
|
+
if (!fs.existsSync(runsRoot)) return [];
|
|
32
|
+
|
|
33
|
+
const limit = options.limit ?? 5;
|
|
34
|
+
const teamFilter = options.team;
|
|
35
|
+
|
|
36
|
+
const entries = fs
|
|
37
|
+
.readdirSync(runsRoot)
|
|
38
|
+
.filter((e) => {
|
|
39
|
+
try {
|
|
40
|
+
return fs.statSync(path.join(runsRoot, e)).isDirectory();
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
.sort()
|
|
46
|
+
.reverse()
|
|
47
|
+
.slice(0, 100);
|
|
48
|
+
|
|
49
|
+
const summaries: RunSummary[] = [];
|
|
50
|
+
|
|
51
|
+
for (const runId of entries) {
|
|
52
|
+
if (summaries.length >= limit) break;
|
|
53
|
+
|
|
54
|
+
const manifestPath = path.join(runsRoot, runId, "manifest.json");
|
|
55
|
+
if (!fs.existsSync(manifestPath)) continue;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const raw = JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as TeamRunManifest & { completedAt?: string };
|
|
59
|
+
|
|
60
|
+
if (teamFilter && raw.team !== teamFilter) continue;
|
|
61
|
+
|
|
62
|
+
summaries.push({
|
|
63
|
+
runId,
|
|
64
|
+
status: raw.status,
|
|
65
|
+
goal: raw.goal ?? "",
|
|
66
|
+
team: raw.team,
|
|
67
|
+
createdAt: raw.createdAt,
|
|
68
|
+
completedAt: raw.completedAt ?? raw.updatedAt,
|
|
69
|
+
taskCount: 0, // tasks stored separately, not in manifest
|
|
70
|
+
});
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logInternalError("team-onboard.load-manifest", error, undefined, "debug");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return summaries;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Format duration in minutes. Defensive against missing/invalid timestamps
|
|
81
|
+
* (e.g. legacy test runs) — returns "?" instead of "NaNm".
|
|
82
|
+
*/
|
|
83
|
+
export function formatDuration(createdAt: string, completedAt?: string): string {
|
|
84
|
+
const start = new Date(createdAt).getTime();
|
|
85
|
+
const end = completedAt ? new Date(completedAt).getTime() : Date.now();
|
|
86
|
+
if (!Number.isFinite(start) || !Number.isFinite(end) || end < start) return "?";
|
|
87
|
+
const minutes = Math.round((end - start) / 1000 / 60);
|
|
88
|
+
if (minutes < 1) return "<1m";
|
|
89
|
+
if (minutes >= 60) return `${Math.round(minutes / 60)}h`;
|
|
90
|
+
return `${minutes}m`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build markdown onboarding guide for a team.
|
|
95
|
+
*/
|
|
96
|
+
export function buildTeamOnboarding(team: string, cwd: string, options: OnboardingOptions = {}): string {
|
|
97
|
+
const runs = loadRunSummaries(cwd, { ...options, team });
|
|
98
|
+
|
|
99
|
+
const lines: string[] = [];
|
|
100
|
+
|
|
101
|
+
// Header
|
|
102
|
+
lines.push(`# Team: ${team}`);
|
|
103
|
+
lines.push("");
|
|
104
|
+
lines.push(`> Multi-agent ${team} team for pi-crew.`);
|
|
105
|
+
lines.push("");
|
|
106
|
+
|
|
107
|
+
// Overview stats
|
|
108
|
+
const completed = runs.filter((r) => r.status === "completed");
|
|
109
|
+
const failed = runs.filter((r) => r.status === "failed" || r.status === "cancelled");
|
|
110
|
+
|
|
111
|
+
let avgDuration = 0;
|
|
112
|
+
if (completed.length > 0) {
|
|
113
|
+
const totalMs = completed.reduce((sum, r) => {
|
|
114
|
+
const start = new Date(r.createdAt).getTime();
|
|
115
|
+
const end = r.completedAt ? new Date(r.completedAt).getTime() : Date.now();
|
|
116
|
+
return sum + (end - start);
|
|
117
|
+
}, 0);
|
|
118
|
+
avgDuration = totalMs / completed.length / 1000 / 60;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
lines.push("## Overview");
|
|
122
|
+
lines.push("");
|
|
123
|
+
lines.push(`| Metric | Value |`);
|
|
124
|
+
lines.push(`|--------|-------|`);
|
|
125
|
+
lines.push(`| Total runs | ${runs.length} |`);
|
|
126
|
+
lines.push(`| Completed | ${completed.length} |`);
|
|
127
|
+
lines.push(`| Failed/Cancelled | ${failed.length} |`);
|
|
128
|
+
if (avgDuration > 0) {
|
|
129
|
+
lines.push(`| Avg duration | ${avgDuration.toFixed(1)} min |`);
|
|
130
|
+
}
|
|
131
|
+
lines.push("");
|
|
132
|
+
|
|
133
|
+
// Past runs table
|
|
134
|
+
if (runs.length > 0) {
|
|
135
|
+
lines.push("## Past Runs");
|
|
136
|
+
lines.push("");
|
|
137
|
+
lines.push("| Run | Goal | Duration | Status |");
|
|
138
|
+
lines.push("|-----|------|----------|--------|");
|
|
139
|
+
|
|
140
|
+
for (const run of runs) {
|
|
141
|
+
const duration = formatDuration(run.createdAt, run.completedAt);
|
|
142
|
+
const goalPreview = run.goal ? run.goal.slice(0, 40) : "N/A";
|
|
143
|
+
const goalSuffix = run.goal && run.goal.length > 40 ? "..." : "";
|
|
144
|
+
const status = run.status ?? "unknown";
|
|
145
|
+
const statusIcon = status === "completed" ? "✅" : status === "failed" ? "❌" : status === "cancelled" ? "⏹️" : "⚠️";
|
|
146
|
+
lines.push(`| \`${run.runId.slice(-8)}\` | ${goalPreview}${goalSuffix} | ${duration} | ${statusIcon} ${status} |`);
|
|
147
|
+
}
|
|
148
|
+
lines.push("");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// How to run
|
|
152
|
+
lines.push("## How to Run");
|
|
153
|
+
lines.push("");
|
|
154
|
+
lines.push("```bash");
|
|
155
|
+
lines.push(`team action='run' team='${team}' goal="<your goal>"`);
|
|
156
|
+
lines.push("```");
|
|
157
|
+
lines.push("");
|
|
158
|
+
lines.push("See `team action='help'` for more options.");
|
|
159
|
+
lines.push("");
|
|
160
|
+
|
|
161
|
+
// Teams
|
|
162
|
+
lines.push("## Available Teams");
|
|
163
|
+
lines.push("");
|
|
164
|
+
try {
|
|
165
|
+
const discovery = discoverTeams(cwd);
|
|
166
|
+
const teams = allTeams(discovery);
|
|
167
|
+
for (const t of teams) {
|
|
168
|
+
lines.push(`- \`${t.name}\` — ${t.description ?? "No description"}`);
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
lines.push("- *(team discovery unavailable)*");
|
|
172
|
+
}
|
|
173
|
+
lines.push("");
|
|
174
|
+
|
|
175
|
+
// Footer
|
|
176
|
+
lines.push("---");
|
|
177
|
+
lines.push("*Generated by pi-crew. For up-to-date info, check recent run history.*");
|
|
178
|
+
|
|
179
|
+
return lines.join("\n");
|
|
180
|
+
}
|