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,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* autopilot-effectiveness.mjs — /evolve type 8 analyzer (issue #298).
|
|
3
|
+
*
|
|
4
|
+
* Type 8: autopilot-effectiveness — compares manual vs autopilot effectiveness
|
|
5
|
+
* per session mode (housekeeping / feature / deep). Surfaces a learning per
|
|
6
|
+
* mode once enough paired runs accumulate to make the comparison statistically
|
|
7
|
+
* useful.
|
|
8
|
+
*
|
|
9
|
+
* DATA-GATING (intentional): the analyzer returns [] until at least
|
|
10
|
+
* `minPairedRuns` (default 20) sessions of EACH variant (manual + autopilot)
|
|
11
|
+
* exist for a given mode. This keeps the skeleton inert through the early
|
|
12
|
+
* autopilot rollout while wiring lives end-to-end. Once data accumulates the
|
|
13
|
+
* function activates without any further code change.
|
|
14
|
+
*
|
|
15
|
+
* Manual vs. autopilot is determined by the presence of an `autopilot_run_id`
|
|
16
|
+
* field on the session record (written by `scripts/autopilot.mjs` for every
|
|
17
|
+
* session it spawns). Sessions without that field are treated as manual.
|
|
18
|
+
*
|
|
19
|
+
* Existing analyzer types (1-7) for reference: fragile-file, effective-sizing,
|
|
20
|
+
* recurring-issue, scope-guidance, deviation-pattern, stagnation-class-frequency,
|
|
21
|
+
* hardware-pattern. This module adds type 8 to the family.
|
|
22
|
+
*
|
|
23
|
+
* Contract: pure, no-throw, ES module, zero external deps. Mirrors the style of
|
|
24
|
+
* `scripts/lib/hardware-pattern-detector.mjs` (issue #171 / C2).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { randomUUID } from 'node:crypto';
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Constants
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/** Default data-gating threshold per mode (manual AND autopilot side). */
|
|
34
|
+
export const DEFAULT_MIN_PAIRED_RUNS = 20;
|
|
35
|
+
|
|
36
|
+
/** Modes recognised by mode-selector / autopilot. */
|
|
37
|
+
export const KNOWN_MODES = Object.freeze(['housekeeping', 'feature', 'deep']);
|
|
38
|
+
|
|
39
|
+
/** Initial confidence — lifecycle (decay/boost) handled by evolve infrastructure. */
|
|
40
|
+
const INITIAL_CONFIDENCE = 0.5;
|
|
41
|
+
|
|
42
|
+
/** Learning lifetime: 90 days from emit. */
|
|
43
|
+
const EXPIRY_DAYS = 90;
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Defensive helpers
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
/** Safe number coercion. Returns null for non-finite inputs. */
|
|
50
|
+
function num(v) {
|
|
51
|
+
if (typeof v !== 'number' || !Number.isFinite(v)) return null;
|
|
52
|
+
return v;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Round to 3 decimals for stable evidence shape (no float noise). */
|
|
56
|
+
function round3(v) {
|
|
57
|
+
if (v === null || v === undefined) return null;
|
|
58
|
+
return Math.round(v * 1000) / 1000;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** ISO-8601 string for `now + N days`. */
|
|
62
|
+
function isoPlusDays(nowIso, days) {
|
|
63
|
+
try {
|
|
64
|
+
const t = new Date(nowIso).getTime();
|
|
65
|
+
if (!Number.isFinite(t)) return new Date(Date.now() + days * 86400e3).toISOString();
|
|
66
|
+
return new Date(t + days * 86400e3).toISOString();
|
|
67
|
+
} catch {
|
|
68
|
+
return new Date(Date.now() + days * 86400e3).toISOString();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Extract a completion ratio from a session record. Sessions encode this in a
|
|
74
|
+
* few historically-evolved shapes; we tolerate all of them and fall back to
|
|
75
|
+
* `null` (excluded from the average) when nothing usable is present.
|
|
76
|
+
*
|
|
77
|
+
* @param {object} s
|
|
78
|
+
* @returns {number|null} ratio in [0, 1], or null when unknown
|
|
79
|
+
*/
|
|
80
|
+
function completionOf(s) {
|
|
81
|
+
if (!s || typeof s !== 'object') return null;
|
|
82
|
+
// Direct ratio fields
|
|
83
|
+
const direct = num(s.completion_rate ?? s.completion_ratio);
|
|
84
|
+
if (direct !== null && direct >= 0 && direct <= 1) return direct;
|
|
85
|
+
// Planned vs completed counts
|
|
86
|
+
const planned = num(s.planned_count ?? s.tasks_planned);
|
|
87
|
+
const completed = num(s.completed_count ?? s.tasks_completed);
|
|
88
|
+
if (planned !== null && completed !== null && planned > 0) {
|
|
89
|
+
return Math.max(0, Math.min(1, completed / planned));
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Extract a carryover ratio from a session record. Carryover = work not closed
|
|
96
|
+
* within the session that flowed to a follow-up. Same tolerance pattern as
|
|
97
|
+
* `completionOf`.
|
|
98
|
+
*
|
|
99
|
+
* @param {object} s
|
|
100
|
+
* @returns {number|null} ratio in [0, 1], or null when unknown
|
|
101
|
+
*/
|
|
102
|
+
function carryoverOf(s) {
|
|
103
|
+
if (!s || typeof s !== 'object') return null;
|
|
104
|
+
const direct = num(s.carryover_ratio ?? s.carryover_rate);
|
|
105
|
+
if (direct !== null && direct >= 0 && direct <= 1) return direct;
|
|
106
|
+
const planned = num(s.planned_count ?? s.tasks_planned);
|
|
107
|
+
const carried = num(s.carryover_count ?? s.tasks_carried_over);
|
|
108
|
+
if (planned !== null && carried !== null && planned > 0) {
|
|
109
|
+
return Math.max(0, Math.min(1, carried / planned));
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Mean of an array of numbers, ignoring null/undefined. Returns null when empty. */
|
|
115
|
+
function mean(values) {
|
|
116
|
+
const xs = values.filter((v) => typeof v === 'number' && Number.isFinite(v));
|
|
117
|
+
if (xs.length === 0) return null;
|
|
118
|
+
return xs.reduce((a, b) => a + b, 0) / xs.length;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Grouping
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Group sessions by mode and split into manual vs. autopilot variants.
|
|
127
|
+
* A session is "autopilot" when it carries an `autopilot_run_id` field (added
|
|
128
|
+
* by scripts/autopilot.mjs for every spawned wave), "manual" otherwise.
|
|
129
|
+
*
|
|
130
|
+
* The `autopilotRuns` array is accepted for symmetry with the public `analyze`
|
|
131
|
+
* signature and to allow future per-run filtering (e.g., only count sessions
|
|
132
|
+
* whose autopilot_run_id matches a known run). For the skeleton it is used
|
|
133
|
+
* only as a non-emptiness signal.
|
|
134
|
+
*
|
|
135
|
+
* @param {Array} autopilotRuns
|
|
136
|
+
* @param {Array} sessions
|
|
137
|
+
* @returns {Map<string, {n_manual:number, n_autopilot:number,
|
|
138
|
+
* completion_rate_manual:number|null, completion_rate_autopilot:number|null,
|
|
139
|
+
* carryover_ratio_manual:number|null, carryover_ratio_autopilot:number|null}>}
|
|
140
|
+
*/
|
|
141
|
+
export function groupByMode(autopilotRuns, sessions) {
|
|
142
|
+
const out = new Map();
|
|
143
|
+
if (!Array.isArray(sessions) || sessions.length === 0) return out;
|
|
144
|
+
|
|
145
|
+
// Optional: known autopilot_run_id set for stricter pairing. Empty set means
|
|
146
|
+
// accept any session with a non-empty autopilot_run_id field.
|
|
147
|
+
const knownRunIds = new Set();
|
|
148
|
+
if (Array.isArray(autopilotRuns)) {
|
|
149
|
+
for (const r of autopilotRuns) {
|
|
150
|
+
if (r && typeof r === 'object') {
|
|
151
|
+
const id = r.autopilot_run_id ?? r.run_id ?? r.id;
|
|
152
|
+
if (typeof id === 'string' && id.length > 0) knownRunIds.add(id);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Bucket: mode → {manual: [], autopilot: []}
|
|
158
|
+
const buckets = new Map();
|
|
159
|
+
for (const s of sessions) {
|
|
160
|
+
if (!s || typeof s !== 'object') continue;
|
|
161
|
+
const mode = typeof s.mode === 'string' ? s.mode : null;
|
|
162
|
+
if (!mode) continue;
|
|
163
|
+
const apId = typeof s.autopilot_run_id === 'string' ? s.autopilot_run_id : null;
|
|
164
|
+
const isAutopilot =
|
|
165
|
+
apId !== null && apId.length > 0 && (knownRunIds.size === 0 || knownRunIds.has(apId));
|
|
166
|
+
const variant = isAutopilot ? 'autopilot' : 'manual';
|
|
167
|
+
if (!buckets.has(mode)) buckets.set(mode, { manual: [], autopilot: [] });
|
|
168
|
+
buckets.get(mode)[variant].push(s);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
for (const [mode, { manual, autopilot }] of buckets) {
|
|
172
|
+
out.set(mode, {
|
|
173
|
+
n_manual: manual.length,
|
|
174
|
+
n_autopilot: autopilot.length,
|
|
175
|
+
completion_rate_manual: round3(mean(manual.map(completionOf))),
|
|
176
|
+
completion_rate_autopilot: round3(mean(autopilot.map(completionOf))),
|
|
177
|
+
carryover_ratio_manual: round3(mean(manual.map(carryoverOf))),
|
|
178
|
+
carryover_ratio_autopilot: round3(mean(autopilot.map(carryoverOf))),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// Learning record builder
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Build a canonical schema_version:1 learning record for a single mode.
|
|
190
|
+
* Subject form: `<mode>-manual-vs-autopilot`. Insight is human-readable.
|
|
191
|
+
*
|
|
192
|
+
* @param {string} mode
|
|
193
|
+
* @param {object} stats — output of `groupByMode().get(mode)`
|
|
194
|
+
* @param {string} nowIso
|
|
195
|
+
* @returns {object} learning record
|
|
196
|
+
*/
|
|
197
|
+
export function buildLearning(mode, stats, nowIso) {
|
|
198
|
+
const cm = stats.completion_rate_manual;
|
|
199
|
+
const ca = stats.completion_rate_autopilot;
|
|
200
|
+
const km = stats.carryover_ratio_manual;
|
|
201
|
+
const ka = stats.carryover_ratio_autopilot;
|
|
202
|
+
|
|
203
|
+
const completionDelta = cm !== null && ca !== null ? round3(ca - cm) : null;
|
|
204
|
+
const carryoverDelta = km !== null && ka !== null ? round3(ka - km) : null;
|
|
205
|
+
|
|
206
|
+
const parts = [];
|
|
207
|
+
if (completionDelta !== null) {
|
|
208
|
+
const dir = completionDelta > 0 ? 'higher' : completionDelta < 0 ? 'lower' : 'matched';
|
|
209
|
+
parts.push(`autopilot completion ${dir} by ${Math.abs(completionDelta)} (${ca} vs ${cm})`);
|
|
210
|
+
}
|
|
211
|
+
if (carryoverDelta !== null) {
|
|
212
|
+
const dir = carryoverDelta > 0 ? 'more' : carryoverDelta < 0 ? 'less' : 'equal';
|
|
213
|
+
parts.push(`carryover ${dir} (${ka} vs ${km})`);
|
|
214
|
+
}
|
|
215
|
+
const insight =
|
|
216
|
+
parts.length > 0
|
|
217
|
+
? `Mode '${mode}': ${parts.join('; ')} across ${stats.n_manual} manual + ${stats.n_autopilot} autopilot runs.`
|
|
218
|
+
: `Mode '${mode}': insufficient effectiveness data despite ${stats.n_manual}+${stats.n_autopilot} paired runs.`;
|
|
219
|
+
|
|
220
|
+
const sourceSession = `evolve-${nowIso.slice(0, 10)}`;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
schema_version: 1,
|
|
224
|
+
id: randomUUID(),
|
|
225
|
+
type: 'autopilot-effectiveness',
|
|
226
|
+
subject: `${mode}-manual-vs-autopilot`,
|
|
227
|
+
insight,
|
|
228
|
+
evidence: {
|
|
229
|
+
mode,
|
|
230
|
+
n_manual: stats.n_manual,
|
|
231
|
+
n_autopilot: stats.n_autopilot,
|
|
232
|
+
completion_rate_manual: cm,
|
|
233
|
+
completion_rate_autopilot: ca,
|
|
234
|
+
carryover_ratio_manual: km,
|
|
235
|
+
carryover_ratio_autopilot: ka,
|
|
236
|
+
completion_delta: completionDelta,
|
|
237
|
+
carryover_delta: carryoverDelta,
|
|
238
|
+
},
|
|
239
|
+
confidence: INITIAL_CONFIDENCE,
|
|
240
|
+
source_session: sourceSession,
|
|
241
|
+
created_at: nowIso,
|
|
242
|
+
expires_at: isoPlusDays(nowIso, EXPIRY_DAYS),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// Public entry point
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Analyze paired manual + autopilot runs and emit `autopilot-effectiveness`
|
|
252
|
+
* learnings — one per qualifying mode. Returns `[]` until the data-gating
|
|
253
|
+
* threshold is met (default: 20 paired runs per mode).
|
|
254
|
+
*
|
|
255
|
+
* Never throws. Defensive against malformed entries.
|
|
256
|
+
*
|
|
257
|
+
* @param {Array} autopilotRuns — entries from .orchestrator/metrics/autopilot.jsonl
|
|
258
|
+
* @param {Array} sessions — entries from .orchestrator/metrics/sessions.jsonl
|
|
259
|
+
* @param {object} [opts]
|
|
260
|
+
* @param {number} [opts.minPairedRuns=20] — min(n_manual, n_autopilot) per mode
|
|
261
|
+
* @param {string} [opts.now] — ISO 8601 timestamp for created_at (testable)
|
|
262
|
+
* @returns {Array<object>} learning records (empty until threshold met)
|
|
263
|
+
*/
|
|
264
|
+
export function analyze(autopilotRuns, sessions, opts = {}) {
|
|
265
|
+
const minPairedRuns = Number.isFinite(opts.minPairedRuns)
|
|
266
|
+
? opts.minPairedRuns
|
|
267
|
+
: DEFAULT_MIN_PAIRED_RUNS;
|
|
268
|
+
const now = typeof opts.now === 'string' && opts.now.length > 0
|
|
269
|
+
? opts.now
|
|
270
|
+
: new Date().toISOString();
|
|
271
|
+
|
|
272
|
+
if (!Array.isArray(autopilotRuns) || autopilotRuns.length === 0) return [];
|
|
273
|
+
if (!Array.isArray(sessions) || sessions.length === 0) return [];
|
|
274
|
+
|
|
275
|
+
let grouped;
|
|
276
|
+
try {
|
|
277
|
+
grouped = groupByMode(autopilotRuns, sessions);
|
|
278
|
+
} catch {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
if (!grouped || grouped.size === 0) return [];
|
|
282
|
+
|
|
283
|
+
const learnings = [];
|
|
284
|
+
for (const [mode, stats] of grouped) {
|
|
285
|
+
if (Math.min(stats.n_manual, stats.n_autopilot) < minPairedRuns) continue;
|
|
286
|
+
try {
|
|
287
|
+
learnings.push(buildLearning(mode, stats, now));
|
|
288
|
+
} catch {
|
|
289
|
+
// Skip a single failed mode rather than collapsing the whole analysis.
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return learnings;
|
|
293
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* exclusivity-matrix.mjs — Classify orchestrator skill modes into exclusivity classes.
|
|
3
|
+
*
|
|
4
|
+
* Pure ESM, no imports required. Stateless pure functions only.
|
|
5
|
+
*
|
|
6
|
+
* Exclusivity classes control whether a session mode may run concurrently with
|
|
7
|
+
* other active sessions in the same working directory:
|
|
8
|
+
* - "exclusive" — must hold the session lock alone (e.g., housekeeping)
|
|
9
|
+
* - "parallel-ok" — may coexist with other parallel-ok sessions
|
|
10
|
+
* - "always-ok" — lightweight; always allowed regardless of active sessions
|
|
11
|
+
*
|
|
12
|
+
* Used by:
|
|
13
|
+
* - parallel-aware-preamble (W3-P3, #574) — inject concurrency guardrail text
|
|
14
|
+
* - acquire() integration (P1.2, #570) — enforce lock semantics at session-start
|
|
15
|
+
*
|
|
16
|
+
* Stable contract: the list of modes per class is documented in
|
|
17
|
+
* `skills/_shared/state-ownership.md` (PRD §3.A line 195).
|
|
18
|
+
* Do NOT add or move modes without coordinating with that document.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// EXCLUSIVITY_MATRIX
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Static, frozen mapping from exclusivity class → mode names.
|
|
27
|
+
*
|
|
28
|
+
* All inner arrays are also frozen to prevent mutation. Any attempt to push,
|
|
29
|
+
* splice, or reassign will throw a TypeError in strict mode (and silently fail
|
|
30
|
+
* in sloppy mode after Object.freeze — prefer strict mode callers).
|
|
31
|
+
*
|
|
32
|
+
* @type {{ exclusive: readonly string[], "parallel-ok": readonly string[], "always-ok": readonly string[] }}
|
|
33
|
+
*/
|
|
34
|
+
export const EXCLUSIVITY_MATRIX = Object.freeze({
|
|
35
|
+
exclusive: Object.freeze(['bootstrap', 'housekeeping', 'memory-cleanup']),
|
|
36
|
+
'parallel-ok': Object.freeze(['deep', 'feature']),
|
|
37
|
+
'always-ok': Object.freeze(['discovery', 'evolve', 'plan', 'repo-audit', 'portfolio']),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// classifyMode
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Classify a skill mode name into its exclusivity class.
|
|
46
|
+
*
|
|
47
|
+
* Lookup is case-sensitive; all documented modes are lowercase. Whitespace is
|
|
48
|
+
* trimmed defensively before the lookup so callers do not need to pre-strip.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} modeName - The skill mode name to classify (e.g. "deep").
|
|
51
|
+
* @returns {"exclusive" | "parallel-ok" | "always-ok"} The exclusivity class.
|
|
52
|
+
* @throws {Error} If `modeName` (after trimming) is not a recognised mode.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* classifyMode('deep') // → "parallel-ok"
|
|
56
|
+
* classifyMode('housekeeping') // → "exclusive"
|
|
57
|
+
* classifyMode('discovery') // → "always-ok"
|
|
58
|
+
*/
|
|
59
|
+
export function classifyMode(modeName) {
|
|
60
|
+
const name = String(modeName).trim();
|
|
61
|
+
|
|
62
|
+
for (const [cls, modes] of Object.entries(EXCLUSIVITY_MATRIX)) {
|
|
63
|
+
if (modes.includes(name)) return cls;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const known = Object.values(EXCLUSIVITY_MATRIX).flat().join(', ');
|
|
67
|
+
throw new Error(`classifyMode: unknown mode "${name}". Known modes: ${known}`);
|
|
68
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/lib/fetch-baseline.mjs
|
|
4
|
+
*
|
|
5
|
+
* Fetch a file from a GitLab project's repository raw endpoint.
|
|
6
|
+
* Node.js port of scripts/lib/fetch-baseline.sh (issue #218).
|
|
7
|
+
*
|
|
8
|
+
* Used by the bootstrap skill to pull canonical baseline files (rules, agents,
|
|
9
|
+
* CLAUDE.md / AGENTS.md snippets) on demand instead of relying on local copies
|
|
10
|
+
* that drift.
|
|
11
|
+
*
|
|
12
|
+
* Required env vars:
|
|
13
|
+
* GITLAB_TOKEN — personal access token with read_api or read_repository scope
|
|
14
|
+
*
|
|
15
|
+
* Optional env vars:
|
|
16
|
+
* GITLAB_HOST — GitLab host (required; no private domain fallback)
|
|
17
|
+
* BASELINE_REF — default: main
|
|
18
|
+
* FETCH_TIMEOUT_MS — default: 10000 (ms; maps to FETCH_TIMEOUT seconds in .sh)
|
|
19
|
+
* BASELINE_CACHE_DIR — override cache directory
|
|
20
|
+
*
|
|
21
|
+
* Named export (module usage):
|
|
22
|
+
* import { fetchBaselineFile } from './fetch-baseline.mjs';
|
|
23
|
+
* const result = await fetchBaselineFile({ baselineRef, filePath, token, projectId });
|
|
24
|
+
*
|
|
25
|
+
* CLI usage:
|
|
26
|
+
* node scripts/lib/fetch-baseline.mjs <project_id> <file_path> [ref]
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { promises as fs } from 'node:fs';
|
|
30
|
+
import path from 'node:path';
|
|
31
|
+
import { execSync } from 'node:child_process';
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Configuration defaults (mirrors the .sh defaults)
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
const DEFAULT_BASELINE_REF = 'main';
|
|
38
|
+
const DEFAULT_FETCH_TIMEOUT_MS = 10_000;
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Internal helpers
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the cache directory.
|
|
46
|
+
* Honours BASELINE_CACHE_DIR env override, then falls back to
|
|
47
|
+
* <repo-root>/.claude/.baseline-cache (same logic as .sh).
|
|
48
|
+
*
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
function _cacheDir() {
|
|
52
|
+
if (process.env.BASELINE_CACHE_DIR) {
|
|
53
|
+
return process.env.BASELINE_CACHE_DIR;
|
|
54
|
+
}
|
|
55
|
+
let root;
|
|
56
|
+
try {
|
|
57
|
+
root = execSync('git rev-parse --show-toplevel', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
58
|
+
} catch {
|
|
59
|
+
root = process.cwd();
|
|
60
|
+
}
|
|
61
|
+
return path.join(root, '.claude', '.baseline-cache');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* URL-encode a file path for the GitLab API.
|
|
66
|
+
* Encodes slashes as %2F and other special characters via encodeURIComponent.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} filePath
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
function _urlEncodePath(filePath) {
|
|
72
|
+
// encodeURIComponent handles all characters except A-Z a-z 0-9 - _ . ! ~ * ' ( )
|
|
73
|
+
// GitLab expects slashes encoded as %2F in the path segment (already the default output).
|
|
74
|
+
return encodeURIComponent(filePath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Derive a filesystem-safe cache key from (projectId, ref, filePath).
|
|
79
|
+
* Mirrors the __cache_key() function in the .sh original.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} projectId
|
|
82
|
+
* @param {string} ref
|
|
83
|
+
* @param {string} filePath
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
function _cacheKey(projectId, ref, filePath) {
|
|
87
|
+
const raw = `${projectId}-${ref}-${filePath}`;
|
|
88
|
+
return raw.replace(/[/.]/g, '_');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Public named export
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Fetch a single file from a GitLab project's repository.
|
|
97
|
+
*
|
|
98
|
+
* @param {object} opts
|
|
99
|
+
* @param {string} opts.filePath - repo-relative path (e.g. ".claude/rules/security.md")
|
|
100
|
+
* @param {string} [opts.baselineRef] - git ref (default: BASELINE_REF env or "main")
|
|
101
|
+
* @param {string} [opts.token] - GitLab personal access token (default: GITLAB_TOKEN env)
|
|
102
|
+
* @param {string|number} [opts.projectId] - GitLab project ID (default: 52)
|
|
103
|
+
* @param {string} [opts.host] - GitLab host (required; falls back to GITLAB_HOST env; no default private host)
|
|
104
|
+
* @param {number} [opts.timeoutMs] - fetch timeout in ms (default: FETCH_TIMEOUT_MS env or 10000)
|
|
105
|
+
*
|
|
106
|
+
* @returns {Promise<{
|
|
107
|
+
* ok: boolean,
|
|
108
|
+
* body?: string,
|
|
109
|
+
* status?: number,
|
|
110
|
+
* error?: string,
|
|
111
|
+
* fromCache?: boolean
|
|
112
|
+
* }>}
|
|
113
|
+
*
|
|
114
|
+
* Exit-code semantics (mirrored as result shapes):
|
|
115
|
+
* ok:true — success (network or cache fallback)
|
|
116
|
+
* ok:false, status:401/403 — auth failure — fatal
|
|
117
|
+
* ok:false, status:404 — file not found + no cache
|
|
118
|
+
* ok:false, status:0 — network/transport failure + no cache (status 0 = transport)
|
|
119
|
+
*/
|
|
120
|
+
export async function fetchBaselineFile({
|
|
121
|
+
filePath,
|
|
122
|
+
baselineRef,
|
|
123
|
+
token,
|
|
124
|
+
projectId = 52,
|
|
125
|
+
host,
|
|
126
|
+
timeoutMs,
|
|
127
|
+
} = {}) {
|
|
128
|
+
const resolvedToken = token ?? process.env.GITLAB_TOKEN;
|
|
129
|
+
const resolvedRef = baselineRef ?? process.env.BASELINE_REF ?? DEFAULT_BASELINE_REF;
|
|
130
|
+
const resolvedHost = host ?? process.env.GITLAB_HOST;
|
|
131
|
+
const resolvedTimeout = timeoutMs ?? Number(process.env.FETCH_TIMEOUT_MS ?? DEFAULT_FETCH_TIMEOUT_MS);
|
|
132
|
+
|
|
133
|
+
if (!resolvedHost) {
|
|
134
|
+
const errMsg = 'GITLAB_HOST not configured — set GITLAB_HOST env or pass opts.host';
|
|
135
|
+
process.stderr.write(`ERROR: ${errMsg}\n`);
|
|
136
|
+
return {
|
|
137
|
+
ok: false,
|
|
138
|
+
status: 0,
|
|
139
|
+
error: errMsg,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!resolvedToken) {
|
|
144
|
+
const errMsg = 'GITLAB_TOKEN not set — cannot fetch from baseline.';
|
|
145
|
+
process.stderr.write(`ERROR: ${errMsg}\n`);
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
status: 401,
|
|
149
|
+
error: errMsg,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!filePath) {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
status: 0,
|
|
157
|
+
error: 'filePath is required.',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const cacheDirectory = _cacheDir();
|
|
162
|
+
const cacheKey = _cacheKey(String(projectId), resolvedRef, filePath);
|
|
163
|
+
const cacheFile = path.join(cacheDirectory, cacheKey);
|
|
164
|
+
|
|
165
|
+
const encodedPath = _urlEncodePath(filePath);
|
|
166
|
+
const url = `https://${resolvedHost}/api/v4/projects/${projectId}/repository/files/${encodedPath}/raw?ref=${resolvedRef}`;
|
|
167
|
+
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Attempt network fetch
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
let response;
|
|
172
|
+
try {
|
|
173
|
+
const controller = new AbortController();
|
|
174
|
+
const timer = setTimeout(() => controller.abort(), resolvedTimeout);
|
|
175
|
+
try {
|
|
176
|
+
response = await fetch(url, {
|
|
177
|
+
headers: { 'PRIVATE-TOKEN': resolvedToken },
|
|
178
|
+
signal: controller.signal,
|
|
179
|
+
});
|
|
180
|
+
} finally {
|
|
181
|
+
clearTimeout(timer);
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
// Transport/network failure (DNS, timeout, connection refused, etc.)
|
|
185
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
186
|
+
|
|
187
|
+
// Try cache fallback
|
|
188
|
+
try {
|
|
189
|
+
const cached = await fs.readFile(cacheFile, 'utf8');
|
|
190
|
+
process.stderr.write(`WARNING: fetch failed (network error: ${errMsg}); using cache for ${filePath}\n`);
|
|
191
|
+
return { ok: true, body: cached, fromCache: true };
|
|
192
|
+
} catch {
|
|
193
|
+
process.stderr.write(
|
|
194
|
+
`ERROR: fetch failed and no cache for ${filePath} (project ${projectId}, ref ${resolvedRef}): ${errMsg}\n`,
|
|
195
|
+
);
|
|
196
|
+
return { ok: false, status: 0, error: errMsg };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Handle HTTP status codes (mirrors the case statement in .sh)
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
if (response.status === 200) {
|
|
205
|
+
const body = await response.text();
|
|
206
|
+
// Write to cache (best-effort — don't fail the caller if cache write fails)
|
|
207
|
+
try {
|
|
208
|
+
await fs.mkdir(cacheDirectory, { recursive: true });
|
|
209
|
+
await fs.writeFile(cacheFile, body, 'utf8');
|
|
210
|
+
} catch {
|
|
211
|
+
// ignore cache write errors
|
|
212
|
+
}
|
|
213
|
+
return { ok: true, body, status: 200 };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (response.status === 401 || response.status === 403) {
|
|
217
|
+
// Auth failure — fatal, no cache fallback (matches .sh behaviour)
|
|
218
|
+
const errMsg = `auth failed (${response.status}) fetching ${filePath} — check GITLAB_TOKEN scope`;
|
|
219
|
+
process.stderr.write(`ERROR: ${errMsg}\n`);
|
|
220
|
+
return { ok: false, status: response.status, error: errMsg };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (response.status === 404) {
|
|
224
|
+
// Try cache fallback
|
|
225
|
+
try {
|
|
226
|
+
const cached = await fs.readFile(cacheFile, 'utf8');
|
|
227
|
+
process.stderr.write(
|
|
228
|
+
`WARNING: 404 for ${filePath}; using cache (file may have been removed from baseline)\n`,
|
|
229
|
+
);
|
|
230
|
+
return { ok: true, body: cached, status: 404, fromCache: true };
|
|
231
|
+
} catch {
|
|
232
|
+
const errMsg = `file not found (${filePath} on ${resolvedRef}) and no cache`;
|
|
233
|
+
process.stderr.write(`ERROR: ${errMsg}\n`);
|
|
234
|
+
return { ok: false, status: 404, error: errMsg };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Any other non-2xx status — try cache fallback
|
|
239
|
+
try {
|
|
240
|
+
const cached = await fs.readFile(cacheFile, 'utf8');
|
|
241
|
+
process.stderr.write(`WARNING: HTTP ${response.status} for ${filePath}; using cache\n`);
|
|
242
|
+
return { ok: true, body: cached, status: response.status, fromCache: true };
|
|
243
|
+
} catch {
|
|
244
|
+
const errMsg = `HTTP ${response.status} fetching ${filePath} — no cache`;
|
|
245
|
+
process.stderr.write(`ERROR: ${errMsg}\n`);
|
|
246
|
+
return { ok: false, status: response.status, error: errMsg };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// CLI mode
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* When invoked directly: node scripts/lib/fetch-baseline.mjs <project_id> <file_path> [ref]
|
|
256
|
+
*
|
|
257
|
+
* Fetches the file and prints the body to stdout.
|
|
258
|
+
* Exit codes mirror the .sh original:
|
|
259
|
+
* 0 — success
|
|
260
|
+
* 1 — auth failure (401/403) or missing GITLAB_TOKEN
|
|
261
|
+
* 2 — file not found (404, no cache)
|
|
262
|
+
* 3 — network/transport failure, no cache; or unexpected HTTP status
|
|
263
|
+
*/
|
|
264
|
+
async function _cliMain() {
|
|
265
|
+
const args = process.argv.slice(2);
|
|
266
|
+
|
|
267
|
+
if (args.length < 2) {
|
|
268
|
+
process.stderr.write(
|
|
269
|
+
`Usage: node ${path.basename(process.argv[1])} <project_id> <file_path> [ref]\n`,
|
|
270
|
+
);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const [projectId, filePath, ref] = args;
|
|
275
|
+
|
|
276
|
+
const result = await fetchBaselineFile({
|
|
277
|
+
projectId,
|
|
278
|
+
filePath,
|
|
279
|
+
baselineRef: ref, // undefined when not supplied → uses env/default
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (result.ok) {
|
|
283
|
+
process.stdout.write(result.body ?? '');
|
|
284
|
+
process.exit(0);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Map error to exit code matching .sh
|
|
288
|
+
const status = result.status ?? 0;
|
|
289
|
+
if (status === 401 || status === 403) {
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
if (status === 404) {
|
|
293
|
+
process.exit(2);
|
|
294
|
+
}
|
|
295
|
+
// network failure or unexpected HTTP status
|
|
296
|
+
process.exit(3);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Detect CLI invocation: import.meta.url matches the argv[1] path
|
|
300
|
+
const isMain = process.argv[1] && (
|
|
301
|
+
process.argv[1] === new URL(import.meta.url).pathname ||
|
|
302
|
+
// Handle invocation without file:// prefix
|
|
303
|
+
process.argv[1].endsWith('fetch-baseline.mjs')
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
if (isMain) {
|
|
307
|
+
_cliMain().catch((err) => {
|
|
308
|
+
process.stderr.write(`FATAL: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
309
|
+
process.exit(3);
|
|
310
|
+
});
|
|
311
|
+
}
|