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,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* product-repo-detect.mjs — #190
|
|
3
|
+
* Detects product-repo signals in a repo root. Returns scored signal object.
|
|
4
|
+
*
|
|
5
|
+
* Stdlib-only, cross-platform, no zx, never throws.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
|
|
11
|
+
const FRAMEWORK_DEPS = [
|
|
12
|
+
'next',
|
|
13
|
+
'nuxt',
|
|
14
|
+
'astro',
|
|
15
|
+
'@remix-run/react',
|
|
16
|
+
'@remix-run/node',
|
|
17
|
+
'@sveltejs/kit',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const PRODUCT_ENV_PATTERNS = [
|
|
21
|
+
/^SUPABASE_URL=/m,
|
|
22
|
+
/^SUPABASE_ANON_KEY=/m,
|
|
23
|
+
/^STRIPE_[A-Z_]+=/m,
|
|
24
|
+
/^(NEXT_PUBLIC_)?AUTH0_[A-Z_]+=/m,
|
|
25
|
+
/^(NEXT_PUBLIC_)?CLERK_[A-Z_]+=/m,
|
|
26
|
+
/^(NEXT_PUBLIC_)?FIREBASE_[A-Z_]+=/m,
|
|
27
|
+
/^(NEXT_PUBLIC_)?POSTHOG_[A-Z_]+=/m,
|
|
28
|
+
/^SENTRY_DSN=/m,
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const CONTENT_DIRS = ['src/lib/personas', 'src/content', 'content', 'src/data/personas'];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {{ repoRoot: string }} opts
|
|
35
|
+
* @returns {{
|
|
36
|
+
* isProductRepo: boolean,
|
|
37
|
+
* framework: string | null,
|
|
38
|
+
* contentDirs: string[],
|
|
39
|
+
* productEnvMatches: string[],
|
|
40
|
+
* score: number,
|
|
41
|
+
* signals: { framework: boolean, contentDir: boolean, envVars: boolean }
|
|
42
|
+
* }}
|
|
43
|
+
*/
|
|
44
|
+
export function detectProductRepo({ repoRoot } = {}) {
|
|
45
|
+
let framework = null;
|
|
46
|
+
const pkgPath = join(repoRoot, 'package.json');
|
|
47
|
+
if (existsSync(pkgPath)) {
|
|
48
|
+
try {
|
|
49
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
50
|
+
const deps = {
|
|
51
|
+
...(pkg.dependencies || {}),
|
|
52
|
+
...(pkg.devDependencies || {}),
|
|
53
|
+
...(pkg.peerDependencies || {}),
|
|
54
|
+
};
|
|
55
|
+
for (const dep of FRAMEWORK_DEPS) {
|
|
56
|
+
if (dep in deps) {
|
|
57
|
+
framework = dep;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
/* malformed package.json — no framework match */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const contentDirs = CONTENT_DIRS.filter((d) => existsSync(join(repoRoot, d)));
|
|
67
|
+
|
|
68
|
+
const productEnvMatches = [];
|
|
69
|
+
const envPath = join(repoRoot, '.env.local.example');
|
|
70
|
+
if (existsSync(envPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const content = readFileSync(envPath, 'utf8');
|
|
73
|
+
for (const re of PRODUCT_ENV_PATTERNS) {
|
|
74
|
+
const m = content.match(re);
|
|
75
|
+
if (m) productEnvMatches.push(m[0].split('=')[0]);
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
/* unreadable — ignore */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const signals = {
|
|
83
|
+
framework: framework !== null,
|
|
84
|
+
contentDir: contentDirs.length > 0,
|
|
85
|
+
envVars: productEnvMatches.length > 0,
|
|
86
|
+
};
|
|
87
|
+
const score =
|
|
88
|
+
(signals.framework ? 2 : 0) + (signals.contentDir ? 1 : 0) + (signals.envVars ? 2 : 0);
|
|
89
|
+
return {
|
|
90
|
+
isProductRepo: score >= 2,
|
|
91
|
+
framework,
|
|
92
|
+
contentDirs,
|
|
93
|
+
productEnvMatches,
|
|
94
|
+
score,
|
|
95
|
+
signals,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Check whether CLAUDE.md (or AGENTS.md) already has a `vault:` key in Session Config.
|
|
101
|
+
* @param {string} configFilePath
|
|
102
|
+
* @returns {boolean}
|
|
103
|
+
*/
|
|
104
|
+
export function hasVaultConfig(configFilePath) {
|
|
105
|
+
if (!existsSync(configFilePath)) return false;
|
|
106
|
+
let text;
|
|
107
|
+
try {
|
|
108
|
+
text = readFileSync(configFilePath, 'utf8');
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
// Scan only within the Session Config section (bounded by the next ## heading).
|
|
113
|
+
const startMatch = text.match(/^## Session Config[ \t]*(?:\r?\n|$)/m);
|
|
114
|
+
if (!startMatch || startMatch.index === undefined) return false;
|
|
115
|
+
const afterBlock = text.slice(startMatch.index + startMatch[0].length);
|
|
116
|
+
const nextHeadingMatch = afterBlock.match(/^## /m);
|
|
117
|
+
const blockContent = nextHeadingMatch
|
|
118
|
+
? afterBlock.slice(0, nextHeadingMatch.index)
|
|
119
|
+
: afterBlock;
|
|
120
|
+
return /^\s*vault\s*:/m.test(blockContent);
|
|
121
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* profiles/registry.mjs — Pure loader for test profile registry.
|
|
3
|
+
*
|
|
4
|
+
* Reads `.orchestrator/policy/test-profiles.json`, validates each entry
|
|
5
|
+
* against profileRegistrySchema, and exposes pure helper accessors.
|
|
6
|
+
* DI-friendly: accepts an `fs` seam so callers can inject mock file systems.
|
|
7
|
+
*
|
|
8
|
+
* Issue: #383 (part 3) — /test command: profile registry schema + seed profiles
|
|
9
|
+
*
|
|
10
|
+
* Exports:
|
|
11
|
+
* loadProfiles({ profilesPath?, fs? }) → Promise<{ok, profiles?, error?}>
|
|
12
|
+
* listProfileNames(profiles) → string[]
|
|
13
|
+
* getProfile(profiles, name) → {ok, profile?, error?}
|
|
14
|
+
* validateProfile(entry) → {ok, value?, error?}
|
|
15
|
+
* ProfileRegistryError
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fsPromises from 'node:fs/promises';
|
|
19
|
+
import { profileEntrySchema, profileRegistrySchema } from './schema.mjs';
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Default path
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
const DEFAULT_PROFILES_PATH = '.orchestrator/policy/test-profiles.json';
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// ProfileRegistryError
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Typed error for profile registry failures.
|
|
33
|
+
* Codes: 'FILE_NOT_FOUND' | 'PARSE_ERROR' | 'SCHEMA_INVALID' | 'UNKNOWN_PROFILE'
|
|
34
|
+
*/
|
|
35
|
+
export class ProfileRegistryError extends Error {
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} code
|
|
38
|
+
* @param {string} message
|
|
39
|
+
*/
|
|
40
|
+
constructor(code, message) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = 'ProfileRegistryError';
|
|
43
|
+
this.code = code;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// loadProfiles
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Load and validate the profiles registry from disk.
|
|
53
|
+
*
|
|
54
|
+
* @param {object} [opts]
|
|
55
|
+
* @param {string} [opts.profilesPath] - path to the JSON registry file;
|
|
56
|
+
* defaults to `.orchestrator/policy/test-profiles.json`
|
|
57
|
+
* @param {{ readFile: Function }} [opts.fs] - injected fs/promises (DI seam);
|
|
58
|
+
* defaults to the real `fs/promises`
|
|
59
|
+
* @returns {Promise<{ ok: true, profiles: Record<string, object> } | { ok: false, error: ProfileRegistryError }>}
|
|
60
|
+
*/
|
|
61
|
+
export async function loadProfiles({ profilesPath, fs: fsImpl } = {}) {
|
|
62
|
+
const filePath = profilesPath ?? DEFAULT_PROFILES_PATH;
|
|
63
|
+
const fsRead = fsImpl ?? fsPromises;
|
|
64
|
+
|
|
65
|
+
let raw;
|
|
66
|
+
try {
|
|
67
|
+
raw = await fsRead.readFile(filePath, 'utf8');
|
|
68
|
+
} catch (err) {
|
|
69
|
+
const code = err && err.code === 'ENOENT' ? 'FILE_NOT_FOUND' : 'PARSE_ERROR';
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
error: new ProfileRegistryError(code, `Cannot read profiles file '${filePath}': ${err.message}`),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = JSON.parse(raw);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
error: new ProfileRegistryError('PARSE_ERROR', `Invalid JSON in '${filePath}': ${err.message}`),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const result = profileRegistrySchema.safeParse(parsed);
|
|
87
|
+
if (!result.success) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
error: new ProfileRegistryError(
|
|
91
|
+
'SCHEMA_INVALID',
|
|
92
|
+
`Profile registry schema validation failed: ${result.error.message}`,
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { ok: true, profiles: result.data };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// listProfileNames
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Return the sorted list of profile names in the registry.
|
|
106
|
+
*
|
|
107
|
+
* @param {Record<string, object>} profiles
|
|
108
|
+
* @returns {string[]}
|
|
109
|
+
*/
|
|
110
|
+
export function listProfileNames(profiles) {
|
|
111
|
+
if (profiles === null || typeof profiles !== 'object' || Array.isArray(profiles)) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
return Object.keys(profiles).sort();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// getProfile
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Retrieve a single profile by name.
|
|
123
|
+
*
|
|
124
|
+
* @param {Record<string, object>} profiles
|
|
125
|
+
* @param {string} name
|
|
126
|
+
* @returns {{ ok: true, profile: object } | { ok: false, error: ProfileRegistryError }}
|
|
127
|
+
*/
|
|
128
|
+
export function getProfile(profiles, name) {
|
|
129
|
+
if (profiles === null || typeof profiles !== 'object' || Array.isArray(profiles)) {
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
error: new ProfileRegistryError('SCHEMA_INVALID', 'profiles must be a non-null object'),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
error: new ProfileRegistryError('UNKNOWN_PROFILE', 'profile name must be a non-empty string'),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!Object.prototype.hasOwnProperty.call(profiles, name)) {
|
|
144
|
+
const available = Object.keys(profiles).sort().join(', ') || '(none)';
|
|
145
|
+
return {
|
|
146
|
+
ok: false,
|
|
147
|
+
error: new ProfileRegistryError(
|
|
148
|
+
'UNKNOWN_PROFILE',
|
|
149
|
+
`Profile '${name}' not found. Available: ${available}`,
|
|
150
|
+
),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { ok: true, profile: profiles[name] };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// validateProfile
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Validate a raw profile entry object against the schema.
|
|
163
|
+
*
|
|
164
|
+
* @param {unknown} entry
|
|
165
|
+
* @returns {{ ok: true, value: object } | { ok: false, error: ProfileRegistryError }}
|
|
166
|
+
*/
|
|
167
|
+
export function validateProfile(entry) {
|
|
168
|
+
const result = profileEntrySchema.safeParse(entry);
|
|
169
|
+
if (!result.success) {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
error: new ProfileRegistryError('SCHEMA_INVALID', result.error.message),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return { ok: true, value: result.data };
|
|
176
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* profiles/schema.mjs — Validation schemas for test profile entries.
|
|
3
|
+
*
|
|
4
|
+
* Zod was not available in this project's node_modules at implementation time
|
|
5
|
+
* (issue #383 part 3), so validation is implemented as a hand-rolled validator
|
|
6
|
+
* that mirrors the Zod schema contract specified in the task brief.
|
|
7
|
+
*
|
|
8
|
+
* Exports:
|
|
9
|
+
* profileEntrySchema — validator for a single profile entry
|
|
10
|
+
* profileRegistrySchema — validator for a full profiles record
|
|
11
|
+
*
|
|
12
|
+
* Both exports expose a `.safeParse(value)` method returning
|
|
13
|
+
* `{ success: true, data }` or `{ success: false, error: ZodLike }`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { validatePathInsideProject } from '../path-utils.mjs';
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Validation helpers
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
const VALID_DRIVERS = new Set(['playwright', 'peekaboo']);
|
|
23
|
+
const VALID_MODES = new Set(['headless', 'headed']);
|
|
24
|
+
const NAME_REGEX = /^[a-z0-9-]+$/;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Build a ZodLike error object matching Zod's safeParse error shape.
|
|
28
|
+
* @param {string} message
|
|
29
|
+
* @returns {{ issues: Array<{message: string}> }}
|
|
30
|
+
*/
|
|
31
|
+
function makeError(message) {
|
|
32
|
+
return { issues: [{ message }], message };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate a single profile entry and return the parsed (defaulted) value.
|
|
37
|
+
* Mirrors the Zod schema:
|
|
38
|
+
*
|
|
39
|
+
* name: string, matches /^[a-z0-9-]+$/, min 1, max 50
|
|
40
|
+
* target: string | null | undefined
|
|
41
|
+
* driver: 'playwright' | 'peekaboo'
|
|
42
|
+
* mode: 'headless' | 'headed', default 'headless'
|
|
43
|
+
* rubric: string, default 'skills/test-runner/rubric-v1.md'
|
|
44
|
+
* checks: string[] | undefined
|
|
45
|
+
* tags: string[] | undefined
|
|
46
|
+
* rubric_features: string[] | undefined
|
|
47
|
+
* timeout_ms: positive integer, default 120000
|
|
48
|
+
* description: string | undefined
|
|
49
|
+
*
|
|
50
|
+
* @param {unknown} value
|
|
51
|
+
* @returns {{ success: true, data: object } | { success: false, error: object }}
|
|
52
|
+
*/
|
|
53
|
+
function parseProfileEntry(value) {
|
|
54
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
55
|
+
return { success: false, error: makeError('profile entry must be a non-null object') };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const v = /** @type {Record<string, unknown>} */ (value);
|
|
59
|
+
|
|
60
|
+
// name
|
|
61
|
+
if (typeof v.name !== 'string') {
|
|
62
|
+
return { success: false, error: makeError('name must be a string') };
|
|
63
|
+
}
|
|
64
|
+
if (v.name.length < 1 || v.name.length > 50) {
|
|
65
|
+
return { success: false, error: makeError('name must be between 1 and 50 characters') };
|
|
66
|
+
}
|
|
67
|
+
if (!NAME_REGEX.test(v.name)) {
|
|
68
|
+
return { success: false, error: makeError('name must match /^[a-z0-9-]+$/') };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// target
|
|
72
|
+
if (v.target !== undefined && v.target !== null && typeof v.target !== 'string') {
|
|
73
|
+
return { success: false, error: makeError('target must be a string, null, or undefined') };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// driver
|
|
77
|
+
if (!VALID_DRIVERS.has(/** @type {string} */ (v.driver))) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: makeError(`driver must be one of: ${[...VALID_DRIVERS].join(', ')}`),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// mode (with default)
|
|
85
|
+
const mode = v.mode === undefined ? 'headless' : v.mode;
|
|
86
|
+
if (!VALID_MODES.has(/** @type {string} */ (mode))) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: makeError(`mode must be one of: ${[...VALID_MODES].join(', ')}`),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// rubric (with default)
|
|
94
|
+
const rubric = v.rubric === undefined ? 'skills/test-runner/rubric-v1.md' : v.rubric;
|
|
95
|
+
if (typeof rubric !== 'string') {
|
|
96
|
+
return { success: false, error: makeError('rubric must be a string') };
|
|
97
|
+
}
|
|
98
|
+
// SEC-IR-LOW-3 + SEC-Q2-LOW-1: rubric must stay within project root; also reject symlink-escape (CWE-23)
|
|
99
|
+
// Two-phase guard (Phase 1 lexical + Phase 2 realpath-when-exists) delegated to validatePathInsideProject.
|
|
100
|
+
const projectRoot = process.cwd();
|
|
101
|
+
const rubricGuard = validatePathInsideProject(rubric, projectRoot);
|
|
102
|
+
if (!rubricGuard.ok) {
|
|
103
|
+
return { success: false, error: makeError('rubric path escapes project root') };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// checks (optional array of strings)
|
|
107
|
+
if (v.checks !== undefined) {
|
|
108
|
+
if (!Array.isArray(v.checks) || v.checks.some((c) => typeof c !== 'string')) {
|
|
109
|
+
return { success: false, error: makeError('checks must be an array of strings') };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// tags (optional array of strings)
|
|
114
|
+
if (v.tags !== undefined) {
|
|
115
|
+
if (!Array.isArray(v.tags) || v.tags.some((t) => typeof t !== 'string')) {
|
|
116
|
+
return { success: false, error: makeError('tags must be an array of strings') };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// rubric_features (optional array of strings)
|
|
121
|
+
if (v.rubric_features !== undefined) {
|
|
122
|
+
if (!Array.isArray(v.rubric_features) || v.rubric_features.some((f) => typeof f !== 'string')) {
|
|
123
|
+
return { success: false, error: makeError('rubric_features must be an array of strings') };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// timeout_ms (positive integer, with default)
|
|
128
|
+
const timeout_ms = v.timeout_ms === undefined ? 120000 : v.timeout_ms;
|
|
129
|
+
if (
|
|
130
|
+
typeof timeout_ms !== 'number' ||
|
|
131
|
+
!Number.isInteger(timeout_ms) ||
|
|
132
|
+
timeout_ms <= 0
|
|
133
|
+
) {
|
|
134
|
+
return { success: false, error: makeError('timeout_ms must be a positive integer') };
|
|
135
|
+
}
|
|
136
|
+
// SEC-PD-LOW-3: V8 setTimeout uses int32 — values > 2^31-1 silently overflow to immediate-fire.
|
|
137
|
+
// Practical ceiling is 1 hour for test profiles.
|
|
138
|
+
const MAX_TIMEOUT_MS = 3_600_000; // 1 hour
|
|
139
|
+
if (timeout_ms > MAX_TIMEOUT_MS) {
|
|
140
|
+
return { success: false, error: makeError(`timeout_ms must not exceed ${MAX_TIMEOUT_MS} (1 hour)`) };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// description (optional string)
|
|
144
|
+
if (v.description !== undefined && typeof v.description !== 'string') {
|
|
145
|
+
return { success: false, error: makeError('description must be a string') };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// SEC-Q2-LOW-2 (deep-5): store resolved rubric path (realPath when exists, else lexicalPath)
|
|
149
|
+
// to defeat post-validation symlink-swap TOCTOU. Mirrors the #405 pattern in config/test.mjs.
|
|
150
|
+
const resolvedRubric = rubricGuard.realPath ?? rubricGuard.lexicalPath;
|
|
151
|
+
/** @type {Record<string, unknown>} */
|
|
152
|
+
const data = {
|
|
153
|
+
name: v.name,
|
|
154
|
+
driver: v.driver,
|
|
155
|
+
mode,
|
|
156
|
+
rubric: resolvedRubric,
|
|
157
|
+
timeout_ms,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (v.target !== undefined) data.target = v.target;
|
|
161
|
+
if (v.checks !== undefined) data.checks = v.checks;
|
|
162
|
+
if (v.tags !== undefined) data.tags = v.tags;
|
|
163
|
+
if (v.rubric_features !== undefined) data.rubric_features = v.rubric_features;
|
|
164
|
+
if (v.description !== undefined) data.description = v.description;
|
|
165
|
+
|
|
166
|
+
return { success: true, data };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Validate a full profiles registry (Record<string, profileEntry>).
|
|
171
|
+
*
|
|
172
|
+
* @param {unknown} value
|
|
173
|
+
* @returns {{ success: true, data: Record<string, object> } | { success: false, error: object }}
|
|
174
|
+
*/
|
|
175
|
+
function parseProfileRegistry(value) {
|
|
176
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
177
|
+
return { success: false, error: makeError('profile registry must be a non-null object') };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** @type {Record<string, object>} */
|
|
181
|
+
const data = {};
|
|
182
|
+
|
|
183
|
+
for (const [key, entry] of Object.entries(/** @type {Record<string, unknown>} */ (value))) {
|
|
184
|
+
const result = parseProfileEntry(entry);
|
|
185
|
+
if (!result.success) {
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
error: makeError(`profile '${key}': ${result.error.message}`),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
data[key] = result.data;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return { success: true, data };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// Exports — Zod-compatible safeParse interface
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
export const profileEntrySchema = {
|
|
202
|
+
/** @param {unknown} value */
|
|
203
|
+
safeParse: (value) => parseProfileEntry(value),
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export const profileRegistrySchema = {
|
|
207
|
+
/** @param {unknown} value */
|
|
208
|
+
safeParse: (value) => parseProfileRegistry(value),
|
|
209
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* qg-command-drift-banner.mjs — session-start banner probe for *-command drift detection.
|
|
3
|
+
*
|
|
4
|
+
* Detects when CLAUDE.md Session Config `lint-command` / `typecheck-command` /
|
|
5
|
+
* `test-command` values deviate from project defaults. Returns a structured
|
|
6
|
+
* banner object `{severity, message}` (or null for silent no-op), mirroring
|
|
7
|
+
* the contract used by `checkVaultStaleness` and other session-start probes.
|
|
8
|
+
*
|
|
9
|
+
* Used by session-start Phase 4 alongside other freshness/drift probes.
|
|
10
|
+
*
|
|
11
|
+
* Config-read path: this module reads Session Config exclusively via
|
|
12
|
+
* `loadCommandsFromSessionConfig` from `./quality-gate.mjs`. That helper
|
|
13
|
+
* returns a partial object (missing keys are absent — no default substitution),
|
|
14
|
+
* which means absent `*-command` keys cannot trigger spurious drift against
|
|
15
|
+
* `PROJECT_DEFAULTS`. Drift is only reported when a value is explicitly set
|
|
16
|
+
* AND differs from the corresponding `PROJECT_DEFAULTS` entry.
|
|
17
|
+
*
|
|
18
|
+
* Cross-references:
|
|
19
|
+
* - .claude/rules/quality-gates-autofix.md § Session Config Command Injection
|
|
20
|
+
* - scripts/lib/quality-gate.mjs `loadCommandsFromSessionConfig()`
|
|
21
|
+
*
|
|
22
|
+
* @see #525 (Pattern 4 Auto-Fix-Loop residuals)
|
|
23
|
+
* @see #526 (Pattern 4 banner ecosystem coherence)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { loadCommandsFromSessionConfig } from './quality-gate.mjs';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Default *-command values for the session-orchestrator plugin.
|
|
30
|
+
* Source of truth: CLAUDE.md `## Session Config` block as shipped.
|
|
31
|
+
*
|
|
32
|
+
* Override this constant only when the canonical defaults change in CLAUDE.md.
|
|
33
|
+
*/
|
|
34
|
+
export const PROJECT_DEFAULTS = Object.freeze({
|
|
35
|
+
lint: 'npm run lint',
|
|
36
|
+
typecheck: 'npm run typecheck',
|
|
37
|
+
test: 'npm test',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check for *-command drift.
|
|
42
|
+
*
|
|
43
|
+
* Reads Session Config from CLAUDE.md (or AGENTS.md) in `repoRoot` via
|
|
44
|
+
* `loadCommandsFromSessionConfig` and compares any resolved `lint`,
|
|
45
|
+
* `typecheck`, and `test` values against PROJECT_DEFAULTS. Returns null
|
|
46
|
+
* when no drift is detected, or when the config-read returns no recognised
|
|
47
|
+
* `*-command` keys at all (graceful no-op).
|
|
48
|
+
*
|
|
49
|
+
* Important: missing keys do NOT trigger drift. Only explicit overrides
|
|
50
|
+
* that differ from PROJECT_DEFAULTS produce a banner.
|
|
51
|
+
*
|
|
52
|
+
* Marked `async` for backward compatibility with existing callers that
|
|
53
|
+
* `await` the result; internally the helper is synchronous.
|
|
54
|
+
*
|
|
55
|
+
* @param {object} [opts]
|
|
56
|
+
* @param {string} [opts.repoRoot] — project root (defaults to process.cwd()).
|
|
57
|
+
* @returns {Promise<null | {severity: 'warn', message: string}>}
|
|
58
|
+
*/
|
|
59
|
+
export async function checkQgCommandDrift(opts = {}) {
|
|
60
|
+
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
61
|
+
|
|
62
|
+
let cfg;
|
|
63
|
+
try {
|
|
64
|
+
cfg = loadCommandsFromSessionConfig(repoRoot);
|
|
65
|
+
} catch {
|
|
66
|
+
return null; // graceful no-op on config load failure
|
|
67
|
+
}
|
|
68
|
+
if (!cfg || typeof cfg !== 'object') return null;
|
|
69
|
+
|
|
70
|
+
const drifts = [];
|
|
71
|
+
for (const [key, defaultVal] of Object.entries(PROJECT_DEFAULTS)) {
|
|
72
|
+
const currentVal = cfg[key];
|
|
73
|
+
// Skip the comparison when the key is absent — missing keys cannot drift.
|
|
74
|
+
if (typeof currentVal !== 'string' || !currentVal.trim()) continue;
|
|
75
|
+
if (currentVal !== defaultVal) {
|
|
76
|
+
drifts.push(` ${key}-command: "${currentVal}" ← deviates from default "${defaultVal}"`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (drifts.length === 0) return null;
|
|
80
|
+
|
|
81
|
+
const message = [
|
|
82
|
+
'⚠ Session Config drift (*-command keys):',
|
|
83
|
+
...drifts,
|
|
84
|
+
'Verify the overrides are intentional. See .claude/rules/quality-gates-autofix.md § Session Config Command Injection for the RCE-equivalent trust-model.',
|
|
85
|
+
].join('\n');
|
|
86
|
+
|
|
87
|
+
return { severity: 'warn', message };
|
|
88
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/lib/quality-gate/diagnostics.mjs
|
|
3
|
+
*
|
|
4
|
+
* Diagnostics-bundle redaction helpers for the auto-fix loop (#521 + #525).
|
|
5
|
+
* Strips token-shaped strings, secret env-var values, sensitive HTTP headers,
|
|
6
|
+
* and user-path identifiers before persisting the bundle to disk.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from scripts/lib/quality-gate.mjs in #525 to give W4 tests a clean
|
|
9
|
+
* module surface and isolate redaction concerns from gate-runner concerns.
|
|
10
|
+
*
|
|
11
|
+
* @see .claude/rules/quality-gates-autofix.md § Session Config Command Injection
|
|
12
|
+
* @see scripts/lib/quality-gate.mjs (consumer — writes diagnostics bundle)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Token-shaped patterns that may appear in diagnostics output (gate command
|
|
17
|
+
* output lines, corrective_context entries, etc.). Replace each match with a
|
|
18
|
+
* placeholder so bundles are safe to attach to issue trackers or share with
|
|
19
|
+
* support.
|
|
20
|
+
*
|
|
21
|
+
* Ordering: more-specific patterns first (long prefixes before short ones) to
|
|
22
|
+
* prevent partial overlap.
|
|
23
|
+
*/
|
|
24
|
+
const REDACTION_PATTERNS = [
|
|
25
|
+
[/ghp_[A-Za-z0-9]{36}/g, '***GITHUB_PAT***'],
|
|
26
|
+
[/glpat-[A-Za-z0-9_-]{20,}/g, '***GITLAB_PAT***'],
|
|
27
|
+
[/npm_[A-Za-z0-9]{36,}/g, '***NPM_TOKEN***'],
|
|
28
|
+
[/AKIA[0-9A-Z]{16}/g, '***AWS_ACCESS_KEY***'],
|
|
29
|
+
[/sk-ant-[A-Za-z0-9_-]{20,}/g, '***ANTHROPIC_KEY***'],
|
|
30
|
+
[/sbp_[A-Za-z0-9_-]{40,}/g, '***SUPABASE_TOKEN***'],
|
|
31
|
+
[/sk-[A-Za-z0-9]{40,}/g, '***OPENAI_KEY***'],
|
|
32
|
+
[/xox[bpae]-[A-Za-z0-9-]+/g, '***SLACK_TOKEN***'],
|
|
33
|
+
[/sk_(live|test)_[A-Za-z0-9]{24,}/g, '***STRIPE_KEY***'],
|
|
34
|
+
[/pk_(live|test)_[A-Za-z0-9]{24,}/g, '***STRIPE_PK***'],
|
|
35
|
+
[/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, '***JWT***'],
|
|
36
|
+
[/https:\/\/(discord(app)?\.com|hooks\.slack\.com)\/[^\s"']+/g, '***WEBHOOK_URL***'],
|
|
37
|
+
[/Bearer\s+[A-Za-z0-9._-]+/gi, 'Bearer ***REDACTED***'],
|
|
38
|
+
[/Authorization:\s*Basic\s+[A-Za-z0-9+/=]+/gi, 'Authorization: Basic ***REDACTED***'],
|
|
39
|
+
[/Cookie:\s*[^\n\r]+/gi, 'Cookie: ***REDACTED***'],
|
|
40
|
+
[/\/Users\/[^/\s"']+\//g, '/Users/<redacted>/'],
|
|
41
|
+
[/\/home\/[^/\s"']+\//g, '/home/<redacted>/'],
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Matches env-var names that are semantically secret-bearing.
|
|
46
|
+
* Keys matching this pattern have their VALUES redacted (the key name is kept
|
|
47
|
+
* so operators can still audit which secret categories were present).
|
|
48
|
+
*/
|
|
49
|
+
const SECRET_ENV_NAME_RE = /^([A-Z][A-Z0-9_]*(?:_TOKEN|_KEY|_SECRET|_PASSWORD|_CREDENTIAL[A-Z_]*))$/;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Redact secrets, env-var values, and user-specific paths from a diagnostics
|
|
53
|
+
* bundle before writing it to disk.
|
|
54
|
+
*
|
|
55
|
+
* Applies two passes:
|
|
56
|
+
* 1. Strip values of env-var keys that match SECRET_ENV_NAME_RE.
|
|
57
|
+
* 2. Serialize the bundle to JSON, apply REDACTION_PATTERNS against the full
|
|
58
|
+
* JSON string, then parse back. This catches token-shaped strings nested
|
|
59
|
+
* anywhere in the object graph (gate output lines, corrective_context
|
|
60
|
+
* entries, error messages) without requiring a recursive string-walker.
|
|
61
|
+
*
|
|
62
|
+
* Never throws — on any error the original bundle is returned so the write
|
|
63
|
+
* still completes (a partially-unredacted bundle is better than a lost bundle).
|
|
64
|
+
*
|
|
65
|
+
* @param {object} bundle
|
|
66
|
+
* @returns {object}
|
|
67
|
+
*/
|
|
68
|
+
export function redactDiagnosticsBundle(bundle) {
|
|
69
|
+
try {
|
|
70
|
+
// Deep-clone via JSON round-trip so we never mutate the caller's object.
|
|
71
|
+
const copy = JSON.parse(JSON.stringify(bundle ?? {}));
|
|
72
|
+
|
|
73
|
+
// Pass 1 — redact secret-bearing env-var values.
|
|
74
|
+
if (copy.env && typeof copy.env === 'object') {
|
|
75
|
+
for (const key of Object.keys(copy.env)) {
|
|
76
|
+
if (SECRET_ENV_NAME_RE.test(key)) {
|
|
77
|
+
copy.env[key] = '***REDACTED***';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Pass 2 — regex-replace token/path patterns over the full JSON string.
|
|
83
|
+
let serialized = JSON.stringify(copy);
|
|
84
|
+
for (const [re, replacement] of REDACTION_PATTERNS) {
|
|
85
|
+
serialized = serialized.replace(re, replacement);
|
|
86
|
+
}
|
|
87
|
+
return JSON.parse(serialized);
|
|
88
|
+
} catch {
|
|
89
|
+
// Return unredacted rather than losing the bundle entirely.
|
|
90
|
+
return bundle ?? {};
|
|
91
|
+
}
|
|
92
|
+
}
|