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,568 @@
|
|
|
1
|
+
// dead-bridge-detectors.mjs — PURE detection functions for the dead-bridge
|
|
2
|
+
// validator (#671). These functions consolidate the dead-reference guards that
|
|
3
|
+
// previously lived as standalone scripts (check-subagent-types.mjs,
|
|
4
|
+
// check-rules-references.mjs, check-baseline-fetch-bridge.mjs) plus a NEW
|
|
5
|
+
// bridge-balance detector for the producer/consumer dead-bridge class.
|
|
6
|
+
//
|
|
7
|
+
// PURITY CONTRACT (load-bearing — do not violate):
|
|
8
|
+
// - NO node:fs import. All filesystem access is INJECTED via the RepoContext
|
|
9
|
+
// (`ctx.listMdFiles`, `ctx.listFiles`, `ctx.readText`, `ctx.exists`), so
|
|
10
|
+
// detectors are unit-testable with in-memory fakes.
|
|
11
|
+
// - NO console.* output. Detectors return Finding[] — the orchestrator
|
|
12
|
+
// (check-dead-bridge.mjs) is responsible for rendering and exit codes.
|
|
13
|
+
// - NO process.exit. Tool-error semantics are signalled via a Finding whose
|
|
14
|
+
// `rule` ends in `-tool-error` (see TOOL-ERROR CONVENTION below).
|
|
15
|
+
// - `path` from node:path IS allowed (pure, no IO).
|
|
16
|
+
//
|
|
17
|
+
// TOOL-ERROR CONVENTION:
|
|
18
|
+
// The original standalone guards exited 2 (tool-error) when a REQUIRED scan
|
|
19
|
+
// directory was missing/unreadable (rules dir for dangling-rule-reference,
|
|
20
|
+
// bootstrap dir for dangling-bootstrap-bridge). Because these detectors are
|
|
21
|
+
// pure (no process.exit), a tool-error is instead signalled by RETURNING a
|
|
22
|
+
// Finding with `rule: '<subrule>-tool-error'`. The orchestrator translates any
|
|
23
|
+
// `*-tool-error` rule into process exit code 2 (distinct from a normal exit-1
|
|
24
|
+
// validation failure). Detectors that find a missing dir which is a vacuous
|
|
25
|
+
// pass (e.g. skills/ for dangling-subagent-type) return NO finding instead.
|
|
26
|
+
//
|
|
27
|
+
// @typedef {Object} Finding
|
|
28
|
+
// @property {string} rule stable detector / sub-rule id (or `<id>-tool-error`)
|
|
29
|
+
// @property {'fail'} severity always 'fail' for this validator
|
|
30
|
+
// @property {string} file absolute path of the offending file ('' = N/A)
|
|
31
|
+
// @property {number} line 1-based line number (0 = file-level / no line)
|
|
32
|
+
// @property {string} message human-readable description
|
|
33
|
+
//
|
|
34
|
+
// @typedef {Object} RepoContext
|
|
35
|
+
// @property {string} pluginRoot absolute plugin root
|
|
36
|
+
// @property {(absDir: string) => string[]} listMdFiles recursive list of *.md under absDir (abs paths)
|
|
37
|
+
// @property {(absDir: string, exts: string[]) => string[]} listFiles recursive list of files with given exts under absDir (abs paths)
|
|
38
|
+
// @property {(absPath: string) => string} readText read file content as UTF-8
|
|
39
|
+
// @property {(absPath: string) => boolean} exists path-existence test
|
|
40
|
+
|
|
41
|
+
import { join, dirname, basename } from 'node:path';
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Shared constants (ported verbatim from the three standalone guards).
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
// Any line carrying the consolidated dead-bridge ignore marker is skipped by
|
|
48
|
+
// EVERY sub-rule, in addition to each sub-rule's own historical marker — this
|
|
49
|
+
// preserves the original per-guard ignore behaviour while giving the merged
|
|
50
|
+
// validator a single uniform escape hatch.
|
|
51
|
+
const DEAD_BRIDGE_IGNORE = 'check-dead-bridge:ignore';
|
|
52
|
+
|
|
53
|
+
// --- dangling-subagent-type (port of check-subagent-types.mjs) -------------
|
|
54
|
+
const SUBAGENT_IGNORE_MARKER = 'check-subagent-types:ignore';
|
|
55
|
+
const SUBAGENT_REF_RE = /subagent_type:\s*["']session-orchestrator:([a-z0-9-]+)["']/g;
|
|
56
|
+
|
|
57
|
+
// --- dangling-rule-reference (port of check-rules-references.mjs) -----------
|
|
58
|
+
const RULES_IGNORE_MARKER = 'check-rules-references:ignore';
|
|
59
|
+
// Backtick-wrapped reference: `name.md`. Capture group = inner ref so
|
|
60
|
+
// path-qualified refs (containing `/`) can be excluded downstream.
|
|
61
|
+
const BACKTICK_REF_RE = /`([^`]+\.md)`/g;
|
|
62
|
+
// See-Also footer tokens — bare `[a-z0-9-]+\.md` basenames in a `· `-delimited
|
|
63
|
+
// list line.
|
|
64
|
+
const SEE_ALSO_TOKEN_RE = /([a-z0-9-]+\.md)/g;
|
|
65
|
+
// A rule-style basename is lowercase-kebab. Deliberately EXCLUDES uppercase doc
|
|
66
|
+
// names (CLAUDE.md, AGENTS.md, SECURITY.md, SKILL.md, MIGRATION-vN.md) and
|
|
67
|
+
// wildcard prose (`*.md`).
|
|
68
|
+
const RULE_BASENAME_RE = /^[a-z0-9-]+\.md$/;
|
|
69
|
+
|
|
70
|
+
// --- dangling-bootstrap-bridge (port of check-baseline-fetch-bridge.mjs) ---
|
|
71
|
+
const BOOTSTRAP_IGNORE_MARKER = 'check-baseline-fetch-bridge:ignore';
|
|
72
|
+
const BOOTSTRAP_FILES = [
|
|
73
|
+
'_shared-template.md',
|
|
74
|
+
'SKILL.md',
|
|
75
|
+
'standard-template.md',
|
|
76
|
+
'deep-template.md',
|
|
77
|
+
];
|
|
78
|
+
const STALE_BASENAME = 'fetch-baseline.sh';
|
|
79
|
+
const DEAD_FUNCTIONS = [
|
|
80
|
+
'fetch_baseline_file',
|
|
81
|
+
'fetch_baseline_files_batch',
|
|
82
|
+
'write_baseline_fetch_lock',
|
|
83
|
+
];
|
|
84
|
+
// A `-f "$PLUGIN_ROOT/scripts/lib/fetch-baseline.<ext>"` guard reference.
|
|
85
|
+
// Capture group = guarded path relative to the plugin root.
|
|
86
|
+
const GUARD_RE = /-f\s+["']?\$(?:\{)?PLUGIN_ROOT(?:\})?\/(scripts\/lib\/fetch-baseline\.[a-z]+)["']?/g;
|
|
87
|
+
|
|
88
|
+
// Default file extensions searched by detectBridgeBalance scope entries when a
|
|
89
|
+
// scope entry does not carry its own `exts`.
|
|
90
|
+
const DEFAULT_BRIDGE_EXTS = ['.mjs', '.md'];
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Small helpers (pure).
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Split file content into lines, tolerant of CRLF.
|
|
98
|
+
* @param {string} text
|
|
99
|
+
* @returns {string[]}
|
|
100
|
+
*/
|
|
101
|
+
function splitLines(text) {
|
|
102
|
+
return String(text).split(/\r?\n/);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* True if a line should be skipped by a sub-rule: it carries either the
|
|
107
|
+
* sub-rule's own historical ignore marker or the consolidated dead-bridge
|
|
108
|
+
* marker.
|
|
109
|
+
* @param {string} text
|
|
110
|
+
* @param {string} ownMarker
|
|
111
|
+
* @returns {boolean}
|
|
112
|
+
*/
|
|
113
|
+
function isIgnored(text, ownMarker) {
|
|
114
|
+
return text.includes(ownMarker) || text.includes(DEAD_BRIDGE_IGNORE);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Make a fail Finding.
|
|
119
|
+
* @param {string} rule
|
|
120
|
+
* @param {string} file
|
|
121
|
+
* @param {number} line
|
|
122
|
+
* @param {string} message
|
|
123
|
+
* @returns {Finding}
|
|
124
|
+
*/
|
|
125
|
+
function fail(rule, file, line, message) {
|
|
126
|
+
return { rule, severity: 'fail', file: file || '', line: line ?? 0, message };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// Sub-rule 1 — dangling-subagent-type (port of check-subagent-types.mjs).
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Scan recursive skills/**.md for `subagent_type: "session-orchestrator:<X>"`
|
|
135
|
+
* references and FAIL when no `agents/<X>.md` exists. Missing skills/ dir is a
|
|
136
|
+
* vacuous pass (no findings).
|
|
137
|
+
*
|
|
138
|
+
* @param {RepoContext} ctx
|
|
139
|
+
* @returns {Finding[]}
|
|
140
|
+
*/
|
|
141
|
+
function detectDanglingSubagentTypes(ctx) {
|
|
142
|
+
/** @type {Finding[]} */
|
|
143
|
+
const findings = [];
|
|
144
|
+
const skillsDir = join(ctx.pluginRoot, 'skills');
|
|
145
|
+
const agentsDir = join(ctx.pluginRoot, 'agents');
|
|
146
|
+
|
|
147
|
+
// Missing skills/ dir → vacuously OK (no findings, NOT a tool-error).
|
|
148
|
+
if (!ctx.exists(skillsDir)) return findings;
|
|
149
|
+
|
|
150
|
+
for (const file of ctx.listMdFiles(skillsDir)) {
|
|
151
|
+
const lines = splitLines(ctx.readText(file));
|
|
152
|
+
lines.forEach((text, idx) => {
|
|
153
|
+
if (isIgnored(text, SUBAGENT_IGNORE_MARKER)) return;
|
|
154
|
+
SUBAGENT_REF_RE.lastIndex = 0;
|
|
155
|
+
let m;
|
|
156
|
+
while ((m = SUBAGENT_REF_RE.exec(text)) !== null) {
|
|
157
|
+
const agent = m[1];
|
|
158
|
+
const agentFile = join(agentsDir, `${agent}.md`);
|
|
159
|
+
if (!ctx.exists(agentFile)) {
|
|
160
|
+
findings.push(
|
|
161
|
+
fail(
|
|
162
|
+
'dangling-subagent-type',
|
|
163
|
+
file,
|
|
164
|
+
idx + 1,
|
|
165
|
+
`subagent_type session-orchestrator:${agent} → agents/${agent}.md NOT FOUND`,
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return findings;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
// Sub-rule 2 — dangling-rule-reference (port of check-rules-references.mjs).
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Scan TOP-LEVEL `.claude/rules/*.md` (non-recursive) for bare-basename rule
|
|
182
|
+
* references — backtick refs and See-Also footer tokens — and FAIL when the
|
|
183
|
+
* referenced basename does not resolve to an existing sibling rule file.
|
|
184
|
+
*
|
|
185
|
+
* Tool-error: missing/unreadable rules dir → a `dangling-rule-reference-tool-error`
|
|
186
|
+
* Finding (orchestrator maps to exit 2).
|
|
187
|
+
*
|
|
188
|
+
* @param {RepoContext} ctx
|
|
189
|
+
* @returns {Finding[]}
|
|
190
|
+
*/
|
|
191
|
+
function detectDanglingRuleReferences(ctx) {
|
|
192
|
+
/** @type {Finding[]} */
|
|
193
|
+
const findings = [];
|
|
194
|
+
const rulesDir = join(ctx.pluginRoot, '.claude', 'rules');
|
|
195
|
+
|
|
196
|
+
// Tool-error gate: the rules dir is REQUIRED for this sub-rule.
|
|
197
|
+
if (!ctx.exists(rulesDir)) {
|
|
198
|
+
return [
|
|
199
|
+
fail(
|
|
200
|
+
'dangling-rule-reference-tool-error',
|
|
201
|
+
'',
|
|
202
|
+
0,
|
|
203
|
+
`rules directory not found: ${rulesDir}`,
|
|
204
|
+
),
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// listMdFiles is recursive; filter to DIRECT children of rulesDir only — the
|
|
209
|
+
// original guard scanned the top level of `.claude/rules/` exclusively.
|
|
210
|
+
const allMd = ctx.listMdFiles(rulesDir);
|
|
211
|
+
const topLevel = allMd.filter((f) => dirname(f) === rulesDir);
|
|
212
|
+
|
|
213
|
+
// The resolution universe: the set of existing top-level rule basenames.
|
|
214
|
+
const existing = new Set(topLevel.map((f) => basename(f)));
|
|
215
|
+
|
|
216
|
+
for (const full of topLevel) {
|
|
217
|
+
const name = basename(full);
|
|
218
|
+
const lines = splitLines(ctx.readText(full));
|
|
219
|
+
|
|
220
|
+
lines.forEach((text, idx) => {
|
|
221
|
+
if (isIgnored(text, RULES_IGNORE_MARKER)) return;
|
|
222
|
+
const lineNo = idx + 1;
|
|
223
|
+
|
|
224
|
+
// Locus (a): backtick refs `name.md` anywhere in the body.
|
|
225
|
+
BACKTICK_REF_RE.lastIndex = 0;
|
|
226
|
+
let m;
|
|
227
|
+
while ((m = BACKTICK_REF_RE.exec(text)) !== null) {
|
|
228
|
+
const inner = m[1];
|
|
229
|
+
if (inner.includes('/')) continue; // path-qualified — out of scope
|
|
230
|
+
if (!RULE_BASENAME_RE.test(inner)) continue; // not a rule-style basename
|
|
231
|
+
if (inner === name) continue; // self-reference
|
|
232
|
+
if (!existing.has(inner)) {
|
|
233
|
+
findings.push(
|
|
234
|
+
fail(
|
|
235
|
+
'dangling-rule-reference',
|
|
236
|
+
full,
|
|
237
|
+
lineNo,
|
|
238
|
+
`rule reference ${inner} NOT FOUND in .claude/rules/`,
|
|
239
|
+
),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Locus (b): See-Also footer tokens — only on `· `-delimited list lines.
|
|
245
|
+
if (text.includes('· ') && /[a-z0-9-]+\.md/.test(text)) {
|
|
246
|
+
SEE_ALSO_TOKEN_RE.lastIndex = 0;
|
|
247
|
+
let s;
|
|
248
|
+
while ((s = SEE_ALSO_TOKEN_RE.exec(text)) !== null) {
|
|
249
|
+
const token = s[1];
|
|
250
|
+
// Skip tokens that are the tail of a path-qualified ref (e.g.
|
|
251
|
+
// `skills/_shared/state-ownership.md`): walk back to the nearest
|
|
252
|
+
// whitespace/backtick/`·` boundary and check for a slash.
|
|
253
|
+
const before = text.slice(0, s.index);
|
|
254
|
+
if (/[\w./-]$/.test(before.slice(-1)) && /\//.test(before.slice(-40))) {
|
|
255
|
+
const segMatch = before.match(/([^\s`·]+)$/);
|
|
256
|
+
if (segMatch && segMatch[1].includes('/')) continue;
|
|
257
|
+
}
|
|
258
|
+
if (token === name) continue; // self-reference
|
|
259
|
+
if (!existing.has(token)) {
|
|
260
|
+
findings.push(
|
|
261
|
+
fail(
|
|
262
|
+
'dangling-rule-reference',
|
|
263
|
+
full,
|
|
264
|
+
lineNo,
|
|
265
|
+
`rule reference ${token} NOT FOUND in .claude/rules/`,
|
|
266
|
+
),
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return findings;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
// Sub-rule 3 — dangling-bootstrap-bridge (port of check-baseline-fetch-bridge.mjs).
|
|
279
|
+
// ---------------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Scan the fixed bootstrap file set for the baseline-fetch bridge and FAIL on:
|
|
283
|
+
* - a `-f "$PLUGIN_ROOT/scripts/lib/fetch-baseline.<ext>"` guard whose target
|
|
284
|
+
* does not exist (the #618 always-false dead-bridge),
|
|
285
|
+
* - any reference to the stale `fetch-baseline.sh` basename,
|
|
286
|
+
* - any reference to a dead shell-function name,
|
|
287
|
+
* - ZERO guards found across the whole scanned set (requireAtLeastOne).
|
|
288
|
+
*
|
|
289
|
+
* Absent individual bootstrap files are skipped silently (informational, not a
|
|
290
|
+
* finding). Tool-error: missing bootstrap dir → a
|
|
291
|
+
* `dangling-bootstrap-bridge-tool-error` Finding (orchestrator maps to exit 2).
|
|
292
|
+
*
|
|
293
|
+
* @param {RepoContext} ctx
|
|
294
|
+
* @returns {Finding[]}
|
|
295
|
+
*/
|
|
296
|
+
function detectDanglingBootstrapBridge(ctx) {
|
|
297
|
+
/** @type {Finding[]} */
|
|
298
|
+
const findings = [];
|
|
299
|
+
const bootstrapDir = join(ctx.pluginRoot, 'skills', 'bootstrap');
|
|
300
|
+
|
|
301
|
+
// Tool-error gate: the bootstrap dir is REQUIRED for this sub-rule.
|
|
302
|
+
if (!ctx.exists(bootstrapDir)) {
|
|
303
|
+
return [
|
|
304
|
+
fail(
|
|
305
|
+
'dangling-bootstrap-bridge-tool-error',
|
|
306
|
+
'',
|
|
307
|
+
0,
|
|
308
|
+
`bootstrap directory not found: ${bootstrapDir}`,
|
|
309
|
+
),
|
|
310
|
+
];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let guardCount = 0;
|
|
314
|
+
|
|
315
|
+
for (const name of BOOTSTRAP_FILES) {
|
|
316
|
+
const full = join(bootstrapDir, name);
|
|
317
|
+
// Absent bootstrap file → skip silently (informational, not a finding).
|
|
318
|
+
if (!ctx.exists(full)) continue;
|
|
319
|
+
|
|
320
|
+
const lines = splitLines(ctx.readText(full));
|
|
321
|
+
lines.forEach((text, idx) => {
|
|
322
|
+
if (isIgnored(text, BOOTSTRAP_IGNORE_MARKER)) return;
|
|
323
|
+
const lineNo = idx + 1;
|
|
324
|
+
|
|
325
|
+
// Guard refs — every guarded fetch-baseline path must resolve.
|
|
326
|
+
GUARD_RE.lastIndex = 0;
|
|
327
|
+
let m;
|
|
328
|
+
while ((m = GUARD_RE.exec(text)) !== null) {
|
|
329
|
+
guardCount++;
|
|
330
|
+
const guardedPath = m[1];
|
|
331
|
+
const target = join(ctx.pluginRoot, guardedPath);
|
|
332
|
+
if (!ctx.exists(target)) {
|
|
333
|
+
findings.push(
|
|
334
|
+
fail(
|
|
335
|
+
'dangling-bootstrap-bridge',
|
|
336
|
+
full,
|
|
337
|
+
lineNo,
|
|
338
|
+
`guard "${guardedPath}" points at a non-existent file — dead always-false bridge (#618 regression class)`,
|
|
339
|
+
),
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Forbidden-presence: stale `.sh` basename.
|
|
345
|
+
if (text.includes(STALE_BASENAME)) {
|
|
346
|
+
findings.push(
|
|
347
|
+
fail(
|
|
348
|
+
'dangling-bootstrap-bridge',
|
|
349
|
+
full,
|
|
350
|
+
lineNo,
|
|
351
|
+
`stale "${STALE_BASENAME}" reference (the plugin ships .mjs only)`,
|
|
352
|
+
),
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Forbidden-presence: dead shell-function names.
|
|
357
|
+
for (const fn of DEAD_FUNCTIONS) {
|
|
358
|
+
if (text.includes(fn)) {
|
|
359
|
+
findings.push(
|
|
360
|
+
fail(
|
|
361
|
+
'dangling-bootstrap-bridge',
|
|
362
|
+
full,
|
|
363
|
+
lineNo,
|
|
364
|
+
`dead shell-function reference "${fn}" (removed shell-source bridge)`,
|
|
365
|
+
),
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// requireAtLeastOne: zero fetch-baseline guards anywhere is itself a dead
|
|
373
|
+
// bridge (the on-demand baseline-fetch step lost its guard entirely).
|
|
374
|
+
if (guardCount === 0) {
|
|
375
|
+
findings.push(
|
|
376
|
+
fail(
|
|
377
|
+
'dangling-bootstrap-bridge',
|
|
378
|
+
'',
|
|
379
|
+
0,
|
|
380
|
+
'no fetch-baseline guard reference found in bootstrap files (expected the on-demand baseline-fetch bridge guard)',
|
|
381
|
+
),
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return findings;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ---------------------------------------------------------------------------
|
|
389
|
+
// Public detector: detectDanglingReferences — composes the 3 sub-rules.
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Run all three dangling-reference sub-rules and return their combined
|
|
394
|
+
* Finding[]. Tool-errors are surfaced as `*-tool-error` Findings (see the
|
|
395
|
+
* TOOL-ERROR CONVENTION at the top of this file).
|
|
396
|
+
*
|
|
397
|
+
* @param {RepoContext} ctx
|
|
398
|
+
* @returns {Finding[]}
|
|
399
|
+
*/
|
|
400
|
+
export function detectDanglingReferences(ctx) {
|
|
401
|
+
return [
|
|
402
|
+
...detectDanglingSubagentTypes(ctx),
|
|
403
|
+
...detectDanglingRuleReferences(ctx),
|
|
404
|
+
...detectDanglingBootstrapBridge(ctx),
|
|
405
|
+
];
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ---------------------------------------------------------------------------
|
|
409
|
+
// Public detector: detectBridgeBalance — the NEW dead-bridge class.
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Build a RegExp from a producer/consumer pattern. The pattern may be a string
|
|
414
|
+
* (treated as a literal substring) or a RegExp source string. We always compile
|
|
415
|
+
* with the 'g' flag so `match` counts every occurrence. A leading/trailing `/`
|
|
416
|
+
* pair is treated as a regex literal; otherwise the string is escaped to a
|
|
417
|
+
* literal match.
|
|
418
|
+
*
|
|
419
|
+
* @param {string} pattern
|
|
420
|
+
* @returns {RegExp}
|
|
421
|
+
*/
|
|
422
|
+
function compilePattern(pattern) {
|
|
423
|
+
const src = String(pattern);
|
|
424
|
+
// `/.../flags` form → treat the inside as a regex source.
|
|
425
|
+
const literalRe = src.match(/^\/(.*)\/([a-z]*)$/s);
|
|
426
|
+
if (literalRe) {
|
|
427
|
+
const flags = literalRe[2].includes('g') ? literalRe[2] : `${literalRe[2]}g`;
|
|
428
|
+
return new RegExp(literalRe[1], flags);
|
|
429
|
+
}
|
|
430
|
+
// Plain string → escape regex metacharacters and match literally.
|
|
431
|
+
const escaped = src.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
432
|
+
return new RegExp(escaped, 'g');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Normalize an extension allow-list so every entry carries a leading dot. This
|
|
437
|
+
* lets the corpus declare exts either way (`'md'` or `'.md'`) while the
|
|
438
|
+
* underlying `ctx.listFiles` (which compares against `path.extname()`, e.g.
|
|
439
|
+
* `'.md'`) still matches. Idempotent: `'.md'` stays `'.md'`.
|
|
440
|
+
*
|
|
441
|
+
* @param {string[]|undefined} exts
|
|
442
|
+
* @param {string[]} defaultExts
|
|
443
|
+
* @returns {string[]}
|
|
444
|
+
*/
|
|
445
|
+
function normalizeExts(exts, defaultExts) {
|
|
446
|
+
const list = exts && exts.length ? exts : defaultExts;
|
|
447
|
+
return list.map((e) => (String(e).startsWith('.') ? String(e) : `.${e}`));
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Count total matches of `pattern` across every file in `scope`. Each scope
|
|
452
|
+
* entry is a repo-relative path that may be EITHER a single FILE or a DIRECTORY
|
|
453
|
+
* (optionally an object `{ dir, exts }`):
|
|
454
|
+
*
|
|
455
|
+
* - FILE entry → read it directly via ctx.readText and count matches. We
|
|
456
|
+
* detect "is a file" by ATTEMPTING ctx.readText first: detectors only have
|
|
457
|
+
* the injected ctx callbacks (no isDirectory), so a successful read of a
|
|
458
|
+
* string is the cheapest reliable file-signal. If the read throws (the path
|
|
459
|
+
* is a directory, or missing), we fall through to the directory branch.
|
|
460
|
+
* - DIRECTORY entry → enumerate via ctx.listFiles with the scope's `exts`
|
|
461
|
+
* (default `.mjs` + `.md`, leading dot normalized) and read each file.
|
|
462
|
+
*
|
|
463
|
+
* This file-or-dir handling lets the corpus declare scope as concrete file
|
|
464
|
+
* paths (e.g. `docs/session-config-template.md`) without a new ctx callback.
|
|
465
|
+
*
|
|
466
|
+
* @param {RepoContext} ctx
|
|
467
|
+
* @param {string} pattern
|
|
468
|
+
* @param {Array<string|{dir: string, exts?: string[]}>} scope
|
|
469
|
+
* @param {string[]} defaultExts
|
|
470
|
+
* @returns {number}
|
|
471
|
+
*/
|
|
472
|
+
function countMatches(ctx, pattern, scope, defaultExts) {
|
|
473
|
+
let count = 0;
|
|
474
|
+
const countIn = (text) => {
|
|
475
|
+
const re = compilePattern(pattern);
|
|
476
|
+
const matches = String(text).match(re);
|
|
477
|
+
return matches ? matches.length : 0;
|
|
478
|
+
};
|
|
479
|
+
for (const entry of scope ?? []) {
|
|
480
|
+
const relPath = typeof entry === 'string' ? entry : entry.dir;
|
|
481
|
+
const exts = normalizeExts(typeof entry === 'object' ? entry.exts : undefined, defaultExts);
|
|
482
|
+
const abs = join(ctx.pluginRoot, relPath);
|
|
483
|
+
if (!ctx.exists(abs)) continue;
|
|
484
|
+
|
|
485
|
+
// FILE-first: try reading the entry directly. A successful read means it is
|
|
486
|
+
// a single file; a throw means it is a directory (or vanished) → fall back
|
|
487
|
+
// to the recursive directory walk.
|
|
488
|
+
let fileText;
|
|
489
|
+
try {
|
|
490
|
+
fileText = ctx.readText(abs);
|
|
491
|
+
} catch {
|
|
492
|
+
// Not a readable file (directory or vanished) → fall through to dir walk.
|
|
493
|
+
}
|
|
494
|
+
if (typeof fileText === 'string') {
|
|
495
|
+
count += countIn(fileText);
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
for (const file of ctx.listFiles(abs, exts)) {
|
|
499
|
+
count += countIn(ctx.readText(file));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return count;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* The NEW dead-bridge class. For each declared bridge — a producer/consumer
|
|
507
|
+
* pair — FAIL when exactly one side has zero matches:
|
|
508
|
+
* - producerCount === 0 && consumerCount > 0 → "read but never set"
|
|
509
|
+
* - consumerCount === 0 && producerCount > 0 → "set but never read"
|
|
510
|
+
*
|
|
511
|
+
* A bridge where BOTH sides are zero, or BOTH are non-zero, is NOT a finding.
|
|
512
|
+
* `bridges` defaults to [] → no findings (zero false positives by construction;
|
|
513
|
+
* only declared bridges are checked).
|
|
514
|
+
*
|
|
515
|
+
* @param {RepoContext} ctx
|
|
516
|
+
* @param {Array<{id: string, description?: string, producer: {pattern: string, scope: Array<string|{dir:string,exts?:string[]}>, exts?: string[]}, consumer: {pattern: string, scope: Array<string|{dir:string,exts?:string[]}>, exts?: string[]}}>} bridges
|
|
517
|
+
* @returns {Finding[]}
|
|
518
|
+
*/
|
|
519
|
+
export function detectBridgeBalance(ctx, bridges) {
|
|
520
|
+
/** @type {Finding[]} */
|
|
521
|
+
const findings = [];
|
|
522
|
+
|
|
523
|
+
for (const bridge of bridges ?? []) {
|
|
524
|
+
const id = bridge?.id ?? '<unnamed>';
|
|
525
|
+
const producer = bridge?.producer;
|
|
526
|
+
const consumer = bridge?.consumer;
|
|
527
|
+
if (!producer || !consumer) continue; // malformed bridge — nothing to check
|
|
528
|
+
|
|
529
|
+
const producerExts = producer.exts || DEFAULT_BRIDGE_EXTS;
|
|
530
|
+
const consumerExts = consumer.exts || DEFAULT_BRIDGE_EXTS;
|
|
531
|
+
|
|
532
|
+
const producerCount = countMatches(ctx, producer.pattern, producer.scope, producerExts);
|
|
533
|
+
const consumerCount = countMatches(ctx, consumer.pattern, consumer.scope, consumerExts);
|
|
534
|
+
|
|
535
|
+
if (producerCount === 0 && consumerCount > 0) {
|
|
536
|
+
findings.push(
|
|
537
|
+
fail(
|
|
538
|
+
'bridge-balance',
|
|
539
|
+
'',
|
|
540
|
+
0,
|
|
541
|
+
`bridge "${id}": consumer side matches (${consumerCount}) but producer side has ZERO matches — read but never set (dead bridge)`,
|
|
542
|
+
),
|
|
543
|
+
);
|
|
544
|
+
} else if (consumerCount === 0 && producerCount > 0) {
|
|
545
|
+
findings.push(
|
|
546
|
+
fail(
|
|
547
|
+
'bridge-balance',
|
|
548
|
+
'',
|
|
549
|
+
0,
|
|
550
|
+
`bridge "${id}": producer side matches (${producerCount}) but consumer side has ZERO matches — set but never read (dead bridge)`,
|
|
551
|
+
),
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return findings;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// ---------------------------------------------------------------------------
|
|
560
|
+
// Detector registry — consumed by the orchestrator (check-dead-bridge.mjs).
|
|
561
|
+
// Each entry's `fn(ctx, corpus)` returns Finding[]. The corpus carries the
|
|
562
|
+
// declared BRIDGES list for the bridge-balance detector.
|
|
563
|
+
// ---------------------------------------------------------------------------
|
|
564
|
+
|
|
565
|
+
export const DETECTORS = [
|
|
566
|
+
{ id: 'dangling-reference', fn: (ctx, _corpus) => detectDanglingReferences(ctx) },
|
|
567
|
+
{ id: 'bridge-balance', fn: (ctx, corpus) => detectBridgeBalance(ctx, corpus.BRIDGES ?? []) },
|
|
568
|
+
];
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tier-inference.mjs — Sandbox-tier vocabulary + inference helpers (issue #418).
|
|
3
|
+
*
|
|
4
|
+
* Four tiers (ordered from most-restricted to most-permissive):
|
|
5
|
+
* read-only — only observes the repo; no writes, no network
|
|
6
|
+
* repo-write — may create or modify files (Edit, Write)
|
|
7
|
+
* network-allowed — may make outbound network calls (future use)
|
|
8
|
+
* dangerous — may run destructive shell commands (future use)
|
|
9
|
+
*
|
|
10
|
+
* Backward-compat: agents without a `sandbox-tier:` field in frontmatter
|
|
11
|
+
* infer their tier from their declared tools list. The validator emits
|
|
12
|
+
* WARN (not FAIL) when the field is absent, so existing agents continue
|
|
13
|
+
* to work during migration.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Canonical ordered tier enum.
|
|
18
|
+
*
|
|
19
|
+
* @type {string[]}
|
|
20
|
+
*/
|
|
21
|
+
export const TIER_ENUM = ['read-only', 'repo-write', 'network-allowed', 'dangerous'];
|
|
22
|
+
|
|
23
|
+
// Tools that signal write capability.
|
|
24
|
+
const WRITE_TOOLS = new Set(['Edit', 'Write']);
|
|
25
|
+
|
|
26
|
+
// Tools that are acceptable in the read-only tier (Bash is fine — fine-grained
|
|
27
|
+
// Bash control lives in hooks/pre-bash-destructive-guard.mjs, NOT here).
|
|
28
|
+
const READ_ONLY_TOOLS = new Set(['Read', 'Grep', 'Glob', 'Bash', 'Skill']);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Infer the sandbox tier from a parsed tools array.
|
|
32
|
+
*
|
|
33
|
+
* The inference is intentionally conservative:
|
|
34
|
+
* - Any Edit or Write entry → repo-write
|
|
35
|
+
* - All entries are read-only-compatible → read-only
|
|
36
|
+
* - Anything else (unknown tool, empty array) → repo-write (safe default)
|
|
37
|
+
*
|
|
38
|
+
* Skill(...) entries (e.g. "Skill(session-orchestrator:*)") are normalised
|
|
39
|
+
* to "Skill" before lookup so they match READ_ONLY_TOOLS.
|
|
40
|
+
*
|
|
41
|
+
* @param {string[]} toolsArray - array of tool name strings from frontmatter
|
|
42
|
+
* @returns {string} one of the values in TIER_ENUM
|
|
43
|
+
*/
|
|
44
|
+
export function inferTierFromTools(toolsArray) {
|
|
45
|
+
if (!Array.isArray(toolsArray) || toolsArray.length === 0) {
|
|
46
|
+
return 'repo-write'; // safe default
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Normalise "Skill(...)" → "Skill"
|
|
50
|
+
const normalised = toolsArray.map((t) => {
|
|
51
|
+
if (typeof t !== 'string') return String(t);
|
|
52
|
+
return t.startsWith('Skill(') ? 'Skill' : t.trim();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Any write tool → repo-write
|
|
56
|
+
for (const t of normalised) {
|
|
57
|
+
if (WRITE_TOOLS.has(t)) return 'repo-write';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// If every tool is in the read-only-compatible set → read-only
|
|
61
|
+
const allReadOnly = normalised.every((t) => READ_ONLY_TOOLS.has(t));
|
|
62
|
+
if (allReadOnly) return 'read-only';
|
|
63
|
+
|
|
64
|
+
// Unknown tool encountered — safe default
|
|
65
|
+
return 'repo-write';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validate that a declared sandbox-tier is consistent with the agent's tools.
|
|
70
|
+
*
|
|
71
|
+
* @param {{
|
|
72
|
+
* declared: string,
|
|
73
|
+
* inferred: string,
|
|
74
|
+
* tools?: string[]
|
|
75
|
+
* }} params
|
|
76
|
+
* @returns {{ ok: true } | { ok: false, error: string }}
|
|
77
|
+
*/
|
|
78
|
+
export function validateTierConsistency({ declared, inferred, tools }) {
|
|
79
|
+
// 1. Declared value must be in the enum.
|
|
80
|
+
if (!TIER_ENUM.includes(declared)) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: `sandbox-tier "${declared}" is not a valid tier; must be one of: ${TIER_ENUM.join(', ')}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 2. Read-only agent must not have write tools.
|
|
88
|
+
if (declared === 'read-only' && inferred !== 'read-only') {
|
|
89
|
+
const writeToolsPresent = Array.isArray(tools)
|
|
90
|
+
? tools.filter((t) => WRITE_TOOLS.has(t))
|
|
91
|
+
: [];
|
|
92
|
+
const detail = writeToolsPresent.length > 0 ? ` (tools include: ${writeToolsPresent.join(', ')})` : '';
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
error: `agent declares sandbox-tier "read-only" but tools suggest "${inferred}"${detail}`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { ok: true };
|
|
100
|
+
}
|