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,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workspace.mjs — workspace resolution and path-validation helpers.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from worktree.mjs (issue #287) so that workspace concerns
|
|
5
|
+
* are separate from git worktree lifecycle operations.
|
|
6
|
+
*
|
|
7
|
+
* Contains:
|
|
8
|
+
* - resolveWorkspaceRoot — canonical repo-root resolution via git + walk-up fallback
|
|
9
|
+
* - restoreCoordinatorCwd — chdir guard: detects worktree drift and restores CWD
|
|
10
|
+
* - validatePathInWorkspace — path-containment check with worktree-subtree exclusion
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'node:fs/promises';
|
|
14
|
+
import fsSync from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { $ } from 'zx';
|
|
17
|
+
import { listWorktrees } from './worktree.mjs';
|
|
18
|
+
|
|
19
|
+
// Do not spam stdout/stderr with git command echoes.
|
|
20
|
+
$.verbose = false;
|
|
21
|
+
$.quiet = true;
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// resolveWorkspaceRoot
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Return the canonical workspace root path — the directory containing the main
|
|
29
|
+
* .git/worktrees/ (i.e. the coordinator's tree), NOT any agent worktree.
|
|
30
|
+
*
|
|
31
|
+
* Resolution order:
|
|
32
|
+
* 1. `git rev-parse --git-common-dir` → parent directory (this returns the
|
|
33
|
+
* SAME path whether called from the main tree or any worktree, unlike
|
|
34
|
+
* --show-toplevel which returns the current worktree's tree).
|
|
35
|
+
* 2. Fallback: walk up from process.cwd() looking for `.git` (directory or file).
|
|
36
|
+
* 3. If both fail → throw Error with message starting "resolveWorkspaceRoot:".
|
|
37
|
+
*
|
|
38
|
+
* Does NOT change process.cwd(). Pure resolution.
|
|
39
|
+
*
|
|
40
|
+
* @returns {Promise<string>} absolute path
|
|
41
|
+
*/
|
|
42
|
+
export async function resolveWorkspaceRoot() {
|
|
43
|
+
const cwd = process.cwd();
|
|
44
|
+
const git = $({ cwd });
|
|
45
|
+
|
|
46
|
+
// Strategy 1: git rev-parse --git-common-dir
|
|
47
|
+
try {
|
|
48
|
+
const result = await git`git rev-parse --git-common-dir`;
|
|
49
|
+
const commonDir = result.stdout.trim();
|
|
50
|
+
if (commonDir) {
|
|
51
|
+
// May be relative (when inside a linked worktree) or absolute.
|
|
52
|
+
const absCommonDir = path.resolve(cwd, commonDir);
|
|
53
|
+
return path.dirname(absCommonDir);
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// Fall through to walk-up fallback.
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Strategy 2: walk up from cwd looking for .git (directory or worktree link file).
|
|
60
|
+
let p = cwd;
|
|
61
|
+
let levels = 0;
|
|
62
|
+
while (levels < 20) {
|
|
63
|
+
const gitEntry = path.join(p, '.git');
|
|
64
|
+
if (fsSync.existsSync(gitEntry)) {
|
|
65
|
+
// Check if .git is a file (worktree link) or a directory.
|
|
66
|
+
try {
|
|
67
|
+
const stat = await fs.stat(gitEntry);
|
|
68
|
+
if (stat.isDirectory()) {
|
|
69
|
+
return p;
|
|
70
|
+
}
|
|
71
|
+
// .git is a file — worktree link. Read it to find gitdir.
|
|
72
|
+
const content = await fs.readFile(gitEntry, 'utf8');
|
|
73
|
+
const match = content.match(/^gitdir:\s*(.+)$/m);
|
|
74
|
+
if (match) {
|
|
75
|
+
// gitdir points to e.g. /repo/.git/worktrees/agent1
|
|
76
|
+
// common dir would be /repo/.git — go up to /repo
|
|
77
|
+
const gitdir = path.resolve(p, match[1].trim());
|
|
78
|
+
// Walk up to find the real .git directory (not a worktrees subdir).
|
|
79
|
+
let candidate = gitdir;
|
|
80
|
+
while (path.dirname(candidate) !== candidate) {
|
|
81
|
+
const base = path.basename(candidate);
|
|
82
|
+
const parentBase = path.basename(path.dirname(candidate));
|
|
83
|
+
if (base !== 'worktrees' && parentBase !== 'worktrees' && fsSync.existsSync(path.join(candidate, 'config'))) {
|
|
84
|
+
return path.dirname(candidate);
|
|
85
|
+
}
|
|
86
|
+
candidate = path.dirname(candidate);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
// Could not stat or read — treat as found anyway.
|
|
91
|
+
return p;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const parent = path.dirname(p);
|
|
95
|
+
if (parent === p) break; // filesystem root
|
|
96
|
+
p = parent;
|
|
97
|
+
levels++;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
throw new Error(`resolveWorkspaceRoot: could not locate workspace root from '${cwd}'`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// restoreCoordinatorCwd
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Check whether CWD is inside a worktree (anything under .git/worktrees/ or a
|
|
109
|
+
* registered linked-worktree path), and if so, chdir back to the workspace root.
|
|
110
|
+
*
|
|
111
|
+
* Idempotent — when CWD already equals the workspace root, returns without
|
|
112
|
+
* calling process.chdir().
|
|
113
|
+
*
|
|
114
|
+
* @returns {Promise<{restored: boolean, from: string|null, to: string}>}
|
|
115
|
+
* restored=true when chdir happened; from is the previous CWD (only when restored);
|
|
116
|
+
* to is always the workspace root.
|
|
117
|
+
*/
|
|
118
|
+
export async function restoreCoordinatorCwd() {
|
|
119
|
+
const cwd = process.cwd();
|
|
120
|
+
const root = await resolveWorkspaceRoot();
|
|
121
|
+
|
|
122
|
+
// Normalize both for comparison (handles trailing slash differences, etc.)
|
|
123
|
+
const normCwd = path.resolve(cwd);
|
|
124
|
+
const normRoot = path.resolve(root);
|
|
125
|
+
|
|
126
|
+
if (normCwd === normRoot) {
|
|
127
|
+
return { restored: false, from: null, to: normRoot };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check if CWD is inside a worktree by listing registered worktrees.
|
|
131
|
+
let insideWorktree = false;
|
|
132
|
+
try {
|
|
133
|
+
const worktrees = await listWorktrees();
|
|
134
|
+
for (const wt of worktrees) {
|
|
135
|
+
const wtResolved = path.resolve(wt.path);
|
|
136
|
+
if (wtResolved === normRoot) continue; // skip main worktree
|
|
137
|
+
// CWD is inside this linked worktree if it equals or is a descendant.
|
|
138
|
+
const rel = path.relative(wtResolved, normCwd);
|
|
139
|
+
if (rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel))) {
|
|
140
|
+
insideWorktree = true;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
// If listing fails, fall back to checking if CWD differs from root.
|
|
146
|
+
// Any divergence from root is treated as suspect when git-common-dir already
|
|
147
|
+
// resolved to a different root, so we still restore.
|
|
148
|
+
insideWorktree = true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (insideWorktree) {
|
|
152
|
+
process.chdir(normRoot);
|
|
153
|
+
return { restored: true, from: normCwd, to: normRoot };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return { restored: false, from: null, to: normRoot };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// validatePathInWorkspace
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Return true IFF filePath resolves to a location inside the workspace root AND
|
|
165
|
+
* NOT inside any .claude/worktrees/* (or .codex/worktrees, .cursor/worktrees) subtree.
|
|
166
|
+
*
|
|
167
|
+
* Accepts absolute or relative paths. Relative paths resolve against process.cwd().
|
|
168
|
+
* Does NOT touch the filesystem beyond reading the workspace root.
|
|
169
|
+
*
|
|
170
|
+
* @param {string} filePath
|
|
171
|
+
* @param {string} [workspaceRoot] optional — if omitted, resolves via resolveWorkspaceRoot()
|
|
172
|
+
* @returns {Promise<boolean>}
|
|
173
|
+
*/
|
|
174
|
+
export async function validatePathInWorkspace(filePath, workspaceRoot) {
|
|
175
|
+
const root = workspaceRoot !== undefined ? workspaceRoot : await resolveWorkspaceRoot();
|
|
176
|
+
const absRoot = path.resolve(root);
|
|
177
|
+
const absFile = path.resolve(process.cwd(), filePath);
|
|
178
|
+
|
|
179
|
+
// Must be inside workspace root (descendant — not equal to root itself).
|
|
180
|
+
const relToRoot = path.relative(absRoot, absFile);
|
|
181
|
+
const isInside = relToRoot !== '' && !relToRoot.startsWith('..') && !path.isAbsolute(relToRoot);
|
|
182
|
+
if (!isInside) return false;
|
|
183
|
+
|
|
184
|
+
// Normalize separator to forward-slash for cross-platform startsWith checks.
|
|
185
|
+
const relNorm = relToRoot.split(path.sep).join('/');
|
|
186
|
+
|
|
187
|
+
// Reject paths inside known worktree subtrees.
|
|
188
|
+
const worktreeSubtrees = [
|
|
189
|
+
'.claude/worktrees/',
|
|
190
|
+
'.codex/worktrees/',
|
|
191
|
+
'.cursor/worktrees/',
|
|
192
|
+
];
|
|
193
|
+
for (const subtree of worktreeSubtrees) {
|
|
194
|
+
if (relNorm.startsWith(subtree)) return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree/constants.mjs — shared constants for the worktree module.
|
|
3
|
+
*
|
|
4
|
+
* Leaf module: imports only stdlib and zx. No imports from other
|
|
5
|
+
* worktree/* submodules.
|
|
6
|
+
*
|
|
7
|
+
* Side effect: configures zx to suppress git command echoes on load.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { $ } from 'zx';
|
|
11
|
+
|
|
12
|
+
// Do not spam stdout/stderr with git command echoes.
|
|
13
|
+
$.verbose = false;
|
|
14
|
+
$.quiet = true;
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Constants
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default list of top-level directory names to exclude from new worktrees.
|
|
22
|
+
* Issue #192 — skip build artifacts to reduce RAM spikes on memory-constrained
|
|
23
|
+
* sessions. Can be overridden per-call via options.excludePatterns or via
|
|
24
|
+
* Session Config `worktree-exclude`.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_EXCLUDE_PATTERNS = [
|
|
27
|
+
'node_modules', 'dist', 'build', '.next', '.nuxt',
|
|
28
|
+
'coverage', '.cache', '.turbo', '.vercel', 'out',
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Relative path (from repo root) where worktree meta JSON files are stored.
|
|
33
|
+
* Each file is named `<suffix>.json`.
|
|
34
|
+
*/
|
|
35
|
+
export const WORKTREE_META_DIR = '.orchestrator/tmp/worktree-meta';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree/index.mjs — re-export hub for all public worktree symbols.
|
|
3
|
+
*
|
|
4
|
+
* Public API (7 exports):
|
|
5
|
+
* WORKTREE_META_DIR (constants)
|
|
6
|
+
* metaPathFor (meta)
|
|
7
|
+
* listWorktrees (listing)
|
|
8
|
+
* applyWorktreeExcludes (listing)
|
|
9
|
+
* createWorktree (lifecycle)
|
|
10
|
+
* removeWorktree (lifecycle)
|
|
11
|
+
* cleanupAllWorktrees (lifecycle)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export { WORKTREE_META_DIR } from './constants.mjs';
|
|
15
|
+
export { metaPathFor } from './meta.mjs';
|
|
16
|
+
export { listWorktrees, applyWorktreeExcludes } from './listing.mjs';
|
|
17
|
+
export { createWorktree, removeWorktree, cleanupAllWorktrees } from './lifecycle.mjs';
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree/lifecycle.mjs — git worktree lifecycle operations.
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* createWorktree(suffix, baseRef, options) — create a git worktree
|
|
6
|
+
* removeWorktree(wtPath) — remove a worktree + branch
|
|
7
|
+
* cleanupAllWorktrees() — remove all so-worktree-* worktrees
|
|
8
|
+
*
|
|
9
|
+
* Internal:
|
|
10
|
+
* _exists(p) — path existence check
|
|
11
|
+
* _worktreeInfo(suffix) — compute branch name + tmp path
|
|
12
|
+
*
|
|
13
|
+
* Import DAG (no cycles):
|
|
14
|
+
* lifecycle → listing (cleanupAllWorktrees calls listWorktrees)
|
|
15
|
+
* lifecycle → meta (_writeWorktreeMeta)
|
|
16
|
+
* lifecycle → constants (WORKTREE_META_DIR, DEFAULT_EXCLUDE_PATTERNS)
|
|
17
|
+
* lifecycle → config.mjs (readConfigFile, parseSessionConfig)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { $, nothrow, ProcessOutput } from 'zx';
|
|
21
|
+
import fs from 'node:fs/promises';
|
|
22
|
+
import os from 'node:os';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import { readConfigFile, parseSessionConfig } from '../config.mjs';
|
|
25
|
+
import { WORKTREE_META_DIR, DEFAULT_EXCLUDE_PATTERNS } from './constants.mjs';
|
|
26
|
+
import { _writeWorktreeMeta } from './meta.mjs';
|
|
27
|
+
import { listWorktrees, applyWorktreeExcludes } from './listing.mjs';
|
|
28
|
+
import { isPathInside } from '../path-utils.mjs';
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Internal helpers
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Return true if the given path exists on disk (file or directory).
|
|
36
|
+
* Uses try/catch — no TOCTOU widening.
|
|
37
|
+
* @param {string} p
|
|
38
|
+
* @returns {Promise<boolean>}
|
|
39
|
+
*/
|
|
40
|
+
async function _exists(p) {
|
|
41
|
+
try {
|
|
42
|
+
await fs.access(p);
|
|
43
|
+
return true;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compute the standard branch name and tmp path for a given suffix.
|
|
51
|
+
* @param {string} suffix
|
|
52
|
+
* @returns {{ branch: string, wtPath: string }}
|
|
53
|
+
*/
|
|
54
|
+
function _worktreeInfo(suffix) {
|
|
55
|
+
const branch = `so-worktree-${suffix}`;
|
|
56
|
+
const wtPath = path.join(os.tmpdir(), 'so-worktrees', branch);
|
|
57
|
+
return { branch, wtPath };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// validateWorkspacePath
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Validate that a computed worktree path is contained within `root`.
|
|
66
|
+
*
|
|
67
|
+
* PURE function — no I/O, no side effects. Operates on string inputs only.
|
|
68
|
+
* Detects: `..` traversal, absolute paths outside root, empty/identical paths,
|
|
69
|
+
* UNC paths on Windows, and null-byte injection.
|
|
70
|
+
* Does NOT detect symlink-escape — symlinks are resolved by the filesystem at
|
|
71
|
+
* write time, not by this helper. Callers that require symlink-safety must run
|
|
72
|
+
* `fs.realpathSync(computed)` first and pass the resolved path to this helper.
|
|
73
|
+
*
|
|
74
|
+
* Delegates CWE-23 hardening to `isPathInside` from `../path-utils.mjs`, which
|
|
75
|
+
* is the single source of truth for path-traversal safety in this plugin.
|
|
76
|
+
*
|
|
77
|
+
* @param {string} computed The path that will be used as the worktree.
|
|
78
|
+
* @param {string} root The allowed root containing legitimate worktrees.
|
|
79
|
+
* @returns {boolean} true when `computed` is a strict descendant of `root`.
|
|
80
|
+
* @throws {TypeError} when either argument is not a non-empty string.
|
|
81
|
+
*
|
|
82
|
+
* Used by ADR-364 thin-slice substrate; no production call-sites in this slice.
|
|
83
|
+
* Will be wired into createWorktree in a follow-up that introduces user-controlled
|
|
84
|
+
* worktree paths.
|
|
85
|
+
*/
|
|
86
|
+
export function validateWorkspacePath(computed, root) {
|
|
87
|
+
// isPathInside already validates non-empty strings and throws TypeError on
|
|
88
|
+
// invalid input — propagate those errors directly to the caller.
|
|
89
|
+
return isPathInside(computed, root);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// createWorktree
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create a git worktree for isolated agent work.
|
|
98
|
+
*
|
|
99
|
+
* After a successful `git worktree add`, persists a meta JSON file at
|
|
100
|
+
* `.orchestrator/tmp/worktree-meta/<suffix>.json` for the base-ref freshness
|
|
101
|
+
* guard (issue #195). Meta write failures are non-fatal — they emit a warning
|
|
102
|
+
* to stderr but never block worktree creation.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} suffix Unique suffix for the branch (e.g. "wave2-agent1").
|
|
105
|
+
* @param {string} [baseRef="HEAD"] Git ref to base the worktree on.
|
|
106
|
+
* @param {{ excludePatterns?: string[] }} [options={}]
|
|
107
|
+
* Optional configuration.
|
|
108
|
+
* - `excludePatterns`: list of top-level directory names to remove after
|
|
109
|
+
* worktree creation. Overrides the Session Config `worktree-exclude` value
|
|
110
|
+
* and the hardcoded default. Pass `[]` to disable all exclusions.
|
|
111
|
+
* @returns {Promise<string>} Absolute path to the created worktree.
|
|
112
|
+
*/
|
|
113
|
+
export async function createWorktree(suffix, baseRef = 'HEAD', options = {}) {
|
|
114
|
+
const { branch, wtPath } = _worktreeInfo(suffix);
|
|
115
|
+
// Capture cwd at call time so git commands run in the correct repo even if
|
|
116
|
+
// zx's module-level default cwd differs (e.g. during tests with chdir).
|
|
117
|
+
const cwd = process.cwd();
|
|
118
|
+
const git = $({ cwd });
|
|
119
|
+
|
|
120
|
+
// Resolve baseSha BEFORE creating the worktree so we capture the exact sha used.
|
|
121
|
+
let baseSha = null;
|
|
122
|
+
try {
|
|
123
|
+
const result = await git`git rev-parse ${baseRef}`;
|
|
124
|
+
baseSha = result.stdout.trim();
|
|
125
|
+
} catch {
|
|
126
|
+
// Non-fatal: freshness guard will treat missing baseSha as no-meta.
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Ensure parent directory exists — path.join + recursive mkdir is cross-platform.
|
|
130
|
+
await fs.mkdir(path.dirname(wtPath), { recursive: true });
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await git`git worktree add -b ${branch} ${wtPath} ${baseRef}`;
|
|
134
|
+
} catch {
|
|
135
|
+
// Branch or worktree may already exist from a previous failed run — force-cleanup and retry.
|
|
136
|
+
await nothrow(git`git worktree remove ${wtPath} --force`);
|
|
137
|
+
await nothrow(git`git branch -D ${branch}`);
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await git`git worktree add -b ${branch} ${wtPath} ${baseRef}`;
|
|
141
|
+
} catch (secondErr) {
|
|
142
|
+
const msg = secondErr instanceof ProcessOutput ? secondErr.stderr.trim() : String(secondErr);
|
|
143
|
+
throw new Error(
|
|
144
|
+
`createWorktree: failed to create worktree for branch '${branch}' at '${wtPath}' (base: ${baseRef}). ` +
|
|
145
|
+
`git error: ${msg}`,
|
|
146
|
+
{ cause: secondErr }
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// -------------------------------------------------------------------------
|
|
152
|
+
// Exclude build artifacts (issue #192)
|
|
153
|
+
// -------------------------------------------------------------------------
|
|
154
|
+
|
|
155
|
+
// Resolve exclude patterns: explicit options > Session Config > hardcoded default.
|
|
156
|
+
let excludePatterns;
|
|
157
|
+
if (options.excludePatterns !== undefined) {
|
|
158
|
+
excludePatterns = options.excludePatterns;
|
|
159
|
+
} else {
|
|
160
|
+
// Attempt to read from Session Config; fall back to hardcoded default.
|
|
161
|
+
// Semantics:
|
|
162
|
+
// key absent (undefined) → fall back to DEFAULT_EXCLUDE_PATTERNS
|
|
163
|
+
// key explicit null → empty array (disable all excludes)
|
|
164
|
+
// key is an array → use that array
|
|
165
|
+
try {
|
|
166
|
+
const content = await readConfigFile(process.cwd());
|
|
167
|
+
const config = parseSessionConfig(content);
|
|
168
|
+
const fromConfig = config['worktree-exclude'];
|
|
169
|
+
if (fromConfig === null) {
|
|
170
|
+
// Explicit `worktree-exclude: null` (YAML null) — user opted out of excludes.
|
|
171
|
+
excludePatterns = [];
|
|
172
|
+
} else if (Array.isArray(fromConfig)) {
|
|
173
|
+
excludePatterns = fromConfig;
|
|
174
|
+
} else {
|
|
175
|
+
// Key missing (undefined) or unrecognised value — use hardcoded default.
|
|
176
|
+
excludePatterns = DEFAULT_EXCLUDE_PATTERNS;
|
|
177
|
+
}
|
|
178
|
+
} catch {
|
|
179
|
+
excludePatterns = DEFAULT_EXCLUDE_PATTERNS;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
await applyWorktreeExcludes(wtPath, excludePatterns);
|
|
184
|
+
|
|
185
|
+
// Persist meta for freshness guard (issue #195). Non-fatal on failure.
|
|
186
|
+
await _writeWorktreeMeta(suffix, { branch, wtPath, baseRef, baseSha, repoRoot: cwd }).catch((err) => {
|
|
187
|
+
console.warn(`createWorktree: meta write failed for '${suffix}' — freshness guard will be skipped: ${err.message}`);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return wtPath;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// removeWorktree
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Remove a worktree and its associated branch.
|
|
199
|
+
* Emits a warning to stderr (does not throw) if uncommitted changes exist.
|
|
200
|
+
* Also cleans up the meta file written by createWorktree (issue #195).
|
|
201
|
+
* Always resolves — never throws.
|
|
202
|
+
*
|
|
203
|
+
* @param {string} wtPath Absolute path to the worktree to remove.
|
|
204
|
+
* @returns {Promise<void>}
|
|
205
|
+
*/
|
|
206
|
+
export async function removeWorktree(wtPath) {
|
|
207
|
+
// No-op if path does not exist.
|
|
208
|
+
if (!(await _exists(wtPath))) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Capture cwd at call time for the same reason as createWorktree.
|
|
213
|
+
const cwd = process.cwd();
|
|
214
|
+
const git = $({ cwd });
|
|
215
|
+
|
|
216
|
+
// Warn about uncommitted changes without throwing.
|
|
217
|
+
try {
|
|
218
|
+
const status = await git`git -C ${wtPath} status --porcelain`;
|
|
219
|
+
if (status.stdout.trim().length > 0) {
|
|
220
|
+
console.error(`WARNING: worktree at ${wtPath} has uncommitted changes`);
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
// Ignore — worktree may already be partially detached.
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Resolve the branch name, but only accept so-worktree-* names.
|
|
227
|
+
let branch = '';
|
|
228
|
+
try {
|
|
229
|
+
const ref = await git`git -C ${wtPath} rev-parse --abbrev-ref HEAD`;
|
|
230
|
+
const candidate = ref.stdout.trim();
|
|
231
|
+
if (/^so-worktree-/.test(candidate)) {
|
|
232
|
+
branch = candidate;
|
|
233
|
+
}
|
|
234
|
+
} catch {
|
|
235
|
+
// Ignore — best-effort.
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Remove worktree (force so it works even with dirty state).
|
|
239
|
+
await nothrow(git`git worktree remove ${wtPath} --force`);
|
|
240
|
+
|
|
241
|
+
// Clean up the temporary branch (best-effort).
|
|
242
|
+
if (branch) {
|
|
243
|
+
await nothrow(git`git branch -D ${branch}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Clean up meta file (issue #195). Derive suffix from branch name.
|
|
247
|
+
// branch is "so-worktree-<suffix>"; if branch is empty, fall back to wtPath basename.
|
|
248
|
+
const suffix = branch
|
|
249
|
+
? branch.replace(/^so-worktree-/, '')
|
|
250
|
+
: path.basename(wtPath).replace(/^so-worktree-/, '');
|
|
251
|
+
|
|
252
|
+
if (suffix) {
|
|
253
|
+
const metaPath = path.join(cwd, WORKTREE_META_DIR, `${suffix}.json`);
|
|
254
|
+
await fs.unlink(metaPath).catch((err) => {
|
|
255
|
+
if (err.code !== 'ENOENT') {
|
|
256
|
+
console.warn(`removeWorktree: meta cleanup failed for '${suffix}': ${err.message}`);
|
|
257
|
+
}
|
|
258
|
+
// ENOENT — already gone or never written; silently ignore.
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// cleanupAllWorktrees
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Remove all session-orchestrator worktrees (branches matching so-worktree-*),
|
|
269
|
+
* then prune stale worktree references.
|
|
270
|
+
* Always resolves — never throws.
|
|
271
|
+
*
|
|
272
|
+
* @returns {Promise<void>}
|
|
273
|
+
*/
|
|
274
|
+
export async function cleanupAllWorktrees() {
|
|
275
|
+
try {
|
|
276
|
+
const worktrees = await listWorktrees();
|
|
277
|
+
const soWorktrees = worktrees.filter(wt => /^so-worktree-/.test(wt.branch));
|
|
278
|
+
|
|
279
|
+
for (const wt of soWorktrees) {
|
|
280
|
+
await removeWorktree(wt.path);
|
|
281
|
+
}
|
|
282
|
+
} catch {
|
|
283
|
+
// Best-effort — swallow any unexpected errors.
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
await nothrow($({ cwd: process.cwd() })`git worktree prune`);
|
|
287
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree/listing.mjs — list and filter worktrees.
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* listWorktrees() — list all git worktrees
|
|
6
|
+
* applyWorktreeExcludes(wtPath, patterns) — remove top-level dirs from worktree
|
|
7
|
+
*
|
|
8
|
+
* No imports from lifecycle.mjs — this module is intentionally a leaf of the
|
|
9
|
+
* lifecycle import chain (lifecycle imports listing, not the reverse).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { $ as defaultDollar } from 'zx';
|
|
13
|
+
import fs from 'node:fs/promises';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// listWorktrees
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* List all git worktrees in the current repository.
|
|
22
|
+
*
|
|
23
|
+
* @param {object} [opts]
|
|
24
|
+
* @param {Function} [opts.$] Optional zx-compatible executor. Defaults to real zx.$.
|
|
25
|
+
* Tests pass a mock here to avoid vi.mock('zx') under fork pool.
|
|
26
|
+
* @returns {Promise<Array<{path: string, branch: string, head: string}>>}
|
|
27
|
+
* Array of worktree descriptors; empty array if none or on parse error.
|
|
28
|
+
*/
|
|
29
|
+
export async function listWorktrees(opts = {}) {
|
|
30
|
+
const dollar = opts.$ ?? defaultDollar;
|
|
31
|
+
const git = dollar({ cwd: process.cwd() });
|
|
32
|
+
let output;
|
|
33
|
+
try {
|
|
34
|
+
const result = await git`git worktree list --porcelain`;
|
|
35
|
+
output = result.stdout;
|
|
36
|
+
} catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const worktrees = [];
|
|
41
|
+
let current = null;
|
|
42
|
+
|
|
43
|
+
for (const rawLine of output.split('\n')) {
|
|
44
|
+
const line = rawLine.trimEnd();
|
|
45
|
+
|
|
46
|
+
if (line.startsWith('worktree ')) {
|
|
47
|
+
// Each new 'worktree' line starts a new record.
|
|
48
|
+
if (current !== null) {
|
|
49
|
+
worktrees.push(current);
|
|
50
|
+
}
|
|
51
|
+
current = { path: line.slice('worktree '.length), branch: '', head: '' };
|
|
52
|
+
} else if (line.startsWith('HEAD ') && current !== null) {
|
|
53
|
+
current.head = line.slice('HEAD '.length);
|
|
54
|
+
} else if (line.startsWith('branch ') && current !== null) {
|
|
55
|
+
// Branch is stored as refs/heads/<name> — strip the prefix.
|
|
56
|
+
const ref = line.slice('branch '.length);
|
|
57
|
+
current.branch = ref.replace(/^refs\/heads\//, '');
|
|
58
|
+
} else if (line === '' && current !== null) {
|
|
59
|
+
// Blank line separates records in --porcelain output.
|
|
60
|
+
worktrees.push(current);
|
|
61
|
+
current = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Flush any trailing record (no trailing blank line at EOF).
|
|
66
|
+
if (current !== null) {
|
|
67
|
+
worktrees.push(current);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return worktrees;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// applyWorktreeExcludes
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Remove top-level directories matching `patterns` from a worktree path.
|
|
79
|
+
* Pure fs operation — no git, no zx. Exported for unit testing (issue #192).
|
|
80
|
+
*
|
|
81
|
+
* @param {string} wtPath Absolute path to the worktree root.
|
|
82
|
+
* @param {string[]} patterns Top-level directory names to remove.
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
*/
|
|
85
|
+
export async function applyWorktreeExcludes(wtPath, patterns) {
|
|
86
|
+
if (!patterns || patterns.length === 0) return;
|
|
87
|
+
|
|
88
|
+
let anyRemoved = false;
|
|
89
|
+
let allFailed = true;
|
|
90
|
+
|
|
91
|
+
for (const pattern of patterns) {
|
|
92
|
+
const targetPath = path.join(wtPath, pattern);
|
|
93
|
+
let dirExists;
|
|
94
|
+
try {
|
|
95
|
+
const stat = await fs.stat(targetPath);
|
|
96
|
+
dirExists = stat.isDirectory();
|
|
97
|
+
} catch {
|
|
98
|
+
// Pattern does not exist — silently skip.
|
|
99
|
+
allFailed = false; // not a failure, just absent
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (dirExists) {
|
|
104
|
+
try {
|
|
105
|
+
await fs.rm(targetPath, { recursive: true, force: true });
|
|
106
|
+
process.stderr.write(`[worktree] excluded: ${pattern}\n`);
|
|
107
|
+
anyRemoved = true;
|
|
108
|
+
allFailed = false;
|
|
109
|
+
} catch {
|
|
110
|
+
// Individual removal failure — continue with remaining patterns.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (anyRemoved === false && allFailed) {
|
|
116
|
+
process.stderr.write(`[worktree] WARNING: all pattern removals failed for worktree at ${wtPath}\n`);
|
|
117
|
+
}
|
|
118
|
+
}
|