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,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 1.5 worker-thread atomic writer (RFC: 15-PHASE1.5-WORKER-WRITER-RFC.md).
|
|
3
|
+
*
|
|
4
|
+
* Background: multi-step goal-wrapped workflows crash silently and
|
|
5
|
+
* non-deterministically during batch transitions. The crash point moves to
|
|
6
|
+
* every `await` yield in the write path (mkdir, open, stat, rename,
|
|
7
|
+
* appendEvent). Sync replacements regress. Hypothesis: V8/libuv-level race
|
|
8
|
+
* during event-loop yields. Mitigation: route writes through a dedicated
|
|
9
|
+
* worker thread that performs SYNC fs operations with no internal yields.
|
|
10
|
+
*
|
|
11
|
+
* Opt-in via `PI_CREW_WORKER_ATOMIC_WRITER=1`. When disabled, callers fall
|
|
12
|
+
* back to the regular async path. Safe to ship behind a flag.
|
|
13
|
+
*
|
|
14
|
+
* Protocol (main → worker):
|
|
15
|
+
* { kind: "write", id, filePath, content }
|
|
16
|
+
* { kind: "mkdir", id, dirPath }
|
|
17
|
+
* { kind: "append", id, filePath, content }
|
|
18
|
+
*
|
|
19
|
+
* Protocol (worker → main):
|
|
20
|
+
* { kind: "done", id }
|
|
21
|
+
* { kind: "error", id, message }
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import * as fs from "node:fs";
|
|
25
|
+
import { createRequire } from "node:module";
|
|
26
|
+
import * as path from "node:path";
|
|
27
|
+
import { Worker } from "node:worker_threads";
|
|
28
|
+
|
|
29
|
+
const require = createRequire(import.meta.url);
|
|
30
|
+
|
|
31
|
+
let worker: Worker | undefined;
|
|
32
|
+
let nextRequestId = 1;
|
|
33
|
+
const pending = new Map<number, { resolve: () => void; reject: (e: Error) => void }>();
|
|
34
|
+
|
|
35
|
+
/** Worker script source — runs SYNC fs ops with no internal yields. */
|
|
36
|
+
const WORKER_SOURCE = `
|
|
37
|
+
const { parentPort, workerData } = require("node:worker_threads");
|
|
38
|
+
const fs = require("node:fs");
|
|
39
|
+
const path = require("node:path");
|
|
40
|
+
const crypto = require("node:crypto");
|
|
41
|
+
|
|
42
|
+
function isSymlinkSafePath(filePath) {
|
|
43
|
+
try {
|
|
44
|
+
// Safe roots: the system temp dir (resolved through any symlinks, e.g.
|
|
45
|
+
// macOS /var → /private/var) and the current project cwd.
|
|
46
|
+
var tmpReal;
|
|
47
|
+
try { tmpReal = fs.realpathSync(require("node:os").tmpdir()); } catch (e2) { tmpReal = require("node:os").tmpdir(); }
|
|
48
|
+
var cwd = process.cwd();
|
|
49
|
+
// Accept a symlink ancestor as safe if the resolved target (a) IS a safe
|
|
50
|
+
// root, (b) is UNDER a safe root (target is a descendant), or (c) is an
|
|
51
|
+
// ANCESTOR of a safe root (target is a parent dir of a safe root).
|
|
52
|
+
// Case (c) is essential on macOS: /var → /private/var is an ancestor of
|
|
53
|
+
// the tmpdir /private/var/folders/…, so it must be allowed.
|
|
54
|
+
var isSafeAncestor = function (real) {
|
|
55
|
+
var roots = [tmpReal, cwd];
|
|
56
|
+
return roots.some(function (root) {
|
|
57
|
+
return real === root
|
|
58
|
+
|| real.indexOf(root + path.sep) === 0
|
|
59
|
+
|| root.indexOf(real + path.sep) === 0;
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
var currentPath = filePath;
|
|
63
|
+
while (currentPath !== path.dirname(currentPath)) {
|
|
64
|
+
var dir = path.dirname(currentPath);
|
|
65
|
+
try {
|
|
66
|
+
var stat = fs.lstatSync(dir);
|
|
67
|
+
if (stat.isSymbolicLink()) {
|
|
68
|
+
var real = fs.realpathSync(dir);
|
|
69
|
+
if (!isSafeAncestor(real)) return false;
|
|
70
|
+
}
|
|
71
|
+
} catch (e3) { /* not found — fine */ }
|
|
72
|
+
currentPath = dir;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
} catch (e) { return true; }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function syncAtomicWriteFile(filePath, content) {
|
|
79
|
+
if (!isSymlinkSafePath(filePath)) throw new Error("Refusing to write: unsafe path: " + filePath);
|
|
80
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
81
|
+
const tempPath = filePath + "." + crypto.randomUUID() + ".tmp";
|
|
82
|
+
try {
|
|
83
|
+
const fd = fs.openSync(tempPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600);
|
|
84
|
+
try {
|
|
85
|
+
fs.writeFileSync(fd, content, "utf-8");
|
|
86
|
+
} finally {
|
|
87
|
+
fs.closeSync(fd);
|
|
88
|
+
}
|
|
89
|
+
fs.renameSync(tempPath, filePath);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
try { fs.rmSync(tempPath, { force: true }); } catch (cleanupError) {
|
|
92
|
+
logInternalError("worker-atomic-writer.cleanup-temp", cleanupError, "tempPath=" + tempPath, "debug");
|
|
93
|
+
}
|
|
94
|
+
// If rename raced with another writer that produced identical content, swallow.
|
|
95
|
+
if (error && error.code === "EEXIST") {
|
|
96
|
+
try {
|
|
97
|
+
const existing = fs.readFileSync(filePath, "utf-8");
|
|
98
|
+
if (existing === content) return;
|
|
99
|
+
} catch (compareError) {
|
|
100
|
+
logInternalError("worker-atomic-writer.compare-existing", compareError, "filePath=" + filePath, "debug");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function syncAppend(filePath, content) {
|
|
108
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
109
|
+
fs.appendFileSync(filePath, content, "utf-8");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
parentPort.on("message", (msg) => {
|
|
113
|
+
try {
|
|
114
|
+
if (msg.kind === "write") syncAtomicWriteFile(msg.filePath, msg.content);
|
|
115
|
+
else if (msg.kind === "mkdir") fs.mkdirSync(msg.dirPath, { recursive: true });
|
|
116
|
+
else if (msg.kind === "append") syncAppend(msg.filePath, msg.content);
|
|
117
|
+
else throw new Error("worker-atomic-writer: unknown message kind: " + msg.kind);
|
|
118
|
+
parentPort.postMessage({ kind: "done", id: msg.id });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
parentPort.postMessage({ kind: "error", id: msg.id, message: error && error.message ? error.message : String(error) });
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
`;
|
|
124
|
+
|
|
125
|
+
function getWorker(): Worker {
|
|
126
|
+
if (worker) return worker;
|
|
127
|
+
// Write worker source to a temp file (so Worker can load it as CJS via
|
|
128
|
+
// require() inside the worker, which always has CommonJS available).
|
|
129
|
+
const os = require("node:os");
|
|
130
|
+
const tmpPath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "pi-crew-waw-")), "worker.cjs");
|
|
131
|
+
fs.writeFileSync(tmpPath, WORKER_SOURCE, "utf-8");
|
|
132
|
+
worker = new Worker(tmpPath);
|
|
133
|
+
worker.on("message", (msg: { kind: string; id: number; message?: string }) => {
|
|
134
|
+
const entry = pending.get(msg.id);
|
|
135
|
+
if (!entry) return;
|
|
136
|
+
pending.delete(msg.id);
|
|
137
|
+
if (msg.kind === "done") entry.resolve();
|
|
138
|
+
else entry.reject(new Error(msg.message ?? "worker-atomic-writer: unknown error"));
|
|
139
|
+
});
|
|
140
|
+
worker.on("error", (error: Error) => {
|
|
141
|
+
// Reject ALL pending requests — worker died.
|
|
142
|
+
for (const [, entry] of pending) entry.reject(error);
|
|
143
|
+
pending.clear();
|
|
144
|
+
worker = undefined;
|
|
145
|
+
});
|
|
146
|
+
worker.unref(); // don't keep event loop alive (unless tests request it)
|
|
147
|
+
if (keepRefForTests) worker.ref();
|
|
148
|
+
return worker;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function dispatch(kind: "write" | "mkdir" | "append", payload: Record<string, unknown>): Promise<void> {
|
|
152
|
+
return new Promise((resolve, reject) => {
|
|
153
|
+
const id = nextRequestId++;
|
|
154
|
+
pending.set(id, { resolve, reject });
|
|
155
|
+
try {
|
|
156
|
+
getWorker().postMessage({ kind, id, ...payload });
|
|
157
|
+
} catch (error) {
|
|
158
|
+
pending.delete(id);
|
|
159
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Whether the worker writer is enabled (env var opt-in). */
|
|
165
|
+
export function isWorkerAtomicWriterEnabled(): boolean {
|
|
166
|
+
return process.env.PI_CREW_WORKER_ATOMIC_WRITER === "1" || process.env.PI_TEAMS_WORKER_ATOMIC_WRITER === "1";
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Atomic-write a file via the worker thread. Sync fs ops inside worker. */
|
|
170
|
+
export function atomicWriteFileViaWorker(filePath: string, content: string): Promise<void> {
|
|
171
|
+
return dispatch("write", { filePath, content });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Append to a file via the worker thread (used by event-log). */
|
|
175
|
+
export function appendFileViaWorker(filePath: string, content: string): Promise<void> {
|
|
176
|
+
return dispatch("append", { filePath, content });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Terminate the worker (for tests / cleanup). */
|
|
180
|
+
export function terminateWorkerAtomicWriter(): void {
|
|
181
|
+
if (worker) {
|
|
182
|
+
const w = worker;
|
|
183
|
+
worker = undefined;
|
|
184
|
+
w.terminate().catch(() => {
|
|
185
|
+
/* ignore */
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
for (const [, entry] of pending) entry.reject(new Error("worker terminated"));
|
|
189
|
+
pending.clear();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Tests-only knob: keep the worker ref'd so the test runner doesn't exit
|
|
193
|
+
* before promises resolve. Production code leaves this false (worker is
|
|
194
|
+
* unref'd to avoid blocking process exit). */
|
|
195
|
+
let keepRefForTests = false;
|
|
196
|
+
export function __setKeepWorkerRefForTests(value: boolean): void {
|
|
197
|
+
keepRefForTests = value;
|
|
198
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../runtime/async-runner.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../../runtime/live-agent-control.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../../runtime/live-agent-manager.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../../runtime/live-control-realtime.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../../runtime/live-session-runtime.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../runtime/subagent-manager.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../runtime/child-pi.ts";
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { ResourceSource } from "../agents/agent-config.ts";
|
|
4
|
+
import { parseCsv, parseFrontmatter } from "../utils/frontmatter.ts";
|
|
5
|
+
import { parseGitUrl } from "../utils/git.ts";
|
|
6
|
+
import { packageRoot, projectCrewRoot, userPiRoot } from "../utils/paths.ts";
|
|
7
|
+
import type { TeamConfig, TeamRole } from "./team-config.ts";
|
|
8
|
+
|
|
9
|
+
export interface TeamDiscoveryResult {
|
|
10
|
+
builtin: TeamConfig[];
|
|
11
|
+
user: TeamConfig[];
|
|
12
|
+
project: TeamConfig[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function parseRoleSkills(value: string | undefined): string[] | false | undefined {
|
|
16
|
+
if (!value) return undefined;
|
|
17
|
+
if (value === "false") return false;
|
|
18
|
+
const skills = value
|
|
19
|
+
.split(",")
|
|
20
|
+
.map((entry) => entry.trim())
|
|
21
|
+
.filter(Boolean);
|
|
22
|
+
return skills.length ? skills : undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseRoleLine(line: string): TeamRole | undefined {
|
|
26
|
+
const trimmed = line.trim();
|
|
27
|
+
if (!trimmed.startsWith("-")) return undefined;
|
|
28
|
+
const value = trimmed.slice(1).trim();
|
|
29
|
+
if (!value) return undefined;
|
|
30
|
+
const separator = value.indexOf(":");
|
|
31
|
+
const namePart = separator >= 0 ? value.slice(0, separator) : value;
|
|
32
|
+
const restPart = separator >= 0 ? value.slice(separator + 1) : "";
|
|
33
|
+
const name = namePart.trim();
|
|
34
|
+
if (!name) return undefined;
|
|
35
|
+
const metadata: Record<string, string> = {};
|
|
36
|
+
let descriptionSource = restPart.replace(/\bskills\s*=\s*([\w-]+(?:\s*,\s*[\w-]+)*)/g, (_match, raw: string) => {
|
|
37
|
+
metadata.skills = raw.replace(/\s*,\s*/g, ",").trim();
|
|
38
|
+
return "";
|
|
39
|
+
});
|
|
40
|
+
descriptionSource = descriptionSource.replace(/\b(agent|model|maxConcurrency)\s*=\s*(\S+)/g, (_match, key: string, raw: string) => {
|
|
41
|
+
metadata[key] = raw.trim();
|
|
42
|
+
return "";
|
|
43
|
+
});
|
|
44
|
+
const description = descriptionSource.replace(/\s+/g, " ").trim() || undefined;
|
|
45
|
+
const maxConcurrency = metadata.maxConcurrency
|
|
46
|
+
? (() => {
|
|
47
|
+
const p = Number.parseInt(metadata.maxConcurrency, 10);
|
|
48
|
+
return p > 0 ? p : undefined;
|
|
49
|
+
})()
|
|
50
|
+
: undefined;
|
|
51
|
+
return {
|
|
52
|
+
name,
|
|
53
|
+
agent: metadata.agent ?? name,
|
|
54
|
+
description,
|
|
55
|
+
model: metadata.model,
|
|
56
|
+
skills: parseRoleSkills(metadata.skills),
|
|
57
|
+
maxConcurrency: maxConcurrency && maxConcurrency > 0 ? maxConcurrency : undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parseCost(value: string | undefined): "free" | "cheap" | "expensive" | undefined {
|
|
62
|
+
return value === "free" || value === "cheap" || value === "expensive" ? value : undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseTeamSource(
|
|
66
|
+
rawSource: string | undefined,
|
|
67
|
+
fallback: ResourceSource,
|
|
68
|
+
): { source: ResourceSource; sourceUrl: string | undefined } {
|
|
69
|
+
if (!rawSource) return { source: fallback, sourceUrl: undefined };
|
|
70
|
+
const parsed = parseGitUrl(rawSource);
|
|
71
|
+
if (!parsed) return { source: fallback, sourceUrl: undefined };
|
|
72
|
+
return { source: "git", sourceUrl: parsed.repo };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function parseTeamFile(filePath: string, source: ResourceSource): TeamConfig | undefined {
|
|
76
|
+
try {
|
|
77
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
78
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
79
|
+
const name = frontmatter.name?.trim() || path.basename(filePath, ".team.md");
|
|
80
|
+
const roles = body
|
|
81
|
+
.split("\n")
|
|
82
|
+
.map(parseRoleLine)
|
|
83
|
+
.filter((role): role is TeamRole => role !== undefined);
|
|
84
|
+
const triggers = parseCsv(frontmatter.triggers ?? frontmatter.trigger);
|
|
85
|
+
const useWhen = parseCsv(frontmatter.useWhen);
|
|
86
|
+
const avoidWhen = parseCsv(frontmatter.avoidWhen);
|
|
87
|
+
const cost = parseCost(frontmatter.cost);
|
|
88
|
+
const category = frontmatter.category?.trim() || undefined;
|
|
89
|
+
const sourceInfo = parseTeamSource(frontmatter.source, source);
|
|
90
|
+
return {
|
|
91
|
+
name,
|
|
92
|
+
description: frontmatter.description?.trim() || "No description provided.",
|
|
93
|
+
source: sourceInfo.source,
|
|
94
|
+
sourceUrl: sourceInfo.sourceUrl,
|
|
95
|
+
filePath,
|
|
96
|
+
roles,
|
|
97
|
+
defaultWorkflow: frontmatter.defaultWorkflow || frontmatter.workflow || undefined,
|
|
98
|
+
workspaceMode: frontmatter.workspaceMode?.trim() === "worktree" ? "worktree" : "single",
|
|
99
|
+
maxConcurrency: frontmatter.maxConcurrency ? Number.parseInt(frontmatter.maxConcurrency, 10) : undefined,
|
|
100
|
+
routing: triggers || useWhen || avoidWhen || cost || category ? { triggers, useWhen, avoidWhen, cost, category } : undefined,
|
|
101
|
+
};
|
|
102
|
+
} catch {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readTeamDir(dir: string, source: ResourceSource): TeamConfig[] {
|
|
108
|
+
if (!fs.existsSync(dir)) return [];
|
|
109
|
+
return fs
|
|
110
|
+
.readdirSync(dir)
|
|
111
|
+
.filter((entry) => entry.endsWith(".team.md"))
|
|
112
|
+
.map((entry) => parseTeamFile(path.join(dir, entry), source))
|
|
113
|
+
.filter((team): team is TeamConfig => team !== undefined)
|
|
114
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ─── Team Discovery Cache (F15) ──────────────────────────────────────────
|
|
118
|
+
// Mirrors the Workflow Discovery Cache (see workflows/discover-workflows.ts).
|
|
119
|
+
// `discoverTeams(cwd)` is called from recommend/scheduler paths and previously
|
|
120
|
+
// re-scanned 3 roots on every call. 5 s TTL + dir-stamp invalidation keeps
|
|
121
|
+
// changes (e.g. user edits a `.team.md`) visible within a few seconds without
|
|
122
|
+
// paying a full re-scan per call.
|
|
123
|
+
const TEAM_DISCOVERY_TTL_MS = 5000;
|
|
124
|
+
const TEAM_DISCOVERY_MAX_ENTRIES = 32;
|
|
125
|
+
interface CachedTeamEntry {
|
|
126
|
+
result: TeamDiscoveryResult;
|
|
127
|
+
expiresAt: number;
|
|
128
|
+
dirStamp: string;
|
|
129
|
+
}
|
|
130
|
+
const teamCache = new Map<string, CachedTeamEntry>();
|
|
131
|
+
|
|
132
|
+
function teamDirStamp(cwd: string): string {
|
|
133
|
+
const dirs = [path.join(packageRoot(), "teams"), path.join(userPiRoot(), "teams"), path.join(projectCrewRoot(cwd), "teams")];
|
|
134
|
+
let out = "";
|
|
135
|
+
for (const d of dirs) {
|
|
136
|
+
try {
|
|
137
|
+
out += `${fs.statSync(d).mtimeMs}|`;
|
|
138
|
+
} catch {
|
|
139
|
+
out += "0|";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Drop one or all cached entries. Called from management actions and tests. */
|
|
146
|
+
export function invalidateTeamDiscoveryCache(cwd?: string): void {
|
|
147
|
+
if (cwd) {
|
|
148
|
+
teamCache.delete(cwd);
|
|
149
|
+
} else {
|
|
150
|
+
teamCache.clear();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function discoverTeams(cwd: string): TeamDiscoveryResult {
|
|
155
|
+
// F15: serve from cache when both TTL is fresh AND dir-stamp is unchanged.
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
const stamp = teamDirStamp(cwd);
|
|
158
|
+
const cached = teamCache.get(cwd);
|
|
159
|
+
if (cached && cached.expiresAt > now && cached.dirStamp === stamp) {
|
|
160
|
+
return cached.result;
|
|
161
|
+
}
|
|
162
|
+
const result: TeamDiscoveryResult = {
|
|
163
|
+
builtin: readTeamDir(path.join(packageRoot(), "teams"), "builtin"),
|
|
164
|
+
user: readTeamDir(path.join(userPiRoot(), "teams"), "user"),
|
|
165
|
+
project: readTeamDir(path.join(projectCrewRoot(cwd), "teams"), "project"),
|
|
166
|
+
};
|
|
167
|
+
teamCache.set(cwd, {
|
|
168
|
+
result,
|
|
169
|
+
expiresAt: now + TEAM_DISCOVERY_TTL_MS,
|
|
170
|
+
dirStamp: stamp,
|
|
171
|
+
});
|
|
172
|
+
while (teamCache.size > TEAM_DISCOVERY_MAX_ENTRIES) {
|
|
173
|
+
const oldest = teamCache.keys().next().value;
|
|
174
|
+
if (oldest === undefined) break;
|
|
175
|
+
teamCache.delete(oldest);
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function allTeams(discovery: TeamDiscoveryResult | undefined): TeamConfig[] {
|
|
181
|
+
if (!discovery) return [];
|
|
182
|
+
const byName = new Map<string, TeamConfig>();
|
|
183
|
+
for (const team of [...discovery.project, ...discovery.builtin, ...discovery.user]) {
|
|
184
|
+
byName.set(team.name, team);
|
|
185
|
+
}
|
|
186
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
187
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ResourceSource, RoutingMetadata } from "../agents/agent-config.ts";
|
|
2
|
+
|
|
3
|
+
export interface TeamRole {
|
|
4
|
+
name: string;
|
|
5
|
+
agent: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
model?: string;
|
|
8
|
+
/** Additional skills for this role; false disables role-default injected skills for tasks using this role. */
|
|
9
|
+
skills?: string[] | false;
|
|
10
|
+
maxConcurrency?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TeamConfig {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
source: ResourceSource;
|
|
17
|
+
filePath: string;
|
|
18
|
+
roles: TeamRole[];
|
|
19
|
+
defaultWorkflow?: string;
|
|
20
|
+
workspaceMode?: "single" | "worktree";
|
|
21
|
+
maxConcurrency?: number;
|
|
22
|
+
routing?: RoutingMetadata;
|
|
23
|
+
/**
|
|
24
|
+
* Optional git-based source URL when this team config is sourced from a remote URL.
|
|
25
|
+
*/
|
|
26
|
+
sourceUrl?: string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TeamConfig, TeamRole } from "./team-config.ts";
|
|
2
|
+
|
|
3
|
+
function line(key: string, value: string | string[] | undefined): string | undefined {
|
|
4
|
+
if (value === undefined) return undefined;
|
|
5
|
+
if (Array.isArray(value)) return `${key}: ${value.join(", ")}`;
|
|
6
|
+
return `${key}: ${value}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function serializeRole(role: TeamRole): string {
|
|
10
|
+
const parts = [`agent=${role.agent}`];
|
|
11
|
+
if (role.model) parts.push(`model=${role.model}`);
|
|
12
|
+
if (role.skills === false) parts.push("skills=false");
|
|
13
|
+
else if (role.skills?.length) parts.push(`skills=${role.skills.join(",")}`);
|
|
14
|
+
if (role.maxConcurrency !== undefined) parts.push(`maxConcurrency=${role.maxConcurrency}`);
|
|
15
|
+
if (role.description) parts.push(role.description);
|
|
16
|
+
return `- ${role.name}: ${parts.join(" ")}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function serializeTeam(team: TeamConfig): string {
|
|
20
|
+
const lines = [
|
|
21
|
+
"---",
|
|
22
|
+
`name: ${team.name}`,
|
|
23
|
+
`description: ${team.description}`,
|
|
24
|
+
team.defaultWorkflow ? `defaultWorkflow: ${team.defaultWorkflow}` : undefined,
|
|
25
|
+
team.workspaceMode ? `workspaceMode: ${team.workspaceMode}` : undefined,
|
|
26
|
+
team.maxConcurrency !== undefined ? `maxConcurrency: ${team.maxConcurrency}` : undefined,
|
|
27
|
+
line("triggers", team.routing?.triggers),
|
|
28
|
+
line("useWhen", team.routing?.useWhen),
|
|
29
|
+
line("avoidWhen", team.routing?.avoidWhen),
|
|
30
|
+
line("cost", team.routing?.cost),
|
|
31
|
+
line("category", team.routing?.category),
|
|
32
|
+
"---",
|
|
33
|
+
"",
|
|
34
|
+
...team.roles.map(serializeRole),
|
|
35
|
+
"",
|
|
36
|
+
].filter((entry): entry is string => entry !== undefined);
|
|
37
|
+
return lines.join("\n");
|
|
38
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe Bash Extension for pi-crew
|
|
3
|
+
* Wraps the built-in bash tool with dangerous command blocking.
|
|
4
|
+
*
|
|
5
|
+
* Delegates pattern matching to the core `safe-bash.ts` module which uses
|
|
6
|
+
* linear-time string scanning (no ReDoS-vulnerable regex).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* 1. Enable in config: { "tools": { "bash": { "safeMode": true } } }
|
|
10
|
+
* 2. Or use via agent config: { "extensions": ["path/to/safe-bash-extension.ts"] }
|
|
11
|
+
* 3. Or set env var: PI_CREW_SAFE_BASH=true
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { createBashTool } from "@earendil-works/pi-coding-agent";
|
|
16
|
+
import { Type } from "@sinclair/typebox";
|
|
17
|
+
import { checkCommand } from "./safe-bash.ts";
|
|
18
|
+
|
|
19
|
+
export default function safeBashExtension(pi: ExtensionAPI): void {
|
|
20
|
+
const cwd = process.cwd();
|
|
21
|
+
const bashTool = createBashTool(cwd);
|
|
22
|
+
|
|
23
|
+
pi.registerTool({
|
|
24
|
+
name: "safe_bash",
|
|
25
|
+
label: "Safe Bash",
|
|
26
|
+
description: "Execute a bash command safely. Blocks dangerous commands like `rm -rf /`, `sudo`, `curl | sh`, etc.",
|
|
27
|
+
parameters: Type.Object({
|
|
28
|
+
command: Type.String({ description: "Bash command to execute" }),
|
|
29
|
+
/** Timeout in seconds (optional). Default: no timeout. If exceeded, the command is killed. */
|
|
30
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (optional)" })),
|
|
31
|
+
description: Type.Optional(
|
|
32
|
+
Type.String({
|
|
33
|
+
description: "Description of what this command does (optional)",
|
|
34
|
+
}),
|
|
35
|
+
),
|
|
36
|
+
}),
|
|
37
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
38
|
+
const danger = checkCommand(params.command);
|
|
39
|
+
if (danger) {
|
|
40
|
+
return {
|
|
41
|
+
details: {},
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: "text" as const,
|
|
45
|
+
text: `🚫 ${danger}\n\nCommand blocked by safety policy. If this is a false positive, ask the user for confirmation or use force: true with explicit user approval.`,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Safe - delegate to real bash tool
|
|
51
|
+
return bashTool.execute(toolCallId, params, signal, onUpdate);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|