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,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context-status-injection.ts — Ambient crew-status injection (GAP-2).
|
|
3
|
+
*
|
|
4
|
+
* Registers a `context` event handler that keeps the parent agent continuously
|
|
5
|
+
* aware of in-flight crew runs. Without this, the agent "forgets" about active
|
|
6
|
+
* runs between turns unless it explicitly calls the `team` tool.
|
|
7
|
+
*
|
|
8
|
+
* ## How it works
|
|
9
|
+
*
|
|
10
|
+
* Pi's `context` event fires before EVERY LLM call (see Pi source
|
|
11
|
+
* `extensions/runner.ts:emitContext`). The handler receives the full messages
|
|
12
|
+
* array and may return a modified copy. Critically, the returned messages are
|
|
13
|
+
* used ONLY for that single LLM call (`agent-loop.ts:283-289` feeds the result
|
|
14
|
+
* straight into `convertToLlm` for the request) — they do NOT mutate the
|
|
15
|
+
* agent's persistent `state.messages`. So injection is transient per-call:
|
|
16
|
+
* - No accumulation across turns (the note never enters history).
|
|
17
|
+
* - No need to dedup against prior injections.
|
|
18
|
+
* - No risk of corrupting the conversation transcript.
|
|
19
|
+
*
|
|
20
|
+
* The injected note is a compact 1–4 line ambient status, inserted BEFORE the
|
|
21
|
+
* last message so the last message remains the active turn driver (preserves
|
|
22
|
+
* the user/assistant/tool alternation the LLMs expect).
|
|
23
|
+
*
|
|
24
|
+
* ## Safety
|
|
25
|
+
*
|
|
26
|
+
* - No-op when zero runs are in-flight (returns undefined → Pi uses original
|
|
27
|
+
* messages unchanged). Normal single-agent operation is completely unaffected.
|
|
28
|
+
* - `emitContext` already wraps handlers in try/catch and emits errors instead
|
|
29
|
+
* of crashing the loop (Pi `runner.ts:933`), so a throw here can't break the
|
|
30
|
+
* agent — but we also guard defensively.
|
|
31
|
+
* - Opt-out: `runtime.reliability.ambientStatusInjection: false` in config.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
35
|
+
import type { Message } from "@earendil-works/pi-ai";
|
|
36
|
+
import type { ContextEvent, ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
37
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
38
|
+
import { extractSessionId } from "../utils/session-utils.ts";
|
|
39
|
+
import { collectInFlightRuns } from "./registration/compaction-guard.ts";
|
|
40
|
+
|
|
41
|
+
/** Sentinel that marks an injected ambient-status user message. */
|
|
42
|
+
export const AMBIENT_STATUS_SENTINEL = "[pi-crew ambient status";
|
|
43
|
+
|
|
44
|
+
/** Cap the number of runs listed inline to keep the note compact. */
|
|
45
|
+
const MAX_INLINE_RUNS = 3;
|
|
46
|
+
/** Truncate long goals so one run can't dominate the context window. */
|
|
47
|
+
const MAX_GOAL_LEN = 80;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Cheap human-readable run age from manifest timestamps (no extra I/O).
|
|
51
|
+
* Returns "running 12m" / "updated 3m ago" style, or "" if timestamps are
|
|
52
|
+
* missing/invalid. Keeps the ambient note informative without reading
|
|
53
|
+
* tasks.json on every LLM call.
|
|
54
|
+
*/
|
|
55
|
+
function runAge(createdAt?: string, updatedAt?: string): string {
|
|
56
|
+
try {
|
|
57
|
+
const updated = updatedAt ? Date.parse(updatedAt) : NaN;
|
|
58
|
+
const created = createdAt ? Date.parse(createdAt) : NaN;
|
|
59
|
+
if (Number.isFinite(updated)) {
|
|
60
|
+
const sinceUpdate = Date.now() - updated;
|
|
61
|
+
if (sinceUpdate < 60_000) return `, updated just now`;
|
|
62
|
+
return `, updated ${humanizeMs(sinceUpdate)} ago`;
|
|
63
|
+
}
|
|
64
|
+
if (Number.isFinite(created)) {
|
|
65
|
+
return `, running ${humanizeMs(Date.now() - created)}`;
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
/* ignore malformed timestamps */
|
|
69
|
+
}
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function humanizeMs(ms: number): string {
|
|
74
|
+
if (ms < 60_000) return `${Math.round(ms / 1000)}s`;
|
|
75
|
+
const m = Math.floor(ms / 60_000);
|
|
76
|
+
if (m < 60) return `${m}m`;
|
|
77
|
+
const h = Math.floor(m / 60);
|
|
78
|
+
return h < 24 ? `${h}h${m % 60}m` : `${Math.floor(h / 24)}d`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Build a compact, human+LLM-readable ambient status string for the given
|
|
83
|
+
* in-flight runs. Returns "" for an empty list (caller treats as no-op).
|
|
84
|
+
*
|
|
85
|
+
* Exported for unit testing.
|
|
86
|
+
*/
|
|
87
|
+
export function formatAmbientStatus(runs: TeamRunManifest[]): string {
|
|
88
|
+
if (runs.length === 0) return "";
|
|
89
|
+
const truncate = (s: string, n: number): string => (s.length > n ? `${s.slice(0, n - 1)}…` : s);
|
|
90
|
+
const lines: string[] = [
|
|
91
|
+
`${AMBIENT_STATUS_SENTINEL} — environmental context, not a user request]`,
|
|
92
|
+
`${runs.length} pi-crew run${runs.length === 1 ? "" : "s"} in flight:`,
|
|
93
|
+
];
|
|
94
|
+
const shown = runs.slice(0, MAX_INLINE_RUNS);
|
|
95
|
+
for (const run of shown) {
|
|
96
|
+
const wf = run.workflow ? `, ${run.workflow}` : "";
|
|
97
|
+
const age = runAge(run.createdAt, run.updatedAt);
|
|
98
|
+
lines.push(`• ${run.runId} (${run.status}, ${run.team}${wf})${age}: ${truncate(run.goal ?? "(no goal)", MAX_GOAL_LEN)}`);
|
|
99
|
+
}
|
|
100
|
+
if (runs.length > MAX_INLINE_RUNS) {
|
|
101
|
+
lines.push(`• …and ${runs.length - MAX_INLINE_RUNS} more`);
|
|
102
|
+
}
|
|
103
|
+
lines.push(
|
|
104
|
+
'Inspect/join via the `team` tool: action="status" (list), action="wait" (join running), action="summary"/action="get" (results).',
|
|
105
|
+
);
|
|
106
|
+
return lines.join("\n");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Construct a user-role AgentMessage carrying the ambient status. Uses the
|
|
111
|
+
* `user` role (the Message union has no `system` role — the system prompt is a
|
|
112
|
+
* separate field). The sentinel prefix signals to the model that this is
|
|
113
|
+
* environmental information, not a typed user instruction.
|
|
114
|
+
*
|
|
115
|
+
* Exported for unit testing.
|
|
116
|
+
*/
|
|
117
|
+
export function buildStatusMessage(runs: TeamRunManifest[]): Message {
|
|
118
|
+
return {
|
|
119
|
+
role: "user",
|
|
120
|
+
content: [{ type: "text", text: formatAmbientStatus(runs) }],
|
|
121
|
+
timestamp: Date.now(),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Result type for the `context` event handler (mirrors Pi's ContextEventResult,
|
|
126
|
+
* which isn't re-exported from the coding-agent package entry). */
|
|
127
|
+
export interface AmbientContextResult {
|
|
128
|
+
messages?: AgentMessage[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Core handler logic, separated from the Pi registration so it is trivially
|
|
133
|
+
* unit-testable without a live ExtensionAPI.
|
|
134
|
+
*
|
|
135
|
+
* Returns `{messages}` with the ambient status inserted before the last
|
|
136
|
+
* message, or `undefined` to leave the context untouched (no in-flight runs).
|
|
137
|
+
*
|
|
138
|
+
* Exported for unit testing.
|
|
139
|
+
*/
|
|
140
|
+
export function handleContextEvent(event: ContextEvent, cwd: string, sessionId?: string): AmbientContextResult | undefined {
|
|
141
|
+
let runs: TeamRunManifest[] = [];
|
|
142
|
+
try {
|
|
143
|
+
runs = collectInFlightRuns(cwd, sessionId);
|
|
144
|
+
} catch {
|
|
145
|
+
// State read failure → don't inject, don't crash. Pi catches handler
|
|
146
|
+
// errors anyway, but we avoid noisy error emission for a best-effort
|
|
147
|
+
// awareness feature.
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
if (runs.length === 0) return undefined;
|
|
151
|
+
|
|
152
|
+
const messages = [...event.messages];
|
|
153
|
+
const statusMsg = buildStatusMessage(runs);
|
|
154
|
+
// Insert BEFORE the last message so the genuine last message (the current
|
|
155
|
+
// turn driver — user prompt or tool result) stays last. When there are 0–1
|
|
156
|
+
// messages, appending is the only sensible option.
|
|
157
|
+
const insertAt = messages.length > 1 ? messages.length - 1 : messages.length;
|
|
158
|
+
messages.splice(insertAt, 0, statusMsg as unknown as AgentMessage);
|
|
159
|
+
return { messages };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Register the ambient-status `context` event handler. Reads the project cwd
|
|
164
|
+
* from the session context on each call (crew state is per-project).
|
|
165
|
+
*
|
|
166
|
+
* Pass `enabled: false` (from `runtime.reliability.ambientStatusInjection`) to
|
|
167
|
+
* disable the feature without unwiring the handler.
|
|
168
|
+
*/
|
|
169
|
+
export function registerContextStatusInjection(pi: ExtensionAPI, opts: { enabled?: boolean } = {}): void {
|
|
170
|
+
if (opts.enabled === false) return;
|
|
171
|
+
pi.on("context", (event: ContextEvent, ctx: unknown): AmbientContextResult | undefined => {
|
|
172
|
+
// crew state is per-project; use the session ctx cwd when available,
|
|
173
|
+
// falling back to process.cwd(). Thread the session id so ambient
|
|
174
|
+
// status only reflects runs owned by THIS session (the state store is
|
|
175
|
+
// per-project, shared across sessions).
|
|
176
|
+
const cwd =
|
|
177
|
+
typeof ctx === "object" && ctx !== null && typeof (ctx as { cwd?: unknown }).cwd === "string"
|
|
178
|
+
? (ctx as { cwd: string }).cwd
|
|
179
|
+
: typeof process.cwd === "function"
|
|
180
|
+
? process.cwd()
|
|
181
|
+
: ".";
|
|
182
|
+
const sessionId = extractSessionId(ctx);
|
|
183
|
+
return handleContextEvent(event, cwd, sessionId);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crew editor autocomplete provider (Round 13 UX).
|
|
3
|
+
*
|
|
4
|
+
* Wraps Pi's built-in autocomplete provider and adds natural-language crew
|
|
5
|
+
* phrase completion: when the user types `crew <prefix>` or `team <prefix>`
|
|
6
|
+
* at the start of the input line, we suggest the matching phrases (e.g.
|
|
7
|
+
* "crew status → /team-status"). This teaches users the natural-language
|
|
8
|
+
* phrases that the input router (crew-input-router.ts) will rewrite on
|
|
9
|
+
* submit — so they discover the feature without reading docs.
|
|
10
|
+
*
|
|
11
|
+
* For any non-crew input we delegate to the wrapped (`current`) provider, so
|
|
12
|
+
* slash-command, file (`@`), and command-argument completion all keep working
|
|
13
|
+
* unchanged.
|
|
14
|
+
*/
|
|
15
|
+
import type { AutocompleteItem, AutocompleteProvider, AutocompleteSuggestions } from "@earendil-works/pi-tui";
|
|
16
|
+
import { CREW_PHRASES } from "./crew-input-router.ts";
|
|
17
|
+
|
|
18
|
+
/** Max phrases to suggest. */
|
|
19
|
+
const MAX_PHRASES = 12;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* If the text before the cursor is a crew-natural-language trigger, return the
|
|
23
|
+
* query word (the partial keyword after `crew `/`team `), or `undefined` when
|
|
24
|
+
* it is not a crew trigger.
|
|
25
|
+
*
|
|
26
|
+
* Triggers look like `crew ` or `team ` optionally followed by a partial word
|
|
27
|
+
* made of word characters, anchored at the start of the line.
|
|
28
|
+
*/
|
|
29
|
+
function extractCrewQuery(textBeforeCursor: string): string | undefined {
|
|
30
|
+
// Anchor at start of line; require the `crew|team` keyword + whitespace,
|
|
31
|
+
// then an optional partial word. We do NOT trigger mid-word on the keyword
|
|
32
|
+
// itself (e.g. "cre" alone is not a trigger) — the keyword must be complete.
|
|
33
|
+
const match = textBeforeCursor.match(/^(?:crew|team)\s+([\w-]*)$/i);
|
|
34
|
+
return match?.[1];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Filter the shared phrase list by a partial keyword prefix. */
|
|
38
|
+
export function suggestCrewPhrases(query: string): AutocompleteItem[] {
|
|
39
|
+
const q = query.toLowerCase();
|
|
40
|
+
// Phrases are keyed by their keyword after "crew "/"team " (or the bare
|
|
41
|
+
// word for "teams"). Build a lookup keyword per phrase.
|
|
42
|
+
const seen = new Set<string>();
|
|
43
|
+
const items: AutocompleteItem[] = [];
|
|
44
|
+
for (const entry of CREW_PHRASES) {
|
|
45
|
+
// Derive the autocomplete keyword: for "crew status" → "status";
|
|
46
|
+
// for "teams" → "teams".
|
|
47
|
+
const parts = entry.phrase.split(/\s+/);
|
|
48
|
+
const keyword = parts.length > 1 ? parts.slice(1).join(" ") : entry.phrase;
|
|
49
|
+
if (seen.has(entry.phrase)) continue;
|
|
50
|
+
if (q && !keyword.toLowerCase().startsWith(q)) continue;
|
|
51
|
+
seen.add(entry.phrase);
|
|
52
|
+
items.push({
|
|
53
|
+
value: entry.phrase,
|
|
54
|
+
label: entry.phrase,
|
|
55
|
+
description: `→ ${entry.command}`,
|
|
56
|
+
});
|
|
57
|
+
if (items.length >= MAX_PHRASES) break;
|
|
58
|
+
}
|
|
59
|
+
return items;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create a crew autocomplete provider that wraps `current`. When the input is
|
|
64
|
+
* a crew natural-language trigger, returns phrase suggestions; otherwise
|
|
65
|
+
* delegates to `current`.
|
|
66
|
+
*/
|
|
67
|
+
export function createCrewAutocompleteProvider(current: AutocompleteProvider): AutocompleteProvider {
|
|
68
|
+
return {
|
|
69
|
+
async getSuggestions(
|
|
70
|
+
lines: string[],
|
|
71
|
+
cursorLine: number,
|
|
72
|
+
cursorCol: number,
|
|
73
|
+
options: { signal: AbortSignal; force?: boolean },
|
|
74
|
+
): Promise<AutocompleteSuggestions | null> {
|
|
75
|
+
// Only trigger on the first line (Pi's main input is single-line;
|
|
76
|
+
// multiline editors are out of scope and would surprise the user).
|
|
77
|
+
if (cursorLine === 0) {
|
|
78
|
+
const currentLine = lines[cursorLine] ?? "";
|
|
79
|
+
const before = currentLine.slice(0, cursorCol);
|
|
80
|
+
const query = extractCrewQuery(before);
|
|
81
|
+
if (query !== undefined) {
|
|
82
|
+
const items = suggestCrewPhrases(query);
|
|
83
|
+
if (items.length > 0) {
|
|
84
|
+
// prefix = the full text to replace (e.g. "crew st").
|
|
85
|
+
// The default applyCompletion replaces the trailing
|
|
86
|
+
// `prefix`-length chars with item.value.
|
|
87
|
+
return { items, prefix: before };
|
|
88
|
+
}
|
|
89
|
+
// Triggered but no matches — return empty rather than
|
|
90
|
+
// falling through to file/command completion, so the user
|
|
91
|
+
// doesn't get a confusing file list while typing a phrase.
|
|
92
|
+
return { items: [], prefix: before };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return current.getSuggestions(lines, cursorLine, cursorCol, options);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
applyCompletion(
|
|
99
|
+
lines: string[],
|
|
100
|
+
cursorLine: number,
|
|
101
|
+
cursorCol: number,
|
|
102
|
+
item: AutocompleteItem,
|
|
103
|
+
prefix: string,
|
|
104
|
+
): { lines: string[]; cursorLine: number; cursorCol: number } {
|
|
105
|
+
// Delegate to the wrapped provider. For a non-slash, non-@ prefix
|
|
106
|
+
// the default applyCompletion replaces the trailing `prefix`-length
|
|
107
|
+
// chars with item.value — which is exactly the full phrase. This
|
|
108
|
+
// matches our prefix contract (prefix = full text to cursor).
|
|
109
|
+
return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
shouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean {
|
|
113
|
+
// Suppress file-completion trigger inside a crew phrase so the
|
|
114
|
+
// editor doesn't pop a file list over our phrase suggestions.
|
|
115
|
+
if (cursorLine === 0) {
|
|
116
|
+
const before = (lines[cursorLine] ?? "").slice(0, cursorCol);
|
|
117
|
+
if (extractCrewQuery(before) !== undefined) return false;
|
|
118
|
+
}
|
|
119
|
+
return current.shouldTriggerFileCompletion?.(lines, cursorLine, cursorCol) ?? true;
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Register the crew autocomplete provider on a Pi UI context. Safe to call once. */
|
|
125
|
+
export function registerCrewAutocomplete(ctx: {
|
|
126
|
+
ui?: {
|
|
127
|
+
addAutocompleteProvider?: (factory: (current: AutocompleteProvider) => AutocompleteProvider) => void;
|
|
128
|
+
};
|
|
129
|
+
}): void {
|
|
130
|
+
ctx.ui?.addAutocompleteProvider?.((current) => createCrewAutocompleteProvider(current));
|
|
131
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { cleanupAllTrackedTempDirs } from "../runtime/pi-args.ts";
|
|
3
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
4
|
+
|
|
5
|
+
// NOTE: globalProgressTracker import kept for documentation but not directly used
|
|
6
|
+
// since we don't have agent IDs to untrack. Actual progress clearing should be
|
|
7
|
+
// handled by the progress tracker itself on shutdown.
|
|
8
|
+
// import { globalProgressTracker } from "../runtime/progress-tracker.ts";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Registers cleanup handlers for graceful shutdown.
|
|
12
|
+
* Handles session_shutdown and SIGTERM/SIGHUP signals.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Module-level flag to ensure signal handlers are registered only once,
|
|
16
|
+
// even if registerCleanupHandler is called multiple times (e.g., on extension
|
|
17
|
+
// reload or during dev hot-reload). Without this, listeners stack up and
|
|
18
|
+
// cleanupChildProcesses fires N times on shutdown.
|
|
19
|
+
let signalHandlersRegistered = false;
|
|
20
|
+
|
|
21
|
+
interface ChildProcessInfo {
|
|
22
|
+
pid: number;
|
|
23
|
+
runId: string;
|
|
24
|
+
agentId: string;
|
|
25
|
+
startedAt: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class ChildProcessRegistry {
|
|
29
|
+
private processes = new Map<number, ChildProcessInfo>();
|
|
30
|
+
|
|
31
|
+
register(pid: number, runId: string, agentId: string): void {
|
|
32
|
+
this.processes.set(pid, { pid, runId, agentId, startedAt: Date.now() });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
unregister(pid: number): void {
|
|
36
|
+
this.processes.delete(pid);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getAllPids(): number[] {
|
|
40
|
+
return Array.from(this.processes.keys());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getInfo(pid: number): ChildProcessInfo | undefined {
|
|
44
|
+
return this.processes.get(pid);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
clear(): void {
|
|
48
|
+
this.processes.clear();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const childProcessRegistry = new ChildProcessRegistry();
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Optional UI timer disposal hook. Set by register.ts so the SIGTERM/SIGHUP
|
|
56
|
+
* signal handler can dispose terminal-status timers (idle re-assert loop +
|
|
57
|
+
* flash clear) that would otherwise keep the event loop alive.
|
|
58
|
+
*/
|
|
59
|
+
let terminalStatusDispose: (() => void) | undefined;
|
|
60
|
+
|
|
61
|
+
export function registerCleanupHandler(pi: ExtensionAPI, opts?: { disposeTerminalStatus?: () => void }): void {
|
|
62
|
+
// Store the latest dispose fn at module level so the signal handler
|
|
63
|
+
// (registered once) always uses the current one even across re-registrations.
|
|
64
|
+
terminalStatusDispose = opts?.disposeTerminalStatus;
|
|
65
|
+
// Handle session_shutdown event
|
|
66
|
+
pi.on("session_shutdown", async () => {
|
|
67
|
+
console.log("[pi-crew] Session shutdown - cleaning up resources");
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
// Kill all child-pi processes
|
|
71
|
+
await cleanupChildProcesses();
|
|
72
|
+
|
|
73
|
+
// Cleanup temp directories
|
|
74
|
+
await cleanupTempDirectories();
|
|
75
|
+
|
|
76
|
+
console.log("[pi-crew] Cleanup complete");
|
|
77
|
+
} catch (error) {
|
|
78
|
+
logInternalError("crew-cleanup.shutdown", error);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Register signal handlers exactly once, even if registerCleanupHandler
|
|
83
|
+
// is called multiple times. This prevents listener stacking on extension
|
|
84
|
+
// reload and avoids double-cleanup on shutdown.
|
|
85
|
+
if (!signalHandlersRegistered) {
|
|
86
|
+
signalHandlersRegistered = true;
|
|
87
|
+
const handleSignal = async (signal: string): Promise<void> => {
|
|
88
|
+
console.log(`[pi-crew] Received ${signal} - starting cleanup`);
|
|
89
|
+
// Dispose UI timers so the idle re-assert loop / flash clear don't
|
|
90
|
+
// keep the event loop alive after child processes are killed.
|
|
91
|
+
try {
|
|
92
|
+
terminalStatusDispose?.();
|
|
93
|
+
} catch {
|
|
94
|
+
// Best-effort — must not block child cleanup on signal.
|
|
95
|
+
}
|
|
96
|
+
await cleanupChildProcesses();
|
|
97
|
+
};
|
|
98
|
+
process.on("SIGTERM", () => {
|
|
99
|
+
handleSignal("SIGTERM").catch((error) => {
|
|
100
|
+
logInternalError("crew-cleanup.SIGTERM", error);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
process.on("SIGHUP", () => {
|
|
104
|
+
handleSignal("SIGHUP").catch((error) => {
|
|
105
|
+
logInternalError("crew-cleanup.SIGHUP", error);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function cleanupChildProcesses(): Promise<void> {
|
|
112
|
+
const pids = childProcessRegistry.getAllPids();
|
|
113
|
+
if (pids.length === 0) return;
|
|
114
|
+
// Lazy-import to avoid eagerly pulling the heavy runtime/child-pi chain into the
|
|
115
|
+
// extension load path (check-lazy-imports) AND to avoid a static import cycle
|
|
116
|
+
// (child-pi.ts already imports registerChildProcess from this module).
|
|
117
|
+
// LAZY: defer child-pi.ts load (heavy runtime chain) + avoid static import cycle.
|
|
118
|
+
const { killProcessPid } = await import("../runtime/child-pi.ts");
|
|
119
|
+
for (const pid of pids) {
|
|
120
|
+
// killProcessPid signals the whole process GROUP (-pid on POSIX, taskkill /T on
|
|
121
|
+
// Windows) and escalates to SIGKILL after HARD_KILL_MS. A bare
|
|
122
|
+
// process.kill(pid, "SIGTERM") only reaches the direct child — grandchildren
|
|
123
|
+
// (bash/MCP the worker spawned) in the child's group survive as orphans, and
|
|
124
|
+
// a child that lags SIGTERM would linger forever with no escalation.
|
|
125
|
+
// NOTE: on graceful session_shutdown (reason=quit/reload) register.ts ALSO runs
|
|
126
|
+
// terminateActiveChildPiProcesses(); both are idempotent for a dying pid. This
|
|
127
|
+
// signal handler (SIGTERM/SIGHUP) is the SOLE teardown path when the Pi process
|
|
128
|
+
// is killed by signal, so it must be robust on its own.
|
|
129
|
+
try {
|
|
130
|
+
killProcessPid(pid);
|
|
131
|
+
console.log(`[pi-crew] Sent group SIGTERM (+SIGKILL escalation) to child process ${pid}`);
|
|
132
|
+
} catch (error: unknown) {
|
|
133
|
+
// Process may already be dead or not exist
|
|
134
|
+
const err = error as NodeJS.ErrnoException;
|
|
135
|
+
if (err.code !== "ESRCH" && err.code !== "ENOENT") {
|
|
136
|
+
logInternalError("crew-cleanup.kill", error, `pid=${pid}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
childProcessRegistry.unregister(pid);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Clear progress tracker
|
|
143
|
+
// Note: Can't call untrack on all because we don't track agent IDs here
|
|
144
|
+
// The progress tracker should clear itself on shutdown via session_dispose
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function cleanupTempDirectories(): Promise<void> {
|
|
148
|
+
// Clean up every temp dir created in this process. Previously this was
|
|
149
|
+
// a stub that just logged; it caused /tmp/pi-crew-* dirs to accumulate
|
|
150
|
+
// from killed test runs and child-pi invocations. See issue #<n>.
|
|
151
|
+
try {
|
|
152
|
+
const result = cleanupAllTrackedTempDirs();
|
|
153
|
+
if (result.cleaned > 0) {
|
|
154
|
+
console.log(`[pi-crew] Cleaned ${result.cleaned} tracked temp dirs (${result.failed} failed)`);
|
|
155
|
+
}
|
|
156
|
+
} catch (error) {
|
|
157
|
+
logInternalError("crew-cleanup.temp", error);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Export for child-pi.ts to register processes
|
|
162
|
+
export function registerChildProcess(pid: number, runId: string, agentId: string): void {
|
|
163
|
+
childProcessRegistry.register(pid, runId, agentId);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function unregisterChildProcess(pid: number): void {
|
|
167
|
+
childProcessRegistry.unregister(pid);
|
|
168
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Natural-language crew input routing (Round 13 UX).
|
|
3
|
+
*
|
|
4
|
+
* Pi fires the `input` event before skill/template expansion and before
|
|
5
|
+
* before_agent_start. A handler can transform the text (e.g. rewrite
|
|
6
|
+
* "crew status" → "/team-status"), or fully handle it.
|
|
7
|
+
*
|
|
8
|
+
* This module matches a small set of natural-language crew phrases and
|
|
9
|
+
* rewrites them to the equivalent slash command, so users do not need to
|
|
10
|
+
* memorize command names. Slash-command input (text starting with "/") is
|
|
11
|
+
* always passed through unchanged — we never shadow explicit commands.
|
|
12
|
+
*/
|
|
13
|
+
import type { InputEvent, InputEventResult } from "@earendil-works/pi-coding-agent";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Natural-language crew phrases → slash-command mapping.
|
|
17
|
+
*
|
|
18
|
+
* Single source of truth shared by:
|
|
19
|
+
* - the `input`-event router (rewrites submitted text), and
|
|
20
|
+
* - the editor autocomplete provider (suggests phrases as you type).
|
|
21
|
+
*
|
|
22
|
+
* Each entry maps a phrase (what the user types) to a slash command.
|
|
23
|
+
* The router matches when submitted text STARTS WITH a phrase (word boundary);
|
|
24
|
+
* the autocomplete matches when the line starts with `crew `/`team ` and the
|
|
25
|
+
* partial word is a prefix of a phrase's keyword.
|
|
26
|
+
*/
|
|
27
|
+
export const CREW_PHRASES: ReadonlyArray<{ phrase: string; command: string }> = [
|
|
28
|
+
{ phrase: "crew status", command: "/team-status" },
|
|
29
|
+
{ phrase: "crew list", command: "/team-status" },
|
|
30
|
+
{ phrase: "crew dashboard", command: "/team-dashboard" },
|
|
31
|
+
{ phrase: "crew board", command: "/team-dashboard" },
|
|
32
|
+
{ phrase: "crew panel", command: "/team-dashboard" },
|
|
33
|
+
{ phrase: "crew help", command: "/team-help" },
|
|
34
|
+
{ phrase: "crew commands", command: "/team-help" },
|
|
35
|
+
{ phrase: "crew doctor", command: "/team-doctor" },
|
|
36
|
+
{ phrase: "crew diagnose", command: "/team-doctor" },
|
|
37
|
+
{ phrase: "teams", command: "/teams" },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build a case-insensitive anchored regex from a phrase. The leading `crew `
|
|
42
|
+
* keyword is treated as interchangeable with `team ` (so "crew status" matches
|
|
43
|
+
* both "crew status" and "team status"). Bare phrases like "teams" match
|
|
44
|
+
* verbatim.
|
|
45
|
+
*/
|
|
46
|
+
function phraseToRegex(phrase: string): RegExp {
|
|
47
|
+
const kw = phrase.match(/^(crew|team)\s+(.*)$/i);
|
|
48
|
+
if (kw) {
|
|
49
|
+
const rest = kw[2].replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
50
|
+
return new RegExp(`^(?:crew|team)\\s+${rest}\\b`, "i");
|
|
51
|
+
}
|
|
52
|
+
const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
53
|
+
return new RegExp(`^${escaped}\\b`, "i");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Try to rewrite a natural-language crew phrase into a slash command.
|
|
58
|
+
* Returns the rewritten command string, or `null` if no rule matches.
|
|
59
|
+
*
|
|
60
|
+
* Rules intentionally only match at the START of the input and require a
|
|
61
|
+
* word boundary, so ordinary sentences mentioning "crew" are untouched.
|
|
62
|
+
*/
|
|
63
|
+
export function rewriteCrewInput(text: string): string | null {
|
|
64
|
+
const trimmed = text.trim();
|
|
65
|
+
// Never transform explicit slash commands or inputs that don't start with
|
|
66
|
+
// a crew/team keyword phrase.
|
|
67
|
+
if (trimmed.startsWith("/")) return null;
|
|
68
|
+
for (const entry of CREW_PHRASES) {
|
|
69
|
+
const match = trimmed.match(phraseToRegex(entry.phrase));
|
|
70
|
+
if (!match) continue;
|
|
71
|
+
// Carry any remaining args after the matched phrase forward.
|
|
72
|
+
const rest = trimmed.slice(match[0].length).trim();
|
|
73
|
+
return rest ? `${entry.command} ${rest}` : entry.command;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Pi `input` event handler. Transforms matching crew phrases; passes
|
|
80
|
+
* everything else through unchanged.
|
|
81
|
+
*/
|
|
82
|
+
export function handleCrewInput(event: InputEvent): InputEventResult {
|
|
83
|
+
// Only transform interactive user input — never programmatic/scripted input.
|
|
84
|
+
if (event.source !== "interactive") return { action: "continue" };
|
|
85
|
+
const rewritten = rewriteCrewInput(event.text);
|
|
86
|
+
if (!rewritten) return { action: "continue" };
|
|
87
|
+
return { action: "transform", text: rewritten, images: event.images };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Register the crew input router on a Pi instance. Safe to call once. */
|
|
91
|
+
export function registerCrewInputRouter(pi: { on?: (event: "input", handler: (e: InputEvent) => InputEventResult) => void }): void {
|
|
92
|
+
pi.on?.("input", handleCrewInput);
|
|
93
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crew keyboard shortcuts (Round 13 UX).
|
|
3
|
+
*
|
|
4
|
+
* Registers a small set of keyboard shortcuts for fast access to the most
|
|
5
|
+
* useful pi-crew overlays. Keys are chosen to avoid collisions with Pi's
|
|
6
|
+
* built-in keymap (see analysis of pi-tui core/keybindings defaults):
|
|
7
|
+
*
|
|
8
|
+
* alt+s → open the pi-crew settings overlay (config + theme picker)
|
|
9
|
+
* alt+c → open the pi-crew run dashboard overlay (mnemonic: **C**rew)
|
|
10
|
+
*
|
|
11
|
+
* OCCUPIED alt+ keys in the built-in keymap (must NOT reuse — verified against
|
|
12
|
+
* pi-tui TUI_KEYBINDINGS + pi core keybindings.js):
|
|
13
|
+
* alt+b (cursor word left) alt+f (cursor word right)
|
|
14
|
+
* alt+d (delete word forward) alt+y (yank pop)
|
|
15
|
+
* alt+v (paste) alt+s (crew settings — this module)
|
|
16
|
+
* alt+enter / alt+up/down/left/right / alt+backspace / alt+delete
|
|
17
|
+
* Free alt+<letter> keys include: a, c, e, g, h, i, j, k, l, m, n, o, p, q,
|
|
18
|
+
* r, t, u, w, x, z.
|
|
19
|
+
*
|
|
20
|
+
* NOTE: an earlier revision used `alt+d` for the dashboard; that collided
|
|
21
|
+
* with `tui.editor.deleteWordForward` and Pi's conflict detector stripped the
|
|
22
|
+
* editor binding. `alt+c` is free AND mnemonic.
|
|
23
|
+
*
|
|
24
|
+
* NOTE: alt+m (mailbox) and alt+t (status) were considered but are NOT wired
|
|
25
|
+
* — the mailbox overlay is run-scoped (requires a runId; reached via the
|
|
26
|
+
* dashboard) and there is no standalone status overlay (status is a text
|
|
27
|
+
* command). See the K-2 note accompanying openTeamDashboard in commands.ts.
|
|
28
|
+
*
|
|
29
|
+
* Shortcuts are guarded by `hasUI` so they never fire in print/RPC mode, and
|
|
30
|
+
* by the optional `registerShortcut` API so older Pi versions degrade
|
|
31
|
+
* gracefully (no-op).
|
|
32
|
+
*/
|
|
33
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
34
|
+
import type { KeyId } from "@earendil-works/pi-tui";
|
|
35
|
+
|
|
36
|
+
type ShortcutHandler = (ctx: ExtensionContext) => Promise<void> | void;
|
|
37
|
+
|
|
38
|
+
interface ShortcutRegistration {
|
|
39
|
+
/** Pi KeyId, e.g. "alt+s". */
|
|
40
|
+
key: KeyId;
|
|
41
|
+
description: string;
|
|
42
|
+
handler: ShortcutHandler;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const CREW_SHORTCUTS: ReadonlyArray<ShortcutRegistration> = [
|
|
46
|
+
{
|
|
47
|
+
key: "alt+s",
|
|
48
|
+
description: "pi-crew: open settings (config + theme picker)",
|
|
49
|
+
// Lazy-import the overlay so this module stays lightweight at load time
|
|
50
|
+
// (avoids pulling the full commands.ts dependency tree into every
|
|
51
|
+
// process that imports this module, e.g. the unit test).
|
|
52
|
+
handler: async (ctx) => {
|
|
53
|
+
// LAZY: defer dynamic import of ./registration/commands.ts to its call site.
|
|
54
|
+
const { openTeamSettingsOverlay } = await import("./registration/commands.ts");
|
|
55
|
+
await openTeamSettingsOverlay(ctx);
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
key: "alt+c",
|
|
60
|
+
description: "pi-crew: open run dashboard (Crew)",
|
|
61
|
+
// Lazy-import so the heavy UI module chain (RunDashboard etc.) is only
|
|
62
|
+
// loaded on first use, not at extension load.
|
|
63
|
+
handler: async (ctx) => {
|
|
64
|
+
// LAZY: defer dynamic import of ./registration/commands.ts to its call site.
|
|
65
|
+
const { openTeamDashboard } = await import("./registration/commands.ts");
|
|
66
|
+
await openTeamDashboard(ctx);
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Register all crew keyboard shortcuts on a Pi instance. Safe to call once at
|
|
73
|
+
* extension load. No-ops when `registerShortcut` is unavailable (older Pi).
|
|
74
|
+
*/
|
|
75
|
+
export function registerCrewShortcuts(pi: {
|
|
76
|
+
registerShortcut?: (shortcut: KeyId, options: { description?: string; handler: ShortcutHandler }) => void;
|
|
77
|
+
}): void {
|
|
78
|
+
for (const sc of CREW_SHORTCUTS) {
|
|
79
|
+
pi.registerShortcut?.(sc.key, {
|
|
80
|
+
description: sc.description,
|
|
81
|
+
handler: sc.handler,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Exported for tests / introspection. */
|
|
87
|
+
export const CREW_SHORTCUT_KEYS: readonly KeyId[] = CREW_SHORTCUTS.map((s) => s.key);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ANSI art cat animation frames extracted from runner-spritesheet.png.
|
|
3
|
+
* Used when PUA font glyphs are not available (web terminals like gotty).
|
|
4
|
+
* Each frame is an array of strings (one per line).
|
|
5
|
+
*/
|
|
6
|
+
export const CAT_FRAMES: readonly string[][] = [
|
|
7
|
+
// Frame 0 — standing
|
|
8
|
+
["▄▄ ███▄ ", "▀▀█████▄", " ██▀██ ", " ▀▀ ▀▀"],
|
|
9
|
+
// Frame 1 — step 1
|
|
10
|
+
[" ████ ", " █████▄", "▄██████▀", " ██ ▀█▄"],
|
|
11
|
+
// Frame 2 — step 2
|
|
12
|
+
[" ▄██████", " █████▀ ", "███████ ", "▀██████ "],
|
|
13
|
+
// Frame 3 — step 3
|
|
14
|
+
[" ████ ", "▄█████▄ ", "▀██████▄", "▄█▀ ██ "],
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
/** Number of animation frames. */
|
|
18
|
+
export const CAT_FRAME_COUNT = CAT_FRAMES.length;
|