mandrel 2.59.0 → 2.61.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/.agents/README.md +21 -19
- package/.agents/agents/acceptance-critic.md +24 -43
- package/.agents/agents/story-worker.md +20 -21
- package/.agents/docs/agentrc-reference.json +5 -84
- package/.agents/docs/configuration.md +48 -132
- package/.agents/docs/workflows.md +2 -2
- package/.agents/instructions.md +5 -8
- package/.agents/rules/ci-remediation.md +41 -8
- package/.agents/rules/gherkin-standards.md +4 -0
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +16 -496
- package/.agents/schemas/story-deliver-terminal.schema.json +13 -3
- package/.agents/schemas/validation-evidence.schema.json +2 -1
- package/.agents/scripts/README.md +23 -13
- package/.agents/scripts/acceptance-eval.js +35 -169
- package/.agents/scripts/agents-bootstrap-github.js +27 -129
- package/.agents/scripts/apply-quality-bootstrap.js +4 -40
- package/.agents/scripts/audit-baselines.js +6 -27
- package/.agents/scripts/audit-labels-bootstrap.js +5 -29
- package/.agents/scripts/audit-to-stories.js +91 -361
- package/.agents/scripts/boot-sweep.js +19 -72
- package/.agents/scripts/bootstrap.js +74 -395
- package/.agents/scripts/ceremony-derive.js +10 -41
- package/.agents/scripts/check-arch-cycles.js +14 -64
- package/.agents/scripts/check-baselines.js +8 -48
- package/.agents/scripts/check-context-budget.js +105 -311
- package/.agents/scripts/check-cyclomatic.js +12 -44
- package/.agents/scripts/check-dead-exports.js +13 -64
- package/.agents/scripts/check-doc-links.js +38 -186
- package/.agents/scripts/check-gherkin-corpus.js +24 -127
- package/.agents/scripts/check-test-temp-hygiene.js +28 -169
- package/.agents/scripts/coverage-capture.js +34 -98
- package/.agents/scripts/deliver-light.js +25 -171
- package/.agents/scripts/deliver-recover.js +6 -42
- package/.agents/scripts/deliver-run.js +534 -0
- package/.agents/scripts/diagnose-friction.js +24 -116
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +23 -85
- package/.agents/scripts/file-ci-gap.js +57 -49
- package/.agents/scripts/generate-config-docs.js +30 -170
- package/.agents/scripts/generate-lens-checklists.js +8 -52
- package/.agents/scripts/generate-skills-index.js +14 -112
- package/.agents/scripts/generate-workflows-doc.js +10 -70
- package/.agents/scripts/git-cleanup.js +2 -34
- package/.agents/scripts/lib/Graph.js +24 -81
- package/.agents/scripts/lib/ITicketingProvider.js +47 -143
- package/.agents/scripts/lib/Logger.js +14 -76
- package/.agents/scripts/lib/audit-baselines/engine.js +11 -31
- package/.agents/scripts/lib/audit-baselines/gate-surface.js +3 -17
- package/.agents/scripts/lib/audit-baselines/headroom.js +4 -20
- package/.agents/scripts/lib/audit-baselines/hotspots.js +3 -15
- package/.agents/scripts/lib/audit-baselines/kinds.js +23 -89
- package/.agents/scripts/lib/audit-baselines/outliers.js +6 -23
- package/.agents/scripts/lib/audit-baselines/read.js +3 -16
- package/.agents/scripts/lib/audit-baselines/staleness.js +7 -26
- package/.agents/scripts/lib/audit-baselines/surface-entry.js +7 -28
- package/.agents/scripts/lib/audit-baselines/trend.js +7 -20
- package/.agents/scripts/lib/audit-baselines/weights.js +9 -37
- package/.agents/scripts/lib/audit-suite/audit-rules-reader.js +3 -18
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +26 -124
- package/.agents/scripts/lib/audit-suite/dispatch-checklist.js +14 -45
- package/.agents/scripts/lib/audit-suite/findings.js +12 -52
- package/.agents/scripts/lib/audit-suite/frontmatter.js +5 -29
- package/.agents/scripts/lib/audit-suite/index.js +1 -15
- package/.agents/scripts/lib/audit-suite/lens-checklist.js +10 -45
- package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +11 -76
- package/.agents/scripts/lib/audit-suite/runner.js +13 -43
- package/.agents/scripts/lib/audit-suite/selector.js +49 -349
- package/.agents/scripts/lib/audit-suite/substitutions.js +12 -40
- package/.agents/scripts/lib/audit-suite/workflow-loader.js +3 -15
- package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +11 -73
- package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +8 -37
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +25 -143
- package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +14 -80
- package/.agents/scripts/lib/audit-to-stories/epic-grouping-directive.js +4 -18
- package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +7 -40
- package/.agents/scripts/lib/audit-to-stories/group-findings.js +9 -39
- package/.agents/scripts/lib/audit-to-stories/issue-corpus.js +10 -66
- package/.agents/scripts/lib/audit-to-stories/issue-index.js +6 -30
- package/.agents/scripts/lib/audit-to-stories/issues-file.js +6 -36
- package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +17 -69
- package/.agents/scripts/lib/audit-to-stories/ledger-pr.js +17 -85
- package/.agents/scripts/lib/audit-to-stories/ledger-record.js +10 -48
- package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +38 -146
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +7 -42
- package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +10 -57
- package/.agents/scripts/lib/baseline-loader.js +12 -51
- package/.agents/scripts/lib/baseline-schema-registry.js +5 -22
- package/.agents/scripts/lib/baselines/component-matcher.js +3 -12
- package/.agents/scripts/lib/baselines/components.js +10 -61
- package/.agents/scripts/lib/baselines/coverage-updater-cli.js +7 -36
- package/.agents/scripts/lib/baselines/crap-preview-incremental.js +7 -19
- package/.agents/scripts/lib/baselines/crap-preview-scan.js +19 -45
- package/.agents/scripts/lib/baselines/crap-updater-cli.js +16 -65
- package/.agents/scripts/lib/baselines/diff-scope-cli.js +5 -33
- package/.agents/scripts/lib/baselines/duplication-scanner.js +11 -60
- package/.agents/scripts/lib/baselines/env-overrides.js +12 -67
- package/.agents/scripts/lib/baselines/envelope.js +13 -129
- package/.agents/scripts/lib/baselines/exit-codes.js +6 -49
- package/.agents/scripts/lib/baselines/git-base.js +23 -142
- package/.agents/scripts/lib/baselines/kernel.js +11 -111
- package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +9 -47
- package/.agents/scripts/lib/baselines/kinds/_crap-read.js +8 -51
- package/.agents/scripts/lib/baselines/kinds/_shared-metric.js +7 -62
- package/.agents/scripts/lib/baselines/kinds/bundle-size.js +8 -29
- package/.agents/scripts/lib/baselines/kinds/coverage.js +3 -15
- package/.agents/scripts/lib/baselines/kinds/crap.js +94 -415
- package/.agents/scripts/lib/baselines/kinds/duplication.js +4 -30
- package/.agents/scripts/lib/baselines/kinds/kind-factory.js +7 -53
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +15 -80
- package/.agents/scripts/lib/baselines/kinds/mutation.js +12 -72
- package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +4 -11
- package/.agents/scripts/lib/baselines/merge-envelopes.js +30 -146
- package/.agents/scripts/lib/baselines/path-canon.js +19 -128
- package/.agents/scripts/lib/baselines/preview-gates.js +8 -37
- package/.agents/scripts/lib/baselines/reader.js +14 -107
- package/.agents/scripts/lib/baselines/refresh-service.js +35 -252
- package/.agents/scripts/lib/baselines/scope.js +11 -135
- package/.agents/scripts/lib/baselines/writer.js +16 -149
- package/.agents/scripts/lib/bdd-runner-detect.js +18 -113
- package/.agents/scripts/lib/bdd-scenario-budget.js +9 -36
- package/.agents/scripts/lib/bdd-scenario-scanner.js +11 -73
- package/.agents/scripts/lib/bdd-step-index.js +26 -100
- package/.agents/scripts/lib/bootstrap/baseline-merge-driver.js +23 -103
- package/.agents/scripts/lib/bootstrap/branch-protection.js +2 -5
- package/.agents/scripts/lib/bootstrap/commit-push.js +12 -50
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +14 -101
- package/.agents/scripts/lib/bootstrap/hitl-confirm.js +5 -30
- package/.agents/scripts/lib/bootstrap/install-ledger.js +17 -73
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +17 -121
- package/.agents/scripts/lib/bootstrap/manifest.js +15 -86
- package/.agents/scripts/lib/bootstrap/merge-methods.js +4 -33
- package/.agents/scripts/lib/bootstrap/preflight.js +12 -61
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +34 -235
- package/.agents/scripts/lib/bootstrap/prompt.js +21 -129
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +27 -141
- package/.agents/scripts/lib/bootstrap/summary.js +1 -8
- package/.agents/scripts/lib/bootstrap/workflow-audit.js +11 -84
- package/.agents/scripts/lib/branch-name-guard.js +4 -23
- package/.agents/scripts/lib/changed-files.js +39 -121
- package/.agents/scripts/lib/checks/core-bare-clean.js +4 -24
- package/.agents/scripts/lib/checks/index.js +18 -101
- package/.agents/scripts/lib/checks/loop-health.js +11 -53
- package/.agents/scripts/lib/checks/state.js +16 -91
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +10 -61
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +14 -83
- package/.agents/scripts/lib/child-exec.js +39 -108
- package/.agents/scripts/lib/cli/standard-args.js +9 -116
- package/.agents/scripts/lib/cli-args.js +31 -146
- package/.agents/scripts/lib/cli-usage.js +5 -27
- package/.agents/scripts/lib/cli-utils.js +2 -12
- package/.agents/scripts/lib/close-validation/commands.js +13 -76
- package/.agents/scripts/lib/close-validation/gates.js +92 -331
- package/.agents/scripts/lib/close-validation/process.js +121 -151
- package/.agents/scripts/lib/close-validation/projections/advisories.js +5 -31
- package/.agents/scripts/lib/close-validation/projections/crap.js +19 -67
- package/.agents/scripts/lib/close-validation/projections/head-sha.js +1 -17
- package/.agents/scripts/lib/close-validation/projections/inputs.js +4 -34
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +11 -58
- package/.agents/scripts/lib/close-validation/runner.js +53 -110
- package/.agents/scripts/lib/command-header.js +6 -28
- package/.agents/scripts/lib/config/acceptance-eval.js +4 -30
- package/.agents/scripts/lib/config/baselines.js +3 -14
- package/.agents/scripts/lib/config/ci.js +8 -38
- package/.agents/scripts/lib/config/commands.js +1 -15
- package/.agents/scripts/lib/config/defaults.js +4 -33
- package/.agents/scripts/lib/config/delivery-routing.js +6 -34
- package/.agents/scripts/lib/config/explain.js +10 -83
- package/.agents/scripts/lib/config/gates/coverage.schema.js +0 -12
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +3 -31
- package/.agents/scripts/lib/config/gates/crap.schema.js +1 -34
- package/.agents/scripts/lib/config/gates/duplication.schema.js +1 -8
- package/.agents/scripts/lib/config/gates/index.js +2 -17
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +1 -20
- package/.agents/scripts/lib/config/gates/mutation.schema.js +1 -7
- package/.agents/scripts/lib/config/gates/shared.js +4 -64
- package/.agents/scripts/lib/config/github.js +4 -22
- package/.agents/scripts/lib/config/limits.js +4 -57
- package/.agents/scripts/lib/config/paths.js +2 -21
- package/.agents/scripts/lib/config/qa.js +4 -38
- package/.agents/scripts/lib/config/quality.js +76 -432
- package/.agents/scripts/lib/config/runners.js +12 -56
- package/.agents/scripts/lib/config/runtime.js +13 -57
- package/.agents/scripts/lib/config/shared.js +2 -16
- package/.agents/scripts/lib/config/sync-agentrc.js +9 -50
- package/.agents/scripts/lib/config/temp-paths.js +40 -280
- package/.agents/scripts/lib/config/validate-orchestration.js +5 -17
- package/.agents/scripts/lib/config/worktree-isolation.js +9 -28
- package/.agents/scripts/lib/config-resolver.js +11 -52
- package/.agents/scripts/lib/config-schema-shared.js +1 -4
- package/.agents/scripts/lib/config-settings-schema-delivery.js +16 -291
- package/.agents/scripts/lib/config-settings-schema-quality.js +7 -219
- package/.agents/scripts/lib/config-settings-schema.js +37 -274
- package/.agents/scripts/lib/coverage-baseline.js +20 -110
- package/.agents/scripts/lib/coverage-capture-fullscope.js +18 -35
- package/.agents/scripts/lib/coverage-capture-incremental.js +15 -45
- package/.agents/scripts/lib/coverage-capture-usage.js +8 -25
- package/.agents/scripts/lib/coverage-capture.js +99 -224
- package/.agents/scripts/lib/coverage-utils.js +14 -79
- package/.agents/scripts/lib/cpu-pool.js +17 -123
- package/.agents/scripts/lib/crap-baseline-join.js +16 -88
- package/.agents/scripts/lib/crap-coordinates.js +6 -25
- package/.agents/scripts/lib/crap-engine.js +40 -153
- package/.agents/scripts/lib/crap-method-identity.js +16 -78
- package/.agents/scripts/lib/crap-utils.js +31 -155
- package/.agents/scripts/lib/cyclomatic-ceiling.js +14 -87
- package/.agents/scripts/lib/cyclomatic-scope.js +10 -50
- package/.agents/scripts/lib/dead-exports-knip.js +18 -56
- package/.agents/scripts/lib/dead-exports-mode.js +5 -24
- package/.agents/scripts/lib/degraded-mode.js +3 -26
- package/.agents/scripts/lib/dependency-parser.js +8 -40
- package/.agents/scripts/lib/dependency-version.js +13 -35
- package/.agents/scripts/lib/detect-package-manager.js +6 -36
- package/.agents/scripts/lib/doc-tiers.js +22 -124
- package/.agents/scripts/lib/duplicate-search.js +8 -72
- package/.agents/scripts/lib/env-loader.js +5 -27
- package/.agents/scripts/lib/error-redactor.js +6 -31
- package/.agents/scripts/lib/errors/index.js +2 -22
- package/.agents/scripts/lib/escomplex-ast-compat.js +22 -163
- package/.agents/scripts/lib/escomplex-kernel.js +21 -123
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +94 -419
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +20 -98
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +27 -149
- package/.agents/scripts/lib/findings/audit-ledger.js +23 -97
- package/.agents/scripts/lib/findings/classify-finding.js +14 -69
- package/.agents/scripts/lib/findings/promote-finding.js +14 -112
- package/.agents/scripts/lib/findings/provenance-field.js +11 -52
- package/.agents/scripts/lib/findings/route-finding.js +37 -255
- package/.agents/scripts/lib/findings/semantic-issue-search.js +14 -59
- package/.agents/scripts/lib/findings/severity.js +18 -104
- package/.agents/scripts/lib/format-generated-json.js +12 -44
- package/.agents/scripts/lib/full-suite-lock.js +160 -429
- package/.agents/scripts/lib/full-suite-queue.js +213 -0
- package/.agents/scripts/lib/gates/baseline-store.js +5 -9
- package/.agents/scripts/lib/gates/friction.js +1 -3
- package/.agents/scripts/lib/generated/agentrc-validator.js +2 -2
- package/.agents/scripts/lib/gh-exec.js +123 -191
- package/.agents/scripts/lib/git/cached-fetch.js +9 -52
- package/.agents/scripts/lib/git/sync-from-base.js +15 -107
- package/.agents/scripts/lib/git-branch-cleanup.js +14 -61
- package/.agents/scripts/lib/git-branch-lifecycle.js +16 -87
- package/.agents/scripts/lib/git-utils.js +37 -161
- package/.agents/scripts/lib/github/framework-repo.js +8 -71
- package/.agents/scripts/lib/github-url.js +2 -17
- package/.agents/scripts/lib/import-graph.js +11 -38
- package/.agents/scripts/lib/install-cmd-parser.js +4 -13
- package/.agents/scripts/lib/json-utils.js +3 -18
- package/.agents/scripts/lib/label-constants.js +16 -111
- package/.agents/scripts/lib/label-taxonomy.js +4 -30
- package/.agents/scripts/lib/maintainability-engine.js +12 -80
- package/.agents/scripts/lib/maintainability-unscorable.js +5 -25
- package/.agents/scripts/lib/maintainability-utils.js +16 -92
- package/.agents/scripts/lib/mandrel-catalog.js +12 -70
- package/.agents/scripts/lib/notifications/notifier.js +13 -38
- package/.agents/scripts/lib/npm-scripts.js +4 -23
- package/.agents/scripts/lib/observability/metrics-ledger.js +16 -76
- package/.agents/scripts/lib/observability/runtime-friction.js +44 -286
- package/.agents/scripts/lib/observability/signal-validator.js +5 -37
- package/.agents/scripts/lib/observability/signals-writer.js +11 -121
- package/.agents/scripts/lib/observability/source-classifier.js +22 -203
- package/.agents/scripts/lib/observability/terse-result.js +14 -47
- package/.agents/scripts/lib/onboard/init-tail.js +13 -74
- package/.agents/scripts/lib/onboard/scaffold-docs.js +11 -34
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +25 -139
- package/.agents/scripts/lib/orchestration/auto-merge-cwd.js +11 -66
- package/.agents/scripts/lib/orchestration/behind-recovery.js +10 -55
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +28 -156
- package/.agents/scripts/lib/orchestration/change-set.js +7 -36
- package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +9 -46
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +11 -44
- package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +5 -24
- package/.agents/scripts/lib/orchestration/check-baselines/phases/friction.js +3 -12
- package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +5 -38
- package/.agents/scripts/lib/orchestration/check-baselines/phases/pipeline.js +3 -8
- package/.agents/scripts/lib/orchestration/check-baselines/phases/refresh-ack.js +28 -136
- package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +3 -13
- package/.agents/scripts/lib/orchestration/check-state.js +92 -0
- package/.agents/scripts/lib/orchestration/ci-gap-intake.js +37 -144
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +121 -157
- package/.agents/scripts/lib/orchestration/code-review.js +24 -128
- package/.agents/scripts/lib/orchestration/column-sync.js +23 -111
- package/.agents/scripts/lib/orchestration/complexity-gate.js +118 -462
- package/.agents/scripts/lib/orchestration/deliver-recover.js +36 -168
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +4 -35
- package/.agents/scripts/lib/orchestration/dependency-candidates.js +9 -41
- package/.agents/scripts/lib/orchestration/diff-magnitude.js +25 -115
- package/.agents/scripts/lib/orchestration/doc-reader.js +2 -6
- package/.agents/scripts/lib/orchestration/docs-digest.js +13 -51
- package/.agents/scripts/lib/orchestration/epic-candidates.js +13 -53
- package/.agents/scripts/lib/orchestration/epic-checklist.js +9 -33
- package/.agents/scripts/lib/orchestration/epic-container.js +41 -167
- package/.agents/scripts/lib/orchestration/epic-expansion.js +10 -40
- package/.agents/scripts/lib/orchestration/epic-rollup.js +55 -207
- package/.agents/scripts/lib/orchestration/file-assumption-enum.js +2 -22
- package/.agents/scripts/lib/orchestration/file-assumptions.js +45 -253
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-detect.js +9 -46
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-reap.js +7 -57
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +21 -126
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +2 -6
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +1 -4
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/filters.js +1 -5
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes-ff.js +9 -34
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +41 -190
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/merged-tip.js +9 -45
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +1 -4
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +19 -85
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/prompts.js +3 -17
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/prune.js +1 -5
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +16 -100
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/stashes.js +1 -4
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +16 -67
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +9 -33
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +8 -29
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +15 -68
- package/.agents/scripts/lib/orchestration/light-backstop.js +11 -49
- package/.agents/scripts/lib/orchestration/light-escalation.js +21 -89
- package/.agents/scripts/lib/orchestration/light-suitability.js +37 -307
- package/.agents/scripts/lib/orchestration/merge-block-class.js +39 -216
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -377
- package/.agents/scripts/lib/orchestration/pinned-identifier-lint.js +17 -57
- package/.agents/scripts/lib/orchestration/plan-context.js +55 -318
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +22 -79
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +5 -31
- package/.agents/scripts/lib/orchestration/plan-metrics.js +29 -108
- package/.agents/scripts/lib/orchestration/plan-navigation.js +8 -27
- package/.agents/scripts/lib/orchestration/plan-persist/acceptance-handle-repair.js +12 -49
- package/.agents/scripts/lib/orchestration/plan-persist/audit-provenance.js +18 -73
- package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +16 -78
- package/.agents/scripts/lib/orchestration/plan-persist/cross-plan-links.js +6 -30
- package/.agents/scripts/lib/orchestration/plan-persist/epic-adoption.js +14 -65
- package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +21 -76
- package/.agents/scripts/lib/orchestration/plan-persist/external-deps.js +6 -43
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +14 -68
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +9 -41
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +71 -289
- package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +2 -12
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +96 -362
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +9 -39
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +60 -186
- package/.agents/scripts/lib/orchestration/plan-persist/wave-collision-gate.js +9 -49
- package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +7 -28
- package/.agents/scripts/lib/orchestration/plan-reachability.js +13 -42
- package/.agents/scripts/lib/orchestration/plan-run-labels/reap.js +16 -91
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +16 -54
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +10 -52
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +14 -109
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +11 -83
- package/.agents/scripts/lib/orchestration/pr-watch.js +48 -306
- package/.agents/scripts/lib/orchestration/project-meta-cache.js +15 -80
- package/.agents/scripts/lib/orchestration/project-meta-resolver.js +10 -51
- package/.agents/scripts/lib/orchestration/reassert-status-column.js +12 -76
- package/.agents/scripts/lib/orchestration/remote-verifier.js +12 -36
- package/.agents/scripts/lib/orchestration/resolve-stories.js +33 -164
- package/.agents/scripts/lib/orchestration/retro-proposals.js +66 -361
- package/.agents/scripts/lib/orchestration/review-base-ref.js +9 -43
- package/.agents/scripts/lib/orchestration/review-depth.js +12 -91
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +10 -111
- package/.agents/scripts/lib/orchestration/review-providers/degraded-gates.js +13 -71
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +7 -51
- package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +8 -53
- package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -214
- package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +7 -52
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +4 -33
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +11 -71
- package/.agents/scripts/lib/orchestration/review-providers/scoped-lint.js +19 -131
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +6 -89
- package/.agents/scripts/lib/orchestration/review-providers/types.js +26 -56
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +3 -46
- package/.agents/scripts/lib/orchestration/run-epilogue.js +245 -326
- package/.agents/scripts/lib/orchestration/run-scoped-config.js +55 -159
- package/.agents/scripts/lib/orchestration/single-story-close/close-note.js +7 -34
- package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +16 -89
- package/.agents/scripts/lib/orchestration/single-story-close/gate-log.js +18 -109
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +83 -239
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +20 -82
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +103 -97
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -64
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +136 -486
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +30 -134
- package/.agents/scripts/lib/orchestration/single-story-close/phases/graphql-preflight.js +96 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/lock-wait-pending.js +49 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +13 -86
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +20 -95
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +46 -203
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pre-gate-steps.js +8 -50
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +12 -83
- package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -39
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +1 -6
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-outcome.js +4 -28
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +6 -51
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +6 -34
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +23 -121
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +180 -273
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +15 -69
- package/.agents/scripts/lib/orchestration/story-body-gate.js +4 -23
- package/.agents/scripts/lib/orchestration/story-close/baseline-upward-writeback.js +50 -179
- package/.agents/scripts/lib/orchestration/story-close/context-budget-writeback.js +11 -41
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +23 -311
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +22 -147
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +16 -67
- package/.agents/scripts/lib/orchestration/story-deliver-terminal-schema.js +12 -68
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +42 -200
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +167 -252
- package/.agents/scripts/lib/orchestration/story-init-envelope.js +52 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +2 -6
- package/.agents/scripts/lib/orchestration/story-reachability.js +3 -23
- package/.agents/scripts/lib/orchestration/task-body-validator.js +12 -135
- package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -131
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +15 -149
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +5 -33
- package/.agents/scripts/lib/orchestration/ticket-validator.js +63 -344
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +51 -244
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +39 -261
- package/.agents/scripts/lib/orchestration/ticketing/state.js +14 -68
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +46 -263
- package/.agents/scripts/lib/orchestration/ticketing.js +2 -26
- package/.agents/scripts/lib/orchestration/verify-credit.js +18 -79
- package/.agents/scripts/lib/orchestration/worktree-dirty.js +5 -30
- package/.agents/scripts/lib/path-security.js +3 -6
- package/.agents/scripts/lib/plan-phase-cleanup.js +9 -49
- package/.agents/scripts/lib/preflight-runner.js +13 -69
- package/.agents/scripts/lib/process-group.js +143 -0
- package/.agents/scripts/lib/project-root.js +2 -8
- package/.agents/scripts/lib/provider-factory.js +2 -25
- package/.agents/scripts/lib/qa/console-allowlist.js +10 -59
- package/.agents/scripts/lib/qa/qa-session.js +15 -69
- package/.agents/scripts/lib/qa/redact-evidence.js +18 -129
- package/.agents/scripts/lib/qa/resolve-qa-contract.js +26 -135
- package/.agents/scripts/lib/qa/resolve-selection.js +16 -84
- package/.agents/scripts/lib/reserved-test-ids.js +9 -47
- package/.agents/scripts/lib/runtime-deps/dep-resolution.js +13 -53
- package/.agents/scripts/lib/runtime-deps/ensure-installed.js +10 -44
- package/.agents/scripts/lib/runtime-deps/manifest.js +10 -30
- package/.agents/scripts/lib/runtime-deps/parser-major.js +15 -51
- package/.agents/scripts/lib/runtime-deps/preflight.js +4 -22
- package/.agents/scripts/lib/runtime-deps/scan-imports.js +11 -55
- package/.agents/scripts/lib/signals/detectors/common.js +12 -38
- package/.agents/scripts/lib/signals/index.js +2 -19
- package/.agents/scripts/lib/signals/schema.js +14 -109
- package/.agents/scripts/lib/signals/write.js +2 -11
- package/.agents/scripts/lib/single-story/confirm-merge.js +31 -72
- package/.agents/scripts/lib/single-story/story-merged-notify.js +8 -49
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +4 -26
- package/.agents/scripts/lib/single-story-sweep/protection.js +13 -100
- package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +60 -258
- package/.agents/scripts/lib/single-story-sweep.js +14 -97
- package/.agents/scripts/lib/skills/parse-skill.js +13 -59
- package/.agents/scripts/lib/skills/skills-index.js +8 -29
- package/.agents/scripts/lib/skills/walk-skill-files.js +17 -78
- package/.agents/scripts/lib/source-extensions.js +11 -42
- package/.agents/scripts/lib/source-text/strip-js-comments.js +8 -41
- package/.agents/scripts/lib/stdio-flush.js +9 -36
- package/.agents/scripts/lib/story-adjacency.js +8 -40
- package/.agents/scripts/lib/story-body/body-format-lints.js +51 -66
- package/.agents/scripts/lib/story-body/footer-block.js +11 -44
- package/.agents/scripts/lib/story-body/story-body.js +123 -344
- package/.agents/scripts/lib/temp-retention.js +42 -143
- package/.agents/scripts/lib/templates/decomposer-prompts.js +18 -80
- package/.agents/scripts/lib/test-env.js +12 -65
- package/.agents/scripts/lib/test-run-credit.js +10 -65
- package/.agents/scripts/lib/test-runner-contract.js +19 -74
- package/.agents/scripts/lib/test-temp.js +38 -184
- package/.agents/scripts/lib/test-tiers.js +12 -96
- package/.agents/scripts/lib/ticket-body-sections.js +21 -96
- package/.agents/scripts/lib/transpile.js +15 -74
- package/.agents/scripts/lib/util/concurrent-map.js +6 -25
- package/.agents/scripts/lib/util/parse-id-list.js +8 -34
- package/.agents/scripts/lib/util/poll-loop.js +8 -28
- package/.agents/scripts/lib/util/with-timeout.js +2 -11
- package/.agents/scripts/lib/validation-evidence.js +21 -96
- package/.agents/scripts/lib/wave-runner/footprint.js +17 -70
- package/.agents/scripts/lib/wave-runner/live-probe.js +67 -209
- package/.agents/scripts/lib/wave-runner/ready-set.js +72 -301
- package/.agents/scripts/lib/workers/crap-worker.js +11 -64
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +8 -45
- package/.agents/scripts/lib/workers/maintainability-worker.js +5 -21
- package/.agents/scripts/lib/workers/serve-worker-messages.js +2 -18
- package/.agents/scripts/lib/workflow-closure.js +22 -105
- package/.agents/scripts/lib/workspace-provisioner.js +16 -54
- package/.agents/scripts/lib/worktree/git-hooks.js +15 -60
- package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +20 -60
- package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +10 -62
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +22 -86
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +24 -126
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +2 -20
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +46 -180
- package/.agents/scripts/lib/worktree-manager.js +15 -40
- package/.agents/scripts/lint-issue-body.js +12 -68
- package/.agents/scripts/mandrel-update-preflight.js +10 -70
- package/.agents/scripts/merge-baseline.js +30 -105
- package/.agents/scripts/nav-registry-diff.js +22 -108
- package/.agents/scripts/notify.js +12 -69
- package/.agents/scripts/plan-context.js +97 -92
- package/.agents/scripts/plan-critics.js +12 -66
- package/.agents/scripts/plan-persist.js +71 -137
- package/.agents/scripts/plan-run-epilogue.js +20 -43
- package/.agents/scripts/pr-watch-with-update.js +79 -263
- package/.agents/scripts/providers/github/auth.js +3 -8
- package/.agents/scripts/providers/github/blocked-by-add.js +14 -61
- package/.agents/scripts/providers/github/board-add.js +5 -21
- package/.agents/scripts/providers/github/branch-protection.js +11 -43
- package/.agents/scripts/providers/github/cache.js +2 -14
- package/.agents/scripts/providers/github/comments.js +7 -40
- package/.agents/scripts/providers/github/compose.js +3 -19
- package/.agents/scripts/providers/github/errors.js +22 -125
- package/.agents/scripts/providers/github/issues.js +31 -123
- package/.agents/scripts/providers/github/labels.js +24 -124
- package/.agents/scripts/providers/github/mappers.js +5 -27
- package/.agents/scripts/providers/github/merge-methods.js +3 -22
- package/.agents/scripts/providers/github/project-board.js +2 -17
- package/.agents/scripts/providers/github/projects-v2-graphql.js +3 -6
- package/.agents/scripts/providers/github/request-helpers.js +5 -32
- package/.agents/scripts/providers/github/search-budget.js +9 -39
- package/.agents/scripts/providers/github/search-query.js +5 -26
- package/.agents/scripts/providers/github/sub-issue-add.js +15 -65
- package/.agents/scripts/providers/github/sub-issues.js +7 -36
- package/.agents/scripts/providers/github/tickets.js +32 -134
- package/.agents/scripts/providers/github.js +18 -59
- package/.agents/scripts/prune-plan-run-labels.js +12 -46
- package/.agents/scripts/quality-preview.js +35 -220
- package/.agents/scripts/resolve-doc-tiers.js +3 -26
- package/.agents/scripts/resolve-stories.js +16 -67
- package/.agents/scripts/resync-status-column.js +6 -25
- package/.agents/scripts/run-tests.js +29 -104
- package/.agents/scripts/single-story-close.js +23 -135
- package/.agents/scripts/single-story-confirm-merge.js +32 -139
- package/.agents/scripts/single-story-init.js +47 -259
- package/.agents/scripts/stories-wave-tick.js +179 -415
- package/.agents/scripts/sync-agentrc.js +3 -21
- package/.agents/scripts/sync-claude-agents.js +9 -55
- package/.agents/scripts/sync-claude-commands.js +20 -110
- package/.agents/scripts/test-wrapper.js +6 -43
- package/.agents/scripts/update-coverage-baseline.js +5 -22
- package/.agents/scripts/update-crap-baseline.js +7 -30
- package/.agents/scripts/update-duplication-baseline.js +16 -92
- package/.agents/scripts/update-maintainability-baseline.js +8 -62
- package/.agents/scripts/update-ticket-state.js +1 -7
- package/.agents/scripts/validate-skills.js +6 -26
- package/.agents/skills/core/gates-and-baselines/reference.md +0 -1
- package/.agents/skills/skills.index.json +2 -2
- package/.agents/skills/stack/qa/playwright/SKILL.md +26 -0
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/templates/docs/audit-sweep-runbook.md +3 -4
- package/.agents/workflows/audit-accessibility.md +4 -7
- package/.agents/workflows/audit-adrs.md +3 -6
- package/.agents/workflows/audit-architecture.md +3 -11
- package/.agents/workflows/audit-baselines.md +6 -9
- package/.agents/workflows/audit-clean-code.md +8 -12
- package/.agents/workflows/audit-data-model.md +3 -7
- package/.agents/workflows/audit-dependencies.md +4 -7
- package/.agents/workflows/audit-devops.md +4 -8
- package/.agents/workflows/audit-documentation.md +1 -4
- package/.agents/workflows/audit-mobile.md +4 -7
- package/.agents/workflows/audit-navigability.md +3 -6
- package/.agents/workflows/audit-performance.md +2 -4
- package/.agents/workflows/audit-privacy.md +4 -7
- package/.agents/workflows/audit-quality.md +4 -7
- package/.agents/workflows/audit-security.md +4 -7
- package/.agents/workflows/audit-seo.md +4 -7
- package/.agents/workflows/audit-sre.md +4 -7
- package/.agents/workflows/audit-to-stories.md +17 -30
- package/.agents/workflows/audit-ux-ui.md +4 -7
- package/.agents/workflows/helpers/acceptance-self-eval.md +84 -157
- package/.agents/workflows/helpers/audit-lens-core.md +1 -1
- package/.agents/workflows/helpers/code-quality-guardrails.md +12 -15
- package/.agents/workflows/helpers/code-review.md +4 -2
- package/.agents/workflows/helpers/deliver-digest.md +37 -28
- package/.agents/workflows/helpers/deliver-light.md +92 -101
- package/.agents/workflows/helpers/deliver-reference.md +162 -220
- package/.agents/workflows/helpers/deliver-story-reference.md +197 -607
- package/.agents/workflows/helpers/deliver-story.md +17 -18
- package/.agents/workflows/helpers/plan-reference.md +125 -167
- package/.agents/workflows/helpers/qa-core.md +12 -0
- package/.agents/workflows/mandrel-deliver.md +47 -31
- package/.agents/workflows/mandrel-plan.md +22 -21
- package/.agents/workflows/mandrel-update.md +36 -21
- package/.agents/workflows/memory-consolidate.md +1 -1
- package/.agents/workflows/qa-assist.md +27 -71
- package/.agents/workflows/qa-explore.md +19 -59
- package/.agents/workflows/qa-run.md +11 -33
- package/README.md +1 -1
- package/bin/mandrel.js +5 -46
- package/bin/postinstall.js +18 -107
- package/docs/CHANGELOG.md +60 -0
- package/lib/cli/doctor.js +11 -71
- package/lib/cli/init.js +26 -165
- package/lib/cli/migrate.js +5 -64
- package/lib/cli/registry.js +84 -467
- package/lib/cli/sync-agents.js +4 -64
- package/lib/cli/sync-commands.js +11 -64
- package/lib/cli/sync.js +34 -213
- package/lib/cli/uninstall.js +40 -219
- package/lib/cli/update.js +326 -523
- package/lib/cli/version-check.js +8 -72
- package/lib/cli/version-helpers.js +14 -87
- package/lib/migrations/helpers/retire-agentrc-key.js +11 -52
- package/lib/migrations/index.js +17 -92
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +2 -15
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +1 -13
- package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +2 -15
- package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +9 -38
- package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +1 -13
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +2 -27
- package/lib/migrations/steps/2.57.0-retire-delivery-limit-knobs.js +2 -23
- package/lib/migrations/steps/2.57.0-retire-planning-limit-knobs.js +2 -27
- package/lib/migrations/steps/2.60.0-retire-audit-results-autofile.js +21 -0
- package/lib/migrations/steps/strip-removed-agentrc-keys.js +333 -0
- package/package.json +17 -14
- package/.agents/docs/SDLC.md +0 -590
- package/.agents/docs/quality-gates.md +0 -1179
- package/.agents/rules/known-tooling-behavior.md +0 -150
- package/.agents/rules/orchestration-error-handling.md +0 -61
- package/.agents/rules/test-seams.md +0 -59
- package/.agents/schemas/baselines/lighthouse.schema.json +0 -59
- package/.agents/schemas/baselines/lint.schema.json +0 -47
- package/.agents/schemas/model-attribution.schema.json +0 -53
- package/.agents/scripts/check-action-pinning.js +0 -260
- package/.agents/scripts/check-audit-attribution.js +0 -302
- package/.agents/scripts/check-baseline-drift.js +0 -211
- package/.agents/scripts/check-baseline-scope.js +0 -362
- package/.agents/scripts/check-generated-validator.js +0 -202
- package/.agents/scripts/check-knip-entries.js +0 -159
- package/.agents/scripts/check-lifecycle-lint.js +0 -294
- package/.agents/scripts/check-pinned-override-notes.js +0 -102
- package/.agents/scripts/check-schema-references.js +0 -366
- package/.agents/scripts/check-test-portability.js +0 -512
- package/.agents/scripts/check-workflow-citations.js +0 -332
- package/.agents/scripts/check-workflow-cli-lint.js +0 -299
- package/.agents/scripts/check-workflow-timeouts.js +0 -291
- package/.agents/scripts/install-matrix-assert.js +0 -326
- package/.agents/scripts/lib/audit-advisories.js +0 -195
- package/.agents/scripts/lib/audit-attribution.js +0 -134
- package/.agents/scripts/lib/baselines/drift-detector.js +0 -351
- package/.agents/scripts/lib/baselines/kinds/lighthouse.js +0 -87
- package/.agents/scripts/lib/baselines/kinds/lint.js +0 -184
- package/.agents/scripts/lib/baselines/orphan-pruner.js +0 -233
- package/.agents/scripts/lib/baselines/scope-assert.js +0 -223
- package/.agents/scripts/lib/baselines/scope-inventory.js +0 -314
- package/.agents/scripts/lib/c8-cli-path.js +0 -21
- package/.agents/scripts/lib/config/gates/lighthouse.schema.js +0 -51
- package/.agents/scripts/lib/config/gates/lint.schema.js +0 -18
- package/.agents/scripts/lib/dynamic-workflow/architecture-report-contract.js +0 -70
- package/.agents/scripts/lib/dynamic-workflow/audit-orchestrator.js +0 -284
- package/.agents/scripts/lib/dynamic-workflow/clean-code-report-contract.js +0 -80
- package/.agents/scripts/lib/dynamic-workflow/degraded-coverage.js +0 -81
- package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +0 -87
- package/.agents/scripts/lib/dynamic-workflow/performance-report-contract.js +0 -74
- package/.agents/scripts/lib/dynamic-workflow/quality-report-contract.js +0 -90
- package/.agents/scripts/lib/dynamic-workflow/report-contract-core.js +0 -43
- package/.agents/scripts/lib/dynamic-workflow/security-report-contract.js +0 -83
- package/.agents/scripts/lib/fs-walk.js +0 -52
- package/.agents/scripts/lib/knip-config-resolver.js +0 -181
- package/.agents/scripts/lib/knip-entry-sync.js +0 -452
- package/.agents/scripts/lib/orchestration/model-attribution.js +0 -418
- package/.agents/scripts/lib/orchestration/story-plan-state.js +0 -33
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +0 -67
- package/.agents/scripts/lib/pinned-override-notes.js +0 -88
- package/.agents/scripts/lib/pinned-override-resolve.js +0 -212
- package/.agents/scripts/lib/test-isolate/cli-options.js +0 -93
- package/.agents/scripts/lib/test-isolate/env-snapshot-loader.js +0 -52
- package/.agents/scripts/lib/test-isolate/list-files.js +0 -90
- package/.agents/scripts/lib/test-isolate/parse-tap.js +0 -75
- package/.agents/scripts/lib/test-isolate/progress-log.js +0 -45
- package/.agents/scripts/lib/test-isolate/render-report.js +0 -97
- package/.agents/scripts/lib/test-isolate/run-isolate.js +0 -87
- package/.agents/scripts/lib/test-isolate/runner.js +0 -483
- package/.agents/scripts/lib/test-profile/parse-tap.js +0 -136
- package/.agents/scripts/lib/test-profile/render-report.js +0 -45
- package/.agents/scripts/lint-label-vocabulary.js +0 -214
- package/.agents/scripts/post-structured-comment.js +0 -127
- package/.agents/scripts/provision-git-hooks.js +0 -85
- package/.agents/scripts/prune-baseline-orphans.js +0 -181
- package/.agents/scripts/run-coverage.js +0 -197
- package/.agents/scripts/run-lint.js +0 -133
- package/.agents/scripts/run-test-profile.js +0 -129
- package/.agents/scripts/run-verify.js +0 -118
- package/.agents/scripts/test-isolate.js +0 -55
- package/.agents/scripts/update-dead-exports-baseline.js +0 -321
package/lib/cli/update.js
CHANGED
|
@@ -1,133 +1,12 @@
|
|
|
1
1
|
// lib/cli/update.js
|
|
2
2
|
/**
|
|
3
|
-
* `mandrel update
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `.agents/rules/git-conventions.md` § Contract Cutovers).
|
|
11
|
-
*
|
|
12
|
-
* ## Ordered cycle (happy path)
|
|
13
|
-
*
|
|
14
|
-
* 1. resolve target version (newest published) and the current version
|
|
15
|
-
* 2. drift-aware short-circuit — version check gates on installed version AND
|
|
16
|
-
* materialized `.agents/` state (Story #4065). When already on newest AND
|
|
17
|
-
* no drift: nothing to do (true no-op). When already on newest BUT drift
|
|
18
|
-
* detected: skip npm-update/migrations, run sync + sync-commands to heal.
|
|
19
|
-
* 3. install — bump the dependency (lockfile bump left STAGED).
|
|
20
|
-
* The package manager is auto-detected from the lockfile in the project
|
|
21
|
-
* root: `pnpm-lock.yaml` ⇒ `pnpm add -D …` (with `-w` at a
|
|
22
|
-
* `pnpm-workspace.yaml` root), `yarn.lock` ⇒ `yarn add -D …`, otherwise
|
|
23
|
-
* `npm install …`. An explicit `--install-cmd "<pm> <args>"` overrides
|
|
24
|
-
* detection; a `{target}` placeholder in the override is substituted with
|
|
25
|
-
* the resolved version so an override can still consume the auto-probed
|
|
26
|
-
* newest. The registry probe in step 1 always stays on `npm view` (a
|
|
27
|
-
* PM-agnostic registry query).
|
|
28
|
-
* 4. runSync — re-materialize ./.agents/ **from the newly-installed
|
|
29
|
-
* binary** so the materialized payload is always the target version's.
|
|
30
|
-
* 5. runMigrations — apply version-keyed steps for the crossed range,
|
|
31
|
-
* **from the newly-installed binary**.
|
|
32
|
-
* 6. doctor — run the check registry **from the newly-installed
|
|
33
|
-
* binary** so `agents-drift` is never a false-green against stale payload.
|
|
34
|
-
* 7. surface the changelog for the target version
|
|
35
|
-
*
|
|
36
|
-
* ## Re-exec of post-install phases (Story #4034)
|
|
37
|
-
*
|
|
38
|
-
* Steps 4–6 execute as **child processes spawned from the newly-installed
|
|
39
|
-
* bin script** (`node <cwd>/node_modules/mandrel/bin/mandrel.js`; Story #4613
|
|
40
|
-
* resolves the script layout-agnostically rather than via the `.bin` shim)
|
|
41
|
-
* rather than in the running
|
|
42
|
-
* process. Node cannot hot-swap a `require`d module mid-process, so without
|
|
43
|
-
* re-exec, the still-running old binary's `runSync`/`runMigrations`/`runDoctor`
|
|
44
|
-
* code would materialise the old payload even though the package on disk has
|
|
45
|
-
* already been updated. This produced the silent stale-`.agents/`
|
|
46
|
-
* materialization and `doctor` false-green observed in the v1.58.0 → v1.59.0
|
|
47
|
-
* consumer upgrade.
|
|
48
|
-
*
|
|
49
|
-
* The orchestration (progress messages, step tracking, changelog surface, exit
|
|
50
|
-
* code) stays in the parent process; only the version-sensitive phases run from
|
|
51
|
-
* the new bin. The `spawnPhase` seam makes the child-process boundary fully
|
|
52
|
-
* injectable so tests can verify the re-exec path without a real npm install.
|
|
53
|
-
* It is the **only** post-install execution path — tests stub the spawn
|
|
54
|
-
* boundary rather than swapping in an in-process implementation (No-Shim:
|
|
55
|
-
* `.agents/rules/git-conventions.md` § Contract Cutovers).
|
|
56
|
-
*
|
|
57
|
-
* ## No git mutation
|
|
58
|
-
*
|
|
59
|
-
* The npm dependency bump rewrites `package.json` / `package-lock.json` in the
|
|
60
|
-
* working tree but the orchestrator performs **no** `git add` / `git commit`:
|
|
61
|
-
* the lockfile bump is left staged-on-disk for the operator to review and
|
|
62
|
-
* commit. This module never shells out to git.
|
|
63
|
-
*
|
|
64
|
-
* ## `--dry-run`
|
|
65
|
-
*
|
|
66
|
-
* Prints the resolved target version and the ordered step plan, then returns
|
|
67
|
-
* without invoking any effectful seam (no npm update, no sync, no migrations,
|
|
68
|
-
* no doctor) and writing nothing.
|
|
69
|
-
*
|
|
70
|
-
* ## Changelog surface
|
|
71
|
-
*
|
|
72
|
-
* `defaultSurfaceChangelog` prints the `docs/CHANGELOG.md` section(s) for the
|
|
73
|
-
* applied range `(current, target]`. It resolves the file against the target
|
|
74
|
-
* version's install directory (the freshly bumped `node_modules/mandrel/`),
|
|
75
|
-
* where the changelog is now included in the published tarball
|
|
76
|
-
* (`docs/CHANGELOG.md` in the `files` allowlist — Story #4035).
|
|
77
|
-
*
|
|
78
|
-
* When the packaged file is absent (e.g. an older installed version predating
|
|
79
|
-
* Story #4035), the seam attempts a one-shot HTTP GET of the raw file from
|
|
80
|
-
* GitHub via the injectable `fetchChangelog` seam. If that fetch also fails,
|
|
81
|
-
* the seam degrades gracefully — never throwing — and emits an actionable
|
|
82
|
-
* message directing the operator to the GitHub Releases page.
|
|
83
|
-
*
|
|
84
|
-
* ## Injectable seams (used by lib/cli/__tests__/update*.test.js)
|
|
85
|
-
*
|
|
86
|
-
* - `argv` — subcommand args (after `mandrel update`)
|
|
87
|
-
* - `currentVersion` — the installed `mandrel` version string
|
|
88
|
-
* - `resolveTargetVersion`— async, returns the newest published version
|
|
89
|
-
* - `checkDrift` — sync or async, returns `true` when `.agents/`
|
|
90
|
-
* differs from the installed payload. Used by the
|
|
91
|
-
* drift-aware no-op short-circuit (Story #4065).
|
|
92
|
-
* Defaults to `() => !runAgentsDrift().ok`, which
|
|
93
|
-
* reuses the same `agents-drift` doctor signal.
|
|
94
|
-
* - `npmUpdate` — async, performs the dependency bump (no git);
|
|
95
|
-
* receives `(target, { installCmd })`
|
|
96
|
-
* - `spawnPhase` — async, spawns a post-install phase from the new
|
|
97
|
-
* binary; receives `(phase, args, { binPath, cwd })`
|
|
98
|
-
* and returns `{ ok, stdout, stderr }`. This is the
|
|
99
|
-
* sole post-install execution path. See § Re-exec of
|
|
100
|
-
* post-install phases.
|
|
101
|
-
* - `surfaceChangelog` — emits the target changelog section
|
|
102
|
-
* - `write` / `writeErr` — stdout / stderr sinks
|
|
103
|
-
* - `exit` — process.exit replacement
|
|
104
|
-
* - `cwd` — process.cwd() replacement (used to resolve the
|
|
105
|
-
* new binary path the post-install phases spawn from)
|
|
106
|
-
*
|
|
107
|
-
* Security (security-baseline § 5 — Data Leakage & Logging): logs only version
|
|
108
|
-
* strings and step names. No tokens, credentials, or env
|
|
109
|
-
* values are read or logged; no shell-string interpolation occurs here (the
|
|
110
|
-
* npm bump is delegated to the injected `npmUpdate` seam, which owns transport).
|
|
111
|
-
*
|
|
112
|
-
* ## Windows spawn (CVE-2024-27980)
|
|
113
|
-
*
|
|
114
|
-
* Both child-process boundaries — the `npm view` registry probe and the
|
|
115
|
-
* install — route through helpers that pass `shell: process.platform ===
|
|
116
|
-
* 'win32'`. On Windows `npm`/`pnpm`/`yarn` resolve to `.cmd` shims, and
|
|
117
|
-
* Node 18.20+/20.12+/22+/24 refuses to spawn `.cmd`/`.bat` with `shell:false`
|
|
118
|
-
* (the CVE-2024-27980 hardening), throwing `spawnSync npm ENOENT`. The win32
|
|
119
|
-
* shell flag is the documented fix. It is injection-safe because every argv
|
|
120
|
-
* here is a **fixed vector**: the probe argv is the constant package name, and
|
|
121
|
-
* the install argv is a tokenized list whose only variable segment is a
|
|
122
|
-
* resolved semver string — see `lib/install-cmd-parser.js` for the shared
|
|
123
|
-
* tokenize-and-spawn rationale this module reuses (no duplicated workaround).
|
|
124
|
-
*
|
|
125
|
-
* The `spawnPhase` default (Story #4034) does **not** use the win32 shell flag:
|
|
126
|
-
* it spawns `process.execPath` (node) against the resolved `bin/mandrel.js`
|
|
127
|
-
* script (Story #4613), so it never touches a `.cmd` shim and needs no
|
|
128
|
-
* shell on any platform. The per-phase argv vector is a constant fixed list
|
|
129
|
-
* (e.g. `['sync']`, `['migrate', '--from', v, '--to', v]`, `['doctor']`) with
|
|
130
|
-
* no operator-supplied data.
|
|
3
|
+
* `mandrel update`: install the newest published version, then re-materialize
|
|
4
|
+
* `.agents/`, run migrations and doctor from the NEW binary (Node cannot
|
|
5
|
+
* hot-swap loaded modules, so in-process phases would materialize the old
|
|
6
|
+
* payload), and surface the changelog. Already-newest with `.agents/` drift
|
|
7
|
+
* runs only the sync phases. Never mutates git: it reports the real index state.
|
|
8
|
+
* Windows: `npm`/`pnpm`/`yarn` are `.cmd` shims that need `shell: true` under
|
|
9
|
+
* CVE-2024-27980; every argv here is a fixed vector, so that stays safe.
|
|
131
10
|
*/
|
|
132
11
|
|
|
133
12
|
import { spawnSync } from 'node:child_process';
|
|
@@ -146,38 +25,18 @@ import {
|
|
|
146
25
|
resolveConsumerPinVersion,
|
|
147
26
|
} from './version-helpers.js';
|
|
148
27
|
|
|
149
|
-
/** The published package whose newest version `mandrel update` advances to. */
|
|
150
28
|
const PACKAGE_NAME = 'mandrel';
|
|
151
29
|
|
|
152
|
-
/**
|
|
153
|
-
* GitHub raw-file base URL for fetching `docs/CHANGELOG.md` when the packaged
|
|
154
|
-
* file is absent (Story #4035 — GitHub fallback). Resolves to the tagged
|
|
155
|
-
* release, e.g. `.../mandrel-v1.59.0/docs/CHANGELOG.md`.
|
|
156
|
-
*/
|
|
157
30
|
const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/dsj1984/mandrel/';
|
|
158
31
|
|
|
159
|
-
/**
|
|
160
|
-
* Human-readable GitHub Releases page — surfaced in the actionable fallback
|
|
161
|
-
* message when neither the packaged file nor the GitHub fetch succeeds.
|
|
162
|
-
*/
|
|
163
32
|
const GITHUB_RELEASES_URL = 'https://github.com/dsj1984/mandrel/releases';
|
|
164
33
|
|
|
165
|
-
/** Default freshness-cache filename — mirrors version-check.js. */
|
|
166
34
|
const DEFAULT_CACHE_FILENAME = 'version-check.json';
|
|
167
35
|
|
|
168
36
|
/**
|
|
169
|
-
*
|
|
170
|
-
* `
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* Pre-Story-#4525 this was the update decision's `current` — the exact
|
|
174
|
-
* self-referential confusion #4525 filed: "the version of the mandrel that
|
|
175
|
-
* is executing" is tautologically `>= target` whenever the installed
|
|
176
|
-
* package is newest, which made the `npm-update` step unreachable whenever
|
|
177
|
-
* a consumer's declared pin had fallen behind what happened to be resolved
|
|
178
|
-
* in `node_modules`. It survives here only as the last-resort fallback
|
|
179
|
-
* inside {@link resolveCurrentVersionForUpdate}, for the case where even
|
|
180
|
-
* `node_modules` resolution from the consumer root fails.
|
|
37
|
+
* The version of the EXECUTING package — last-resort fallback only: as the
|
|
38
|
+
* update decision's `current` it is tautologically newest and would hide a
|
|
39
|
+
* lagging consumer pin.
|
|
181
40
|
*
|
|
182
41
|
* @param {typeof nodeFs} [fs]
|
|
183
42
|
* @returns {string}
|
|
@@ -190,27 +49,14 @@ function defaultCurrentVersion(fs = nodeFs) {
|
|
|
190
49
|
}
|
|
191
50
|
|
|
192
51
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
* `consumerRoot` via the same resolution `mandrel sync` uses, unlike the
|
|
198
|
-
* pre-#4525 self-referential `defaultCurrentVersion`), and finally to
|
|
199
|
-
* `defaultCurrentVersion` itself when neither resolves (a corrupted or
|
|
200
|
-
* highly unusual install — keeps `mandrel update` from throwing outright
|
|
201
|
-
* rather than silently misreporting "already current").
|
|
202
|
-
*
|
|
203
|
-
* The declared pin is preferred because it is exactly what the `npm-update`
|
|
204
|
-
* step moves: a consumer whose `package.json` pin lags an inflated
|
|
205
|
-
* `node_modules` resolution (e.g. an out-of-band symlink or manual
|
|
206
|
-
* `npm install mandrel@latest --no-save`) must still see `planUpdate`
|
|
207
|
-
* choose `updated`, not `resynced` — the bug #4525 reported.
|
|
52
|
+
* The update decision's `current`: the consumer's declared pin (it is what
|
|
53
|
+
* `npm-update` moves, so a pin lagging an inflated `node_modules` still
|
|
54
|
+
* updates), else the consumer's resolved `node_modules` version, else the
|
|
55
|
+
* executing package's own.
|
|
208
56
|
*
|
|
209
57
|
* @param {string} consumerRoot
|
|
210
58
|
* @param {typeof nodeFs} [fs]
|
|
211
|
-
* @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts]
|
|
212
|
-
* seam for the `node_modules` resolution tier; defaults to the real
|
|
213
|
-
* `defaultResolvePackageRoot` from `sync.js`.
|
|
59
|
+
* @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts]
|
|
214
60
|
* @returns {string}
|
|
215
61
|
*/
|
|
216
62
|
export function resolveCurrentVersionForUpdate(
|
|
@@ -231,42 +77,15 @@ export function resolveCurrentVersionForUpdate(
|
|
|
231
77
|
}
|
|
232
78
|
}
|
|
233
79
|
|
|
234
|
-
/**
|
|
235
|
-
* Resolve the project root — the directory two levels up from this module
|
|
236
|
-
* (`<root>/lib/cli/update.js`). Mirrors `lib/cli/registry.js#resolveProjectRoot`.
|
|
237
|
-
*
|
|
238
|
-
* @returns {string}
|
|
239
|
-
*/
|
|
80
|
+
/** @returns {string} */
|
|
240
81
|
function resolveProjectRoot() {
|
|
241
82
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
242
83
|
return path.resolve(here, '..', '..');
|
|
243
84
|
}
|
|
244
85
|
|
|
245
86
|
/**
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
* When `bypassCache` is `true` (the default for an explicit `mandrel update`
|
|
250
|
-
* call — Story #4046 A1b), `isStale` is asked to skip its freshness read via
|
|
251
|
-
* `forceRefresh`, so any existing cache is ignored and exactly one network
|
|
252
|
-
* probe is issued. The cache is still written so the post-update
|
|
253
|
-
* `version-current` advisory has a fresh baseline.
|
|
254
|
-
*
|
|
255
|
-
* The bypass deliberately does **not** shift `now`: the same clock is stamped
|
|
256
|
-
* into the refreshed `checkedAt`, so a shifted clock would persist a
|
|
257
|
-
* ~48h-future timestamp and defeat the 24h window for every later passive
|
|
258
|
-
* check until real time caught up (Story #4878).
|
|
259
|
-
*
|
|
260
|
-
* When `bypassCache` is `false` (passive staleness checks only), the normal
|
|
261
|
-
* 24h-cache semantics apply: a fresh cache returns the cached version with
|
|
262
|
-
* zero network I/O.
|
|
263
|
-
*
|
|
264
|
-
* The network probe shells `npm view` through `spawnSync` with a fixed argument
|
|
265
|
-
* vector (no shell-string interpolation; the package name is a constant). On
|
|
266
|
-
* Windows the spawn sets `shell: true` so the `npm.cmd` shim resolves under the
|
|
267
|
-
* CVE-2024-27980 hardening (mirrors `lib/install-cmd-parser.js`); the fixed
|
|
268
|
-
* argv carries no injection risk even with the shell flag set
|
|
269
|
-
* (security-baseline § Output & Rendering).
|
|
87
|
+
* Newest published version via the daily freshness cache. `bypassCache` forces
|
|
88
|
+
* one probe but still writes the cache.
|
|
270
89
|
*
|
|
271
90
|
* @param {{
|
|
272
91
|
* cachePath?: string,
|
|
@@ -286,9 +105,8 @@ async function defaultResolveTargetVersion({
|
|
|
286
105
|
bypassCache = false,
|
|
287
106
|
log = () => {},
|
|
288
107
|
} = {}) {
|
|
289
|
-
// Bypass
|
|
290
|
-
//
|
|
291
|
-
// (Story #4046 A1b forcing the probe; Story #4878 keeping the stamp honest).
|
|
108
|
+
// Bypass via forceRefresh, never by shifting `now`: `now` is persisted as
|
|
109
|
+
// `checkedAt`, and a shifted stamp would defeat the 24h window.
|
|
292
110
|
const result = await isStale({
|
|
293
111
|
cachePath,
|
|
294
112
|
now,
|
|
@@ -301,15 +119,10 @@ async function defaultResolveTargetVersion({
|
|
|
301
119
|
}
|
|
302
120
|
|
|
303
121
|
/**
|
|
304
|
-
*
|
|
305
|
-
* `npm view mandrel version` synchronously and returns the trimmed
|
|
306
|
-
* stdout. Fixed argv (the package name is a constant), and `shell:true` only on
|
|
307
|
-
* Windows so the `npm.cmd` shim resolves under CVE-2024-27980 — the fixed
|
|
308
|
-
* vector keeps it injection-safe with or without the shell flag.
|
|
122
|
+
* `npm view mandrel version`, trimmed.
|
|
309
123
|
*
|
|
310
|
-
* @param {{ spawnSync?: typeof spawnSync }} [deps]
|
|
311
|
-
*
|
|
312
|
-
* @returns {string} The newest published version string.
|
|
124
|
+
* @param {{ spawnSync?: typeof spawnSync }} [deps]
|
|
125
|
+
* @returns {string}
|
|
313
126
|
*/
|
|
314
127
|
export function defaultVersionRunner({ spawnSync: spawn = spawnSync } = {}) {
|
|
315
128
|
const r = spawn('npm', ['view', PACKAGE_NAME, 'version'], {
|
|
@@ -337,9 +150,7 @@ export function defaultVersionRunner({ spawnSync: spawn = spawnSync } = {}) {
|
|
|
337
150
|
}
|
|
338
151
|
|
|
339
152
|
/**
|
|
340
|
-
*
|
|
341
|
-
* Surfaced in the repair hint when an install fails so the operator can restore
|
|
342
|
-
* `node_modules` to a consistent state (Story #3575 AC-4).
|
|
153
|
+
* The full-install command named in the failed-install repair hint.
|
|
343
154
|
*
|
|
344
155
|
* @param {'pnpm' | 'yarn' | 'npm'} packageManager
|
|
345
156
|
* @returns {string}
|
|
@@ -351,29 +162,30 @@ function repairInstallCommand(packageManager) {
|
|
|
351
162
|
}
|
|
352
163
|
|
|
353
164
|
/**
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
* `
|
|
357
|
-
*
|
|
358
|
-
* signal that `pnpm add` must carry `-w` to target the workspace-root manifest.
|
|
359
|
-
*
|
|
360
|
-
* Running the wrong package manager (e.g. `npm install` in a pnpm workspace) is
|
|
361
|
-
* the root cause this resolves (Story #3575): npm chokes on the pnpm-managed
|
|
362
|
-
* tree, exits non-zero, and can flip `node_modules` to a stale store entry.
|
|
363
|
-
* Detecting the lockfile keeps the bump on the operator's real package manager
|
|
364
|
-
* so the change lands in the matching lockfile.
|
|
165
|
+
* The lockfile-detected package manager, so the bump runs under the tool that
|
|
166
|
+
* owns the tree (npm in a pnpm workspace fails and can corrupt `node_modules`).
|
|
167
|
+
* `workspaceRoot` means `pnpm add` needs `-w`. `bun` coerces to `npm`: the
|
|
168
|
+
* install builder has no bun command.
|
|
365
169
|
*
|
|
366
|
-
*
|
|
367
|
-
* (Story #4048 B3 — one implementation per concept). The `fs` seam is adapted
|
|
368
|
-
* to the shared module's `exists` contract; the shared module's `bun` return
|
|
369
|
-
* value coerces to `npm` here because `bun add` is not yet a first-class update
|
|
370
|
-
* path for this orchestrator.
|
|
371
|
-
*
|
|
372
|
-
* @param {string} [cwd] - Project root to probe (default `process.cwd()`).
|
|
170
|
+
* @param {string} [cwd]
|
|
373
171
|
* @param {typeof nodeFs} [fs]
|
|
374
172
|
* @returns {{ packageManager: 'pnpm' | 'yarn' | 'npm', workspaceRoot: boolean }}
|
|
375
173
|
*/
|
|
376
174
|
export function detectPackageManager(cwd = process.cwd(), fs = nodeFs) {
|
|
175
|
+
const result = probePackageManager(cwd, fs);
|
|
176
|
+
const packageManager =
|
|
177
|
+
result.packageManager === 'bun' ? 'npm' : result.packageManager;
|
|
178
|
+
return { packageManager, workspaceRoot: result.workspaceRoot };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The uncoerced probe; the staging report must name bun's real lockfile.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} cwd
|
|
185
|
+
* @param {typeof nodeFs} [fs]
|
|
186
|
+
* @returns {{ packageManager: 'pnpm'|'yarn'|'bun'|'npm', workspaceRoot: boolean }}
|
|
187
|
+
*/
|
|
188
|
+
function probePackageManager(cwd, fs = nodeFs) {
|
|
377
189
|
const exists = (p) => {
|
|
378
190
|
try {
|
|
379
191
|
return fs.existsSync(p);
|
|
@@ -381,33 +193,15 @@ export function detectPackageManager(cwd = process.cwd(), fs = nodeFs) {
|
|
|
381
193
|
return false;
|
|
382
194
|
}
|
|
383
195
|
};
|
|
384
|
-
|
|
385
|
-
// Coerce `bun` → `npm` because this orchestrator's install-command builder
|
|
386
|
-
// only handles pnpm / yarn / npm today.
|
|
387
|
-
const packageManager =
|
|
388
|
-
result.packageManager === 'bun' ? 'npm' : result.packageManager;
|
|
389
|
-
return { packageManager, workspaceRoot: result.workspaceRoot };
|
|
196
|
+
return detectPackageManagerWithWorkspace(cwd, exists);
|
|
390
197
|
}
|
|
391
198
|
|
|
392
199
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* With no override the command is built from the detected package manager:
|
|
396
|
-
* - `pnpm` ⇒ `pnpm add -D mandrel@<target>` (plus ` -w` at a pnpm
|
|
397
|
-
* workspace root)
|
|
398
|
-
* - `yarn` ⇒ `yarn add -D mandrel@<target>`
|
|
399
|
-
* - `npm` ⇒ `npm install mandrel@<target>` (the unchanged default)
|
|
400
|
-
*
|
|
401
|
-
* An explicit `--install-cmd` override is used verbatim, except that a
|
|
402
|
-
* `{target}` placeholder is substituted with the resolved semver so an override
|
|
403
|
-
* can still consume the auto-probed newest version (Story #3575 AC-3).
|
|
404
|
-
*
|
|
405
|
-
* This function is pure: package-manager detection happens in
|
|
406
|
-
* `detectPackageManager` (the only filesystem seam) and is passed in as
|
|
407
|
-
* `detected`, keeping the command-string assembly trivially unit-testable.
|
|
200
|
+
* The install command. An `--install-cmd` override is used verbatim except
|
|
201
|
+
* that `{target}` is replaced with the resolved semver.
|
|
408
202
|
*
|
|
409
|
-
* @param {string} target
|
|
410
|
-
* @param {string} [override]
|
|
203
|
+
* @param {string} target
|
|
204
|
+
* @param {string} [override]
|
|
411
205
|
* @param {{
|
|
412
206
|
* packageManager?: 'pnpm' | 'yarn' | 'npm',
|
|
413
207
|
* workspaceRoot?: boolean,
|
|
@@ -434,27 +228,221 @@ export function resolveInstallCmd(
|
|
|
434
228
|
return `npm install ${PACKAGE_NAME}@${target}`;
|
|
435
229
|
}
|
|
436
230
|
|
|
231
|
+
const LOCKFILE_BY_PACKAGE_MANAGER = {
|
|
232
|
+
pnpm: 'pnpm-lock.yaml',
|
|
233
|
+
yarn: 'yarn.lock',
|
|
234
|
+
bun: 'bun.lockb',
|
|
235
|
+
npm: 'package-lock.json',
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const MANIFEST_FILENAME = 'package.json';
|
|
239
|
+
|
|
240
|
+
const AGENTS_DIR = '.agents';
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @param {string} cwd
|
|
244
|
+
* @param {typeof nodeFs} [fs]
|
|
245
|
+
* @returns {string}
|
|
246
|
+
*/
|
|
247
|
+
export function defaultDetectLockfile(cwd, fs = nodeFs) {
|
|
248
|
+
const { packageManager } = probePackageManager(cwd, fs);
|
|
249
|
+
return LOCKFILE_BY_PACKAGE_MANAGER[packageManager] ?? 'package-lock.json';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const NEUTRAL_STAGING_LINE =
|
|
253
|
+
'Review the working tree and commit the bump (git not available to report staging state).';
|
|
254
|
+
|
|
255
|
+
const DEGRADED_GIT_STATE = Object.freeze({
|
|
256
|
+
ok: false,
|
|
257
|
+
stagedManifest: false,
|
|
258
|
+
stagedLockfile: false,
|
|
259
|
+
stagedPayload: false,
|
|
260
|
+
tracksAgents: false,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const spawnOk = (result) =>
|
|
264
|
+
Boolean(result) && !result.error && result.status === 0;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* The path a porcelain v1 record reports as staged, else `null`. Staged means
|
|
268
|
+
* index differs from HEAD AND the worktree agrees (`Y` blank), so a pair staged
|
|
269
|
+
* before the install and rewritten by it (`MM`) is not staged. Renames yield
|
|
270
|
+
* the new path; porcelain quoting is stripped.
|
|
271
|
+
*
|
|
272
|
+
* @param {string} record
|
|
273
|
+
* @returns {string | null}
|
|
274
|
+
*/
|
|
275
|
+
function stagedPathFromPorcelain(record) {
|
|
276
|
+
if (record.length < 4 || record[1] !== ' ') return null;
|
|
277
|
+
if (record[0] === ' ' || record[0] === '?') return null;
|
|
278
|
+
const raw = record.slice(3).trim();
|
|
279
|
+
const arrow = raw.lastIndexOf(' -> ');
|
|
280
|
+
const pathPart = arrow === -1 ? raw : raw.slice(arrow + 4);
|
|
281
|
+
const path =
|
|
282
|
+
pathPart.startsWith('"') && pathPart.endsWith('"')
|
|
283
|
+
? pathPart.slice(1, -1)
|
|
284
|
+
: pathPart;
|
|
285
|
+
return path.length > 0 ? path : null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The probe-state slot a staged path fills. Payload is tested first (a
|
|
290
|
+
* `package.json` inside `.agents/` is payload). The manifest/lockfile tests
|
|
291
|
+
* accept a leading prefix because porcelain paths are repo-root-relative;
|
|
292
|
+
* that is safe only because the caller's pathspec anchors the query at `cwd`.
|
|
293
|
+
*
|
|
294
|
+
* @param {string} path
|
|
295
|
+
* @param {string} lockfile
|
|
296
|
+
* @returns {'stagedPayload' | 'stagedManifest' | 'stagedLockfile' | null}
|
|
297
|
+
*/
|
|
298
|
+
function stagedSlotFor(path, lockfile) {
|
|
299
|
+
if (path === AGENTS_DIR || /(^|\/)\.agents\//.test(path))
|
|
300
|
+
return 'stagedPayload';
|
|
301
|
+
const isRootFile = (name) => path === name || path.endsWith(`/${name}`);
|
|
302
|
+
if (isRootFile(MANIFEST_FILENAME)) return 'stagedManifest';
|
|
303
|
+
return isRootFile(lockfile) ? 'stagedLockfile' : null;
|
|
304
|
+
}
|
|
305
|
+
|
|
437
306
|
/**
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
307
|
+
* Read-only index probe: a `cwd`-anchored `git status --porcelain` over the
|
|
308
|
+
* manifest, lockfile and `.agents`, plus `git ls-files .agents` (is the
|
|
309
|
+
* payload tracked?). Never throws; any failure degrades to `{ ok: false }`.
|
|
441
310
|
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
311
|
+
* @param {{
|
|
312
|
+
* cwd?: string,
|
|
313
|
+
* lockfile?: string,
|
|
314
|
+
* spawnSync?: typeof spawnSync,
|
|
315
|
+
* }} [opts]
|
|
316
|
+
* @returns {{
|
|
317
|
+
* ok: boolean,
|
|
318
|
+
* stagedManifest: boolean,
|
|
319
|
+
* stagedLockfile: boolean,
|
|
320
|
+
* stagedPayload: boolean,
|
|
321
|
+
* tracksAgents: boolean,
|
|
322
|
+
* }}
|
|
323
|
+
*/
|
|
324
|
+
export function defaultGitStatus({
|
|
325
|
+
cwd = process.cwd(),
|
|
326
|
+
lockfile = LOCKFILE_BY_PACKAGE_MANAGER.npm,
|
|
327
|
+
spawnSync: spawn = spawnSync,
|
|
328
|
+
} = {}) {
|
|
329
|
+
const run = (args) => spawn('git', args, { cwd, encoding: 'utf8' });
|
|
330
|
+
try {
|
|
331
|
+
const status = run([
|
|
332
|
+
'status',
|
|
333
|
+
'--porcelain',
|
|
334
|
+
'--',
|
|
335
|
+
MANIFEST_FILENAME,
|
|
336
|
+
lockfile,
|
|
337
|
+
AGENTS_DIR,
|
|
338
|
+
]);
|
|
339
|
+
if (!spawnOk(status)) return DEGRADED_GIT_STATE;
|
|
340
|
+
const agents = run(['ls-files', AGENTS_DIR]);
|
|
341
|
+
const state = {
|
|
342
|
+
...DEGRADED_GIT_STATE,
|
|
343
|
+
ok: true,
|
|
344
|
+
tracksAgents: spawnOk(agents) && String(agents.stdout).trim().length > 0,
|
|
345
|
+
};
|
|
346
|
+
for (const record of String(status.stdout).split('\n')) {
|
|
347
|
+
const path = stagedPathFromPorcelain(record);
|
|
348
|
+
const slot = path && stagedSlotFor(path, lockfile);
|
|
349
|
+
if (slot) state[slot] = true;
|
|
350
|
+
}
|
|
351
|
+
return state;
|
|
352
|
+
} catch {
|
|
353
|
+
return DEGRADED_GIT_STATE;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* The staging line a successful run closes with. `bump` reports the manifest +
|
|
359
|
+
* lockfile pair, and a not-staged hint also names a tracked `.agents/` (else
|
|
360
|
+
* that diff is silently dropped). `payload` (drift heal) reports only
|
|
361
|
+
* `.agents/`, or `''` when it is untracked.
|
|
451
362
|
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
363
|
+
* @param {{
|
|
364
|
+
* ok?: boolean,
|
|
365
|
+
* stagedManifest?: boolean,
|
|
366
|
+
* stagedLockfile?: boolean,
|
|
367
|
+
* stagedPayload?: boolean,
|
|
368
|
+
* tracksAgents?: boolean,
|
|
369
|
+
* }} gitState
|
|
370
|
+
* @param {string} lockfile
|
|
371
|
+
* @param {{ scope?: 'bump' | 'payload' }} [opts]
|
|
372
|
+
* @returns {string}
|
|
373
|
+
*/
|
|
374
|
+
export function formatStagingReport(
|
|
375
|
+
gitState,
|
|
376
|
+
lockfile,
|
|
377
|
+
{ scope = 'bump' } = {},
|
|
378
|
+
) {
|
|
379
|
+
const {
|
|
380
|
+
ok = false,
|
|
381
|
+
stagedManifest = false,
|
|
382
|
+
stagedLockfile = false,
|
|
383
|
+
stagedPayload = false,
|
|
384
|
+
tracksAgents = false,
|
|
385
|
+
} = gitState ?? {};
|
|
386
|
+
|
|
387
|
+
if (!ok) return NEUTRAL_STAGING_LINE;
|
|
388
|
+
|
|
389
|
+
if (scope === 'payload') {
|
|
390
|
+
if (!tracksAgents) return '';
|
|
391
|
+
return stagedPayload
|
|
392
|
+
? `The re-materialized ${AGENTS_DIR}/ payload is staged for review.`
|
|
393
|
+
: `The re-materialized ${AGENTS_DIR}/ payload is NOT staged. Review and stage it: git add ${AGENTS_DIR}/`;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (stagedManifest && stagedLockfile) {
|
|
397
|
+
return `The dependency bump is staged for review (${MANIFEST_FILENAME}, ${lockfile}).`;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const targets = [MANIFEST_FILENAME, lockfile];
|
|
401
|
+
let note = '';
|
|
402
|
+
if (tracksAgents) {
|
|
403
|
+
targets.push(`${AGENTS_DIR}/`);
|
|
404
|
+
note = ` ${AGENTS_DIR}/ is tracked here, so stage the re-materialized payload too.`;
|
|
405
|
+
}
|
|
406
|
+
return `The dependency bump is NOT staged.${note} Review and stage it: git add ${targets.join(' ')}`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* The staging line, degrading any seam failure to the neutral line so a
|
|
411
|
+
* courtesy report never crashes a completed update.
|
|
456
412
|
*
|
|
457
|
-
* @param {
|
|
413
|
+
* @param {{
|
|
414
|
+
* gitStatus: (opts: { cwd: string, lockfile: string }) => object,
|
|
415
|
+
* detectLockfile: (cwd: string) => string,
|
|
416
|
+
* projectRoot: string,
|
|
417
|
+
* scope?: 'bump' | 'payload',
|
|
418
|
+
* }} deps
|
|
419
|
+
* @returns {string}
|
|
420
|
+
*/
|
|
421
|
+
function resolveStagingReport({
|
|
422
|
+
gitStatus,
|
|
423
|
+
detectLockfile,
|
|
424
|
+
projectRoot,
|
|
425
|
+
scope = 'bump',
|
|
426
|
+
}) {
|
|
427
|
+
try {
|
|
428
|
+
const lockfile = detectLockfile(projectRoot);
|
|
429
|
+
return formatStagingReport(
|
|
430
|
+
gitStatus({ cwd: projectRoot, lockfile }),
|
|
431
|
+
lockfile,
|
|
432
|
+
{ scope },
|
|
433
|
+
);
|
|
434
|
+
} catch {
|
|
435
|
+
return NEUTRAL_STAGING_LINE;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Install `target` with the detected package manager (no git mutation). The
|
|
441
|
+
* shared `runInstallCommand` tokenizes and escapes per-arg under the win32
|
|
442
|
+
* shell. A failure names the repair install so `node_modules` is never left
|
|
443
|
+
* silently half-mutated.
|
|
444
|
+
*
|
|
445
|
+
* @param {string} target
|
|
458
446
|
* @param {{
|
|
459
447
|
* installCmd?: string,
|
|
460
448
|
* runInstall?: typeof runInstallCommand,
|
|
@@ -494,30 +482,16 @@ export function defaultNpmUpdate(
|
|
|
494
482
|
}
|
|
495
483
|
|
|
496
484
|
/**
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
* npm `files` allowlist).
|
|
501
|
-
*
|
|
502
|
-
* Injectable via the `fetchChangelog` seam so tests can verify the fallback
|
|
503
|
-
* path without issuing real network calls.
|
|
485
|
+
* `docs/CHANGELOG.md` from GitHub raw, for installs whose package lacks it.
|
|
486
|
+
* Tags are `mandrel-vX.Y.Z` from 1.44.0 and bare `vX.Y.Z` before, so both
|
|
487
|
+
* are tried.
|
|
504
488
|
*
|
|
505
|
-
*
|
|
506
|
-
* `mandrel-v1.44.0`; bare `vX.Y.Z` for earlier releases). This function
|
|
507
|
-
* tries the namespaced tag first, then the bare-tag form, so it covers both
|
|
508
|
-
* tag series without forcing callers to know the boundary.
|
|
509
|
-
*
|
|
510
|
-
* Security (security-baseline § Transport & Headers): the URL is constructed
|
|
511
|
-
* from a constant base and a semver string — no user input, no shell
|
|
512
|
-
* interpolation. The GET is a read-only fetch with no credentials.
|
|
513
|
-
*
|
|
514
|
-
* @param {string} version - The target semver string (e.g. `"1.59.0"`).
|
|
489
|
+
* @param {string} version
|
|
515
490
|
* @param {{
|
|
516
491
|
* https?: typeof nodeHttps,
|
|
517
492
|
* }} [deps]
|
|
518
|
-
* @returns {Promise<string>}
|
|
519
|
-
* @throws {Error} When
|
|
520
|
-
* errors out — the caller handles this gracefully.
|
|
493
|
+
* @returns {Promise<string>}
|
|
494
|
+
* @throws {Error} When every tag form is non-2xx or the request errors.
|
|
521
495
|
*/
|
|
522
496
|
export async function fetchChangelogFromGitHub(
|
|
523
497
|
version,
|
|
@@ -560,25 +534,10 @@ export async function fetchChangelogFromGitHub(
|
|
|
560
534
|
}
|
|
561
535
|
|
|
562
536
|
/**
|
|
563
|
-
*
|
|
564
|
-
*
|
|
565
|
-
* authored by release-please with `## [<version>](…)` section headers; this
|
|
566
|
-
* prints every section whose version is newer than `current` and no newer than
|
|
567
|
-
* `target`.
|
|
568
|
-
*
|
|
569
|
-
* Resolution order (Story #4035):
|
|
570
|
-
* 1. Read `docs/CHANGELOG.md` from the target version's install directory
|
|
571
|
-
* (`node_modules/mandrel/docs/CHANGELOG.md` — now in the published
|
|
572
|
-
* tarball since `package.json` lists `docs/CHANGELOG.md` in `files`).
|
|
573
|
-
* 2. When the packaged file is absent (older install), fetch it from GitHub
|
|
574
|
-
* via the injectable `fetchChangelog` seam.
|
|
575
|
-
* 3. When both sources fail, emit an actionable warning with a link to the
|
|
576
|
-
* GitHub Releases page — never a bare "not found … skipping".
|
|
537
|
+
* Print the changelog sections in `(current, target]` — packaged file first,
|
|
538
|
+
* then GitHub, then a Releases link. Best-effort: warns, never throws.
|
|
577
539
|
*
|
|
578
|
-
*
|
|
579
|
-
* best-effort and must never fail an otherwise-successful upgrade.
|
|
580
|
-
*
|
|
581
|
-
* @param {string} target - The applied target version.
|
|
540
|
+
* @param {string} target
|
|
582
541
|
* @param {{
|
|
583
542
|
* current?: string,
|
|
584
543
|
* changelogPath?: string,
|
|
@@ -602,19 +561,16 @@ async function defaultSurfaceChangelog(
|
|
|
602
561
|
) {
|
|
603
562
|
let raw;
|
|
604
563
|
|
|
605
|
-
// 1. Try the packaged file (present in installs since Story #4035).
|
|
606
564
|
try {
|
|
607
565
|
raw = fs.readFileSync(changelogPath, 'utf8');
|
|
608
566
|
} catch {
|
|
609
|
-
//
|
|
567
|
+
// Absent: fall through to the GitHub fetch.
|
|
610
568
|
}
|
|
611
569
|
|
|
612
|
-
// 2. Packaged file absent: attempt a GitHub fetch for the target tag.
|
|
613
570
|
if (raw === undefined) {
|
|
614
571
|
try {
|
|
615
572
|
raw = await fetchChangelog(target);
|
|
616
573
|
} catch {
|
|
617
|
-
// Both sources unavailable — emit an actionable message and return.
|
|
618
574
|
writeErr(
|
|
619
575
|
`mandrel update: changelog not available for v${target} — ` +
|
|
620
576
|
`view the release notes at ${GITHUB_RELEASES_URL}\n`,
|
|
@@ -645,9 +601,8 @@ async function defaultSurfaceChangelog(
|
|
|
645
601
|
}
|
|
646
602
|
|
|
647
603
|
/**
|
|
648
|
-
* Split a release-please
|
|
649
|
-
*
|
|
650
|
-
* everything up to (but not including) the next version header.
|
|
604
|
+
* Split a release-please changelog on `## [<version>]` headers; each body
|
|
605
|
+
* includes its header line.
|
|
651
606
|
*
|
|
652
607
|
* @param {string} raw
|
|
653
608
|
* @returns {Array<{ version: string, body: string }>}
|
|
@@ -680,32 +635,13 @@ function parseChangelogSections(raw) {
|
|
|
680
635
|
}
|
|
681
636
|
|
|
682
637
|
/**
|
|
683
|
-
*
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
* `process.execPath` (node) rather than executed directly — see
|
|
687
|
-
* {@link defaultSpawnPhase}.
|
|
638
|
+
* The consumer's installed `bin/mandrel.js`, NOT the `.bin` shim: the script
|
|
639
|
+
* ships non-executable and pnpm symlinks the shim straight at it, so spawning
|
|
640
|
+
* the shim fails with EACCES. Node runs the script directly instead.
|
|
688
641
|
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
* symlinks `.bin/mandrel` straight at it, so spawning the shim directly fails
|
|
693
|
-
* with `EACCES` under pnpm (Story #4613). Spawning node against the resolved
|
|
694
|
-
* `.js` script removes the dependency on the exec bit, the shebang, and the
|
|
695
|
-
* Windows `.cmd` shim entirely.
|
|
696
|
-
*
|
|
697
|
-
* Resolution reuses the same consumer-anchored resolver
|
|
698
|
-
* (`defaultResolvePackageRoot`) that {@link resolveCurrentVersionForUpdate}
|
|
699
|
-
* uses, so it points at the consumer's install rather than a copy hoisted next
|
|
700
|
-
* to this CLI module. The `mandrel` package directory is version-invariant
|
|
701
|
-
* (`node_modules/mandrel/`), so resolving it before the in-place `npm-update`
|
|
702
|
-
* step still yields the directory whose `bin/mandrel.js` the install overwrites.
|
|
703
|
-
*
|
|
704
|
-
* @param {string} projectRoot - Absolute path to the consumer project.
|
|
705
|
-
* @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts] - test
|
|
706
|
-
* seam for the `node_modules` resolution; defaults to the real
|
|
707
|
-
* `defaultResolvePackageRoot` from `sync.js`.
|
|
708
|
-
* @returns {string} Absolute path to the new bin script.
|
|
642
|
+
* @param {string} projectRoot
|
|
643
|
+
* @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts]
|
|
644
|
+
* @returns {string}
|
|
709
645
|
*/
|
|
710
646
|
export function resolveNewBinScriptPath(
|
|
711
647
|
projectRoot,
|
|
@@ -716,33 +652,19 @@ export function resolveNewBinScriptPath(
|
|
|
716
652
|
}
|
|
717
653
|
|
|
718
654
|
/**
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
* newly-installed module code (not the currently-loaded old module) executes.
|
|
723
|
-
*
|
|
724
|
-
* The child is spawned as `process.execPath <binScript> <phase> …` — node run
|
|
725
|
-
* against the resolved `bin/mandrel.js` (see {@link resolveNewBinScriptPath}).
|
|
726
|
-
* Spawning node against a plain `.js` file removes any dependency on the bin's
|
|
727
|
-
* exec bit, its shebang, or a Windows `.cmd` shim, so **no** `shell` flag is
|
|
728
|
-
* needed on any platform (this is the pnpm/layout-agnostic fix, Story #4613,
|
|
729
|
-
* that retired the former win32-only `shell: true` branch). The argv vector is
|
|
730
|
-
* a fixed constant list per phase — no operator-supplied data enters it
|
|
731
|
-
* (security-baseline § Output & Rendering).
|
|
655
|
+
* Run one post-install phase as `node <binScript> <phase> …` so the new
|
|
656
|
+
* package's code executes; no shell is needed on any platform. Throws only on
|
|
657
|
+
* a spawn error.
|
|
732
658
|
*
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
* @param {string} phase - The mandrel sub-command to run (e.g. `'sync'`).
|
|
737
|
-
* @param {string[]} args - Additional arguments for the sub-command.
|
|
659
|
+
* @param {string} phase
|
|
660
|
+
* @param {string[]} args
|
|
738
661
|
* @param {{
|
|
739
662
|
* binPath: string,
|
|
740
663
|
* cwd: string,
|
|
741
664
|
* write: (s: string) => void,
|
|
742
665
|
* writeErr: (s: string) => void,
|
|
743
666
|
* spawnFn?: typeof spawnSync,
|
|
744
|
-
* }} opts
|
|
745
|
-
* `node_modules/.bin` shim); it becomes node's first argv entry.
|
|
667
|
+
* }} opts
|
|
746
668
|
* @returns {{ ok: boolean, stdout: string, stderr: string }}
|
|
747
669
|
*/
|
|
748
670
|
export function defaultSpawnPhase(
|
|
@@ -768,19 +690,7 @@ export function defaultSpawnPhase(
|
|
|
768
690
|
return { ok, stdout, stderr };
|
|
769
691
|
}
|
|
770
692
|
|
|
771
|
-
/**
|
|
772
|
-
* The ordered step names the orchestrator drives on an update. Shared
|
|
773
|
-
* by the live path and the `--dry-run` plan printout so the two never drift.
|
|
774
|
-
*
|
|
775
|
-
* Step ordering (Story #4046 A1c; sync-agents added by Story #4528/#4530):
|
|
776
|
-
* 1. npm-update — install the new version
|
|
777
|
-
* 2. runSync — re-materialize .agents/ from the new payload
|
|
778
|
-
* 3. sync-commands — regenerate .claude/commands/ from the new payload
|
|
779
|
-
* 4. sync-agents — regenerate .claude/agents/ from the new payload
|
|
780
|
-
* 5. runMigrations — apply version-keyed migrations
|
|
781
|
-
* 6. doctor — validate the post-upgrade state
|
|
782
|
-
* 7. surface changelog — print the changelog (always last, best-effort)
|
|
783
|
-
*/
|
|
693
|
+
/** The `--dry-run` printout of the full-upgrade step order. */
|
|
784
694
|
const STEP_PLAN = [
|
|
785
695
|
'npm-update',
|
|
786
696
|
'runSync',
|
|
@@ -792,19 +702,9 @@ const STEP_PLAN = [
|
|
|
792
702
|
];
|
|
793
703
|
|
|
794
704
|
/**
|
|
795
|
-
*
|
|
796
|
-
* a
|
|
797
|
-
*
|
|
798
|
-
* - `kind: 'npm-update'` — bump the dependency via the `npmUpdate` seam.
|
|
799
|
-
* - `kind: 'spawn'` — spawn `phase`/`args` from the new binary; a
|
|
800
|
-
* non-zero exit is fatal and throws `failMessage`.
|
|
801
|
-
* - `kind: 'doctor'` — spawn `doctor` from the new binary; a non-zero
|
|
802
|
-
* exit is *soft* (maps to `action: 'doctor-failed'`
|
|
803
|
-
* + exit 1), so it carries no `failMessage`.
|
|
804
|
-
*
|
|
805
|
-
* `label` is the name pushed into the run's `stepsRun[]` (the external return
|
|
806
|
-
* contract). `migrate` is the only phase whose argv depends on the version
|
|
807
|
-
* range, so its descriptor is built per-plan in `planUpdate`.
|
|
705
|
+
* Full-upgrade phases. A `spawn` non-zero exit is fatal (throws
|
|
706
|
+
* `failMessage`); a `doctor` failure is soft (`doctor-failed`, exit 1).
|
|
707
|
+
* `label` is what `stepsRun[]` reports.
|
|
808
708
|
*
|
|
809
709
|
* @param {string} current
|
|
810
710
|
* @param {string} target
|
|
@@ -834,10 +734,7 @@ function fullUpgradeSteps(current, target) {
|
|
|
834
734
|
'Run `npm run sync:commands` manually to restore.',
|
|
835
735
|
},
|
|
836
736
|
{
|
|
837
|
-
//
|
|
838
|
-
// role-agent tree at all — only the bootstrap path did. Added alongside
|
|
839
|
-
// sync-commands so `.claude/agents/` materializes here too, which is
|
|
840
|
-
// what makes the tightened `agents-in-sync` doctor check satisfiable.
|
|
737
|
+
// Required for the `agents-in-sync` doctor check to be satisfiable.
|
|
841
738
|
kind: 'spawn',
|
|
842
739
|
phase: 'sync-agents',
|
|
843
740
|
args: [],
|
|
@@ -862,10 +759,7 @@ function fullUpgradeSteps(current, target) {
|
|
|
862
759
|
}
|
|
863
760
|
|
|
864
761
|
/**
|
|
865
|
-
*
|
|
866
|
-
* `.agents/` is stale). No npm-update, no migrations, no doctor — only the
|
|
867
|
-
* three sync phases re-materialize the payload from the already-installed
|
|
868
|
-
* binary.
|
|
762
|
+
* Drift-heal phases: the sync phases only, from the installed binary.
|
|
869
763
|
*
|
|
870
764
|
* @returns {Array<{ kind: 'spawn', phase: string, args: string[], label: string, failMessage: string }>}
|
|
871
765
|
*/
|
|
@@ -892,7 +786,6 @@ function driftHealSteps() {
|
|
|
892
786
|
'Run `npm run sync:commands` manually to restore.',
|
|
893
787
|
},
|
|
894
788
|
{
|
|
895
|
-
// Story #4528/#4530: see the matching entry in fullUpgradeSteps().
|
|
896
789
|
kind: 'spawn',
|
|
897
790
|
phase: 'sync-agents',
|
|
898
791
|
args: [],
|
|
@@ -906,25 +799,7 @@ function driftHealSteps() {
|
|
|
906
799
|
}
|
|
907
800
|
|
|
908
801
|
/**
|
|
909
|
-
* Pure
|
|
910
|
-
* inputs and the two flags, decide which of the four actions to take and the
|
|
911
|
-
* ordered phase plan for that action. **No I/O** — no filesystem, child
|
|
912
|
-
* process, network, `write`, or `exit`. This isolates the scheduler-style
|
|
913
|
-
* branch-selection and step-sequencing logic (the surface under review in
|
|
914
|
-
* Story #4182 / audit::architecture) so it can be exercised as a table over
|
|
915
|
-
* plain inputs rather than by running the whole async orchestration with every
|
|
916
|
-
* seam stubbed.
|
|
917
|
-
*
|
|
918
|
-
* The four actions:
|
|
919
|
-
*
|
|
920
|
-
* - `up-to-date` — version is current and no drift. True no-op; `steps: []`.
|
|
921
|
-
* - `dry-run` — `dryRun` is set. `steps: []` (nothing is executed); the
|
|
922
|
-
* `variant` distinguishes the drift-heal preview from the
|
|
923
|
-
* full-upgrade preview so the executor prints the right plan.
|
|
924
|
-
* - `resynced` — version is current but drift detected. Heal via the two
|
|
925
|
-
* sync phases (`driftHealSteps()`).
|
|
926
|
-
* - `updated` — a newer version is available. Full upgrade
|
|
927
|
-
* (`fullUpgradeSteps(current, target)`).
|
|
802
|
+
* Pure (no I/O) choice of action and phase plan.
|
|
928
803
|
*
|
|
929
804
|
* @param {{ current: string, target: string, dryRun: boolean, hasDrift: boolean }} input
|
|
930
805
|
* @returns {{
|
|
@@ -937,7 +812,6 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
|
|
|
937
812
|
const versionCurrent = compareVersions(target, current) <= 0;
|
|
938
813
|
|
|
939
814
|
if (versionCurrent) {
|
|
940
|
-
// Version is already newest. The only remaining question is drift.
|
|
941
815
|
if (!hasDrift) {
|
|
942
816
|
return { action: 'up-to-date', steps: [] };
|
|
943
817
|
}
|
|
@@ -947,8 +821,7 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
|
|
|
947
821
|
return { action: 'resynced', steps: driftHealSteps() };
|
|
948
822
|
}
|
|
949
823
|
|
|
950
|
-
//
|
|
951
|
-
// post-upgrade doctor phase re-checks materialization).
|
|
824
|
+
// Drift is irrelevant here: the post-upgrade doctor re-checks it.
|
|
952
825
|
if (dryRun) {
|
|
953
826
|
return { action: 'dry-run', steps: [], variant: 'full-upgrade' };
|
|
954
827
|
}
|
|
@@ -956,14 +829,8 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
|
|
|
956
829
|
}
|
|
957
830
|
|
|
958
831
|
/**
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
* following token group) and the `=` form (`--install-cmd="<cmd>"`). Returns
|
|
962
|
-
* `undefined` when the flag is absent so the default package manager is used.
|
|
963
|
-
*
|
|
964
|
-
* The argv tokenizer hands us a pre-split array; with the space form the shell
|
|
965
|
-
* has already collapsed a quoted value into one element, so the immediate next
|
|
966
|
-
* token is the full command string.
|
|
832
|
+
* `--install-cmd <cmd>` or `--install-cmd=<cmd>`; the shell has already joined
|
|
833
|
+
* a quoted value into one token.
|
|
967
834
|
*
|
|
968
835
|
* @param {string[]} argv
|
|
969
836
|
* @returns {string | undefined}
|
|
@@ -982,12 +849,6 @@ function parseInstallCmdFlag(argv) {
|
|
|
982
849
|
}
|
|
983
850
|
|
|
984
851
|
/**
|
|
985
|
-
* Resolve the drift signal for the no-op short-circuit. Prefers the injected
|
|
986
|
-
* `checkDrift` seam (unit-test friendly); falls back to the production
|
|
987
|
-
* `runAgentsDrift` helper. Only consulted when the installed version is already
|
|
988
|
-
* the newest (Story #4065) — a real version bump skips drift entirely (the
|
|
989
|
-
* post-upgrade doctor phase re-checks materialization).
|
|
990
|
-
*
|
|
991
852
|
* @param {(() => boolean | Promise<boolean>) | undefined} checkDrift
|
|
992
853
|
* @returns {Promise<boolean>}
|
|
993
854
|
*/
|
|
@@ -998,12 +859,8 @@ async function resolveDrift(checkDrift) {
|
|
|
998
859
|
}
|
|
999
860
|
|
|
1000
861
|
/**
|
|
1001
|
-
*
|
|
1002
|
-
*
|
|
1003
|
-
* `npmUpdate` seam call, the `spawnPhase` re-exec boundary, the per-step
|
|
1004
|
-
* `stepsRun` accounting, and the soft doctor-fail (`exit(1)` +
|
|
1005
|
-
* `action: 'doctor-failed'`). The branch-selection logic that produced `steps`
|
|
1006
|
-
* lives in the pure `planUpdate`.
|
|
862
|
+
* Drive `planUpdate`'s steps. A doctor failure still surfaces the changelog,
|
|
863
|
+
* then exits 1.
|
|
1007
864
|
*
|
|
1008
865
|
* @param {{
|
|
1009
866
|
* steps: Array<{ kind: 'npm-update' | 'spawn' | 'doctor', label: string, phase?: string, args?: string[], failMessage?: string }>,
|
|
@@ -1036,11 +893,8 @@ async function executePlan({
|
|
|
1036
893
|
const stepsRun = [];
|
|
1037
894
|
let doctorOk = true;
|
|
1038
895
|
|
|
1039
|
-
//
|
|
1040
|
-
//
|
|
1041
|
-
// seam surfaces its own clear error first, and (b) resolution reflects the
|
|
1042
|
-
// just-installed package. The `mandrel` package directory is version-stable,
|
|
1043
|
-
// so resolving after the in-place bump yields the same directory either way.
|
|
896
|
+
// Resolved lazily after `npm-update`, so a missing npmUpdate seam reports
|
|
897
|
+
// its own error first.
|
|
1044
898
|
let binPath;
|
|
1045
899
|
const binScript = () => {
|
|
1046
900
|
if (binPath === undefined) binPath = resolveBinScript(projectRoot);
|
|
@@ -1049,8 +903,6 @@ async function executePlan({
|
|
|
1049
903
|
|
|
1050
904
|
for (const step of steps) {
|
|
1051
905
|
if (step.kind === 'npm-update') {
|
|
1052
|
-
// Bump the dependency. The lockfile change is left STAGED on disk; this
|
|
1053
|
-
// module never commits.
|
|
1054
906
|
if (typeof npmUpdate !== 'function') {
|
|
1055
907
|
throw new Error(
|
|
1056
908
|
'mandrel update: npmUpdate seam is required to bump the dependency',
|
|
@@ -1062,9 +914,6 @@ async function executePlan({
|
|
|
1062
914
|
continue;
|
|
1063
915
|
}
|
|
1064
916
|
|
|
1065
|
-
// Both 'spawn' and 'doctor' kinds run a post-install phase from the
|
|
1066
|
-
// newly-installed binary (the Story #4034 re-exec boundary), so the new
|
|
1067
|
-
// package's module code — not the old loaded module — executes.
|
|
1068
917
|
// eslint-disable-next-line no-await-in-loop
|
|
1069
918
|
const result = await spawnPhase(step.phase, step.args, {
|
|
1070
919
|
binPath: binScript(),
|
|
@@ -1075,18 +924,12 @@ async function executePlan({
|
|
|
1075
924
|
stepsRun.push(step.label);
|
|
1076
925
|
|
|
1077
926
|
if (step.kind === 'doctor') {
|
|
1078
|
-
// Doctor failure is SOFT: record it, keep going to surface the changelog,
|
|
1079
|
-
// then map to exit(1) + doctor-failed by the caller.
|
|
1080
927
|
doctorOk = result.ok;
|
|
1081
928
|
} else if (!result.ok) {
|
|
1082
|
-
// sync / sync-commands / migrate failures are FATAL.
|
|
1083
929
|
throw new Error(step.failMessage);
|
|
1084
930
|
}
|
|
1085
931
|
}
|
|
1086
932
|
|
|
1087
|
-
// Surface the target changelog (best-effort; optional seam). Runs even when
|
|
1088
|
-
// doctor failed, so the operator still sees the changelog for the version
|
|
1089
|
-
// that landed on disk.
|
|
1090
933
|
if (typeof surfaceChangelog === 'function') {
|
|
1091
934
|
await surfaceChangelog(target);
|
|
1092
935
|
}
|
|
@@ -1103,14 +946,6 @@ async function executePlan({
|
|
|
1103
946
|
}
|
|
1104
947
|
|
|
1105
948
|
/**
|
|
1106
|
-
* Run the `mandrel update` orchestration cycle.
|
|
1107
|
-
*
|
|
1108
|
-
* The cycle is split into a pure decision (`planUpdate`) and a side-effecting
|
|
1109
|
-
* shell (this function + `executePlan`). `runUpdate` resolves the inputs
|
|
1110
|
-
* (current / target / drift) through the injectable seams, calls `planUpdate`
|
|
1111
|
-
* to select the action and its ordered phase plan, then drives the plan through
|
|
1112
|
-
* the `spawnPhase` / `write` / `exit` shell.
|
|
1113
|
-
*
|
|
1114
949
|
* @param {{
|
|
1115
950
|
* argv?: string[],
|
|
1116
951
|
* currentVersion?: string | (() => string),
|
|
@@ -1119,6 +954,8 @@ async function executePlan({
|
|
|
1119
954
|
* checkDrift?: () => (boolean | Promise<boolean>),
|
|
1120
955
|
* spawnPhase?: (phase: string, args: string[], opts: { binPath: string, cwd: string, write: (s: string) => void, writeErr: (s: string) => void }) => Promise<{ ok: boolean, stdout: string, stderr: string }> | { ok: boolean, stdout: string, stderr: string },
|
|
1121
956
|
* surfaceChangelog?: (version: string) => unknown | Promise<unknown>,
|
|
957
|
+
* gitStatus?: (opts: { cwd: string }) => { ok: boolean, staged?: string[], unstaged?: string[], tracksAgents?: boolean },
|
|
958
|
+
* detectLockfile?: (cwd: string) => string,
|
|
1122
959
|
* write?: (s: string) => void,
|
|
1123
960
|
* writeErr?: (s: string) => void,
|
|
1124
961
|
* exit?: (code: number) => void,
|
|
@@ -1142,6 +979,8 @@ export async function runUpdate({
|
|
|
1142
979
|
checkDrift,
|
|
1143
980
|
spawnPhase,
|
|
1144
981
|
surfaceChangelog,
|
|
982
|
+
gitStatus = defaultGitStatus,
|
|
983
|
+
detectLockfile = defaultDetectLockfile,
|
|
1145
984
|
write = (s) => process.stdout.write(s),
|
|
1146
985
|
writeErr = (s) => process.stderr.write(s),
|
|
1147
986
|
exit = (code) => process.exit(code),
|
|
@@ -1163,9 +1002,7 @@ export async function runUpdate({
|
|
|
1163
1002
|
}
|
|
1164
1003
|
const target = String(await resolveTargetVersion());
|
|
1165
1004
|
|
|
1166
|
-
//
|
|
1167
|
-
// it solely on that branch so a real version bump never calls the drift seam
|
|
1168
|
-
// (Story #4065).
|
|
1005
|
+
// Probe drift only when already newest; a real bump never calls the seam.
|
|
1169
1006
|
const hasDrift =
|
|
1170
1007
|
compareVersions(target, current) <= 0
|
|
1171
1008
|
? await resolveDrift(checkDrift)
|
|
@@ -1173,7 +1010,6 @@ export async function runUpdate({
|
|
|
1173
1010
|
|
|
1174
1011
|
const plan = planUpdate({ current, target, dryRun, hasDrift });
|
|
1175
1012
|
|
|
1176
|
-
// --- up-to-date: true no-op ----------------------------------------------
|
|
1177
1013
|
if (plan.action === 'up-to-date') {
|
|
1178
1014
|
write(`✅ Already up to date (v${current} is the newest version).\n`);
|
|
1179
1015
|
return {
|
|
@@ -1186,7 +1022,6 @@ export async function runUpdate({
|
|
|
1186
1022
|
};
|
|
1187
1023
|
}
|
|
1188
1024
|
|
|
1189
|
-
// --- dry-run: print the plan, execute nothing -----------------------------
|
|
1190
1025
|
if (plan.action === 'dry-run') {
|
|
1191
1026
|
if (plan.variant === 'drift-heal') {
|
|
1192
1027
|
write(
|
|
@@ -1219,7 +1054,6 @@ export async function runUpdate({
|
|
|
1219
1054
|
};
|
|
1220
1055
|
}
|
|
1221
1056
|
|
|
1222
|
-
// --- resynced / updated: execute the phase plan ---------------------------
|
|
1223
1057
|
const projectRoot = cwd();
|
|
1224
1058
|
|
|
1225
1059
|
if (plan.action === 'resynced') {
|
|
@@ -1245,8 +1079,16 @@ export async function runUpdate({
|
|
|
1245
1079
|
});
|
|
1246
1080
|
|
|
1247
1081
|
if (plan.action === 'resynced') {
|
|
1082
|
+
// No dependency is bumped here, so the re-materialized payload IS the diff.
|
|
1083
|
+
const payloadLine = resolveStagingReport({
|
|
1084
|
+
gitStatus,
|
|
1085
|
+
detectLockfile,
|
|
1086
|
+
projectRoot,
|
|
1087
|
+
scope: 'payload',
|
|
1088
|
+
});
|
|
1248
1089
|
write(
|
|
1249
|
-
`✅ Healed .agents/ drift (v${current}). The materialized payload is now current
|
|
1090
|
+
`✅ Healed .agents/ drift (v${current}). The materialized payload is now current.` +
|
|
1091
|
+
`${payloadLine ? ` ${payloadLine}` : ''}\n`,
|
|
1250
1092
|
);
|
|
1251
1093
|
return {
|
|
1252
1094
|
ok: true,
|
|
@@ -1258,7 +1100,6 @@ export async function runUpdate({
|
|
|
1258
1100
|
};
|
|
1259
1101
|
}
|
|
1260
1102
|
|
|
1261
|
-
// plan.action === 'updated'
|
|
1262
1103
|
if (!doctorOk) {
|
|
1263
1104
|
return {
|
|
1264
1105
|
ok: false,
|
|
@@ -1270,7 +1111,9 @@ export async function runUpdate({
|
|
|
1270
1111
|
};
|
|
1271
1112
|
}
|
|
1272
1113
|
|
|
1273
|
-
write(
|
|
1114
|
+
write(
|
|
1115
|
+
`✅ Updated to v${target}. ${resolveStagingReport({ gitStatus, detectLockfile, projectRoot })}\n`,
|
|
1116
|
+
);
|
|
1274
1117
|
return {
|
|
1275
1118
|
ok: true,
|
|
1276
1119
|
action: 'updated',
|
|
@@ -1282,46 +1125,10 @@ export async function runUpdate({
|
|
|
1282
1125
|
}
|
|
1283
1126
|
|
|
1284
1127
|
/**
|
|
1285
|
-
*
|
|
1128
|
+
* Entry point for `bin/mandrel.js`: wires the production seams. `deps` exposes
|
|
1129
|
+
* the process boundaries for tests only; it is not public contract.
|
|
1286
1130
|
*
|
|
1287
|
-
*
|
|
1288
|
-
* - `resolveTargetVersion` always probes the registry via `isStale` with
|
|
1289
|
-
* `bypassCache: true` — the 24h cache is overridden for explicit update
|
|
1290
|
-
* calls so the resolved version is always fresh (Story #4046 A1b). The
|
|
1291
|
-
* cache is still written so the `version-current` doctor advisory reads
|
|
1292
|
-
* a current baseline after the upgrade.
|
|
1293
|
-
* - `npmUpdate` runs the install command — auto-detected from the project
|
|
1294
|
-
* lockfile (`pnpm`/`yarn`/`npm`), or the `--install-cmd` override —
|
|
1295
|
-
* through the shared `runInstallCommand` helper — no git mutation;
|
|
1296
|
-
* lockfile left staged.
|
|
1297
|
-
* - `spawnPhase` is wired to `defaultSpawnPhase`, which spawns each
|
|
1298
|
-
* post-install phase (sync, sync-commands, migrate, doctor) as
|
|
1299
|
-
* `node <packageRoot>/bin/mandrel.js …` (Story #4613 — the resolved bin
|
|
1300
|
-
* script, not the `node_modules/.bin` shim). This is the
|
|
1301
|
-
* Story #4034 fix: the new bin loads the new package's module code and
|
|
1302
|
-
* resolves paths against the new install dir, so these phases can never
|
|
1303
|
-
* observe the old payload.
|
|
1304
|
-
* - `surfaceChangelog` prints the relevant `docs/CHANGELOG.md` section(s)
|
|
1305
|
-
* for the applied range. Reads from the packaged file first; falls back to
|
|
1306
|
-
* a GitHub raw-content fetch via the injectable `fetchChangelog` seam when
|
|
1307
|
-
* the packaged file is absent; emits an actionable link to the GitHub
|
|
1308
|
-
* Releases page when both sources fail (Story #4035).
|
|
1309
|
-
*
|
|
1310
|
-
* Every seam stays injectable on `runUpdate`; these are merely the
|
|
1311
|
-
* no-seam-provided fallbacks, so the existing seam-driven tests stay green.
|
|
1312
|
-
* `--dry-run` / `--install-cmd` are parsed from `argv` by
|
|
1313
|
-
* `runUpdate` itself.
|
|
1314
|
-
*
|
|
1315
|
-
* The second `deps` argument exposes the **process boundaries** the production
|
|
1316
|
-
* defaults shell out across (`versionRunner` = `npm view`, `runInstall` =
|
|
1317
|
-
* the install spawn, `spawnFn` = the phase-spawn boundary) plus `fs` /
|
|
1318
|
-
* `cachePath` / `now`, so the entrypoint can be driven end-to-end with the
|
|
1319
|
-
* network/npm boundary stubbed and no real I/O.
|
|
1320
|
-
* `bin/mandrel.js` calls `run(argv)` with no `deps`, getting the production
|
|
1321
|
-
* wiring; tests pass fakes. The `deps` surface is NOT part of the public
|
|
1322
|
-
* subcommand contract — `bin/mandrel.js` only ever supplies `argv`.
|
|
1323
|
-
*
|
|
1324
|
-
* @param {string[]} argv - Subcommand arguments (after `mandrel update`).
|
|
1131
|
+
* @param {string[]} argv
|
|
1325
1132
|
* @param {{
|
|
1326
1133
|
* currentVersion?: string,
|
|
1327
1134
|
* cachePath?: string,
|
|
@@ -1336,6 +1143,8 @@ export async function runUpdate({
|
|
|
1336
1143
|
* cwd?: () => string,
|
|
1337
1144
|
* resolveBinScript?: (projectRoot: string) => string,
|
|
1338
1145
|
* checkDrift?: () => (boolean | Promise<boolean>),
|
|
1146
|
+
* gitStatus?: (opts: { cwd: string }) => object,
|
|
1147
|
+
* detectLockfile?: (cwd: string) => string,
|
|
1339
1148
|
* write?: (s: string) => void,
|
|
1340
1149
|
* writeErr?: (s: string) => void,
|
|
1341
1150
|
* exit?: (code: number) => void,
|
|
@@ -1361,22 +1170,15 @@ export default async function run(argv = [], deps = {}) {
|
|
|
1361
1170
|
cwd,
|
|
1362
1171
|
resolveBinScript,
|
|
1363
1172
|
checkDrift,
|
|
1173
|
+
gitStatus,
|
|
1174
|
+
detectLockfile,
|
|
1364
1175
|
} = deps;
|
|
1365
1176
|
|
|
1366
1177
|
const cwdFn = typeof cwd === 'function' ? cwd : () => process.cwd();
|
|
1367
1178
|
|
|
1368
|
-
// Story #4525/#4530: prefer the consumer's declared dependency pin over
|
|
1369
|
-
// the pre-#4525 self-referential read — see resolveCurrentVersionForUpdate.
|
|
1370
1179
|
const current =
|
|
1371
1180
|
deps.currentVersion ?? resolveCurrentVersionForUpdate(cwdFn(), fs);
|
|
1372
1181
|
|
|
1373
|
-
// The production spawnPhase: spawn each post-install phase as
|
|
1374
|
-
// `node <packageRoot>/bin/mandrel.js …` (the newly-installed bin script,
|
|
1375
|
-
// resolved layout-agnostically per Story #4613 — not the node_modules/.bin
|
|
1376
|
-
// shim). This is the sole post-install execution path (No-Shim — Story #4182
|
|
1377
|
-
// retired the in-process runSync/runMigrations/runDoctor seam set). spawnFn
|
|
1378
|
-
// is injectable so tests can stub the spawn boundary without running a real
|
|
1379
|
-
// child process.
|
|
1380
1182
|
const productionSpawnPhase = (phase, args, opts) =>
|
|
1381
1183
|
defaultSpawnPhase(phase, args, {
|
|
1382
1184
|
...opts,
|
|
@@ -1386,8 +1188,7 @@ export default async function run(argv = [], deps = {}) {
|
|
|
1386
1188
|
await runUpdateImpl({
|
|
1387
1189
|
argv,
|
|
1388
1190
|
currentVersion: current,
|
|
1389
|
-
//
|
|
1390
|
-
// resolved target is fresh from the registry (Story #4046 A1b).
|
|
1191
|
+
// An explicit update always bypasses the 24h cache.
|
|
1391
1192
|
resolveTargetVersion: () =>
|
|
1392
1193
|
defaultResolveTargetVersion({
|
|
1393
1194
|
cachePath:
|
|
@@ -1405,6 +1206,10 @@ export default async function run(argv = [], deps = {}) {
|
|
|
1405
1206
|
fs,
|
|
1406
1207
|
}),
|
|
1407
1208
|
...(checkDrift ? { checkDrift } : {}),
|
|
1209
|
+
...(gitStatus ? { gitStatus } : {}),
|
|
1210
|
+
...(detectLockfile
|
|
1211
|
+
? { detectLockfile }
|
|
1212
|
+
: { detectLockfile: (dir) => defaultDetectLockfile(dir, fs) }),
|
|
1408
1213
|
spawnPhase: productionSpawnPhase,
|
|
1409
1214
|
surfaceChangelog: (target) =>
|
|
1410
1215
|
defaultSurfaceChangelog(target, {
|
|
@@ -1419,8 +1224,6 @@ export default async function run(argv = [], deps = {}) {
|
|
|
1419
1224
|
writeErr,
|
|
1420
1225
|
exit,
|
|
1421
1226
|
cwd: cwdFn,
|
|
1422
|
-
// Pass through undefined in production so runUpdate applies its default
|
|
1423
|
-
// resolver (resolveNewBinScriptPath); tests inject a stub for a fake root.
|
|
1424
1227
|
resolveBinScript,
|
|
1425
1228
|
});
|
|
1426
1229
|
}
|