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,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* playwright-driver/runner.mjs — Phase 2 entry-point for the /test command.
|
|
4
|
+
*
|
|
5
|
+
* Spawns `npx playwright test` in the target repo directory, pipes output to
|
|
6
|
+
* ${RUN_DIR}/console.log, and exits with a deterministic exit code per the
|
|
7
|
+
* Composability Contract in skills/playwright-driver/SKILL.md.
|
|
8
|
+
*
|
|
9
|
+
* Exit codes:
|
|
10
|
+
* 0 — all tests passed
|
|
11
|
+
* 1 — at least one test failed (Playwright exit 1)
|
|
12
|
+
* 2 — framework error, timeout, spawn failure, or invalid profile
|
|
13
|
+
*
|
|
14
|
+
* CLI args (all also accepted via env vars as fallback):
|
|
15
|
+
* --run-dir <path> absolute path to artifact dir (env: RUN_DIR)
|
|
16
|
+
* --profile <name> profile name, e.g. "web-gate" (env: PROFILE)
|
|
17
|
+
* --target <path> absolute path to target repo (env: TARGET)
|
|
18
|
+
* --dry-run print resolved command, skip subprocess, exit 0
|
|
19
|
+
*
|
|
20
|
+
* DI seams for testability (all optional, accepted via opts object):
|
|
21
|
+
* opts.spawn — override node:child_process spawn
|
|
22
|
+
* opts.fs — override node:fs (sync subset: mkdirSync, createWriteStream, readFileSync, existsSync, writeFileSync)
|
|
23
|
+
*
|
|
24
|
+
* Issue: #385 — web-gate end-to-end proof against an EspoCRM web target
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { spawn as realSpawn } from 'node:child_process';
|
|
28
|
+
import realFs from 'node:fs';
|
|
29
|
+
import os from 'node:os';
|
|
30
|
+
import path from 'node:path';
|
|
31
|
+
import { parseArgs } from 'node:util';
|
|
32
|
+
import { getProfile, loadProfiles, validateProfile } from '../profiles/registry.mjs';
|
|
33
|
+
import { validatePathInsideProject } from '../path-utils.mjs';
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Path resolution helper
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Expand a leading `~` to $HOME; absolute paths pass through verbatim;
|
|
41
|
+
* relative paths are resolved against process.cwd().
|
|
42
|
+
* @param {string} p
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
function resolvePath(p) {
|
|
46
|
+
if (!p) return process.cwd();
|
|
47
|
+
if (p.startsWith('~/') || p === '~') {
|
|
48
|
+
return path.join(os.homedir(), p.slice(1));
|
|
49
|
+
}
|
|
50
|
+
if (path.isAbsolute(p)) return p;
|
|
51
|
+
return path.resolve(process.cwd(), p);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// axe-core presence check
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check whether @axe-core/playwright is listed in the target's package.json.
|
|
60
|
+
* Returns true if present (or if package.json cannot be read — fail-open).
|
|
61
|
+
* @param {string} targetPath
|
|
62
|
+
* @param {{ readFileSync: Function, existsSync: Function }} fsImpl
|
|
63
|
+
* @returns {{ present: boolean, skipped: boolean }}
|
|
64
|
+
*/
|
|
65
|
+
function checkAxeCore(targetPath, fsImpl) {
|
|
66
|
+
const pkgPath = path.join(targetPath, 'package.json');
|
|
67
|
+
if (!fsImpl.existsSync(pkgPath)) {
|
|
68
|
+
return { present: false, skipped: true };
|
|
69
|
+
}
|
|
70
|
+
let pkg;
|
|
71
|
+
try {
|
|
72
|
+
pkg = JSON.parse(fsImpl.readFileSync(pkgPath, 'utf8'));
|
|
73
|
+
} catch {
|
|
74
|
+
return { present: false, skipped: true };
|
|
75
|
+
}
|
|
76
|
+
const deps = pkg.dependencies ?? {};
|
|
77
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
78
|
+
const present = '@axe-core/playwright' in deps || '@axe-core/playwright' in devDeps;
|
|
79
|
+
return { present, skipped: false };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Main run() function
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {object} [opts]
|
|
88
|
+
* @param {Function} [opts.spawn]
|
|
89
|
+
* @param {object} [opts.fs]
|
|
90
|
+
* @returns {Promise<void>}
|
|
91
|
+
*/
|
|
92
|
+
export default async function run(opts = {}) {
|
|
93
|
+
const spawnFn = opts.spawn ?? realSpawn;
|
|
94
|
+
const fsImpl = opts.fs ?? realFs;
|
|
95
|
+
|
|
96
|
+
// -------------------------------------------------------------------------
|
|
97
|
+
// Parse CLI args + env fallbacks
|
|
98
|
+
// -------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
const { values } = parseArgs({
|
|
101
|
+
args: process.argv.slice(2),
|
|
102
|
+
options: {
|
|
103
|
+
'run-dir': { type: 'string' },
|
|
104
|
+
'profile': { type: 'string' },
|
|
105
|
+
'target': { type: 'string' },
|
|
106
|
+
'dry-run': { type: 'boolean', default: false },
|
|
107
|
+
},
|
|
108
|
+
strict: false,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const runDir = resolvePath(values['run-dir'] ?? process.env.RUN_DIR ?? '');
|
|
112
|
+
const profileName = values['profile'] ?? process.env.PROFILE ?? '';
|
|
113
|
+
const targetRaw = values['target'] ?? process.env.TARGET ?? '';
|
|
114
|
+
const dryRun = values['dry-run'] ?? false;
|
|
115
|
+
|
|
116
|
+
if (!runDir) {
|
|
117
|
+
console.error('runner: --run-dir (or env RUN_DIR) is required');
|
|
118
|
+
process.exit(2);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// SEC-PD-MED-2: reject runDir values containing commas or colons as defensive
|
|
122
|
+
// path-sanitization (original rationale: reporter comma+colon split is now obsolete
|
|
123
|
+
// after the 2026-05-14 deep-3 reporter fix — paths flow via env vars, not CLI string).
|
|
124
|
+
// Kept as belt-and-braces against pathological run-dirs; revisit alongside #390 path-traversal.
|
|
125
|
+
if (/[,:]/.test(runDir)) {
|
|
126
|
+
console.error('runner: --run-dir must not contain commas or colons');
|
|
127
|
+
process.exit(2);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// #398 / #402: path-traversal guard — runDir must be strictly inside
|
|
131
|
+
// .orchestrator/metrics/test-runs/ relative to cwd (CWE-22).
|
|
132
|
+
// Delegated to validatePathInsideProject (two-phase: lexical + realpath).
|
|
133
|
+
const testRunsRoot = path.resolve(process.cwd(), '.orchestrator/metrics/test-runs');
|
|
134
|
+
const result = validatePathInsideProject(runDir, testRunsRoot);
|
|
135
|
+
if (!result.ok) {
|
|
136
|
+
if (result.reason === 'symlink') {
|
|
137
|
+
console.error('runner: --run-dir resolves (via symlink) outside .orchestrator/metrics/test-runs');
|
|
138
|
+
} else {
|
|
139
|
+
console.error(`runner: --run-dir must be inside .orchestrator/metrics/test-runs/ (got: ${runDir})`);
|
|
140
|
+
}
|
|
141
|
+
process.exit(2);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!profileName) {
|
|
145
|
+
console.error('runner: --profile (or env PROFILE) is required');
|
|
146
|
+
process.exit(2);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// -------------------------------------------------------------------------
|
|
150
|
+
// Load + validate profile
|
|
151
|
+
// -------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
const loadResult = await loadProfiles();
|
|
154
|
+
if (!loadResult.ok) {
|
|
155
|
+
console.error(`runner: failed to load profiles — ${loadResult.error.message}`);
|
|
156
|
+
process.exit(2);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const getResult = getProfile(loadResult.profiles, profileName);
|
|
160
|
+
if (!getResult.ok) {
|
|
161
|
+
console.error(`runner: ${getResult.error.message}`);
|
|
162
|
+
process.exit(2);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const validateResult = validateProfile(getResult.profile);
|
|
166
|
+
if (!validateResult.ok) {
|
|
167
|
+
console.error(`runner: profile '${profileName}' is invalid — ${validateResult.error.message}`);
|
|
168
|
+
process.exit(2);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const profile = validateResult.value;
|
|
172
|
+
|
|
173
|
+
// -------------------------------------------------------------------------
|
|
174
|
+
// Resolve target path
|
|
175
|
+
// -------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
const targetPath = resolvePath(targetRaw || profile.target || process.cwd());
|
|
178
|
+
|
|
179
|
+
// -------------------------------------------------------------------------
|
|
180
|
+
// axe-core presence check
|
|
181
|
+
// -------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
const axe = checkAxeCore(targetPath, fsImpl);
|
|
184
|
+
if (!axe.present && !axe.skipped) {
|
|
185
|
+
console.log('axe-violations: skipped — @axe-core/playwright not installed in target');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// -------------------------------------------------------------------------
|
|
189
|
+
// Build Playwright args
|
|
190
|
+
// -------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
// Playwright reporter syntax: comma-separated reporter NAMES, paths via env vars.
|
|
193
|
+
// Canonical: https://playwright.dev/docs/test-reporters#html-reporter (PLAYWRIGHT_HTML_OUTPUT_DIR)
|
|
194
|
+
// and #json-reporter (PLAYWRIGHT_JSON_OUTPUT_FILE). The previous `html:<path>` form
|
|
195
|
+
// was Jest/Vitest syntax — Playwright rejected it as `Cannot find module 'html:<path>'`.
|
|
196
|
+
const playwrightArgs = [
|
|
197
|
+
'playwright',
|
|
198
|
+
'test',
|
|
199
|
+
'--output', path.join(runDir, 'test-results'),
|
|
200
|
+
'--reporter', 'html,json',
|
|
201
|
+
'--trace', 'on',
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
if (profile.mode === 'headed') {
|
|
205
|
+
playwrightArgs.push('--headed');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (dryRun) {
|
|
209
|
+
console.log('runner: dry-run mode — resolved command:');
|
|
210
|
+
console.log(` npx ${playwrightArgs.join(' ')}`);
|
|
211
|
+
console.log(` cwd: ${targetPath}`);
|
|
212
|
+
process.exit(0);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// -------------------------------------------------------------------------
|
|
216
|
+
// Create artifact directories
|
|
217
|
+
// -------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
fsImpl.mkdirSync(path.join(runDir, 'test-results'), { recursive: true });
|
|
220
|
+
|
|
221
|
+
// #396: pre-create artifact namespace directories so test fixtures don't fail
|
|
222
|
+
// on missing parent paths. `test-results/` is created by Playwright via --output.
|
|
223
|
+
// `console.log` is created by createWriteStream below. AX + screenshots dirs are
|
|
224
|
+
// written by fixtures — pre-creating ensures a clean artifact layout on every run.
|
|
225
|
+
fsImpl.mkdirSync(path.join(runDir, 'ax-snapshots'), { recursive: true });
|
|
226
|
+
fsImpl.mkdirSync(path.join(runDir, 'screenshots'), { recursive: true });
|
|
227
|
+
|
|
228
|
+
// -------------------------------------------------------------------------
|
|
229
|
+
// Spawn Playwright
|
|
230
|
+
// -------------------------------------------------------------------------
|
|
231
|
+
|
|
232
|
+
const controller = new AbortController();
|
|
233
|
+
const timeoutHandle = setTimeout(
|
|
234
|
+
() => controller.abort(),
|
|
235
|
+
profile.timeout_ms,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
const logStream = fsImpl.createWriteStream(path.join(runDir, 'console.log'), { flags: 'a' });
|
|
239
|
+
|
|
240
|
+
const proc = spawnFn('npx', playwrightArgs, {
|
|
241
|
+
cwd: targetPath,
|
|
242
|
+
signal: controller.signal,
|
|
243
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
244
|
+
env: {
|
|
245
|
+
...process.env,
|
|
246
|
+
PLAYWRIGHT_HTML_OUTPUT_DIR: path.join(runDir, 'report'),
|
|
247
|
+
PLAYWRIGHT_JSON_OUTPUT_FILE: path.join(runDir, 'results.json'),
|
|
248
|
+
PLAYWRIGHT_HTML_OPEN: 'never',
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
proc.stdout.pipe(logStream, { end: false });
|
|
253
|
+
proc.stderr.pipe(logStream, { end: false });
|
|
254
|
+
|
|
255
|
+
// -------------------------------------------------------------------------
|
|
256
|
+
// Exit-code resolution
|
|
257
|
+
// -------------------------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @param {number} mapped
|
|
261
|
+
*/
|
|
262
|
+
function finish(mapped) {
|
|
263
|
+
clearTimeout(timeoutHandle);
|
|
264
|
+
logStream.end(() => {
|
|
265
|
+
fsImpl.writeFileSync(path.join(runDir, 'exit_code'), String(mapped));
|
|
266
|
+
process.exit(mapped);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
proc.on('error', (err) => {
|
|
271
|
+
console.error(`runner: spawn error — ${err.message}`);
|
|
272
|
+
finish(2);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
proc.on('close', (code, signal) => {
|
|
276
|
+
if (signal === 'SIGTERM' || code === null) {
|
|
277
|
+
finish(2);
|
|
278
|
+
} else if (code === 0) {
|
|
279
|
+
finish(0);
|
|
280
|
+
} else if (code === 1) {
|
|
281
|
+
finish(1);
|
|
282
|
+
} else {
|
|
283
|
+
finish(2);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
// CLI entry-point
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
293
|
+
run().catch((err) => {
|
|
294
|
+
console.error(`runner: unhandled error — ${err.message}`);
|
|
295
|
+
process.exit(2);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugin-root.mjs — Robust plugin root resolution with layered fallback.
|
|
3
|
+
*
|
|
4
|
+
* Issue #212: manual installs may not have CLAUDE_PLUGIN_ROOT set. This module
|
|
5
|
+
* provides a deterministic, testable resolution strategy so hook handlers and
|
|
6
|
+
* scripts never silently fall back to an empty path or wrong directory.
|
|
7
|
+
*
|
|
8
|
+
* Fallback order (stops at first success):
|
|
9
|
+
* 1. PLUGIN_ROOT native env var
|
|
10
|
+
* 2. Compatibility root matching explicit SO_PLATFORM
|
|
11
|
+
* 3. Remaining Claude, Codex, Cursor, and Pi compatibility roots
|
|
12
|
+
* 4. Walk up from import.meta.url looking for package.json whose name === "session-orchestrator"
|
|
13
|
+
* 5. Walk up from process.cwd() looking for the same marker
|
|
14
|
+
*
|
|
15
|
+
* Throws PluginRootResolutionError when all resolution levels fail.
|
|
16
|
+
*
|
|
17
|
+
* Backward compat: without native or explicit platform inputs, compatibility
|
|
18
|
+
* roots retain their legacy Claude → Codex → Cursor → Pi order.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
|
|
25
|
+
const COMPATIBILITY_ROOTS = [
|
|
26
|
+
['claude', 'CLAUDE_PLUGIN_ROOT'],
|
|
27
|
+
['codex', 'CODEX_PLUGIN_ROOT'],
|
|
28
|
+
['cursor', 'CURSOR_RULES_DIR'],
|
|
29
|
+
['pi', 'PI_PLUGIN_ROOT'],
|
|
30
|
+
];
|
|
31
|
+
const VALID_PLATFORMS = new Set(COMPATIBILITY_ROOTS.map(([platform]) => platform));
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// PluginRootResolutionError
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Thrown when all resolution levels fail. Callers may inspect
|
|
39
|
+
* `error.triedPaths` to understand what was attempted.
|
|
40
|
+
*/
|
|
41
|
+
export class PluginRootResolutionError extends Error {
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} message
|
|
44
|
+
* @param {string[]} triedPaths Directories / env vars that were checked
|
|
45
|
+
*/
|
|
46
|
+
constructor(message, triedPaths = []) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = 'PluginRootResolutionError';
|
|
49
|
+
this.triedPaths = triedPaths;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Internal helpers
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Return true when `dir` is an existing directory.
|
|
59
|
+
* @param {string} dir
|
|
60
|
+
* @returns {boolean}
|
|
61
|
+
*/
|
|
62
|
+
function _isDir(dir) {
|
|
63
|
+
try { return statSync(dir).isDirectory(); } catch { return false; }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Return a trimmed allowlisted platform or null.
|
|
68
|
+
* @param {string|undefined} value
|
|
69
|
+
* @returns {"claude"|"codex"|"cursor"|"pi"|null}
|
|
70
|
+
*/
|
|
71
|
+
function _validPlatform(value) {
|
|
72
|
+
const platform = (value || '').trim();
|
|
73
|
+
if (!VALID_PLATFORMS.has(platform)) return null;
|
|
74
|
+
return /** @type {"claude"|"codex"|"cursor"|"pi"} */ (platform);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolve one environment path, recording why it was skipped.
|
|
79
|
+
* @param {string} envName
|
|
80
|
+
* @param {string[]} tried
|
|
81
|
+
* @returns {string|null}
|
|
82
|
+
*/
|
|
83
|
+
function _envDirectory(envName, tried) {
|
|
84
|
+
const rawValue = process.env[envName];
|
|
85
|
+
const value = (rawValue || '').trim();
|
|
86
|
+
|
|
87
|
+
if (!value) {
|
|
88
|
+
tried.push(rawValue === undefined
|
|
89
|
+
? `${envName} (not set)`
|
|
90
|
+
: `${envName} (empty after trim)`);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (_isDir(value)) return value;
|
|
95
|
+
tried.push(`${envName}=${value} (not a directory)`);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Order compatibility roots by explicit platform, then an optional caller hint,
|
|
101
|
+
* while retaining the legacy Claude → Codex → Cursor → Pi order for the rest.
|
|
102
|
+
* @param {string|undefined} platformHint
|
|
103
|
+
* @returns {Array<[string, string]>}
|
|
104
|
+
*/
|
|
105
|
+
function _orderedCompatibilityRoots(platformHint) {
|
|
106
|
+
const preferredPlatform = _validPlatform(process.env.SO_PLATFORM) ?? _validPlatform(platformHint);
|
|
107
|
+
if (!preferredPlatform) return COMPATIBILITY_ROOTS;
|
|
108
|
+
|
|
109
|
+
const matching = COMPATIBILITY_ROOTS.filter(([platform]) => platform === preferredPlatform);
|
|
110
|
+
const remaining = COMPATIBILITY_ROOTS.filter(([platform]) => platform !== preferredPlatform);
|
|
111
|
+
return [...matching, ...remaining];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Return true when `dir` contains a package.json whose `name` field equals
|
|
116
|
+
* `"session-orchestrator"`. Silently returns false on any read/parse error.
|
|
117
|
+
*
|
|
118
|
+
* @param {string} dir Candidate directory to inspect
|
|
119
|
+
* @returns {boolean}
|
|
120
|
+
*/
|
|
121
|
+
function _isPluginRoot(dir) {
|
|
122
|
+
try {
|
|
123
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
124
|
+
if (!existsSync(pkgPath)) return false;
|
|
125
|
+
const raw = readFileSync(pkgPath, 'utf8');
|
|
126
|
+
const pkg = JSON.parse(raw);
|
|
127
|
+
return pkg.name === 'session-orchestrator';
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Walk up the directory tree from `startDir`, testing each directory with
|
|
135
|
+
* `_isPluginRoot`. Returns the matching directory path or `null`.
|
|
136
|
+
*
|
|
137
|
+
* Terminates at the filesystem root ("/", "C:\\", etc.) or when the parent
|
|
138
|
+
* path stops changing (guard against edge cases).
|
|
139
|
+
*
|
|
140
|
+
* @param {string} startDir Absolute directory to begin walking from
|
|
141
|
+
* @returns {string|null}
|
|
142
|
+
*/
|
|
143
|
+
function _walkUp(startDir) {
|
|
144
|
+
let dir = path.resolve(startDir);
|
|
145
|
+
const root = path.parse(dir).root;
|
|
146
|
+
|
|
147
|
+
while (dir !== root) {
|
|
148
|
+
if (_isPluginRoot(dir)) return dir;
|
|
149
|
+
const parent = path.dirname(dir);
|
|
150
|
+
if (parent === dir) break; // safety guard
|
|
151
|
+
dir = parent;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Check root itself
|
|
155
|
+
if (_isPluginRoot(root)) return root;
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// resolvePluginRoot
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Resolve the absolute path to the session-orchestrator plugin directory.
|
|
166
|
+
*
|
|
167
|
+
* Fallback order:
|
|
168
|
+
* 1. Trimmed native PLUGIN_ROOT when it is an existing directory
|
|
169
|
+
* 2. Compatibility root matching a valid explicit SO_PLATFORM
|
|
170
|
+
* 3. Remaining compatibility roots in legacy order
|
|
171
|
+
* 4. Walk up from import.meta.url (the location of this file) looking for a
|
|
172
|
+
* package.json with name "session-orchestrator"
|
|
173
|
+
* 5. Walk up from process.cwd() looking for the same marker
|
|
174
|
+
*
|
|
175
|
+
* @param {string} [platformHint] Optional compatibility hint for wrapper callers
|
|
176
|
+
* @returns {string} Absolute path to the plugin root
|
|
177
|
+
* @throws {PluginRootResolutionError} When all resolution levels fail
|
|
178
|
+
*/
|
|
179
|
+
export function resolvePluginRoot(platformHint) {
|
|
180
|
+
const tried = [];
|
|
181
|
+
|
|
182
|
+
// Level 1: native root identifies location only; platform detection is separate.
|
|
183
|
+
const nativeRoot = _envDirectory('PLUGIN_ROOT', tried);
|
|
184
|
+
if (nativeRoot) return nativeRoot;
|
|
185
|
+
|
|
186
|
+
// Levels 2-5: prefer the compatibility root matching explicit SO_PLATFORM.
|
|
187
|
+
for (const [, envName] of _orderedCompatibilityRoots(platformHint)) {
|
|
188
|
+
const compatibilityRoot = _envDirectory(envName, tried);
|
|
189
|
+
if (compatibilityRoot) return compatibilityRoot;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Level 6: walk up from this file's location
|
|
193
|
+
const thisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
194
|
+
const byImportMeta = _walkUp(thisDir);
|
|
195
|
+
if (byImportMeta) return byImportMeta;
|
|
196
|
+
tried.push(`walk from import.meta.url (${thisDir}) — no package.json{name:session-orchestrator} found`);
|
|
197
|
+
|
|
198
|
+
// Level 7: walk up from cwd
|
|
199
|
+
const byCwd = _walkUp(process.cwd());
|
|
200
|
+
if (byCwd) return byCwd;
|
|
201
|
+
tried.push(`walk from cwd (${process.cwd()}) — no package.json{name:session-orchestrator} found`);
|
|
202
|
+
|
|
203
|
+
throw new PluginRootResolutionError(
|
|
204
|
+
'Could not resolve session-orchestrator plugin root. ' +
|
|
205
|
+
'Set PLUGIN_ROOT, CLAUDE_PLUGIN_ROOT, CODEX_PLUGIN_ROOT, CURSOR_RULES_DIR, or PI_PLUGIN_ROOT ' +
|
|
206
|
+
'to the plugin directory, or ensure a package.json with name "session-orchestrator" exists in an ' +
|
|
207
|
+
'ancestor of the cwd or this script. Attempted: ' + tried.join('; '),
|
|
208
|
+
tried,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pre-dispatch-check.mjs — guard against untracked-file data loss when
|
|
3
|
+
* Claude Code's Agent tool runs with `isolation: "worktree"`.
|
|
4
|
+
*
|
|
5
|
+
* Background (issue #180): the Agent tool's built-in worktree isolation
|
|
6
|
+
* syncs directories back to the coordinator's working tree when the agent
|
|
7
|
+
* completes. If the coordinator holds untracked files that overlap with
|
|
8
|
+
* the agent's scope, the sync silently overwrites them. This helper lets
|
|
9
|
+
* the coordinator detect the overlap before dispatching, and either warn
|
|
10
|
+
* or hard-refuse per the caller's policy.
|
|
11
|
+
*
|
|
12
|
+
* Stdlib-only, cross-platform, no zx.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { execFileSync } from 'node:child_process';
|
|
16
|
+
import { pathMatchesPattern } from './hardening.mjs';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} OverlapResult
|
|
20
|
+
* @property {string[]} overlapping relative paths of untracked files overlapping the scope
|
|
21
|
+
* @property {string[]} untracked full list of untracked files discovered
|
|
22
|
+
* @property {'ok'|'warn'|'block'} decision
|
|
23
|
+
* @property {string} message human-readable summary
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Detect untracked coordinator files that overlap a planned agent scope.
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} opts
|
|
30
|
+
* @param {string[]} opts.scope agent file scope (paths, directories, or globs)
|
|
31
|
+
* @param {string} [opts.cwd] project root (defaults to process.cwd())
|
|
32
|
+
* @param {'warn'|'block'|'off'} [opts.mode='warn']
|
|
33
|
+
* - `warn` — return decision:'warn' when overlap found
|
|
34
|
+
* - `block` — return decision:'block' when overlap found
|
|
35
|
+
* - `off` — never flag (short-circuit, decision:'ok')
|
|
36
|
+
* @returns {OverlapResult}
|
|
37
|
+
*/
|
|
38
|
+
export function checkUntrackedOverlap({ scope, cwd = process.cwd(), mode = 'warn' } = {}) {
|
|
39
|
+
if (mode === 'off') {
|
|
40
|
+
return { overlapping: [], untracked: [], decision: 'ok', message: 'pre-dispatch check disabled' };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!Array.isArray(scope) || scope.length === 0) {
|
|
44
|
+
return { overlapping: [], untracked: [], decision: 'ok', message: 'empty scope — no overlap possible' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const untracked = listUntracked(cwd);
|
|
48
|
+
|
|
49
|
+
const overlapping = untracked.filter((file) => scope.some((pat) => pathMatchesPattern(file, pat)));
|
|
50
|
+
|
|
51
|
+
if (overlapping.length === 0) {
|
|
52
|
+
return { overlapping: [], untracked, decision: 'ok', message: 'no overlap between untracked files and agent scope' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const decision = mode === 'block' ? 'block' : 'warn';
|
|
56
|
+
const verb = decision === 'block' ? 'REFUSING' : 'WARNING';
|
|
57
|
+
const action =
|
|
58
|
+
decision === 'block'
|
|
59
|
+
? 'commit or stash them before dispatching, or rerun with mode=warn to acknowledge the risk'
|
|
60
|
+
: 'the Agent tool\'s worktree isolation may overwrite these on merge-back (issue #180)';
|
|
61
|
+
const message =
|
|
62
|
+
`${verb}: ${overlapping.length} untracked coordinator file(s) overlap the agent scope — ${action}\n` +
|
|
63
|
+
overlapping.map((f) => ` - ${f}`).join('\n');
|
|
64
|
+
|
|
65
|
+
return { overlapping, untracked, decision, message };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Run `git status --porcelain` and extract untracked files (prefix "??").
|
|
70
|
+
* Returns paths relative to `cwd`. Returns [] on any git failure.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} cwd
|
|
73
|
+
* @returns {string[]}
|
|
74
|
+
*/
|
|
75
|
+
export function listUntracked(cwd) {
|
|
76
|
+
let stdout;
|
|
77
|
+
try {
|
|
78
|
+
stdout = execFileSync('git', ['status', '--porcelain'], { cwd, encoding: 'utf8' });
|
|
79
|
+
} catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const files = [];
|
|
84
|
+
for (const rawLine of stdout.split('\n')) {
|
|
85
|
+
if (!rawLine.startsWith('?? ')) continue;
|
|
86
|
+
// Porcelain v1 wraps paths in quotes when they contain special chars. Strip them.
|
|
87
|
+
let rel = rawLine.slice(3);
|
|
88
|
+
if (rel.startsWith('"') && rel.endsWith('"')) {
|
|
89
|
+
rel = rel.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
90
|
+
}
|
|
91
|
+
// Directory entries end with `/` in porcelain output — expand to file list for fidelity.
|
|
92
|
+
if (rel.endsWith('/')) {
|
|
93
|
+
files.push(...expandDirectory(cwd, rel));
|
|
94
|
+
} else {
|
|
95
|
+
files.push(rel);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return files;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Enumerate untracked files under a directory via `git ls-files --others`.
|
|
103
|
+
* Runs in the given cwd and resolves the directory relative to that root.
|
|
104
|
+
* Returns repo-relative paths. Best-effort — swallows errors and falls back
|
|
105
|
+
* to the directory entry itself so callers still see something.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} cwd
|
|
108
|
+
* @param {string} relDir repo-relative directory with trailing `/`
|
|
109
|
+
* @returns {string[]}
|
|
110
|
+
*/
|
|
111
|
+
function expandDirectory(cwd, relDir) {
|
|
112
|
+
try {
|
|
113
|
+
const stdout = execFileSync(
|
|
114
|
+
'git',
|
|
115
|
+
['ls-files', '--others', '--exclude-standard', '--', relDir],
|
|
116
|
+
{ cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }
|
|
117
|
+
);
|
|
118
|
+
const lines = stdout
|
|
119
|
+
.split('\n')
|
|
120
|
+
.map((line) => line.trim())
|
|
121
|
+
.filter((line) => line.length > 0);
|
|
122
|
+
return lines.length > 0 ? lines : [relDir];
|
|
123
|
+
} catch {
|
|
124
|
+
return [relDir];
|
|
125
|
+
}
|
|
126
|
+
}
|