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,155 @@
|
|
|
1
|
+
import { isSecretKey } from "./redaction.ts";
|
|
2
|
+
|
|
3
|
+
// Well-known LLM provider API keys that are intentionally allowlisted in
|
|
4
|
+
// child-pi.ts and async-runner.ts for pass-through to worker processes.
|
|
5
|
+
// These are "secret" by pattern (contain KEY/API) but are safe to allowlist
|
|
6
|
+
// because they are standard provider credentials, not arbitrary secrets.
|
|
7
|
+
const KNOWN_PROVIDER_KEYS = new Set([
|
|
8
|
+
"MINIMAX_API_KEY",
|
|
9
|
+
"MINIMAX_GROUP_ID",
|
|
10
|
+
"OPENAI_API_KEY",
|
|
11
|
+
"OPENAI_ORG_ID",
|
|
12
|
+
"ANTHROPIC_API_KEY",
|
|
13
|
+
"GOOGLE_API_KEY",
|
|
14
|
+
"GOOGLE_GENERATIVE_LANGUAGE_API_KEY",
|
|
15
|
+
"AZURE_OPENAI_API_KEY",
|
|
16
|
+
"AZURE_OPENAI_ENDPOINT",
|
|
17
|
+
"AWS_ACCESS_KEY_ID",
|
|
18
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
19
|
+
"AWS_REGION",
|
|
20
|
+
"ZEU_API_KEY",
|
|
21
|
+
"ZERODEV_API_KEY",
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
// Map from provider prefix (lowercase) to env var names.
|
|
25
|
+
// Used by providerEnvKeys() to scope API keys per task.
|
|
26
|
+
const PROVIDER_ENV_KEY_MAP: Record<string, string[]> = {
|
|
27
|
+
minimax: ["MINIMAX_API_KEY", "MINIMAX_GROUP_ID"],
|
|
28
|
+
openai: ["OPENAI_API_KEY", "OPENAI_ORG_ID"],
|
|
29
|
+
anthropic: ["ANTHROPIC_API_KEY"],
|
|
30
|
+
google: ["GOOGLE_API_KEY", "GOOGLE_GENERATIVE_LANGUAGE_API_KEY"],
|
|
31
|
+
gemini: ["GOOGLE_API_KEY", "GOOGLE_GENERATIVE_LANGUAGE_API_KEY"],
|
|
32
|
+
azure: ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"],
|
|
33
|
+
"azure-openai": ["AZURE_OPENAI_API_KEY", "AZURE_OPENAI_ENDPOINT"],
|
|
34
|
+
aws: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"],
|
|
35
|
+
bedrock: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION"],
|
|
36
|
+
zai: ["ZEU_API_KEY"],
|
|
37
|
+
zerodev: ["ZERODEV_API_KEY"],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function isKnownProviderKey(key: string): boolean {
|
|
41
|
+
return KNOWN_PROVIDER_KEYS.has(key);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Extract provider prefix from a model ID string and return the corresponding
|
|
46
|
+
* env var names. Returns empty array for unknown/custom providers or invalid input.
|
|
47
|
+
*
|
|
48
|
+
* @param modelId - Model in "provider/modelId" format (e.g. "openai/gpt-4o")
|
|
49
|
+
* @returns Array of env var names needed for this provider
|
|
50
|
+
*/
|
|
51
|
+
export function providerEnvKeys(modelId: string | undefined): string[] {
|
|
52
|
+
if (!modelId) return [];
|
|
53
|
+
const separatorIndex = modelId.indexOf("/");
|
|
54
|
+
if (separatorIndex <= 0) return [];
|
|
55
|
+
const provider = modelId.substring(0, separatorIndex).toLowerCase();
|
|
56
|
+
return PROVIDER_ENV_KEY_MAP[provider] ?? [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build a scoped allowlist that includes system vars + only the provider keys
|
|
61
|
+
* relevant to the assigned model (and its fallback chain).
|
|
62
|
+
*
|
|
63
|
+
* @param baseAllowList - Non-provider system vars to always include
|
|
64
|
+
* @param models - Model IDs to include provider keys for (primary + fallbacks)
|
|
65
|
+
* @returns Combined allowlist with scoped provider keys
|
|
66
|
+
*/
|
|
67
|
+
export function buildScopedAllowList(baseAllowList: string[], models: (string | undefined)[]): string[] {
|
|
68
|
+
const providerKeys = new Set<string>();
|
|
69
|
+
for (const model of models) {
|
|
70
|
+
for (const key of providerEnvKeys(model)) {
|
|
71
|
+
providerKeys.add(key);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [...baseAllowList, ...providerKeys];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface SanitizeEnvOptions {
|
|
78
|
+
/** Allow-list of env var names to preserve. Supports trailing glob, e.g. `"PI_*"`. */
|
|
79
|
+
allowList?: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Keywords that indicate a secret env var
|
|
83
|
+
const SECRET_SUFFIXES = ["token", "api", "key", "password", "passwd", "secret", "credential", "authorization", "private"];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if a glob pattern could match secret env vars.
|
|
87
|
+
* A pattern like "PI_*" is dangerous because it could match PI_TOKEN, PI_API_KEY, etc.
|
|
88
|
+
*
|
|
89
|
+
* Exception: `PI_CREW_*` is a controlled namespace — the pi-crew codebase owns
|
|
90
|
+
* every PI_CREW_* env var (PI_CREW_PARENT_PID, PI_CREW_ADAPTIVE_REPAIR, etc.)
|
|
91
|
+
* and none of them are secrets. Allowing the glob here lets child Pi processes
|
|
92
|
+
* inherit our config without needing a per-var allowlist.
|
|
93
|
+
*/
|
|
94
|
+
function isDangerousGlob(pattern: string): boolean {
|
|
95
|
+
if (!pattern.endsWith("*")) return false;
|
|
96
|
+
const prefix = pattern.slice(0, -1); // Remove trailing *
|
|
97
|
+
if (prefix === "") return true; // Single "*" matches everything
|
|
98
|
+
// PI_CREW_* is the pi-crew controlled namespace — no secrets live here.
|
|
99
|
+
// This covers PI_CREW_*, PI_CREW_TEAMS_*, PI_CREW_AGENT_*, etc.
|
|
100
|
+
if (prefix.startsWith("PI_CREW_") || prefix === "PI_CREW") return false;
|
|
101
|
+
// Check if combining the prefix with any secret suffix would create a secret key
|
|
102
|
+
for (const suffix of SECRET_SUFFIXES) {
|
|
103
|
+
if (isSecretKey(prefix + suffix)) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Strip env vars whose keys look like secrets before passing to child processes.
|
|
112
|
+
*
|
|
113
|
+
* Default mode (no allowList): deny-list using isSecretKey.
|
|
114
|
+
* When allowList is provided, only keys matching the allow-list are preserved.
|
|
115
|
+
*/
|
|
116
|
+
export function sanitizeEnvSecrets(env: NodeJS.ProcessEnv, options?: SanitizeEnvOptions): Record<string, string> {
|
|
117
|
+
const filtered: Record<string, string> = {};
|
|
118
|
+
if (options?.allowList && options.allowList.length > 0) {
|
|
119
|
+
// Validate allowlist patterns don't match secrets
|
|
120
|
+
for (const pattern of options.allowList) {
|
|
121
|
+
if (isDangerousGlob(pattern)) {
|
|
122
|
+
throw new Error(`Allowlist pattern "${pattern}" could match secret env vars. Use a more specific pattern.`);
|
|
123
|
+
}
|
|
124
|
+
// Validate non-glob entries don't look like secret keys.
|
|
125
|
+
// Exception 1: if the key exists in env, it was intentionally set and
|
|
126
|
+
// should be allowed through (user knows what they're doing).
|
|
127
|
+
// Exception 2: known provider keys (MINIMAX_API_KEY, etc.) are always
|
|
128
|
+
// allowed because they are standard provider credentials explicitly
|
|
129
|
+
// listed in async-runner.ts / child-pi.ts allowlists.
|
|
130
|
+
if (!pattern.endsWith("*") && isSecretKey(pattern) && !(pattern in env) && !isKnownProviderKey(pattern)) {
|
|
131
|
+
throw new Error(`Allowlist entry "${pattern}" looks like a secret key. Use a more specific pattern.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const matchers = options.allowList.map((p) => {
|
|
135
|
+
if (p.endsWith("*")) {
|
|
136
|
+
// Glob pattern: matches keys that start with the prefix AND have
|
|
137
|
+
// at least one additional character (distinguishes "PI_CREW_*" from "PI_CREW_").
|
|
138
|
+
// For example, "PI_CREW_*" matches "PI_CREW_DEPTH" but not "PI_CREW_".
|
|
139
|
+
// This ensures trailing glob patterns require extra chars, not exact-prefix-only matches.
|
|
140
|
+
const prefix = p.slice(0, -1);
|
|
141
|
+
return (k: string) => k.startsWith(prefix) && k.length > prefix.length;
|
|
142
|
+
}
|
|
143
|
+
// Exact match is case-sensitive; Unix env vars are uppercase by convention.
|
|
144
|
+
return (k: string) => k === p;
|
|
145
|
+
});
|
|
146
|
+
for (const [key, value] of Object.entries(env)) {
|
|
147
|
+
if (value !== undefined && matchers.some((fn) => fn(key))) filtered[key] = value;
|
|
148
|
+
}
|
|
149
|
+
return filtered;
|
|
150
|
+
}
|
|
151
|
+
for (const [key, value] of Object.entries(env)) {
|
|
152
|
+
if (value !== undefined && !isSecretKey(key)) filtered[key] = value;
|
|
153
|
+
}
|
|
154
|
+
return filtered;
|
|
155
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
|
|
3
|
+
interface TimerApi {
|
|
4
|
+
setTimeout(handler: () => void, delayMs: number): unknown;
|
|
5
|
+
clearTimeout(handle: unknown): void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const defaultTimerApi: TimerApi = {
|
|
9
|
+
setTimeout: (handler, delayMs) => {
|
|
10
|
+
const t = setTimeout(handler, delayMs);
|
|
11
|
+
// Defense in depth: never let a coalescer timer block process exit.
|
|
12
|
+
// The timer may be cleared before it fires; .unref() is idempotent.
|
|
13
|
+
if (typeof t === "object" && t && "unref" in t && typeof t.unref === "function") {
|
|
14
|
+
t.unref();
|
|
15
|
+
}
|
|
16
|
+
return t;
|
|
17
|
+
},
|
|
18
|
+
clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export interface FileCoalescer {
|
|
22
|
+
schedule(file: string, delayMs?: number): boolean;
|
|
23
|
+
clear(): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createFileCoalescer(
|
|
27
|
+
handler: (file: string) => void,
|
|
28
|
+
defaultDelayMs: number,
|
|
29
|
+
timerApi: TimerApi = defaultTimerApi,
|
|
30
|
+
): FileCoalescer {
|
|
31
|
+
const pending = new Map<string, unknown>();
|
|
32
|
+
return {
|
|
33
|
+
schedule(file, delayMs = defaultDelayMs) {
|
|
34
|
+
if (pending.has(file)) return false;
|
|
35
|
+
const timer = timerApi.setTimeout(() => {
|
|
36
|
+
pending.delete(file);
|
|
37
|
+
handler(file);
|
|
38
|
+
}, delayMs);
|
|
39
|
+
pending.set(file, timer);
|
|
40
|
+
return true;
|
|
41
|
+
},
|
|
42
|
+
clear() {
|
|
43
|
+
for (const timer of pending.values()) timerApi.clearTimeout(timer);
|
|
44
|
+
pending.clear();
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ReadCacheEntry<T> {
|
|
50
|
+
value: T;
|
|
51
|
+
mtimeMs: number;
|
|
52
|
+
size: number;
|
|
53
|
+
expiresAt: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const readCache = new Map<string, ReadCacheEntry<unknown>>();
|
|
57
|
+
const readCacheSizeLimit = 128;
|
|
58
|
+
|
|
59
|
+
function evictOldestCacheEntry(): void {
|
|
60
|
+
if (readCache.size < readCacheSizeLimit) return;
|
|
61
|
+
// Map iteration order is insertion order; first key is LRU.
|
|
62
|
+
const oldestKey = readCache.keys().next().value;
|
|
63
|
+
if (oldestKey !== undefined) readCache.delete(oldestKey);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function clearReadCache(): void {
|
|
67
|
+
readCache.clear();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function readJsonFileCoalesced<T>(filePath: string, ttlMs: number, read: () => T): T {
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
const stat = (() => {
|
|
73
|
+
try {
|
|
74
|
+
const fileStat = fs.statSync(filePath);
|
|
75
|
+
return { mtimeMs: fileStat.mtimeMs, size: fileStat.size };
|
|
76
|
+
} catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
const cached = readCache.get(filePath);
|
|
81
|
+
if (cached && stat && cached.expiresAt > now && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
|
|
82
|
+
// Re-insert to implement LRU: move to end of Map.
|
|
83
|
+
readCache.delete(filePath);
|
|
84
|
+
readCache.set(filePath, cached);
|
|
85
|
+
return cached.value as T;
|
|
86
|
+
}
|
|
87
|
+
const value = read();
|
|
88
|
+
if (stat !== undefined) {
|
|
89
|
+
readCache.set(filePath, {
|
|
90
|
+
value,
|
|
91
|
+
mtimeMs: stat.mtimeMs,
|
|
92
|
+
size: stat.size,
|
|
93
|
+
expiresAt: now + ttlMs,
|
|
94
|
+
});
|
|
95
|
+
evictOldestCacheEntry();
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental fingerprinting — detect file changes to skip unchanged work.
|
|
3
|
+
*
|
|
4
|
+
* Pattern origin: Understand-Anything/packages/core/src/fingerprint.ts
|
|
5
|
+
* Content hash + structural signature per file. Change classifier:
|
|
6
|
+
* NONE (unchanged), COSMETIC (whitespace/comments only), STRUCTURAL (code changed).
|
|
7
|
+
* Only STRUCTURAL changes trigger re-analysis.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
13
|
+
|
|
14
|
+
// ── Types ────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export type ChangeClass = "NONE" | "COSMETIC" | "STRUCTURAL";
|
|
17
|
+
|
|
18
|
+
export interface FileFingerprint {
|
|
19
|
+
path: string;
|
|
20
|
+
contentHash: string;
|
|
21
|
+
structuralSignature: string;
|
|
22
|
+
lastModified: number; // mtime ms
|
|
23
|
+
changeClass: ChangeClass;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface FingerprintDelta {
|
|
27
|
+
added: string[];
|
|
28
|
+
removed: string[];
|
|
29
|
+
modified: FileFingerprint[]; // only STRUCTURAL changes
|
|
30
|
+
unchanged: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Fingerprinting ───────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Compute SHA-256 content hash of a file.
|
|
37
|
+
*/
|
|
38
|
+
export function computeContentHash(filePath: string): string {
|
|
39
|
+
try {
|
|
40
|
+
const content = readFileSync(filePath);
|
|
41
|
+
return createHash("sha256").update(content).digest("hex");
|
|
42
|
+
} catch {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Extract a structural signature from source code.
|
|
49
|
+
*
|
|
50
|
+
* Captures function signatures, class methods, and import specifiers.
|
|
51
|
+
* Ignores whitespace, comments, and string content.
|
|
52
|
+
* Returns a hash of the structural elements.
|
|
53
|
+
*/
|
|
54
|
+
export function computeStructuralSignature(content: string, filePath: string): string {
|
|
55
|
+
const lines = content.split("\n");
|
|
56
|
+
const structural: string[] = [];
|
|
57
|
+
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
|
|
61
|
+
// Skip empty lines and comments
|
|
62
|
+
if (trimmed.length === 0) continue;
|
|
63
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*")) continue;
|
|
64
|
+
|
|
65
|
+
// Capture structural lines
|
|
66
|
+
if (
|
|
67
|
+
// Function/method declarations
|
|
68
|
+
/^(export\s+)?(async\s+)?function\s/.test(trimmed) ||
|
|
69
|
+
(/^\w+\s*\(/.test(trimmed) && !/^(if|for|while|switch|return|throw|await)/.test(trimmed)) ||
|
|
70
|
+
// Class declarations
|
|
71
|
+
/^(export\s+)?(abstract\s+)?class\s/.test(trimmed) ||
|
|
72
|
+
/^(public|private|protected|static|readonly|abstract)\s/.test(trimmed) ||
|
|
73
|
+
// Interface/type declarations
|
|
74
|
+
/^(export\s+)?(interface|type)\s/.test(trimmed) ||
|
|
75
|
+
// Import declarations
|
|
76
|
+
/^import\s/.test(trimmed) ||
|
|
77
|
+
// Export declarations
|
|
78
|
+
/^export\s+(default\s+)?(const|let|var|function|class|interface|type|enum)\s/.test(trimmed)
|
|
79
|
+
) {
|
|
80
|
+
structural.push(trimmed);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return createHash("sha256").update(structural.join("\n")).digest("hex");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Classify the change between two fingerprints.
|
|
89
|
+
*/
|
|
90
|
+
export function classifyChange(previous: FileFingerprint | undefined, current: FileFingerprint): ChangeClass {
|
|
91
|
+
if (!previous) return "STRUCTURAL"; // New file
|
|
92
|
+
|
|
93
|
+
if (previous.contentHash === current.contentHash) return "NONE";
|
|
94
|
+
if (previous.structuralSignature === current.structuralSignature) return "COSMETIC";
|
|
95
|
+
return "STRUCTURAL";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Compute fingerprint for a single file.
|
|
100
|
+
*/
|
|
101
|
+
export function fingerprintFile(filePath: string): FileFingerprint {
|
|
102
|
+
const content = readFileSync(filePath, "utf-8");
|
|
103
|
+
const stat = statSync(filePath);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
path: filePath,
|
|
107
|
+
contentHash: computeContentHash(filePath),
|
|
108
|
+
structuralSignature: computeStructuralSignature(content, filePath),
|
|
109
|
+
lastModified: stat.mtimeMs,
|
|
110
|
+
changeClass: "NONE", // will be classified during delta computation
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── Fingerprint Store ────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
const MAX_FINGERPRINTS = 10000;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Load fingerprint baseline from a JSON file.
|
|
120
|
+
*/
|
|
121
|
+
export function loadFingerprintBaseline(storePath: string): Map<string, FileFingerprint> {
|
|
122
|
+
const map = new Map<string, FileFingerprint>();
|
|
123
|
+
if (!existsSync(storePath)) return map;
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const data = JSON.parse(readFileSync(storePath, "utf-8")) as FileFingerprint[];
|
|
127
|
+
for (const fp of data) {
|
|
128
|
+
if (map.size >= MAX_FINGERPRINTS) break;
|
|
129
|
+
map.set(fp.path, fp);
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logInternalError("fingerprint.load", error, `storePath=${storePath}`);
|
|
133
|
+
}
|
|
134
|
+
return map;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Save fingerprint baseline to a JSON file.
|
|
139
|
+
*/
|
|
140
|
+
export function saveFingerprintBaseline(storePath: string, fingerprints: Map<string, FileFingerprint>): void {
|
|
141
|
+
const entries = [...fingerprints.entries()].slice(0, MAX_FINGERPRINTS).map(([, fp]) => fp);
|
|
142
|
+
writeFileSync(storePath, JSON.stringify(entries, null, 2), "utf-8");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Compute delta between baseline and current fingerprints.
|
|
147
|
+
*
|
|
148
|
+
* Only returns STRUCTURAL changes in the `modified` field.
|
|
149
|
+
*/
|
|
150
|
+
export function computeFingerprintDelta(baseline: Map<string, FileFingerprint>, current: Map<string, FileFingerprint>): FingerprintDelta {
|
|
151
|
+
const added: string[] = [];
|
|
152
|
+
const removed: string[] = [];
|
|
153
|
+
const modified: FileFingerprint[] = [];
|
|
154
|
+
let unchanged = 0;
|
|
155
|
+
|
|
156
|
+
// Find added and modified
|
|
157
|
+
for (const [path, currentFp] of current) {
|
|
158
|
+
const prev = baseline.get(path);
|
|
159
|
+
const changeClass = classifyChange(prev, currentFp);
|
|
160
|
+
|
|
161
|
+
if (!prev) {
|
|
162
|
+
added.push(path);
|
|
163
|
+
} else if (changeClass === "STRUCTURAL") {
|
|
164
|
+
modified.push({ ...currentFp, changeClass: "STRUCTURAL" });
|
|
165
|
+
} else if (changeClass === "COSMETIC") {
|
|
166
|
+
unchanged++; // cosmetic = treated as unchanged for re-analysis
|
|
167
|
+
} else {
|
|
168
|
+
unchanged++;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Find removed
|
|
173
|
+
for (const path of baseline.keys()) {
|
|
174
|
+
if (!current.has(path)) {
|
|
175
|
+
removed.push(path);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { added, removed, modified, unchanged };
|
|
180
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export interface ParsedFrontmatter {
|
|
2
|
+
frontmatter: Record<string, string>;
|
|
3
|
+
body: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function parseFrontmatter(content: string): ParsedFrontmatter {
|
|
7
|
+
if (!content.startsWith("---\n") && !content.startsWith("---\r\n")) {
|
|
8
|
+
return { frontmatter: {}, body: content };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const normalized = content.replaceAll("\r\n", "\n");
|
|
12
|
+
const end = normalized.indexOf("\n---\n", 4);
|
|
13
|
+
if (end === -1) {
|
|
14
|
+
// Support frontmatter that ends at EOF without trailing newline after ---.
|
|
15
|
+
const altEnd = normalized.indexOf("\n---", 4);
|
|
16
|
+
if (altEnd !== -1 && altEnd + 4 === normalized.length) {
|
|
17
|
+
const raw = normalized.slice(4, altEnd);
|
|
18
|
+
const frontmatter = parseLines(raw);
|
|
19
|
+
return { frontmatter, body: "" };
|
|
20
|
+
}
|
|
21
|
+
return { frontmatter: {}, body: content };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const raw = normalized.slice(4, end);
|
|
25
|
+
const body = normalized.slice(end + "\n---\n".length);
|
|
26
|
+
const frontmatter = parseLines(raw);
|
|
27
|
+
return { frontmatter, body };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseLines(raw: string): Record<string, string> {
|
|
31
|
+
const frontmatter: Record<string, string> = {};
|
|
32
|
+
for (const line of raw.split("\n")) {
|
|
33
|
+
const trimmed = line.trim();
|
|
34
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
35
|
+
const separator = trimmed.indexOf(":");
|
|
36
|
+
if (separator === -1) continue;
|
|
37
|
+
const key = trimmed.slice(0, separator).trim();
|
|
38
|
+
const value = trimmed.slice(separator + 1).trim();
|
|
39
|
+
if (key) frontmatter[key] = value;
|
|
40
|
+
}
|
|
41
|
+
return frontmatter;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function parseCsv(value: string | undefined): string[] | undefined {
|
|
45
|
+
if (value === undefined) return undefined;
|
|
46
|
+
// Handle quoted values with commas inside.
|
|
47
|
+
const values = splitCsv(value)
|
|
48
|
+
.map((item) => item.trim())
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
return values.length > 0 ? [...new Set(values)] : undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function splitCsv(input: string): string[] {
|
|
54
|
+
const result: string[] = [];
|
|
55
|
+
let current = "";
|
|
56
|
+
let inQuotes = false;
|
|
57
|
+
for (let i = 0; i < input.length; i++) {
|
|
58
|
+
const char = input[i];
|
|
59
|
+
if (char === '"') {
|
|
60
|
+
inQuotes = !inQuotes;
|
|
61
|
+
} else if (char === "," && !inQuotes) {
|
|
62
|
+
result.push(current);
|
|
63
|
+
current = "";
|
|
64
|
+
} else {
|
|
65
|
+
current += char;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
result.push(current);
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { FSWatcher, WatchListener } from "node:fs";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Filesystem watcher helpers (slimmed down — pts/2 hang fix 2026-06-16).
|
|
6
|
+
*
|
|
7
|
+
* The recursive-watcher helpers (createRecursiveWatcher / watchCrewState /
|
|
8
|
+
* runIdFromStateRelativePath) were REMOVED: a recursive fs.watch on the run
|
|
9
|
+
* state tree exploded to O(total run history) inotify watches on Linux and
|
|
10
|
+
* caused a permanent interactive-session busy-loop. The bounded
|
|
11
|
+
* {@link RunWatcherRegistry} (one non-recursive watcher per ACTIVE run) now
|
|
12
|
+
* replaces them. Only the two primitives below survive — they are still used by
|
|
13
|
+
* manifest-cache, result-watcher, and run-watcher-registry.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export function closeWatcher(watcher: FSWatcher | null | undefined): void {
|
|
17
|
+
if (!watcher) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
watcher.close();
|
|
23
|
+
} catch {
|
|
24
|
+
// Ignore watcher close errors
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function watchWithErrorHandler(path: string, listener: WatchListener<string>, onError: (error?: unknown) => void): FSWatcher | null {
|
|
29
|
+
try {
|
|
30
|
+
const watcher = fs.watch(path, listener);
|
|
31
|
+
watcher.on("error", onError);
|
|
32
|
+
return watcher;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
onError(error);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|