session-orchestrator 3.16.0
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/.claude-plugin/marketplace.json +29 -0
- package/.claude-plugin/plugin.json +18 -0
- package/.codex-plugin/agents/explorer.toml +14 -0
- package/.codex-plugin/agents/session-reviewer.toml +23 -0
- package/.codex-plugin/agents/wave-worker.toml +15 -0
- package/.codex-plugin/config.toml +20 -0
- package/.codex-plugin/plugin.json +37 -0
- package/.cursor/rules/000-session-orchestrator.mdc +73 -0
- package/.cursor/rules/010-session-workflow.mdc +170 -0
- package/.cursor/rules/020-quality-gates.mdc +128 -0
- package/.cursor/rules/030-wave-execution.mdc +216 -0
- package/.cursor/rules/040-discovery.mdc +242 -0
- package/.cursor/rules/050-plan.mdc +235 -0
- package/.cursor/rules/060-evolve.mdc +232 -0
- package/.cursor/rules/070-gitlab-ops.mdc +246 -0
- package/.cursor/rules/080-ecosystem-health.mdc +145 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +1544 -0
- package/LICENSE +21 -0
- package/NOTICE +64 -0
- package/README.md +242 -0
- package/SECURITY.md +90 -0
- package/agents/AGENTS.md +136 -0
- package/agents/analyst.md +99 -0
- package/agents/architect-reviewer.md +93 -0
- package/agents/code-implementer.md +106 -0
- package/agents/db-specialist.md +104 -0
- package/agents/dialectic-deriver.md +139 -0
- package/agents/docs-writer.md +113 -0
- package/agents/eval-judge.md +146 -0
- package/agents/memory-proposal-collector.md +297 -0
- package/agents/qa-strategist.md +102 -0
- package/agents/schemas/analyst.schema.json +46 -0
- package/agents/schemas/architect-reviewer.schema.json +50 -0
- package/agents/schemas/code-implementer.schema.json +61 -0
- package/agents/schemas/db-specialist.schema.json +80 -0
- package/agents/schemas/docs-writer.schema.json +56 -0
- package/agents/schemas/persona-panel-sidecar.schema.json +245 -0
- package/agents/schemas/qa-strategist.schema.json +46 -0
- package/agents/schemas/security-reviewer.schema.json +86 -0
- package/agents/schemas/session-reviewer.schema.json +69 -0
- package/agents/schemas/test-writer.schema.json +69 -0
- package/agents/schemas/ui-developer.schema.json +90 -0
- package/agents/schemas/ux-evaluator.schema.json +51 -0
- package/agents/security-reviewer.md +236 -0
- package/agents/session-reviewer.md +201 -0
- package/agents/skill-applied-judge.md +122 -0
- package/agents/test-writer.md +123 -0
- package/agents/ui-developer.md +109 -0
- package/agents/ux-evaluator.md +161 -0
- package/assets/icon.svg +11 -0
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +47 -0
- package/commands/autopilot-multi.md +74 -0
- package/commands/autopilot.md +80 -0
- package/commands/bootstrap.md +56 -0
- package/commands/brainstorm.md +48 -0
- package/commands/close.md +24 -0
- package/commands/debug.md +36 -0
- package/commands/discovery.md +32 -0
- package/commands/dispatcher.md +59 -0
- package/commands/eval.md +28 -0
- package/commands/evolve.md +10 -0
- package/commands/go.md +41 -0
- package/commands/grill.md +45 -0
- package/commands/harness-audit.md +26 -0
- package/commands/memory-cleanup.md +25 -0
- package/commands/persona-panel.md +121 -0
- package/commands/plan.md +15 -0
- package/commands/portfolio.md +97 -0
- package/commands/reconcile.md +23 -0
- package/commands/repo-audit.md +24 -0
- package/commands/session.md +30 -0
- package/commands/spinout.md +15 -0
- package/commands/sunset-review.md +27 -0
- package/commands/templates-ack.md +96 -0
- package/commands/test.md +97 -0
- package/docs/README.md +105 -0
- package/docs/USER-GUIDE.md +1403 -0
- package/docs/ci-setup.md +81 -0
- package/docs/codex-setup.md +142 -0
- package/docs/components.md +74 -0
- package/docs/cursor-setup.md +104 -0
- package/docs/events-schema.md +81 -0
- package/docs/migration-v3.md +148 -0
- package/docs/owner-config-schema.md +154 -0
- package/docs/persona-panel.md +433 -0
- package/docs/pi-setup.md +115 -0
- package/docs/plugin-architecture-v3.md +296 -0
- package/docs/pm-skills-marketplace.md +114 -0
- package/docs/policy-cache-validation-2026-04-28.md +118 -0
- package/docs/rule-authoring.md +316 -0
- package/docs/session-config-reference.md +1439 -0
- package/docs/session-config-template.md +961 -0
- package/docs/vault-docs-architecture.md +297 -0
- package/hooks/_lib/lock-bootstrap.mjs +272 -0
- package/hooks/_lib/lock-reconcile.mjs +93 -0
- package/hooks/_lib/profile-gate.mjs +95 -0
- package/hooks/_lib/transcript-history.mjs +211 -0
- package/hooks/agent-teams-h3-test.sh +362 -0
- package/hooks/config-protection.mjs +0 -0
- package/hooks/cwd-change-restore.mjs +131 -0
- package/hooks/enforce-commands.mjs +179 -0
- package/hooks/enforce-scope.mjs +273 -0
- package/hooks/hooks-codex.json +60 -0
- package/hooks/hooks-cursor.json +15 -0
- package/hooks/hooks-pi.json +115 -0
- package/hooks/hooks.json +215 -0
- package/hooks/loop-guard.mjs +260 -0
- package/hooks/on-session-end.mjs +217 -0
- package/hooks/on-session-start.mjs +660 -0
- package/hooks/on-stop.mjs +294 -0
- package/hooks/operator-steer.mjs +64 -0
- package/hooks/post-edit-validate.mjs +225 -0
- package/hooks/post-subagent-discovery-validator.mjs +398 -0
- package/hooks/post-tool-batch-wave-signal.mjs +328 -0
- package/hooks/post-tool-failure-corrective-context.mjs +248 -0
- package/hooks/post-tooluse-frontend-slop.mjs +184 -0
- package/hooks/pre-bash-destructive-guard.mjs +515 -0
- package/hooks/pre-bash-memory-propose-audit.mjs +206 -0
- package/hooks/pre-bash-staging-fence.mjs +223 -0
- package/hooks/pre-bash-templates-first.mjs +404 -0
- package/hooks/run-node.sh +72 -0
- package/hooks/skill-invocation-telemetry.mjs +99 -0
- package/hooks/subagent-telemetry.mjs +249 -0
- package/hooks/wave-scope-commit-guard.mjs +191 -0
- package/monitors/monitors.json +14 -0
- package/output-styles/finding-report.md +48 -0
- package/output-styles/session-report.md +53 -0
- package/output-styles/wave-summary.md +38 -0
- package/package.json +94 -0
- package/pi/extensions/session-orchestrator.ts +25 -0
- package/pi/prompts/autopilot-multi.md +12 -0
- package/pi/prompts/autopilot.md +12 -0
- package/pi/prompts/bootstrap.md +12 -0
- package/pi/prompts/brainstorm.md +12 -0
- package/pi/prompts/close.md +11 -0
- package/pi/prompts/debug.md +12 -0
- package/pi/prompts/discovery.md +12 -0
- package/pi/prompts/dispatcher.md +12 -0
- package/pi/prompts/eval.md +12 -0
- package/pi/prompts/evolve.md +12 -0
- package/pi/prompts/go.md +12 -0
- package/pi/prompts/grill.md +12 -0
- package/pi/prompts/harness-audit.md +12 -0
- package/pi/prompts/memory-cleanup.md +12 -0
- package/pi/prompts/persona-panel.md +12 -0
- package/pi/prompts/plan.md +12 -0
- package/pi/prompts/portfolio.md +12 -0
- package/pi/prompts/reconcile.md +12 -0
- package/pi/prompts/repo-audit.md +12 -0
- package/pi/prompts/session.md +12 -0
- package/pi/prompts/spinout.md +12 -0
- package/pi/prompts/sunset-review.md +12 -0
- package/pi/prompts/templates-ack.md +12 -0
- package/pi/prompts/test.md +12 -0
- package/rules/_index.md +51 -0
- package/rules/always-on/commit-discipline.md +26 -0
- package/rules/always-on/npm-quality-gates.md +26 -0
- package/rules/always-on/parallel-sessions.md +43 -0
- package/rules/opt-in-domain/prompt-caching.md +270 -0
- package/rules/opt-in-stack/backend-data.md +188 -0
- package/rules/opt-in-stack/backend.md +390 -0
- package/rules/opt-in-stack/frontend.md +98 -0
- package/rules/opt-in-stack/security-web.md +194 -0
- package/rules/opt-in-stack/swift.md +65 -0
- package/scripts/archive-closed-prds.mjs +416 -0
- package/scripts/autopilot-multi.mjs +802 -0
- package/scripts/autopilot.mjs +383 -0
- package/scripts/backfill-abandoned-sessions.mjs +265 -0
- package/scripts/backfill-learnings-expires.mjs +196 -0
- package/scripts/backfill-learnings.mjs +203 -0
- package/scripts/backfill-sessions.mjs +282 -0
- package/scripts/check-doc-consistency.sh +279 -0
- package/scripts/check-package-manager.mjs +445 -0
- package/scripts/ci/assert-vitest-green.mjs +267 -0
- package/scripts/codex-install.mjs +435 -0
- package/scripts/compute-grounding-injection.sh +186 -0
- package/scripts/cursor-install.mjs +113 -0
- package/scripts/dialectic-deriver.mjs +573 -0
- package/scripts/emit-event.mjs +160 -0
- package/scripts/emit-session.mjs +212 -0
- package/scripts/eval-session.mjs +262 -0
- package/scripts/export-hw-learnings.mjs +437 -0
- package/scripts/gc-stale-worktrees.mjs +666 -0
- package/scripts/generate-pi-prompts.mjs +127 -0
- package/scripts/harness-audit.mjs +287 -0
- package/scripts/lib/agent-frontmatter.mjs +266 -0
- package/scripts/lib/agent-output-schema.mjs +166 -0
- package/scripts/lib/agent-status.mjs +303 -0
- package/scripts/lib/ajv-loader.mjs +34 -0
- package/scripts/lib/auto-dialectic.mjs +382 -0
- package/scripts/lib/auto-dream.mjs +471 -0
- package/scripts/lib/autonomy/suitability.mjs +212 -0
- package/scripts/lib/autopilot/dep-graph.mjs +417 -0
- package/scripts/lib/autopilot/durable-telemetry.mjs +121 -0
- package/scripts/lib/autopilot/flags.mjs +104 -0
- package/scripts/lib/autopilot/kill-switches.mjs +174 -0
- package/scripts/lib/autopilot/loop.mjs +320 -0
- package/scripts/lib/autopilot/mr-draft.mjs +520 -0
- package/scripts/lib/autopilot/multi-killswitch.mjs +184 -0
- package/scripts/lib/autopilot/recent-runs.mjs +106 -0
- package/scripts/lib/autopilot/stall-sampler.mjs +97 -0
- package/scripts/lib/autopilot/telemetry.mjs +224 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +605 -0
- package/scripts/lib/autopilot-telemetry.mjs +11 -0
- package/scripts/lib/autopilot.mjs +39 -0
- package/scripts/lib/backlog-scan.mjs +179 -0
- package/scripts/lib/bootstrap-lock-freshness.mjs +260 -0
- package/scripts/lib/bootstrap-lock-refresh.mjs +186 -0
- package/scripts/lib/build-live-signals.mjs +150 -0
- package/scripts/lib/ci-status-banner.mjs +425 -0
- package/scripts/lib/claude-md-budget-lint.mjs +246 -0
- package/scripts/lib/cli-flags.mjs +158 -0
- package/scripts/lib/codex/plugin-contract.mjs +610 -0
- package/scripts/lib/cold-start-detector.mjs +240 -0
- package/scripts/lib/command-blocker.mjs +458 -0
- package/scripts/lib/common.mjs +333 -0
- package/scripts/lib/config/auto-dream.mjs +77 -0
- package/scripts/lib/config/block-header.mjs +94 -0
- package/scripts/lib/config/broken-window.mjs +114 -0
- package/scripts/lib/config/coercers.mjs +248 -0
- package/scripts/lib/config/cold-start.mjs +92 -0
- package/scripts/lib/config/config-protection.mjs +120 -0
- package/scripts/lib/config/cross-repo.mjs +104 -0
- package/scripts/lib/config/custom-phases.mjs +213 -0
- package/scripts/lib/config/dialectic.mjs +92 -0
- package/scripts/lib/config/discovery-validator.mjs +75 -0
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +240 -0
- package/scripts/lib/config/dispatcher-autonomy.mjs +152 -0
- package/scripts/lib/config/docs-orchestrator.mjs +90 -0
- package/scripts/lib/config/docs-staleness.mjs +96 -0
- package/scripts/lib/config/drift-check.mjs +155 -0
- package/scripts/lib/config/eval.mjs +130 -0
- package/scripts/lib/config/events-rotation.mjs +74 -0
- package/scripts/lib/config/evolve.mjs +308 -0
- package/scripts/lib/config/frontend-slop-hook.mjs +104 -0
- package/scripts/lib/config/gitlab-portfolio.mjs +150 -0
- package/scripts/lib/config/handover-gate.mjs +106 -0
- package/scripts/lib/config/host-paths.mjs +76 -0
- package/scripts/lib/config/io.mjs +54 -0
- package/scripts/lib/config/loop-guard.mjs +117 -0
- package/scripts/lib/config/memory.mjs +150 -0
- package/scripts/lib/config/persona-gate-wave.mjs +258 -0
- package/scripts/lib/config/reconcile.mjs +205 -0
- package/scripts/lib/config/section-extractor.mjs +100 -0
- package/scripts/lib/config/skill-evolution.mjs +112 -0
- package/scripts/lib/config/slopcheck.mjs +99 -0
- package/scripts/lib/config/state-md-lock.mjs +83 -0
- package/scripts/lib/config/templates-first.mjs +94 -0
- package/scripts/lib/config/test.mjs +113 -0
- package/scripts/lib/config/vault-integration.mjs +201 -0
- package/scripts/lib/config/vault-mirror-quality.mjs +99 -0
- package/scripts/lib/config/vault-staleness.mjs +84 -0
- package/scripts/lib/config/vault-sync.mjs +96 -0
- package/scripts/lib/config/verification-auto-fix.mjs +84 -0
- package/scripts/lib/config/wave-reviewers.mjs +133 -0
- package/scripts/lib/config-schema.mjs +345 -0
- package/scripts/lib/config.mjs +474 -0
- package/scripts/lib/convergence-monitor.mjs +389 -0
- package/scripts/lib/coordinator-snapshot.mjs +371 -0
- package/scripts/lib/crypto-digest-utils.mjs +91 -0
- package/scripts/lib/discovery/helpers.mjs +127 -0
- package/scripts/lib/discovery/triage-state.mjs +279 -0
- package/scripts/lib/dispatcher/cli.mjs +257 -0
- package/scripts/lib/dispatcher/enumerate.mjs +243 -0
- package/scripts/lib/dispatcher/rank.mjs +363 -0
- package/scripts/lib/ecosystem-health.mjs +224 -0
- package/scripts/lib/ecosystem-wizard/ci-detector.mjs +18 -0
- package/scripts/lib/ecosystem-wizard/config-parser.mjs +54 -0
- package/scripts/lib/ecosystem-wizard/config-writer.mjs +287 -0
- package/scripts/lib/ecosystem-wizard/package-manager-detector.mjs +42 -0
- package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +246 -0
- package/scripts/lib/ecosystem-wizard.mjs +48 -0
- package/scripts/lib/env-check.mjs +89 -0
- package/scripts/lib/eval/engine.mjs +605 -0
- package/scripts/lib/eval/judge.mjs +433 -0
- package/scripts/lib/eval/report.mjs +367 -0
- package/scripts/lib/eval/schema.mjs +618 -0
- package/scripts/lib/eval/session-resolve.mjs +137 -0
- package/scripts/lib/eval/sink.mjs +77 -0
- package/scripts/lib/events-rotation.mjs +86 -0
- package/scripts/lib/events-schema.mjs +81 -0
- package/scripts/lib/events.mjs +80 -0
- package/scripts/lib/evolve/autonomy-verdict.mjs +461 -0
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +293 -0
- package/scripts/lib/exclusivity-matrix.mjs +68 -0
- package/scripts/lib/fetch-baseline.mjs +311 -0
- package/scripts/lib/file-lock.mjs +512 -0
- package/scripts/lib/frontend-detect/detect.mjs +138 -0
- package/scripts/lib/frontend-detect/rules.mjs +295 -0
- package/scripts/lib/frontmatter-guard.mjs +241 -0
- package/scripts/lib/gates/echo-stub-detect.mjs +39 -0
- package/scripts/lib/gates/gate-baseline.mjs +42 -0
- package/scripts/lib/gates/gate-full.mjs +85 -0
- package/scripts/lib/gates/gate-helpers.mjs +231 -0
- package/scripts/lib/gates/gate-incremental.mjs +76 -0
- package/scripts/lib/gates/gate-per-file.mjs +55 -0
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/aggregator.mjs +383 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +428 -0
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +289 -0
- package/scripts/lib/gitlab-portfolio/vcs-detect.mjs +182 -0
- package/scripts/lib/handover-gate.mjs +222 -0
- package/scripts/lib/hardening.mjs +43 -0
- package/scripts/lib/hardware-pattern-detector.mjs +238 -0
- package/scripts/lib/harness-audit/categories/category1.mjs +123 -0
- package/scripts/lib/harness-audit/categories/category2.mjs +145 -0
- package/scripts/lib/harness-audit/categories/category3.mjs +143 -0
- package/scripts/lib/harness-audit/categories/category4.mjs +202 -0
- package/scripts/lib/harness-audit/categories/category5.mjs +152 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +211 -0
- package/scripts/lib/harness-audit/categories/category7.mjs +125 -0
- package/scripts/lib/harness-audit/categories/category8.mjs +328 -0
- package/scripts/lib/harness-audit/categories/category9.mjs +294 -0
- package/scripts/lib/harness-audit/categories/helpers.mjs +165 -0
- package/scripts/lib/harness-audit/categories.mjs +19 -0
- package/scripts/lib/historical-guard.mjs +15 -0
- package/scripts/lib/host-identity.mjs +262 -0
- package/scripts/lib/instruction-budget-guard.mjs +332 -0
- package/scripts/lib/io.mjs +304 -0
- package/scripts/lib/issue-close-strip-labels.mjs +161 -0
- package/scripts/lib/language-mappers/README.md +57 -0
- package/scripts/lib/language-mappers/index.mjs +165 -0
- package/scripts/lib/language-mappers/markdown.mjs +149 -0
- package/scripts/lib/language-mappers/python.mjs +249 -0
- package/scripts/lib/language-mappers/swift.mjs +201 -0
- package/scripts/lib/language-mappers/typescript.mjs +433 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +164 -0
- package/scripts/lib/learnings/filters.mjs +43 -0
- package/scripts/lib/learnings/io.mjs +255 -0
- package/scripts/lib/learnings/schema.mjs +518 -0
- package/scripts/lib/learnings/surface.mjs +207 -0
- package/scripts/lib/learnings.mjs +42 -0
- package/scripts/lib/lock-reaper.mjs +648 -0
- package/scripts/lib/locks/index.mjs +31 -0
- package/scripts/lib/locks/lock-body.mjs +62 -0
- package/scripts/lib/locks/staging-fence-lock.mjs +267 -0
- package/scripts/lib/locks/state-md-lock.mjs +351 -0
- package/scripts/lib/loop-readiness-banner.mjs +144 -0
- package/scripts/lib/memory-banner.mjs +478 -0
- package/scripts/lib/memory-cleanup/worktree-sweep.mjs +108 -0
- package/scripts/lib/memory-cleanup-stamp.mjs +56 -0
- package/scripts/lib/memory-paths.mjs +31 -0
- package/scripts/lib/memory-proposals/collector.mjs +334 -0
- package/scripts/lib/memory-proposals/schema.mjs +289 -0
- package/scripts/lib/memory-proposals/sink.mjs +507 -0
- package/scripts/lib/memory-proposals/store.mjs +441 -0
- package/scripts/lib/mission-status-schema.mjs +114 -0
- package/scripts/lib/mode-selector/alternatives.mjs +64 -0
- package/scripts/lib/mode-selector/constants.mjs +29 -0
- package/scripts/lib/mode-selector/context-pressure.mjs +157 -0
- package/scripts/lib/mode-selector/rationale.mjs +55 -0
- package/scripts/lib/mode-selector/scoring.mjs +221 -0
- package/scripts/lib/mode-selector-accuracy.mjs +121 -0
- package/scripts/lib/mode-selector.mjs +160 -0
- package/scripts/lib/multi-provider-build/providers.mjs +64 -0
- package/scripts/lib/multi-provider-build/templating.mjs +130 -0
- package/scripts/lib/named-baseline-resolver.mjs +233 -0
- package/scripts/lib/named-vault-resolver.mjs +433 -0
- package/scripts/lib/owner-config/coerce.mjs +29 -0
- package/scripts/lib/owner-config/constants.mjs +21 -0
- package/scripts/lib/owner-config/defaults.mjs +50 -0
- package/scripts/lib/owner-config/error.mjs +19 -0
- package/scripts/lib/owner-config/index.mjs +13 -0
- package/scripts/lib/owner-config/merge.mjs +52 -0
- package/scripts/lib/owner-config/validate.mjs +259 -0
- package/scripts/lib/owner-config-banner.mjs +126 -0
- package/scripts/lib/owner-config-loader.mjs +159 -0
- package/scripts/lib/owner-config.example.yaml +72 -0
- package/scripts/lib/owner-config.mjs +28 -0
- package/scripts/lib/owner-interview.mjs +243 -0
- package/scripts/lib/owner-yaml.mjs +571 -0
- package/scripts/lib/package-manager.mjs +160 -0
- package/scripts/lib/path-utils.mjs +217 -0
- package/scripts/lib/peer-cards/merger.mjs +310 -0
- package/scripts/lib/peer-cards/reader.mjs +125 -0
- package/scripts/lib/peer-cards/schema.mjs +230 -0
- package/scripts/lib/peer-cards/staleness-banner.mjs +86 -0
- package/scripts/lib/peer-cards/writer.mjs +138 -0
- package/scripts/lib/peer-discovery.mjs +200 -0
- package/scripts/lib/persona-panel/catalog-loader.mjs +577 -0
- package/scripts/lib/persona-panel/consolidator.mjs +370 -0
- package/scripts/lib/persona-panel/persona-runner.mjs +375 -0
- package/scripts/lib/persona-panel/threshold.mjs +130 -0
- package/scripts/lib/pi-hook-bridge.mjs +328 -0
- package/scripts/lib/platform.mjs +266 -0
- package/scripts/lib/playwright-driver/runner.mjs +297 -0
- package/scripts/lib/plugin-root.mjs +210 -0
- package/scripts/lib/pre-dispatch-check.mjs +126 -0
- package/scripts/lib/product-repo-detect.mjs +121 -0
- package/scripts/lib/profiles/registry.mjs +176 -0
- package/scripts/lib/profiles/schema.mjs +209 -0
- package/scripts/lib/qg-command-drift-banner.mjs +88 -0
- package/scripts/lib/quality-gate/diagnostics.mjs +92 -0
- package/scripts/lib/quality-gate.mjs +536 -0
- package/scripts/lib/quality-gates-cache.mjs +228 -0
- package/scripts/lib/quality-gates-policy.mjs +95 -0
- package/scripts/lib/recommendations-v0.mjs +156 -0
- package/scripts/lib/reconcile/eligibility.mjs +203 -0
- package/scripts/lib/reconcile/emitter.mjs +244 -0
- package/scripts/lib/reconcile/engine.mjs +412 -0
- package/scripts/lib/reconcile/idempotency.mjs +239 -0
- package/scripts/lib/reconcile/renderer.mjs +211 -0
- package/scripts/lib/reconcile/writer.mjs +293 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +284 -0
- package/scripts/lib/resource-probe/evaluate.mjs +190 -0
- package/scripts/lib/resource-probe/parsers.mjs +181 -0
- package/scripts/lib/resource-probe/probe-platform.mjs +300 -0
- package/scripts/lib/resource-probe.mjs +95 -0
- package/scripts/lib/rule-loader.mjs +552 -0
- package/scripts/lib/rules-sync.mjs +439 -0
- package/scripts/lib/scope-gate.mjs +496 -0
- package/scripts/lib/session-close-backfill.mjs +539 -0
- package/scripts/lib/session-discovery.mjs +256 -0
- package/scripts/lib/session-end/phase-skip.mjs +357 -0
- package/scripts/lib/session-end/worktree-cleanup.mjs +112 -0
- package/scripts/lib/session-id.mjs +362 -0
- package/scripts/lib/session-lock.mjs +703 -0
- package/scripts/lib/session-registry.mjs +355 -0
- package/scripts/lib/session-schema/aliases.mjs +71 -0
- package/scripts/lib/session-schema/constants.mjs +115 -0
- package/scripts/lib/session-schema/normalizer.mjs +66 -0
- package/scripts/lib/session-schema/timestamps.mjs +64 -0
- package/scripts/lib/session-schema/validator.mjs +453 -0
- package/scripts/lib/session-schema.mjs +71 -0
- package/scripts/lib/session-token-rollup.mjs +137 -0
- package/scripts/lib/sessions-staleness-banner.mjs +247 -0
- package/scripts/lib/skill-evolution/blast-radius-classifier.mjs +114 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +270 -0
- package/scripts/lib/skill-evolution/config-validation-gate.mjs +279 -0
- package/scripts/lib/skill-evolution/engine.mjs +719 -0
- package/scripts/lib/skill-evolution/idempotency.mjs +279 -0
- package/scripts/lib/skill-evolution/mr-opener.mjs +507 -0
- package/scripts/lib/skill-health/join.mjs +181 -0
- package/scripts/lib/skill-health/score.mjs +123 -0
- package/scripts/lib/skill-invocations-schema.mjs +214 -0
- package/scripts/lib/skill-judge.mjs +348 -0
- package/scripts/lib/skill-judgments-schema.mjs +264 -0
- package/scripts/lib/slopcheck.mjs +501 -0
- package/scripts/lib/soul-resolve.mjs +118 -0
- package/scripts/lib/spiral-carryover.mjs +495 -0
- package/scripts/lib/state-md/body-sections.mjs +851 -0
- package/scripts/lib/state-md/frontmatter-mutators.mjs +453 -0
- package/scripts/lib/state-md/mission-status.mjs +247 -0
- package/scripts/lib/state-md/recommendations.mjs +57 -0
- package/scripts/lib/state-md/yaml-parser.mjs +234 -0
- package/scripts/lib/state-md-peer-guard.mjs +232 -0
- package/scripts/lib/state-md.mjs +53 -0
- package/scripts/lib/subagents-schema.mjs +309 -0
- package/scripts/lib/sunset/walker.mjs +1192 -0
- package/scripts/lib/test-runner/artifact-paths.mjs +94 -0
- package/scripts/lib/test-runner/fingerprint.mjs +33 -0
- package/scripts/lib/test-runner/issue-reconcile.mjs +770 -0
- package/scripts/lib/tmux-layout/layouts.mjs +224 -0
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +100 -0
- package/scripts/lib/tmux-layout/telemetry.mjs +88 -0
- package/scripts/lib/tmux-layout/tmux-shell.mjs +82 -0
- package/scripts/lib/tmux-layout/vcs-detector.mjs +88 -0
- package/scripts/lib/validate/check-agents.mjs +457 -0
- package/scripts/lib/validate/check-codex-plugin.mjs +37 -0
- package/scripts/lib/validate/check-commands.mjs +148 -0
- package/scripts/lib/validate/check-component-paths.mjs +112 -0
- package/scripts/lib/validate/check-dead-bridge.mjs +180 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +258 -0
- package/scripts/lib/validate/check-json-files.mjs +116 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +1011 -0
- package/scripts/lib/validate/check-path-utils-canary.mjs +175 -0
- package/scripts/lib/validate/check-peekaboo-driver-canary.mjs +201 -0
- package/scripts/lib/validate/check-pi-package.mjs +110 -0
- package/scripts/lib/validate/check-pi-prompts.mjs +43 -0
- package/scripts/lib/validate/check-playwright-mcp-canary.mjs +154 -0
- package/scripts/lib/validate/check-plugin-json.mjs +96 -0
- package/scripts/lib/validate/check-plugin-monitors.mjs +206 -0
- package/scripts/lib/validate/check-plugin-schema.mjs +137 -0
- package/scripts/lib/validate/check-rules.mjs +143 -0
- package/scripts/lib/validate/check-session-plan-routing.mjs +154 -0
- package/scripts/lib/validate/check-test-fixture-shapes.mjs +280 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +533 -0
- package/scripts/lib/validate/confidential-names.mjs +169 -0
- package/scripts/lib/validate/dead-bridge-corpus.mjs +141 -0
- package/scripts/lib/validate/dead-bridge-detectors.mjs +568 -0
- package/scripts/lib/validate/tier-inference.mjs +100 -0
- package/scripts/lib/validate-vendored-rules.mjs +519 -0
- package/scripts/lib/vault-archive.mjs +404 -0
- package/scripts/lib/vault-backfill/glab.mjs +164 -0
- package/scripts/lib/vault-backfill/manifest.mjs +75 -0
- package/scripts/lib/vault-backfill/template.mjs +130 -0
- package/scripts/lib/vault-consolidate-fs.mjs +331 -0
- package/scripts/lib/vault-migration-rules.mjs +155 -0
- package/scripts/lib/vault-mirror/auto-commit.mjs +203 -0
- package/scripts/lib/vault-mirror/namespace.mjs +152 -0
- package/scripts/lib/vault-mirror/process.mjs +567 -0
- package/scripts/lib/vault-mirror/pseudonym-map.mjs +164 -0
- package/scripts/lib/vault-mirror/render-learnings.mjs +201 -0
- package/scripts/lib/vault-mirror/render-sessions.mjs +367 -0
- package/scripts/lib/vault-mirror/render.mjs +8 -0
- package/scripts/lib/vault-mirror/utils.mjs +217 -0
- package/scripts/lib/vault-relocation-rules.mjs +555 -0
- package/scripts/lib/vault-repo-backfill.mjs +235 -0
- package/scripts/lib/vault-staleness-banner.mjs +142 -0
- package/scripts/lib/vault-status/board-writer.mjs +769 -0
- package/scripts/lib/vault-status/narrative-mirror.mjs +544 -0
- package/scripts/lib/vault-sync-baseline.mjs +152 -0
- package/scripts/lib/wave-context.mjs +29 -0
- package/scripts/lib/wave-executor/pool.mjs +248 -0
- package/scripts/lib/wave-resource-gate.mjs +204 -0
- package/scripts/lib/wave-sizing.mjs +75 -0
- package/scripts/lib/webhook-url.mjs +105 -0
- package/scripts/lib/workspace.mjs +198 -0
- package/scripts/lib/worktree/constants.mjs +35 -0
- package/scripts/lib/worktree/index.mjs +17 -0
- package/scripts/lib/worktree/lifecycle.mjs +287 -0
- package/scripts/lib/worktree/listing.mjs +118 -0
- package/scripts/lib/worktree/meta.mjs +64 -0
- package/scripts/lib/worktree-freshness.mjs +313 -0
- package/scripts/lib/worktree.mjs +15 -0
- package/scripts/lifecycle-sim-v6.mjs +347 -0
- package/scripts/lock-reaper.mjs +185 -0
- package/scripts/mcp-server.sh +241 -0
- package/scripts/measure-policy-cache-effectiveness.mjs +427 -0
- package/scripts/memory-propose.mjs +464 -0
- package/scripts/migrate-cold-start-seed.mjs +404 -0
- package/scripts/migrate-learnings-jsonl.mjs +189 -0
- package/scripts/migrate-legacy-learnings.sh +61 -0
- package/scripts/migrate-sessions-jsonl.mjs +448 -0
- package/scripts/migrate-subagents-jsonl.mjs +196 -0
- package/scripts/migrate-vault-paths.mjs +796 -0
- package/scripts/parse-config.mjs +149 -0
- package/scripts/pi-install.mjs +117 -0
- package/scripts/print-applicable-rules.mjs +247 -0
- package/scripts/promote-vault-strict.mjs +496 -0
- package/scripts/relocate-vault-corpus.mjs +1178 -0
- package/scripts/run-migrate-v2-cross-repo.mjs +385 -0
- package/scripts/run-quality-gate.mjs +216 -0
- package/scripts/spikes/h3-agent-teams/preflight.sh +53 -0
- package/scripts/spikes/h3-agent-teams/run-h3.sh +112 -0
- package/scripts/spikes/h3-agent-teams/setup.sh +137 -0
- package/scripts/spikes/h3-agent-teams/toggle.sh +38 -0
- package/scripts/sweep-expired-learnings.mjs +135 -0
- package/scripts/sync-vault-schema.mjs +376 -0
- package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +8 -0
- package/scripts/tmux-layout.mjs +245 -0
- package/scripts/token-audit.sh +191 -0
- package/scripts/typecheck.mjs +42 -0
- package/scripts/upload-social-preview.mjs +316 -0
- package/scripts/validate-config.mjs +46 -0
- package/scripts/validate-plugin-manifests.mjs +163 -0
- package/scripts/validate-plugin.mjs +264 -0
- package/scripts/validate-wave-scope.mjs +289 -0
- package/scripts/vault-backfill.mjs +404 -0
- package/scripts/vault-consolidate.mjs +596 -0
- package/scripts/vault-integration-watcher.mjs +394 -0
- package/scripts/vault-mirror.mjs +430 -0
- package/skills/_shared/bootstrap-gate.md +111 -0
- package/skills/_shared/config-reading.md +226 -0
- package/skills/_shared/instruction-file-resolution.md +79 -0
- package/skills/_shared/model-selection.md +64 -0
- package/skills/_shared/monitor-patterns.md +300 -0
- package/skills/_shared/parallel-aware-auq.md +121 -0
- package/skills/_shared/parallel-aware-preamble.md +185 -0
- package/skills/_shared/platform-tools.md +96 -0
- package/skills/_shared/state-ownership.md +221 -0
- package/skills/architecture/DEEPENING.md +37 -0
- package/skills/architecture/INTERFACE-DESIGN.md +44 -0
- package/skills/architecture/LANGUAGE.md +53 -0
- package/skills/architecture/SKILL.md +92 -0
- package/skills/autopilot/SKILL.md +419 -0
- package/skills/bootstrap/SKILL.md +592 -0
- package/skills/bootstrap/STATE.md.template +24 -0
- package/skills/bootstrap/_shared-template.md +243 -0
- package/skills/bootstrap/deep-template.md +659 -0
- package/skills/bootstrap/fast-template.md +251 -0
- package/skills/bootstrap/intensity-heuristic.md +80 -0
- package/skills/bootstrap/public-fallback.md +342 -0
- package/skills/bootstrap/standard-template.md +736 -0
- package/skills/bootstrap/templates/agents/project-code-review.md +18 -0
- package/skills/bootstrap/templates/agents/project-discovery.md +18 -0
- package/skills/bootstrap/templates/agents/project-quality-gate.md +18 -0
- package/skills/brainstorm/SKILL.md +268 -0
- package/skills/brainstorm/soul.md +49 -0
- package/skills/claude-md-drift-check/SKILL.md +186 -0
- package/skills/claude-md-drift-check/checker.mjs +1380 -0
- package/skills/claude-md-drift-check/checker.sh +37 -0
- package/skills/claude-md-drift-check/package.json +16 -0
- package/skills/convergence-monitoring/README.md +39 -0
- package/skills/convergence-monitoring/SIGNALS.md +246 -0
- package/skills/convergence-monitoring/SKILL.md +285 -0
- package/skills/daily/SKILL.md +222 -0
- package/skills/daily/generate.sh +92 -0
- package/skills/daily/templates/daily.md.tpl +36 -0
- package/skills/debug/SKILL.md +188 -0
- package/skills/debug/soul.md +35 -0
- package/skills/discovery/SKILL.md +567 -0
- package/skills/discovery/issue-templates.md +237 -0
- package/skills/discovery/probes/docs-staleness.mjs +195 -0
- package/skills/discovery/probes/frontend-slop.mjs +186 -0
- package/skills/discovery/probes/ssot-code-diff.mjs +310 -0
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +440 -0
- package/skills/discovery/probes/vault-narrative-staleness.mjs +355 -0
- package/skills/discovery/probes/vault-staleness.mjs +272 -0
- package/skills/discovery/probes-arch.md +252 -0
- package/skills/discovery/probes-audit.md +95 -0
- package/skills/discovery/probes-code.md +329 -0
- package/skills/discovery/probes-docs.md +76 -0
- package/skills/discovery/probes-feature.md +150 -0
- package/skills/discovery/probes-infra.md +138 -0
- package/skills/discovery/probes-intro.md +25 -0
- package/skills/discovery/probes-session.md +495 -0
- package/skills/discovery/probes-supply-chain.md +94 -0
- package/skills/discovery/probes-ui.md +147 -0
- package/skills/discovery/probes-vault.md +64 -0
- package/skills/discovery/slop-patterns.md +115 -0
- package/skills/dispatcher/SKILL.md +173 -0
- package/skills/docs-orchestrator/SKILL.md +362 -0
- package/skills/docs-orchestrator/audience-mapping.md +140 -0
- package/skills/domain-model/ADR-FORMAT.md +47 -0
- package/skills/domain-model/CONTEXT-FORMAT.md +77 -0
- package/skills/domain-model/SKILL.md +85 -0
- package/skills/ecosystem-health/SKILL.md +119 -0
- package/skills/ecosystem-health/wizard.md +193 -0
- package/skills/eval/SKILL.md +293 -0
- package/skills/eval/rubric-v1.md +218 -0
- package/skills/evolve/SKILL.md +546 -0
- package/skills/frontmatter-guard/SKILL.md +126 -0
- package/skills/gitlab-ops/SKILL.md +368 -0
- package/skills/gitlab-portfolio/SKILL.md +196 -0
- package/skills/grill/SKILL.md +185 -0
- package/skills/grill/soul.md +55 -0
- package/skills/hook-development/SKILL.md +413 -0
- package/skills/mcp-builder/SKILL.md +260 -0
- package/skills/memory-cleanup/SKILL.md +310 -0
- package/skills/mode-selector/SKILL.md +226 -0
- package/skills/peekaboo-driver/SKILL.md +237 -0
- package/skills/peekaboo-driver/soul.md +32 -0
- package/skills/persona-panel/SKILL.md +365 -0
- package/skills/persona-panel/persona-format.md +205 -0
- package/skills/persona-panel/presets/designer-lens.md +87 -0
- package/skills/persona-panel/presets/engineer-lens.md +88 -0
- package/skills/persona-panel/presets/pm-lens.md +86 -0
- package/skills/plan/SKILL.md +496 -0
- package/skills/plan/mode-feature.md +141 -0
- package/skills/plan/mode-new.md +297 -0
- package/skills/plan/mode-retro.md +271 -0
- package/skills/plan/prd-feature-template.md +132 -0
- package/skills/plan/prd-full-template.md +151 -0
- package/skills/plan/prd-reviewer-prompt.md +103 -0
- package/skills/plan/retro-template.md +75 -0
- package/skills/plan/soul.md +62 -0
- package/skills/playwright-driver/SKILL.md +226 -0
- package/skills/playwright-driver/soul.md +30 -0
- package/skills/quality-gates/SKILL.md +212 -0
- package/skills/reconcile/SKILL.md +324 -0
- package/skills/repo-audit/SKILL.md +272 -0
- package/skills/session-end/SKILL.md +1044 -0
- package/skills/session-end/discovery-scan.md +37 -0
- package/skills/session-end/drift-operations.md +97 -0
- package/skills/session-end/learning-patterns.md +78 -0
- package/skills/session-end/metrics-collection.md +175 -0
- package/skills/session-end/phase-3-2-docs-verification.md +148 -0
- package/skills/session-end/phase-3-6-tail.md +344 -0
- package/skills/session-end/phase-3-7a-recommendations.md +86 -0
- package/skills/session-end/plan-verification.md +288 -0
- package/skills/session-end/session-metrics-write.md +223 -0
- package/skills/session-end/vault-operations.md +50 -0
- package/skills/session-end/verification-checklist.md +20 -0
- package/skills/session-plan/SKILL.md +554 -0
- package/skills/session-plan/wave-template.md +37 -0
- package/skills/session-start/SKILL.md +1043 -0
- package/skills/session-start/phase-2-5-docs-planning.md +119 -0
- package/skills/session-start/phase-4-5-resource-health.md +49 -0
- package/skills/session-start/phase-7-1-premise-check.md +47 -0
- package/skills/session-start/phase-7-5-mode-selector.md +237 -0
- package/skills/session-start/phase-8-5-express-path.md +61 -0
- package/skills/session-start/presentation-format.md +81 -0
- package/skills/session-start/soul.md +57 -0
- package/skills/skill-creator/SKILL.md +168 -0
- package/skills/spinout/SKILL.md +76 -0
- package/skills/sunset-review/SKILL.md +96 -0
- package/skills/test-runner/SKILL.md +362 -0
- package/skills/test-runner/rubric-v1.md +388 -0
- package/skills/test-runner/soul.md +46 -0
- package/skills/tmux-layout/SKILL.md +104 -0
- package/skills/ubiquitous-language/SKILL.md +97 -0
- package/skills/using-orchestrator/SKILL.md +144 -0
- package/skills/vault-mirror/SKILL.md +234 -0
- package/skills/vault-sync/SKILL.md +319 -0
- package/skills/vault-sync/package-lock.json +40 -0
- package/skills/vault-sync/package.json +11 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +8 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +10 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +9 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +8 -0
- package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +7 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +9 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +11 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +11 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/schema-drift.test.mjs +133 -0
- package/skills/vault-sync/validator.mjs +658 -0
- package/skills/vault-sync/validator.sh +55 -0
- package/skills/wave-executor/SKILL.md +496 -0
- package/skills/wave-executor/circuit-breaker.md +169 -0
- package/skills/wave-executor/wave-loop.md +1043 -0
- package/skills/write-executable-plan/SKILL.md +237 -0
- package/skills/write-executable-plan/plan-template.md +154 -0
- package/templates/_minimal/CLAUDE.md.tmpl +41 -0
- package/templates/_minimal/README.md.tmpl +15 -0
- package/templates/_minimal/gitignore.tmpl +47 -0
- package/templates/_shared/harte-regeln.md +16 -0
- package/templates/_shared/loop.md +90 -0
- package/templates/_shared/rules/parallel-sessions.md +77 -0
- package/templates/nextjs-minimal/README.md +30 -0
- package/templates/nextjs-minimal/app/layout.tsx +18 -0
- package/templates/nextjs-minimal/app/page.tsx +7 -0
- package/templates/nextjs-minimal/eslint.config.mjs +16 -0
- package/templates/nextjs-minimal/next.config.mjs +4 -0
- package/templates/nextjs-minimal/package.json +27 -0
- package/templates/nextjs-minimal/tsconfig.json +23 -0
- package/templates/node-minimal/README.md +33 -0
- package/templates/node-minimal/eslint.config.mjs +10 -0
- package/templates/node-minimal/package.json +21 -0
- package/templates/node-minimal/src/index.ts +1 -0
- package/templates/node-minimal/tests/sanity.test.ts +5 -0
- package/templates/node-minimal/tsconfig.json +17 -0
- package/templates/personas/README.md +150 -0
- package/templates/personas/accounting-compliance.v1.md +120 -0
- package/templates/personas/accounting-tax-advisor.v1.md +116 -0
- package/templates/personas/buyer-p1-cto.v1.md +125 -0
- package/templates/personas/buyer-p2-kanzlei.v1.md +134 -0
- package/templates/personas/buyer-p3-build.v1.md +130 -0
- package/templates/personas/buyer-p4-tech-veto.v1.md +130 -0
- package/templates/personas/buyer-p5-solo.v1.md +132 -0
- package/templates/personas/buyer-p6-ld.v1.md +130 -0
- package/templates/personas/klima-ai-expert.v1.md +114 -0
- package/templates/personas/klima-physicist.v1.md +117 -0
- package/templates/python-uv/README.md +28 -0
- package/templates/python-uv/pyproject.toml +32 -0
- package/templates/python-uv/src/__PROJECT_NAME__/__init__.py +0 -0
- package/templates/python-uv/src/__PROJECT_NAME__/main.py +6 -0
- package/templates/python-uv/tests/test_sanity.py +2 -0
- package/templates/static-html/README.md +19 -0
- package/templates/static-html/index.html +15 -0
- package/templates/static-html/script.js +1 -0
- package/templates/static-html/styles.css +26 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scope-gate.mjs — scope / pattern primitives.
|
|
3
|
+
*
|
|
4
|
+
* Split out of scripts/lib/hardening.mjs (concern B). Used by Wave 3 hooks on
|
|
5
|
+
* hot-paths — all sync. Re-exported by hardening.mjs as a barrel so existing
|
|
6
|
+
* importers keep working unchanged.
|
|
7
|
+
*
|
|
8
|
+
* Layering: hook-safe — pure functions only; no I/O at import time;
|
|
9
|
+
* ESM-pure for fast hook hot-paths. Hooks (under `hooks/`) import from
|
|
10
|
+
* this lib; this lib MUST NOT reverse-import from `hooks/`. Cross-cutting
|
|
11
|
+
* invariant for all exports below — see #554 A2.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Find the wave-scope.json file for the given project root.
|
|
19
|
+
*
|
|
20
|
+
* Precedence (mirrors find_scope_file in hardening.sh):
|
|
21
|
+
* <root>/.pi/wave-scope.json
|
|
22
|
+
* <root>/.cursor/wave-scope.json
|
|
23
|
+
* <root>/.codex/wave-scope.json
|
|
24
|
+
* <root>/.claude/wave-scope.json
|
|
25
|
+
*
|
|
26
|
+
* Returns the absolute path string, or null if none exist.
|
|
27
|
+
* Sync (uses fs.existsSync).
|
|
28
|
+
*
|
|
29
|
+
* @param {string} projectRoot — absolute path to project root
|
|
30
|
+
* @returns {string|null}
|
|
31
|
+
*/
|
|
32
|
+
export function findScopeFile(projectRoot) {
|
|
33
|
+
for (const dir of ['.pi', '.cursor', '.codex', '.claude']) {
|
|
34
|
+
const candidate = path.join(projectRoot, dir, 'wave-scope.json');
|
|
35
|
+
if (existsSync(candidate)) {
|
|
36
|
+
return candidate;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Read the enforcement level from a scope file.
|
|
44
|
+
* Defaults to "strict" (fail-closed) on parse error or missing field.
|
|
45
|
+
* Sync. Never throws.
|
|
46
|
+
*
|
|
47
|
+
* @param {string} scopeFilePath — absolute path to wave-scope.json
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
export function getEnforcementLevel(scopeFilePath) {
|
|
51
|
+
try {
|
|
52
|
+
const data = JSON.parse(readFileSync(scopeFilePath, 'utf8'));
|
|
53
|
+
return data.enforcement ?? 'strict';
|
|
54
|
+
} catch {
|
|
55
|
+
return 'strict';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check whether a named gate is enabled in the scope file.
|
|
61
|
+
* Returns true if the field is missing or true, false only if explicitly false.
|
|
62
|
+
* Sync. Never throws.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} scopeFilePath — absolute path to wave-scope.json
|
|
65
|
+
* @param {string} gateName — key within .gates
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
export function gateEnabled(scopeFilePath, gateName) {
|
|
69
|
+
try {
|
|
70
|
+
const data = JSON.parse(readFileSync(scopeFilePath, 'utf8'));
|
|
71
|
+
const gates = data.gates;
|
|
72
|
+
if (gates === undefined || gates === null) return true;
|
|
73
|
+
const value = gates[gateName];
|
|
74
|
+
if (value === undefined || value === null) return true;
|
|
75
|
+
return value !== false;
|
|
76
|
+
} catch {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Test whether a relative file path matches a single glob-style pattern.
|
|
83
|
+
*
|
|
84
|
+
* Supported patterns:
|
|
85
|
+
* - `prefix/` — directory prefix: any file under prefix/ (including nested)
|
|
86
|
+
* - `src/**\/*.ts` — recursive glob: `**` = any depth (including zero dirs)
|
|
87
|
+
* - `src/*.ts` — single-segment glob: `*` = one segment (no slashes)
|
|
88
|
+
* - `path/to/file` — exact match
|
|
89
|
+
*
|
|
90
|
+
* Conversion order:
|
|
91
|
+
* 1. Escape all regex special chars EXCEPT `*` and `/`.
|
|
92
|
+
* 2. Replace `**` with `<<DBL>>` placeholder.
|
|
93
|
+
* 3. Replace remaining `*` with `[^/]*` (single segment).
|
|
94
|
+
* 4. Replace `<<DBL>>` with `.*` (any depth).
|
|
95
|
+
* 5. Anchor: `^...$`.
|
|
96
|
+
*
|
|
97
|
+
* Case-sensitive. Empty pattern returns false.
|
|
98
|
+
*
|
|
99
|
+
* Hook-safe: pure, deterministic, no I/O. Current importers (grep-verified
|
|
100
|
+
* #554 A2): hooks/wave-scope-commit-guard.mjs, hooks/enforce-scope.mjs,
|
|
101
|
+
* scripts/lib/worktree-freshness.mjs, scripts/lib/pre-dispatch-check.mjs.
|
|
102
|
+
*
|
|
103
|
+
* @param {string} relPath
|
|
104
|
+
* @param {string} pattern
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
export function pathMatchesPattern(relPath, pattern) {
|
|
108
|
+
if (!pattern) return false;
|
|
109
|
+
|
|
110
|
+
// Directory prefix shortcut: pattern ends with '/'
|
|
111
|
+
if (pattern.endsWith('/')) {
|
|
112
|
+
return relPath.startsWith(pattern);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Build a regex from the glob pattern.
|
|
116
|
+
// Step 1: Escape regex special chars (everything except * and /)
|
|
117
|
+
const specialChars = /[.+?|[\](){}\\^$]/g;
|
|
118
|
+
let regex = pattern.replace(specialChars, (ch) => `\\${ch}`);
|
|
119
|
+
|
|
120
|
+
// Step 2: Replace `**/` with placeholder (matches zero-or-more dir segments WITH trailing slash)
|
|
121
|
+
// `src/**/foo` must match `src/foo` (zero dirs) and `src/a/b/foo` (two dirs).
|
|
122
|
+
// Replacing `**/` → `(.*\/)?` captures "any number of segments + slash, or nothing".
|
|
123
|
+
regex = regex.replace(/\*\*\//g, '<<DBLS>>');
|
|
124
|
+
|
|
125
|
+
// Replace remaining `**` (not followed by /) with a second placeholder.
|
|
126
|
+
// MUST use a placeholder (not `.*` directly) — the single-* pass below would otherwise
|
|
127
|
+
// re-process the `*` quantifier in `.*`, yielding `.[^/]*` which blocks nested paths
|
|
128
|
+
// under `tests/**` etc. (issue #220).
|
|
129
|
+
regex = regex.replace(/\*\*/g, '<<DBLG>>');
|
|
130
|
+
|
|
131
|
+
// Step 3: Single * → one path segment (no slashes)
|
|
132
|
+
regex = regex.replace(/\*/g, '[^/]*');
|
|
133
|
+
|
|
134
|
+
// Step 4: Expand placeholders
|
|
135
|
+
regex = regex.replace(/<<DBLS>>/g, '(.*\\/)?');
|
|
136
|
+
regex = regex.replace(/<<DBLG>>/g, '.*');
|
|
137
|
+
|
|
138
|
+
// Step 5: Anchor
|
|
139
|
+
regex = `^${regex}$`;
|
|
140
|
+
|
|
141
|
+
return new RegExp(regex).test(relPath);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Is this fileScope entry a glob/prefix pattern (vs. a concrete file path)?
|
|
146
|
+
* A `*` metachar OR a trailing `/` (directory prefix) marks it as a glob.
|
|
147
|
+
* @param {string} entry
|
|
148
|
+
* @returns {boolean}
|
|
149
|
+
*/
|
|
150
|
+
function isGlobScopeEntry(entry) {
|
|
151
|
+
return entry.includes('*') || entry.endsWith('/');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Literal prefix of a glob entry — the segment before the first `*`
|
|
156
|
+
* metachar (or the whole entry when it has none). `src/**` → `src/`,
|
|
157
|
+
* `src/lib/*.mjs` → `src/lib/`, `tests/` → `tests/`.
|
|
158
|
+
* @param {string} entry
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
function literalScopePrefix(entry) {
|
|
162
|
+
const star = entry.indexOf('*');
|
|
163
|
+
return star === -1 ? entry : entry.slice(0, star);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Assert that every entry of an agent's declared file scope is covered by the
|
|
168
|
+
* wave's `allowedPaths` union — the mechanical form of the "allowedPaths is the
|
|
169
|
+
* UNION of all agent file scopes" contract (wave-loop.md § Scope Manifest #3).
|
|
170
|
+
*
|
|
171
|
+
* Motivation (#796): `.claude/wave-scope.json` is GLOBAL per wave — one
|
|
172
|
+
* allowedPaths union gates every agent in the wave (hooks/enforce-scope.mjs
|
|
173
|
+
* Gate 7). A coordinator that (re)writes the union for only ONE agent of a
|
|
174
|
+
* multi-agent batch silently denies its siblings' legitimate writes (observed
|
|
175
|
+
* fix-pass incident). Running this assertion for EVERY agent before dispatch
|
|
176
|
+
* catches that class before an agent is blocked mid-run.
|
|
177
|
+
*
|
|
178
|
+
* Semantics:
|
|
179
|
+
* - CONCRETE fileScope entry (no `*`, not a `dir/` prefix): covered iff it
|
|
180
|
+
* matches ≥1 allowedPaths pattern via `pathMatchesPattern` (the same matcher
|
|
181
|
+
* the enforcement hook uses at check time). `src/a.ts` ⊆ `src/**` → covered.
|
|
182
|
+
* - GLOB fileScope entry (`*` present, or a `dir/` prefix): covered iff it is
|
|
183
|
+
* present verbatim in allowedPaths, OR its literal prefix (the segment
|
|
184
|
+
* before the first glob metachar) matches an allowedPaths pattern.
|
|
185
|
+
*
|
|
186
|
+
* GLOB-vs-GLOB LIMITATION (deliberate design boundary): this is NOT a full
|
|
187
|
+
* glob-⊆-glob subset calculus. For a glob fileScope entry the check reduces to
|
|
188
|
+
* verbatim presence + literal-prefix coverage; it does not prove that e.g.
|
|
189
|
+
* `src/**\/*.ts` ⊆ `src/**\/*.js` is false. The concrete-path branch above is
|
|
190
|
+
* exact and carries the incident-relevant load (the union the coordinator
|
|
191
|
+
* actually writes is verbatim, deduplicated agent scopes). Erring toward
|
|
192
|
+
* over-approximating coverage keeps a legitimate union from being rejected on a
|
|
193
|
+
* glob technicality rather than pretending to a precision this matcher lacks.
|
|
194
|
+
*
|
|
195
|
+
* Fail-closed & no-throw (module convention): a non-array `fileScope` or
|
|
196
|
+
* `allowedPaths` returns `{ ok: false, missing: [] }` — "cannot assert → treat
|
|
197
|
+
* as failure". An empty `fileScope` is a trivial subset → `{ ok: true }`.
|
|
198
|
+
* Non-string / empty-string entries are skipped (the CLI caller validates the
|
|
199
|
+
* array-of-strings shape upstream). Never throws.
|
|
200
|
+
*
|
|
201
|
+
* @param {string[]} fileScope — one agent's declared file scope entries
|
|
202
|
+
* @param {string[]} allowedPaths — the wave's allowedPaths union
|
|
203
|
+
* @returns {{ ok: boolean, missing: string[] }} missing = uncovered fileScope entries
|
|
204
|
+
*/
|
|
205
|
+
export function assertFileScopeSubset(fileScope, allowedPaths) {
|
|
206
|
+
if (!Array.isArray(fileScope) || !Array.isArray(allowedPaths)) {
|
|
207
|
+
return { ok: false, missing: [] };
|
|
208
|
+
}
|
|
209
|
+
const missing = [];
|
|
210
|
+
for (const entry of fileScope) {
|
|
211
|
+
if (typeof entry !== 'string' || entry.length === 0) continue;
|
|
212
|
+
let covered;
|
|
213
|
+
if (isGlobScopeEntry(entry)) {
|
|
214
|
+
// GLOB entry: verbatim presence OR literal-prefix coverage.
|
|
215
|
+
const prefix = literalScopePrefix(entry);
|
|
216
|
+
covered = allowedPaths.some((p) => p === entry || pathMatchesPattern(prefix, p));
|
|
217
|
+
} else {
|
|
218
|
+
// CONCRETE entry: must match ≥1 allowedPaths pattern.
|
|
219
|
+
covered = allowedPaths.some((p) => pathMatchesPattern(entry, p));
|
|
220
|
+
}
|
|
221
|
+
if (!covered) missing.push(entry);
|
|
222
|
+
}
|
|
223
|
+
return { ok: missing.length === 0, missing };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Extract likely file-write TARGETS from a Bash command string (#800).
|
|
228
|
+
*
|
|
229
|
+
* Motivation: `hooks/enforce-scope.mjs` Gate 1 only gates the Edit/Write/MultiEdit
|
|
230
|
+
* tools — Bash write channels (heredocs, `>`/`>>` redirects, `tee`, `sed -i`,
|
|
231
|
+
* `dd of=`) bypass the wave-scope gate structurally. This function is the parsing
|
|
232
|
+
* half of the opt-in, WARN-ONLY `bash-write-guard` (wired in enforce-commands.mjs).
|
|
233
|
+
*
|
|
234
|
+
* DESIGN POSTURE — conservative, under- rather than over-match (v1 is warn-only):
|
|
235
|
+
* a false NEGATIVE (missed write) is a silent no-warn; a false POSITIVE (spurious
|
|
236
|
+
* warn on a benign command) is operator noise that erodes trust in the guard. When
|
|
237
|
+
* in doubt we DROP the candidate. This is deliberately NOT a full shell parser.
|
|
238
|
+
*
|
|
239
|
+
* MATCHED write channels:
|
|
240
|
+
* (a) redirects `> p`, `>> p`, `2> p`, `2>> p`, `&> p`, `&>> p` (fd/`&` prefix ok)
|
|
241
|
+
* (b) `tee [-a] p [p2 …]` (all non-flag file args of a `tee` command-head)
|
|
242
|
+
* (c) `sed -i[.bak] … p` (the LAST non-flag argument of a `sed` command-head
|
|
243
|
+
* carrying an in-place `-i*` flag; the `sed` SCRIPT arg is not the target)
|
|
244
|
+
* (d) `dd of=p` (the `of=` argument of a `dd` command-head)
|
|
245
|
+
* (e) heredocs `cat > p <<EOF` — covered by the redirect part `(a)`; the `<<`
|
|
246
|
+
* delimiter itself is an INPUT redirect, never a write target.
|
|
247
|
+
*
|
|
248
|
+
* Deliberately NOT matched (documented skip rules — each is a false-positive trap):
|
|
249
|
+
* - targets beginning with `$` or `~`, or containing ANY `$` (variable /
|
|
250
|
+
* expansion — the concrete path is unknowable at parse time; e.g. `> $LOG`,
|
|
251
|
+
* `>> ${TMPDIR}/x`)
|
|
252
|
+
* - `/dev/…`, `/tmp/…`, `/private/tmp/…` (device + temp sinks — never wave scope)
|
|
253
|
+
* - process substitution `>(…)` (an operator, not a file; the `(` breaks it)
|
|
254
|
+
* - fd duplication `>&`, `2>&1` (dup, not a file target)
|
|
255
|
+
* - input redirects `<`, `<<` (reads, not writes)
|
|
256
|
+
* - quoted targets containing a space (best-effort — a spaced path is far more
|
|
257
|
+
* likely a quoting artefact than a real wave-scoped file)
|
|
258
|
+
* - a `>` / `tee` / `sed` / `dd` that appears INSIDE quotes (e.g. `echo '>' x`)
|
|
259
|
+
* — the tokenizer tracks quote state, so a quoted `>` is a word, not an op.
|
|
260
|
+
*
|
|
261
|
+
* Targets are returned VERBATIM (repo-relative where the command wrote them
|
|
262
|
+
* relatively, absolute where the command used an absolute path) and de-duplicated
|
|
263
|
+
* in first-seen order. The caller relativises + matches against allowedPaths.
|
|
264
|
+
*
|
|
265
|
+
* Hook-safe: pure, deterministic, no I/O. Never throws — a non-string / empty
|
|
266
|
+
* input returns `[]`.
|
|
267
|
+
*
|
|
268
|
+
* @param {string} command — the raw Bash command string
|
|
269
|
+
* @returns {string[]} de-duplicated list of likely write targets (may be empty)
|
|
270
|
+
*/
|
|
271
|
+
export function extractBashWriteTargets(command) {
|
|
272
|
+
if (typeof command !== 'string' || command.length === 0) return [];
|
|
273
|
+
|
|
274
|
+
const tokens = tokenizeShellForWrites(command);
|
|
275
|
+
|
|
276
|
+
const out = [];
|
|
277
|
+
const seen = new Set();
|
|
278
|
+
const add = (value, hadSpace) => {
|
|
279
|
+
if (shouldSkipWriteTarget(value, hadSpace)) return;
|
|
280
|
+
const v = value.replace(/^\.\//, '');
|
|
281
|
+
if (!v || seen.has(v)) return;
|
|
282
|
+
seen.add(v);
|
|
283
|
+
out.push(v);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// Second pass: interpret the token stream. `mode` tracks a command-head that
|
|
287
|
+
// owns following args (tee/sed/dd); `pendingRedirect` marks that the NEXT word
|
|
288
|
+
// token is a redirect target.
|
|
289
|
+
let mode = null; // null | 'tee' | 'sed' | 'dd'
|
|
290
|
+
let pendingRedirect = false;
|
|
291
|
+
let expectCommand = true; // next word is the command head of this segment
|
|
292
|
+
let sedArgs = []; // { value } collected for a `sed` head
|
|
293
|
+
let sedInPlace = false;
|
|
294
|
+
|
|
295
|
+
const flushSed = () => {
|
|
296
|
+
if (mode === 'sed' && sedInPlace) {
|
|
297
|
+
for (let i = sedArgs.length - 1; i >= 0; i--) {
|
|
298
|
+
if (!isShellFlag(sedArgs[i].value)) {
|
|
299
|
+
add(sedArgs[i].value, sedArgs[i].hadSpace);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
sedArgs = [];
|
|
305
|
+
sedInPlace = false;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
for (const tk of tokens) {
|
|
309
|
+
if (tk.type === 'redirect') {
|
|
310
|
+
pendingRedirect = true;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (tk.type === 'in') {
|
|
314
|
+
// input redirect / heredoc delimiter — not a write target
|
|
315
|
+
pendingRedirect = false;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (tk.type === 'sep') {
|
|
319
|
+
flushSed();
|
|
320
|
+
mode = null;
|
|
321
|
+
pendingRedirect = false;
|
|
322
|
+
expectCommand = true;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
// word token
|
|
326
|
+
if (pendingRedirect) {
|
|
327
|
+
add(tk.value, tk.hadSpace);
|
|
328
|
+
pendingRedirect = false;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (expectCommand) {
|
|
332
|
+
expectCommand = false;
|
|
333
|
+
flushSed(); // flush any prior sed segment defensively
|
|
334
|
+
if (tk.value === 'tee') { mode = 'tee'; continue; }
|
|
335
|
+
if (tk.value === 'sed') { mode = 'sed'; continue; }
|
|
336
|
+
if (tk.value === 'dd') { mode = 'dd'; continue; }
|
|
337
|
+
mode = null;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
// subsequent argument words, interpreted per active command-head mode
|
|
341
|
+
if (mode === 'tee') {
|
|
342
|
+
if (!isShellFlag(tk.value)) add(tk.value, tk.hadSpace);
|
|
343
|
+
} else if (mode === 'sed') {
|
|
344
|
+
if (/^-i/.test(tk.value)) sedInPlace = true;
|
|
345
|
+
sedArgs.push(tk);
|
|
346
|
+
} else if (mode === 'dd') {
|
|
347
|
+
if (tk.value.startsWith('of=')) add(tk.value.slice(3), tk.hadSpace);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
flushSed();
|
|
351
|
+
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Is this token a CLI flag (starts with `-`)? Used to skip flags when picking
|
|
357
|
+
* file arguments for tee/sed. `-` alone (stdin) also counts as a flag.
|
|
358
|
+
* @param {string} v
|
|
359
|
+
* @returns {boolean}
|
|
360
|
+
*/
|
|
361
|
+
function isShellFlag(v) {
|
|
362
|
+
return typeof v === 'string' && v.startsWith('-');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Skip-rule gate for a candidate write target — see the documented skip list on
|
|
367
|
+
* {@link extractBashWriteTargets}. Returns true when the candidate must be dropped.
|
|
368
|
+
* @param {string} value — unquoted target text
|
|
369
|
+
* @param {boolean} hadSpace — true if the source token was quoted AND contained a space
|
|
370
|
+
* @returns {boolean}
|
|
371
|
+
*/
|
|
372
|
+
function shouldSkipWriteTarget(value, hadSpace) {
|
|
373
|
+
if (typeof value !== 'string' || value.length === 0) return true;
|
|
374
|
+
if (hadSpace || value.includes(' ')) return true; // quoted-with-space (best-effort)
|
|
375
|
+
if (value.startsWith('$') || value.startsWith('~')) return true; // variable / expansion
|
|
376
|
+
if (value.includes('$')) return true; // any embedded expansion (covers ${TMPDIR})
|
|
377
|
+
if (value.includes('(') || value.includes(')')) return true; // process-sub remnants
|
|
378
|
+
if (value.startsWith('/dev/')) return true; // device sink
|
|
379
|
+
if (value.startsWith('/tmp/') || value.startsWith('/private/tmp/')) return true; // temp sink
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Minimal quote-aware tokenizer for write-target extraction. Walks the command
|
|
385
|
+
* left-to-right tracking single/double quote state; recognises redirect / input /
|
|
386
|
+
* separator operators ONLY outside quotes, and emits everything else as `word`
|
|
387
|
+
* tokens with the quotes stripped. Not a general shell tokenizer — it captures
|
|
388
|
+
* exactly what {@link extractBashWriteTargets} needs.
|
|
389
|
+
*
|
|
390
|
+
* Token shapes: { type: 'redirect' } | { type: 'in' } | { type: 'sep' }
|
|
391
|
+
* | { type: 'word', value: string, hadSpace: boolean }
|
|
392
|
+
*
|
|
393
|
+
* @param {string} command
|
|
394
|
+
* @returns {Array<{type:string, value?:string, hadSpace?:boolean}>}
|
|
395
|
+
*/
|
|
396
|
+
function tokenizeShellForWrites(command) {
|
|
397
|
+
const tokens = [];
|
|
398
|
+
const n = command.length;
|
|
399
|
+
let i = 0;
|
|
400
|
+
const isWs = (c) => c === ' ' || c === '\t' || c === '\n' || c === '\r';
|
|
401
|
+
|
|
402
|
+
while (i < n) {
|
|
403
|
+
const c = command[i];
|
|
404
|
+
const next = command[i + 1];
|
|
405
|
+
|
|
406
|
+
if (isWs(c)) { i++; continue; }
|
|
407
|
+
|
|
408
|
+
// `&>` / `&>>` — redirect stdout+stderr to a file (write target follows)
|
|
409
|
+
if (c === '&' && next === '>') {
|
|
410
|
+
i += 2;
|
|
411
|
+
if (command[i] === '>') i++;
|
|
412
|
+
tokens.push({ type: 'redirect' });
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
// `&&` / `&` — command separators
|
|
416
|
+
if (c === '&') {
|
|
417
|
+
i += next === '&' ? 2 : 1;
|
|
418
|
+
tokens.push({ type: 'sep' });
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
// `>&` — fd duplication (NOT a file target); consume the dup + trailing fd/`-`
|
|
422
|
+
if (c === '>' && next === '&') {
|
|
423
|
+
i += 2;
|
|
424
|
+
while (i < n && (/[0-9]/.test(command[i]) || command[i] === '-')) i++;
|
|
425
|
+
continue; // no token — dup carries no write target
|
|
426
|
+
}
|
|
427
|
+
// `>(` — process substitution: leave the `>` inert; `(` is emitted as a sep
|
|
428
|
+
if (c === '>' && next === '(') { i++; continue; }
|
|
429
|
+
// `>>` / `>` — write redirects
|
|
430
|
+
if (c === '>') {
|
|
431
|
+
i += next === '>' ? 2 : 1;
|
|
432
|
+
tokens.push({ type: 'redirect' });
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
// `<<` / `<` — input redirects / heredoc delimiters (never a write target)
|
|
436
|
+
if (c === '<') {
|
|
437
|
+
i += next === '<' ? 2 : 1;
|
|
438
|
+
tokens.push({ type: 'in' });
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
// `||` / `|` / `;` / `(` / `)` — separators (break the current command)
|
|
442
|
+
if (c === '|') { i += next === '|' ? 2 : 1; tokens.push({ type: 'sep' }); continue; }
|
|
443
|
+
if (c === ';') { i++; tokens.push({ type: 'sep' }); continue; }
|
|
444
|
+
if (c === '(' || c === ')') { i++; tokens.push({ type: 'sep' }); continue; }
|
|
445
|
+
|
|
446
|
+
// Otherwise: read a WORD, honouring single/double quotes (quotes stripped).
|
|
447
|
+
let value = '';
|
|
448
|
+
let quoted = false;
|
|
449
|
+
let hadSpace = false;
|
|
450
|
+
while (i < n) {
|
|
451
|
+
const ch = command[i];
|
|
452
|
+
if (ch === "'") {
|
|
453
|
+
quoted = true;
|
|
454
|
+
i++;
|
|
455
|
+
while (i < n && command[i] !== "'") { if (command[i] === ' ') hadSpace = true; value += command[i]; i++; }
|
|
456
|
+
i++; // closing quote (or EOF)
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (ch === '"') {
|
|
460
|
+
quoted = true;
|
|
461
|
+
i++;
|
|
462
|
+
while (i < n && command[i] !== '"') { if (command[i] === ' ') hadSpace = true; value += command[i]; i++; }
|
|
463
|
+
i++; // closing quote (or EOF)
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (isWs(ch)) break;
|
|
467
|
+
// unquoted operator chars end the word
|
|
468
|
+
if (ch === '>' || ch === '<' || ch === '|' || ch === ';' || ch === '&' || ch === '(' || ch === ')') break;
|
|
469
|
+
value += ch;
|
|
470
|
+
i++;
|
|
471
|
+
}
|
|
472
|
+
tokens.push({ type: 'word', value, hadSpace, quoted });
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return tokens;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Build an actionable suggestion string for a scope violation.
|
|
480
|
+
*
|
|
481
|
+
* @param {string} relPath — the relative path that was blocked
|
|
482
|
+
* @param {string} allowedCsv — comma-separated list of allowed paths (may be empty)
|
|
483
|
+
* @returns {string}
|
|
484
|
+
*/
|
|
485
|
+
export function suggestForScopeViolation(relPath, allowedCsv) {
|
|
486
|
+
if (!allowedCsv) {
|
|
487
|
+
return (
|
|
488
|
+
`No paths are currently allowed for this wave. ` +
|
|
489
|
+
`If '${relPath}' is in-scope, update the session plan and restart the wave.`
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
return (
|
|
493
|
+
`Allowed paths: [${allowedCsv}]. ` +
|
|
494
|
+
`If '${relPath}' belongs to this wave, add its directory to the plan's wave scope and restart.`
|
|
495
|
+
);
|
|
496
|
+
}
|