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,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase B-1 Mode-Selector heuristic v1 (issue #291, Epic #271).
|
|
3
|
+
*
|
|
4
|
+
* Thin orchestrator — delegates to submodules (W1A2 split, issue #358).
|
|
5
|
+
* Pure function: same inputs → same outputs, zero side effects, no I/O.
|
|
6
|
+
*
|
|
7
|
+
* Public API:
|
|
8
|
+
* selectMode(signals) → Recommendation
|
|
9
|
+
* computeContextPressure (re-export from context-pressure.mjs)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { isValidMode } from './recommendations-v0.mjs';
|
|
13
|
+
import { DEFAULT_MODE } from './mode-selector/constants.mjs';
|
|
14
|
+
import { computeDelta, round2, isNonExecutionMode } from './mode-selector/scoring.mjs';
|
|
15
|
+
import { buildAlternatives } from './mode-selector/alternatives.mjs';
|
|
16
|
+
import { buildPassthroughRationale } from './mode-selector/rationale.mjs';
|
|
17
|
+
import { computeContextPressure } from './mode-selector/context-pressure.mjs';
|
|
18
|
+
export { computeContextPressure };
|
|
19
|
+
|
|
20
|
+
// clamp inlined — used only in selectMode; avoids importing scoring helpers
|
|
21
|
+
// for a single expression.
|
|
22
|
+
function clamp(v, min, max) {
|
|
23
|
+
return Math.min(max, Math.max(min, v));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Deterministic mode selection for session-start.
|
|
28
|
+
*
|
|
29
|
+
* Pure function: same inputs → same outputs, zero side effects, no I/O.
|
|
30
|
+
*
|
|
31
|
+
* Heuristic v1 (issue #291) — consumes carryoverRatio, completionRate,
|
|
32
|
+
* recentSessions trend, bootstrapLock tier-alignment, and learnings hints
|
|
33
|
+
* to compute a confidence-weighted recommendation with alternatives.
|
|
34
|
+
*
|
|
35
|
+
* Decision priority:
|
|
36
|
+
* 1. SPIRAL — completionRate < 0.5
|
|
37
|
+
* 2. CARRYOVER — carryoverRatio >= 0.3
|
|
38
|
+
* 3. PASSTHROUGH-WEIGHTED — valid recommendedMode + signal scoring
|
|
39
|
+
* 4. DEFAULT — no valid mode, no spiral/carryover trigger
|
|
40
|
+
*
|
|
41
|
+
* @param {import('./mode-selector/constants.mjs').Signals|null|undefined} signals
|
|
42
|
+
* @returns {import('./mode-selector/constants.mjs').Recommendation}
|
|
43
|
+
*/
|
|
44
|
+
export function selectMode(signals) {
|
|
45
|
+
// Null-guard: treat null/undefined as empty signals object.
|
|
46
|
+
if (signals === null || signals === undefined) {
|
|
47
|
+
return {
|
|
48
|
+
mode: DEFAULT_MODE,
|
|
49
|
+
rationale: 'scaffold: null signals → default',
|
|
50
|
+
confidence: 0.0,
|
|
51
|
+
alternatives: [],
|
|
52
|
+
context_pressure: computeContextPressure({}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Defensive extraction of numeric signals.
|
|
57
|
+
const completionRate =
|
|
58
|
+
typeof signals.completionRate === 'number' && !Number.isNaN(signals.completionRate)
|
|
59
|
+
? signals.completionRate
|
|
60
|
+
: null;
|
|
61
|
+
const carryoverRatio =
|
|
62
|
+
typeof signals.carryoverRatio === 'number' && !Number.isNaN(signals.carryoverRatio)
|
|
63
|
+
? signals.carryoverRatio
|
|
64
|
+
: null;
|
|
65
|
+
|
|
66
|
+
// Branch 1 — SPIRAL: low completion rate
|
|
67
|
+
if (completionRate !== null && completionRate < 0.5) {
|
|
68
|
+
return {
|
|
69
|
+
mode: 'plan-retro',
|
|
70
|
+
rationale: 'low completion → retrospective',
|
|
71
|
+
confidence: 0.8,
|
|
72
|
+
alternatives: [
|
|
73
|
+
{ mode: 'feature', confidence: 0.3 },
|
|
74
|
+
{ mode: 'discovery', confidence: 0.25 },
|
|
75
|
+
],
|
|
76
|
+
context_pressure: computeContextPressure(signals),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Branch 2 — CARRYOVER: high carryover ratio
|
|
81
|
+
if (carryoverRatio !== null && carryoverRatio >= 0.3) {
|
|
82
|
+
const pct = Math.round(carryoverRatio * 100);
|
|
83
|
+
return {
|
|
84
|
+
mode: 'deep',
|
|
85
|
+
rationale: `carryover ${pct}% — deep clear`,
|
|
86
|
+
confidence: 0.75,
|
|
87
|
+
alternatives: [
|
|
88
|
+
{ mode: 'feature', confidence: 0.3 },
|
|
89
|
+
{ mode: 'plan-retro', confidence: 0.2 },
|
|
90
|
+
],
|
|
91
|
+
context_pressure: computeContextPressure(signals),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Branch 3 — PASSTHROUGH-WEIGHTED: valid recommendedMode from Phase A
|
|
96
|
+
const m = signals.recommendedMode;
|
|
97
|
+
if (typeof m === 'string' && isValidMode(m)) {
|
|
98
|
+
const delta = computeDelta(m, signals);
|
|
99
|
+
const confidence = round2(clamp(0.5 + delta, 0.0, 0.9));
|
|
100
|
+
const pressure = computeContextPressure(signals);
|
|
101
|
+
let rationale = buildPassthroughRationale(m, confidence, signals);
|
|
102
|
+
|
|
103
|
+
// Annotate rationale with pressure level when non-trivial (≤120 char limit).
|
|
104
|
+
if (pressure.level !== 'low') {
|
|
105
|
+
const pressureAnnotation = `; pressure:${pressure.level}(${pressure.score})`;
|
|
106
|
+
rationale = (rationale + pressureAnnotation).slice(0, 120);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const alternatives = buildAlternatives(m, signals);
|
|
110
|
+
|
|
111
|
+
// Global-max swap (#299): the primary recommendation must be the
|
|
112
|
+
// highest-confidence mode across {primary, ...alternatives}. When an
|
|
113
|
+
// alternative outranks the passthrough primary (strict greater-than to
|
|
114
|
+
// preserve passthrough preference on ties), promote it and demote the
|
|
115
|
+
// original primary into alternatives, re-sort, slice to top-3.
|
|
116
|
+
//
|
|
117
|
+
// NON-EXECUTION GUARDRAIL (#678, PRD §2 line 56 / §4 line 244): read-only
|
|
118
|
+
// precursor modes (discovery / plan-retro) must NEVER be scoring-promoted
|
|
119
|
+
// into the primary executed `mode`. They stay surfaceable as SUGGESTIONS in
|
|
120
|
+
// the alternatives[] list, but the swap candidate is the highest-confidence
|
|
121
|
+
// EXECUTABLE alternative only. This is guard (b): exclude precursors from
|
|
122
|
+
// the global-max promotion (the alternatives list itself is unchanged).
|
|
123
|
+
const promotable = alternatives.find((a) => !isNonExecutionMode(a.mode));
|
|
124
|
+
if (promotable !== undefined && promotable.confidence > confidence) {
|
|
125
|
+
const promoted = promotable;
|
|
126
|
+
const demoted = { mode: m, confidence };
|
|
127
|
+
// Remove the promoted alternative by reference (it may not be at index 0
|
|
128
|
+
// once precursors are skipped), then fold the demoted primary back in.
|
|
129
|
+
// Precursor alternatives are retained here so they remain SUGGESTIONS.
|
|
130
|
+
const reranked = [demoted, ...alternatives.filter((a) => a !== promoted)]
|
|
131
|
+
.filter((e) => e.confidence >= 0.1)
|
|
132
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
133
|
+
.slice(0, 3);
|
|
134
|
+
return {
|
|
135
|
+
mode: promoted.mode,
|
|
136
|
+
rationale: `global-max swap: ${promoted.mode} (${promoted.confidence}) outranked passthrough ${m} (${confidence})`.slice(0, 120),
|
|
137
|
+
confidence: promoted.confidence,
|
|
138
|
+
alternatives: reranked,
|
|
139
|
+
context_pressure: pressure,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
mode: m,
|
|
145
|
+
rationale,
|
|
146
|
+
confidence,
|
|
147
|
+
alternatives,
|
|
148
|
+
context_pressure: pressure,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Branch 4 — DEFAULT: no valid mode signal and no spiral/carryover trigger
|
|
153
|
+
return {
|
|
154
|
+
mode: DEFAULT_MODE,
|
|
155
|
+
rationale: 'missing/invalid recommendedMode → default',
|
|
156
|
+
confidence: 0.0,
|
|
157
|
+
alternatives: [],
|
|
158
|
+
context_pressure: computeContextPressure(signals),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/lib/multi-provider-build/providers.mjs
|
|
3
|
+
*
|
|
4
|
+
* Provider configuration for the single-source → many-provider build PoC.
|
|
5
|
+
*
|
|
6
|
+
* Scope: the THREE harnesses session-orchestrator actually targets
|
|
7
|
+
* (Claude Code primary, Codex CLI, Cursor IDE). Inspired by pbakaus/impeccable's
|
|
8
|
+
* 12-provider build (Apache-2.0) — we deliberately keep the templating mechanic
|
|
9
|
+
* and drop the 12-provider breadth (maintenance ≫ value for providers with ~0
|
|
10
|
+
* users; see the linked backlog issue's overengineering verdict).
|
|
11
|
+
*
|
|
12
|
+
* Each provider entry:
|
|
13
|
+
* {
|
|
14
|
+
* key, // canonical id used in build output paths
|
|
15
|
+
* tags, // <tag> block markers this provider keeps (rest are stripped)
|
|
16
|
+
* configDir, // on-disk harness dir
|
|
17
|
+
* placeholders: { model, configFile, commandPrefix, askInstruction }
|
|
18
|
+
* }
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** @typedef {{key:string, tags:string[], configDir:string, placeholders:{model:string, configFile:string, commandPrefix:string, askInstruction:string}}} ProviderConfig */
|
|
22
|
+
|
|
23
|
+
/** @type {Record<string, ProviderConfig>} */
|
|
24
|
+
export const PROVIDERS = {
|
|
25
|
+
'claude-code': {
|
|
26
|
+
key: 'claude-code',
|
|
27
|
+
tags: ['claude-code', 'claude'],
|
|
28
|
+
configDir: '.claude',
|
|
29
|
+
placeholders: {
|
|
30
|
+
model: 'Claude',
|
|
31
|
+
configFile: 'CLAUDE.md',
|
|
32
|
+
commandPrefix: '/',
|
|
33
|
+
askInstruction: 'STOP and call the AskUserQuestion tool to clarify.',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
codex: {
|
|
37
|
+
key: 'codex',
|
|
38
|
+
tags: ['codex'],
|
|
39
|
+
configDir: '.codex',
|
|
40
|
+
placeholders: {
|
|
41
|
+
model: 'GPT',
|
|
42
|
+
configFile: 'AGENTS.md',
|
|
43
|
+
commandPrefix: '$',
|
|
44
|
+
askInstruction: "STOP and use Codex's structured user-input tool to clarify.",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
cursor: {
|
|
48
|
+
key: 'cursor',
|
|
49
|
+
tags: ['cursor'],
|
|
50
|
+
configDir: '.cursor',
|
|
51
|
+
placeholders: {
|
|
52
|
+
model: 'the model',
|
|
53
|
+
configFile: '.cursorrules',
|
|
54
|
+
commandPrefix: '/',
|
|
55
|
+
askInstruction: 'ask the user directly to clarify before proceeding.',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** The full set of recognized provider block tags across ALL providers. */
|
|
61
|
+
export const ALL_BLOCK_TAGS = new Set(Object.values(PROVIDERS).flatMap((p) => p.tags));
|
|
62
|
+
|
|
63
|
+
/** Provider keys in a stable order. */
|
|
64
|
+
export const PROVIDER_KEYS = Object.keys(PROVIDERS);
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/lib/multi-provider-build/templating.mjs
|
|
3
|
+
*
|
|
4
|
+
* Single-source → many-provider templating (PoC). One source document with
|
|
5
|
+
* provider-conditional blocks + placeholders renders to a provider-specific
|
|
6
|
+
* artifact. This replaces the "maintain N near-identical copies by hand" model
|
|
7
|
+
* our codex-install.mjs / cursor-install.mjs currently imply.
|
|
8
|
+
*
|
|
9
|
+
* Two primitives (clean-room reimplementation of impeccable's mechanic):
|
|
10
|
+
* 1. compileProviderBlocks(content, activeTags)
|
|
11
|
+
* <codex> … </codex> blocks: keep body iff the tag is active, else drop.
|
|
12
|
+
* Unknown tags are left untouched (so real HTML/JSX isn't mangled).
|
|
13
|
+
* 2. replacePlaceholders(content, providerKey, opts)
|
|
14
|
+
* {{model}} {{config_file}} {{command_prefix}} {{ask_instruction}}
|
|
15
|
+
* {{available_commands}} substitution + optional /cmd → $cmd rewrite.
|
|
16
|
+
*
|
|
17
|
+
* renderForProvider() composes both in the correct order (blocks first, then
|
|
18
|
+
* placeholders — matching impeccable's pipeline).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { PROVIDERS, ALL_BLOCK_TAGS } from './providers.mjs';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Keep `<tag>…</tag>` blocks whose tag is in `activeTags`; strip the rest.
|
|
25
|
+
* Only tags in `knownTags` are treated as conditional blocks — any other
|
|
26
|
+
* `<foo>…</foo>` (e.g. real markup) is left exactly as-is.
|
|
27
|
+
*
|
|
28
|
+
* Tags must sit on their own line, e.g.:
|
|
29
|
+
* <codex>
|
|
30
|
+
* Codex-only text.
|
|
31
|
+
* </codex>
|
|
32
|
+
*
|
|
33
|
+
* @param {string} content
|
|
34
|
+
* @param {Iterable<string>} activeTags
|
|
35
|
+
* @param {Iterable<string>} [knownTags] — defaults to ALL_BLOCK_TAGS
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
export function compileProviderBlocks(content, activeTags, knownTags = ALL_BLOCK_TAGS) {
|
|
39
|
+
const active = new Set(activeTags);
|
|
40
|
+
const known = new Set(knownTags);
|
|
41
|
+
let compiledAny = false;
|
|
42
|
+
|
|
43
|
+
// Match a block: leading newline (or start), <tag> on its own line, body,
|
|
44
|
+
// </tag> on its own line. Non-greedy body; the backreference enforces matching.
|
|
45
|
+
const blockRe = /(^|\r?\n)[ \t]*<([a-z][a-z0-9-]*)>[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]*<\/\2>[ \t]*(?=\r?\n|$)/g;
|
|
46
|
+
|
|
47
|
+
const out = content.replace(blockRe, (match, prefix, tag, body) => {
|
|
48
|
+
if (!known.has(tag)) return match; // not a provider block — leave untouched
|
|
49
|
+
compiledAny = true;
|
|
50
|
+
return active.has(tag) ? `${prefix}${body}` : prefix;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Collapse the 3+ blank-line runs that stripping can leave behind.
|
|
54
|
+
return compiledAny ? out.replace(/(\r?\n){3,}/g, '\n\n') : out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Substitute {{placeholders}} for a provider and (optionally) rewrite command
|
|
59
|
+
* invocations from `/name` to the provider's prefix.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} content
|
|
62
|
+
* @param {string} providerKey
|
|
63
|
+
* @param {{ commandNames?: string[] }} [opts]
|
|
64
|
+
* @returns {string}
|
|
65
|
+
*/
|
|
66
|
+
export function replacePlaceholders(content, providerKey, opts = {}) {
|
|
67
|
+
const provider = PROVIDERS[providerKey];
|
|
68
|
+
if (!provider) throw new Error(`Unknown provider: ${providerKey}`);
|
|
69
|
+
const { model, configFile, commandPrefix, askInstruction } = provider.placeholders;
|
|
70
|
+
const commandNames = opts.commandNames ?? [];
|
|
71
|
+
|
|
72
|
+
const availableCommands = commandNames.map((n) => `${commandPrefix}${n}`).join(', ');
|
|
73
|
+
|
|
74
|
+
let result = content
|
|
75
|
+
.replace(/\{\{model\}\}/g, model)
|
|
76
|
+
.replace(/\{\{config_file\}\}/g, configFile)
|
|
77
|
+
.replace(/\{\{ask_instruction\}\}/g, askInstruction)
|
|
78
|
+
.replace(/\{\{available_commands\}\}/g, availableCommands)
|
|
79
|
+
.replace(/\{\{command_prefix\}\}/g, commandPrefix);
|
|
80
|
+
|
|
81
|
+
// Rewrite `/cmd` invocations to the provider prefix (e.g. Codex uses `$`).
|
|
82
|
+
// Longest names first so `/session-end` is rewritten before `/session`.
|
|
83
|
+
if (commandPrefix !== '/' && commandNames.length > 0) {
|
|
84
|
+
const sorted = [...commandNames].sort((a, b) => b.length - a.length);
|
|
85
|
+
for (const name of sorted) {
|
|
86
|
+
// `/name` where name is followed by a non-identifier char or end.
|
|
87
|
+
const re = new RegExp(`\\/(${escapeRegex(name)})(?=[^a-zA-Z0-9_-]|$)`, 'g');
|
|
88
|
+
// Use a replacement FUNCTION, not a string — a `$` commandPrefix would
|
|
89
|
+
// otherwise collide with String.replace's `$$`/`$1` escape syntax.
|
|
90
|
+
result = result.replace(re, (_match, g1) => `${commandPrefix}${g1}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Render a source document for a single provider: compile blocks, then
|
|
99
|
+
* substitute placeholders.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} source
|
|
102
|
+
* @param {string} providerKey
|
|
103
|
+
* @param {{ commandNames?: string[] }} [opts]
|
|
104
|
+
* @returns {string}
|
|
105
|
+
*/
|
|
106
|
+
export function renderForProvider(source, providerKey, opts = {}) {
|
|
107
|
+
const provider = PROVIDERS[providerKey];
|
|
108
|
+
if (!provider) throw new Error(`Unknown provider: ${providerKey}`);
|
|
109
|
+
const compiled = compileProviderBlocks(source, provider.tags);
|
|
110
|
+
return replacePlaceholders(compiled, providerKey, opts);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Render a source for every configured provider.
|
|
115
|
+
* @param {string} source
|
|
116
|
+
* @param {{ commandNames?: string[] }} [opts]
|
|
117
|
+
* @returns {Record<string,string>} keyed by provider key
|
|
118
|
+
*/
|
|
119
|
+
export function renderAll(source, opts = {}) {
|
|
120
|
+
const out = {};
|
|
121
|
+
for (const key of Object.keys(PROVIDERS)) {
|
|
122
|
+
out[key] = renderForProvider(source, key, opts);
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** @param {string} s */
|
|
128
|
+
function escapeRegex(s) {
|
|
129
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
130
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* named-baseline-resolver.mjs — Per-context plan-baseline resolution (Issue #819).
|
|
3
|
+
*
|
|
4
|
+
* Cousin of `named-vault-resolver.mjs`. Where the vault resolver matches a repo
|
|
5
|
+
* to a named vault by its git-remote org/repo slug, this resolver matches the
|
|
6
|
+
* current working directory to a named baseline by a local FILESYSTEM directory
|
|
7
|
+
* prefix. Each named baseline is declared in the host-local `owner.yaml` under an
|
|
8
|
+
* optional `baselines:` list. When `baselines:` is absent, every export degrades
|
|
9
|
+
* gracefully to a null-fallback — absent-tolerant, never throws.
|
|
10
|
+
*
|
|
11
|
+
* ── Deliberate divergences from named-vault-resolver ─────────────────────────
|
|
12
|
+
*
|
|
13
|
+
* - Match key is `path-prefix` (a local directory tree like `~/Projects/private-world`),
|
|
14
|
+
* NOT `org-prefix` (which matches git-remote slugs). Baseline selection is by
|
|
15
|
+
* directory tree, so no git calls are needed — this module is pure/synchronous.
|
|
16
|
+
* - Entry field is `path` (a single directory), replacing vaults' `root` + `suffix`.
|
|
17
|
+
* - `match` is REQUIRED: a baseline with no `path-prefix` can never be selected, so
|
|
18
|
+
* it is dropped-and-WARNed rather than kept (vaults keep entries without a match).
|
|
19
|
+
*
|
|
20
|
+
* ── Precedence (implemented by resolveNamedBaseline) ─────────────────────────
|
|
21
|
+
*
|
|
22
|
+
* The caller (scripts/lib/config.mjs) owns the full plan-baseline-path chain:
|
|
23
|
+
* 1. SO_BASELINE_PATH env (highest)
|
|
24
|
+
* 2. baselines: match (this module → {source:'match'})
|
|
25
|
+
* 3. owner.yaml paths.baseline-path (legacy)
|
|
26
|
+
* 4. committed Session Config value
|
|
27
|
+
*
|
|
28
|
+
* This module implements tier 2 only. As a self-contained, testable contract it
|
|
29
|
+
* also YIELDS to tier 1: when SO_BASELINE_PATH is set (non-blank), resolveNamedBaseline
|
|
30
|
+
* returns the null-fallback ({source:null}) so the caller's env tier wins.
|
|
31
|
+
*
|
|
32
|
+
* ── Exports ──────────────────────────────────────────────────────────────────
|
|
33
|
+
*
|
|
34
|
+
* parseBaselines(ownerConfig)
|
|
35
|
+
* matchBaselineForPath(absPath, baselines)
|
|
36
|
+
* resolveNamedBaseline({ cwd, ownerConfig, env })
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { normalize, sep } from 'node:path';
|
|
40
|
+
import { expandTilde } from './common.mjs';
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Internal helpers
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
/** @param {unknown} v */
|
|
47
|
+
function isPlainObject(v) {
|
|
48
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Tilde-expand + normalize a filesystem path, stripping any trailing separator
|
|
53
|
+
* (but preserving the filesystem root). Used to canonicalise BOTH the target
|
|
54
|
+
* path and each configured prefix before a directory-prefix comparison.
|
|
55
|
+
*
|
|
56
|
+
* @param {unknown} p
|
|
57
|
+
* @returns {string}
|
|
58
|
+
*/
|
|
59
|
+
function _normalizePath(p) {
|
|
60
|
+
const expanded = expandTilde(String(p ?? ''));
|
|
61
|
+
const normalized = normalize(expanded);
|
|
62
|
+
if (normalized.length > 1 && normalized.endsWith(sep)) {
|
|
63
|
+
return normalized.slice(0, -1);
|
|
64
|
+
}
|
|
65
|
+
return normalized;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// parseBaselines — PURE
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Extract and validate the `baselines:` list from a raw ownerConfig object.
|
|
74
|
+
*
|
|
75
|
+
* Drop-and-WARN on malformed entries; never throw. Returns [] when the section
|
|
76
|
+
* is absent, null, or empty — the backward-compat no-op path.
|
|
77
|
+
*
|
|
78
|
+
* Each valid entry: { name: string, path: string, match: { 'path-prefix': string } }
|
|
79
|
+
*
|
|
80
|
+
* @param {object|undefined} ownerConfig — raw parsed owner.yaml (NOT merged with defaults)
|
|
81
|
+
* @returns {Array<{name:string, path:string, match:{'path-prefix':string}}>}
|
|
82
|
+
*/
|
|
83
|
+
export function parseBaselines(ownerConfig) {
|
|
84
|
+
const raw = ownerConfig?.baselines;
|
|
85
|
+
|
|
86
|
+
// Absent or explicit null/empty → no-op
|
|
87
|
+
if (raw === undefined || raw === null) return [];
|
|
88
|
+
if (Array.isArray(raw) && raw.length === 0) return [];
|
|
89
|
+
|
|
90
|
+
if (!Array.isArray(raw)) {
|
|
91
|
+
process.stderr.write(
|
|
92
|
+
'WARN named-baseline-resolver: owner.yaml baselines: must be an array; ignoring\n',
|
|
93
|
+
);
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const result = [];
|
|
98
|
+
for (let i = 0; i < raw.length; i++) {
|
|
99
|
+
const entry = raw[i];
|
|
100
|
+
if (!isPlainObject(entry)) {
|
|
101
|
+
process.stderr.write(
|
|
102
|
+
`WARN named-baseline-resolver: owner.yaml baselines[${i}] is not an object; dropping\n`,
|
|
103
|
+
);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { name, path: entryPath, match } = entry;
|
|
108
|
+
|
|
109
|
+
if (typeof name !== 'string' || name.trim() === '') {
|
|
110
|
+
process.stderr.write(
|
|
111
|
+
`WARN named-baseline-resolver: owner.yaml baselines[${i}].name must be a non-empty string; dropping entry\n`,
|
|
112
|
+
);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (typeof entryPath !== 'string' || entryPath.trim() === '') {
|
|
116
|
+
process.stderr.write(
|
|
117
|
+
`WARN named-baseline-resolver: owner.yaml baselines[${i}].path must be a non-empty string; dropping entry\n`,
|
|
118
|
+
);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// match is REQUIRED (unlike vaults) — a baseline with no path-prefix can
|
|
123
|
+
// never be selected, so it is dropped rather than kept.
|
|
124
|
+
if (!isPlainObject(match)) {
|
|
125
|
+
process.stderr.write(
|
|
126
|
+
`WARN named-baseline-resolver: owner.yaml baselines[${i}].match must be an object; dropping entry\n`,
|
|
127
|
+
);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const pathPrefix = match['path-prefix'];
|
|
131
|
+
if (typeof pathPrefix !== 'string' || pathPrefix.trim() === '') {
|
|
132
|
+
process.stderr.write(
|
|
133
|
+
`WARN named-baseline-resolver: owner.yaml baselines[${i}].match.path-prefix must be a non-empty string; dropping entry\n`,
|
|
134
|
+
);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
result.push({
|
|
139
|
+
name: name.trim(),
|
|
140
|
+
path: entryPath.trim(),
|
|
141
|
+
match: { 'path-prefix': pathPrefix.trim() },
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// matchBaselineForPath — PURE
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Find the first named baseline whose `match.path-prefix` is a directory-prefix
|
|
154
|
+
* of the given absolute path. Both the path and each prefix are tilde-expanded
|
|
155
|
+
* and path-normalized before comparison.
|
|
156
|
+
*
|
|
157
|
+
* A prefix matches when the target path IS the prefix, or starts with
|
|
158
|
+
* `prefix + path.sep` (so `/a/b` matches `/a/b` and `/a/b/c`, but NOT `/a/bc`).
|
|
159
|
+
*
|
|
160
|
+
* First-match-wins. On an ambiguous multi-match, emits a WARN (never throws) and
|
|
161
|
+
* returns the first match.
|
|
162
|
+
*
|
|
163
|
+
* @param {string} absPath — the working directory to classify
|
|
164
|
+
* @param {Array<{name:string, path:string, match:{'path-prefix':string}}>} baselines
|
|
165
|
+
* @returns {{name:string, path:string, match:{'path-prefix':string}}|null}
|
|
166
|
+
*/
|
|
167
|
+
export function matchBaselineForPath(absPath, baselines) {
|
|
168
|
+
if (!absPath || !Array.isArray(baselines) || baselines.length === 0) return null;
|
|
169
|
+
|
|
170
|
+
const target = _normalizePath(absPath);
|
|
171
|
+
const matched = [];
|
|
172
|
+
|
|
173
|
+
for (const b of baselines) {
|
|
174
|
+
const prefixRaw = b?.match?.['path-prefix'];
|
|
175
|
+
if (typeof prefixRaw !== 'string' || prefixRaw.trim() === '') continue;
|
|
176
|
+
const prefix = _normalizePath(prefixRaw);
|
|
177
|
+
if (target === prefix || target.startsWith(`${prefix}${sep}`)) {
|
|
178
|
+
matched.push(b);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (matched.length > 1) {
|
|
183
|
+
process.stderr.write(
|
|
184
|
+
`WARN named-baseline-resolver: multiple baselines match path "${absPath}": ${matched
|
|
185
|
+
.map((b) => b.name)
|
|
186
|
+
.join(', ')}; using first match "${matched[0].name}"\n`,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return matched.length > 0 ? matched[0] : null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// resolveNamedBaseline — main resolution entry point (tier 2)
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Resolve the `baselines:` match tier for a given cwd.
|
|
199
|
+
*
|
|
200
|
+
* Returns:
|
|
201
|
+
* { path, name, source:'match' } when a baseline directory-prefix matches cwd.
|
|
202
|
+
* { path:null, name:null, source:null } (the null-fallback) otherwise — including
|
|
203
|
+
* when SO_BASELINE_PATH env is set (this tier yields to the higher env tier the
|
|
204
|
+
* caller owns), when no `baselines:` are configured, or when nothing matches.
|
|
205
|
+
*
|
|
206
|
+
* Pure + synchronous — no git calls, no disk reads. Never throws.
|
|
207
|
+
*
|
|
208
|
+
* @param {{
|
|
209
|
+
* cwd?: string,
|
|
210
|
+
* ownerConfig?: object,
|
|
211
|
+
* env?: Record<string, string|undefined>,
|
|
212
|
+
* }} [opts]
|
|
213
|
+
* @returns {{ path: string|null, name: string|null, source: 'match'|null }}
|
|
214
|
+
*/
|
|
215
|
+
export function resolveNamedBaseline({
|
|
216
|
+
cwd = process.cwd(),
|
|
217
|
+
ownerConfig,
|
|
218
|
+
env = process.env,
|
|
219
|
+
} = {}) {
|
|
220
|
+
// Yield to the higher-precedence env tier the caller owns (SO_BASELINE_PATH).
|
|
221
|
+
const envVal = env?.SO_BASELINE_PATH;
|
|
222
|
+
if (typeof envVal === 'string' && envVal.trim() !== '') {
|
|
223
|
+
return { path: null, name: null, source: null };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const baselines = parseBaselines(ownerConfig);
|
|
227
|
+
const matched = baselines.length > 0 ? matchBaselineForPath(cwd, baselines) : null;
|
|
228
|
+
if (matched !== null) {
|
|
229
|
+
return { path: matched.path, name: matched.name, source: 'match' };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return { path: null, name: null, source: null };
|
|
233
|
+
}
|