@event4u/agent-config 16.4.0 → 17.0.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/skills/install-agent-config/SKILL.md +4 -4
- package/CHANGELOG.md +483 -669
- package/CONTRIBUTING.md +47 -0
- package/README.md +25 -25
- package/dist/agent-src/commands/{design.md → agent-design.md} +11 -7
- package/dist/agent-src/commands/agents/init.md +66 -3
- package/dist/agent-src/commands/agents/user/init.md +3 -0
- package/dist/agent-src/commands/{optimize/deep.md → analyze/and-optimize.md} +72 -33
- package/dist/agent-src/commands/analyze/inbox.md +168 -37
- package/dist/agent-src/commands/analyze/pr.md +144 -0
- package/dist/agent-src/commands/analyze/{repo.md → reference.md} +85 -14
- package/dist/agent-src/commands/analyze/roadmap-repos.md +19 -10
- package/dist/agent-src/commands/analyze.md +28 -12
- package/dist/agent-src/commands/audit.md +115 -0
- package/dist/agent-src/commands/brand/review.md +13 -2
- package/dist/agent-src/commands/brand/tokens.md +3 -3
- package/dist/agent-src/commands/brand/voice.md +8 -5
- package/dist/agent-src/commands/brand.md +2 -2
- package/dist/agent-src/commands/challenge-me/closure.md +25 -0
- package/dist/agent-src/commands/design-system/import.md +8 -3
- package/dist/agent-src/commands/design-system/sync.md +98 -0
- package/dist/agent-src/commands/design-system.md +6 -1
- package/dist/agent-src/commands/evals/analyze.json +44 -0
- package/dist/agent-src/commands/evals/audit.json +12 -0
- package/dist/agent-src/commands/evals/retro.json +12 -0
- package/dist/agent-src/commands/fix/ci.md +10 -8
- package/dist/agent-src/commands/ghostwriter/write.md +22 -0
- package/dist/agent-src/commands/memory/learn-low-impact.md +70 -83
- package/dist/agent-src/commands/memory/propose.md +12 -0
- package/dist/agent-src/commands/memory.md +1 -1
- package/dist/agent-src/commands/optimize.md +4 -3
- package/dist/agent-src/commands/post-as/me.md +18 -0
- package/dist/agent-src/commands/pr/create.md +17 -342
- package/dist/agent-src/commands/pr/merge.md +129 -368
- package/dist/agent-src/commands/retro.md +121 -0
- package/dist/agent-src/commands/review/routing.md +1 -1
- package/dist/agent-src/commands/roadmap/next.md +4 -1
- package/dist/agent-src/commands/roadmap/process-full.md +46 -333
- package/dist/agent-src/commands/roadmap/triage-parked.md +6 -2
- package/dist/agent-src/commands/team-knowledge/consolidate.md +2 -2
- package/dist/agent-src/commands/team-knowledge.md +1 -1
- package/dist/agent-src/commands/threat-model.md +1 -1
- package/dist/agent-src/commands/upstream-contribute.md +91 -101
- package/dist/agent-src/commands/worktree/cleanup.md +18 -11
- package/dist/agent-src/contexts/contracts/consumer-agents-md-guide.md +54 -0
- package/dist/agent-src/contexts/execution/analysis-loop.md +137 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +3 -3
- package/dist/agent-src/contexts/execution/decision-closure-execution.md +1 -1
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +6 -0
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +11 -4
- package/dist/agent-src/contexts/execution/pr-create-branches-content.md +250 -0
- package/dist/agent-src/contexts/execution/pr-create-branches-freshness.md +150 -0
- package/dist/agent-src/contexts/execution/pr-merge-queue.md +287 -0
- package/dist/agent-src/contexts/execution/pr-merge-sync.md +276 -0
- package/dist/agent-src/contexts/execution/pr-merge-trust-boundary.md +69 -0
- package/dist/agent-src/contexts/execution/process-full-all.md +233 -0
- package/dist/agent-src/contexts/execution/process-full-delivery.md +231 -0
- package/dist/agent-src/contexts/execution/roadmap-execution-contract-branches.md +42 -0
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +1 -35
- package/dist/agent-src/contexts/execution/roadmap-process-loop-close.md +235 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop-prescan.md +216 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop-run.md +253 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop-set.md +212 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +25 -864
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +14 -2
- package/dist/agent-src/contexts/execution/subagent-steering.md +65 -8
- package/dist/agent-src/contexts/model-recommendations.md +5 -5
- package/dist/agent-src/contexts/subagent-configuration.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/api-cost-levers.md +4 -3
- package/dist/agent-src/guidelines/agent-infra/model-recommendation.md +11 -5
- package/dist/agent-src/guidelines/agent-infra/rule-body-migration-inventory.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +47 -24
- package/dist/agent-src/guidelines/design-antipatterns.md +6 -1
- package/dist/agent-src/guidelines/design-asset-discipline.md +17 -0
- package/dist/agent-src/guidelines/design-fidelity-routing.md +1 -0
- package/dist/agent-src/guidelines/php/api-design.md +10 -1
- package/dist/agent-src/rules/brand-source-of-truth.md +7 -6
- package/dist/agent-src/rules/code-provenance.md +1 -1
- package/dist/agent-src/rules/context-hygiene.md +0 -1
- package/dist/agent-src/rules/design-fidelity.md +12 -13
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
- package/dist/agent-src/rules/fix-what-you-see.md +1 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +14 -15
- package/dist/agent-src/rules/missing-skill-recovery.md +12 -11
- package/dist/agent-src/rules/neighbour-precedence.md +1 -1
- package/dist/agent-src/rules/recurring-criticism.md +1 -1
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -2
- package/dist/agent-src/rules/self-repair-loop.md +2 -2
- package/dist/agent-src/rules/token-budget-discipline.md +2 -2
- package/dist/agent-src/scripts/dashboard_mode.ts +4 -1
- package/dist/agent-src/scripts/roadmap_age.ts +175 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +10 -2
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +3 -3
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +2 -0
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- package/dist/agent-src/skills/agent-security-review/SKILL.md +2 -2
- package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +12 -4
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +7 -0
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +11 -1
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +7 -0
- package/dist/agent-src/skills/ai-council/references/execution-modes.md +7 -0
- package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +19 -0
- package/dist/agent-src/skills/ai-council/references/procedure.md +7 -0
- package/dist/agent-src/skills/api-design/SKILL.md +2 -2
- package/dist/agent-src/skills/api-endpoint/SKILL.md +2 -2
- package/dist/agent-src/skills/authz-review/SKILL.md +5 -3
- package/dist/agent-src/skills/blade-ui/SKILL.md +2 -2
- package/dist/agent-src/skills/brand/SKILL.md +7 -3
- package/dist/agent-src/skills/brand-asset-generation/SKILL.md +13 -6
- package/dist/agent-src/skills/brand-audit/SKILL.md +2 -1
- package/dist/agent-src/skills/brand-identity/SKILL.md +5 -4
- package/dist/agent-src/skills/brand-strategy/SKILL.md +5 -4
- package/dist/agent-src/skills/brand-to-tokens/SKILL.md +18 -39
- package/dist/agent-src/skills/brand-to-tokens/templates/marp-brand-deck.md.example +2 -2
- package/dist/agent-src/skills/brand-to-tokens/templates/reveal-brand-deck.yaml +2 -2
- package/dist/agent-src/skills/canvas-design/SKILL.md +16 -4
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/composer-packages/SKILL.md +18 -0
- package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
- package/dist/agent-src/skills/content-funnel-design/SKILL.md +8 -1
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/context-document/SKILL.md +2 -2
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +6 -6
- package/dist/agent-src/skills/corpus-grounding/scripts/decision_engine.ts +13 -11
- package/dist/agent-src/skills/corpus-grounding/scripts/ground.mjs +1706 -0
- package/dist/agent-src/skills/corpus-grounding/scripts/schema_validator.ts +7 -1
- package/dist/agent-src/skills/dashboard-design/SKILL.md +1 -1
- package/dist/agent-src/skills/data-flow-mapper/SKILL.md +5 -2
- package/dist/agent-src/skills/database/SKILL.md +5 -4
- package/dist/agent-src/skills/decision-record/references/weighted-matrix.md +12 -0
- package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +1 -1
- package/dist/agent-src/skills/dependency-upgrade/SKILL.md +61 -11
- package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
- package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
- package/dist/agent-src/skills/design-intelligence/data/landing.csv +1 -1
- package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +10 -2
- package/dist/agent-src/skills/design-intelligence/references/design-languages.md +1 -1
- package/dist/agent-src/skills/design-intelligence/references/design-rules-checklist.md +4 -3
- package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +25 -22
- package/dist/agent-src/skills/{wireframe → design-review}/references/story-beat-ledger.schema.json +1 -1
- package/dist/agent-src/skills/design-review/references/verification-automation.md +11 -3
- package/dist/agent-src/skills/design-system-capture/SKILL.md +15 -10
- package/dist/agent-src/skills/design-system-capture/references/design-system-json.md +20 -5
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +2 -2
- package/dist/agent-src/skills/design-tokens/SKILL.md +23 -23
- package/dist/agent-src/skills/design-tokens/scripts/tokens.mjs +1037 -0
- package/dist/agent-src/skills/design-tokens/scripts/tokens.ts +336 -13
- package/dist/agent-src/skills/design-tokens/templates/design-tokens-starter.json +52 -53
- package/dist/agent-src/skills/docker/SKILL.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +14 -276
- package/dist/agent-src/skills/existing-ui-audit/procedure.yaml +44 -0
- package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/references/steps/00-inspect-stack-and-input.md +4 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/01-enumerate-components.md +26 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/01b-detect-component-taxonomy.md +7 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/02-identify-design-system.md +12 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/03-detect-design-tokens.md +35 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/04-detect-shadcn-inventory.md +13 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/04b-live-storybook-read.md +26 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/05-list-reusable-patterns.md +14 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/06-match-candidates.md +52 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/07-greenfield-branch.md +29 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/08-a11y-baseline.md +40 -0
- package/dist/agent-src/skills/existing-ui-audit/references/steps/09-validate-and-write.md +5 -0
- package/dist/agent-src/skills/experiment-loop/SKILL.md +1 -1
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +65 -4
- package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +1 -1
- package/dist/agent-src/skills/fe-design/references/source-led-port.md +1 -1
- package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +6 -6
- package/dist/agent-src/skills/flux/SKILL.md +1 -1
- package/dist/agent-src/skills/form-handler/SKILL.md +15 -2
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +9 -0
- package/dist/agent-src/skills/git-workflow/SKILL.md +2 -2
- package/dist/agent-src/skills/git-workflow/references/branch-update.md +7 -0
- package/dist/agent-src/skills/git-workflow/references/commit-subject.md +12 -0
- package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +10 -0
- package/dist/agent-src/skills/gtm-launch/SKILL.md +8 -1
- package/dist/agent-src/skills/html-deck/SKILL.md +7 -2
- package/dist/agent-src/skills/humanizer/SKILL.md +16 -3
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +13 -1
- package/dist/agent-src/skills/humanizer/references/fixtures.md +8 -0
- package/dist/agent-src/skills/iconography/SKILL.md +4 -2
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +16 -5
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +5 -4
- package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.mjs +197 -0
- package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
- package/dist/agent-src/skills/judge-security-auditor/SKILL.md +2 -2
- package/dist/agent-src/skills/launch-readiness/SKILL.md +18 -22
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +5 -5
- package/dist/agent-src/skills/livewire/SKILL.md +1 -1
- package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +18 -0
- package/dist/agent-src/skills/logo-generation/SKILL.md +21 -11
- package/dist/agent-src/skills/market-entry-analysis/SKILL.md +6 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +2 -2
- package/dist/agent-src/skills/messaging-architecture/SKILL.md +8 -1
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +2 -2
- package/dist/agent-src/skills/playwright-testing/SKILL.md +2 -0
- package/dist/agent-src/skills/positioning-strategy/SKILL.md +10 -2
- package/dist/agent-src/skills/prediction-pool-optimizer/references/ev-fixtures.md +13 -0
- package/dist/agent-src/skills/prediction-pool-optimizer/references/odds-and-bonus.md +7 -0
- package/dist/agent-src/skills/project-analysis-core/SKILL.md +1 -1
- package/dist/agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +1 -1
- package/dist/agent-src/skills/project-analyzer/SKILL.md +14 -75
- package/dist/agent-src/skills/project-analyzer/procedure.yaml +39 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/01-project-overview.md +9 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/02-architecture.md +7 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/03-data-layer.md +6 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/04-business-domains.md +7 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/05-api-surface.md +6 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/06-service-map.md +6 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/07-module-inventory.md +6 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/08-infrastructure-and-testing.md +5 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/09-agent-docs-audit.md +6 -0
- package/dist/agent-src/skills/project-analyzer/references/steps/10-gap-analysis.md +6 -0
- package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +10 -0
- package/dist/agent-src/skills/quality-tools/references/php-tools.md +13 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +11 -0
- package/dist/agent-src/skills/react-native-setup/SKILL.md +7 -2
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +5 -1
- package/dist/agent-src/skills/react-shadcn-ui/references/registry.md +13 -0
- package/dist/agent-src/skills/react-shadcn-ui/scripts/shadcn_add.mjs +315 -0
- package/dist/agent-src/skills/react-shadcn-ui/scripts/shadcn_add.ts +6 -6
- package/dist/agent-src/skills/receiving-code-review/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/SKILL.md +2 -2
- package/dist/agent-src/skills/release-comms/SKILL.md +2 -1
- package/dist/agent-src/skills/repomix-packer/SKILL.md +2 -2
- package/dist/agent-src/skills/requesting-code-review/SKILL.md +3 -3
- package/dist/agent-src/skills/roadmap-management/references/archival.md +3 -3
- package/dist/agent-src/skills/roadmap-management/references/authoring.md +9 -2
- package/dist/agent-src/skills/roadmap-management/references/dashboard.md +3 -3
- package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +3 -3
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +4 -4
- package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +1 -1
- package/dist/agent-src/skills/rule-writing/SKILL.md +1 -2
- package/dist/agent-src/skills/security/SKILL.md +41 -5
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
- package/dist/agent-src/skills/skill-reviewer/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +23 -8
- package/dist/agent-src/skills/skill-writing/references/section-patterns.md +19 -1
- package/dist/agent-src/skills/source-discovery/SKILL.md +3 -3
- package/dist/agent-src/skills/sql-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/standards-from-config/SKILL.md +2 -2
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +5 -0
- package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.mjs +99 -0
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +13 -13
- package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +4 -3
- package/dist/agent-src/skills/svg-authoring/SKILL.md +324 -0
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +2 -2
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +8 -3
- package/dist/agent-src/skills/tailwind-engineer/scripts/tailwind_config_gen.mjs +482 -0
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/threat-modeling/SKILL.md +8 -1
- package/dist/agent-src/skills/threat-modeling/data/manifest.json +5 -2
- package/dist/agent-src/skills/threat-modeling/data/reasoning.csv +10 -10
- package/dist/agent-src/skills/threat-modeling/data/surfaces.csv +11 -10
- package/dist/agent-src/skills/threat-modeling/data/threats.csv +34 -20
- package/dist/agent-src/skills/type-design/SKILL.md +196 -0
- package/dist/agent-src/skills/type-design/references/font-spec.md +90 -0
- package/dist/agent-src/skills/type-design/references/gates.md +113 -0
- package/dist/agent-src/skills/type-design/references/toolchain.md +66 -0
- package/dist/agent-src/skills/typography-system/SKILL.md +21 -11
- package/dist/agent-src/skills/typography-system/references/worked-scales.md +85 -0
- package/dist/agent-src/skills/ui-apply-generic/SKILL.md +1 -1
- package/dist/agent-src/skills/universal-project-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +120 -125
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -0
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +1 -1
- package/dist/agent-src/skills/voice-and-tone-design/SKILL.md +71 -2
- package/dist/agent-src/skills/wireframe/SKILL.md +5 -2
- package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +16 -14
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/agents/proposal.example.md +1 -1
- package/dist/agent-src/templates/github-workflows/advisories-daily.yml +85 -0
- package/dist/agent-src/templates/roadmaps.md +39 -1
- package/dist/agent-src/templates/scripts/check_memory.ts +2 -2
- package/dist/agent-src/templates/scripts/historical-bug-patterns.example.yml +13 -1
- package/dist/agent-src/templates/scripts/work_engine/cli.ts +17 -0
- package/dist/agent-src/templates/scripts/work_engine/cli_args.ts +10 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +22 -13
- package/dist/agent-src/templates/scripts/work_engine/dispatcher.ts +18 -4
- package/dist/agent-src/templates/scripts/work_engine/procedure/checks.ts +283 -0
- package/dist/agent-src/templates/scripts/work_engine/procedure/run.ts +432 -0
- package/dist/agent-src/templates/scripts/work_engine/procedure/sidecar.ts +185 -0
- package/dist/agent-src/templates/scripts/work_engine/state.ts +3 -0
- package/dist/cli/commands/uiRender.js +265 -13
- package/dist/cli/commands/uiRenderAsset.js +1316 -0
- package/dist/cli/commands/uiRenderAssetCompare.js +752 -0
- package/dist/cli/commands/uiRenderAssetFonts.js +158 -0
- package/dist/cli/commands/uiRenderAssetGrid.js +265 -0
- package/dist/cli/commands/uiRenderAssetGrounds.js +323 -0
- package/dist/cli/commands/uiRenderAssetIsolation.js +85 -0
- package/dist/cli/commands/uiRenderAssetTypes.js +133 -0
- package/dist/cli/initRouting.js +1 -1
- package/dist/cli/main.js +47 -1
- package/dist/cli/mcp/dispatch.js +31 -9
- package/dist/cli/python/knowledge_ingest.js +7 -39
- package/dist/cli/python/workspace_secrets.js +4 -177
- package/dist/cli/registry.js +5 -2
- package/dist/cli-delegate/archive_completed_roadmaps.js +3 -2
- package/dist/cli-delegate/check_update_banner.js +635 -266
- package/dist/cli-delegate/{chunk-AED2VQGM.js → chunk-2CX7NWRB.js} +233 -437
- package/dist/cli-delegate/{chunk-F3QDG2GT.js → chunk-2SFOOFJJ.js} +259 -159
- package/dist/cli-delegate/chunk-2Y5AMLMY.js +136 -0
- package/dist/cli-delegate/{chunk-OKJUIAPS.js → chunk-4E2W63DA.js} +1 -62
- package/dist/cli-delegate/{chunk-6SRJTYJS.js → chunk-4SHNERBL.js} +37 -16
- package/dist/cli-delegate/chunk-5VO3RTMV.js +208 -0
- package/dist/cli-delegate/chunk-6E2LDY4F.js +27 -0
- package/dist/cli-delegate/{chunk-XQTPXKFA.js → chunk-6ZD2QZMN.js} +8 -17
- package/dist/cli-delegate/{chunk-6V5FMA3P.js → chunk-7CEI2YPS.js} +1 -1
- package/dist/cli-delegate/{chunk-DMSZHXZS.js → chunk-7DRWULEQ.js} +80 -7
- package/dist/cli-delegate/chunk-7NXCZSDL.js +54 -0
- package/dist/cli-delegate/chunk-AX3U3UWJ.js +328 -0
- package/dist/cli-delegate/{chunk-WOKWX5PL.js → chunk-BKALSTBZ.js} +18 -3
- package/dist/cli-delegate/chunk-BMGUGSY6.js +103 -0
- package/dist/cli-delegate/chunk-BYV46VRM.js +3309 -0
- package/dist/cli-delegate/{chunk-5GPULQBE.js → chunk-CAL76ADI.js} +4215 -682
- package/dist/cli-delegate/chunk-DSY6LLCQ.js +127 -0
- package/dist/cli-delegate/chunk-E6XNBVE4.js +581 -0
- package/dist/cli-delegate/{chunk-ZUZKZYCN.js → chunk-ESUNQ5XR.js} +46 -10
- package/dist/cli-delegate/{chunk-G5VVIZRC.js → chunk-F7HLSW6P.js} +1 -1
- package/dist/cli-delegate/chunk-FIEEOH5C.js +133 -0
- package/dist/cli-delegate/chunk-FKYZOWVD.js +719 -0
- package/dist/cli-delegate/chunk-GOMILC3O.js +918 -0
- package/dist/cli-delegate/chunk-I4KJYVPB.js +69 -0
- package/dist/cli-delegate/{chunk-JQOM7TTM.js → chunk-IDAOTDPE.js} +1 -100
- package/dist/cli-delegate/chunk-IROAQ77A.js +137 -0
- package/dist/cli-delegate/chunk-J2SJZICO.js +698 -0
- package/dist/cli-delegate/{chunk-6YQC2HLF.js → chunk-JHMMWNJ2.js} +54 -40
- package/dist/cli-delegate/{chunk-WQWXGIWB.js → chunk-M6CRG5O6.js} +165 -15
- package/dist/cli-delegate/{chunk-PHD7OOLE.js → chunk-MQ2UDEGV.js} +14 -3
- package/dist/cli-delegate/chunk-NTM7G2V4.js +220 -0
- package/dist/cli-delegate/chunk-PV7S5DPJ.js +175 -0
- package/dist/cli-delegate/{chunk-F7DQWYR5.js → chunk-RAXQOETZ.js} +6 -2
- package/dist/cli-delegate/{chunk-T7XSJSUQ.js → chunk-SIOMDAKL.js} +980 -784
- package/dist/cli-delegate/{chunk-KR6EM23A.js → chunk-TULVDQ4L.js} +3 -48
- package/dist/cli-delegate/chunk-UUYEKXKR.js +20 -0
- package/dist/cli-delegate/{chunk-7XGD2OEU.js → chunk-UXWHY6CQ.js} +2 -1
- package/dist/cli-delegate/{chunk-PWX7CKMD.js → chunk-VC3ECUEK.js} +1 -1
- package/dist/cli-delegate/{chunk-KXXWJTDO.js → chunk-WMNYVIYX.js} +1 -1
- package/dist/cli-delegate/chunk-XCY5KKUC.js +181 -0
- package/dist/cli-delegate/{chunk-H76PHZBI.js → chunk-XLY5G3CM.js} +14 -19
- package/dist/cli-delegate/cmd_adr_effective.js +41 -2
- package/dist/cli-delegate/cmd_analyze_session.js +7 -7
- package/dist/cli-delegate/cmd_brand_status.js +1287 -10
- package/dist/cli-delegate/cmd_ci_settle.js +330 -0
- package/dist/cli-delegate/cmd_conformance.js +669 -822
- package/dist/cli-delegate/cmd_doctor.js +26 -16
- package/dist/cli-delegate/cmd_doctor_neighbours.js +10 -7
- package/dist/cli-delegate/cmd_explain.js +9 -24
- package/dist/cli-delegate/cmd_export.js +14 -11
- package/dist/cli-delegate/cmd_fleet.js +5 -3
- package/dist/cli-delegate/cmd_git_convention.js +8 -4
- package/dist/cli-delegate/cmd_handoff.js +79 -242
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +4 -2
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_preflight.js +7 -5
- package/dist/cli-delegate/cmd_prune.js +16 -13
- package/dist/cli-delegate/cmd_route_audit.js +11 -8
- package/dist/cli-delegate/cmd_route_explain.js +649 -16
- package/dist/cli-delegate/cmd_session_recycle.js +15 -6
- package/dist/cli-delegate/cmd_settings_check.js +2 -2
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +14 -11
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +16 -13
- package/dist/cli-delegate/cmd_uninstall.js +185 -31
- package/dist/cli-delegate/cmd_update.js +67 -3
- package/dist/cli-delegate/cmd_upgrade.js +38 -7
- package/dist/cli-delegate/cmd_validate.js +16 -13
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/cli-delegate/dashboard_mode.js +1 -1
- package/dist/cli-delegate/gate_execute.js +4 -3
- package/dist/cli-delegate/roadmap_age.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +4 -3
- package/dist/cli-delegate/update_roadmap_progress.js +3 -2
- package/dist/cli-delegate/upgrade_unattended-VVL6JLSX.js +541 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +409 -201
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +8 -8
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +36 -27
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +25 -16
- package/dist/hooks/chat_history.chunk.cjs +6852 -0
- package/dist/hooks/chat_history.chunk.cjs.sha256 +1 -0
- package/dist/hooks/dispatch.js +17706 -18627
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/egress_engine.chunk.cjs +915 -0
- package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -0
- package/dist/hooks/svg_rules.chunk.cjs +1858 -0
- package/dist/hooks/svg_rules.chunk.cjs.sha256 +1 -0
- package/dist/install/conflict.js +36 -6
- package/dist/install/conflictTracker.js +81 -7
- package/dist/install/detect.js +3 -3
- package/dist/install/hostPermissionsAdapter.js +106 -0
- package/dist/install/install.mjs +3117 -1949
- package/dist/install/installThinLayer.js +1 -1
- package/dist/install/manifestFiles.js +21 -0
- package/dist/install/noGoDenyRows.js +300 -0
- package/dist/install/noGoRules.js +368 -0
- package/dist/install/ownershipLedger.js +133 -0
- package/dist/install/plan.js +1 -1
- package/dist/install/preflight.js +39 -8
- package/dist/install/preserve.js +74 -12
- package/dist/install/recordedOwnership.js +88 -5
- package/dist/install/rule_scope.js +20 -3
- package/dist/install/stagedRuntime.js +455 -0
- package/dist/install/toolsArg.js +19 -0
- package/dist/install/userPin.js +47 -0
- package/dist/install/wizard-plan.js +26 -4
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +3 -2
- package/dist/mcp/registry-manifest.json +6 -5
- package/dist/mcp/server.json +3 -3
- package/dist/mcp/server.mjs +9814 -5333
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +7 -12
- package/dist/scripts/_lib/asset_render_record.js +219 -0
- package/dist/scripts/_lib/claude_builtin_names.js +9 -4
- package/dist/scripts/_lib/claude_settings_hooks.js +102 -34
- package/dist/scripts/_lib/egress_rules.js +766 -0
- package/dist/scripts/_lib/environment_detector.js +24 -33
- package/dist/scripts/_lib/global_deploy_inventory.js +146 -9
- package/dist/scripts/_lib/guidelines_lane.js +10 -1
- package/dist/scripts/_lib/identifier_redaction.js +24 -0
- package/dist/scripts/_lib/installed_layer.js +17 -0
- package/dist/scripts/_lib/installed_layer_normalise.js +72 -0
- package/dist/scripts/_lib/lean_projection_mode.js +34 -152
- package/dist/scripts/_lib/project_tree_form.js +118 -0
- package/dist/scripts/_lib/scan_scope.js +105 -0
- package/dist/scripts/_lib/scoped_projection.js +133 -3
- package/dist/scripts/_lib/settings_removed_keys.js +29 -0
- package/dist/scripts/_lib/settings_renamed_keys.js +17 -5
- package/dist/scripts/_lib/subagent_native_fields.js +35 -0
- package/dist/scripts/_lib/svg_dom.js +284 -0
- package/dist/scripts/_lib/svg_filled_counter.js +285 -0
- package/dist/scripts/_lib/svg_geometry.js +477 -0
- package/dist/scripts/_lib/svg_motion.js +179 -0
- package/dist/scripts/_lib/svg_rules.js +1339 -0
- package/dist/scripts/_lib/svg_urls.js +67 -0
- package/dist/scripts/_lib/ui_surface.js +19 -0
- package/dist/scripts/_lib/verification_command.js +364 -0
- package/dist/scripts/_lib/workspace_secrets.js +229 -0
- package/dist/scripts/ai_council/seat_policy.js +27 -0
- package/dist/scripts/ai_council/transport_resolver.js +9 -10
- package/dist/scripts/condense.js +25 -29
- package/dist/scripts/lint_svg_asset.js +324 -0
- package/dist/server/io/substituteTemplate.js +2 -2
- package/dist/server/io/yamlIO.js +2 -2
- package/dist/server/routes/install.js +5 -1
- package/dist/server/routes/settings.js +2 -2
- package/dist/server/routes/wizard.js +1 -1
- package/dist/server/schemas/settings.js +22 -16
- package/dist/server/serverInfo.js +1 -1
- package/dist/shared/brandTokenDocument.js +47 -0
- package/dist/shared/brandTokenPaths.js +43 -0
- package/dist/shared/settingsCarveOut.js +0 -7
- package/dist/shared/skillRanking.js +103 -5
- package/dist/shared/userMd/formAdapter.js +16 -0
- package/dist/shared/userMd/schema.js +17 -0
- package/dist/ui/assets/index-w70Ado09.js +23 -0
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +89 -2
- package/docs/THIRD-PARTY-NOTICES.md +27 -0
- package/docs/contracts/settings-classes.md +27 -9
- package/docs/guidelines/agent-infra/api-cost-levers.md +4 -3
- package/docs/guidelines/agent-infra/model-recommendation.md +11 -5
- package/docs/guidelines/agent-infra/rule-body-migration-inventory.md +1 -1
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +47 -24
- package/docs/guidelines/design-antipatterns.md +6 -1
- package/docs/guidelines/design-asset-discipline.md +17 -0
- package/docs/guidelines/design-fidelity-routing.md +1 -0
- package/docs/guidelines/php/api-design.md +10 -1
- package/llms.txt +12 -10
- package/package.json +15 -5
- package/src/agent-src/scripts/dashboard_mode.ts +4 -1
- package/src/agent-src/scripts/roadmap_age.ts +175 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +10 -2
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/check_memory.ts +2 -2
- package/src/agent-src/templates/scripts/historical-bug-patterns.example.yml +13 -1
- package/src/agent-src/templates/scripts/work_engine/cli.ts +17 -0
- package/src/agent-src/templates/scripts/work_engine/cli_args.ts +10 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +22 -13
- package/src/agent-src/templates/scripts/work_engine/dispatcher.ts +18 -4
- package/src/agent-src/templates/scripts/work_engine/procedure/checks.ts +283 -0
- package/src/agent-src/templates/scripts/work_engine/procedure/run.ts +432 -0
- package/src/agent-src/templates/scripts/work_engine/procedure/sidecar.ts +185 -0
- package/src/agent-src/templates/scripts/work_engine/state.ts +3 -0
- package/src/config/agent-settings.template.yml +120 -57
- package/src/config/agents-paths.yml +13 -7
- package/src/config/ci-local-parity.yml +44 -0
- package/src/config/continuity-surface.json +51 -2
- package/src/config/depth-audit-samples.json +5 -0
- package/src/config/discovery/command-verbs.yml +1 -0
- package/src/config/dispatch-economy-metrics.json +1 -0
- package/src/config/egress-mutations/class-precedence-swapped.json +7 -0
- package/src/config/egress-mutations/credential-token-restores.json +7 -0
- package/src/config/egress-mutations/iban-checksum-disabled.json +7 -0
- package/src/config/egress-mutations/raw-value-in-the-count-record.json +7 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +149 -3
- package/src/config/gate-reachability-exemptions.json +3 -5
- package/src/config/gate-violation-baselines.json +29 -25
- package/src/config/gitignore-block.txt +6 -3
- package/src/config/hook-bundle-budget.json +34 -3
- package/src/config/hook-carrier-probes.json +30 -0
- package/src/config/hook-latency-budget.json +4 -2
- package/src/config/host-injection-effect.json +8 -1
- package/src/config/host-skill-frontmatter-keys.json +56 -0
- package/src/config/layer-form-brakes.json +4 -0
- package/src/config/layer-form-comparison-corpus.json +42 -0
- package/src/config/loop-surfaces.yaml +28 -47
- package/src/config/memory-twin-verdicts.yml +9 -1
- package/src/config/metric-registry.yml +40 -3
- package/src/config/pack-size-budget.json +15 -0
- package/src/config/procedure-core-budget.json +21 -0
- package/src/config/profiles/minimal.ini +1 -1
- package/src/config/project-tree-form.json +24 -0
- package/src/config/reach-channels.yml +3 -3
- package/src/config/reach-prescriptions-intake.yml +1 -1
- package/src/config/roadmap-step-size-baseline.json +118 -0
- package/src/config/routing-coverage-seed.json +100 -2
- package/src/config/skill-body-budget.json +686 -0
- package/src/config/skill-fork-pilot.json +17 -0
- package/src/config/skill-lifecycle-probes-codex.json +57 -0
- package/src/config/skill-lifecycle-probes.json +57 -0
- package/src/config/skill-output-shape.json +316 -0
- package/src/config/step-depth-table.json +58 -0
- package/src/config/turnaround-budget.json +90 -0
- package/src/install/conflict.ts +38 -6
- package/src/install/conflictTracker.ts +126 -8
- package/src/install/detect.ts +3 -3
- package/src/install/hostPermissionsAdapter.ts +136 -0
- package/src/install/installThinLayer.ts +6 -1
- package/src/install/manifestFiles.ts +24 -0
- package/src/install/noGoDenyRows.ts +368 -0
- package/src/install/noGoRules.ts +438 -0
- package/src/install/ownershipLedger.ts +180 -0
- package/src/install/plan.ts +1 -1
- package/src/install/preflight.ts +42 -10
- package/src/install/preserve.ts +94 -12
- package/src/install/recordedOwnership.ts +121 -3
- package/src/install/rule_scope.ts +20 -3
- package/src/install/stagedRuntime.ts +528 -0
- package/src/install/toolsArg.ts +16 -0
- package/src/install/types.ts +23 -2
- package/src/install/userPin.ts +54 -0
- package/src/install/wizard-plan.ts +25 -4
- package/src/scripts/_cli/_doctor_emit.ts +24 -0
- package/src/scripts/_cli/cmd_brand_status.ts +223 -12
- package/src/scripts/_cli/cmd_ci_settle.ts +32 -0
- package/src/scripts/_cli/cmd_doctor.ts +23 -44
- package/src/scripts/_cli/cmd_route_explain.ts +176 -2
- package/src/scripts/_cli/cmd_uninstall.ts +270 -26
- package/src/scripts/_cli/cmd_update.ts +34 -1
- package/src/scripts/_cli/cmd_upgrade.ts +38 -2
- package/src/scripts/_cli/detection_report.ts +7 -0
- package/src/scripts/_cli/doctor_execution.ts +1 -2
- package/src/scripts/_cli/doctor_install_mode.ts +39 -0
- package/src/scripts/_cli/doctor_network_posture.ts +3 -3
- package/src/scripts/_cli/doctor_ownership_ledger.ts +126 -0
- package/src/scripts/_cli/doctor_update_verdict.ts +104 -0
- package/src/scripts/_cli/explain_last/scrubber.ts +9 -21
- package/src/scripts/_cli/upgrade_unattended.ts +548 -0
- package/src/scripts/_dispatch.bash +42 -9
- package/src/scripts/_lib/activation_ladder.ts +1 -1
- package/src/scripts/_lib/advisory_inventory.ts +405 -0
- package/src/scripts/_lib/advisory_notice.ts +270 -0
- package/src/scripts/_lib/advisory_sources.ts +752 -0
- package/src/scripts/_lib/advisory_verdicts.ts +363 -0
- package/src/scripts/_lib/agent_artifact_location.ts +177 -0
- package/src/scripts/_lib/agent_settings.ts +7 -12
- package/src/scripts/_lib/arrival_record.ts +217 -0
- package/src/scripts/_lib/asset_render_record.ts +247 -0
- package/src/scripts/_lib/billing_grant.ts +4 -2
- package/src/scripts/_lib/brand_voice_slot.ts +258 -0
- package/src/scripts/_lib/canary_status.ts +24 -0
- package/src/scripts/_lib/cc_transcript.ts +276 -0
- package/src/scripts/_lib/changelog_eras.ts +83 -0
- package/src/scripts/_lib/claim_phrases.ts +71 -0
- package/src/scripts/_lib/classify_update.ts +171 -0
- package/src/scripts/_lib/claude_builtin_names.ts +9 -4
- package/src/scripts/_lib/claude_hook_scope.ts +195 -0
- package/src/scripts/_lib/claude_settings_hooks.ts +118 -34
- package/src/scripts/_lib/codex_resident_set.ts +460 -0
- package/src/scripts/_lib/codex_skill_finder.ts +225 -0
- package/src/scripts/_lib/confusables.ts +19 -0
- package/src/scripts/_lib/consumer_impact.ts +199 -0
- package/src/scripts/_lib/content_class.ts +127 -0
- package/src/scripts/_lib/counted_probe.ts +68 -1
- package/src/scripts/_lib/credential_deny_rows.ts +151 -0
- package/src/scripts/_lib/credential_files.ts +367 -0
- package/src/scripts/_lib/delivery_arm_experiment.ts +2 -2
- package/src/scripts/_lib/depth_flip.ts +141 -0
- package/src/scripts/_lib/design_lock.ts +117 -0
- package/src/scripts/_lib/design_sync_ledger.ts +142 -0
- package/src/scripts/_lib/design_sync_plan.ts +177 -0
- package/src/scripts/_lib/design_sync_reconcile.ts +159 -0
- package/src/scripts/_lib/design_sync_verify.ts +99 -0
- package/src/scripts/_lib/design_system_claude_design.ts +323 -0
- package/src/scripts/_lib/design_system_import.ts +84 -6
- package/src/scripts/_lib/dispatch_import_closure.ts +42 -11
- package/src/scripts/_lib/egress_credential_env.ts +35 -0
- package/src/scripts/_lib/egress_engine.ts +22 -0
- package/src/scripts/_lib/egress_known_values.ts +223 -0
- package/src/scripts/_lib/egress_normalize.ts +67 -0
- package/src/scripts/_lib/egress_prefixes.ts +42 -0
- package/src/scripts/_lib/egress_rules.ts +896 -0
- package/src/scripts/_lib/egress_rules_linearity.ts +56 -0
- package/src/scripts/_lib/egress_sender_scan.ts +290 -0
- package/src/scripts/_lib/egress_session_table.ts +510 -0
- package/src/scripts/_lib/egress_text.ts +63 -0
- package/src/scripts/_lib/egress_token.ts +252 -0
- package/src/scripts/_lib/empty_cycles.ts +1 -1
- package/src/scripts/_lib/environment_detector.ts +45 -30
- package/src/scripts/_lib/exemption_shape.ts +75 -1
- package/src/scripts/_lib/floor_posture.ts +185 -0
- package/src/scripts/_lib/forge_protection.ts +140 -2
- package/src/scripts/_lib/global_deploy_inventory.ts +159 -6
- package/src/scripts/_lib/guidelines_lane.ts +12 -1
- package/src/scripts/_lib/hand_kinds.ts +65 -0
- package/src/scripts/_lib/headless_invocation.ts +3 -3
- package/src/scripts/_lib/host_capability.ts +34 -2
- package/src/scripts/_lib/host_env_write.ts +39 -18
- package/src/scripts/_lib/host_listing_model.ts +14 -7
- package/src/scripts/_lib/identifier_redaction.ts +26 -0
- package/src/scripts/_lib/impact_bump.ts +70 -0
- package/src/scripts/_lib/install_reach_checks.ts +53 -1
- package/src/scripts/_lib/installed_layer.ts +31 -0
- package/src/scripts/_lib/installed_layer_normalise.ts +75 -0
- package/src/scripts/_lib/instructions_loaded_classify.ts +75 -0
- package/src/scripts/_lib/instructions_loaded_event.ts +53 -0
- package/src/scripts/_lib/knowledge_global_redaction.ts +2 -2
- package/src/scripts/_lib/layer_form_corpus.ts +165 -0
- package/src/scripts/_lib/layer_form_decision.ts +248 -0
- package/src/scripts/_lib/layer_form_evidence.ts +112 -0
- package/src/scripts/_lib/lean_projection_mode.ts +32 -161
- package/src/scripts/_lib/llm_proposer_transport.ts +1 -0
- package/src/scripts/_lib/loop_guards.ts +8 -120
- package/src/scripts/_lib/major_surface.ts +207 -0
- package/src/scripts/_lib/mcp_bridge.ts +30 -69
- package/src/scripts/_lib/merge_clearance_record.ts +384 -0
- package/src/scripts/_lib/neighbour_census.ts +18 -1
- package/src/scripts/_lib/orchestration_record.ts +61 -0
- package/src/scripts/_lib/outcome_vocabularies.ts +15 -23
- package/src/scripts/_lib/pin_advice.ts +49 -0
- package/src/scripts/_lib/platform_anchor.ts +222 -0
- package/src/scripts/_lib/procedure_checks.ts +5 -0
- package/src/scripts/_lib/project_tree_form.ts +134 -0
- package/src/scripts/_lib/projection_key_migration.ts +122 -0
- package/src/scripts/_lib/prompt_shape.ts +13 -2
- package/src/scripts/_lib/provenance_verify.ts +272 -0
- package/src/scripts/_lib/quota_parked.ts +27 -1
- package/src/scripts/_lib/ratification_artifact.ts +64 -1
- package/src/scripts/_lib/ratification_subject.ts +508 -46
- package/src/scripts/_lib/release_findings_ingest.ts +52 -5
- package/src/scripts/_lib/release_highlights.ts +72 -2
- package/src/scripts/_lib/release_settings_surface.ts +392 -0
- package/src/scripts/_lib/review_round.ts +134 -0
- package/src/scripts/_lib/roadmap_checkboxes.ts +1 -1
- package/src/scripts/_lib/roadmap_slug_lock.ts +254 -0
- package/src/scripts/_lib/roadmap_step_size.ts +75 -0
- package/src/scripts/_lib/run_checkpoint.ts +60 -3
- package/src/scripts/_lib/runtime_wiring_checks.ts +15 -1
- package/src/scripts/_lib/scope_guard.sh +64 -5
- package/src/scripts/_lib/scoped_projection.ts +136 -3
- package/src/scripts/_lib/secret_detector.ts +24 -103
- package/src/scripts/_lib/security_fix.ts +429 -0
- package/src/scripts/_lib/security_fix_context.ts +133 -0
- package/src/scripts/_lib/self_repair.ts +69 -39
- package/src/scripts/_lib/self_repair_outgoing.ts +209 -0
- package/src/scripts/_lib/self_repair_payload.ts +86 -0
- package/src/scripts/_lib/session_eol.ts +3 -0
- package/src/scripts/_lib/session_pr_watch.ts +207 -0
- package/src/scripts/_lib/session_register.ts +51 -1
- package/src/scripts/_lib/session_tag.ts +51 -0
- package/src/scripts/_lib/settings_removed_keys.ts +30 -0
- package/src/scripts/_lib/settings_renamed_keys.ts +19 -5
- package/src/scripts/_lib/skill_catalogue.ts +66 -25
- package/src/scripts/_lib/skill_fork_pilot.ts +121 -0
- package/src/scripts/_lib/skill_leases.ts +203 -0
- package/src/scripts/_lib/skill_listing_overrides.ts +294 -0
- package/src/scripts/_lib/skill_materialisation.ts +388 -0
- package/src/scripts/_lib/skill_materialisation_gate.ts +210 -0
- package/src/scripts/_lib/skill_materialisation_lock.ts +149 -0
- package/src/scripts/_lib/skill_materialisation_triggers.ts +96 -0
- package/src/scripts/_lib/skill_origin.ts +21 -1
- package/src/scripts/_lib/skill_reach.ts +342 -0
- package/src/scripts/_lib/source_shape.ts +7 -2
- package/src/scripts/_lib/sqlite_guard.ts +1 -1
- package/src/scripts/_lib/step_anatomy.ts +330 -0
- package/src/scripts/_lib/subagent_capsule.ts +8 -2
- package/src/scripts/_lib/subagent_native_fields.ts +30 -0
- package/src/scripts/_lib/subagent_spawn.ts +36 -0
- package/src/scripts/_lib/subagent_steering.ts +367 -0
- package/src/scripts/_lib/svg_dom.ts +318 -0
- package/src/scripts/_lib/svg_filled_counter.ts +287 -0
- package/src/scripts/_lib/svg_geometry.ts +480 -0
- package/src/scripts/_lib/svg_motion.ts +208 -0
- package/src/scripts/_lib/svg_rules.ts +1455 -0
- package/src/scripts/_lib/svg_urls.ts +72 -0
- package/src/scripts/_lib/test_delta.ts +89 -6
- package/src/scripts/_lib/tier_downshift_verdict.ts +222 -0
- package/src/scripts/_lib/tool_result_census.ts +62 -0
- package/src/scripts/_lib/trigger_routers.ts +1 -0
- package/src/scripts/_lib/turnaround_throughput.ts +378 -0
- package/src/scripts/_lib/ui_surface.ts +22 -0
- package/src/scripts/_lib/unattended_gates.ts +100 -0
- package/src/scripts/_lib/undeployed_fixes.ts +34 -0
- package/src/scripts/_lib/update_check.ts +143 -12
- package/src/scripts/_lib/update_invite.ts +116 -0
- package/src/scripts/_lib/update_notice.ts +259 -0
- package/src/scripts/_lib/verification_command.ts +59 -0
- package/src/scripts/_lib/verify_clause.ts +31 -19
- package/src/scripts/_lib/version_order.ts +146 -0
- package/src/scripts/_lib/worker_reuse.ts +191 -0
- package/src/scripts/_lib/workspace_secrets.ts +273 -0
- package/src/scripts/_lib/worktree_git.ts +67 -0
- package/src/scripts/_lib/worktree_ignored.ts +116 -0
- package/src/scripts/adoption_status.ts +330 -3
- package/src/scripts/adr_cite_check.ts +93 -0
- package/src/scripts/advisories.ts +803 -0
- package/src/scripts/ai-image/adapters/flux.sh +1 -0
- package/src/scripts/ai-image/adapters/gemini-image.sh +1 -0
- package/src/scripts/ai-image/adapters/ideogram.sh +1 -0
- package/src/scripts/ai-image/adapters/recraft.sh +1 -0
- package/src/scripts/ai-video/adapters/comfyui.sh +1 -0
- package/src/scripts/ai-video/adapters/fal.sh +1 -0
- package/src/scripts/ai-video/adapters/gemini-veo.sh +1 -0
- package/src/scripts/ai-video/adapters/higgsfield.sh +1 -0
- package/src/scripts/ai-video/adapters/kling.sh +1 -0
- package/src/scripts/ai-video/adapters/musetalk.sh +1 -0
- package/src/scripts/ai-video/adapters/openai-images.sh +1 -0
- package/src/scripts/ai-video/adapters/replicate.sh +1 -0
- package/src/scripts/ai-video/adapters/sora.sh +1 -0
- package/src/scripts/ai-video/adapters/syncso.sh +1 -0
- package/src/scripts/ai-video/stitch.sh +115 -2
- package/src/scripts/ai_council/airgap.ts +1 -0
- package/src/scripts/ai_council/api_transport.ts +27 -4
- package/src/scripts/ai_council/budget_guard.ts +300 -11
- package/src/scripts/ai_council/bundler.ts +20 -29
- package/src/scripts/ai_council/chairman.ts +17 -2
- package/src/scripts/ai_council/cli_agency_bounds.ts +158 -0
- package/src/scripts/ai_council/cli_launch_env.ts +54 -0
- package/src/scripts/ai_council/cli_least_agency_canary.ts +18 -2
- package/src/scripts/ai_council/clients.ts +55 -73
- package/src/scripts/ai_council/config.ts +16 -22
- package/src/scripts/ai_council/council_egress.ts +167 -0
- package/src/scripts/ai_council/exposure_ledger.ts +117 -0
- package/src/scripts/ai_council/free_hand_shadow.ts +276 -0
- package/src/scripts/ai_council/free_route.ts +216 -0
- package/src/scripts/ai_council/free_route_catalog.ts +173 -0
- package/src/scripts/ai_council/free_route_client.ts +95 -0
- package/src/scripts/ai_council/free_rung.ts +61 -0
- package/src/scripts/ai_council/hand_payload.ts +230 -0
- package/src/scripts/ai_council/hand_schemas.ts +123 -0
- package/src/scripts/ai_council/jury_aggregate.ts +155 -1
- package/src/scripts/ai_council/kind_flip.ts +221 -0
- package/src/scripts/ai_council/kind_flip_state.ts +19 -0
- package/src/scripts/ai_council/learn_low_impact_preview.ts +50 -22
- package/src/scripts/ai_council/modification_review.ts +98 -5
- package/src/scripts/ai_council/necessity.ts +7 -0
- package/src/scripts/ai_council/orchestrator.ts +16 -16
- package/src/scripts/ai_council/pricing.ts +10 -0
- package/src/scripts/ai_council/redact_low_impact_entry.ts +5 -2
- package/src/scripts/ai_council/response_render.ts +27 -4
- package/src/scripts/ai_council/round_reservations.ts +119 -0
- package/src/scripts/ai_council/route_chooser.ts +161 -0
- package/src/scripts/ai_council/route_liveness.ts +152 -0
- package/src/scripts/ai_council/route_member.ts +52 -0
- package/src/scripts/ai_council/seat_policy.ts +40 -0
- package/src/scripts/ai_council/seat_resume.ts +40 -0
- package/src/scripts/ai_council/shadow_dispatch.ts +13 -3
- package/src/scripts/ai_council/spend_gate.ts +18 -2
- package/src/scripts/ai_council/status_surface.ts +37 -0
- package/src/scripts/ai_council/transport_resolver.ts +15 -18
- package/src/scripts/analysis_analysts.ts +226 -0
- package/src/scripts/analysis_anchors.ts +140 -0
- package/src/scripts/analysis_and_optimize.ts +44 -0
- package/src/scripts/analysis_emit.ts +150 -0
- package/src/scripts/analysis_exchange.ts +186 -0
- package/src/scripts/analysis_gate.ts +158 -0
- package/src/scripts/analysis_inbox_final.ts +173 -0
- package/src/scripts/analysis_ledger.ts +119 -0
- package/src/scripts/analysis_pr_optimize.ts +219 -0
- package/src/scripts/analysis_pr_review.ts +240 -0
- package/src/scripts/analysis_pr_target.ts +121 -0
- package/src/scripts/analysis_prompts.ts +130 -0
- package/src/scripts/analysis_publish.ts +358 -0
- package/src/scripts/analysis_questions.ts +78 -0
- package/src/scripts/analysis_run.ts +248 -0
- package/src/scripts/analysis_run_record.ts +210 -0
- package/src/scripts/analysis_target.ts +343 -0
- package/src/scripts/analysis_validate.ts +161 -0
- package/src/scripts/anchor_eval_falsify.ts +1 -1
- package/src/scripts/arrival_report.ts +115 -0
- package/src/scripts/ask_transport.ts +284 -35
- package/src/scripts/backfill_model_tier.ts +4 -5
- package/src/scripts/before_complete_hook.ts +48 -2
- package/src/scripts/bench_hook_latency.ts +3 -3
- package/src/scripts/bench_procedure_cursor.ts +440 -0
- package/src/scripts/bootstrap.sh +3 -3
- package/src/scripts/build_exit_code_register.ts +440 -0
- package/src/scripts/build_proof.ts +23 -1
- package/src/scripts/cache_realization_report.ts +155 -14
- package/src/scripts/canary_liveness.ts +117 -0
- package/src/scripts/chat_history.ts +12 -15
- package/src/scripts/check_account_surface.ts +318 -0
- package/src/scripts/check_agent_artifact_location.ts +17 -160
- package/src/scripts/check_bundle_path_leakage.ts +6 -0
- package/src/scripts/check_ci_local_parity.ts +21 -13
- package/src/scripts/check_claims.ts +77 -2
- package/src/scripts/check_completion_review.ts +34 -1
- package/src/scripts/check_concern_admissions.ts +41 -5
- package/src/scripts/check_consumer_scope_flip.ts +1 -1
- package/src/scripts/check_context_paths.ts +2 -0
- package/src/scripts/check_council_layout.ts +9 -0
- package/src/scripts/check_credential_spelling.ts +267 -0
- package/src/scripts/check_egress_mutations.ts +239 -0
- package/src/scripts/check_enforcement_coverage.ts +16 -3
- package/src/scripts/check_estate_count.ts +16 -1
- package/src/scripts/check_finding_dispositions.ts +147 -0
- package/src/scripts/check_gate_coverage.ts +123 -21
- package/src/scripts/check_gate_reachability.ts +16 -2
- package/src/scripts/check_generator_sync.ts +46 -0
- package/src/scripts/check_hook_bundle_composition.ts +139 -1
- package/src/scripts/check_host_docs_digest.ts +142 -5
- package/src/scripts/check_kernel_edit_ratified.ts +301 -76
- package/src/scripts/check_kernel_rule_bundle.ts +14 -10
- package/src/scripts/check_knowledge_cards.ts +83 -3
- package/src/scripts/check_memory.ts +13 -3
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_permission_deny_syntax.ts +247 -0
- package/src/scripts/check_platform_anchor.ts +243 -0
- package/src/scripts/check_proposal.ts +183 -1
- package/src/scripts/check_reach_channels.ts +5 -3
- package/src/scripts/check_references.ts +5 -2
- package/src/scripts/check_release_manifest_content.ts +33 -0
- package/src/scripts/check_release_pr_shape.ts +5 -0
- package/src/scripts/check_rule_invariants.ts +9 -1
- package/src/scripts/check_skill_body_budget.ts +343 -0
- package/src/scripts/check_skill_requires.ts +137 -1
- package/src/scripts/check_step_receipts.ts +246 -0
- package/src/scripts/check_structural_breaking.ts +7 -1
- package/src/scripts/check_suppression_hygiene.ts +8 -0
- package/src/scripts/check_test_weakening.ts +14 -0
- package/src/scripts/check_token_quality_golden.ts +5 -1
- package/src/scripts/check_update_banner.ts +50 -3
- package/src/scripts/check_verify_expectation_delta.ts +2 -1
- package/src/scripts/check_voice_lexicon.ts +531 -0
- package/src/scripts/ci_os_attribution.ts +327 -0
- package/src/scripts/ci_platform_scope.ts +69 -0
- package/src/scripts/ci_settle.ts +4 -1
- package/src/scripts/ci_status.ts +0 -1
- package/src/scripts/ci_time_to_green.ts +748 -0
- package/src/scripts/classify_merge_risk.ts +407 -19
- package/src/scripts/code_graph/sqlite_store.ts +1 -1
- package/src/scripts/compile_trigger_evals.ts +353 -0
- package/src/scripts/condense.ts +25 -31
- package/src/scripts/consumer_matrix.ts +61 -3
- package/src/scripts/council_availability_hook.ts +12 -4
- package/src/scripts/council_cli.ts +48 -38
- package/src/scripts/cross_repo_retrieve.ts +9 -72
- package/src/scripts/design_lock.ts +106 -0
- package/src/scripts/design_slop_rules.ts +30 -0
- package/src/scripts/design_sync.ts +264 -0
- package/src/scripts/design_system_import.ts +30 -13
- package/src/scripts/dispatch_economy_report.ts +46 -1
- package/src/scripts/dispatch_r2_reviewer.ts +41 -5
- package/src/scripts/egress_canary.ts +166 -0
- package/src/scripts/egress_sender_census.ts +101 -0
- package/src/scripts/external_sources_denylist.json +94 -12
- package/src/scripts/generate_catalog.ts +6 -2
- package/src/scripts/generate_owned_digest.ts +163 -0
- package/src/scripts/generate_reference_toc.ts +143 -0
- package/src/scripts/generate_subagent_floor.ts +11 -1
- package/src/scripts/handoff_context_hook.ts +34 -0
- package/src/scripts/hook_carrier_census.ts +263 -0
- package/src/scripts/hook_chunks.d.mts +12 -0
- package/src/scripts/hook_chunks.mjs +114 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +65 -42
- package/src/scripts/hooks/block_config_weakening.ts +127 -3
- package/src/scripts/hooks/block_credential_reads.ts +165 -0
- package/src/scripts/hooks/block_no_verify.ts +47 -23
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +54 -8
- package/src/scripts/hooks/category_a.ts +202 -25
- package/src/scripts/hooks/concern_failure_policy.ts +6 -6
- package/src/scripts/hooks/concern_registry.ts +31 -6
- package/src/scripts/hooks/concern_registry_types.ts +11 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +105 -15
- package/src/scripts/hooks/design_pass_hook.ts +199 -13
- package/src/scripts/hooks/dispatch_hook.ts +13 -12
- package/src/scripts/hooks/egress_engine_loader.ts +125 -0
- package/src/scripts/hooks/egress_stage.ts +440 -0
- package/src/scripts/hooks/hook_stdin.ts +29 -4
- package/src/scripts/hooks/host_docs_pages.ts +194 -0
- package/src/scripts/hooks/host_docs_pages.yaml +75 -0
- package/src/scripts/hooks/host_lowering.json +1 -1
- package/src/scripts/hooks/host_lowering.yaml +22 -21
- package/src/scripts/hooks/host_rate_marker_hook.ts +73 -0
- package/src/scripts/hooks/instructions_observer_hook.ts +170 -0
- package/src/scripts/hooks/interruption_ledger_hook.ts +1 -1
- package/src/scripts/hooks/lazy_concern.ts +148 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +80 -2
- package/src/scripts/hooks/payload_stub.ts +16 -6
- package/src/scripts/hooks/permission_gate.ts +7 -7
- package/src/scripts/hooks/push_settle_hook.ts +32 -3
- package/src/scripts/hooks/replay_hook.ts +43 -0
- package/src/scripts/hooks/reread_guard_hook.ts +87 -10
- package/src/scripts/hooks/rtk_wrap_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +8 -1
- package/src/scripts/hooks/session_eol_hook.ts +48 -1
- package/src/scripts/hooks/source_first_gate_hook.ts +6 -8
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +203 -10
- package/src/scripts/hooks/state_io.ts +2 -2
- package/src/scripts/hooks/svg_obligation.ts +553 -0
- package/src/scripts/hooks/svg_rules_loader.ts +133 -0
- package/src/scripts/hooks/tool_result_bytes_hook.ts +14 -5
- package/src/scripts/hooks/turn_end_gate_hook.ts +5 -3
- package/src/scripts/hooks/ui_route_nudge_hook.ts +2 -12
- package/src/scripts/hooks_doctor.ts +31 -0
- package/src/scripts/hooks_status.ts +58 -3
- package/src/scripts/inbox_source_census.ts +74 -13
- package/src/scripts/install +25 -3
- package/src/scripts/install.ts +60 -91
- package/src/scripts/interruption_report.ts +11 -1
- package/src/scripts/layer_baseline_report.ts +267 -0
- package/src/scripts/learning_sidecar.ts +1 -1
- package/src/scripts/lint_agents_layout.ts +3 -0
- package/src/scripts/lint_blocker_citations.ts +104 -0
- package/src/scripts/lint_breaking_changes_index.ts +40 -7
- package/src/scripts/lint_consolidation_lineage.ts +150 -19
- package/src/scripts/lint_consumer_internal_refs.ts +90 -42
- package/src/scripts/lint_context_spine_usage.ts +21 -0
- package/src/scripts/lint_design_antipattern_parity.ts +39 -7
- package/src/scripts/lint_design_quality.ts +60 -12
- package/src/scripts/lint_evidence_artifacts.ts +31 -0
- package/src/scripts/lint_example_fences.ts +11 -2
- package/src/scripts/lint_ghostwriter_source.ts +42 -10
- package/src/scripts/lint_hook_manifest.ts +94 -0
- package/src/scripts/lint_marketplace_install_completeness.ts +4 -0
- package/src/scripts/lint_mcp_config_security.ts +9 -11
- package/src/scripts/lint_motion_authority_drift.ts +27 -0
- package/src/scripts/lint_plan_risk_register.ts +28 -1
- package/src/scripts/lint_positioning.ts +4 -4
- package/src/scripts/lint_provenance.ts +1 -0
- package/src/scripts/lint_roadmap_blockers.ts +65 -4
- package/src/scripts/lint_roadmap_complexity.ts +98 -0
- package/src/scripts/lint_skill_link_reach.ts +251 -9
- package/src/scripts/lint_skill_procedures.ts +252 -0
- package/src/scripts/lint_skill_scripts_readonly.ts +12 -1
- package/src/scripts/lint_subagent_determinism.ts +14 -1
- package/src/scripts/lint_svg_asset.ts +365 -0
- package/src/scripts/lint_ticked_unmet_criteria.ts +300 -21
- package/src/scripts/mcp_registry_submit.sh +2 -2
- package/src/scripts/mcp_server/consumer_tool_catalog.json +4 -4
- package/src/scripts/mcp_server/suggest_skill.ts +89 -4
- package/src/scripts/mcp_server/tools.ts +2 -33
- package/src/scripts/measure_skill_ranker_baseline.ts +203 -5
- package/src/scripts/measure_turn_end_gate.ts +61 -1
- package/src/scripts/merge_clearance.ts +225 -0
- package/src/scripts/migrate_command_suggestions.ts +1 -1
- package/src/scripts/network_sources.ts +236 -0
- package/src/scripts/no_go_deny_rows.ts +225 -0
- package/src/scripts/orchestration_record.ts +15 -2
- package/src/scripts/plan_test_shards.ts +103 -0
- package/src/scripts/pr_url_reminder_hook.ts +10 -0
- package/src/scripts/print_required_checks.ts +5 -5
- package/src/scripts/probe_depth_shadow.ts +406 -0
- package/src/scripts/probe_turnaround.ts +342 -10
- package/src/scripts/ratification_subject.ts +176 -0
- package/src/scripts/rdp_gate_classify.ts +1 -0
- package/src/scripts/rdp_quality_eval.ts +1 -0
- package/src/scripts/release.ts +26 -55
- package/src/scripts/release_drill.ts +52 -23
- package/src/scripts/release_env.ts +37 -0
- package/src/scripts/release_publication.ts +11 -1
- package/src/scripts/report_consumer_gate_reach.ts +233 -0
- package/src/scripts/report_merge_gate_load.ts +295 -0
- package/src/scripts/report_project_contract.ts +123 -0
- package/src/scripts/report_reducible_classes.ts +342 -0
- package/src/scripts/report_required_checks_drift.ts +5 -3
- package/src/scripts/report_skill_host_census.ts +199 -0
- package/src/scripts/report_standing_payload_by_host.ts +7 -7
- package/src/scripts/retro.ts +758 -0
- package/src/scripts/retro_facts.ts +575 -0
- package/src/scripts/retro_queue.ts +739 -0
- package/src/scripts/retro_session_facts.ts +218 -0
- package/src/scripts/retro_sweep.ts +493 -0
- package/src/scripts/retro_triage.ts +126 -0
- package/src/scripts/roadmap_context.ts +2 -2
- package/src/scripts/roadmap_drain_queue.ts +601 -0
- package/src/scripts/run_supervise.ts +24 -2
- package/src/scripts/schemas/agent-settings.schema.json +0 -16
- package/src/scripts/schemas/command.schema.json +3 -3
- package/src/scripts/schemas/review-findings.schema.json +8 -0
- package/src/scripts/schemas/skill.schema.json +7 -6
- package/src/scripts/schemas/subagent.schema.json +33 -2
- package/src/scripts/score_skill_selection.ts +5 -2
- package/src/scripts/second_brain_retrieval.ts +1 -0
- package/src/scripts/second_brain_run.ts +1 -0
- package/src/scripts/self_repair_cli.ts +563 -87
- package/src/scripts/self_review_gate.ts +59 -4
- package/src/scripts/session_register_hook.ts +91 -4
- package/src/scripts/sessions_cli.ts +100 -5
- package/src/scripts/skill_collision_clusters.ts +1 -1
- package/src/scripts/skill_tools/score_skill_relevance.ts +31 -2
- package/src/scripts/skill_trigger_eval.ts +4 -2
- package/src/scripts/source_adapters.ts +183 -0
- package/src/scripts/step_receipt.ts +100 -0
- package/src/scripts/sync_pr_branch.ts +4 -0
- package/src/scripts/test_council_qualification.ts +1 -1
- package/src/scripts/trigger_eval_grandfather.json +3 -49
- package/src/scripts/trigger_eval_rotation.ts +31 -3
- package/src/scripts/ui_conformance_probe.ts +405 -1
- package/src/scripts/update_counts.ts +230 -11
- package/src/scripts/update_invite_hook.ts +245 -0
- package/src/scripts/update_notice_hook.ts +298 -0
- package/src/scripts/validate_frontmatter.ts +85 -0
- package/src/scripts/worker_reuse_shadow_report.ts +121 -0
- package/src/scripts/worktree_cleanup_check.ts +915 -121
- package/src/server/io/yamlIO.ts +2 -2
- package/src/server/schemas/settings.ts +37 -23
- package/src/shared/brandTokenDocument.ts +46 -0
- package/src/shared/brandTokenPaths.ts +43 -0
- package/src/shared/settingsCarveOut.ts +0 -8
- package/src/shared/skillRanking.ts +106 -5
- package/src/shared/userMd/formAdapter.ts +15 -1
- package/src/shared/userMd/schema.ts +22 -0
- package/src/templates/agent-user.md +4 -0
- package/src/templates/agent-user.yml +4 -0
- package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
- package/dist/cli-delegate/chunk-AL3RZ22D.js +0 -74
- package/dist/cli-delegate/chunk-KB5FZ655.js +0 -208
- package/dist/cli-delegate/chunk-MDPPKQCQ.js +0 -875
- package/dist/cli-delegate/chunk-MSUL5NYP.js +0 -1123
- package/dist/cli-delegate/chunk-OAFSJBHH.js +0 -272
- package/dist/ui/assets/index-C3djEJah.js +0 -23
- package/src/scripts/_lib/continuation_ladder.ts +0 -335
- package/src/scripts/hooks/run_continuation_hook.ts +0 -1524
|
@@ -18,11 +18,18 @@
|
|
|
18
18
|
* /worktree status + verify must surface).
|
|
19
19
|
* inventory Classify EVERY worktree safe / review / live and
|
|
20
20
|
* report the counts, the review reasons, and a
|
|
21
|
-
* prepared removal plan for the safe set.
|
|
22
|
-
* only
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* prepared removal plan for the safe set. Reports
|
|
22
|
+
* only.
|
|
23
|
+
* apply Remove the safe set: `git worktree remove` without
|
|
24
|
+
* `--force`, then `git worktree prune`, one journal
|
|
25
|
+
* line per removal. `safe` here is the scoped set the
|
|
26
|
+
* owner made housekeeping on 2026-10-21
|
|
27
|
+
* (road-to-worktrees-that-leave-after-the-merge D1):
|
|
28
|
+
* proven merged, clean, no local-only ignored file,
|
|
29
|
+
* not live. Every other removal stays a decision.
|
|
30
|
+
* Branches are never deleted by this tool.
|
|
31
|
+
* register <path> Record a worktree this package created, so a
|
|
32
|
+
* sibling outside the standard roots is eligible.
|
|
26
33
|
*
|
|
27
34
|
* Reachability counts ALL other refs — local branches, remotes, AND tags
|
|
28
35
|
* (a branch whose only other ref is a tag is still reachable elsewhere).
|
|
@@ -34,7 +41,14 @@ import { execFileSync } from 'node:child_process';
|
|
|
34
41
|
import * as fs from 'node:fs';
|
|
35
42
|
import * as path from 'node:path';
|
|
36
43
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
44
|
+
import { parse as parseYaml } from 'yaml';
|
|
45
|
+
import { load_agent_settings, settings_layer_states, user_global_settings_paths } from './_lib/agent_settings.js';
|
|
37
46
|
import { read_live_records, register_dir } from './_lib/session_register.js';
|
|
47
|
+
import { git, gitEnv, timeLeftMs, tryGit, withDeadline } from './_lib/worktree_git.js';
|
|
48
|
+
import { localOnlyIgnored } from './_lib/worktree_ignored.js';
|
|
49
|
+
|
|
50
|
+
export { withDeadline } from './_lib/worktree_git.js';
|
|
51
|
+
export { localOnlyIgnored, REGENERABLE_IGNORED } from './_lib/worktree_ignored.js';
|
|
38
52
|
|
|
39
53
|
export interface CheckResult {
|
|
40
54
|
allowed: boolean;
|
|
@@ -46,21 +60,6 @@ export interface ScopeOverlap {
|
|
|
46
60
|
b: { worktree: string; own: string };
|
|
47
61
|
}
|
|
48
62
|
|
|
49
|
-
function git(cwd: string, args: string[]): string {
|
|
50
|
-
return execFileSync('git', ['-C', cwd, ...args], {
|
|
51
|
-
encoding: 'utf-8',
|
|
52
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function tryGit(cwd: string, args: string[]): string | null {
|
|
57
|
-
try {
|
|
58
|
-
return git(cwd, args);
|
|
59
|
-
} catch {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
63
|
/**
|
|
65
64
|
* `git status` with the index left alone. Plain `git status` refreshes the
|
|
66
65
|
* on-disk index, which bumps its mtime — and the inventory mode reads that
|
|
@@ -126,6 +125,8 @@ export interface WorktreeEntry {
|
|
|
126
125
|
path: string;
|
|
127
126
|
/** Short branch name (`refs/heads/` stripped), or `null` when detached. */
|
|
128
127
|
branch: string | null;
|
|
128
|
+
/** `git worktree lock` was set on it — never removable here. */
|
|
129
|
+
locked?: boolean;
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
export function listWorktrees(repoPath: string): WorktreeEntry[] {
|
|
@@ -135,17 +136,19 @@ export function listWorktrees(repoPath: string): WorktreeEntry[] {
|
|
|
135
136
|
for (const line of out.split('\n')) {
|
|
136
137
|
if (line.startsWith('worktree ')) {
|
|
137
138
|
if (current.path !== undefined) {
|
|
138
|
-
entries.push({ path: current.path, branch: current.branch ?? null });
|
|
139
|
+
entries.push({ path: current.path, branch: current.branch ?? null, locked: current.locked === true });
|
|
139
140
|
}
|
|
140
141
|
current = { path: line.slice('worktree '.length) };
|
|
141
142
|
} else if (line.startsWith('branch ')) {
|
|
142
143
|
current.branch = line.slice('branch '.length).replace(/^refs\/heads\//, '');
|
|
143
144
|
} else if (line === 'detached') {
|
|
144
145
|
current.branch = null;
|
|
146
|
+
} else if (line === 'locked' || line.startsWith('locked ')) {
|
|
147
|
+
current.locked = true;
|
|
145
148
|
}
|
|
146
149
|
}
|
|
147
150
|
if (current.path !== undefined) {
|
|
148
|
-
entries.push({ path: current.path, branch: current.branch ?? null });
|
|
151
|
+
entries.push({ path: current.path, branch: current.branch ?? null, locked: current.locked === true });
|
|
149
152
|
}
|
|
150
153
|
return entries;
|
|
151
154
|
}
|
|
@@ -266,7 +269,13 @@ export interface InventoryRow {
|
|
|
266
269
|
reasons: string[];
|
|
267
270
|
/** The branch is an ancestor of the trunk, so `git branch -d` is safe. */
|
|
268
271
|
mergedIntoTrunk: boolean;
|
|
272
|
+
/** Strongest available proof the branch's work is on the trunk, or `null`. */
|
|
273
|
+
mergeEvidence: MergeEvidence | null;
|
|
274
|
+
/** Branch tip at inventory time — what a removal journals. */
|
|
275
|
+
tip: string | null;
|
|
269
276
|
standardLocation: boolean;
|
|
277
|
+
/** A registration this package wrote names this worktree. */
|
|
278
|
+
registered: boolean;
|
|
270
279
|
/** Main worktree — never removable, and never counted as residue. */
|
|
271
280
|
isMain: boolean;
|
|
272
281
|
}
|
|
@@ -274,6 +283,8 @@ export interface InventoryRow {
|
|
|
274
283
|
export interface Inventory {
|
|
275
284
|
trunk: string;
|
|
276
285
|
rows: InventoryRow[];
|
|
286
|
+
/** What classified these rows — reused to re-check a row right before removal. */
|
|
287
|
+
context: ClassifyContext;
|
|
277
288
|
counts: {
|
|
278
289
|
total: number;
|
|
279
290
|
safe: number;
|
|
@@ -306,6 +317,196 @@ function isAncestor(repoPath: string, branch: string, trunk: string): boolean {
|
|
|
306
317
|
}
|
|
307
318
|
}
|
|
308
319
|
|
|
320
|
+
/**
|
|
321
|
+
* How a branch's work is known to be on the trunk, strongest first.
|
|
322
|
+
*
|
|
323
|
+
* `merged-ancestor` is the graph proof. It never holds for a squash merge,
|
|
324
|
+
* which is how most PRs on a squash-merging trunk land — so on its own it
|
|
325
|
+
* classed every one of them as unmerged work forever. `merged-pr` is the forge
|
|
326
|
+
* proof: the host reports the branch's PR MERGED **and** the branch tip still
|
|
327
|
+
* equals that PR's head, so no commit was made after the merge. `merged-
|
|
328
|
+
* equivalent` is the offline proof, used only when the forge cannot be read:
|
|
329
|
+
* merging the branch into the trunk produces the trunk's own tree, so nothing
|
|
330
|
+
* on the branch is missing from it. `closed-unmerged` is never removed.
|
|
331
|
+
*/
|
|
332
|
+
export type MergeEvidence = 'merged-ancestor' | 'merged-pr' | 'merged-equivalent' | 'closed-unmerged';
|
|
333
|
+
|
|
334
|
+
export interface PrRecord {
|
|
335
|
+
state: 'MERGED' | 'CLOSED' | 'OPEN';
|
|
336
|
+
headOid: string;
|
|
337
|
+
mergedAt: string | null;
|
|
338
|
+
/** The PR's base branch; a merge into another branch proves nothing about the trunk. */
|
|
339
|
+
base: string | null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** PR records keyed by head branch name. `null` means the forge was not read. */
|
|
343
|
+
export type PrIndex = Map<string, PrRecord[]>;
|
|
344
|
+
|
|
345
|
+
export type GhRunner = (args: string[], timeoutMs: number) => string;
|
|
346
|
+
|
|
347
|
+
const defaultGh =
|
|
348
|
+
(cwd: string): GhRunner =>
|
|
349
|
+
(args, timeoutMs) =>
|
|
350
|
+
execFileSync('gh', args, {
|
|
351
|
+
cwd,
|
|
352
|
+
encoding: 'utf-8',
|
|
353
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
354
|
+
timeout: timeoutMs,
|
|
355
|
+
env: gitEnv(),
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* ONE forge read for the whole estate. The retro reads PR state per PR, which
|
|
360
|
+
* scales with the number of worktrees; an estate of dozens needs one list call.
|
|
361
|
+
* Any failure — no `gh`, no login, no network, a timeout — returns `null`, and
|
|
362
|
+
* the caller falls back to the offline proof rather than guessing.
|
|
363
|
+
*/
|
|
364
|
+
export function readPrIndex(
|
|
365
|
+
repoPath: string,
|
|
366
|
+
opts: { run?: GhRunner; timeoutMs?: number; limit?: number } = {},
|
|
367
|
+
): PrIndex | null {
|
|
368
|
+
const run = opts.run ?? defaultGh(repoPath);
|
|
369
|
+
try {
|
|
370
|
+
const raw = run(
|
|
371
|
+
['pr', 'list', '--state', 'all', '--limit', String(opts.limit ?? 1000), '--json', 'headRefName,headRefOid,state,mergedAt,baseRefName'],
|
|
372
|
+
Math.min(opts.timeoutMs ?? 15000, timeLeftMs() ?? Number.POSITIVE_INFINITY),
|
|
373
|
+
);
|
|
374
|
+
const rows = JSON.parse(raw) as Array<{
|
|
375
|
+
headRefName?: string;
|
|
376
|
+
headRefOid?: string;
|
|
377
|
+
state?: string;
|
|
378
|
+
mergedAt?: string | null;
|
|
379
|
+
baseRefName?: string;
|
|
380
|
+
}>;
|
|
381
|
+
if (!Array.isArray(rows)) return null;
|
|
382
|
+
const index: PrIndex = new Map();
|
|
383
|
+
for (const r of rows) {
|
|
384
|
+
if (typeof r.headRefName !== 'string' || typeof r.headRefOid !== 'string') continue;
|
|
385
|
+
if (r.state !== 'MERGED' && r.state !== 'CLOSED' && r.state !== 'OPEN') continue;
|
|
386
|
+
const list = index.get(r.headRefName) ?? [];
|
|
387
|
+
list.push({
|
|
388
|
+
state: r.state,
|
|
389
|
+
headOid: r.headRefOid,
|
|
390
|
+
mergedAt: typeof r.mergedAt === 'string' ? r.mergedAt : null,
|
|
391
|
+
base: typeof r.baseRefName === 'string' ? r.baseRefName : null,
|
|
392
|
+
});
|
|
393
|
+
index.set(r.headRefName, list);
|
|
394
|
+
}
|
|
395
|
+
return index;
|
|
396
|
+
} catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function branchTip(repoPath: string, branch: string): string | null {
|
|
402
|
+
const out = tryGit(repoPath, ['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`]);
|
|
403
|
+
return out === null ? null : out.trim();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** Merging `branch` into `trunk` yields the trunk's own tree. */
|
|
407
|
+
function mergeEquivalent(repoPath: string, branch: string, trunk: string): boolean {
|
|
408
|
+
const merged = tryGit(repoPath, ['merge-tree', '--write-tree', trunk, branch]);
|
|
409
|
+
const trunkTree = tryGit(repoPath, ['rev-parse', `${trunk}^{tree}`]);
|
|
410
|
+
if (merged === null || trunkTree === null) return false;
|
|
411
|
+
return merged.split('\n')[0]!.trim() === trunkTree.trim();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export interface MergeVerdict {
|
|
415
|
+
evidence: MergeEvidence | null;
|
|
416
|
+
/** When the forge recorded the merge; `null` for the graph and offline proofs. */
|
|
417
|
+
mergedAt: string | null;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export function mergeEvidence(
|
|
421
|
+
repoPath: string,
|
|
422
|
+
branch: string,
|
|
423
|
+
trunk: string,
|
|
424
|
+
prIndex: PrIndex | null,
|
|
425
|
+
): MergeVerdict {
|
|
426
|
+
if (isAncestor(repoPath, branch, trunk)) return { evidence: 'merged-ancestor', mergedAt: null };
|
|
427
|
+
if (prIndex !== null) {
|
|
428
|
+
const tip = branchTip(repoPath, branch);
|
|
429
|
+
// `refs/remotes/origin/main`, `refs/heads/main` and the short `origin/main`
|
|
430
|
+
// a `--trunk=` flag may pass all name the base branch `main`.
|
|
431
|
+
const trunkName = trunk.replace(/^refs\/(?:remotes\/[^/]+|heads)\//, '').replace(/^(?:origin|upstream)\//, '');
|
|
432
|
+
// Only a PR into the trunk speaks for the trunk; one merged into a
|
|
433
|
+
// release branch is not a record of this branch at all.
|
|
434
|
+
const prs = (prIndex.get(branch) ?? []).filter((p) => p.base === trunkName);
|
|
435
|
+
const merged = prs.find((p) => p.state === 'MERGED' && p.headOid === tip);
|
|
436
|
+
if (merged !== undefined) return { evidence: 'merged-pr', mergedAt: merged.mergedAt };
|
|
437
|
+
if (prs.length > 0 && prs.every((p) => p.state === 'CLOSED')) {
|
|
438
|
+
return { evidence: 'closed-unmerged', mergedAt: null };
|
|
439
|
+
}
|
|
440
|
+
// A PR record exists but none merged at this tip: the forge has spoken.
|
|
441
|
+
if (prs.length > 0) return { evidence: null, mergedAt: null };
|
|
442
|
+
// No record at all — the PR is older than the window read, or the branch
|
|
443
|
+
// never had one. The tree proof is still a proof, so fall through to it.
|
|
444
|
+
}
|
|
445
|
+
if (mergeEquivalent(repoPath, branch, trunk)) return { evidence: 'merged-equivalent', mergedAt: null };
|
|
446
|
+
return { evidence: null, mergedAt: null };
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** One line per worktree this package created, in the repository's common git dir. */
|
|
450
|
+
export const REGISTRY_FILE = 'agent-config-worktrees.jsonl';
|
|
451
|
+
/** One line per worktree this tool removed — the tip SHA recreates the branch. */
|
|
452
|
+
export const REMOVAL_JOURNAL = 'agent-config-worktree-removals.jsonl';
|
|
453
|
+
|
|
454
|
+
function commonDir(repoPath: string): string | null {
|
|
455
|
+
const out = tryGit(repoPath, ['rev-parse', '--path-format=absolute', '--git-common-dir']);
|
|
456
|
+
return out === null ? null : out.trim();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export interface RegistryEntry {
|
|
460
|
+
path: string;
|
|
461
|
+
branch: string | null;
|
|
462
|
+
created_by: 'agent-config';
|
|
463
|
+
purpose: string;
|
|
464
|
+
at: string;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export function registerWorktree(repoPath: string, worktreePath: string, purpose: string, now = new Date()): RegistryEntry {
|
|
468
|
+
const dir = commonDir(repoPath);
|
|
469
|
+
if (dir === null) throw new Error(`not a git repository: ${repoPath}`);
|
|
470
|
+
const headRef = tryGit(worktreePath, ['symbolic-ref', '-q', '--short', 'HEAD']);
|
|
471
|
+
const entry: RegistryEntry = {
|
|
472
|
+
path: canonical(worktreePath),
|
|
473
|
+
branch: headRef === null ? null : headRef.trim(),
|
|
474
|
+
created_by: 'agent-config',
|
|
475
|
+
purpose,
|
|
476
|
+
at: now.toISOString(),
|
|
477
|
+
};
|
|
478
|
+
fs.appendFileSync(path.join(dir, REGISTRY_FILE), `${JSON.stringify(entry)}\n`);
|
|
479
|
+
return entry;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export function readRegistry(repoPath: string): Set<string> {
|
|
483
|
+
const dir = commonDir(repoPath);
|
|
484
|
+
const paths = new Set<string>();
|
|
485
|
+
if (dir === null) return paths;
|
|
486
|
+
let text = '';
|
|
487
|
+
try {
|
|
488
|
+
text = fs.readFileSync(path.join(dir, REGISTRY_FILE), 'utf-8');
|
|
489
|
+
} catch {
|
|
490
|
+
return paths;
|
|
491
|
+
}
|
|
492
|
+
for (const line of text.split('\n')) {
|
|
493
|
+
try {
|
|
494
|
+
const e = JSON.parse(line) as Partial<RegistryEntry>;
|
|
495
|
+
if (e.created_by === 'agent-config' && typeof e.path === 'string') paths.add(canonical(e.path));
|
|
496
|
+
} catch {
|
|
497
|
+
/* a torn or foreign line is not a registration */
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return paths;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** `../<repo>-<suffix>` beside the main checkout: the shape this package's lanes use. */
|
|
504
|
+
export function isRepoSibling(mainPath: string, worktreePath: string): boolean {
|
|
505
|
+
const main = canonical(mainPath);
|
|
506
|
+
const wt = canonical(worktreePath);
|
|
507
|
+
return path.dirname(main) === path.dirname(wt) && path.basename(wt).startsWith(`${path.basename(main)}-`);
|
|
508
|
+
}
|
|
509
|
+
|
|
309
510
|
/**
|
|
310
511
|
* `basePath` MUST be the main worktree, never the invoking repo path.
|
|
311
512
|
*
|
|
@@ -380,6 +581,186 @@ export interface InventoryOptions {
|
|
|
380
581
|
now?: Date;
|
|
381
582
|
minAgeDays?: number | null;
|
|
382
583
|
excludeLiveSessions?: boolean;
|
|
584
|
+
/**
|
|
585
|
+
* Forge PR records for the squash-aware proof. `undefined` and `null` both
|
|
586
|
+
* mean "not read": the inventory then uses the graph and offline proofs only.
|
|
587
|
+
*/
|
|
588
|
+
prIndex?: PrIndex | null;
|
|
589
|
+
/**
|
|
590
|
+
* Hours a forge-proven merge must age before removal (D4). `0` on the
|
|
591
|
+
* `/pr:merge` path, where the session that merged has nothing left to do.
|
|
592
|
+
*/
|
|
593
|
+
mergeDelayHours?: number;
|
|
594
|
+
/** Override the trunk ref (default: origin/main → origin/master → main → master). */
|
|
595
|
+
trunk?: string;
|
|
596
|
+
/** See `ClassifyContext.mergedBranch`. */
|
|
597
|
+
mergedBranch?: string | null;
|
|
598
|
+
/** See `ClassifyContext.selfSessionId`. */
|
|
599
|
+
selfSessionId?: string | null;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/** D4 — a revert or follow-up can reuse the tree for a day. */
|
|
603
|
+
export const MERGE_DELAY_HOURS = 24;
|
|
604
|
+
|
|
605
|
+
export interface ClassifyContext {
|
|
606
|
+
trunk: string;
|
|
607
|
+
mainPath: string;
|
|
608
|
+
now: Date;
|
|
609
|
+
liveCutoff: number;
|
|
610
|
+
sessionPaths: Set<string>;
|
|
611
|
+
registry: Set<string>;
|
|
612
|
+
ageCutoff: number | null;
|
|
613
|
+
minAgeDays: number | null;
|
|
614
|
+
forge: { prIndex: PrIndex | null; mergeDelayHours: number };
|
|
615
|
+
/**
|
|
616
|
+
* The branch whose merge the calling session itself just performed
|
|
617
|
+
* (`/pr:merge`). Its git-dir mtimes are that merge work, not another
|
|
618
|
+
* session, so the mtime fallback is skipped for it; every other gate,
|
|
619
|
+
* the session register included, still applies.
|
|
620
|
+
*/
|
|
621
|
+
mergedBranch?: string | null;
|
|
622
|
+
/** The calling session's own register id, never counted as "another session". */
|
|
623
|
+
selfSessionId?: string | null;
|
|
624
|
+
/** Read sessions from the register (re-read before each removal). */
|
|
625
|
+
readSessions?: boolean;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** Live session worktrees from the register, minus the caller's own session. */
|
|
629
|
+
export function liveSessionPaths(mainPath: string, now: Date, selfSessionId: string | null = null): Set<string> {
|
|
630
|
+
const out = new Set<string>();
|
|
631
|
+
const dir = register_dir(mainPath);
|
|
632
|
+
if (dir === null) return out;
|
|
633
|
+
for (const rec of read_live_records(dir, { now })) {
|
|
634
|
+
if (selfSessionId !== null && rec.session_id === selfSessionId) continue;
|
|
635
|
+
out.add(canonical(rec.worktree));
|
|
636
|
+
}
|
|
637
|
+
return out;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/** One worktree, classified. Shared by the inventory and by the re-check run immediately before a removal. */
|
|
641
|
+
export function classifyEntry(e: WorktreeEntry, ctx: ClassifyContext): InventoryRow {
|
|
642
|
+
const { trunk, mainPath, now, liveCutoff, sessionPaths, registry, ageCutoff, minAgeDays, forge } = ctx;
|
|
643
|
+
const resolved = canonical(e.path);
|
|
644
|
+
const isMain = resolved === mainPath;
|
|
645
|
+
// `mainPath`, NOT `repoPath` — see isStandardLocation's contract.
|
|
646
|
+
const standardLocation = isMain ? true : isStandardLocation(mainPath, resolved);
|
|
647
|
+
const registered = registry.has(resolved);
|
|
648
|
+
const reasons: string[] = [];
|
|
649
|
+
const base = { path: e.path, branch: e.branch, standardLocation, registered, isMain };
|
|
650
|
+
|
|
651
|
+
if (!fs.existsSync(resolved)) {
|
|
652
|
+
return {
|
|
653
|
+
...base,
|
|
654
|
+
classification: 'review',
|
|
655
|
+
reasons: ['registration points at a missing directory — `git worktree prune` clears it'],
|
|
656
|
+
mergedIntoTrunk: false,
|
|
657
|
+
mergeEvidence: null,
|
|
658
|
+
tip: null,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const tip = e.branch === null ? null : branchTip(resolved, e.branch);
|
|
663
|
+
// The session register is the primary liveness source; a registered
|
|
664
|
+
// session in the tree makes it live whatever its mtimes say.
|
|
665
|
+
if (sessionPaths.has(resolved)) {
|
|
666
|
+
return {
|
|
667
|
+
...base,
|
|
668
|
+
classification: 'live',
|
|
669
|
+
reasons: ['a live agent session is registered in this worktree'],
|
|
670
|
+
mergedIntoTrunk: e.branch !== null && isAncestor(resolved, e.branch, trunk),
|
|
671
|
+
mergeEvidence: null,
|
|
672
|
+
tip,
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
// Fallback for sessions the register cannot see (another host, a plain
|
|
676
|
+
// terminal): recent git activity in the per-worktree git dir.
|
|
677
|
+
const activity = lastGitActivity(resolved);
|
|
678
|
+
const ownMerge = ctx.mergedBranch !== undefined && ctx.mergedBranch !== null && e.branch === ctx.mergedBranch;
|
|
679
|
+
if (!ownMerge && activity !== null && activity.getTime() >= liveCutoff) {
|
|
680
|
+
return {
|
|
681
|
+
...base,
|
|
682
|
+
classification: 'live',
|
|
683
|
+
reasons: [`git activity within the last ${LIVE_WINDOW_HOURS}h — another session may hold it`],
|
|
684
|
+
mergedIntoTrunk: e.branch !== null && isAncestor(resolved, e.branch, trunk),
|
|
685
|
+
mergeEvidence: null,
|
|
686
|
+
tip,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (isMain) reasons.push('main worktree — cannot be removed');
|
|
691
|
+
if (e.locked === true) reasons.push('locked (`git worktree unlock` first) — never removed here');
|
|
692
|
+
if (e.branch === null) {
|
|
693
|
+
reasons.push('detached HEAD — no branch to judge reachability for');
|
|
694
|
+
}
|
|
695
|
+
if (ageCutoff !== null && (activity === null || activity.getTime() >= ageCutoff)) {
|
|
696
|
+
// `activity === null` disqualifies rather than passes: an unreadable
|
|
697
|
+
// git-dir mtime is absence of evidence, and under an explicit
|
|
698
|
+
// minimum-age floor that has to read as "cannot show it is old
|
|
699
|
+
// enough", never as "old enough".
|
|
700
|
+
reasons.push(
|
|
701
|
+
activity === null
|
|
702
|
+
? `git activity unreadable — cannot prove ${minAgeDays}+ days of inactivity`
|
|
703
|
+
: `git activity within the last ${minAgeDays} days`,
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
const verdict =
|
|
707
|
+
e.branch === null
|
|
708
|
+
? { evidence: null, mergedAt: null }
|
|
709
|
+
: mergeEvidence(resolved, e.branch, trunk, forge.prIndex);
|
|
710
|
+
const mergedIntoTrunk = verdict.evidence === 'merged-ancestor';
|
|
711
|
+
const proven =
|
|
712
|
+
verdict.evidence === 'merged-ancestor' ||
|
|
713
|
+
verdict.evidence === 'merged-pr' ||
|
|
714
|
+
verdict.evidence === 'merged-equivalent';
|
|
715
|
+
|
|
716
|
+
// Outside the standard roots a tree is eligible only by a registration
|
|
717
|
+
// this package wrote, or by the `<repo>-*` sibling shape plus a merge
|
|
718
|
+
// proof (D2). Location alone never qualifies, and never disqualifies a
|
|
719
|
+
// tree this package created.
|
|
720
|
+
if (!standardLocation && !registered && !(proven && isRepoSibling(mainPath, resolved))) {
|
|
721
|
+
reasons.push('non-standard location — outside the conventional worktree roots');
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
if (e.branch !== null && verdict.evidence === 'closed-unmerged') {
|
|
725
|
+
reasons.push('PR closed unmerged — the work is not on the trunk');
|
|
726
|
+
} else if (e.branch !== null && !proven) {
|
|
727
|
+
reasons.push(`branch is not an ancestor of ${trunk} — unmerged work`);
|
|
728
|
+
const gate = checkWorktree(resolved);
|
|
729
|
+
if (!gate.allowed) reasons.push(...gate.reasons);
|
|
730
|
+
} else if (e.branch !== null) {
|
|
731
|
+
// A proven merge makes the unique-commit gate moot: an ancestor has
|
|
732
|
+
// every commit on the trunk, and a squash- or tree-proven branch's
|
|
733
|
+
// commits are exactly what the trunk already holds as one commit.
|
|
734
|
+
const status = statusPorcelain(resolved);
|
|
735
|
+
if (status.trim().length > 0) {
|
|
736
|
+
const n = status.trimEnd().split('\n').length;
|
|
737
|
+
reasons.push(`unsaved work — ${n} dirty or untracked path(s)`);
|
|
738
|
+
}
|
|
739
|
+
const local = localOnlyIgnored(resolved, trunk);
|
|
740
|
+
if (local.length > 0) {
|
|
741
|
+
const shown = local.slice(0, 5).join(', ');
|
|
742
|
+
reasons.push(`local-only files — ${local.length} ignored path(s) a rebuild would not restore: ${shown}`);
|
|
743
|
+
}
|
|
744
|
+
if (verdict.evidence === 'merged-pr' && forge.mergeDelayHours > 0) {
|
|
745
|
+
// The forge proof carries its own clock; an absent or unparseable
|
|
746
|
+
// merge time cannot show the delay has passed, so it keeps the tree.
|
|
747
|
+
const age = verdict.mergedAt === null ? Number.NaN : now.getTime() - Date.parse(verdict.mergedAt);
|
|
748
|
+
if (!Number.isFinite(age)) {
|
|
749
|
+
reasons.push('merge time unreadable — cannot show the follow-up delay has passed');
|
|
750
|
+
} else if (age < forge.mergeDelayHours * 3600 * 1000) {
|
|
751
|
+
reasons.push(`merged less than ${forge.mergeDelayHours}h ago — kept for a follow-up`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
return {
|
|
757
|
+
...base,
|
|
758
|
+
classification: reasons.length === 0 ? 'safe' : 'review',
|
|
759
|
+
reasons,
|
|
760
|
+
mergedIntoTrunk,
|
|
761
|
+
mergeEvidence: verdict.evidence,
|
|
762
|
+
tip,
|
|
763
|
+
};
|
|
383
764
|
}
|
|
384
765
|
|
|
385
766
|
export function buildInventory(
|
|
@@ -391,7 +772,13 @@ export function buildInventory(
|
|
|
391
772
|
const now = opts.now ?? new Date();
|
|
392
773
|
const minAgeDays = opts.minAgeDays ?? null;
|
|
393
774
|
const ageCutoff = minAgeDays === null ? null : now.getTime() - minAgeDays * 86400 * 1000;
|
|
394
|
-
return buildInventoryInner(repoPath, now, ageCutoff, minAgeDays, opts.excludeLiveSessions === true
|
|
775
|
+
return buildInventoryInner(repoPath, now, ageCutoff, minAgeDays, opts.excludeLiveSessions === true, {
|
|
776
|
+
prIndex: opts.prIndex ?? null,
|
|
777
|
+
mergeDelayHours: opts.mergeDelayHours ?? MERGE_DELAY_HOURS,
|
|
778
|
+
trunk: opts.trunk ?? null,
|
|
779
|
+
mergedBranch: opts.mergedBranch ?? null,
|
|
780
|
+
selfSessionId: opts.selfSessionId ?? null,
|
|
781
|
+
});
|
|
395
782
|
}
|
|
396
783
|
|
|
397
784
|
function buildInventoryInner(
|
|
@@ -400,9 +787,10 @@ function buildInventoryInner(
|
|
|
400
787
|
ageCutoff: number | null,
|
|
401
788
|
minAgeDays: number | null,
|
|
402
789
|
excludeLiveSessions: boolean,
|
|
790
|
+
forge: { prIndex: PrIndex | null; mergeDelayHours: number; trunk: string | null; mergedBranch: string | null; selfSessionId: string | null },
|
|
403
791
|
): Inventory {
|
|
404
792
|
const now = nowArg;
|
|
405
|
-
const trunk = resolveTrunk(repoPath);
|
|
793
|
+
const trunk = forge.trunk ?? resolveTrunk(repoPath);
|
|
406
794
|
const entries = listWorktrees(repoPath);
|
|
407
795
|
const mainPath = canonical(entries.length > 0 ? entries[0]!.path : repoPath);
|
|
408
796
|
const liveCutoff = now.getTime() - LIVE_WINDOW_HOURS * 3600 * 1000;
|
|
@@ -413,96 +801,19 @@ function buildInventoryInner(
|
|
|
413
801
|
// state before any session registers), or when every record has expired.
|
|
414
802
|
const sessionPaths = new Set<string>();
|
|
415
803
|
if (excludeLiveSessions) {
|
|
416
|
-
const
|
|
417
|
-
if (dir !== null) {
|
|
418
|
-
for (const rec of read_live_records(dir, { now })) {
|
|
419
|
-
sessionPaths.add(canonical(rec.worktree));
|
|
420
|
-
}
|
|
421
|
-
}
|
|
804
|
+
for (const p of liveSessionPaths(mainPath, now, forge.selfSessionId ?? null)) sessionPaths.add(p);
|
|
422
805
|
}
|
|
423
806
|
|
|
424
|
-
const
|
|
425
|
-
const resolved = canonical(e.path);
|
|
426
|
-
const isMain = resolved === mainPath;
|
|
427
|
-
// `mainPath`, NOT `repoPath` — see isStandardLocation's contract.
|
|
428
|
-
const standardLocation = isMain ? true : isStandardLocation(mainPath, resolved);
|
|
429
|
-
const reasons: string[] = [];
|
|
430
|
-
|
|
431
|
-
if (!fs.existsSync(resolved)) {
|
|
432
|
-
return {
|
|
433
|
-
path: e.path,
|
|
434
|
-
branch: e.branch,
|
|
435
|
-
classification: 'review',
|
|
436
|
-
reasons: ['registration points at a missing directory — `git worktree prune` clears it'],
|
|
437
|
-
mergedIntoTrunk: false,
|
|
438
|
-
standardLocation,
|
|
439
|
-
isMain,
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const activity = lastGitActivity(resolved);
|
|
444
|
-
if (activity !== null && activity.getTime() >= liveCutoff) {
|
|
445
|
-
return {
|
|
446
|
-
path: e.path,
|
|
447
|
-
branch: e.branch,
|
|
448
|
-
classification: 'live',
|
|
449
|
-
reasons: [`git activity within the last ${LIVE_WINDOW_HOURS}h — another session may hold it`],
|
|
450
|
-
mergedIntoTrunk: e.branch !== null && isAncestor(resolved, e.branch, trunk),
|
|
451
|
-
standardLocation,
|
|
452
|
-
isMain,
|
|
453
|
-
};
|
|
454
|
-
}
|
|
807
|
+
const registry = readRegistry(mainPath);
|
|
455
808
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
// `activity === null` disqualifies rather than passes: an unreadable
|
|
465
|
-
// git-dir mtime is absence of evidence, and under an explicit
|
|
466
|
-
// minimum-age floor that has to read as "cannot show it is old
|
|
467
|
-
// enough", never as "old enough".
|
|
468
|
-
reasons.push(
|
|
469
|
-
activity === null
|
|
470
|
-
? `git activity unreadable — cannot prove ${minAgeDays}+ days of inactivity`
|
|
471
|
-
: `git activity within the last ${minAgeDays} days`,
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
if (!standardLocation) {
|
|
475
|
-
reasons.push('non-standard location — outside the conventional worktree roots');
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const mergedIntoTrunk = e.branch !== null && isAncestor(resolved, e.branch, trunk);
|
|
479
|
-
|
|
480
|
-
// A branch that is an ancestor of the trunk has every commit reachable
|
|
481
|
-
// from the trunk ref, so the expensive rev-list gate cannot find a
|
|
482
|
-
// unique commit. Only run it for the branches that are NOT merged —
|
|
483
|
-
// that is where it can actually refuse.
|
|
484
|
-
if (e.branch !== null && !mergedIntoTrunk) {
|
|
485
|
-
reasons.push(`branch is not an ancestor of ${trunk} — unmerged work`);
|
|
486
|
-
const gate = checkWorktree(resolved);
|
|
487
|
-
if (!gate.allowed) reasons.push(...gate.reasons);
|
|
488
|
-
} else if (e.branch !== null) {
|
|
489
|
-
const status = statusPorcelain(resolved);
|
|
490
|
-
if (status.trim().length > 0) {
|
|
491
|
-
const n = status.trimEnd().split('\n').length;
|
|
492
|
-
reasons.push(`unsaved work — ${n} dirty or untracked path(s)`);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return {
|
|
497
|
-
path: e.path,
|
|
498
|
-
branch: e.branch,
|
|
499
|
-
classification: reasons.length === 0 ? 'safe' : 'review',
|
|
500
|
-
reasons,
|
|
501
|
-
mergedIntoTrunk,
|
|
502
|
-
standardLocation,
|
|
503
|
-
isMain,
|
|
504
|
-
};
|
|
505
|
-
});
|
|
809
|
+
const ctx: ClassifyContext = {
|
|
810
|
+
trunk, mainPath, now, liveCutoff, sessionPaths, registry, ageCutoff, minAgeDays,
|
|
811
|
+
forge: { prIndex: forge.prIndex, mergeDelayHours: forge.mergeDelayHours },
|
|
812
|
+
mergedBranch: forge.mergedBranch,
|
|
813
|
+
selfSessionId: forge.selfSessionId,
|
|
814
|
+
readSessions: excludeLiveSessions,
|
|
815
|
+
};
|
|
816
|
+
const rows: InventoryRow[] = entries.map((e) => classifyEntry(e, ctx));
|
|
506
817
|
|
|
507
818
|
const reviewReasons: Record<string, number> = {};
|
|
508
819
|
for (const r of rows) {
|
|
@@ -516,6 +827,7 @@ function buildInventoryInner(
|
|
|
516
827
|
return {
|
|
517
828
|
trunk,
|
|
518
829
|
rows,
|
|
830
|
+
context: ctx,
|
|
519
831
|
counts: {
|
|
520
832
|
total: rows.length,
|
|
521
833
|
safe: rows.filter((r) => r.classification === 'safe').length,
|
|
@@ -528,6 +840,15 @@ function buildInventoryInner(
|
|
|
528
840
|
};
|
|
529
841
|
}
|
|
530
842
|
|
|
843
|
+
/**
|
|
844
|
+
* One argv element for a printed POSIX shell line, always single-quoted. A
|
|
845
|
+
* branch name can come from a pull request's author, and git allows `$`, `(`,
|
|
846
|
+
* `)` and backticks in it, so double quotes would let a copied line run it.
|
|
847
|
+
*/
|
|
848
|
+
export function shQuote(arg: string): string {
|
|
849
|
+
return `'${arg.replaceAll("'", `'\\''`)}'`;
|
|
850
|
+
}
|
|
851
|
+
|
|
531
852
|
/**
|
|
532
853
|
* The removal commands for the safe set — printed for a human to review and
|
|
533
854
|
* run, never executed here. `git branch -d` (never `-D`) so git itself
|
|
@@ -537,17 +858,417 @@ export function removalPlan(inv: Inventory): string[] {
|
|
|
537
858
|
return inv.rows
|
|
538
859
|
.filter((r) => r.classification === 'safe')
|
|
539
860
|
.map((r) => {
|
|
540
|
-
const remove = `git worktree remove ${
|
|
861
|
+
const remove = `git worktree remove ${shQuote(r.path)}`;
|
|
541
862
|
return r.branch !== null && r.mergedIntoTrunk
|
|
542
|
-
? `${remove} && git branch -d ${
|
|
863
|
+
? `${remove} && git branch -d ${shQuote(r.branch)}`
|
|
543
864
|
: remove;
|
|
544
865
|
});
|
|
545
866
|
}
|
|
546
867
|
|
|
868
|
+
export interface RemovalOutcome {
|
|
869
|
+
path: string;
|
|
870
|
+
branch: string | null;
|
|
871
|
+
tip: string | null;
|
|
872
|
+
evidence: MergeEvidence | null;
|
|
873
|
+
removed: boolean;
|
|
874
|
+
/** git's own refusal text when it declined, e.g. a tree that turned dirty. */
|
|
875
|
+
error: string | null;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/** Per-repository removal lock in the common git dir; stale after this long. */
|
|
879
|
+
export const REMOVAL_LOCK = 'agent-config-worktree-removal.lock';
|
|
880
|
+
const LOCK_STALE_MS = 10 * 60 * 1000;
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Take the per-repository removal lock, or `null` when another pass holds it.
|
|
884
|
+
* Two session starts in two worktrees of one repository must not both judge and
|
|
885
|
+
* remove the same tree; the second one simply does nothing this time.
|
|
886
|
+
*/
|
|
887
|
+
function takeRemovalLock(mainPath: string, now: Date): (() => void) | null {
|
|
888
|
+
const dir = commonDir(mainPath);
|
|
889
|
+
if (dir === null) return null;
|
|
890
|
+
const file = path.join(dir, REMOVAL_LOCK);
|
|
891
|
+
// The token names THIS holder: release deletes the lock only while it is
|
|
892
|
+
// still ours, so a pass that outlived the stale window cannot delete the
|
|
893
|
+
// lock a later pass took over.
|
|
894
|
+
const token = `${process.pid} ${now.toISOString()} ${Math.random().toString(36).slice(2)}`;
|
|
895
|
+
const tryCreate = (): boolean => {
|
|
896
|
+
try {
|
|
897
|
+
fs.writeFileSync(file, `${token}\n`, { flag: 'wx' });
|
|
898
|
+
return true;
|
|
899
|
+
} catch {
|
|
900
|
+
return false;
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
if (!tryCreate()) {
|
|
904
|
+
try {
|
|
905
|
+
if (now.getTime() - fs.statSync(file).mtimeMs < LOCK_STALE_MS) return null;
|
|
906
|
+
// Unlink only the stale lock that was judged: a pass that took it over
|
|
907
|
+
// in between wrote a new token, and that lock is not ours to delete.
|
|
908
|
+
const judged = fs.readFileSync(file, 'utf-8');
|
|
909
|
+
const aside = `${file}.${process.pid}.stale`;
|
|
910
|
+
fs.renameSync(file, aside);
|
|
911
|
+
if (fs.readFileSync(aside, 'utf-8') !== judged) {
|
|
912
|
+
fs.renameSync(aside, file);
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
fs.unlinkSync(aside);
|
|
916
|
+
} catch {
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
if (!tryCreate()) return null;
|
|
920
|
+
}
|
|
921
|
+
return () => {
|
|
922
|
+
try {
|
|
923
|
+
if (fs.readFileSync(file, 'utf-8').trim() === token) fs.unlinkSync(file);
|
|
924
|
+
} catch {
|
|
925
|
+
/* already gone — a stale-lock sweep by another pass */
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Remove the safe set. Plain `git worktree remove`, without the force flag, so git
|
|
932
|
+
* re-checks cleanliness itself at the moment of removal. Before that, every
|
|
933
|
+
* candidate is classified AGAIN, immediately before its own removal, under the
|
|
934
|
+
* repository lock: a tree that turned dirty, gained a local-only file, gained a
|
|
935
|
+
* session or moved its tip since the inventory is kept (council 2026-10-21,
|
|
936
|
+
* check-to-remove). The main worktree and every non-safe row are untouchable
|
|
937
|
+
* here by construction. Branches are left alone (D3); the journal carries the
|
|
938
|
+
* tip SHA, which is enough to recreate one.
|
|
939
|
+
*/
|
|
940
|
+
export function applyRemovals(
|
|
941
|
+
repoPath: string,
|
|
942
|
+
inv: Inventory,
|
|
943
|
+
opts: { onlyBranch?: string | null; now?: Date; recheck?: ClassifyContext; limit?: number; here?: string } = {},
|
|
944
|
+
): RemovalOutcome[] {
|
|
945
|
+
const now = opts.now ?? new Date();
|
|
946
|
+
const mainPath = inv.rows.find((r) => r.isMain)?.path ?? repoPath;
|
|
947
|
+
// Never the tree the caller is standing in: a session started inside a merged
|
|
948
|
+
// worktree would lose its own working directory.
|
|
949
|
+
const here = canonical(opts.here ?? process.cwd());
|
|
950
|
+
const inside = (p: string): boolean => {
|
|
951
|
+
const t = canonical(p);
|
|
952
|
+
return here === t || here.startsWith(t + path.sep);
|
|
953
|
+
};
|
|
954
|
+
const rows = inv.rows
|
|
955
|
+
.filter(
|
|
956
|
+
(r) =>
|
|
957
|
+
r.classification === 'safe' &&
|
|
958
|
+
!r.isMain &&
|
|
959
|
+
!inside(r.path) &&
|
|
960
|
+
(opts.onlyBranch === undefined || opts.onlyBranch === null || r.branch === opts.onlyBranch),
|
|
961
|
+
)
|
|
962
|
+
.slice(0, opts.limit ?? Number.POSITIVE_INFINITY);
|
|
963
|
+
if (rows.length === 0) return [];
|
|
964
|
+
const release = takeRemovalLock(mainPath, now);
|
|
965
|
+
if (release === null) return [];
|
|
966
|
+
const outcomes: RemovalOutcome[] = [];
|
|
967
|
+
const dir = commonDir(mainPath);
|
|
968
|
+
// Write-ahead: an `intent` line with the tip goes down BEFORE the removal and
|
|
969
|
+
// the outcome line after it, so a crash between the two still leaves the SHA
|
|
970
|
+
// that recreates the branch.
|
|
971
|
+
const journal = (o: RemovalOutcome, phase: 'intent' | 'outcome'): void => {
|
|
972
|
+
if (dir === null) return;
|
|
973
|
+
const line = JSON.stringify({ v: 1, phase, at: new Date().toISOString(), path: o.path, branch: o.branch, tip: o.tip, evidence: o.evidence, removed: o.removed, error: o.error });
|
|
974
|
+
try {
|
|
975
|
+
fs.appendFileSync(path.join(dir, REMOVAL_JOURNAL), `${line}\n`);
|
|
976
|
+
} catch {
|
|
977
|
+
/* an unwritable journal never blocks a removal git already judged safe */
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
try {
|
|
981
|
+
const base = opts.recheck ?? inv.context;
|
|
982
|
+
for (const r of rows) {
|
|
983
|
+
let error: string | null = null;
|
|
984
|
+
// The register is read AGAIN for every candidate: a session that
|
|
985
|
+
// entered the tree after the inventory makes it live here.
|
|
986
|
+
const ctx: ClassifyContext =
|
|
987
|
+
base.readSessions === true ? { ...base, sessionPaths: liveSessionPaths(base.mainPath, new Date(), base.selfSessionId ?? null) } : base;
|
|
988
|
+
// The entry is read AGAIN too: a tree switched to another branch or
|
|
989
|
+
// to a detached commit since the inventory is judged as it is now,
|
|
990
|
+
// never by the branch it had then.
|
|
991
|
+
let again: InventoryRow | null = null;
|
|
992
|
+
try {
|
|
993
|
+
const fresh = listWorktrees(mainPath).find((e) => canonical(e.path) === canonical(r.path));
|
|
994
|
+
const head = tryGit(canonical(r.path), ['rev-parse', '--verify', '--quiet', 'HEAD']);
|
|
995
|
+
if (fresh !== undefined && fresh.branch === r.branch && head !== null && head.trim() === r.tip) {
|
|
996
|
+
// The re-check carries a deadline of its own, so one hanging
|
|
997
|
+
// `git status` cannot hold a session start; the removal after
|
|
998
|
+
// it has the fixed REMOVAL_TIMEOUT_MS instead.
|
|
999
|
+
again = withDeadline(Date.now() + RECHECK_BUDGET_MS, () => classifyEntry(fresh, ctx));
|
|
1000
|
+
} else {
|
|
1001
|
+
error = 'HEAD or branch changed since the inventory — kept';
|
|
1002
|
+
}
|
|
1003
|
+
} catch {
|
|
1004
|
+
again = null;
|
|
1005
|
+
}
|
|
1006
|
+
if (error !== null) {
|
|
1007
|
+
/* recorded below */
|
|
1008
|
+
} else if (again === null) {
|
|
1009
|
+
error = 'could not re-check it — kept';
|
|
1010
|
+
} else if (again.classification !== 'safe' || again.tip !== r.tip) {
|
|
1011
|
+
error = `changed since the inventory — kept (${again.reasons[0] ?? 'tip moved'})`;
|
|
1012
|
+
} else {
|
|
1013
|
+
journal({ path: r.path, branch: r.branch, tip: again.tip, evidence: again.mergeEvidence, removed: false, error: null }, 'intent');
|
|
1014
|
+
try {
|
|
1015
|
+
// A fixed, generous timeout of its own: a removal is never
|
|
1016
|
+
// cut short by the classification budget mid-deletion.
|
|
1017
|
+
execFileSync('git', ['-C', mainPath, 'worktree', 'remove', r.path], {
|
|
1018
|
+
encoding: 'utf-8',
|
|
1019
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1020
|
+
env: gitEnv(),
|
|
1021
|
+
timeout: REMOVAL_TIMEOUT_MS,
|
|
1022
|
+
});
|
|
1023
|
+
} catch (e) {
|
|
1024
|
+
const stderr = (e as { stderr?: Buffer | string }).stderr;
|
|
1025
|
+
error = (stderr === undefined ? String(e) : String(stderr)).trim().split('\n')[0] ?? 'refused';
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
const o: RemovalOutcome = {
|
|
1029
|
+
path: r.path,
|
|
1030
|
+
branch: r.branch,
|
|
1031
|
+
tip: again?.tip ?? r.tip,
|
|
1032
|
+
evidence: again?.mergeEvidence ?? r.mergeEvidence,
|
|
1033
|
+
removed: error === null,
|
|
1034
|
+
error,
|
|
1035
|
+
};
|
|
1036
|
+
outcomes.push(o);
|
|
1037
|
+
journal(o, 'outcome');
|
|
1038
|
+
}
|
|
1039
|
+
// No `git worktree prune` here: it is repository-wide, and a tree on an
|
|
1040
|
+
// unmounted volume would lose its registration without ever having been
|
|
1041
|
+
// judged. `git worktree remove` already drops the entry it removes.
|
|
1042
|
+
} finally {
|
|
1043
|
+
release();
|
|
1044
|
+
}
|
|
1045
|
+
return outcomes;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/** Per-candidate re-check deadline, separate from the removal's own timeout. */
|
|
1049
|
+
export const RECHECK_BUDGET_MS = 3000;
|
|
1050
|
+
/** Per-removal timeout, independent of any classification budget. */
|
|
1051
|
+
export const REMOVAL_TIMEOUT_MS = 30000;
|
|
1052
|
+
/** The daily pass removes at most this many trees; the rest wait for tomorrow or `apply`. */
|
|
1053
|
+
export const DAILY_REMOVAL_CAP = 3;
|
|
1054
|
+
|
|
1055
|
+
/** The one line a session start or a merge prints after a removal pass. */
|
|
1056
|
+
export function removalSummary(inv: Inventory, outcomes: RemovalOutcome[], opts: { names?: boolean } = {}): string {
|
|
1057
|
+
const removed = outcomes.filter((o) => o.removed).length;
|
|
1058
|
+
const decide = inv.rows.filter((r) => r.classification === 'review' && !r.isMain).length;
|
|
1059
|
+
const branches = outcomes
|
|
1060
|
+
.filter((o) => o.removed && o.branch !== null && o.evidence !== 'merged-ancestor')
|
|
1061
|
+
.map((o) => o.branch!);
|
|
1062
|
+
// Branch names stay out of what a session start injects; the CLI, run on
|
|
1063
|
+
// purpose, prints them so the cleanup command can be copied.
|
|
1064
|
+
const hint =
|
|
1065
|
+
branches.length === 0
|
|
1066
|
+
? ''
|
|
1067
|
+
: opts.names === false
|
|
1068
|
+
? ` · ${branches.length} local branch(es) kept (the removal journal names them)`
|
|
1069
|
+
: ` · local branches kept: git branch -D ${branches.map(shQuote).join(' ')}`;
|
|
1070
|
+
return `worktrees: ${removed} removed, ${decide} need a decision (/worktree cleanup)${hint}`;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
export type CleanupMode = 'auto' | 'notify' | 'off';
|
|
1074
|
+
|
|
1075
|
+
const CLEANUP_RANK: Record<CleanupMode, number> = { off: 0, notify: 1, auto: 2 };
|
|
1076
|
+
|
|
1077
|
+
function cleanupValue(doc: unknown): CleanupMode | undefined {
|
|
1078
|
+
const section = doc !== null && typeof doc === 'object' && !Array.isArray(doc) ? (doc as Record<string, unknown>)['worktrees'] : undefined;
|
|
1079
|
+
if (section === null || typeof section !== 'object' || Array.isArray(section)) return undefined;
|
|
1080
|
+
const v = (section as Record<string, unknown>)['cleanup'];
|
|
1081
|
+
if (v === 'auto' || v === 'notify' || v === 'off') return v;
|
|
1082
|
+
// An unquoted `off` is YAML's boolean false.
|
|
1083
|
+
return v === false ? 'off' : undefined;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* `worktrees.cleanup`. Absent resolves to `notify` (council 2026-10-21 Q2:
|
|
1088
|
+
* installation alone is not consent to delete). The consent to `auto` is the
|
|
1089
|
+
* user's, so only the user-global layers can grant it, read by hand as
|
|
1090
|
+
* `readUpdateMode` reads `update.mode`; a project layer — which a cloned
|
|
1091
|
+
* repository can carry — may lower the mode but never raise it. Fail-closed: a
|
|
1092
|
+
* malformed settings layer reads as `off`, so a file the user broke never lets
|
|
1093
|
+
* the shipped default speak for it.
|
|
1094
|
+
*/
|
|
1095
|
+
export function cleanupMode(root: string, opts: { userGlobalPath?: string | null } = {}): CleanupMode {
|
|
1096
|
+
const ug = opts.userGlobalPath ?? null;
|
|
1097
|
+
const scope = { cwd: root, ...(ug !== null ? { user_global_path: ug } : {}) };
|
|
1098
|
+
try {
|
|
1099
|
+
if (settings_layer_states(scope).some((l) => l.state === 'malformed')) return 'off';
|
|
1100
|
+
let user: CleanupMode = 'notify';
|
|
1101
|
+
for (const p of ug !== null ? [ug] : user_global_settings_paths()) {
|
|
1102
|
+
if (!fs.existsSync(p)) continue;
|
|
1103
|
+
user = cleanupValue(parseYaml(fs.readFileSync(p, 'utf-8'), { version: '1.1' })) ?? user;
|
|
1104
|
+
}
|
|
1105
|
+
const none = path.join(root, '.agent-settings-none', 'absent.yml');
|
|
1106
|
+
const project = cleanupValue(load_agent_settings({ cwd: root, user_global_path: none, template_path: none }));
|
|
1107
|
+
return project !== undefined && CLEANUP_RANK[project] < CLEANUP_RANK[user] ? project : user;
|
|
1108
|
+
} catch {
|
|
1109
|
+
return 'off';
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/** The daily pass's budget — the retro's, on the same slot (council 2026-10-21 Q1). */
|
|
1114
|
+
export const DAILY_BUDGET_MS = 3000;
|
|
1115
|
+
/** Most recent PRs the daily pass reads; older merges fall back to the tree proof. */
|
|
1116
|
+
export const DAILY_PR_WINDOW = 200;
|
|
1117
|
+
/** Per-candidate record of the last daily pass, in the common git dir. */
|
|
1118
|
+
export const DAILY_RECORD = 'agent-config-worktree-daily.json';
|
|
1119
|
+
|
|
1120
|
+
export interface DailyResult {
|
|
1121
|
+
mode: CleanupMode;
|
|
1122
|
+
scanned: number;
|
|
1123
|
+
total: number;
|
|
1124
|
+
removable: number;
|
|
1125
|
+
removed: number;
|
|
1126
|
+
decide: number;
|
|
1127
|
+
line: string | null;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/**
|
|
1131
|
+
* The once-a-day pass (worktrees roadmap 3.3). Synchronous: classification is
|
|
1132
|
+
* bounded by DAILY_BUDGET_MS as a SUBPROCESS deadline; under `auto` at most
|
|
1133
|
+
* DAILY_REMOVAL_CAP removals follow, each re-checked within RECHECK_BUDGET_MS
|
|
1134
|
+
* and removed within REMOVAL_TIMEOUT_MS. Run on the first session of the
|
|
1135
|
+
* day by the existing `session_start` handler. Declared as an authority
|
|
1136
|
+
* expansion: under `worktrees.cleanup: auto` it deletes worktrees, which is
|
|
1137
|
+
* acting, not observing — the user-owned key is what authorises it (ADR-295).
|
|
1138
|
+
* Under `notify` it removes nothing and says how many it could.
|
|
1139
|
+
*
|
|
1140
|
+
* Fair across days: the traversal starts at a different worktree each day, so
|
|
1141
|
+
* a repository whose first few candidates are slow still has every candidate
|
|
1142
|
+
* reached within a few passes. A pass the budget cut short reports how far it
|
|
1143
|
+
* got and is still stamped — tomorrow's start point is different anyway.
|
|
1144
|
+
*/
|
|
1145
|
+
export function dailyCleanup(
|
|
1146
|
+
root: string,
|
|
1147
|
+
deps: {
|
|
1148
|
+
now?: Date;
|
|
1149
|
+
mode?: CleanupMode;
|
|
1150
|
+
run?: GhRunner;
|
|
1151
|
+
budgetMs?: number;
|
|
1152
|
+
ranToday?: (root: string) => boolean;
|
|
1153
|
+
stamp?: (root: string) => void;
|
|
1154
|
+
} = {},
|
|
1155
|
+
): DailyResult | null {
|
|
1156
|
+
const now = deps.now ?? new Date();
|
|
1157
|
+
try {
|
|
1158
|
+
const mode = deps.mode ?? cleanupMode(root);
|
|
1159
|
+
if (mode === 'off') return null;
|
|
1160
|
+
if (deps.ranToday?.(root) === true) return null;
|
|
1161
|
+
const deadlineAt = Date.now() + (deps.budgetMs ?? DAILY_BUDGET_MS);
|
|
1162
|
+
const classified = withDeadline(deadlineAt, () => {
|
|
1163
|
+
const entries = listWorktrees(root);
|
|
1164
|
+
const mainPath = canonical(entries.length > 0 ? entries[0]!.path : root);
|
|
1165
|
+
const here = canonical(root);
|
|
1166
|
+
const candidates = entries.slice(1).filter((e) => {
|
|
1167
|
+
const t = canonical(e.path);
|
|
1168
|
+
return here !== t && !here.startsWith(t + path.sep);
|
|
1169
|
+
});
|
|
1170
|
+
const day = Math.floor(now.getTime() / 86400000);
|
|
1171
|
+
const start = candidates.length === 0 ? 0 : day % candidates.length;
|
|
1172
|
+
const ordered = [...candidates.slice(start), ...candidates.slice(0, start)];
|
|
1173
|
+
|
|
1174
|
+
const selfSessionId = process.env['CLAUDE_CODE_SESSION_ID'] ?? null;
|
|
1175
|
+
const sessionPaths = liveSessionPaths(mainPath, now, selfSessionId);
|
|
1176
|
+
const ctx: ClassifyContext = {
|
|
1177
|
+
readSessions: true,
|
|
1178
|
+
selfSessionId,
|
|
1179
|
+
trunk: resolveTrunk(mainPath),
|
|
1180
|
+
mainPath,
|
|
1181
|
+
now,
|
|
1182
|
+
liveCutoff: now.getTime() - LIVE_WINDOW_HOURS * 3600 * 1000,
|
|
1183
|
+
sessionPaths,
|
|
1184
|
+
registry: readRegistry(mainPath),
|
|
1185
|
+
ageCutoff: null,
|
|
1186
|
+
minAgeDays: null,
|
|
1187
|
+
forge: {
|
|
1188
|
+
// 200 PRs read in ~1.2 s on this repository (measured 2026-10-21; 1,000 took 6.1 s),
|
|
1189
|
+
// which leaves the rest of the three seconds for the candidates.
|
|
1190
|
+
prIndex: readPrIndex(mainPath, { limit: DAILY_PR_WINDOW, ...(deps.run === undefined ? {} : { run: deps.run }) }),
|
|
1191
|
+
mergeDelayHours: MERGE_DELAY_HOURS,
|
|
1192
|
+
},
|
|
1193
|
+
};
|
|
1194
|
+
const rows: InventoryRow[] = [];
|
|
1195
|
+
for (const e of ordered) {
|
|
1196
|
+
if (Date.now() >= deadlineAt) break;
|
|
1197
|
+
try {
|
|
1198
|
+
rows.push(classifyEntry(e, ctx));
|
|
1199
|
+
} catch {
|
|
1200
|
+
break; // the deadline cut a git call short: fail closed, keep the rest
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
const inv: Inventory = {
|
|
1204
|
+
trunk: ctx.trunk,
|
|
1205
|
+
rows,
|
|
1206
|
+
context: ctx,
|
|
1207
|
+
counts: { total: rows.length, safe: 0, review: 0, live: 0, reviewReasons: {} },
|
|
1208
|
+
};
|
|
1209
|
+
const safe = rows.filter((r) => r.classification === 'safe');
|
|
1210
|
+
return { inv, safe, rows, candidates, ctx };
|
|
1211
|
+
});
|
|
1212
|
+
let outcomes: RemovalOutcome[] = [];
|
|
1213
|
+
if (mode === 'auto' && classified.safe.length > 0) {
|
|
1214
|
+
try {
|
|
1215
|
+
// Every git call of the removal phase is bounded too: the per-tree
|
|
1216
|
+
// re-check and the removal set their own, tighter limits inside.
|
|
1217
|
+
const removalBudget = DAILY_REMOVAL_CAP * (RECHECK_BUDGET_MS + REMOVAL_TIMEOUT_MS) + DAILY_BUDGET_MS;
|
|
1218
|
+
outcomes = withDeadline(Date.now() + removalBudget, () =>
|
|
1219
|
+
applyRemovals(classified.ctx.mainPath, classified.inv, { now, limit: DAILY_REMOVAL_CAP, here: root }),
|
|
1220
|
+
);
|
|
1221
|
+
} catch {
|
|
1222
|
+
outcomes = [];
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
const result = (() => {
|
|
1226
|
+
const { inv, safe, rows, candidates, ctx } = classified;
|
|
1227
|
+
const removed = outcomes.filter((o) => o.removed).length;
|
|
1228
|
+
const decide = rows.filter((r) => r.classification === 'review').length;
|
|
1229
|
+
const dir = withDeadline(Date.now() + DAILY_BUDGET_MS, () => commonDir(ctx.mainPath));
|
|
1230
|
+
if (dir !== null) {
|
|
1231
|
+
const record = {
|
|
1232
|
+
at: now.toISOString(),
|
|
1233
|
+
mode,
|
|
1234
|
+
scanned: rows.length,
|
|
1235
|
+
total: candidates.length,
|
|
1236
|
+
rows: rows.map((r) => ({ path: r.path, branch: r.branch, classification: r.classification, evidence: r.mergeEvidence, reasons: r.reasons })),
|
|
1237
|
+
outcomes,
|
|
1238
|
+
};
|
|
1239
|
+
fs.writeFileSync(path.join(dir, DAILY_RECORD), `${JSON.stringify(record, null, 2)}\n`);
|
|
1240
|
+
}
|
|
1241
|
+
const cut = rows.length < candidates.length ? ` (${rows.length}/${candidates.length} checked today)` : '';
|
|
1242
|
+
let line: string | null = null;
|
|
1243
|
+
if (mode === 'auto' && (removed > 0 || decide > 0)) {
|
|
1244
|
+
line = `${removalSummary(inv, outcomes, { names: false })}${cut}`;
|
|
1245
|
+
} else if (mode === 'notify' && safe.length > 0) {
|
|
1246
|
+
line =
|
|
1247
|
+
`worktrees: ${safe.length} merged, clean and unused — remove with /worktree cleanup --apply, ` +
|
|
1248
|
+
`or set worktrees.cleanup: auto${cut}`;
|
|
1249
|
+
}
|
|
1250
|
+
return { mode, scanned: rows.length, total: candidates.length, removable: safe.length, removed, decide, line };
|
|
1251
|
+
})();
|
|
1252
|
+
deps.stamp?.(root);
|
|
1253
|
+
return result;
|
|
1254
|
+
} catch {
|
|
1255
|
+
// A pass that failed is still today's pass: without the stamp it would
|
|
1256
|
+
// rerun on every session start of the day.
|
|
1257
|
+
try {
|
|
1258
|
+
deps.stamp?.(root);
|
|
1259
|
+
} catch {
|
|
1260
|
+
/* an unwritable stamp is the caller's to report */
|
|
1261
|
+
}
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
547
1266
|
function usage(): never {
|
|
548
1267
|
process.stderr.write(
|
|
549
1268
|
'usage: worktree_cleanup_check check <worktree-path> | scope-overlap [repo-path] | ' +
|
|
550
|
-
'inventory [repo-path] [--json|--plan] [--min-age-days=N] [--exclude-live-sessions]
|
|
1269
|
+
'inventory [repo-path] [--json|--plan] [--min-age-days=N] [--exclude-live-sessions] [--offline] [--trunk=<ref>] | ' +
|
|
1270
|
+
'apply [repo-path] [--branch=<name>] [--no-delay] [--offline] [--json] [--respect-setting] [--trunk=<ref>] | ' +
|
|
1271
|
+
'register <worktree-path> [--purpose=<text>]\n',
|
|
551
1272
|
);
|
|
552
1273
|
process.exit(2);
|
|
553
1274
|
}
|
|
@@ -587,15 +1308,77 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
587
1308
|
}
|
|
588
1309
|
return 1;
|
|
589
1310
|
}
|
|
1311
|
+
if (mode === 'register') {
|
|
1312
|
+
const p = argv[1];
|
|
1313
|
+
if (p === undefined || p.startsWith('--')) usage();
|
|
1314
|
+
const purposeFlag = argv.slice(2).find((a) => a.startsWith('--purpose='));
|
|
1315
|
+
const purpose = purposeFlag === undefined ? 'unspecified' : purposeFlag.slice('--purpose='.length);
|
|
1316
|
+
const e = registerWorktree(path.resolve(p), path.resolve(p), purpose);
|
|
1317
|
+
process.stdout.write(`registered ${e.path} (${e.branch ?? 'detached'}, ${e.purpose})\n`);
|
|
1318
|
+
return 0;
|
|
1319
|
+
}
|
|
1320
|
+
if (mode === 'apply') {
|
|
1321
|
+
const rest = argv.slice(1);
|
|
1322
|
+
const flags = rest.filter((a) => a.startsWith('--'));
|
|
1323
|
+
const positional = rest.filter((a) => !a.startsWith('--'));
|
|
1324
|
+
const KNOWN_APPLY = ['--no-delay', '--offline', '--json', '--respect-setting'];
|
|
1325
|
+
const trunkFlag = flags.find((f) => f.startsWith('--trunk='));
|
|
1326
|
+
const branchFlag = flags.find((f) => f.startsWith('--branch='));
|
|
1327
|
+
if (
|
|
1328
|
+
positional.length > 1 ||
|
|
1329
|
+
flags.some((f) => !KNOWN_APPLY.includes(f) && !f.startsWith('--branch=') && !f.startsWith('--trunk='))
|
|
1330
|
+
) {
|
|
1331
|
+
usage();
|
|
1332
|
+
}
|
|
1333
|
+
const repo = path.resolve(positional[0] ?? '.');
|
|
1334
|
+
// `--respect-setting`: the path a merge takes on its own initiative
|
|
1335
|
+
// (`/pr:merge`). It removes only under `worktrees.cleanup: auto`; the
|
|
1336
|
+
// explicit `/worktree cleanup --apply` is the user's own ask and does not
|
|
1337
|
+
// consult the key (ADR-295).
|
|
1338
|
+
if (flags.includes('--respect-setting')) {
|
|
1339
|
+
const mode = cleanupMode(repo);
|
|
1340
|
+
if (mode !== 'auto') {
|
|
1341
|
+
if (mode === 'notify') {
|
|
1342
|
+
process.stdout.write(
|
|
1343
|
+
'worktrees: not removed — worktrees.cleanup is notify; /worktree cleanup --apply removes the merged ones\n',
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
return 0;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
const onlyBranch = branchFlag === undefined ? null : branchFlag.slice('--branch='.length);
|
|
1350
|
+
const inv = buildInventory(repo, {
|
|
1351
|
+
excludeLiveSessions: true,
|
|
1352
|
+
selfSessionId: process.env['CLAUDE_CODE_SESSION_ID'] ?? null,
|
|
1353
|
+
// `/pr:merge`'s own tree: its fresh mtimes are the merge, not a second session.
|
|
1354
|
+
mergedBranch: flags.includes('--no-delay') ? onlyBranch : null,
|
|
1355
|
+
prIndex: flags.includes('--offline') ? null : readPrIndex(repo),
|
|
1356
|
+
mergeDelayHours: flags.includes('--no-delay') ? 0 : MERGE_DELAY_HOURS,
|
|
1357
|
+
...(trunkFlag === undefined ? {} : { trunk: trunkFlag.slice('--trunk='.length) }),
|
|
1358
|
+
});
|
|
1359
|
+
const outcomes = applyRemovals(repo, inv, { onlyBranch });
|
|
1360
|
+
if (flags.includes('--json')) {
|
|
1361
|
+
process.stdout.write(`${JSON.stringify({ outcomes, counts: inv.counts }, null, 2)}\n`);
|
|
1362
|
+
} else {
|
|
1363
|
+
for (const o of outcomes) {
|
|
1364
|
+
process.stdout.write(
|
|
1365
|
+
o.removed ? `removed ${o.path} (${o.evidence ?? 'merged'})\n` : `refused ${o.path}: ${o.error}\n`,
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
process.stdout.write(`${removalSummary(inv, outcomes)}\n`);
|
|
1369
|
+
}
|
|
1370
|
+
return outcomes.some((o) => !o.removed) ? 1 : 0;
|
|
1371
|
+
}
|
|
590
1372
|
if (mode === 'inventory') {
|
|
591
1373
|
const rest = argv.slice(1);
|
|
592
1374
|
const flags = rest.filter((a) => a.startsWith('--'));
|
|
593
1375
|
const positional = rest.filter((a) => !a.startsWith('--'));
|
|
594
|
-
const KNOWN = ['--json', '--plan', '--exclude-live-sessions'];
|
|
1376
|
+
const KNOWN = ['--json', '--plan', '--exclude-live-sessions', '--offline'];
|
|
1377
|
+
const invTrunk = flags.find((f) => f.startsWith('--trunk='));
|
|
595
1378
|
const ageFlag = flags.find((f) => f.startsWith('--min-age-days='));
|
|
596
1379
|
if (
|
|
597
1380
|
positional.length > 1 ||
|
|
598
|
-
flags.some((f) => !KNOWN.includes(f) && !f.startsWith('--min-age-days='))
|
|
1381
|
+
flags.some((f) => !KNOWN.includes(f) && !f.startsWith('--min-age-days=') && !f.startsWith('--trunk='))
|
|
599
1382
|
) {
|
|
600
1383
|
usage();
|
|
601
1384
|
}
|
|
@@ -616,10 +1399,13 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
616
1399
|
const inv = buildInventory(repo, {
|
|
617
1400
|
minAgeDays,
|
|
618
1401
|
excludeLiveSessions: flags.includes('--exclude-live-sessions'),
|
|
1402
|
+
prIndex: flags.includes('--offline') ? null : readPrIndex(repo),
|
|
1403
|
+
...(invTrunk === undefined ? {} : { trunk: invTrunk.slice('--trunk='.length) }),
|
|
619
1404
|
});
|
|
620
1405
|
|
|
621
1406
|
if (flags.includes('--json')) {
|
|
622
|
-
|
|
1407
|
+
const { context: _ctx, ...plain } = inv;
|
|
1408
|
+
process.stdout.write(`${JSON.stringify(plain, null, 2)}\n`);
|
|
623
1409
|
return 0;
|
|
624
1410
|
}
|
|
625
1411
|
if (flags.includes('--plan')) {
|
|
@@ -630,8 +1416,8 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
630
1416
|
}
|
|
631
1417
|
process.stdout.write(
|
|
632
1418
|
`# Prepared removal plan for ${plan.length} safe worktree(s).\n` +
|
|
633
|
-
'# NOT executed
|
|
634
|
-
'#
|
|
1419
|
+
'# NOT executed here. `apply` removes the safe set itself (worktrees only,\n' +
|
|
1420
|
+
'# never --force); the `branch -d` lines stay yours to run.\n',
|
|
635
1421
|
);
|
|
636
1422
|
process.stdout.write(`${plan.join('\n')}\n`);
|
|
637
1423
|
return 0;
|
|
@@ -652,14 +1438,20 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
652
1438
|
}
|
|
653
1439
|
process.stdout.write(
|
|
654
1440
|
'\nThis mode reports only. `--plan` prints the removal commands for the safe set;\n' +
|
|
655
|
-
'
|
|
1441
|
+
'`apply` removes that set (proven merged, clean, not live), and nothing else.\n',
|
|
656
1442
|
);
|
|
657
1443
|
return 0;
|
|
658
1444
|
}
|
|
659
1445
|
usage();
|
|
660
1446
|
}
|
|
661
1447
|
|
|
1448
|
+
// Bundle-safety: this module is inlined into the hook bundle (the daily pass),
|
|
1449
|
+
// where every module shares the bundle's `import.meta.url` — so the argv match
|
|
1450
|
+
// below would be true inside every hook and `main()` would exit the dispatcher.
|
|
1451
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
1452
|
+
|
|
662
1453
|
function _isCliEntry(): boolean {
|
|
1454
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) return false;
|
|
663
1455
|
if (process.argv[1] === undefined) return false;
|
|
664
1456
|
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
665
1457
|
if (import.meta.url === argvUrl) return true;
|
|
@@ -673,7 +1465,9 @@ function _isCliEntry(): boolean {
|
|
|
673
1465
|
}
|
|
674
1466
|
}
|
|
675
1467
|
|
|
676
|
-
|
|
1468
|
+
// The `typeof` test is folded by the bundler, so inside the hook bundle the CLI
|
|
1469
|
+
// (`main` and what only it reaches) is dead code and dropped, not merely unrun.
|
|
1470
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ === 'undefined' && _isCliEntry()) {
|
|
677
1471
|
try {
|
|
678
1472
|
process.exit(main());
|
|
679
1473
|
} catch (e) {
|