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,213 @@
|
|
|
1
|
+
import { matchBlockHeader } from './block-header.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* custom-phases.mjs — Parser for the `custom-phases:` Session Config block (#637).
|
|
5
|
+
*
|
|
6
|
+
* Lets a repo declare opt-in deterministic phases that run during session close
|
|
7
|
+
* (and/or housekeeping) as a CONTRACT: each phase has a name, a `when` trigger,
|
|
8
|
+
* a shell `command`, a `mode` (warn|hard|off) that gates the close, and an
|
|
9
|
+
* optional `review` file the coordinator reads after the command.
|
|
10
|
+
*
|
|
11
|
+
* Exports:
|
|
12
|
+
* _parseCustomPhases(content) — PURE, no side effects beyond a stderr WARN when
|
|
13
|
+
* a record is dropped for failing validation.
|
|
14
|
+
* Returns [] when the block is absent/empty.
|
|
15
|
+
*
|
|
16
|
+
* Block shape:
|
|
17
|
+
* custom-phases:
|
|
18
|
+
* - name: eval-learn-aggregate # required, non-empty, SAFE slug
|
|
19
|
+
* when: housekeeping # housekeeping | session-end | both (default: session-end)
|
|
20
|
+
* command: npm run eval:aggregate # required; NO interpolation from records
|
|
21
|
+
* mode: hard # warn | hard | off (default: warn)
|
|
22
|
+
* review: docs/eval/last-run.md # optional; SAFE_PATH_RE-validated; default null
|
|
23
|
+
*
|
|
24
|
+
* Modelled on cross-repo.mjs (per-entry SAFE_PATH_RE validation, drop-with-warn)
|
|
25
|
+
* and gitlab-portfolio.mjs (nested list/column-0 block scan). Enum fallbacks are
|
|
26
|
+
* SILENT to the default per block-parser convention (vault-sync.mjs precedent).
|
|
27
|
+
*
|
|
28
|
+
* NOTE on the mode enum: it is ['warn','hard','off'] — `hard` is the correct
|
|
29
|
+
* blocking value for THIS new key. Do NOT copy vault-sync's ['strict','warn','off']
|
|
30
|
+
* (see #217 regression: `hard` was a silent-default bug THERE, intentional HERE).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** Per-phase defaults. */
|
|
34
|
+
export const CUSTOM_PHASE_DEFAULTS = Object.freeze({
|
|
35
|
+
when: 'session-end',
|
|
36
|
+
mode: 'warn',
|
|
37
|
+
review: null,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const VALID_WHEN = new Set(['housekeeping', 'session-end', 'both']);
|
|
41
|
+
const VALID_MODE = new Set(['warn', 'hard', 'off']);
|
|
42
|
+
|
|
43
|
+
// Allowlist: a phase `name` is a SAFE slug — lowercase/uppercase letters, digits,
|
|
44
|
+
// hyphen, underscore, dot. No spaces, no shell metacharacters.
|
|
45
|
+
const SAFE_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
46
|
+
|
|
47
|
+
// Allowlist for `command` and `review`: rejects shell metacharacters (; $ ` | & > <
|
|
48
|
+
// ( ) newline) that would break the exit-code-gated execution / file-read contract.
|
|
49
|
+
// `command` legitimately contains spaces, slashes, colons (e.g. "npm run eval:aggregate"),
|
|
50
|
+
// so the allowlist is broader than SAFE_NAME_RE but still excludes injection vectors.
|
|
51
|
+
const SAFE_COMMAND_RE = /^[A-Za-z0-9._\-/: =]+$/;
|
|
52
|
+
|
|
53
|
+
// `review` is a repo-relative or absolute file path — no spaces, no metacharacters.
|
|
54
|
+
const SAFE_PATH_RE = /^[A-Za-z0-9._~/-]+$/;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Parse the top-level `custom-phases:` YAML list block from markdown content.
|
|
58
|
+
*
|
|
59
|
+
* Each list item is a record. Records are validated per-entry; a record missing
|
|
60
|
+
* a required field (`name`, `command`) or carrying a shell-metacharacter in
|
|
61
|
+
* `command`/`review`/`name` is DROPPED with a stderr WARN. Invalid `when`/`mode`
|
|
62
|
+
* enum values fall back SILENTLY to their defaults.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} content — full CLAUDE.md / AGENTS.md file content
|
|
65
|
+
* @returns {Array<{name: string, when: string, command: string, mode: string, review: string|null}>}
|
|
66
|
+
*/
|
|
67
|
+
export function _parseCustomPhases(content) {
|
|
68
|
+
const lines = content.split(/\r?\n/);
|
|
69
|
+
let inBlock = false;
|
|
70
|
+
const blockLines = [];
|
|
71
|
+
|
|
72
|
+
for (const rawLine of lines) {
|
|
73
|
+
const line = rawLine.replace(/\r$/, '');
|
|
74
|
+
|
|
75
|
+
if (!inBlock) {
|
|
76
|
+
// Detect `custom-phases:` at column 0 with optional trailing spaces.
|
|
77
|
+
if (matchBlockHeader(line, 'custom-phases')) inBlock = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Block terminates at the first non-indented, non-empty line (next top-level key).
|
|
82
|
+
if (line.length > 0 && !/^\s/.test(line)) break;
|
|
83
|
+
|
|
84
|
+
blockLines.push(line);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (blockLines.length === 0) return [];
|
|
88
|
+
|
|
89
|
+
/** @type {Array<{name: string, when: string, command: string, mode: string, review: string|null}>} */
|
|
90
|
+
const records = [];
|
|
91
|
+
/** @type {Record<string, string>|null} */
|
|
92
|
+
let current = null;
|
|
93
|
+
|
|
94
|
+
const flush = () => {
|
|
95
|
+
if (current === null) return;
|
|
96
|
+
const rec = _validateRecord(current);
|
|
97
|
+
if (rec !== null) records.push(rec);
|
|
98
|
+
current = null;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
for (const rawLine of blockLines) {
|
|
102
|
+
// Strip inline comments + trailing whitespace, preserve leading indent.
|
|
103
|
+
const clean = rawLine.replace(/\s*#.*$/, '').replace(/\s+$/, '');
|
|
104
|
+
if (!clean.trim()) continue;
|
|
105
|
+
|
|
106
|
+
// A new list item starts with a dash. The first key may sit on the dash line:
|
|
107
|
+
// - name: foo
|
|
108
|
+
const dashMatch = clean.match(/^\s*-\s+(.*)$/);
|
|
109
|
+
if (dashMatch) {
|
|
110
|
+
flush();
|
|
111
|
+
current = {};
|
|
112
|
+
const inlineKv = dashMatch[1].match(/^([a-zA-Z][a-zA-Z0-9_-]*):\s*(.*)$/);
|
|
113
|
+
if (inlineKv) {
|
|
114
|
+
_assignKv(current, inlineKv[1], inlineKv[2]);
|
|
115
|
+
}
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Continuation key for the current record (deeper indent, no dash).
|
|
120
|
+
const kvMatch = clean.match(/^\s+([a-zA-Z][a-zA-Z0-9_-]*):\s*(.*)$/);
|
|
121
|
+
if (kvMatch && current !== null) {
|
|
122
|
+
_assignKv(current, kvMatch[1], kvMatch[2]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
flush();
|
|
127
|
+
|
|
128
|
+
return records;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Assign a raw key/value onto a record being built, stripping surrounding quotes.
|
|
133
|
+
* Only known keys are stored; unknown keys are silently ignored (additive-friendly).
|
|
134
|
+
*
|
|
135
|
+
* @param {Record<string, string>} record
|
|
136
|
+
* @param {string} key
|
|
137
|
+
* @param {string} rawValue
|
|
138
|
+
*/
|
|
139
|
+
function _assignKv(record, key, rawValue) {
|
|
140
|
+
let v = rawValue.trim();
|
|
141
|
+
if (v.startsWith('"') && v.endsWith('"') && v.length >= 2) v = v.slice(1, -1);
|
|
142
|
+
else if (v.startsWith("'") && v.endsWith("'") && v.length >= 2) v = v.slice(1, -1);
|
|
143
|
+
|
|
144
|
+
switch (key) {
|
|
145
|
+
case 'name':
|
|
146
|
+
case 'when':
|
|
147
|
+
case 'command':
|
|
148
|
+
case 'mode':
|
|
149
|
+
case 'review':
|
|
150
|
+
record[key] = v;
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
// Unknown keys are silently ignored — additive-friendly.
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Validate + normalise a raw record. Returns the fully-defaulted record, or null
|
|
160
|
+
* (with a stderr WARN) when a required field is missing or a SAFE-regex fails.
|
|
161
|
+
*
|
|
162
|
+
* @param {Record<string, string>} raw
|
|
163
|
+
* @returns {{name: string, when: string, command: string, mode: string, review: string|null}|null}
|
|
164
|
+
*/
|
|
165
|
+
function _validateRecord(raw) {
|
|
166
|
+
const name = (raw.name ?? '').trim();
|
|
167
|
+
if (name === '') {
|
|
168
|
+
process.stderr.write('custom-phases: dropped record missing required field: name\n');
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
if (!SAFE_NAME_RE.test(name)) {
|
|
172
|
+
process.stderr.write(
|
|
173
|
+
`custom-phases: dropped record with unsafe name: ${JSON.stringify(name)}\n`,
|
|
174
|
+
);
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const command = (raw.command ?? '').trim();
|
|
179
|
+
if (command === '') {
|
|
180
|
+
process.stderr.write(
|
|
181
|
+
`custom-phases: dropped record '${name}' missing required field: command\n`,
|
|
182
|
+
);
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
if (!SAFE_COMMAND_RE.test(command)) {
|
|
186
|
+
process.stderr.write(
|
|
187
|
+
`custom-phases: dropped record '${name}' with shell metacharacter in command: ${JSON.stringify(command)}\n`,
|
|
188
|
+
);
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// when: enum fallback SILENT to default.
|
|
193
|
+
const when = VALID_WHEN.has(raw.when) ? raw.when : CUSTOM_PHASE_DEFAULTS.when;
|
|
194
|
+
|
|
195
|
+
// mode: enum fallback SILENT to default.
|
|
196
|
+
const mode = VALID_MODE.has(raw.mode) ? raw.mode : CUSTOM_PHASE_DEFAULTS.mode;
|
|
197
|
+
|
|
198
|
+
// review: optional; validated against SAFE_PATH_RE. An unsafe path DROPS the
|
|
199
|
+
// whole record (it is a security-relevant field the coordinator will read).
|
|
200
|
+
let review = CUSTOM_PHASE_DEFAULTS.review;
|
|
201
|
+
const rawReview = (raw.review ?? '').trim();
|
|
202
|
+
if (rawReview !== '' && rawReview !== 'null' && rawReview !== 'none') {
|
|
203
|
+
if (!SAFE_PATH_RE.test(rawReview)) {
|
|
204
|
+
process.stderr.write(
|
|
205
|
+
`custom-phases: dropped record '${name}' with shell metacharacter in review path: ${JSON.stringify(rawReview)}\n`,
|
|
206
|
+
);
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
review = rawReview;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return { name, when, command, mode, review };
|
|
213
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { matchBlockHeader } from './block-header.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* dialectic.mjs — Parser for top-level `dialectic:` YAML block (#506).
|
|
5
|
+
*
|
|
6
|
+
* Drives the Dialectic-Deriver via /evolve --dialectic and session-end Phase 3.6.7.
|
|
7
|
+
* Returns { cadence, model, "budget-tokens" }.
|
|
8
|
+
* Fail-fast on unknown model value (per #506 EARS unwanted-behaviour);
|
|
9
|
+
* silent fallback for out-of-range integers (matches cold-start.mjs precedent).
|
|
10
|
+
*
|
|
11
|
+
* Consumer: skills/evolve/SKILL.md Phase 6, skills/session-end/SKILL.md Phase 3.6.7.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const ALLOWED_MODELS = new Set(['haiku', 'sonnet', 'opus']);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parse the top-level `dialectic:` YAML block from markdown content.
|
|
18
|
+
* Independent of the `## Session Config` section boundary.
|
|
19
|
+
*
|
|
20
|
+
* Defaults:
|
|
21
|
+
* cadence: 5 (integer ≥ 0 — session count between auto-runs; 0 = disabled)
|
|
22
|
+
* model: 'haiku'
|
|
23
|
+
* budget-tokens: 8000 (integer ≥ 0)
|
|
24
|
+
*
|
|
25
|
+
* Throws if `model` is present but not one of haiku|sonnet|opus.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} content — full file contents
|
|
28
|
+
* @returns {{ cadence: number, model: string, "budget-tokens": number }}
|
|
29
|
+
*/
|
|
30
|
+
export function _parseDialectic(content) {
|
|
31
|
+
const defaults = { cadence: 5, model: 'haiku', 'budget-tokens': 8000 };
|
|
32
|
+
|
|
33
|
+
const lines = content.split(/\r?\n/);
|
|
34
|
+
let inBlock = false;
|
|
35
|
+
const blockLines = [];
|
|
36
|
+
|
|
37
|
+
for (const rawLine of lines) {
|
|
38
|
+
const line = rawLine.replace(/\r$/, '');
|
|
39
|
+
if (!inBlock) {
|
|
40
|
+
if (matchBlockHeader(line, 'dialectic')) inBlock = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (line.length > 0 && !/^\s/.test(line)) break;
|
|
44
|
+
blockLines.push(line);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (blockLines.length === 0) return defaults;
|
|
48
|
+
|
|
49
|
+
let cadence = 5;
|
|
50
|
+
let model = 'haiku';
|
|
51
|
+
let budgetTokens = 8000;
|
|
52
|
+
|
|
53
|
+
for (const rawLine of blockLines) {
|
|
54
|
+
const clean = rawLine.replace(/\s*#.*$/, '').replace(/\s+$/, '');
|
|
55
|
+
if (!clean.trim()) continue;
|
|
56
|
+
|
|
57
|
+
const kvMatch = clean.match(/^\s+([a-zA-Z_-]+):\s*(.*)/);
|
|
58
|
+
if (!kvMatch) continue;
|
|
59
|
+
|
|
60
|
+
const k = kvMatch[1];
|
|
61
|
+
let v = kvMatch[2].trim();
|
|
62
|
+
if (v.startsWith('"') && v.endsWith('"') && v.length >= 2) v = v.slice(1, -1);
|
|
63
|
+
else if (v.startsWith("'") && v.endsWith("'") && v.length >= 2) v = v.slice(1, -1);
|
|
64
|
+
|
|
65
|
+
switch (k) {
|
|
66
|
+
case 'cadence': {
|
|
67
|
+
if (/^\d+$/.test(v)) {
|
|
68
|
+
const n = parseInt(v, 10);
|
|
69
|
+
if (n >= 0) cadence = n;
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'model': {
|
|
74
|
+
const lower = v.toLowerCase();
|
|
75
|
+
if (!ALLOWED_MODELS.has(lower)) {
|
|
76
|
+
throw new Error(`dialectic.model must be haiku|sonnet|opus, got '${v}'`);
|
|
77
|
+
}
|
|
78
|
+
model = lower;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'budget-tokens': {
|
|
82
|
+
if (/^\d+$/.test(v)) {
|
|
83
|
+
const n = parseInt(v, 10);
|
|
84
|
+
if (n >= 0) budgetTokens = n;
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return { cadence, model, 'budget-tokens': budgetTokens };
|
|
92
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { matchBlockHeader } from './block-header.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* discovery-validator.mjs — Parser for the top-level `discovery-validator:`
|
|
5
|
+
* YAML block (PSA-006 mechanical enforcement / issue #567).
|
|
6
|
+
*
|
|
7
|
+
* Drives the non-blocking SubagentStop hook that flags distributional
|
|
8
|
+
* claims ("N of M", "100% of", "all N", "no remaining", "every X", "none of")
|
|
9
|
+
* in a subagent's transcript tail that lack an adjacent fenced grep/rg/find
|
|
10
|
+
* transcript. v1 is log+warn only (events.jsonl + stderr WARN); exit 2
|
|
11
|
+
* (blocking) is reserved for a future hard-gate.
|
|
12
|
+
*
|
|
13
|
+
* Returns `{ enabled }`.
|
|
14
|
+
* Tolerant parser: malformed values silently fall back to defaults.
|
|
15
|
+
*
|
|
16
|
+
* Consumer: `hooks/post-subagent-discovery-validator.mjs`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parse the top-level `discovery-validator:` YAML block from markdown content.
|
|
21
|
+
* Independent of the `## Session Config` section boundary.
|
|
22
|
+
*
|
|
23
|
+
* Defaults:
|
|
24
|
+
* enabled: true
|
|
25
|
+
*
|
|
26
|
+
* @param {string} content — full file contents
|
|
27
|
+
* @returns {{ enabled: boolean }}
|
|
28
|
+
*/
|
|
29
|
+
export function _parseDiscoveryValidator(content) {
|
|
30
|
+
const defaults = {
|
|
31
|
+
enabled: true,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const lines = content.split(/\r?\n/);
|
|
35
|
+
let inBlock = false;
|
|
36
|
+
const blockLines = [];
|
|
37
|
+
|
|
38
|
+
for (const rawLine of lines) {
|
|
39
|
+
const line = rawLine.replace(/\r$/, '');
|
|
40
|
+
if (!inBlock) {
|
|
41
|
+
if (matchBlockHeader(line, 'discovery-validator')) inBlock = true;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (line.length > 0 && !/^\s/.test(line)) break;
|
|
45
|
+
blockLines.push(line);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (blockLines.length === 0) return defaults;
|
|
49
|
+
|
|
50
|
+
let dvEnabled = false;
|
|
51
|
+
|
|
52
|
+
for (const rawLine of blockLines) {
|
|
53
|
+
const clean = rawLine.replace(/\s*#.*$/, '').replace(/\s+$/, '');
|
|
54
|
+
if (!clean.trim()) continue;
|
|
55
|
+
|
|
56
|
+
const kvMatch = clean.match(/^\s+([a-zA-Z_-]+):\s*(.*)/);
|
|
57
|
+
if (!kvMatch) continue;
|
|
58
|
+
|
|
59
|
+
const k = kvMatch[1];
|
|
60
|
+
let v = kvMatch[2].trim();
|
|
61
|
+
if (v.startsWith('"') && v.endsWith('"') && v.length >= 2) v = v.slice(1, -1);
|
|
62
|
+
else if (v.startsWith("'") && v.endsWith("'") && v.length >= 2) v = v.slice(1, -1);
|
|
63
|
+
|
|
64
|
+
switch (k) {
|
|
65
|
+
case 'enabled':
|
|
66
|
+
// Only literal "true" (case-insensitive) flips enabled; any other value → false
|
|
67
|
+
dvEnabled = v.toLowerCase() === 'true';
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
enabled: dvEnabled,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dispatcher-autonomy-capture.mjs — One-time per-repo capture of the
|
|
3
|
+
* `dispatcher-autonomy:` preference (Epic #673 / issue #681).
|
|
4
|
+
*
|
|
5
|
+
* The committed `dispatcher-autonomy:` block is asked EXACTLY ONCE per repo at
|
|
6
|
+
* two triggers:
|
|
7
|
+
* (a) bootstrap — new project, block always absent → always asks.
|
|
8
|
+
* (b) migration — first session-start after this feature ships, when the
|
|
9
|
+
* committed block is still absent.
|
|
10
|
+
*
|
|
11
|
+
* The guard is the RAW PRESENCE of the committed block, not the RESOLVED value:
|
|
12
|
+
* `_parseDispatcherAutonomy()` returns `autonomy: 'off'` for BOTH "block absent"
|
|
13
|
+
* AND "block present with autonomy: off" — so the resolved value cannot
|
|
14
|
+
* distinguish first-run from a deliberate `off`. This module therefore detects
|
|
15
|
+
* presence via the shared bold-tolerant `hasBlockHeader()` helper
|
|
16
|
+
* (block-header.mjs) — the same matcher the parser now uses (#830).
|
|
17
|
+
* Block presence IS the never-re-ask marker — there is no separate state file.
|
|
18
|
+
*
|
|
19
|
+
* The committed instruction file is `CLAUDE.md` on Claude Code / Cursor IDE and
|
|
20
|
+
* `AGENTS.md` on Codex CLI — transparent aliases (see
|
|
21
|
+
* `skills/_shared/instruction-file-resolution.md`); every reference to `CLAUDE.md`
|
|
22
|
+
* below resolves to `AGENTS.md` on those platforms.
|
|
23
|
+
*
|
|
24
|
+
* The writer writes ONLY the committed default (autonomy enum + confidence-floor).
|
|
25
|
+
* Host-local overrides (env `SO_DISPATCHER_AUTONOMY`, owner.yaml
|
|
26
|
+
* `dispatcher.autonomy`) NEVER land in CLAUDE.md / AGENTS.md — they stay host-local
|
|
27
|
+
* so a machine may differ from the committed default. See
|
|
28
|
+
* `scripts/lib/config/dispatcher-autonomy.mjs` `resolveDispatcherAutonomy()`.
|
|
29
|
+
*
|
|
30
|
+
* Pure, no-throw, zero-coupling helpers. The coordinator drives the
|
|
31
|
+
* AskUserQuestion call; this module only provides the question definition,
|
|
32
|
+
* the presence guard, the block renderer, and the idempotent writer.
|
|
33
|
+
*
|
|
34
|
+
* Consumers:
|
|
35
|
+
* - `skills/bootstrap/SKILL.md` (Phase 3.5.1 — new-project capture)
|
|
36
|
+
* - `skills/session-start/SKILL.md` (Phase 1.1 — migration capture)
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
40
|
+
|
|
41
|
+
import { hasBlockHeader } from './block-header.mjs';
|
|
42
|
+
|
|
43
|
+
const ALLOWED_AUTONOMY = ['off', 'advisory', 'autonomous-gated'];
|
|
44
|
+
const DEFAULT_CONFIDENCE_FLOOR = 0.5;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Coordinator-facing AUQ question definition for the one-time capture.
|
|
48
|
+
* Mirrors the shape of `getInterviewQuestions()` entries in owner-interview.mjs:
|
|
49
|
+
* { question, header, options: [{ label, description }], multiSelect }
|
|
50
|
+
*
|
|
51
|
+
* Option labels match the ALLOWED_AUTONOMY enum so the selected label maps
|
|
52
|
+
* directly to the `autonomy` value passed to writeDispatcherAutonomyBlock().
|
|
53
|
+
* Option 1 (`off`) is the recommended, fail-closed default.
|
|
54
|
+
*
|
|
55
|
+
* @returns {{ question: string, header: string, options: Array<{ label: string, description: string }>, multiSelect: boolean }}
|
|
56
|
+
*/
|
|
57
|
+
export function getDispatcherAutonomyQuestion() {
|
|
58
|
+
return {
|
|
59
|
+
question:
|
|
60
|
+
'Cross-repo dispatcher autonomy: how should this repo participate when the free-repo dispatcher routes work to it?',
|
|
61
|
+
header: 'Dispatcher Autonomy (one-time)',
|
|
62
|
+
options: [
|
|
63
|
+
{
|
|
64
|
+
label: 'off',
|
|
65
|
+
description:
|
|
66
|
+
'(Recommended) Fail-closed. The dispatcher never routes work to this repo automatically. No behaviour change.',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: 'advisory',
|
|
70
|
+
description:
|
|
71
|
+
'The dispatcher surfaces ranked free-repo candidates for operator review — no automated dispatch.',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
label: 'autonomous-gated',
|
|
75
|
+
description:
|
|
76
|
+
'A deterministic confidence gate is checked first; only dispatches that clear the confidence-floor route automatically.',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
multiSelect: false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Detect whether the committed `dispatcher-autonomy:` block is present in the
|
|
85
|
+
* given CLAUDE.md / AGENTS.md content. This is the one-time-capture guard:
|
|
86
|
+
* absent → ask, present → never re-ask.
|
|
87
|
+
*
|
|
88
|
+
* SOURCE-OF-TRUTH ALIGNMENT: this guard mirrors the block-header detection of
|
|
89
|
+
* `_parseDispatcherAutonomy()` in dispatcher-autonomy.mjs EXACTLY — both now call
|
|
90
|
+
* the shared `matchBlockHeader()` matcher (block-header.mjs). `hasBlockHeader()`
|
|
91
|
+
* splits on `/\r?\n/` and tests each line, so `\r` and trailing whitespace are
|
|
92
|
+
* eaten by the `\s*$` tail and the identical set of inputs matches.
|
|
93
|
+
* Verified-equivalent edge cases:
|
|
94
|
+
* - CRLF line endings (`dispatcher-autonomy:\r\n`) → PRESENT (`\s*$` eats the `\r`).
|
|
95
|
+
* - Trailing spaces / tabs on the header line → PRESENT (`\s*$`).
|
|
96
|
+
* - Header glued to a leading BOM (a U+FEFF byte-order-mark immediately
|
|
97
|
+
* preceding `dispatcher-autonomy:`) → ABSENT, because `^...` requires column-0
|
|
98
|
+
* and the BOM occupies it — IDENTICAL to the parser, which also rejects it.
|
|
99
|
+
* (A BOM at file START with the header on a
|
|
100
|
+
* LATER line is unaffected; the block is always appended to the end of an
|
|
101
|
+
* existing file, so the BOM never touches the header in practice.)
|
|
102
|
+
* - Header inside a ``` fenced code block → PRESENT. This is DELIBERATE: the
|
|
103
|
+
* parser does not track fences and would treat such a header as a real block,
|
|
104
|
+
* so the guard must agree. The never-re-ask marker is "the parser would see a
|
|
105
|
+
* block", not "a human would call it config". If the parser is ever taught to
|
|
106
|
+
* skip fences, update this guard in lock-step.
|
|
107
|
+
* - Header present but BODY malformed (garbage non-yaml lines) → PRESENT. A
|
|
108
|
+
* malformed block is the operator's to fix, never a re-prompt trigger — the
|
|
109
|
+
* parser tolerantly falls back to defaults, and capture must not re-ask.
|
|
110
|
+
*
|
|
111
|
+
* No-throw: null / non-string / empty → false.
|
|
112
|
+
*
|
|
113
|
+
* @param {unknown} claudeMdContent — full CLAUDE.md / AGENTS.md file contents
|
|
114
|
+
* @returns {boolean}
|
|
115
|
+
*/
|
|
116
|
+
export function isDispatcherAutonomyBlockPresent(claudeMdContent) {
|
|
117
|
+
if (typeof claudeMdContent !== 'string' || claudeMdContent.length === 0) return false;
|
|
118
|
+
return hasBlockHeader(claudeMdContent, 'dispatcher-autonomy');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Coerce a candidate autonomy value to a valid lowercase enum, falling back to
|
|
123
|
+
* 'off' when unset/empty/whitespace/invalid (fail-closed). Mirrors the
|
|
124
|
+
* fail-closed posture of resolveDispatcherAutonomy().
|
|
125
|
+
*
|
|
126
|
+
* @param {unknown} value
|
|
127
|
+
* @returns {string} one of ALLOWED_AUTONOMY
|
|
128
|
+
*/
|
|
129
|
+
function coerceAutonomy(value) {
|
|
130
|
+
if (value === null || value === undefined) return 'off';
|
|
131
|
+
const normalized = String(value).toLowerCase().trim();
|
|
132
|
+
return ALLOWED_AUTONOMY.includes(normalized) ? normalized : 'off';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Clamp a candidate confidence-floor to the closed interval [0.0, 1.0],
|
|
137
|
+
* falling back to the default when unset/non-finite.
|
|
138
|
+
*
|
|
139
|
+
* @param {unknown} value
|
|
140
|
+
* @returns {number} float in [0.0, 1.0]
|
|
141
|
+
*/
|
|
142
|
+
function clampConfidenceFloor(value) {
|
|
143
|
+
if (value === null || value === undefined || value === '') return DEFAULT_CONFIDENCE_FLOOR;
|
|
144
|
+
const f = Number(value);
|
|
145
|
+
if (!Number.isFinite(f)) return DEFAULT_CONFIDENCE_FLOOR;
|
|
146
|
+
if (f < 0) return 0;
|
|
147
|
+
if (f > 1) return 1;
|
|
148
|
+
return f;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Render the canonical standalone `## Dispatcher Autonomy` H2 block + fenced
|
|
153
|
+
* yaml. The standalone-H2 placement is MANDATORY: it keeps
|
|
154
|
+
* claude-md-drift-check Check-6 (Session-Config-parity) green. Promoting the
|
|
155
|
+
* block into `## Session Config` would hard-fail drift-check on every repo
|
|
156
|
+
* lacking the key.
|
|
157
|
+
*
|
|
158
|
+
* Validates `autonomy` against the enum (fall back to 'off') and clamps
|
|
159
|
+
* `confidenceFloor` to [0, 1]. Writes ONLY the committed default — never the
|
|
160
|
+
* host-local override.
|
|
161
|
+
*
|
|
162
|
+
* @param {{ autonomy?: unknown, confidenceFloor?: unknown }} [opts]
|
|
163
|
+
* @returns {string} the rendered block (leading newline, trailing newline)
|
|
164
|
+
*/
|
|
165
|
+
export function renderDispatcherAutonomyBlock({ autonomy, confidenceFloor = DEFAULT_CONFIDENCE_FLOOR } = {}) {
|
|
166
|
+
const safeAutonomy = coerceAutonomy(autonomy);
|
|
167
|
+
const safeFloor = clampConfidenceFloor(confidenceFloor);
|
|
168
|
+
|
|
169
|
+
return [
|
|
170
|
+
'',
|
|
171
|
+
'## Dispatcher Autonomy',
|
|
172
|
+
'',
|
|
173
|
+
'> **Parity-exempt section.** This H2 is intentionally placed outside the `## Session Config` block so that the `claude-md-drift-check` Check-6 parity scanner (which extracts only column-0 keys inside the `## Session Config` block) does not flag repos that have not yet adopted this feature. Issue #679 / #681.',
|
|
174
|
+
'',
|
|
175
|
+
'Opt-in configuration for the cross-repo free-repo dispatcher autonomy gate (Epic #673). The default is `off` — fail-closed. The effective `autonomy` resolves with host-local precedence `SO_DISPATCHER_AUTONOMY` env > `owner.yaml` `dispatcher.autonomy` > committed > `off` (#653 pattern).',
|
|
176
|
+
'',
|
|
177
|
+
'```yaml',
|
|
178
|
+
'dispatcher-autonomy:',
|
|
179
|
+
` autonomy: ${safeAutonomy} # off | advisory | autonomous-gated — default off (fail-closed)`,
|
|
180
|
+
` confidence-floor: ${safeFloor} # float 0.0..1.0`,
|
|
181
|
+
'```',
|
|
182
|
+
'',
|
|
183
|
+
'Read by: `scripts/lib/config/dispatcher-autonomy.mjs` (parser + resolver), `skills/dispatcher/SKILL.md` (cross-repo dispatch flow). Issue: #681.',
|
|
184
|
+
'',
|
|
185
|
+
].join('\n');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Idempotently append the committed `dispatcher-autonomy:` block to CLAUDE.md.
|
|
190
|
+
*
|
|
191
|
+
* Behaviour:
|
|
192
|
+
* - block ABSENT → append the rendered block → { written: true, path }.
|
|
193
|
+
* - block PRESENT → no-op → { written: false, reason: 'already-present' }.
|
|
194
|
+
* - IO failure → { written: false, error: <message> } (no-throw).
|
|
195
|
+
*
|
|
196
|
+
* Block presence IS the never-re-ask marker, so a second invocation after a
|
|
197
|
+
* successful write is a safe no-op. A defensive double-write guard re-checks
|
|
198
|
+
* absence against the freshly-read file content immediately before writing (the
|
|
199
|
+
* content read here is the source of truth, not a stale caller-supplied value).
|
|
200
|
+
* This guard fires for a PARTIAL/MALFORMED existing block too: if a (even
|
|
201
|
+
* garbage-bodied) `dispatcher-autonomy:` header already exists, the writer
|
|
202
|
+
* returns `already-present` and never appends a second block — a malformed block
|
|
203
|
+
* is the operator's to repair, not ours to duplicate. The no-op is therefore
|
|
204
|
+
* idempotent against concurrent sessions (parallel writers) AND against
|
|
205
|
+
* hand-edited malformed blocks.
|
|
206
|
+
*
|
|
207
|
+
* @param {{ claudeMdPath: string, autonomy?: unknown, confidenceFloor?: unknown }} opts
|
|
208
|
+
* @returns {{ written: boolean, path?: string, reason?: string, error?: string }}
|
|
209
|
+
*/
|
|
210
|
+
export function writeDispatcherAutonomyBlock({ claudeMdPath, autonomy, confidenceFloor } = {}) {
|
|
211
|
+
if (typeof claudeMdPath !== 'string' || claudeMdPath.length === 0) {
|
|
212
|
+
return { written: false, error: 'writeDispatcherAutonomyBlock requires a claudeMdPath' };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let content;
|
|
216
|
+
try {
|
|
217
|
+
content = readFileSync(claudeMdPath, 'utf8');
|
|
218
|
+
} catch (err) {
|
|
219
|
+
return { written: false, error: `read failed: ${err && err.message ? err.message : String(err)}` };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Defensive double-write guard: re-check absence against the freshly-read
|
|
223
|
+
// content before writing.
|
|
224
|
+
if (isDispatcherAutonomyBlockPresent(content)) {
|
|
225
|
+
return { written: false, reason: 'already-present' };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const block = renderDispatcherAutonomyBlock({ autonomy, confidenceFloor });
|
|
229
|
+
// Ensure exactly one separating newline between existing content and the block.
|
|
230
|
+
const separator = content.endsWith('\n') ? '' : '\n';
|
|
231
|
+
const next = `${content}${separator}${block}`;
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
writeFileSync(claudeMdPath, next, 'utf8');
|
|
235
|
+
} catch (err) {
|
|
236
|
+
return { written: false, error: `write failed: ${err && err.message ? err.message : String(err)}` };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return { written: true, path: claudeMdPath };
|
|
240
|
+
}
|