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,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// scripts/ci/assert-vitest-green.mjs
|
|
3
|
+
//
|
|
4
|
+
// Fail-closed verifier for a vitest JSON result file. Replaces the brittle
|
|
5
|
+
// grep-the-stdout heuristic the GitLab `test`/`coverage` jobs used to rely on.
|
|
6
|
+
//
|
|
7
|
+
// WHY THIS EXISTS (the silent-regression incident class):
|
|
8
|
+
// The old CI test job ran `timeout --preserve-status 240s npm test … || true`
|
|
9
|
+
// and then counted `✓`/`FAIL` markers in the captured stdout. Two ways that
|
|
10
|
+
// reported a RED suite as GREEN:
|
|
11
|
+
// 1. The suite took longer than the 240s cap under runner contention
|
|
12
|
+
// (observed: 257s on the Mac shell-executor). `timeout` killed vitest
|
|
13
|
+
// BEFORE it printed the bottom failure-summary block; `|| true` swallowed
|
|
14
|
+
// the kill; the grep found zero `FAIL tests/…` lines → false GREEN.
|
|
15
|
+
// 2. The verbose reporter marks failing tests with `×` (U+00D7), which the
|
|
16
|
+
// `✗` (U+2717) regex never matched.
|
|
17
|
+
// Net effect: a 12-failure suite shipped green (commit c6e34e1 / pipeline
|
|
18
|
+
// #5828). This is the same class as the documented 8-pipeline silent
|
|
19
|
+
// regression (CLAUDE.md / AGENTS.md Critical Gotchas).
|
|
20
|
+
//
|
|
21
|
+
// THE FIX:
|
|
22
|
+
// vitest's `--reporter=json --outputFile=<path>` writes a machine-readable
|
|
23
|
+
// result on completion. This script is the single authority on pass/fail.
|
|
24
|
+
// It is FAIL-CLOSED: any ambiguity (file missing, unparseable, truncated run,
|
|
25
|
+
// success!==true) exits non-zero. The only GREEN path is an explicit, complete,
|
|
26
|
+
// all-passing result.
|
|
27
|
+
//
|
|
28
|
+
// Robustness to the legacy vitest-2.1.9 tinypool exit-hang (#268): the JSON
|
|
29
|
+
// file is written at onFinished — AFTER all tests pass but BEFORE the hang —
|
|
30
|
+
// so a post-completion hang (whose non-zero exit the CI step swallows with
|
|
31
|
+
// `|| true`) still yields success:true here. A run killed mid-flight never
|
|
32
|
+
// writes a complete file, so it fails closed. Both behaviours are correct.
|
|
33
|
+
//
|
|
34
|
+
// USAGE:
|
|
35
|
+
// node scripts/ci/assert-vitest-green.mjs <result.json> [--min-tests=N] [--log=<path>]
|
|
36
|
+
// Exit 0 → suite is verifiably green. Exit 1 → fail (with a [ci] reason line).
|
|
37
|
+
//
|
|
38
|
+
// The caller MUST `rm -f` the result file before the vitest run so a stale
|
|
39
|
+
// file from a prior run on a reused shell-executor host cannot be mistaken for
|
|
40
|
+
// the current run's result.
|
|
41
|
+
//
|
|
42
|
+
// SELF-DIAGNOSING HANGS (--log):
|
|
43
|
+
// When the result JSON is missing/unreadable — the runner-contention hang case
|
|
44
|
+
// where vitest is killed before writing onFinished — an optional `--log=<path>`
|
|
45
|
+
// pointing at the captured `--reporter=default` stdout lets the verifier name
|
|
46
|
+
// the test files that were IN-FLIGHT at the kill (started with `❯ <file>` but
|
|
47
|
+
// never reached a `✓`/`×` completion marker). This turns a bare ENOENT into an
|
|
48
|
+
// actionable "these N files were running when the run died" hint. Purely
|
|
49
|
+
// additive: absent `--log` (or an unreadable/uninformative log) falls back to
|
|
50
|
+
// the existing bare message and the exit-code contract is unchanged.
|
|
51
|
+
|
|
52
|
+
import { readFileSync } from 'node:fs';
|
|
53
|
+
|
|
54
|
+
const DEFAULT_MIN_TESTS = 5000;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Verify a parsed vitest JSON result is a complete, all-passing run.
|
|
58
|
+
* Pure function — returns { ok, reason, summary } and never throws or exits.
|
|
59
|
+
* @param {unknown} result - parsed JSON (any shape; validated here)
|
|
60
|
+
* @param {{ minTests?: number }} [opts]
|
|
61
|
+
* @returns {{ ok: boolean, reason: string, summary: object }}
|
|
62
|
+
*/
|
|
63
|
+
export function verifyVitestResult(result, opts = {}) {
|
|
64
|
+
const minTests = Number.isFinite(opts.minTests) ? opts.minTests : DEFAULT_MIN_TESTS;
|
|
65
|
+
|
|
66
|
+
if (result === null || typeof result !== 'object') {
|
|
67
|
+
return { ok: false, reason: 'result is not an object', summary: {} };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const numFailedTests = result.numFailedTests ?? null;
|
|
71
|
+
const numFailedTestSuites = result.numFailedTestSuites ?? null;
|
|
72
|
+
const numPassedTests = result.numPassedTests ?? null;
|
|
73
|
+
const numTotalTests = result.numTotalTests ?? null;
|
|
74
|
+
const success = result.success;
|
|
75
|
+
|
|
76
|
+
const summary = { success, numPassedTests, numFailedTests, numFailedTestSuites, numTotalTests };
|
|
77
|
+
|
|
78
|
+
// Shape guard: the required numeric fields must all be present. A partial /
|
|
79
|
+
// truncated JSON (e.g. a half-written file) lacks them → fail closed.
|
|
80
|
+
for (const [k, v] of Object.entries({ numFailedTests, numFailedTestSuites, numPassedTests, numTotalTests })) {
|
|
81
|
+
if (typeof v !== 'number' || !Number.isFinite(v)) {
|
|
82
|
+
return { ok: false, reason: `missing/invalid field "${k}" (truncated or wrong-shape JSON)`, summary };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Failed-count guards run BEFORE the generic success flag so a real failure
|
|
87
|
+
// reports the actionable count ("12 failed test(s)") rather than the opaque
|
|
88
|
+
// "success is false". success!==true with zero counts is a separate edge.
|
|
89
|
+
if (numFailedTests > 0) {
|
|
90
|
+
return { ok: false, reason: `${numFailedTests} failed test(s)`, summary };
|
|
91
|
+
}
|
|
92
|
+
if (numFailedTestSuites > 0) {
|
|
93
|
+
return { ok: false, reason: `${numFailedTestSuites} failed test suite(s)`, summary };
|
|
94
|
+
}
|
|
95
|
+
if (success !== true) {
|
|
96
|
+
return { ok: false, reason: `success is ${JSON.stringify(success)} (expected true)`, summary };
|
|
97
|
+
}
|
|
98
|
+
// Floor guard: a suspiciously small run signals a truncated/partial/empty
|
|
99
|
+
// execution masquerading as green. The real suite is ~12.1k tests (2026-07-17).
|
|
100
|
+
if (numTotalTests < minTests) {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
reason: `only ${numTotalTests} total tests (< floor ${minTests}) — truncated or partial run`,
|
|
104
|
+
summary,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { ok: true, reason: 'all tests passed', summary };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Read + verify a vitest JSON result file. Fail-closed on any read/parse error.
|
|
113
|
+
* @param {string} path
|
|
114
|
+
* @param {{ minTests?: number }} [opts]
|
|
115
|
+
* @returns {{ ok: boolean, reason: string, summary: object }}
|
|
116
|
+
*/
|
|
117
|
+
export function assertVitestGreenFile(path, opts = {}) {
|
|
118
|
+
let raw;
|
|
119
|
+
try {
|
|
120
|
+
raw = readFileSync(path, 'utf8');
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return { ok: false, reason: `cannot read result file "${path}": ${err.message}`, summary: {} };
|
|
123
|
+
}
|
|
124
|
+
let parsed;
|
|
125
|
+
try {
|
|
126
|
+
parsed = JSON.parse(raw);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
return { ok: false, reason: `result file "${path}" is not valid JSON: ${err.message}`, summary: {} };
|
|
129
|
+
}
|
|
130
|
+
return verifyVitestResult(parsed, opts);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Parse a captured vitest `--reporter=default` log and return the test files
|
|
135
|
+
* that bear a `❯ <file>` lead glyph but never reached a per-file completion
|
|
136
|
+
* marker (`✓ <file>` / `× <file>`). The intent is to surface the "in-flight at
|
|
137
|
+
* kill" set — the files that were still running when the runner timed vitest
|
|
138
|
+
* out — so a hang dump names the likely culprit.
|
|
139
|
+
*
|
|
140
|
+
* BEST-EFFORT — DOES NOT reliably mark in-progress files in CI. This is a known
|
|
141
|
+
* limitation, not a bug:
|
|
142
|
+
* - The `❯ U+276F` glyph denotes "in-progress/queued" only on an INTERACTIVE
|
|
143
|
+
* TTY, where vitest live-updates the running-file list in place.
|
|
144
|
+
* - In non-TTY CI (`--reporter=default --no-color`, no live rewrite), `❯`
|
|
145
|
+
* instead marks FAILED files in the FINAL summary block — NOT in-progress.
|
|
146
|
+
* There is no per-file "started" line emitted mid-flight to key off.
|
|
147
|
+
* - Consequence: on a TRUE mid-flight hang (vitest killed before it writes the
|
|
148
|
+
* summary), there is often no `❯` line at all, so this hint returns [] —
|
|
149
|
+
* empty exactly when it would be most useful, and potentially misleading
|
|
150
|
+
* (matching a failed-summary file) when the run did reach the summary.
|
|
151
|
+
*
|
|
152
|
+
* A robust in-flight signal would need `--reporter=verbose` per-file START
|
|
153
|
+
* lines (which DO emit mid-flight). Wiring that in is deferred to a follow-up:
|
|
154
|
+
* it needs a captured real CI log to pin the exact start-line token format
|
|
155
|
+
* before changing the regex. THIS FUNCTION'S PARSE LOGIC IS UNCHANGED — the
|
|
156
|
+
* correction here is documentation-only.
|
|
157
|
+
*
|
|
158
|
+
* Pure function — never throws. Returns [] when the text is empty or no
|
|
159
|
+
* matching files are detectable.
|
|
160
|
+
*
|
|
161
|
+
* Marker glyphs (vitest default reporter, --no-color):
|
|
162
|
+
* ✓ U+2713 per-file PASS completion
|
|
163
|
+
* × U+00D7 per-file FAIL completion
|
|
164
|
+
* ❯ U+276F in-progress/queued on a TTY; FAILED-in-summary in non-TTY CI
|
|
165
|
+
* (see the BEST-EFFORT caveat above — the non-TTY meaning is why
|
|
166
|
+
* this hint can be empty/misleading on a real hang)
|
|
167
|
+
*
|
|
168
|
+
* @param {string} logText - raw captured reporter stdout
|
|
169
|
+
* @returns {string[]} sorted list of candidate test file paths (deduped)
|
|
170
|
+
*/
|
|
171
|
+
export function inFlightFilesFromLog(logText) {
|
|
172
|
+
if (typeof logText !== 'string' || logText.length === 0) return [];
|
|
173
|
+
|
|
174
|
+
const started = new Set();
|
|
175
|
+
const completed = new Set();
|
|
176
|
+
|
|
177
|
+
// Lead glyph, optional indentation, then the file path up to a trailing
|
|
178
|
+
// " (N tests…" annotation or end-of-line. The default reporter emits the
|
|
179
|
+
// file path as the first token after the status glyph.
|
|
180
|
+
const STARTED = /[❯>]\s+(\S+\.(?:test|spec)\.[cm]?[jt]sx?)\b/;
|
|
181
|
+
const COMPLETED = /[✓×✗]\s+(\S+\.(?:test|spec)\.[cm]?[jt]sx?)\b/;
|
|
182
|
+
|
|
183
|
+
// Defense-in-depth (security-review Q2 NICE): the captured path is later
|
|
184
|
+
// printed to stderr in a CI log pane. `\S+` admits control/ESC bytes, so
|
|
185
|
+
// strip C0 + DEL before they reach the terminal — a malicious vitest log
|
|
186
|
+
// line cannot smuggle terminal-escape sequences through the hint.
|
|
187
|
+
// eslint-disable-next-line no-control-regex -- intentional: neutralizing C0 + DEL is the whole point
|
|
188
|
+
const stripCtrl = (s) => s.replace(/[\u0000-\u001f\u007f]/g, '?');
|
|
189
|
+
|
|
190
|
+
for (const line of logText.split('\n')) {
|
|
191
|
+
const c = COMPLETED.exec(line);
|
|
192
|
+
if (c) {
|
|
193
|
+
completed.add(stripCtrl(c[1]));
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const s = STARTED.exec(line);
|
|
197
|
+
if (s) started.add(stripCtrl(s[1]));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const inFlight = [...started].filter((f) => !completed.has(f));
|
|
201
|
+
return inFlight.sort();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Read a log file and extract the in-flight set. Fail-soft: a missing or
|
|
206
|
+
* unreadable log yields [] (the caller falls back to the bare message).
|
|
207
|
+
* @param {string} path
|
|
208
|
+
* @returns {string[]}
|
|
209
|
+
*/
|
|
210
|
+
export function inFlightFilesFromLogFile(path) {
|
|
211
|
+
let raw;
|
|
212
|
+
try {
|
|
213
|
+
raw = readFileSync(path, 'utf8');
|
|
214
|
+
} catch {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
return inFlightFilesFromLog(raw);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function parseArgs(argv) {
|
|
221
|
+
const positional = [];
|
|
222
|
+
let minTests = DEFAULT_MIN_TESTS;
|
|
223
|
+
let logPath = null;
|
|
224
|
+
for (const arg of argv) {
|
|
225
|
+
const m = /^--min-tests=(\d+)$/.exec(arg);
|
|
226
|
+
const l = /^--log=(.+)$/.exec(arg);
|
|
227
|
+
if (m) minTests = Number(m[1]);
|
|
228
|
+
else if (l) logPath = l[1];
|
|
229
|
+
else positional.push(arg);
|
|
230
|
+
}
|
|
231
|
+
return { path: positional[0], minTests, logPath };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// CLI entry — only when run directly, not when imported by tests.
|
|
235
|
+
const isMain = import.meta.url === `file://${process.argv[1]}`;
|
|
236
|
+
if (isMain) {
|
|
237
|
+
const { path, minTests, logPath } = parseArgs(process.argv.slice(2));
|
|
238
|
+
if (!path) {
|
|
239
|
+
console.error('[ci] assert-vitest-green: missing <result.json> argument — failing closed');
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
const { ok, reason, summary } = assertVitestGreenFile(path, { minTests });
|
|
243
|
+
const s = summary || {};
|
|
244
|
+
console.log(
|
|
245
|
+
`[ci] vitest result: success=${s.success} passed=${s.numPassedTests} failed=${s.numFailedTests} ` +
|
|
246
|
+
`failedSuites=${s.numFailedTestSuites} total=${s.numTotalTests} (floor=${minTests})`,
|
|
247
|
+
);
|
|
248
|
+
if (ok) {
|
|
249
|
+
console.log(`[ci] ✓ suite verifiably green — ${reason}`);
|
|
250
|
+
process.exit(0);
|
|
251
|
+
}
|
|
252
|
+
console.error(`[ci] ✗ FAIL-CLOSED: ${reason}`);
|
|
253
|
+
// Self-diagnosing hint: the result JSON could not be READ (the hang case
|
|
254
|
+
// where vitest was killed before writing it). When a --log is supplied,
|
|
255
|
+
// surface the test files that were in-flight at the kill. This is purely an
|
|
256
|
+
// enriched stderr message — the exit-1 contract is unchanged.
|
|
257
|
+
if (logPath && reason.startsWith('cannot read result file')) {
|
|
258
|
+
const inFlight = inFlightFilesFromLogFile(logPath);
|
|
259
|
+
if (inFlight.length > 0) {
|
|
260
|
+
console.error(
|
|
261
|
+
`[ci] hint: ${inFlight.length} test file(s) in-flight when killed ` +
|
|
262
|
+
`(likely hang/slowdown): ${inFlight.join(', ')}`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Install Session Orchestrator through Codex's public plugin lifecycle.
|
|
4
|
+
*
|
|
5
|
+
* Exit codes:
|
|
6
|
+
* 0 — installed, enabled, and verified
|
|
7
|
+
* 1 — argument or user-correctable environment error
|
|
8
|
+
* 2 — system, upstream command/JSON, or runtime postcondition failure
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { spawnSync } from 'node:child_process';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
import { validateCodexPluginContract } from './lib/codex/plugin-contract.mjs';
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const SCRIPT_DIR = path.dirname(__filename);
|
|
20
|
+
const SO_ROOT = path.dirname(SCRIPT_DIR);
|
|
21
|
+
|
|
22
|
+
const MIN_CODEX_VERSION = [0, 144, 4];
|
|
23
|
+
const MARKETPLACE_NAME = 'kanevry';
|
|
24
|
+
const PLUGIN_NAME = 'session-orchestrator';
|
|
25
|
+
const PLUGIN_ID = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
26
|
+
const LEGACY_PLUGIN_IDS = [
|
|
27
|
+
`${PLUGIN_NAME}@openai-curated`,
|
|
28
|
+
`${PLUGIN_NAME}@local`,
|
|
29
|
+
];
|
|
30
|
+
const CODEX_COMMAND_TIMEOUT_MS = 30_000;
|
|
31
|
+
const ERROR_CATEGORIES = Object.freeze({
|
|
32
|
+
USER: 'user',
|
|
33
|
+
SYSTEM: 'system',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
class InstallerError extends Error {
|
|
37
|
+
constructor(message, category = ERROR_CATEGORIES.SYSTEM) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = 'InstallerError';
|
|
40
|
+
this.category = category;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseArguments(argv) {
|
|
45
|
+
let json = false;
|
|
46
|
+
let help = false;
|
|
47
|
+
let version = false;
|
|
48
|
+
|
|
49
|
+
for (const argument of argv) {
|
|
50
|
+
if (argument === '--json') json = true;
|
|
51
|
+
else if (argument === '--help' || argument === '-h') help = true;
|
|
52
|
+
else if (argument === '--version') version = true;
|
|
53
|
+
else {
|
|
54
|
+
throw new InstallerError(
|
|
55
|
+
`Unknown argument '${argument}'. Run with --help for usage.`,
|
|
56
|
+
ERROR_CATEGORIES.USER,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { json, help, version };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function printHelp() {
|
|
65
|
+
process.stdout.write(`Usage: node scripts/codex-install.mjs [options]
|
|
66
|
+
|
|
67
|
+
Install Session Orchestrator through the public Codex plugin lifecycle.
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
--json Print the verified final state as JSON
|
|
71
|
+
--version Print the package/plugin base version
|
|
72
|
+
-h, --help Show this help
|
|
73
|
+
|
|
74
|
+
Examples:
|
|
75
|
+
node scripts/codex-install.mjs
|
|
76
|
+
node scripts/codex-install.mjs --json
|
|
77
|
+
node scripts/codex-install.mjs --version
|
|
78
|
+
`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function progress(jsonMode, message) {
|
|
82
|
+
if (!jsonMode) process.stdout.write(`${message}\n`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function formatCommand(args) {
|
|
86
|
+
return ['codex', ...args].map((part) => (
|
|
87
|
+
/\s/.test(part) ? JSON.stringify(part) : part
|
|
88
|
+
)).join(' ');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function runCodex(args) {
|
|
92
|
+
const command = formatCommand(args);
|
|
93
|
+
const result = spawnSync('codex', args, {
|
|
94
|
+
encoding: 'utf8',
|
|
95
|
+
timeout: CODEX_COMMAND_TIMEOUT_MS,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (result.error) {
|
|
99
|
+
throw new InstallerError(`Cannot run '${command}': ${result.error.message}`);
|
|
100
|
+
}
|
|
101
|
+
if (result.status !== 0) {
|
|
102
|
+
const diagnostic = (result.stderr || result.stdout || '').trim() || 'no diagnostic output';
|
|
103
|
+
throw new InstallerError(`Command failed with exit ${String(result.status)}: ${command}\n${diagnostic}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result.stdout;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function runCodexJson(args, label) {
|
|
110
|
+
const stdout = runCodex(args);
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(stdout);
|
|
113
|
+
if (!isRecord(parsed)) {
|
|
114
|
+
throw new Error('top-level value must be an object');
|
|
115
|
+
}
|
|
116
|
+
return parsed;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
throw new InstallerError(
|
|
119
|
+
`${label} returned malformed JSON from '${formatCommand(args)}': ${error.message}`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function parseCodexVersion(output) {
|
|
125
|
+
const match = output.match(/\b(\d+)\.(\d+)\.(\d+)(?:[-+][^\s]+)?\b/);
|
|
126
|
+
if (!match) {
|
|
127
|
+
throw new InstallerError(`Cannot parse Codex version from: ${output.trim() || '<empty output>'}`);
|
|
128
|
+
}
|
|
129
|
+
return match.slice(1, 4).map(Number);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function compareVersions(left, right) {
|
|
133
|
+
for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
|
|
134
|
+
const delta = (left[index] ?? 0) - (right[index] ?? 0);
|
|
135
|
+
if (delta !== 0) return Math.sign(delta);
|
|
136
|
+
}
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function formatVersion(version) {
|
|
141
|
+
return version.join('.');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function parseFeatures(output) {
|
|
145
|
+
const features = new Map();
|
|
146
|
+
for (const line of output.split(/\r?\n/)) {
|
|
147
|
+
const match = line.match(/^(\S+)\s+(.+?)\s+(true|false)\s*$/);
|
|
148
|
+
if (!match) continue;
|
|
149
|
+
features.set(match[1], { state: match[2].trim(), enabled: match[3] === 'true' });
|
|
150
|
+
}
|
|
151
|
+
return features;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function requireStableFeature(features, name) {
|
|
155
|
+
const feature = features.get(name);
|
|
156
|
+
if (feature?.state !== 'stable' || feature.enabled !== true) {
|
|
157
|
+
const actual = feature ? `${feature.state} ${String(feature.enabled)}` : 'missing';
|
|
158
|
+
throw new InstallerError(
|
|
159
|
+
`Codex feature '${name}' must report 'stable true'; got '${actual}'. `
|
|
160
|
+
+ `Upgrade Codex and verify with 'codex features list'.`,
|
|
161
|
+
ERROR_CATEGORIES.USER,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function readLocalJson(relativePath, label) {
|
|
167
|
+
try {
|
|
168
|
+
return JSON.parse(readFileSync(path.join(SO_ROOT, relativePath), 'utf8'));
|
|
169
|
+
} catch (error) {
|
|
170
|
+
throw new InstallerError(`Cannot read valid ${label}: ${error.message}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function readBaseVersion() {
|
|
175
|
+
const packageJson = readLocalJson('package.json', 'package.json');
|
|
176
|
+
if (typeof packageJson.version !== 'string' || packageJson.version.trim() === '') {
|
|
177
|
+
throw new InstallerError('package.json must contain a non-empty version string.');
|
|
178
|
+
}
|
|
179
|
+
return packageJson.version;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function validateLocalContract() {
|
|
183
|
+
const expectedBaseVersion = readBaseVersion();
|
|
184
|
+
const manifest = readLocalJson(path.join('.codex-plugin', 'plugin.json'), 'Codex plugin manifest');
|
|
185
|
+
const verdict = validateCodexPluginContract({
|
|
186
|
+
pluginRoot: SO_ROOT,
|
|
187
|
+
expectedBaseVersion,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (!verdict.ok) {
|
|
191
|
+
const details = verdict.errors
|
|
192
|
+
.map((error) => `[${error.rule}] ${error.path}: ${error.message}`)
|
|
193
|
+
.join('\n');
|
|
194
|
+
throw new InstallerError(`Local Codex plugin contract failed:\n${details}`);
|
|
195
|
+
}
|
|
196
|
+
if (manifest.name !== PLUGIN_NAME || typeof manifest.version !== 'string') {
|
|
197
|
+
throw new InstallerError('Codex plugin manifest must contain the expected name and version.');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return manifest.version;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function requireArray(value, label) {
|
|
204
|
+
if (!Array.isArray(value)) {
|
|
205
|
+
throw new InstallerError(`${label} must be an array in Codex JSON output.`);
|
|
206
|
+
}
|
|
207
|
+
return value;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function sameLocalPath(left, right) {
|
|
211
|
+
return typeof left === 'string' && path.resolve(left) === path.resolve(right);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function assertMarketplaceCompatible(payload) {
|
|
215
|
+
const marketplaces = requireArray(payload.marketplaces, 'marketplaces');
|
|
216
|
+
const matches = marketplaces.filter((entry) => isRecord(entry) && entry.name === MARKETPLACE_NAME);
|
|
217
|
+
|
|
218
|
+
if (matches.length > 1) {
|
|
219
|
+
throw new InstallerError(
|
|
220
|
+
`Codex reports multiple '${MARKETPLACE_NAME}' marketplaces. `
|
|
221
|
+
+ `Inspect them with 'codex plugin marketplace list --json'; this installer will not remove any marketplace.`,
|
|
222
|
+
ERROR_CATEGORIES.USER,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (matches.length === 0) return;
|
|
226
|
+
|
|
227
|
+
const existing = matches[0];
|
|
228
|
+
const sourceType = existing.marketplaceSource?.sourceType;
|
|
229
|
+
const source = existing.marketplaceSource?.source ?? existing.root;
|
|
230
|
+
if (sourceType !== 'local' || !sameLocalPath(source, SO_ROOT)) {
|
|
231
|
+
throw new InstallerError(
|
|
232
|
+
`Marketplace '${MARKETPLACE_NAME}' already points to '${String(source)}' (${String(sourceType)}), `
|
|
233
|
+
+ `not '${SO_ROOT}'. Resolve the conflict manually after reviewing `
|
|
234
|
+
+ `'codex plugin marketplace list --json'; this installer will not remove or replace it.`,
|
|
235
|
+
ERROR_CATEGORIES.USER,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function assertMarketplaceAddResult(payload) {
|
|
241
|
+
if (payload.marketplaceName !== MARKETPLACE_NAME) {
|
|
242
|
+
throw new InstallerError(
|
|
243
|
+
`Marketplace add returned '${String(payload.marketplaceName)}', expected '${MARKETPLACE_NAME}'.`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
if (!sameLocalPath(payload.installedRoot, SO_ROOT)) {
|
|
247
|
+
throw new InstallerError(
|
|
248
|
+
`Marketplace add returned root '${String(payload.installedRoot)}', expected '${SO_ROOT}'.`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
if (typeof payload.alreadyAdded !== 'boolean') {
|
|
252
|
+
throw new InstallerError("Marketplace add JSON is missing boolean field 'alreadyAdded'.");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function assertPluginAddResult(payload, expectedVersion) {
|
|
257
|
+
if (payload.pluginId !== PLUGIN_ID) {
|
|
258
|
+
throw new InstallerError(`Plugin add returned '${String(payload.pluginId)}', expected '${PLUGIN_ID}'.`);
|
|
259
|
+
}
|
|
260
|
+
if (payload.version !== expectedVersion) {
|
|
261
|
+
throw new InstallerError(
|
|
262
|
+
`Plugin add returned version '${String(payload.version)}', expected '${expectedVersion}'.`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function pluginEntries(payload) {
|
|
268
|
+
const installed = requireArray(payload.installed, 'installed plugins');
|
|
269
|
+
const available = requireArray(payload.available, 'available plugins');
|
|
270
|
+
return { installed, available, all: [...installed, ...available] };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function assertTargetHealthy(payload, expectedVersion, phase) {
|
|
274
|
+
const entries = pluginEntries(payload);
|
|
275
|
+
const matches = entries.all.filter((entry) => isRecord(entry) && entry.pluginId === PLUGIN_ID);
|
|
276
|
+
|
|
277
|
+
if (matches.length !== 1) {
|
|
278
|
+
throw new InstallerError(
|
|
279
|
+
`${phase}: expected exactly one '${PLUGIN_ID}' entry, found ${matches.length}.`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const target = matches[0];
|
|
284
|
+
if (target.installed !== true || target.enabled !== true) {
|
|
285
|
+
throw new InstallerError(
|
|
286
|
+
`${phase}: '${PLUGIN_ID}' must be installed and enabled; got installed=${String(target.installed)}, `
|
|
287
|
+
+ `enabled=${String(target.enabled)}.`,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
if (target.version !== expectedVersion) {
|
|
291
|
+
throw new InstallerError(
|
|
292
|
+
`${phase}: '${PLUGIN_ID}' version '${String(target.version)}' does not match manifest `
|
|
293
|
+
+ `'${expectedVersion}'.`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return entries;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function installedLegacyPluginIds(entries) {
|
|
301
|
+
return LEGACY_PLUGIN_IDS.filter((pluginId) => entries.installed.some((entry) => (
|
|
302
|
+
isRecord(entry) && entry.pluginId === pluginId && entry.installed === true
|
|
303
|
+
)));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function assertRemoveResult(payload, pluginId) {
|
|
307
|
+
if (payload.pluginId !== pluginId) {
|
|
308
|
+
throw new InstallerError(
|
|
309
|
+
`Plugin remove returned '${String(payload.pluginId)}', expected '${pluginId}'.`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function isRecord(value) {
|
|
315
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function install({ json }) {
|
|
319
|
+
progress(json, 'Session Orchestrator — Codex Setup');
|
|
320
|
+
progress(json, '=================================');
|
|
321
|
+
|
|
322
|
+
const version = parseCodexVersion(runCodex(['--version']));
|
|
323
|
+
if (compareVersions(version, MIN_CODEX_VERSION) < 0) {
|
|
324
|
+
throw new InstallerError(
|
|
325
|
+
`Codex ${formatVersion(version)} is unsupported; install Codex ${formatVersion(MIN_CODEX_VERSION)} or newer.`,
|
|
326
|
+
ERROR_CATEGORIES.USER,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
progress(json, `Codex: ${formatVersion(version)}`);
|
|
330
|
+
|
|
331
|
+
const features = parseFeatures(runCodex(['features', 'list']));
|
|
332
|
+
requireStableFeature(features, 'plugins');
|
|
333
|
+
requireStableFeature(features, 'hooks');
|
|
334
|
+
progress(json, 'Features: plugins stable true; hooks stable true');
|
|
335
|
+
|
|
336
|
+
const expectedVersion = validateLocalContract();
|
|
337
|
+
progress(json, `Local contract: valid (${expectedVersion})`);
|
|
338
|
+
|
|
339
|
+
const marketplaceList = runCodexJson(
|
|
340
|
+
['plugin', 'marketplace', 'list', '--json'],
|
|
341
|
+
'Marketplace list',
|
|
342
|
+
);
|
|
343
|
+
assertMarketplaceCompatible(marketplaceList);
|
|
344
|
+
|
|
345
|
+
progress(json, `Marketplace: adding '${SO_ROOT}'`);
|
|
346
|
+
const marketplaceAdd = runCodexJson(
|
|
347
|
+
['plugin', 'marketplace', 'add', SO_ROOT, '--json'],
|
|
348
|
+
'Marketplace add',
|
|
349
|
+
);
|
|
350
|
+
assertMarketplaceAddResult(marketplaceAdd);
|
|
351
|
+
progress(
|
|
352
|
+
json,
|
|
353
|
+
marketplaceAdd.alreadyAdded
|
|
354
|
+
? `Marketplace: '${MARKETPLACE_NAME}' already uses this source; refreshed`
|
|
355
|
+
: `Marketplace: '${MARKETPLACE_NAME}' added`,
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
progress(json, `Plugin: adding '${PLUGIN_ID}'`);
|
|
359
|
+
const pluginAdd = runCodexJson(
|
|
360
|
+
['plugin', 'add', PLUGIN_ID, '--json'],
|
|
361
|
+
'Plugin add',
|
|
362
|
+
);
|
|
363
|
+
assertPluginAddResult(pluginAdd, expectedVersion);
|
|
364
|
+
|
|
365
|
+
const firstPluginList = runCodexJson(
|
|
366
|
+
['plugin', 'list', '--available', '--json'],
|
|
367
|
+
'Plugin list',
|
|
368
|
+
);
|
|
369
|
+
const firstEntries = assertTargetHealthy(firstPluginList, expectedVersion, 'Post-install check');
|
|
370
|
+
progress(json, `Plugin: '${PLUGIN_ID}' is installed, enabled, and at ${expectedVersion}`);
|
|
371
|
+
|
|
372
|
+
const removedLegacyPlugins = [];
|
|
373
|
+
for (const legacyPluginId of installedLegacyPluginIds(firstEntries)) {
|
|
374
|
+
const removeResult = runCodexJson(
|
|
375
|
+
['plugin', 'remove', legacyPluginId, '--json'],
|
|
376
|
+
`Plugin remove (${legacyPluginId})`,
|
|
377
|
+
);
|
|
378
|
+
assertRemoveResult(removeResult, legacyPluginId);
|
|
379
|
+
removedLegacyPlugins.push(legacyPluginId);
|
|
380
|
+
progress(json, `Removed legacy plugin: ${legacyPluginId}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const finalPluginList = runCodexJson(
|
|
384
|
+
['plugin', 'list', '--available', '--json'],
|
|
385
|
+
'Final plugin list',
|
|
386
|
+
);
|
|
387
|
+
const finalEntries = assertTargetHealthy(finalPluginList, expectedVersion, 'Final check');
|
|
388
|
+
const remainingLegacyPlugins = installedLegacyPluginIds(finalEntries);
|
|
389
|
+
if (remainingLegacyPlugins.length > 0) {
|
|
390
|
+
throw new InstallerError(
|
|
391
|
+
`Final check: legacy plugins remain installed: ${remainingLegacyPlugins.join(', ')}.`,
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const result = {
|
|
396
|
+
ok: true,
|
|
397
|
+
codexVersion: formatVersion(version),
|
|
398
|
+
marketplace: MARKETPLACE_NAME,
|
|
399
|
+
marketplaceSource: SO_ROOT,
|
|
400
|
+
marketplaceAlreadyAdded: marketplaceAdd.alreadyAdded,
|
|
401
|
+
pluginId: PLUGIN_ID,
|
|
402
|
+
pluginVersion: expectedVersion,
|
|
403
|
+
installed: true,
|
|
404
|
+
enabled: true,
|
|
405
|
+
removedLegacyPlugins,
|
|
406
|
+
nextSteps: [
|
|
407
|
+
'Start a fresh Codex task or fully restart Codex so the plugin and hooks reload.',
|
|
408
|
+
'In the fresh task, run /hooks and review the hook bundle before approving it.',
|
|
409
|
+
'This installer does not write or bypass hook trust.',
|
|
410
|
+
],
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
if (json) {
|
|
414
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
415
|
+
} else {
|
|
416
|
+
process.stdout.write('\nDone. The public Codex plugin state is healthy.\n\n');
|
|
417
|
+
process.stdout.write('Next steps:\n');
|
|
418
|
+
for (const [index, step] of result.nextSteps.entries()) {
|
|
419
|
+
process.stdout.write(` ${index + 1}. ${step}\n`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
const options = parseArguments(process.argv.slice(2));
|
|
426
|
+
if (options.help) printHelp();
|
|
427
|
+
else if (options.version) process.stdout.write(`${readBaseVersion()}\n`);
|
|
428
|
+
else install(options);
|
|
429
|
+
} catch (error) {
|
|
430
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
431
|
+
process.stderr.write(`ERROR: ${message}\n`);
|
|
432
|
+
process.exitCode = error instanceof InstallerError && error.category === ERROR_CATEGORIES.USER
|
|
433
|
+
? 1
|
|
434
|
+
: 2;
|
|
435
|
+
}
|