pi-crew 0.9.50 → 0.9.52
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 +150 -0
- package/README.md +216 -20
- package/dist/build-meta.json +2222 -1761
- package/dist/index.mjs +18636 -17603
- package/docs/actions-reference.md +431 -1
- package/package.json +19 -9
- package/schema.json +41 -0
- package/scripts/check-bundle-size.mjs +38 -0
- package/scripts/test-runner.mjs +11 -1
- package/skills/distill-software/SKILL.md +24 -1
- package/dist/index.mjs.map +0 -7
- package/docs/FEATURE_INTAKE.md +0 -126
- package/docs/HARNESS.md +0 -87
- package/docs/HARNESS_BACKLOG.md +0 -89
- package/docs/PROJECT_REVIEW.md +0 -270
- package/docs/PROJECT_REVIEW_FIXES.md +0 -342
- package/docs/PROJECT_REVIEW_ROUND4.md +0 -156
- package/docs/PROJECT_REVIEW_ROUND5.md +0 -86
- package/docs/REVIEW-FINDINGS-2026-06 +0 -357
- package/docs/REVIEW-FINDINGS-2026-07-CORE.md +0 -139
- package/docs/REVIEW-FINDINGS-2026-07.md +0 -125
- package/docs/TEST_MATRIX.md +0 -51
- package/docs/archive/README.md +0 -91
- package/docs/archive/followup-review-round3-2026-05-12.md +0 -342
- package/docs/archive/followup-review-round4-2026-05-13.md +0 -107
- package/docs/archive/pi-crew-bugs.md +0 -961
- package/docs/archive/pi-crew-test-final.md +0 -120
- package/docs/archive/pi-crew-test-results.md +0 -260
- package/docs/archive/pi-crew-test-round2.md +0 -136
- package/docs/archive/pi-crew-test-round4.md +0 -100
- package/docs/archive/pi-crew-test-round5.md +0 -70
- package/docs/archive/pi-crew-test-round6.md +0 -110
- package/docs/archive/pi-crew-v0.5.10-audit-fix-plan.md +0 -60
- package/docs/archive/pi-crew-v0.5.11-audit-fix-plan.md +0 -92
- package/docs/archive/pi-crew-v0.5.12-audit-fix-plan.md +0 -76
- package/docs/archive/pi-crew-v0.5.13-audit-fix-plan.md +0 -75
- package/docs/archive/pi-crew-v0.5.14-audit-fix-plan.md +0 -75
- package/docs/archive/pi-crew-v0.5.16-audit-fix-plan.md +0 -35
- package/docs/archive/pi-crew-v0.5.17-audit-fix-plan.md +0 -80
- package/docs/archive/pi-crew-v0.5.5-audit-fix-plan.md +0 -133
- package/docs/archive/pi-crew-v0.5.9-audit-fix-plan.md +0 -88
- package/docs/archive/pi-mono-opportunities.md +0 -969
- package/docs/archive/pi-mono-review.md +0 -291
- package/docs/archive/pi-subagent4-comparison.md +0 -261
- package/docs/archive/pi-subagents3-deep-analysis.md +0 -508
- package/docs/bugs/bug-021-notification-badge-counter-misleading.md +0 -293
- package/docs/bugs/bug-023-chain-windows-path-resolution.md +0 -106
- package/docs/bugs/bug-quota-display-truncation.md +0 -223
- package/docs/bugs/cross-session-notification-leakage.md +0 -82
- package/docs/code-review-2026-05-11.md +0 -592
- package/docs/coding-agent-optimization.md +0 -268
- package/docs/comparison-pi-subagents-vs-pi-crew.md +0 -303
- package/docs/decisions/0001-durable-state.md +0 -41
- package/docs/decisions/0002-child-process-for-async.md +0 -42
- package/docs/decisions/0003-depth-guard.md +0 -36
- package/docs/decisions/0004-execfile-over-exec.md +0 -34
- package/docs/decisions/0005-no-parameter-properties.md +0 -49
- package/docs/decisions/0006-publish-bundled-esm.md +0 -63
- package/docs/decisions/0007-active-run-binary-index.md +0 -54
- package/docs/decisions/0008-child-pi-warm-pool.md +0 -61
- package/docs/decisions/2026-07-21-broker-phase4-default-on.md +0 -77
- package/docs/decisions/2026-07-21-broker-windows-perms.md +0 -91
- package/docs/decisions/2026-07-22-broker-phase4-gated-on.md +0 -99
- package/docs/decisions/2026-07-24-oidc-trusted-publishing.md +0 -112
- package/docs/decisions/README.md +0 -26
- package/docs/deep-review-report.md +0 -384
- package/docs/distillation/cybersecurity-patterns.md +0 -294
- package/docs/feature-analysis-subagent4.md +0 -305
- package/docs/fix-plan-disabletools-exit-null.md +0 -219
- package/docs/fixes/BATCH_A_H1_H2.md +0 -86
- package/docs/fixes/bug-006-foreground-cancel-concurrent.md +0 -78
- package/docs/fixes/bug-007-async-notifier-stale-ctx.md +0 -112
- package/docs/fixes/bug-008-child-process-silent-timeout.md +0 -100
- package/docs/fixes/bug-009-executor-yield-limit-needs-attention.md +0 -75
- package/docs/fixes/bug-010-child-process-api-key-filtered.md +0 -109
- package/docs/fixes/bug-011-spawn-pi-enoent.md +0 -92
- package/docs/fixes/bug-012-essential-env-stripped.md +0 -89
- package/docs/fixes/bug-013-background-runner-death.md +0 -84
- package/docs/fixes/bug-014-infinite-retry-loop-needs-attention.md +0 -82
- package/docs/fixes/bug-015-background-runner-sigterm.md +0 -65
- package/docs/fixes/bug-017-background-runner-session-shutdown.md +0 -66
- package/docs/fixes/bug-017-background-runner-sigkill-double-fork.md +0 -28
- package/docs/fixes/bug-018-child-pi-worker-stdin-hang.md +0 -61
- package/docs/fixes/bug-019-phantom-runs-temp-workspace.md +0 -52
- package/docs/fixes/bug-020-infinite-retry-loop-needs-attention.md +0 -47
- package/docs/fixes/v0.9.10/locks-fix-verify.md +0 -3
- package/docs/fixes/v0.9.10/smoke-test.md +0 -12
- package/docs/followup-plan-2026-05-12.md +0 -463
- package/docs/followup-review-2026-05-12.md +0 -297
- package/docs/goals.md +0 -59
- package/docs/implementation-plan-top3.md +0 -333
- package/docs/issue-29-analysis.md +0 -189
- package/docs/live-mailbox-runtime.md +0 -36
- package/docs/migration/atomic-write-v2-migration.md +0 -299
- package/docs/next-upgrade-roadmap.md +0 -808
- package/docs/oh-my-pi-research.md +0 -509
- package/docs/optimization-plan-2026-07.md +0 -366
- package/docs/optimization-plan.md +0 -644
- package/docs/patterns/command-agent-skill.md +0 -71
- package/docs/perf/baseline-2026-05.md +0 -113
- package/docs/perf/final-report-2026-05.md +0 -206
- package/docs/perf/optimization-plan-2026-07-verified.md +0 -396
- package/docs/perf/performance-audit-report-2026-07.md +0 -948
- package/docs/perf/performance-review-2026-07.md +0 -287
- package/docs/perf/sprint-1-report.md +0 -71
- package/docs/perf/sprint-2-report.md +0 -81
- package/docs/perf/sprint-2.5-report.md +0 -53
- package/docs/perf/sprint-3-report.md +0 -36
- package/docs/perf/sprint-4-report.md +0 -47
- package/docs/perf/sprint-5-report.md +0 -51
- package/docs/perf/sprint-6-report.md +0 -94
- package/docs/perf/sprint-7-report.md +0 -74
- package/docs/perf/upgrade-plan-2026-05.md +0 -147
- package/docs/performance-optimization-execution-plan.md +0 -520
- package/docs/phase4-triage.md +0 -58
- package/docs/pi-crew-investigation-report.md +0 -411
- package/docs/product/README.md +0 -31
- package/docs/product/platform.md +0 -27
- package/docs/product/runtime-safety.md +0 -37
- package/docs/product/team-run.md +0 -39
- package/docs/product/team-tool.md +0 -37
- package/docs/runtime-analysis-child-vs-live.md +0 -171
- package/docs/runtime-migration-in-process-analysis.md +0 -250
- package/docs/skills/REFERENCE.md +0 -138
- package/docs/stories/README.md +0 -32
- package/docs/stories/US-DEPS-major-upgrade.md +0 -62
- package/docs/stories/backlog.md +0 -36
- package/docs/superpowers/plans/2026-06-09-fallow-patterns-adoption.md +0 -1270
- package/docs/templates/decision.md +0 -27
- package/docs/templates/story.md +0 -44
- package/docs/templates/validation-report.md +0 -32
- package/docs/ui-optimization-plan.md +0 -447
- package/src/adapters/claude-adapter.ts +0 -23
- package/src/adapters/codex-adapter.ts +0 -21
- package/src/adapters/cursor-adapter.ts +0 -17
- package/src/adapters/export-util.ts +0 -143
- package/src/adapters/index.ts +0 -15
- package/src/adapters/registry.ts +0 -18
- package/src/adapters/types.ts +0 -23
- package/src/agents/agent-config.ts +0 -194
- package/src/agents/agent-search.ts +0 -98
- package/src/agents/agent-serializer.ts +0 -38
- package/src/agents/discover-agents.ts +0 -623
- package/src/benchmark/benchmark-runner.ts +0 -313
- package/src/benchmark/feedback-loop.ts +0 -73
- package/src/config/config.ts +0 -1269
- package/src/config/defaults.ts +0 -193
- package/src/config/drift-detector.ts +0 -290
- package/src/config/markers.ts +0 -330
- package/src/config/resilient-parser.ts +0 -117
- package/src/config/role-tools.ts +0 -118
- package/src/config/suggestions.ts +0 -75
- package/src/config/types.ts +0 -278
- package/src/errors.ts +0 -194
- package/src/extension/action-suggestions.ts +0 -117
- package/src/extension/async-notifier.ts +0 -179
- package/src/extension/autonomous-policy.ts +0 -209
- package/src/extension/command-completions.ts +0 -120
- package/src/extension/context-status-injection.ts +0 -185
- package/src/extension/crew-autocomplete.ts +0 -131
- package/src/extension/crew-cleanup.ts +0 -168
- package/src/extension/crew-input-router.ts +0 -93
- package/src/extension/crew-shortcuts.ts +0 -87
- package/src/extension/crew-vibes/cat-frames.ts +0 -18
- package/src/extension/crew-vibes/config.ts +0 -195
- package/src/extension/crew-vibes/figures.ts +0 -101
- package/src/extension/crew-vibes/font-detect.ts +0 -71
- package/src/extension/crew-vibes/footer.ts +0 -292
- package/src/extension/crew-vibes/index.ts +0 -432
- package/src/extension/crew-vibes/provider-usage.ts +0 -396
- package/src/extension/crew-vibes/render.ts +0 -206
- package/src/extension/crew-vibes/speed.ts +0 -286
- package/src/extension/cross-extension-rpc.ts +0 -350
- package/src/extension/help.ts +0 -62
- package/src/extension/import-index.ts +0 -74
- package/src/extension/knowledge-injection.ts +0 -402
- package/src/extension/management.ts +0 -571
- package/src/extension/message-renderers.ts +0 -108
- package/src/extension/notification-router.ts +0 -152
- package/src/extension/notification-sink.ts +0 -54
- package/src/extension/pi-api.ts +0 -56
- package/src/extension/plan-orchestrate.ts +0 -302
- package/src/extension/project-init.ts +0 -170
- package/src/extension/register.ts +0 -113
- package/src/extension/registration/artifact-cleanup.ts +0 -20
- package/src/extension/registration/command-registration.ts +0 -58
- package/src/extension/registration/command-utils.ts +0 -58
- package/src/extension/registration/commands.ts +0 -1228
- package/src/extension/registration/compaction-guard.ts +0 -333
- package/src/extension/registration/context-builder.ts +0 -135
- package/src/extension/registration/crash-recovery-cache.ts +0 -54
- package/src/extension/registration/foreground-run-controller.ts +0 -238
- package/src/extension/registration/hook-registration.ts +0 -119
- package/src/extension/registration/lazy-configurers.ts +0 -124
- package/src/extension/registration/lifecycle-handlers.ts +0 -923
- package/src/extension/registration/lifecycle.ts +0 -259
- package/src/extension/registration/observability.ts +0 -322
- package/src/extension/registration/registration-types.ts +0 -158
- package/src/extension/registration/runtime-cleanup.ts +0 -230
- package/src/extension/registration/subagent-helpers.ts +0 -125
- package/src/extension/registration/subagent-manager-setup.ts +0 -330
- package/src/extension/registration/subagent-tools.ts +0 -456
- package/src/extension/registration/team-tool.ts +0 -220
- package/src/extension/registration/tool-registration.ts +0 -50
- package/src/extension/registration/ui.ts +0 -172
- package/src/extension/registration/viewers.ts +0 -121
- package/src/extension/registration/wire-cross-extension.ts +0 -37
- package/src/extension/result-watcher.ts +0 -139
- package/src/extension/rpc-hmac.ts +0 -256
- package/src/extension/run-bundle-schema.ts +0 -104
- package/src/extension/run-export.ts +0 -97
- package/src/extension/run-import.ts +0 -168
- package/src/extension/run-index.ts +0 -131
- package/src/extension/run-maintenance.ts +0 -190
- package/src/extension/session-summary.ts +0 -18
- package/src/extension/team-manager-command.ts +0 -153
- package/src/extension/team-onboard.ts +0 -177
- package/src/extension/team-recommendation.ts +0 -288
- package/src/extension/team-tool/anchor.ts +0 -172
- package/src/extension/team-tool/api.ts +0 -1244
- package/src/extension/team-tool/auto-summarize.ts +0 -142
- package/src/extension/team-tool/cache-control.ts +0 -19
- package/src/extension/team-tool/cancel.ts +0 -337
- package/src/extension/team-tool/chain-dispatch.ts +0 -95
- package/src/extension/team-tool/chain-executor.ts +0 -379
- package/src/extension/team-tool/config-patch.ts +0 -50
- package/src/extension/team-tool/context.ts +0 -149
- package/src/extension/team-tool/destructive-gate.ts +0 -52
- package/src/extension/team-tool/doctor.ts +0 -431
- package/src/extension/team-tool/explain.ts +0 -282
- package/src/extension/team-tool/failure-patterns.ts +0 -118
- package/src/extension/team-tool/goal-wrap.ts +0 -330
- package/src/extension/team-tool/goal.ts +0 -556
- package/src/extension/team-tool/handle-schedule.ts +0 -332
- package/src/extension/team-tool/handle-settings.ts +0 -520
- package/src/extension/team-tool/health-monitor.ts +0 -481
- package/src/extension/team-tool/inspect.ts +0 -88
- package/src/extension/team-tool/intent-policy.ts +0 -45
- package/src/extension/team-tool/lifecycle-actions.ts +0 -615
- package/src/extension/team-tool/orchestrate.ts +0 -98
- package/src/extension/team-tool/parallel-dispatch.ts +0 -183
- package/src/extension/team-tool/plan.ts +0 -50
- package/src/extension/team-tool/respond.ts +0 -150
- package/src/extension/team-tool/run-not-found.ts +0 -49
- package/src/extension/team-tool/run.ts +0 -1035
- package/src/extension/team-tool/status.ts +0 -260
- package/src/extension/team-tool/workflow-manage.ts +0 -261
- package/src/extension/team-tool-types.ts +0 -26
- package/src/extension/team-tool.ts +0 -1116
- package/src/extension/tool-result.ts +0 -16
- package/src/extension/validate-resources.ts +0 -108
- package/src/hooks/registry.ts +0 -200
- package/src/hooks/types.ts +0 -69
- package/src/i18n.ts +0 -212
- package/src/observability/correlation.ts +0 -50
- package/src/observability/event-bus.ts +0 -86
- package/src/observability/event-to-metric.ts +0 -170
- package/src/observability/exporters/adapter.ts +0 -27
- package/src/observability/exporters/otlp-exporter.ts +0 -356
- package/src/observability/exporters/prometheus-exporter.ts +0 -54
- package/src/observability/metric-registry.ts +0 -100
- package/src/observability/metric-retention.ts +0 -64
- package/src/observability/metric-sink.ts +0 -99
- package/src/observability/metrics-primitives.ts +0 -219
- package/src/plugins/plugin-define.ts +0 -6
- package/src/plugins/plugin-registry.ts +0 -32
- package/src/plugins/plugins/index.ts +0 -3
- package/src/plugins/plugins/nextjs.ts +0 -19
- package/src/plugins/plugins/vite.ts +0 -10
- package/src/plugins/plugins/vitest.ts +0 -9
- package/src/prompt/prompt-runtime.ts +0 -380
- package/src/runtime/adaptive-plan.ts +0 -563
- package/src/runtime/agent-control.ts +0 -215
- package/src/runtime/agent-memory.ts +0 -81
- package/src/runtime/agent-observability.ts +0 -122
- package/src/runtime/anchor-manager.ts +0 -475
- package/src/runtime/async-marker.ts +0 -32
- package/src/runtime/async-runner.ts +0 -381
- package/src/runtime/attention-events.ts +0 -28
- package/src/runtime/auto-summarize.ts +0 -344
- package/src/runtime/background-runner.ts +0 -837
- package/src/runtime/batch-barrier.ts +0 -147
- package/src/runtime/broker-issuer.ts +0 -37
- package/src/runtime/cancellation-token.ts +0 -99
- package/src/runtime/cancellation.ts +0 -99
- package/src/runtime/capability-inventory.ts +0 -137
- package/src/runtime/chain-parser.ts +0 -237
- package/src/runtime/chain-runner.ts +0 -606
- package/src/runtime/checkpoint.ts +0 -291
- package/src/runtime/child-pi-constants.ts +0 -42
- package/src/runtime/child-pi-kill.ts +0 -180
- package/src/runtime/child-pi-pool.ts +0 -68
- package/src/runtime/child-pi-spawn.ts +0 -287
- package/src/runtime/child-pi-steering.ts +0 -128
- package/src/runtime/child-pi-streams.ts +0 -296
- package/src/runtime/child-pi-transcript.ts +0 -169
- package/src/runtime/child-pi.ts +0 -1105
- package/src/runtime/coalesce-tasks.ts +0 -268
- package/src/runtime/code-summary.ts +0 -292
- package/src/runtime/command-trace.ts +0 -105
- package/src/runtime/compact-pipeline.ts +0 -56
- package/src/runtime/compact-stages/ansi-strip-stage.ts +0 -25
- package/src/runtime/compact-stages/blank-collapse-stage.ts +0 -31
- package/src/runtime/compact-stages/deduplicate-stage.ts +0 -34
- package/src/runtime/compact-stages/head-snap-stage.ts +0 -57
- package/src/runtime/compact-stages/index.ts +0 -23
- package/src/runtime/compact-stages/tail-capture-stage.ts +0 -77
- package/src/runtime/compact-stages/truncation-stage.ts +0 -74
- package/src/runtime/compaction-summary.ts +0 -278
- package/src/runtime/completion-guard.ts +0 -207
- package/src/runtime/concurrency.ts +0 -58
- package/src/runtime/crash-classification.ts +0 -229
- package/src/runtime/crash-recovery.ts +0 -577
- package/src/runtime/crew-agent-records.ts +0 -593
- package/src/runtime/crew-agent-runtime.ts +0 -62
- package/src/runtime/crew-broker-child.ts +0 -88
- package/src/runtime/crew-broker-client.ts +0 -673
- package/src/runtime/crew-broker-tokens.ts +0 -84
- package/src/runtime/crew-broker.ts +0 -1276
- package/src/runtime/crew-hooks.ts +0 -227
- package/src/runtime/cross-extension-rpc.ts +0 -143
- package/src/runtime/custom-tools/irc-tool.ts +0 -282
- package/src/runtime/custom-tools/submit-result-tool.ts +0 -102
- package/src/runtime/deadletter.ts +0 -47
- package/src/runtime/delivery-coordinator.ts +0 -211
- package/src/runtime/delta-conflict.ts +0 -348
- package/src/runtime/deterministic-ast.ts +0 -161
- package/src/runtime/diagnostic-export.ts +0 -171
- package/src/runtime/direct-run.ts +0 -45
- package/src/runtime/dwf-state-store.ts +0 -102
- package/src/runtime/dynamic-workflow-context.ts +0 -1016
- package/src/runtime/dynamic-workflow-runner.ts +0 -310
- package/src/runtime/effectiveness.ts +0 -90
- package/src/runtime/errors/crew-errors.ts +0 -162
- package/src/runtime/event-stream-bridge.ts +0 -98
- package/src/runtime/foreground-control.ts +0 -193
- package/src/runtime/foreground-watchdog.ts +0 -132
- package/src/runtime/global-worker-cap.ts +0 -96
- package/src/runtime/goal-achievement.ts +0 -148
- package/src/runtime/goal-evaluator.ts +0 -361
- package/src/runtime/goal-loop-runner.ts +0 -828
- package/src/runtime/goal-state-store.ts +0 -219
- package/src/runtime/green-contract.ts +0 -55
- package/src/runtime/group-join.ts +0 -267
- package/src/runtime/handoff-manager.ts +0 -598
- package/src/runtime/heartbeat-gradient.ts +0 -36
- package/src/runtime/heartbeat-watcher.ts +0 -239
- package/src/runtime/hidden-handoff.ts +0 -407
- package/src/runtime/important-line-classifier.ts +0 -140
- package/src/runtime/intercom-bridge.ts +0 -187
- package/src/runtime/iteration-hooks.ts +0 -305
- package/src/runtime/live-agent-control.ts +0 -136
- package/src/runtime/live-agent-manager.ts +0 -704
- package/src/runtime/live-control-realtime.ts +0 -56
- package/src/runtime/live-extension-bridge.ts +0 -148
- package/src/runtime/live-irc.ts +0 -97
- package/src/runtime/live-session-health.ts +0 -108
- package/src/runtime/live-session-runtime.ts +0 -1139
- package/src/runtime/loop-gates.ts +0 -128
- package/src/runtime/manifest-cache.ts +0 -375
- package/src/runtime/mcp-proxy.ts +0 -105
- package/src/runtime/metric-parser.ts +0 -36
- package/src/runtime/model-fallback.ts +0 -409
- package/src/runtime/model-resolver.ts +0 -126
- package/src/runtime/model-scope.ts +0 -158
- package/src/runtime/orphan-worker-registry.ts +0 -465
- package/src/runtime/output-validator.ts +0 -202
- package/src/runtime/overflow-recovery.ts +0 -206
- package/src/runtime/parallel-research.ts +0 -68
- package/src/runtime/parallel-utils.ts +0 -160
- package/src/runtime/parent-guard.ts +0 -134
- package/src/runtime/path-overlap.ts +0 -150
- package/src/runtime/peer-dep.ts +0 -292
- package/src/runtime/per-write-validator.ts +0 -181
- package/src/runtime/phase-progress.ts +0 -217
- package/src/runtime/phase-tracker.ts +0 -385
- package/src/runtime/pi-args.ts +0 -655
- package/src/runtime/pi-json-output.ts +0 -170
- package/src/runtime/pi-spawn.ts +0 -273
- package/src/runtime/pipeline-runner.ts +0 -523
- package/src/runtime/plan-templates.ts +0 -201
- package/src/runtime/policy-engine.ts +0 -115
- package/src/runtime/post-checks.ts +0 -142
- package/src/runtime/post-exit-stdio-guard.ts +0 -104
- package/src/runtime/process-lifecycle.ts +0 -491
- package/src/runtime/process-status.ts +0 -154
- package/src/runtime/progress-event-coalescer.ts +0 -44
- package/src/runtime/progress-tracker.ts +0 -124
- package/src/runtime/prose-compressor.ts +0 -162
- package/src/runtime/recovery-recipes.ts +0 -203
- package/src/runtime/replace.ts +0 -570
- package/src/runtime/resilient-edit.ts +0 -153
- package/src/runtime/result-extractor.ts +0 -213
- package/src/runtime/retry-executor.ts +0 -100
- package/src/runtime/retry-runner.ts +0 -336
- package/src/runtime/role-permission.ts +0 -59
- package/src/runtime/run-coalesced-task-group.ts +0 -308
- package/src/runtime/run-drift.ts +0 -219
- package/src/runtime/run-tracker.ts +0 -116
- package/src/runtime/runtime-policy.ts +0 -29
- package/src/runtime/runtime-resolver.ts +0 -172
- package/src/runtime/runtime-warmup.ts +0 -160
- package/src/runtime/scheduler.ts +0 -353
- package/src/runtime/semaphore.ts +0 -141
- package/src/runtime/sensitive-paths.ts +0 -93
- package/src/runtime/session-resources.ts +0 -25
- package/src/runtime/session-snapshot.ts +0 -59
- package/src/runtime/session-usage.ts +0 -79
- package/src/runtime/settings-store.ts +0 -155
- package/src/runtime/sidechain-output.ts +0 -35
- package/src/runtime/single-agent-compose.ts +0 -84
- package/src/runtime/skill-effectiveness.ts +0 -524
- package/src/runtime/skill-instructions.ts +0 -391
- package/src/runtime/stale-reconciler.ts +0 -680
- package/src/runtime/stream-preview.ts +0 -184
- package/src/runtime/streaming-output.ts +0 -50
- package/src/runtime/subagent-manager.ts +0 -561
- package/src/runtime/subprocess-tool-registry.ts +0 -70
- package/src/runtime/supervisor-contact.ts +0 -64
- package/src/runtime/task-display.ts +0 -52
- package/src/runtime/task-graph-scheduler.ts +0 -227
- package/src/runtime/task-graph.ts +0 -290
- package/src/runtime/task-health.ts +0 -83
- package/src/runtime/task-id.ts +0 -161
- package/src/runtime/task-output-context.ts +0 -602
- package/src/runtime/task-packet.ts +0 -268
- package/src/runtime/task-quality.ts +0 -199
- package/src/runtime/task-runner/capabilities.ts +0 -78
- package/src/runtime/task-runner/context-retrieval.ts +0 -163
- package/src/runtime/task-runner/live-executor.ts +0 -227
- package/src/runtime/task-runner/output-splitter.ts +0 -152
- package/src/runtime/task-runner/progress.ts +0 -157
- package/src/runtime/task-runner/prompt-builder.ts +0 -278
- package/src/runtime/task-runner/prompt-pipeline.ts +0 -88
- package/src/runtime/task-runner/result-utils.ts +0 -16
- package/src/runtime/task-runner/retrieval-orchestrator.ts +0 -310
- package/src/runtime/task-runner/run-projection.ts +0 -125
- package/src/runtime/task-runner/state-helpers.ts +0 -176
- package/src/runtime/task-runner/tail-read.ts +0 -34
- package/src/runtime/task-runner.ts +0 -1420
- package/src/runtime/team-runner-artifacts.ts +0 -13
- package/src/runtime/team-runner.ts +0 -1993
- package/src/runtime/tool-output-pruner.ts +0 -333
- package/src/runtime/tool-progress.ts +0 -278
- package/src/runtime/usage-tracker.ts +0 -73
- package/src/runtime/verification-gates.ts +0 -571
- package/src/runtime/verification-integrity.ts +0 -105
- package/src/runtime/verification-worktree.ts +0 -186
- package/src/runtime/worker-heartbeat.ts +0 -25
- package/src/runtime/worker-startup.ts +0 -83
- package/src/runtime/workflow-state.ts +0 -195
- package/src/runtime/workspace-lock.ts +0 -443
- package/src/runtime/workspace-tree.ts +0 -312
- package/src/runtime/yield-handler.ts +0 -224
- package/src/runtime/zombie-scanner.ts +0 -298
- package/src/schema/config-schema.ts +0 -327
- package/src/schema/team-tool-schema.ts +0 -415
- package/src/schema/validation-types.ts +0 -159
- package/src/skills/discover-skills.ts +0 -197
- package/src/skills/skill-templates.ts +0 -456
- package/src/skills/validate.ts +0 -285
- package/src/state/active-run-registry.ts +0 -439
- package/src/state/artifact-store.ts +0 -176
- package/src/state/atomic-write.ts +0 -903
- package/src/state/blob-store.ts +0 -308
- package/src/state/contracts.ts +0 -158
- package/src/state/crew-init.ts +0 -269
- package/src/state/decision-ledger.ts +0 -345
- package/src/state/event-log-rotation.ts +0 -342
- package/src/state/event-log.ts +0 -1311
- package/src/state/event-reconstructor.ts +0 -229
- package/src/state/gitignore-manager.ts +0 -46
- package/src/state/health-store.ts +0 -79
- package/src/state/hook-instinct-bridge.ts +0 -94
- package/src/state/hook-integrations.ts +0 -51
- package/src/state/instinct-store.ts +0 -275
- package/src/state/jsonl-writer.ts +0 -107
- package/src/state/locks.ts +0 -562
- package/src/state/mailbox.ts +0 -916
- package/src/state/observation-store.ts +0 -176
- package/src/state/run-cache.ts +0 -193
- package/src/state/run-graph.ts +0 -183
- package/src/state/run-metrics.ts +0 -165
- package/src/state/schedule.ts +0 -166
- package/src/state/session-state-map.ts +0 -51
- package/src/state/state-store.ts +0 -958
- package/src/state/task-claims.ts +0 -61
- package/src/state/tiered-eval.ts +0 -480
- package/src/state/types-eval.ts +0 -58
- package/src/state/types.ts +0 -440
- package/src/state/usage.ts +0 -138
- package/src/state/worker-atomic-writer.ts +0 -194
- package/src/subagents/async-entry.ts +0 -1
- package/src/subagents/index.ts +0 -3
- package/src/subagents/live/control.ts +0 -1
- package/src/subagents/live/manager.ts +0 -1
- package/src/subagents/live/realtime.ts +0 -1
- package/src/subagents/live/session-runtime.ts +0 -1
- package/src/subagents/manager.ts +0 -1
- package/src/subagents/spawn.ts +0 -1
- package/src/teams/discover-teams.ts +0 -187
- package/src/teams/team-config.ts +0 -27
- package/src/teams/team-serializer.ts +0 -38
- package/src/tools/safe-bash-extension.ts +0 -54
- package/src/tools/safe-bash.ts +0 -505
- package/src/types/diff.d.ts +0 -18
- package/src/types/new-api-types.ts +0 -35
- package/src/ui/agent-management-overlay.ts +0 -160
- package/src/ui/card-colors.ts +0 -139
- package/src/ui/crew-footer.ts +0 -102
- package/src/ui/crew-select-list.ts +0 -111
- package/src/ui/dashboard-panes/agents-pane.ts +0 -164
- package/src/ui/dashboard-panes/cancellation-pane.ts +0 -66
- package/src/ui/dashboard-panes/capability-pane.ts +0 -77
- package/src/ui/dashboard-panes/health-pane.ts +0 -31
- package/src/ui/dashboard-panes/mailbox-pane.ts +0 -35
- package/src/ui/dashboard-panes/metrics-pane.ts +0 -37
- package/src/ui/dashboard-panes/progress-pane.ts +0 -38
- package/src/ui/dashboard-panes/transcript-pane.ts +0 -10
- package/src/ui/deploy-bundled-themes.ts +0 -71
- package/src/ui/dwf-phase-display.ts +0 -151
- package/src/ui/dynamic-border.ts +0 -35
- package/src/ui/heartbeat-aggregator.ts +0 -74
- package/src/ui/key-utils.ts +0 -42
- package/src/ui/keybinding-map.ts +0 -248
- package/src/ui/layout-primitives.ts +0 -106
- package/src/ui/live-conversation-overlay.ts +0 -183
- package/src/ui/live-duration.ts +0 -55
- package/src/ui/live-run-sidebar.ts +0 -301
- package/src/ui/loaders.ts +0 -176
- package/src/ui/mascot.ts +0 -436
- package/src/ui/overlays/agent-picker-overlay.ts +0 -65
- package/src/ui/overlays/confirm-overlay.ts +0 -60
- package/src/ui/overlays/help-overlay.ts +0 -177
- package/src/ui/overlays/mailbox-compose-overlay.ts +0 -181
- package/src/ui/overlays/mailbox-compose-preview.ts +0 -78
- package/src/ui/overlays/mailbox-detail-overlay.ts +0 -154
- package/src/ui/pi-ui-compat.ts +0 -68
- package/src/ui/powerbar-publisher.ts +0 -459
- package/src/ui/render-coalescer.ts +0 -101
- package/src/ui/render-diff.ts +0 -160
- package/src/ui/render-scheduler.ts +0 -224
- package/src/ui/run-action-dispatcher.ts +0 -182
- package/src/ui/run-dashboard.ts +0 -913
- package/src/ui/run-event-bus.ts +0 -381
- package/src/ui/run-snapshot-cache.ts +0 -1057
- package/src/ui/settings-overlay.ts +0 -1067
- package/src/ui/snapshot-types.ts +0 -90
- package/src/ui/spinner.ts +0 -17
- package/src/ui/status-colors.ts +0 -143
- package/src/ui/syntax-highlight.ts +0 -136
- package/src/ui/terminal-status.ts +0 -269
- package/src/ui/theme-adapter.ts +0 -271
- package/src/ui/theme-discovery.ts +0 -199
- package/src/ui/tool-progress-formatter.ts +0 -93
- package/src/ui/tool-render.ts +0 -453
- package/src/ui/tool-renderers/brief-mode.ts +0 -225
- package/src/ui/tool-renderers/index.ts +0 -726
- package/src/ui/transcript-cache.ts +0 -128
- package/src/ui/transcript-entries.ts +0 -256
- package/src/ui/transcript-viewer.ts +0 -424
- package/src/ui/widget/index.ts +0 -357
- package/src/ui/widget/widget-formatters.ts +0 -182
- package/src/ui/widget/widget-model.ts +0 -110
- package/src/ui/widget/widget-renderer.ts +0 -182
- package/src/ui/widget/widget-types.ts +0 -36
- package/src/utils/bm25-search.ts +0 -235
- package/src/utils/completion-dedupe.ts +0 -63
- package/src/utils/conflict-detect.ts +0 -668
- package/src/utils/env-allowlist.ts +0 -30
- package/src/utils/env-filter.ts +0 -155
- package/src/utils/file-coalescer.ts +0 -98
- package/src/utils/fingerprint.ts +0 -180
- package/src/utils/frontmatter.ts +0 -70
- package/src/utils/fs-watch.ts +0 -37
- package/src/utils/gh-protocol.ts +0 -556
- package/src/utils/git.ts +0 -260
- package/src/utils/guards.ts +0 -107
- package/src/utils/ids.ts +0 -24
- package/src/utils/incremental-reader.ts +0 -220
- package/src/utils/internal-error.ts +0 -10
- package/src/utils/names.ts +0 -36
- package/src/utils/ndjson.ts +0 -115
- package/src/utils/paths.ts +0 -227
- package/src/utils/project-detector.ts +0 -160
- package/src/utils/redaction.ts +0 -347
- package/src/utils/resolve-shell.ts +0 -36
- package/src/utils/run-watcher-registry.ts +0 -152
- package/src/utils/safe-paths.ts +0 -383
- package/src/utils/scan-cache.ts +0 -144
- package/src/utils/session-utils.ts +0 -110
- package/src/utils/sleep.ts +0 -54
- package/src/utils/socket-path.ts +0 -127
- package/src/utils/sse-parser.ts +0 -129
- package/src/utils/task-name-generator.ts +0 -337
- package/src/utils/timings.ts +0 -33
- package/src/utils/token-counter.ts +0 -200
- package/src/utils/visual.ts +0 -207
- package/src/workflows/cost-estimator.ts +0 -34
- package/src/workflows/discover-workflows.ts +0 -290
- package/src/workflows/intermediate-store.ts +0 -166
- package/src/workflows/preflight-validator.ts +0 -168
- package/src/workflows/topology-analyzer.ts +0 -203
- package/src/workflows/validate-workflow.ts +0 -50
- package/src/workflows/workflow-config.ts +0 -70
- package/src/workflows/workflow-serializer.ts +0 -32
- package/src/worktree/branch-freshness.ts +0 -112
- package/src/worktree/cleanup.ts +0 -341
- package/src/worktree/worktree-manager.ts +0 -1200
|
@@ -1,571 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Verification Gates — ECC VERIFICATION_LOOP Pattern Implementation
|
|
3
|
-
*
|
|
4
|
-
* Implements RED/GREEN phase gates for task verification.
|
|
5
|
-
* Sequential execution: cannot skip to Phase N+1 without Phase N passing.
|
|
6
|
-
*
|
|
7
|
-
* Based on: docs/distillation/ECC-10-skills.md §2 (verification-loop)
|
|
8
|
-
*
|
|
9
|
-
* @module verification-gates
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { spawn } from "node:child_process";
|
|
13
|
-
import * as fs from "node:fs";
|
|
14
|
-
import * as path from "node:path";
|
|
15
|
-
import { writeArtifact } from "../state/artifact-store.ts";
|
|
16
|
-
import type { GreenLevel, VerificationCommandResult, VerificationContract } from "../state/types.ts";
|
|
17
|
-
import { WINDOWS_ESSENTIAL_ENV_VARS } from "../utils/env-allowlist.ts";
|
|
18
|
-
import { sanitizeEnvSecrets } from "../utils/env-filter.ts";
|
|
19
|
-
import { redactSecretString } from "../utils/redaction.ts";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Phase 1.5 #1 (RFC 13 §6 info-disclosure mitigation): sanitize the env passed
|
|
23
|
-
* to verification commands so worker-induced output cannot leak model-provider
|
|
24
|
-
* secrets. P1f redaction at artifact-write + judge-bound is regex-best-effort
|
|
25
|
-
* against adversarial workers; this kills the leak at the source by never
|
|
26
|
-
* giving the verification process the secret in the first place.
|
|
27
|
-
*
|
|
28
|
-
* Opt-out via `PI_CREW_VERIFICATION_SANITIZE_ENV=0` or
|
|
29
|
-
* `PI_TEAMS_VERIFICATION_SANITIZE_ENV=0` to disable sanitization (e.g. for
|
|
30
|
-
* tests that legitimately need API access). Escape hatch:
|
|
31
|
-
* `PI_CREW_VERIFICATION_PRESERVE_ENV=KEY1,KEY2,...` lets users explicitly opt
|
|
32
|
-
* specific secrets back in (audited via the allowlist validator).
|
|
33
|
-
*/
|
|
34
|
-
const VERIFICATION_ENV_ALLOWLIST: readonly string[] = [
|
|
35
|
-
// Essential non-secret vars only — NO model-provider keys by default.
|
|
36
|
-
"PATH",
|
|
37
|
-
"HOME",
|
|
38
|
-
"USER",
|
|
39
|
-
"SHELL",
|
|
40
|
-
"TERM",
|
|
41
|
-
"LANG",
|
|
42
|
-
"LC_ALL",
|
|
43
|
-
"LC_COLLATE",
|
|
44
|
-
"LC_CTYPE",
|
|
45
|
-
"LC_MESSAGES",
|
|
46
|
-
"LC_MONETARY",
|
|
47
|
-
"LC_NUMERIC",
|
|
48
|
-
"LC_TIME",
|
|
49
|
-
"XDG_CONFIG_HOME",
|
|
50
|
-
"XDG_DATA_HOME",
|
|
51
|
-
"XDG_CACHE_HOME",
|
|
52
|
-
"XDG_RUNTIME_DIR",
|
|
53
|
-
// Windows essentials — see WINDOWS_ESSENTIAL_ENV_VARS (src/utils/env-allowlist.ts).
|
|
54
|
-
...WINDOWS_ESSENTIAL_ENV_VARS,
|
|
55
|
-
"NVM_BIN",
|
|
56
|
-
"NVM_DIR",
|
|
57
|
-
"NVM_INC",
|
|
58
|
-
"NODE_PATH",
|
|
59
|
-
"NODE_DISABLE_COLORS",
|
|
60
|
-
"NODE_EXTRA_CA_CERTS",
|
|
61
|
-
"NPM_CONFIG_REGISTRY",
|
|
62
|
-
"NPM_CONFIG_USERCONFIG",
|
|
63
|
-
"NPM_CONFIG_GLOBALCONFIG",
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
/** Whether env sanitization for verification is enabled (opt-out: enabled by default). */
|
|
67
|
-
export function isVerificationEnvSanitizeEnabled(): boolean {
|
|
68
|
-
// Opt-out: explicitly disabled via env var set to '0'.
|
|
69
|
-
if (process.env.PI_CREW_VERIFICATION_SANITIZE_ENV === "0" || process.env.PI_TEAMS_VERIFICATION_SANITIZE_ENV === "0") {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Build the env dict for a verification command. When sanitization is enabled,
|
|
77
|
-
* strips everything except VERIFICATION_ENV_ALLOWLIST + any explicitly-preserved
|
|
78
|
-
* keys (PI_CREW_VERIFICATION_PRESERVE_ENV=KEY1,KEY2). Always adds FORCE_COLOR=0
|
|
79
|
-
* to keep output plain-text (matches pre-existing behavior).
|
|
80
|
-
*/
|
|
81
|
-
function buildVerificationEnv(): Record<string, string> {
|
|
82
|
-
if (!isVerificationEnvSanitizeEnabled()) {
|
|
83
|
-
return { ...process.env, FORCE_COLOR: "0" };
|
|
84
|
-
}
|
|
85
|
-
const preserveRaw = process.env.PI_CREW_VERIFICATION_PRESERVE_ENV ?? process.env.PI_TEAMS_VERIFICATION_PRESERVE_ENV ?? "";
|
|
86
|
-
const preserve = preserveRaw
|
|
87
|
-
.split(",")
|
|
88
|
-
.map((s) => s.trim())
|
|
89
|
-
.filter((s) => s.length > 0);
|
|
90
|
-
const allowList = [...VERIFICATION_ENV_ALLOWLIST, ...preserve];
|
|
91
|
-
return {
|
|
92
|
-
...sanitizeEnvSecrets(process.env, { allowList }),
|
|
93
|
-
FORCE_COLOR: "0",
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export interface PhaseGateResult {
|
|
98
|
-
phase: number;
|
|
99
|
-
name: string;
|
|
100
|
-
status: "passed" | "failed" | "skipped";
|
|
101
|
-
command: string;
|
|
102
|
-
exitCode?: number | null;
|
|
103
|
-
output?: string;
|
|
104
|
-
durationMs: number;
|
|
105
|
-
error?: string;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface PhaseGateBundle {
|
|
109
|
-
results: PhaseGateResult[];
|
|
110
|
-
totalDurationMs: number;
|
|
111
|
-
allPassed: boolean;
|
|
112
|
-
stoppedAt?: number; // phase number where stopped
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Standard phase gate definitions for npm/TypeScript projects.
|
|
117
|
-
* Sequential enforcement: each phase must pass before proceeding.
|
|
118
|
-
*/
|
|
119
|
-
export const NPM_TYPESCRIPT_GATES: Array<{
|
|
120
|
-
name: string;
|
|
121
|
-
command: string;
|
|
122
|
-
critical: boolean;
|
|
123
|
-
}> = [
|
|
124
|
-
{ name: "build", command: "npm run build 2>&1", critical: true },
|
|
125
|
-
{ name: "typecheck", command: "npx tsc --noEmit 2>&1", critical: true },
|
|
126
|
-
{ name: "lint", command: "npm run lint 2>&1", critical: false },
|
|
127
|
-
{ name: "tests", command: "npm test 2>&1", critical: true },
|
|
128
|
-
];
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Cargo/Rust project phase gates.
|
|
132
|
-
*/
|
|
133
|
-
export const CARGO_RUST_GATES: Array<{
|
|
134
|
-
name: string;
|
|
135
|
-
command: string;
|
|
136
|
-
critical: boolean;
|
|
137
|
-
}> = [
|
|
138
|
-
{ name: "check", command: "cargo check 2>&1", critical: true },
|
|
139
|
-
{ name: "test", command: "cargo test 2>&1", critical: true },
|
|
140
|
-
{ name: "clippy", command: "cargo clippy 2>&1", critical: false },
|
|
141
|
-
];
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Execute a single command and capture output.
|
|
145
|
-
*/
|
|
146
|
-
/** Characters/patterns that indicate dangerous shell metacharacters. */
|
|
147
|
-
// Round 25 (VULN-3/VULN-4): also block raw newlines (sh -c treats \n as a
|
|
148
|
-
// command separator -> injection) and bare $VARNAME references (can exfiltrate
|
|
149
|
-
// secrets into captured gate output, e.g. `echo $ANTHROPIC_API_KEY`).
|
|
150
|
-
// $+word-char is blocked; special vars like $?/$$/$! are left alone. Built-in
|
|
151
|
-
// gates use only `2>&1` (no $VAR), so this does not break them.
|
|
152
|
-
// M-3/M-4 fix (code-review 2026-06-23): the old pattern used `>>` (append) and
|
|
153
|
-
// `<[^^&]` (a confusing char class). It allowed bare `>` (file redirect, e.g.
|
|
154
|
-
// `npm test > ~/.ssh/authorized_keys`) and used char-class semantics instead of
|
|
155
|
-
// a lookahead. Now `[<>](?![&\d])` blocks BOTH `<` and `>` file redirects while
|
|
156
|
-
// still permitting fd-duplication forms (`2>&1`, `1>&2`, `>&2`, `<&3`) via the
|
|
157
|
-
// negative lookahead (a `>`/`<` followed by `&` or a digit is allowed). A bare
|
|
158
|
-
// `&` is also blocked so an allowed gate cannot background itself and start a
|
|
159
|
-
// second command.
|
|
160
|
-
const DANGEROUS_SHELL_PATTERNS = /(?:;|&&|\|\||\$\(|`|\$\{|\$\w|\b(eval|exec)\b|[<>](?![&\d])|(?<![<>])&|[\r\n])/;
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Verification gates intentionally execute project-controlled code, but they
|
|
164
|
-
* must enter through a known build/test runner. Interpreter eval modes and
|
|
165
|
-
* arbitrary utilities are excluded: `node -e`, `python -c`, `curl`, etc. can
|
|
166
|
-
* otherwise execute attacker-provided code without any shell metacharacters.
|
|
167
|
-
*
|
|
168
|
-
* Prefixes are token-bound and each side of a pipeline is checked separately,
|
|
169
|
-
* preventing an allowed prefix such as `npm test | node -e ...` from bypassing
|
|
170
|
-
* the restriction. Keep this list focused on common verification commands.
|
|
171
|
-
*/
|
|
172
|
-
const ALLOWED_GATE_COMMAND_PATTERNS: readonly RegExp[] = [
|
|
173
|
-
/^npm\s+(?:test(?:\s|$)|run(?:-script)?\s+[A-Za-z0-9_.:-]+(?:\s|$))/,
|
|
174
|
-
/^pnpm\s+(?:test(?:\s|$)|run\s+[A-Za-z0-9_.:-]+(?:\s|$))/,
|
|
175
|
-
/^yarn\s+(?:test(?:\s|$)|run\s+[A-Za-z0-9_.:-]+(?:\s|$))/,
|
|
176
|
-
/^npx\s+(?:--no-install\s+)?(?:biome|eslint|tsc)(?:\s|$)/,
|
|
177
|
-
/^(?:biome|eslint|tsc)(?:\s|$)/,
|
|
178
|
-
/^node\s+(?:(?:--experimental-strip-types|--no-warnings)\s+)*--test(?:\s|$)/,
|
|
179
|
-
/^python(?:3(?:\.\d+)*)?\s+-m\s+(?:pytest|unittest)(?:\s|$)/,
|
|
180
|
-
/^pytest(?:\s|$)/,
|
|
181
|
-
/^cargo\s+(?:build|check|clippy|fmt|test)(?:\s|$)/,
|
|
182
|
-
/^go\s+(?:build|test|vet)(?:\s|$)/,
|
|
183
|
-
/^bun\s+test(?:\s|$)/,
|
|
184
|
-
/^deno\s+(?:check|lint|test)(?:\s|$)/,
|
|
185
|
-
/^dotnet\s+(?:build|test)(?:\s|$)/,
|
|
186
|
-
/^(?:gradle|\.\/gradlew)\s+(?:build|check|test)(?:\s|$)/,
|
|
187
|
-
/^(?:mvn|\.\/mvnw)\s+(?:package|test|verify)(?:\s|$)/,
|
|
188
|
-
/^make\s+(?:build|check|lint|test)(?:\s|$)/,
|
|
189
|
-
/^(?:echo|false|printenv|printf|true)(?:\s|$)/,
|
|
190
|
-
/^(?:grep|head|rg|tail)(?:\s|$)/,
|
|
191
|
-
];
|
|
192
|
-
|
|
193
|
-
const NODE_EVAL_MODE_PATTERN = /(?:^|\s)["']?(?:(?:-e|-p)(?!-)|--(?:eval|print)(?:=|\s|["']|$))/;
|
|
194
|
-
|
|
195
|
-
function validateAllowedGateCommands(normalized: string, original: string): void {
|
|
196
|
-
const pipelineCommands = normalized.split("|").map((part) => part.trim());
|
|
197
|
-
for (const pipelineCommand of pipelineCommands) {
|
|
198
|
-
if (/^node(?:\s|$)/.test(pipelineCommand) && NODE_EVAL_MODE_PATTERN.test(pipelineCommand)) {
|
|
199
|
-
throw new Error(`Security: verification gate command rejected (Node eval/print mode is not allowed): ${original}`);
|
|
200
|
-
}
|
|
201
|
-
if (!pipelineCommand || !ALLOWED_GATE_COMMAND_PATTERNS.some((pattern) => pattern.test(pipelineCommand))) {
|
|
202
|
-
throw new Error(`Security: verification gate command rejected (command is not allowlisted): ${original}`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Validate a verification gate command is safe to execute.
|
|
209
|
-
* Rejects commands with shell metacharacters that could enable injection and
|
|
210
|
-
* commands outside the verification-runner allowlist. Allows safe pipelines
|
|
211
|
-
* and stderr fd duplication (`2>&1`).
|
|
212
|
-
*/
|
|
213
|
-
/** @internal — exported for injection-guard unit testing (Round 25). */
|
|
214
|
-
export function __test__validateGateCommand(command: string): void {
|
|
215
|
-
validateGateCommand(command);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function validateGateCommand(command: string): void {
|
|
219
|
-
// Round 25 (VULN-3): check the ORIGINAL command for raw newlines BEFORE
|
|
220
|
-
// normalization. The regex below runs on the NORMALIZED command (which
|
|
221
|
-
// collapses \s+ incl. newlines to a single space), so a newline would be
|
|
222
|
-
// hidden from it - but `sh -c` treats a raw newline as a command
|
|
223
|
-
// separator, enabling injection (e.g. `npm test\nrm -rf x`).
|
|
224
|
-
if (/[\r\n]/.test(command)) {
|
|
225
|
-
throw new Error(
|
|
226
|
-
`Security: verification gate command rejected (raw newline - potential command injection): ${JSON.stringify(command)}`,
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
const normalized = command
|
|
230
|
-
.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "") // ANSI escape sequences
|
|
231
|
-
.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "") // control chars
|
|
232
|
-
.replace(/\\\n/g, " ") // escaped newlines
|
|
233
|
-
.replace(/\s+/g, " ") // collapse whitespace
|
|
234
|
-
.trim();
|
|
235
|
-
if (DANGEROUS_SHELL_PATTERNS.test(normalized)) {
|
|
236
|
-
throw new Error(`Security: verification gate command rejected (dangerous shell pattern): ${command}`);
|
|
237
|
-
}
|
|
238
|
-
validateAllowedGateCommands(normalized, command);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async function executeCommand(
|
|
242
|
-
command: string,
|
|
243
|
-
cwd: string,
|
|
244
|
-
timeoutMs: number = 120000,
|
|
245
|
-
): Promise<{ exitCode: number | null; output: string; durationMs: number }> {
|
|
246
|
-
// SECURITY: Validate command before shell execution to prevent injection.
|
|
247
|
-
validateGateCommand(command);
|
|
248
|
-
|
|
249
|
-
const start = Date.now();
|
|
250
|
-
let output = "";
|
|
251
|
-
let exitCode: number | null = null;
|
|
252
|
-
|
|
253
|
-
return new Promise((resolve) => {
|
|
254
|
-
const shell = spawn("sh", ["-c", command], {
|
|
255
|
-
cwd,
|
|
256
|
-
timeout: timeoutMs,
|
|
257
|
-
env: buildVerificationEnv(),
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
shell.stdout?.on("data", (data) => {
|
|
261
|
-
output += data.toString();
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
shell.stderr?.on("data", (data) => {
|
|
265
|
-
output += data.toString();
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// M-9 fix (code-review 2026-06-23): store the timeout handle so it can be
|
|
269
|
-
// cleared when the process exits normally, and unref it so it does not
|
|
270
|
-
// keep the Node.js event loop alive for the full timeoutMs after all work
|
|
271
|
-
// is done (previously it fired shell.kill() on an already-dead process).
|
|
272
|
-
const timer = setTimeout(() => {
|
|
273
|
-
shell.kill("SIGKILL");
|
|
274
|
-
resolve({
|
|
275
|
-
exitCode: -1,
|
|
276
|
-
output: output + "\n[TIMEOUT: Command exceeded limit]",
|
|
277
|
-
durationMs: Date.now() - start,
|
|
278
|
-
});
|
|
279
|
-
}, timeoutMs);
|
|
280
|
-
timer.unref();
|
|
281
|
-
const clearTimer = (): void => clearTimeout(timer);
|
|
282
|
-
|
|
283
|
-
shell.on("close", (code) => {
|
|
284
|
-
clearTimer();
|
|
285
|
-
exitCode = code;
|
|
286
|
-
resolve({
|
|
287
|
-
exitCode,
|
|
288
|
-
output: output.slice(-100000), // Cap at 100KB
|
|
289
|
-
durationMs: Date.now() - start,
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
shell.on("error", (err) => {
|
|
294
|
-
clearTimer();
|
|
295
|
-
resolve({
|
|
296
|
-
exitCode: -1,
|
|
297
|
-
output: `Execution error: ${err.message}`,
|
|
298
|
-
durationMs: Date.now() - start,
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Run phase gates sequentially, stopping on first critical failure.
|
|
306
|
-
*
|
|
307
|
-
* @param gates - Array of phase gate definitions
|
|
308
|
-
* @param cwd - Working directory to execute commands in
|
|
309
|
-
* @param signal - Optional abort signal
|
|
310
|
-
* @param onPhase - Optional callback for each phase completion
|
|
311
|
-
* @returns Phase gate bundle with all results
|
|
312
|
-
*/
|
|
313
|
-
export async function runPhaseGates(
|
|
314
|
-
gates: Array<{ name: string; command: string; critical: boolean }>,
|
|
315
|
-
cwd: string,
|
|
316
|
-
signal?: AbortSignal,
|
|
317
|
-
onPhase?: (result: PhaseGateResult) => void,
|
|
318
|
-
): Promise<PhaseGateBundle> {
|
|
319
|
-
const results: PhaseGateResult[] = [];
|
|
320
|
-
const startTime = Date.now();
|
|
321
|
-
let stoppedAt: number | undefined;
|
|
322
|
-
|
|
323
|
-
for (let i = 0; i < gates.length; i++) {
|
|
324
|
-
// Check abort signal
|
|
325
|
-
if (signal?.aborted) {
|
|
326
|
-
results.push({
|
|
327
|
-
phase: i + 1,
|
|
328
|
-
name: gates[i].name,
|
|
329
|
-
status: "skipped",
|
|
330
|
-
command: gates[i].command,
|
|
331
|
-
durationMs: 0,
|
|
332
|
-
error: "Aborted",
|
|
333
|
-
});
|
|
334
|
-
stoppedAt = i + 1;
|
|
335
|
-
break;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const gate = gates[i];
|
|
339
|
-
const phaseStart = Date.now();
|
|
340
|
-
|
|
341
|
-
// Execute the gate command
|
|
342
|
-
const { exitCode, output, durationMs } = await executeCommand(
|
|
343
|
-
gate.command,
|
|
344
|
-
cwd,
|
|
345
|
-
120000, // 2 minute timeout
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
const passed = exitCode === 0;
|
|
349
|
-
const result: PhaseGateResult = {
|
|
350
|
-
phase: i + 1,
|
|
351
|
-
name: gate.name,
|
|
352
|
-
status: passed ? "passed" : "failed",
|
|
353
|
-
command: gate.command,
|
|
354
|
-
exitCode,
|
|
355
|
-
output,
|
|
356
|
-
durationMs,
|
|
357
|
-
error: passed ? undefined : `Exit code: ${exitCode}`,
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
results.push(result);
|
|
361
|
-
onPhase?.(result);
|
|
362
|
-
|
|
363
|
-
// Stop on critical failure
|
|
364
|
-
if (!passed && gate.critical) {
|
|
365
|
-
stoppedAt = i + 1;
|
|
366
|
-
break;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return {
|
|
371
|
-
results,
|
|
372
|
-
totalDurationMs: Date.now() - startTime,
|
|
373
|
-
allPassed: results.every((r) => r.status === "passed"),
|
|
374
|
-
stoppedAt,
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Execute verification commands from a task's verification contract.
|
|
380
|
-
* Maps the contract commands to phase gates and runs them sequentially.
|
|
381
|
-
*
|
|
382
|
-
* @param contract - Verification contract with commands to execute
|
|
383
|
-
* @param cwd - Working directory
|
|
384
|
-
* @param runId - Run ID for artifact naming
|
|
385
|
-
* @param taskId - Task ID for artifact naming
|
|
386
|
-
* @param artifactsRoot - Artifacts root directory
|
|
387
|
-
* @param signal - Optional abort signal
|
|
388
|
-
* @returns Array of verification command results
|
|
389
|
-
*/
|
|
390
|
-
export async function executeVerificationCommands(
|
|
391
|
-
contract: VerificationContract,
|
|
392
|
-
cwd: string,
|
|
393
|
-
runId: string,
|
|
394
|
-
taskId: string,
|
|
395
|
-
artifactsRoot: string,
|
|
396
|
-
signal?: AbortSignal,
|
|
397
|
-
/** Phase 1.5 #2 (RFC 16): when provided, run verification commands in this
|
|
398
|
-
* pristine git-worktree path instead of `cwd`. The caller is responsible
|
|
399
|
-
* for preparing + cleaning up the worktree (see verification-worktree.ts).
|
|
400
|
-
* When undefined, behavior is unchanged (run in `cwd`). */
|
|
401
|
-
worktreeCwd?: string,
|
|
402
|
-
): Promise<VerificationCommandResult[]> {
|
|
403
|
-
if (!contract.commands || contract.commands.length === 0) {
|
|
404
|
-
return [];
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const results: VerificationCommandResult[] = [];
|
|
408
|
-
|
|
409
|
-
// Map commands to phase gates
|
|
410
|
-
const gates = contract.commands.map((cmd, index) => ({
|
|
411
|
-
name: `verification-${index + 1}`,
|
|
412
|
-
command: cmd,
|
|
413
|
-
critical: true, // All verification commands are critical by default
|
|
414
|
-
}));
|
|
415
|
-
|
|
416
|
-
// Create artifacts directory
|
|
417
|
-
const gatesDir = path.join(artifactsRoot, "verification-gates");
|
|
418
|
-
if (!fs.existsSync(gatesDir)) {
|
|
419
|
-
fs.mkdirSync(gatesDir, { recursive: true });
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Phase 1.5 #2: run phase gates inside the worktree when provided.
|
|
423
|
-
const execCwd = worktreeCwd ?? cwd;
|
|
424
|
-
|
|
425
|
-
// Run phase gates
|
|
426
|
-
const bundle = await runPhaseGates(gates, execCwd, signal, (phaseResult) => {
|
|
427
|
-
// P1f: redact secrets from verification output BEFORE persisting to the
|
|
428
|
-
// world-readable artifact file. redactSecretString is best-effort vs
|
|
429
|
-
// adversarial workers (RFC §6 — Med-High residual). writeArtifact ALSO
|
|
430
|
-
// redacts (defense-in-depth); this explicit pass sanitizes the raw output
|
|
431
|
-
// at the source so the in-memory bundle and the summary below are clean.
|
|
432
|
-
const safeOutput = redactSecretString(phaseResult.output || "");
|
|
433
|
-
// Write phase artifact immediately for observability
|
|
434
|
-
const phaseArtifact = writeArtifact(artifactsRoot, {
|
|
435
|
-
kind: "log",
|
|
436
|
-
relativePath: `verification-gates/${taskId}-phase-${phaseResult.phase}-${phaseResult.name}.log`,
|
|
437
|
-
content: [
|
|
438
|
-
`# Phase ${phaseResult.phase}: ${phaseResult.name}`,
|
|
439
|
-
`Status: ${phaseResult.status.toUpperCase()}`,
|
|
440
|
-
`Command: ${phaseResult.command}`,
|
|
441
|
-
`Duration: ${phaseResult.durationMs}ms`,
|
|
442
|
-
phaseResult.exitCode != null ? `Exit Code: ${phaseResult.exitCode}` : "",
|
|
443
|
-
phaseResult.error ? `Error: ${phaseResult.error}` : "",
|
|
444
|
-
"",
|
|
445
|
-
"## Output",
|
|
446
|
-
safeOutput || "(no output)",
|
|
447
|
-
].join("\n"),
|
|
448
|
-
producer: taskId,
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
results.push({
|
|
452
|
-
cmd: phaseResult.command,
|
|
453
|
-
status: phaseResult.status === "passed" ? "passed" : "failed",
|
|
454
|
-
exitCode: phaseResult.exitCode,
|
|
455
|
-
outputArtifact: phaseArtifact,
|
|
456
|
-
});
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
// Write summary artifact. Redact the whole bundle JSON (it embeds the raw
|
|
460
|
-
// per-phase output strings) BEFORE writeArtifact persists it.
|
|
461
|
-
const summaryArtifact = writeArtifact(artifactsRoot, {
|
|
462
|
-
kind: "metadata",
|
|
463
|
-
relativePath: `verification-gates/${taskId}-summary.json`,
|
|
464
|
-
content: redactSecretString(JSON.stringify(bundle, null, 2)),
|
|
465
|
-
producer: taskId,
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
// Fill in any remaining results (in case of early exit)
|
|
469
|
-
for (let i = results.length; i < gates.length; i++) {
|
|
470
|
-
results.push({
|
|
471
|
-
cmd: gates[i].command,
|
|
472
|
-
status: "not_run",
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return results;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* Compute observed green level from verification results.
|
|
481
|
-
* Maps verification outcomes to green levels per ECC pattern.
|
|
482
|
-
*
|
|
483
|
-
* @param commands - Array of verification command results
|
|
484
|
-
* @param requiredLevel - Required green level from contract
|
|
485
|
-
* @returns Observed green level
|
|
486
|
-
*/
|
|
487
|
-
export function computeGreenLevelFromResults(commands: VerificationCommandResult[], requiredLevel: GreenLevel): GreenLevel {
|
|
488
|
-
if (commands.length === 0) {
|
|
489
|
-
return "none";
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
const passed = commands.filter((c) => c.status === "passed").length;
|
|
493
|
-
const failed = commands.filter((c) => c.status === "failed").length;
|
|
494
|
-
const notRun = commands.filter((c) => c.status === "not_run").length;
|
|
495
|
-
|
|
496
|
-
// If any critical verification failed, return none
|
|
497
|
-
if (failed > 0) {
|
|
498
|
-
return "none";
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// If all passed, return the required level (capped at merge_ready)
|
|
502
|
-
if (passed === commands.length) {
|
|
503
|
-
return requiredLevel === "none" ? "targeted" : requiredLevel;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// Partial pass - return targeted
|
|
507
|
-
if (passed > 0) {
|
|
508
|
-
return "targeted";
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// Nothing run
|
|
512
|
-
return "none";
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Create a verification gate report artifact.
|
|
517
|
-
* Formatted for human review per ECC verification-loop pattern.
|
|
518
|
-
*/
|
|
519
|
-
/** @internal */
|
|
520
|
-
function createVerificationGateReport(
|
|
521
|
-
taskId: string,
|
|
522
|
-
contract: VerificationContract,
|
|
523
|
-
results: VerificationCommandResult[],
|
|
524
|
-
bundle: PhaseGateBundle,
|
|
525
|
-
): string {
|
|
526
|
-
const lines = [
|
|
527
|
-
`# Verification Gate Report: ${taskId}`,
|
|
528
|
-
"",
|
|
529
|
-
`## Contract`,
|
|
530
|
-
`- Required Green Level: ${contract.requiredGreenLevel}`,
|
|
531
|
-
`- Allow Manual Evidence: ${contract.allowManualEvidence}`,
|
|
532
|
-
`- Commands: ${contract.commands.length}`,
|
|
533
|
-
"",
|
|
534
|
-
`## Results`,
|
|
535
|
-
"",
|
|
536
|
-
`| Phase | Command | Status | Exit Code | Duration |`,
|
|
537
|
-
`|-------|---------|--------|-----------|----------|`,
|
|
538
|
-
];
|
|
539
|
-
|
|
540
|
-
for (const result of results) {
|
|
541
|
-
const phaseIndex = results.indexOf(result) + 1;
|
|
542
|
-
const statusIcon = result.status === "passed" ? "✅" : result.status === "failed" ? "❌" : "⏭️";
|
|
543
|
-
lines.push(
|
|
544
|
-
`| ${phaseIndex} | \`${truncate(result.cmd, 40)}\` | ${statusIcon} ${result.status} | ${result.exitCode ?? "-"} | ${result.durationMs ?? 0}ms |`,
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
lines.push("");
|
|
549
|
-
lines.push(`## Summary`);
|
|
550
|
-
lines.push(`- Total Phases: ${bundle.results.length}`);
|
|
551
|
-
lines.push(`- Passed: ${bundle.results.filter((r) => r.status === "passed").length}`);
|
|
552
|
-
lines.push(`- Failed: ${bundle.results.filter((r) => r.status === "failed").length}`);
|
|
553
|
-
lines.push(`- Skipped: ${bundle.results.filter((r) => r.status === "skipped").length}`);
|
|
554
|
-
lines.push(`- Total Duration: ${bundle.totalDurationMs}ms`);
|
|
555
|
-
lines.push(`- All Passed: ${bundle.allPassed ? "YES ✅" : "NO ❌"}`);
|
|
556
|
-
|
|
557
|
-
if (bundle.stoppedAt) {
|
|
558
|
-
lines.push(`- Stopped At: Phase ${bundle.stoppedAt}`);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
lines.push("");
|
|
562
|
-
lines.push("## VERIFICATION");
|
|
563
|
-
lines.push(bundle.allPassed ? "**PASSED** - All gates green ✅" : "**FAILED** - One or more gates red ❌");
|
|
564
|
-
|
|
565
|
-
return lines.join("\n");
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
function truncate(str: string, maxLen: number): string {
|
|
569
|
-
if (str.length <= maxLen) return str;
|
|
570
|
-
return str.slice(0, maxLen - 3) + "...";
|
|
571
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Verification Integrity — manifest bookend snapshot helper (RFC §P1a).
|
|
3
|
-
*
|
|
4
|
-
* Hashes a FIXED set of project-manifest files so the goal loop can detect
|
|
5
|
-
* drift between T_snap (before verification runs) and T_verify_done (after the
|
|
6
|
-
* command exits). This closes the PERSISTENT-edit subcase of workspace
|
|
7
|
-
* tampering (a worker that rewrites package.json / lockfile and leaves it
|
|
8
|
-
* changed). See RFC §P1a / §6 STRIDE for the full threat model.
|
|
9
|
-
*
|
|
10
|
-
* RESIDUALS (documented; closed by Phase 1.5 git-worktree sandbox, NOT here):
|
|
11
|
-
* - Round-trip tamper: a worker can edit a manifest, run the test, then REVERT
|
|
12
|
-
* before T_verify_done so the hash matches T_snap. Content-addressed
|
|
13
|
-
* execution (git-worktree) is required to close this. Not fixable by hashing.
|
|
14
|
-
* - Invoked-script tampering: only the manifest files in MANIFEST_FILES are
|
|
15
|
-
* hashed. A worker that overwrites a script the verification command invokes
|
|
16
|
-
* is NOT caught. Phase 1.5 git-worktree closes this.
|
|
17
|
-
* - node_modules/ and transitive deps are deliberately NOT hashed (size +
|
|
18
|
-
* churn); package-lock.json IS hashed, which transitively pins resolved
|
|
19
|
-
* dependency versions.
|
|
20
|
-
*
|
|
21
|
-
* Pure leaf module: depends only on node: built-ins. Does NOT import
|
|
22
|
-
* goal-loop-runner or goal-evaluator (keeps the P1a helper unit-testable in
|
|
23
|
-
* isolation and avoids pulling the conflict-zone modules into this file).
|
|
24
|
-
*
|
|
25
|
-
* @module verification-integrity
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import { createHash } from "node:crypto";
|
|
29
|
-
import * as fs from "node:fs";
|
|
30
|
-
import * as path from "node:path";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Fixed set of project-manifest files considered by {@link snapshotManifests}.
|
|
34
|
-
* Only files from this set that EXIST in the target directory are hashed.
|
|
35
|
-
* (RFC §P1a: package.json, package-lock.json, pyproject.toml, setup.py,
|
|
36
|
-
* Cargo.toml, Cargo.lock, go.mod, tsconfig.json.)
|
|
37
|
-
*/
|
|
38
|
-
export const MANIFEST_FILES = [
|
|
39
|
-
"package.json",
|
|
40
|
-
"package-lock.json",
|
|
41
|
-
"pyproject.toml",
|
|
42
|
-
"setup.py",
|
|
43
|
-
"Cargo.toml",
|
|
44
|
-
"Cargo.lock",
|
|
45
|
-
"go.mod",
|
|
46
|
-
"tsconfig.json",
|
|
47
|
-
] as const;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* sha256-hash the manifest files from the fixed set that EXIST in `cwd`.
|
|
51
|
-
*
|
|
52
|
-
* - Missing files are SKIPPED silently (not errors): a Python project has no
|
|
53
|
-
* package.json, a JS project has no Cargo.toml, etc.
|
|
54
|
-
* - Non-regular files (directories, etc.) are skipped.
|
|
55
|
-
* - node_modules is NEVER hashed.
|
|
56
|
-
*
|
|
57
|
-
* @returns A map of relative manifest path -> sha256 hex digest for each
|
|
58
|
-
* present file. Stable key order = MANIFEST_FILES order (insertion order).
|
|
59
|
-
*/
|
|
60
|
-
export function snapshotManifests(cwd: string): Record<string, string> {
|
|
61
|
-
const snapshot: Record<string, string> = {};
|
|
62
|
-
for (const rel of MANIFEST_FILES) {
|
|
63
|
-
const abs = path.join(cwd, rel);
|
|
64
|
-
let stat: fs.Stats;
|
|
65
|
-
try {
|
|
66
|
-
stat = fs.statSync(abs);
|
|
67
|
-
} catch {
|
|
68
|
-
continue; // missing file — skip gracefully
|
|
69
|
-
}
|
|
70
|
-
if (!stat.isFile()) continue; // directory / special — skip
|
|
71
|
-
try {
|
|
72
|
-
const content = fs.readFileSync(abs);
|
|
73
|
-
snapshot[rel] = createHash("sha256").update(content).digest("hex");
|
|
74
|
-
} catch {}
|
|
75
|
-
}
|
|
76
|
-
return snapshot;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Compare two snapshots and return the list of DRIFTED file paths.
|
|
81
|
-
*
|
|
82
|
-
* A file is considered drifted if:
|
|
83
|
-
* - its hash differs between `a` and `b`, OR
|
|
84
|
-
* - it is present in only one of the two snapshots (added or removed).
|
|
85
|
-
*
|
|
86
|
-
* @returns Sorted array of relative manifest paths that drifted. Identical
|
|
87
|
-
* snapshots yield `[]`.
|
|
88
|
-
*/
|
|
89
|
-
export function compareSnapshot(a: Record<string, string>, b: Record<string, string>): string[] {
|
|
90
|
-
const drifted = new Set<string>();
|
|
91
|
-
for (const [key, hash] of Object.entries(a)) {
|
|
92
|
-
const other = b[key];
|
|
93
|
-
if (other === undefined) {
|
|
94
|
-
drifted.add(key); // removed between a -> b
|
|
95
|
-
} else if (other !== hash) {
|
|
96
|
-
drifted.add(key); // content changed
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
for (const key of Object.keys(b)) {
|
|
100
|
-
if (a[key] === undefined) {
|
|
101
|
-
drifted.add(key); // added between a -> b
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return [...drifted].sort();
|
|
105
|
-
}
|