mandrel 2.60.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 +10 -10
- package/.agents/agents/story-worker.md +1 -1
- package/.agents/docs/agentrc-reference.json +4 -82
- package/.agents/docs/configuration.md +20 -87
- package/.agents/docs/workflows.md +1 -1
- package/.agents/instructions.md +1 -3
- package/.agents/rules/gherkin-standards.md +4 -0
- package/.agents/schemas/agentrc.schema.json +11 -486
- package/.agents/schemas/story-deliver-terminal.schema.json +10 -0
- package/.agents/schemas/validation-evidence.schema.json +2 -1
- package/.agents/scripts/README.md +14 -14
- package/.agents/scripts/acceptance-eval.js +32 -164
- 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 +9 -45
- package/.agents/scripts/check-arch-cycles.js +14 -64
- package/.agents/scripts/check-baselines.js +8 -48
- package/.agents/scripts/check-context-budget.js +46 -172
- 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 +32 -99
- package/.agents/scripts/deliver-light.js +14 -101
- package/.agents/scripts/deliver-recover.js +6 -42
- package/.agents/scripts/deliver-run.js +36 -133
- 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 +10 -50
- 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 +5 -21
- 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 +31 -143
- 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 -35
- package/.agents/scripts/lib/config/explain.js +9 -80
- package/.agents/scripts/lib/config/gates/coverage.schema.js +0 -12
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +2 -30
- 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 +10 -52
- package/.agents/scripts/lib/config-schema-shared.js +1 -4
- package/.agents/scripts/lib/config-settings-schema-delivery.js +12 -294
- 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 +12 -37
- package/.agents/scripts/lib/coverage-capture-incremental.js +12 -43
- package/.agents/scripts/lib/coverage-capture-usage.js +4 -24
- 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 -126
- 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 +93 -419
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +20 -98
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +27 -151
- 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 +31 -259
- 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 +21 -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 +5 -75
- 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 +42 -189
- 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 +42 -214
- 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 +26 -231
- 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 +52 -303
- 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 +14 -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 +49 -248
- package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +2 -12
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +85 -368
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +8 -38
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +30 -190
- 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 -94
- 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 +40 -199
- package/.agents/scripts/lib/orchestration/run-scoped-config.js +13 -81
- 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 +18 -80
- 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 +9 -50
- 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 +110 -290
- 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 -201
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +43 -245
- package/.agents/scripts/lib/orchestration/story-init-envelope.js +4 -23
- 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 -126
- 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 +39 -181
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +51 -244
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +39 -250
- 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 +22 -83
- package/.agents/scripts/lib/story-body/footer-block.js +11 -44
- package/.agents/scripts/lib/story-body/story-body.js +83 -358
- package/.agents/scripts/lib/temp-retention.js +42 -143
- package/.agents/scripts/lib/templates/decomposer-prompts.js +11 -65
- 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 +53 -221
- 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 -104
- package/.agents/scripts/nav-registry-diff.js +22 -108
- package/.agents/scripts/notify.js +12 -69
- package/.agents/scripts/plan-context.js +27 -111
- package/.agents/scripts/plan-critics.js +12 -66
- package/.agents/scripts/plan-persist.js +31 -148
- package/.agents/scripts/plan-run-epilogue.js +11 -37
- package/.agents/scripts/pr-watch-with-update.js +76 -267
- 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 -209
- package/.agents/scripts/stories-wave-tick.js +102 -472
- 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/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/audit-lens-core.md +1 -1
- package/.agents/workflows/helpers/code-quality-guardrails.md +12 -15
- package/.agents/workflows/helpers/deliver-digest.md +6 -4
- package/.agents/workflows/helpers/deliver-reference.md +114 -188
- package/.agents/workflows/helpers/deliver-story-reference.md +189 -533
- package/.agents/workflows/helpers/plan-reference.md +86 -139
- package/.agents/workflows/helpers/qa-core.md +12 -0
- 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 +25 -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 +105 -661
- 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 +15 -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 +2 -21
- package/lib/migrations/steps/strip-removed-agentrc-keys.js +333 -0
- package/package.json +16 -14
- package/.agents/docs/SDLC.md +0 -590
- package/.agents/docs/quality-gates.md +0 -1183
- package/.agents/rules/known-tooling-behavior.md +0 -200
- 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/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 -368
- package/.agents/scripts/check-test-portability.js +0 -512
- package/.agents/scripts/check-workflow-citations.js +0 -218
- 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/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 -125
- 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,164 +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 (manifest + lockfile rewritten on
|
|
20
|
-
* disk; whether the package manager also stages them is reported, never
|
|
21
|
-
* assumed — see § No git mutation).
|
|
22
|
-
* The package manager is auto-detected from the lockfile in the project
|
|
23
|
-
* root: `pnpm-lock.yaml` ⇒ `pnpm add -D …` (with `-w` at a
|
|
24
|
-
* `pnpm-workspace.yaml` root), `yarn.lock` ⇒ `yarn add -D …`, otherwise
|
|
25
|
-
* `npm install …`. An explicit `--install-cmd "<pm> <args>"` overrides
|
|
26
|
-
* detection; a `{target}` placeholder in the override is substituted with
|
|
27
|
-
* the resolved version so an override can still consume the auto-probed
|
|
28
|
-
* newest. The registry probe in step 1 always stays on `npm view` (a
|
|
29
|
-
* PM-agnostic registry query).
|
|
30
|
-
* 4. runSync — re-materialize ./.agents/ **from the newly-installed
|
|
31
|
-
* binary** so the materialized payload is always the target version's.
|
|
32
|
-
* 5. runMigrations — apply version-keyed steps for the crossed range,
|
|
33
|
-
* **from the newly-installed binary**.
|
|
34
|
-
* 6. doctor — run the check registry **from the newly-installed
|
|
35
|
-
* binary** so `agents-drift` is never a false-green against stale payload.
|
|
36
|
-
* 7. surface the changelog for the target version
|
|
37
|
-
*
|
|
38
|
-
* ## Re-exec of post-install phases (Story #4034)
|
|
39
|
-
*
|
|
40
|
-
* Steps 4–6 execute as **child processes spawned from the newly-installed
|
|
41
|
-
* bin script** (`node <cwd>/node_modules/mandrel/bin/mandrel.js`; Story #4613
|
|
42
|
-
* resolves the script layout-agnostically rather than via the `.bin` shim)
|
|
43
|
-
* rather than in the running
|
|
44
|
-
* process. Node cannot hot-swap a `require`d module mid-process, so without
|
|
45
|
-
* re-exec, the still-running old binary's `runSync`/`runMigrations`/`runDoctor`
|
|
46
|
-
* code would materialise the old payload even though the package on disk has
|
|
47
|
-
* already been updated. This produced the silent stale-`.agents/`
|
|
48
|
-
* materialization and `doctor` false-green observed in the v1.58.0 → v1.59.0
|
|
49
|
-
* consumer upgrade.
|
|
50
|
-
*
|
|
51
|
-
* The orchestration (progress messages, step tracking, changelog surface, exit
|
|
52
|
-
* code) stays in the parent process; only the version-sensitive phases run from
|
|
53
|
-
* the new bin. The `spawnPhase` seam makes the child-process boundary fully
|
|
54
|
-
* injectable so tests can verify the re-exec path without a real npm install.
|
|
55
|
-
* It is the **only** post-install execution path — tests stub the spawn
|
|
56
|
-
* boundary rather than swapping in an in-process implementation (No-Shim:
|
|
57
|
-
* `.agents/rules/git-conventions.md` § Contract Cutovers).
|
|
58
|
-
*
|
|
59
|
-
* ## No git mutation
|
|
60
|
-
*
|
|
61
|
-
* The dependency bump rewrites `package.json` / the lockfile in the working
|
|
62
|
-
* tree but the orchestrator performs **no** `git add` / `git commit`. This
|
|
63
|
-
* module **reads git state to report it; never mutates it** (Story #5339).
|
|
64
|
-
*
|
|
65
|
-
* The final success line used to assert "the lockfile bump is staged for
|
|
66
|
-
* review" unconditionally. That is only ever true where `npm install` happens
|
|
67
|
-
* to stage; the `pnpm add` / `yarn add` seams stage nothing, so on a pnpm
|
|
68
|
-
* consumer the operator was told to review an empty index while the manifest,
|
|
69
|
-
* the lockfile and every re-materialized `.agents/` path sat unstaged — an
|
|
70
|
-
* invitation to commit a half-upgrade. The orchestrator now probes the real
|
|
71
|
-
* index through the injectable `gitStatus` seam — two read-only calls, a
|
|
72
|
-
* pathspec-anchored `git status --porcelain` plus `git ls-files .agents` — and
|
|
73
|
-
* prints one of three truthful lines: staged, not staged (with the exact
|
|
74
|
-
* `git add` command), or a neutral "review the working tree" line when git is
|
|
75
|
-
* unavailable or the probe fails. A failed probe never fails the update: the
|
|
76
|
-
* run still exits 0.
|
|
77
|
-
*
|
|
78
|
-
* **Staged means both halves** (Story #5364): the index differs from HEAD
|
|
79
|
-
* *and* the worktree agrees with the index. Reading only the index reported a
|
|
80
|
-
* manifest pair the operator staged *before* running the command as staged
|
|
81
|
-
* afterwards, even though the install had since rewritten both files on disk —
|
|
82
|
-
* the exact false claim this report exists to remove. The drift-heal path
|
|
83
|
-
* reports the re-materialized payload the same way.
|
|
84
|
-
*
|
|
85
|
-
* ## `--dry-run`
|
|
86
|
-
*
|
|
87
|
-
* Prints the resolved target version and the ordered step plan, then returns
|
|
88
|
-
* without invoking any effectful seam (no npm update, no sync, no migrations,
|
|
89
|
-
* no doctor) and writing nothing.
|
|
90
|
-
*
|
|
91
|
-
* ## Changelog surface
|
|
92
|
-
*
|
|
93
|
-
* `defaultSurfaceChangelog` prints the `docs/CHANGELOG.md` section(s) for the
|
|
94
|
-
* applied range `(current, target]`. It resolves the file against the target
|
|
95
|
-
* version's install directory (the freshly bumped `node_modules/mandrel/`),
|
|
96
|
-
* where the changelog is now included in the published tarball
|
|
97
|
-
* (`docs/CHANGELOG.md` in the `files` allowlist — Story #4035).
|
|
98
|
-
*
|
|
99
|
-
* When the packaged file is absent (e.g. an older installed version predating
|
|
100
|
-
* Story #4035), the seam attempts a one-shot HTTP GET of the raw file from
|
|
101
|
-
* GitHub via the injectable `fetchChangelog` seam. If that fetch also fails,
|
|
102
|
-
* the seam degrades gracefully — never throwing — and emits an actionable
|
|
103
|
-
* message directing the operator to the GitHub Releases page.
|
|
104
|
-
*
|
|
105
|
-
* ## Injectable seams (used by lib/cli/__tests__/update*.test.js)
|
|
106
|
-
*
|
|
107
|
-
* - `argv` — subcommand args (after `mandrel update`)
|
|
108
|
-
* - `currentVersion` — the installed `mandrel` version string
|
|
109
|
-
* - `resolveTargetVersion`— async, returns the newest published version
|
|
110
|
-
* - `checkDrift` — sync or async, returns `true` when `.agents/`
|
|
111
|
-
* differs from the installed payload. Used by the
|
|
112
|
-
* drift-aware no-op short-circuit (Story #4065).
|
|
113
|
-
* Defaults to `() => !runAgentsDrift().ok`, which
|
|
114
|
-
* reuses the same `agents-drift` doctor signal.
|
|
115
|
-
* - `npmUpdate` — async, performs the dependency bump (no git);
|
|
116
|
-
* receives `(target, { installCmd })`
|
|
117
|
-
* - `spawnPhase` — async, spawns a post-install phase from the new
|
|
118
|
-
* binary; receives `(phase, args, { binPath, cwd })`
|
|
119
|
-
* and returns `{ ok, stdout, stderr }`. This is the
|
|
120
|
-
* sole post-install execution path. See § Re-exec of
|
|
121
|
-
* post-install phases.
|
|
122
|
-
* - `surfaceChangelog` — emits the target changelog section
|
|
123
|
-
* - `gitStatus` — read-only git probe backing the staging report;
|
|
124
|
-
* receives `({ cwd, lockfile })` and returns
|
|
125
|
-
* `{ ok, stagedManifest, stagedLockfile,
|
|
126
|
-
* stagedPayload, tracksAgents }` (Story #5339,
|
|
127
|
-
* #5364). Never mutates the repository.
|
|
128
|
-
* - `detectLockfile` — resolves the lockfile name the staging report
|
|
129
|
-
* names — and the probe's pathspec uses — from the
|
|
130
|
-
* uncoerced package-manager probe, so a bun
|
|
131
|
-
* consumer is named its real lockfile; receives
|
|
132
|
-
* `(cwd)`
|
|
133
|
-
* - `write` / `writeErr` — stdout / stderr sinks
|
|
134
|
-
* - `exit` — process.exit replacement
|
|
135
|
-
* - `cwd` — process.cwd() replacement (used to resolve the
|
|
136
|
-
* new binary path the post-install phases spawn from)
|
|
137
|
-
*
|
|
138
|
-
* Security (security-baseline § 5 — Data Leakage & Logging): logs only version
|
|
139
|
-
* strings and step names. No tokens, credentials, or env
|
|
140
|
-
* values are read or logged; no shell-string interpolation occurs here (the
|
|
141
|
-
* npm bump is delegated to the injected `npmUpdate` seam, which owns transport).
|
|
142
|
-
*
|
|
143
|
-
* ## Windows spawn (CVE-2024-27980)
|
|
144
|
-
*
|
|
145
|
-
* Both child-process boundaries — the `npm view` registry probe and the
|
|
146
|
-
* install — route through helpers that pass `shell: process.platform ===
|
|
147
|
-
* 'win32'`. On Windows `npm`/`pnpm`/`yarn` resolve to `.cmd` shims, and
|
|
148
|
-
* Node 18.20+/20.12+/22+/24 refuses to spawn `.cmd`/`.bat` with `shell:false`
|
|
149
|
-
* (the CVE-2024-27980 hardening), throwing `spawnSync npm ENOENT`. The win32
|
|
150
|
-
* shell flag is the documented fix. It is injection-safe because every argv
|
|
151
|
-
* here is a **fixed vector**: the probe argv is the constant package name, and
|
|
152
|
-
* the install argv is a tokenized list whose only variable segment is a
|
|
153
|
-
* resolved semver string — see `lib/install-cmd-parser.js` for the shared
|
|
154
|
-
* tokenize-and-spawn rationale this module reuses (no duplicated workaround).
|
|
155
|
-
*
|
|
156
|
-
* The `spawnPhase` default (Story #4034) does **not** use the win32 shell flag:
|
|
157
|
-
* it spawns `process.execPath` (node) against the resolved `bin/mandrel.js`
|
|
158
|
-
* script (Story #4613), so it never touches a `.cmd` shim and needs no
|
|
159
|
-
* shell on any platform. The per-phase argv vector is a constant fixed list
|
|
160
|
-
* (e.g. `['sync']`, `['migrate', '--from', v, '--to', v]`, `['doctor']`) with
|
|
161
|
-
* 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.
|
|
162
10
|
*/
|
|
163
11
|
|
|
164
12
|
import { spawnSync } from 'node:child_process';
|
|
@@ -177,38 +25,18 @@ import {
|
|
|
177
25
|
resolveConsumerPinVersion,
|
|
178
26
|
} from './version-helpers.js';
|
|
179
27
|
|
|
180
|
-
/** The published package whose newest version `mandrel update` advances to. */
|
|
181
28
|
const PACKAGE_NAME = 'mandrel';
|
|
182
29
|
|
|
183
|
-
/**
|
|
184
|
-
* GitHub raw-file base URL for fetching `docs/CHANGELOG.md` when the packaged
|
|
185
|
-
* file is absent (Story #4035 — GitHub fallback). Resolves to the tagged
|
|
186
|
-
* release, e.g. `.../mandrel-v1.59.0/docs/CHANGELOG.md`.
|
|
187
|
-
*/
|
|
188
30
|
const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/dsj1984/mandrel/';
|
|
189
31
|
|
|
190
|
-
/**
|
|
191
|
-
* Human-readable GitHub Releases page — surfaced in the actionable fallback
|
|
192
|
-
* message when neither the packaged file nor the GitHub fetch succeeds.
|
|
193
|
-
*/
|
|
194
32
|
const GITHUB_RELEASES_URL = 'https://github.com/dsj1984/mandrel/releases';
|
|
195
33
|
|
|
196
|
-
/** Default freshness-cache filename — mirrors version-check.js. */
|
|
197
34
|
const DEFAULT_CACHE_FILENAME = 'version-check.json';
|
|
198
35
|
|
|
199
36
|
/**
|
|
200
|
-
*
|
|
201
|
-
* `
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* Pre-Story-#4525 this was the update decision's `current` — the exact
|
|
205
|
-
* self-referential confusion #4525 filed: "the version of the mandrel that
|
|
206
|
-
* is executing" is tautologically `>= target` whenever the installed
|
|
207
|
-
* package is newest, which made the `npm-update` step unreachable whenever
|
|
208
|
-
* a consumer's declared pin had fallen behind what happened to be resolved
|
|
209
|
-
* in `node_modules`. It survives here only as the last-resort fallback
|
|
210
|
-
* inside {@link resolveCurrentVersionForUpdate}, for the case where even
|
|
211
|
-
* `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.
|
|
212
40
|
*
|
|
213
41
|
* @param {typeof nodeFs} [fs]
|
|
214
42
|
* @returns {string}
|
|
@@ -221,27 +49,14 @@ function defaultCurrentVersion(fs = nodeFs) {
|
|
|
221
49
|
}
|
|
222
50
|
|
|
223
51
|
/**
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* `consumerRoot` via the same resolution `mandrel sync` uses, unlike the
|
|
229
|
-
* pre-#4525 self-referential `defaultCurrentVersion`), and finally to
|
|
230
|
-
* `defaultCurrentVersion` itself when neither resolves (a corrupted or
|
|
231
|
-
* highly unusual install — keeps `mandrel update` from throwing outright
|
|
232
|
-
* rather than silently misreporting "already current").
|
|
233
|
-
*
|
|
234
|
-
* The declared pin is preferred because it is exactly what the `npm-update`
|
|
235
|
-
* step moves: a consumer whose `package.json` pin lags an inflated
|
|
236
|
-
* `node_modules` resolution (e.g. an out-of-band symlink or manual
|
|
237
|
-
* `npm install mandrel@latest --no-save`) must still see `planUpdate`
|
|
238
|
-
* 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.
|
|
239
56
|
*
|
|
240
57
|
* @param {string} consumerRoot
|
|
241
58
|
* @param {typeof nodeFs} [fs]
|
|
242
|
-
* @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts]
|
|
243
|
-
* seam for the `node_modules` resolution tier; defaults to the real
|
|
244
|
-
* `defaultResolvePackageRoot` from `sync.js`.
|
|
59
|
+
* @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts]
|
|
245
60
|
* @returns {string}
|
|
246
61
|
*/
|
|
247
62
|
export function resolveCurrentVersionForUpdate(
|
|
@@ -262,42 +77,15 @@ export function resolveCurrentVersionForUpdate(
|
|
|
262
77
|
}
|
|
263
78
|
}
|
|
264
79
|
|
|
265
|
-
/**
|
|
266
|
-
* Resolve the project root — the directory two levels up from this module
|
|
267
|
-
* (`<root>/lib/cli/update.js`). Mirrors `lib/cli/registry.js#resolveProjectRoot`.
|
|
268
|
-
*
|
|
269
|
-
* @returns {string}
|
|
270
|
-
*/
|
|
80
|
+
/** @returns {string} */
|
|
271
81
|
function resolveProjectRoot() {
|
|
272
82
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
273
83
|
return path.resolve(here, '..', '..');
|
|
274
84
|
}
|
|
275
85
|
|
|
276
86
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* When `bypassCache` is `true` (the default for an explicit `mandrel update`
|
|
281
|
-
* call — Story #4046 A1b), `isStale` is asked to skip its freshness read via
|
|
282
|
-
* `forceRefresh`, so any existing cache is ignored and exactly one network
|
|
283
|
-
* probe is issued. The cache is still written so the post-update
|
|
284
|
-
* `version-current` advisory has a fresh baseline.
|
|
285
|
-
*
|
|
286
|
-
* The bypass deliberately does **not** shift `now`: the same clock is stamped
|
|
287
|
-
* into the refreshed `checkedAt`, so a shifted clock would persist a
|
|
288
|
-
* ~48h-future timestamp and defeat the 24h window for every later passive
|
|
289
|
-
* check until real time caught up (Story #4878).
|
|
290
|
-
*
|
|
291
|
-
* When `bypassCache` is `false` (passive staleness checks only), the normal
|
|
292
|
-
* 24h-cache semantics apply: a fresh cache returns the cached version with
|
|
293
|
-
* zero network I/O.
|
|
294
|
-
*
|
|
295
|
-
* The network probe shells `npm view` through `spawnSync` with a fixed argument
|
|
296
|
-
* vector (no shell-string interpolation; the package name is a constant). On
|
|
297
|
-
* Windows the spawn sets `shell: true` so the `npm.cmd` shim resolves under the
|
|
298
|
-
* CVE-2024-27980 hardening (mirrors `lib/install-cmd-parser.js`); the fixed
|
|
299
|
-
* argv carries no injection risk even with the shell flag set
|
|
300
|
-
* (security-baseline § Output & Rendering).
|
|
87
|
+
* Newest published version via the daily freshness cache. `bypassCache` forces
|
|
88
|
+
* one probe but still writes the cache.
|
|
301
89
|
*
|
|
302
90
|
* @param {{
|
|
303
91
|
* cachePath?: string,
|
|
@@ -317,9 +105,8 @@ async function defaultResolveTargetVersion({
|
|
|
317
105
|
bypassCache = false,
|
|
318
106
|
log = () => {},
|
|
319
107
|
} = {}) {
|
|
320
|
-
// Bypass
|
|
321
|
-
//
|
|
322
|
-
// (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.
|
|
323
110
|
const result = await isStale({
|
|
324
111
|
cachePath,
|
|
325
112
|
now,
|
|
@@ -332,15 +119,10 @@ async function defaultResolveTargetVersion({
|
|
|
332
119
|
}
|
|
333
120
|
|
|
334
121
|
/**
|
|
335
|
-
*
|
|
336
|
-
* `npm view mandrel version` synchronously and returns the trimmed
|
|
337
|
-
* stdout. Fixed argv (the package name is a constant), and `shell:true` only on
|
|
338
|
-
* Windows so the `npm.cmd` shim resolves under CVE-2024-27980 — the fixed
|
|
339
|
-
* vector keeps it injection-safe with or without the shell flag.
|
|
122
|
+
* `npm view mandrel version`, trimmed.
|
|
340
123
|
*
|
|
341
|
-
* @param {{ spawnSync?: typeof spawnSync }} [deps]
|
|
342
|
-
*
|
|
343
|
-
* @returns {string} The newest published version string.
|
|
124
|
+
* @param {{ spawnSync?: typeof spawnSync }} [deps]
|
|
125
|
+
* @returns {string}
|
|
344
126
|
*/
|
|
345
127
|
export function defaultVersionRunner({ spawnSync: spawn = spawnSync } = {}) {
|
|
346
128
|
const r = spawn('npm', ['view', PACKAGE_NAME, 'version'], {
|
|
@@ -368,9 +150,7 @@ export function defaultVersionRunner({ spawnSync: spawn = spawnSync } = {}) {
|
|
|
368
150
|
}
|
|
369
151
|
|
|
370
152
|
/**
|
|
371
|
-
*
|
|
372
|
-
* Surfaced in the repair hint when an install fails so the operator can restore
|
|
373
|
-
* `node_modules` to a consistent state (Story #3575 AC-4).
|
|
153
|
+
* The full-install command named in the failed-install repair hint.
|
|
374
154
|
*
|
|
375
155
|
* @param {'pnpm' | 'yarn' | 'npm'} packageManager
|
|
376
156
|
* @returns {string}
|
|
@@ -382,45 +162,26 @@ function repairInstallCommand(packageManager) {
|
|
|
382
162
|
}
|
|
383
163
|
|
|
384
164
|
/**
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
* `
|
|
388
|
-
*
|
|
389
|
-
* signal that `pnpm add` must carry `-w` to target the workspace-root manifest.
|
|
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.
|
|
390
169
|
*
|
|
391
|
-
*
|
|
392
|
-
* the root cause this resolves (Story #3575): npm chokes on the pnpm-managed
|
|
393
|
-
* tree, exits non-zero, and can flip `node_modules` to a stale store entry.
|
|
394
|
-
* Detecting the lockfile keeps the bump on the operator's real package manager
|
|
395
|
-
* so the change lands in the matching lockfile.
|
|
396
|
-
*
|
|
397
|
-
* Delegates to the shared `detectPackageManagerWithWorkspace` helper
|
|
398
|
-
* (Story #4048 B3 — one implementation per concept). The `fs` seam is adapted
|
|
399
|
-
* to the shared module's `exists` contract; the shared module's `bun` return
|
|
400
|
-
* value coerces to `npm` here because `bun add` is not yet a first-class update
|
|
401
|
-
* path for this orchestrator.
|
|
402
|
-
*
|
|
403
|
-
* @param {string} [cwd] - Project root to probe (default `process.cwd()`).
|
|
170
|
+
* @param {string} [cwd]
|
|
404
171
|
* @param {typeof nodeFs} [fs]
|
|
405
172
|
* @returns {{ packageManager: 'pnpm' | 'yarn' | 'npm', workspaceRoot: boolean }}
|
|
406
173
|
*/
|
|
407
174
|
export function detectPackageManager(cwd = process.cwd(), fs = nodeFs) {
|
|
408
175
|
const result = probePackageManager(cwd, fs);
|
|
409
|
-
// Coerce `bun` → `npm` because this orchestrator's install-command builder
|
|
410
|
-
// only handles pnpm / yarn / npm today.
|
|
411
176
|
const packageManager =
|
|
412
177
|
result.packageManager === 'bun' ? 'npm' : result.packageManager;
|
|
413
178
|
return { packageManager, workspaceRoot: result.workspaceRoot };
|
|
414
179
|
}
|
|
415
180
|
|
|
416
181
|
/**
|
|
417
|
-
* The
|
|
418
|
-
* flattens `bun` to `npm` for the install-command builder's benefit; the
|
|
419
|
-
* staging report must not inherit that flattening, or a bun consumer is told
|
|
420
|
-
* to stage a `package-lock.json` its tree does not have (Story #5364). Reading
|
|
421
|
-
* the probe before the coercion is the whole fix.
|
|
182
|
+
* The uncoerced probe; the staging report must name bun's real lockfile.
|
|
422
183
|
*
|
|
423
|
-
* @param {string} cwd
|
|
184
|
+
* @param {string} cwd
|
|
424
185
|
* @param {typeof nodeFs} [fs]
|
|
425
186
|
* @returns {{ packageManager: 'pnpm'|'yarn'|'bun'|'npm', workspaceRoot: boolean }}
|
|
426
187
|
*/
|
|
@@ -436,24 +197,11 @@ function probePackageManager(cwd, fs = nodeFs) {
|
|
|
436
197
|
}
|
|
437
198
|
|
|
438
199
|
/**
|
|
439
|
-
*
|
|
440
|
-
*
|
|
441
|
-
* With no override the command is built from the detected package manager:
|
|
442
|
-
* - `pnpm` ⇒ `pnpm add -D mandrel@<target>` (plus ` -w` at a pnpm
|
|
443
|
-
* workspace root)
|
|
444
|
-
* - `yarn` ⇒ `yarn add -D mandrel@<target>`
|
|
445
|
-
* - `npm` ⇒ `npm install mandrel@<target>` (the unchanged default)
|
|
200
|
+
* The install command. An `--install-cmd` override is used verbatim except
|
|
201
|
+
* that `{target}` is replaced with the resolved semver.
|
|
446
202
|
*
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
* can still consume the auto-probed newest version (Story #3575 AC-3).
|
|
450
|
-
*
|
|
451
|
-
* This function is pure: package-manager detection happens in
|
|
452
|
-
* `detectPackageManager` (the only filesystem seam) and is passed in as
|
|
453
|
-
* `detected`, keeping the command-string assembly trivially unit-testable.
|
|
454
|
-
*
|
|
455
|
-
* @param {string} target - The resolved semver to install.
|
|
456
|
-
* @param {string} [override] - Operator-supplied `--install-cmd` value.
|
|
203
|
+
* @param {string} target
|
|
204
|
+
* @param {string} [override]
|
|
457
205
|
* @param {{
|
|
458
206
|
* packageManager?: 'pnpm' | 'yarn' | 'npm',
|
|
459
207
|
* workspaceRoot?: boolean,
|
|
@@ -480,11 +228,6 @@ export function resolveInstallCmd(
|
|
|
480
228
|
return `npm install ${PACKAGE_NAME}@${target}`;
|
|
481
229
|
}
|
|
482
230
|
|
|
483
|
-
/**
|
|
484
|
-
* The lockfile each supported package manager writes. The staging report names
|
|
485
|
-
* the detected one so the `git add` hint is copy-pasteable in the consumer's
|
|
486
|
-
* real tree rather than always saying `package-lock.json` (Story #5339).
|
|
487
|
-
*/
|
|
488
231
|
const LOCKFILE_BY_PACKAGE_MANAGER = {
|
|
489
232
|
pnpm: 'pnpm-lock.yaml',
|
|
490
233
|
yarn: 'yarn.lock',
|
|
@@ -492,23 +235,12 @@ const LOCKFILE_BY_PACKAGE_MANAGER = {
|
|
|
492
235
|
npm: 'package-lock.json',
|
|
493
236
|
};
|
|
494
237
|
|
|
495
|
-
/** The manifest the dependency bump rewrites alongside the lockfile. */
|
|
496
238
|
const MANIFEST_FILENAME = 'package.json';
|
|
497
239
|
|
|
498
|
-
/** The materialized payload directory the sync phase rewrites. */
|
|
499
240
|
const AGENTS_DIR = '.agents';
|
|
500
241
|
|
|
501
242
|
/**
|
|
502
|
-
*
|
|
503
|
-
* report from the **uncoerced** package-manager probe (`pnpm-lock.yaml` /
|
|
504
|
-
* `yarn.lock` / `bun.lockb` / `package-lock.json`). Reading
|
|
505
|
-
* `probePackageManager` rather than `detectPackageManager` is deliberate: the
|
|
506
|
-
* latter flattens `bun` to `npm` so the install-command builder has a command
|
|
507
|
-
* to emit, and inheriting that here would name a lockfile the consumer's tree
|
|
508
|
-
* does not carry (Story #5364). A directory with no recognizable toolchain
|
|
509
|
-
* still resolves to `npm`, so the report always names a concrete file.
|
|
510
|
-
*
|
|
511
|
-
* @param {string} cwd - Consumer project root.
|
|
243
|
+
* @param {string} cwd
|
|
512
244
|
* @param {typeof nodeFs} [fs]
|
|
513
245
|
* @returns {string}
|
|
514
246
|
*/
|
|
@@ -517,11 +249,9 @@ export function defaultDetectLockfile(cwd, fs = nodeFs) {
|
|
|
517
249
|
return LOCKFILE_BY_PACKAGE_MANAGER[packageManager] ?? 'package-lock.json';
|
|
518
250
|
}
|
|
519
251
|
|
|
520
|
-
/** The line printed when the probe cannot report the index at all. */
|
|
521
252
|
const NEUTRAL_STAGING_LINE =
|
|
522
253
|
'Review the working tree and commit the bump (git not available to report staging state).';
|
|
523
254
|
|
|
524
|
-
/** What the probe reports when it cannot read the index at all. */
|
|
525
255
|
const DEGRADED_GIT_STATE = Object.freeze({
|
|
526
256
|
ok: false,
|
|
527
257
|
stagedManifest: false,
|
|
@@ -530,24 +260,14 @@ const DEGRADED_GIT_STATE = Object.freeze({
|
|
|
530
260
|
tracksAgents: false,
|
|
531
261
|
});
|
|
532
262
|
|
|
533
|
-
/** True when a `spawnSync` result is a clean, non-throwing exit. */
|
|
534
263
|
const spawnOk = (result) =>
|
|
535
264
|
Boolean(result) && !result.error && result.status === 0;
|
|
536
265
|
|
|
537
266
|
/**
|
|
538
|
-
* The path
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
* from HEAD *and* the worktree agrees with the index. Porcelain v1 states that
|
|
543
|
-
* as `XY<space>path` — `X` the index column, `Y` the worktree column — so a
|
|
544
|
-
* path the operator staged before the install and the install then rewrote on
|
|
545
|
-
* disk reports `MM` and is correctly NOT staged. `?` in the index column is an
|
|
546
|
-
* untracked file, which is never staged.
|
|
547
|
-
*
|
|
548
|
-
* A rename carries `orig -> new`; the post-rename path is the one the operator
|
|
549
|
-
* stages. Porcelain quotes paths containing unusual bytes, so the quoting is
|
|
550
|
-
* stripped.
|
|
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.
|
|
551
271
|
*
|
|
552
272
|
* @param {string} record
|
|
553
273
|
* @returns {string | null}
|
|
@@ -566,17 +286,10 @@ function stagedPathFromPorcelain(record) {
|
|
|
566
286
|
}
|
|
567
287
|
|
|
568
288
|
/**
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
* `my.agents/` never matches, and it runs first: a `package.json` *inside*
|
|
574
|
-
* `.agents/` is payload, never the consumer's root manifest. The manifest and
|
|
575
|
-
* lockfile tests tolerate one leading prefix because git reports porcelain
|
|
576
|
-
* paths relative to the repository root, which need not be the probe root —
|
|
577
|
-
* safe only because the caller's pathspec already anchored the query there, so
|
|
578
|
-
* a staged `packages/app/package.json` never reaches this function at all
|
|
579
|
-
* (Story #5364).
|
|
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`.
|
|
580
293
|
*
|
|
581
294
|
* @param {string} path
|
|
582
295
|
* @param {string} lockfile
|
|
@@ -591,26 +304,9 @@ function stagedSlotFor(path, lockfile) {
|
|
|
591
304
|
}
|
|
592
305
|
|
|
593
306
|
/**
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
* mutation):
|
|
598
|
-
* - `git status --porcelain -- package.json <lockfile> .agents` — one read
|
|
599
|
-
* that answers every branch the report chooses between. The pathspecs are
|
|
600
|
-
* resolved relative to `cwd`, which anchors the query at the probe root:
|
|
601
|
-
* a nested workspace manifest is simply not in the output. Both status
|
|
602
|
-
* columns come back, so "staged" can mean index-differs-**and**-worktree-
|
|
603
|
-
* clean rather than the index alone.
|
|
604
|
-
* - `git ls-files .agents` — whether the consumer *tracks* the materialized
|
|
605
|
-
* payload, which decides whether the `git add` hint names it.
|
|
606
|
-
*
|
|
607
|
-
* Any failure — git absent, not a repository, a non-zero exit, a throw from the
|
|
608
|
-
* spawn boundary — degrades to `{ ok: false }` so the caller prints the neutral
|
|
609
|
-
* line and the update still exits 0. The probe never throws.
|
|
610
|
-
*
|
|
611
|
-
* Security (security-baseline § Output & Rendering): every argv segment here is
|
|
612
|
-
* a fixed vector or a lockfile name drawn from `LOCKFILE_BY_PACKAGE_MANAGER`,
|
|
613
|
-
* never operator input, and no shell is used.
|
|
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 }`.
|
|
614
310
|
*
|
|
615
311
|
* @param {{
|
|
616
312
|
* cwd?: string,
|
|
@@ -659,25 +355,10 @@ export function defaultGitStatus({
|
|
|
659
355
|
}
|
|
660
356
|
|
|
661
357
|
/**
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
*
|
|
665
|
-
* `
|
|
666
|
-
*
|
|
667
|
-
* - **staged** — both are staged, so "staged for review" is a fact.
|
|
668
|
-
* - **not staged** — anything else: the line says so and prints the exact
|
|
669
|
-
* `git add` command, naming `.agents/` too when the consumer tracks that
|
|
670
|
-
* tree (the sync re-materialized it, and an operator who stages only the
|
|
671
|
-
* manifest pair silently drops that diff).
|
|
672
|
-
*
|
|
673
|
-
* `scope: 'payload'` (the drift heal, which bumps no dependency) reports the
|
|
674
|
-
* re-materialized `.agents/` tree instead, and returns `''` when the consumer
|
|
675
|
-
* does not track it — there is then nothing to stage and nothing to say.
|
|
676
|
-
*
|
|
677
|
-
* Either scope degrades to a line that claims nothing about the index when the
|
|
678
|
-
* probe could not report.
|
|
679
|
-
*
|
|
680
|
-
* Pure: takes the probe result and the resolved lockfile name, touches no fs.
|
|
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.
|
|
681
362
|
*
|
|
682
363
|
* @param {{
|
|
683
364
|
* ok?: boolean,
|
|
@@ -685,11 +366,10 @@ export function defaultGitStatus({
|
|
|
685
366
|
* stagedLockfile?: boolean,
|
|
686
367
|
* stagedPayload?: boolean,
|
|
687
368
|
* tracksAgents?: boolean,
|
|
688
|
-
* }} gitState
|
|
689
|
-
* @param {string} lockfile
|
|
369
|
+
* }} gitState
|
|
370
|
+
* @param {string} lockfile
|
|
690
371
|
* @param {{ scope?: 'bump' | 'payload' }} [opts]
|
|
691
|
-
* @returns {string}
|
|
692
|
-
* when there is nothing to report.
|
|
372
|
+
* @returns {string}
|
|
693
373
|
*/
|
|
694
374
|
export function formatStagingReport(
|
|
695
375
|
gitState,
|
|
@@ -727,13 +407,8 @@ export function formatStagingReport(
|
|
|
727
407
|
}
|
|
728
408
|
|
|
729
409
|
/**
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
* probe that throws (git missing, a seam raising) must degrade to the neutral
|
|
733
|
-
* line, never turn a successful run into a crash.
|
|
734
|
-
*
|
|
735
|
-
* The lockfile is resolved first because the probe's pathspec names it — that
|
|
736
|
-
* is what anchors the query at the probe root.
|
|
410
|
+
* The staging line, degrading any seam failure to the neutral line so a
|
|
411
|
+
* courtesy report never crashes a completed update.
|
|
737
412
|
*
|
|
738
413
|
* @param {{
|
|
739
414
|
* gitStatus: (opts: { cwd: string, lockfile: string }) => object,
|
|
@@ -762,27 +437,12 @@ function resolveStagingReport({
|
|
|
762
437
|
}
|
|
763
438
|
|
|
764
439
|
/**
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
768
|
-
*
|
|
769
|
-
*
|
|
770
|
-
* The package manager is auto-detected from `cwd`'s lockfile (Story #3575) so
|
|
771
|
-
* the bump lands in the operator's real lockfile rather than running
|
|
772
|
-
* `npm install` against a pnpm/yarn-managed tree. The install routes through
|
|
773
|
-
* the shared `runInstallCommand` helper from `lib/install-cmd-parser.js`, which
|
|
774
|
-
* tokenizes the command and spawns with `shell: process.platform === 'win32'`
|
|
775
|
-
* so the Windows `.cmd` shim resolves under CVE-2024-27980 — the win32 shell
|
|
776
|
-
* handling and tokenization are reused, not re-implemented here. The resolved
|
|
777
|
-
* argv is a fixed vector; an `--install-cmd` override is tokenized and escaped
|
|
778
|
-
* per-arg by the parser even when the win32 shell flag is required.
|
|
779
|
-
*
|
|
780
|
-
* On any install failure the thrown error names the detected package manager's
|
|
781
|
-
* own `install` command so the operator can restore `node_modules` to a
|
|
782
|
-
* consistent state — `mandrel update` never silently leaves a half-mutated
|
|
783
|
-
* tree (Story #3575 AC-4).
|
|
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.
|
|
784
444
|
*
|
|
785
|
-
* @param {string} target
|
|
445
|
+
* @param {string} target
|
|
786
446
|
* @param {{
|
|
787
447
|
* installCmd?: string,
|
|
788
448
|
* runInstall?: typeof runInstallCommand,
|
|
@@ -822,30 +482,16 @@ export function defaultNpmUpdate(
|
|
|
822
482
|
}
|
|
823
483
|
|
|
824
484
|
/**
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
* npm `files` allowlist).
|
|
829
|
-
*
|
|
830
|
-
* Injectable via the `fetchChangelog` seam so tests can verify the fallback
|
|
831
|
-
* path without issuing real network calls.
|
|
832
|
-
*
|
|
833
|
-
* The tag shape follows the `mandrel-vX.Y.Z` namespace (namespaced at
|
|
834
|
-
* `mandrel-v1.44.0`; bare `vX.Y.Z` for earlier releases). This function
|
|
835
|
-
* tries the namespaced tag first, then the bare-tag form, so it covers both
|
|
836
|
-
* tag series without forcing callers to know the boundary.
|
|
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.
|
|
837
488
|
*
|
|
838
|
-
*
|
|
839
|
-
* from a constant base and a semver string — no user input, no shell
|
|
840
|
-
* interpolation. The GET is a read-only fetch with no credentials.
|
|
841
|
-
*
|
|
842
|
-
* @param {string} version - The target semver string (e.g. `"1.59.0"`).
|
|
489
|
+
* @param {string} version
|
|
843
490
|
* @param {{
|
|
844
491
|
* https?: typeof nodeHttps,
|
|
845
492
|
* }} [deps]
|
|
846
|
-
* @returns {Promise<string>}
|
|
847
|
-
* @throws {Error} When
|
|
848
|
-
* 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.
|
|
849
495
|
*/
|
|
850
496
|
export async function fetchChangelogFromGitHub(
|
|
851
497
|
version,
|
|
@@ -888,25 +534,10 @@ export async function fetchChangelogFromGitHub(
|
|
|
888
534
|
}
|
|
889
535
|
|
|
890
536
|
/**
|
|
891
|
-
*
|
|
892
|
-
*
|
|
893
|
-
* authored by release-please with `## [<version>](…)` section headers; this
|
|
894
|
-
* prints every section whose version is newer than `current` and no newer than
|
|
895
|
-
* `target`.
|
|
896
|
-
*
|
|
897
|
-
* Resolution order (Story #4035):
|
|
898
|
-
* 1. Read `docs/CHANGELOG.md` from the target version's install directory
|
|
899
|
-
* (`node_modules/mandrel/docs/CHANGELOG.md` — now in the published
|
|
900
|
-
* tarball since `package.json` lists `docs/CHANGELOG.md` in `files`).
|
|
901
|
-
* 2. When the packaged file is absent (older install), fetch it from GitHub
|
|
902
|
-
* via the injectable `fetchChangelog` seam.
|
|
903
|
-
* 3. When both sources fail, emit an actionable warning with a link to the
|
|
904
|
-
* 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.
|
|
905
539
|
*
|
|
906
|
-
*
|
|
907
|
-
* best-effort and must never fail an otherwise-successful upgrade.
|
|
908
|
-
*
|
|
909
|
-
* @param {string} target - The applied target version.
|
|
540
|
+
* @param {string} target
|
|
910
541
|
* @param {{
|
|
911
542
|
* current?: string,
|
|
912
543
|
* changelogPath?: string,
|
|
@@ -930,19 +561,16 @@ async function defaultSurfaceChangelog(
|
|
|
930
561
|
) {
|
|
931
562
|
let raw;
|
|
932
563
|
|
|
933
|
-
// 1. Try the packaged file (present in installs since Story #4035).
|
|
934
564
|
try {
|
|
935
565
|
raw = fs.readFileSync(changelogPath, 'utf8');
|
|
936
566
|
} catch {
|
|
937
|
-
//
|
|
567
|
+
// Absent: fall through to the GitHub fetch.
|
|
938
568
|
}
|
|
939
569
|
|
|
940
|
-
// 2. Packaged file absent: attempt a GitHub fetch for the target tag.
|
|
941
570
|
if (raw === undefined) {
|
|
942
571
|
try {
|
|
943
572
|
raw = await fetchChangelog(target);
|
|
944
573
|
} catch {
|
|
945
|
-
// Both sources unavailable — emit an actionable message and return.
|
|
946
574
|
writeErr(
|
|
947
575
|
`mandrel update: changelog not available for v${target} — ` +
|
|
948
576
|
`view the release notes at ${GITHUB_RELEASES_URL}\n`,
|
|
@@ -973,9 +601,8 @@ async function defaultSurfaceChangelog(
|
|
|
973
601
|
}
|
|
974
602
|
|
|
975
603
|
/**
|
|
976
|
-
* Split a release-please
|
|
977
|
-
*
|
|
978
|
-
* 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.
|
|
979
606
|
*
|
|
980
607
|
* @param {string} raw
|
|
981
608
|
* @returns {Array<{ version: string, body: string }>}
|
|
@@ -1008,32 +635,13 @@ function parseChangelogSections(raw) {
|
|
|
1008
635
|
}
|
|
1009
636
|
|
|
1010
637
|
/**
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1014
|
-
* `process.execPath` (node) rather than executed directly — see
|
|
1015
|
-
* {@link defaultSpawnPhase}.
|
|
1016
|
-
*
|
|
1017
|
-
* It deliberately does **not** return the `node_modules/.bin/mandrel` shim.
|
|
1018
|
-
* That shim only works because npm chmods the bin target `+x` at install time:
|
|
1019
|
-
* `bin/mandrel.js` ships non-executable in the published tarball, and pnpm
|
|
1020
|
-
* symlinks `.bin/mandrel` straight at it, so spawning the shim directly fails
|
|
1021
|
-
* with `EACCES` under pnpm (Story #4613). Spawning node against the resolved
|
|
1022
|
-
* `.js` script removes the dependency on the exec bit, the shebang, and the
|
|
1023
|
-
* Windows `.cmd` shim entirely.
|
|
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.
|
|
1024
641
|
*
|
|
1025
|
-
*
|
|
1026
|
-
* (
|
|
1027
|
-
*
|
|
1028
|
-
* to this CLI module. The `mandrel` package directory is version-invariant
|
|
1029
|
-
* (`node_modules/mandrel/`), so resolving it before the in-place `npm-update`
|
|
1030
|
-
* step still yields the directory whose `bin/mandrel.js` the install overwrites.
|
|
1031
|
-
*
|
|
1032
|
-
* @param {string} projectRoot - Absolute path to the consumer project.
|
|
1033
|
-
* @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts] - test
|
|
1034
|
-
* seam for the `node_modules` resolution; defaults to the real
|
|
1035
|
-
* `defaultResolvePackageRoot` from `sync.js`.
|
|
1036
|
-
* @returns {string} Absolute path to the new bin script.
|
|
642
|
+
* @param {string} projectRoot
|
|
643
|
+
* @param {{ resolvePackageRoot?: (fromDir: string) => string }} [opts]
|
|
644
|
+
* @returns {string}
|
|
1037
645
|
*/
|
|
1038
646
|
export function resolveNewBinScriptPath(
|
|
1039
647
|
projectRoot,
|
|
@@ -1044,33 +652,19 @@ export function resolveNewBinScriptPath(
|
|
|
1044
652
|
}
|
|
1045
653
|
|
|
1046
654
|
/**
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1050
|
-
* newly-installed module code (not the currently-loaded old module) executes.
|
|
1051
|
-
*
|
|
1052
|
-
* The child is spawned as `process.execPath <binScript> <phase> …` — node run
|
|
1053
|
-
* against the resolved `bin/mandrel.js` (see {@link resolveNewBinScriptPath}).
|
|
1054
|
-
* Spawning node against a plain `.js` file removes any dependency on the bin's
|
|
1055
|
-
* exec bit, its shebang, or a Windows `.cmd` shim, so **no** `shell` flag is
|
|
1056
|
-
* needed on any platform (this is the pnpm/layout-agnostic fix, Story #4613,
|
|
1057
|
-
* that retired the former win32-only `shell: true` branch). The argv vector is
|
|
1058
|
-
* a fixed constant list per phase — no operator-supplied data enters it
|
|
1059
|
-
* (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.
|
|
1060
658
|
*
|
|
1061
|
-
*
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
* @param {string} phase - The mandrel sub-command to run (e.g. `'sync'`).
|
|
1065
|
-
* @param {string[]} args - Additional arguments for the sub-command.
|
|
659
|
+
* @param {string} phase
|
|
660
|
+
* @param {string[]} args
|
|
1066
661
|
* @param {{
|
|
1067
662
|
* binPath: string,
|
|
1068
663
|
* cwd: string,
|
|
1069
664
|
* write: (s: string) => void,
|
|
1070
665
|
* writeErr: (s: string) => void,
|
|
1071
666
|
* spawnFn?: typeof spawnSync,
|
|
1072
|
-
* }} opts
|
|
1073
|
-
* `node_modules/.bin` shim); it becomes node's first argv entry.
|
|
667
|
+
* }} opts
|
|
1074
668
|
* @returns {{ ok: boolean, stdout: string, stderr: string }}
|
|
1075
669
|
*/
|
|
1076
670
|
export function defaultSpawnPhase(
|
|
@@ -1096,19 +690,7 @@ export function defaultSpawnPhase(
|
|
|
1096
690
|
return { ok, stdout, stderr };
|
|
1097
691
|
}
|
|
1098
692
|
|
|
1099
|
-
/**
|
|
1100
|
-
* The ordered step names the orchestrator drives on an update. Shared
|
|
1101
|
-
* by the live path and the `--dry-run` plan printout so the two never drift.
|
|
1102
|
-
*
|
|
1103
|
-
* Step ordering (Story #4046 A1c; sync-agents added by Story #4528/#4530):
|
|
1104
|
-
* 1. npm-update — install the new version
|
|
1105
|
-
* 2. runSync — re-materialize .agents/ from the new payload
|
|
1106
|
-
* 3. sync-commands — regenerate .claude/commands/ from the new payload
|
|
1107
|
-
* 4. sync-agents — regenerate .claude/agents/ from the new payload
|
|
1108
|
-
* 5. runMigrations — apply version-keyed migrations
|
|
1109
|
-
* 6. doctor — validate the post-upgrade state
|
|
1110
|
-
* 7. surface changelog — print the changelog (always last, best-effort)
|
|
1111
|
-
*/
|
|
693
|
+
/** The `--dry-run` printout of the full-upgrade step order. */
|
|
1112
694
|
const STEP_PLAN = [
|
|
1113
695
|
'npm-update',
|
|
1114
696
|
'runSync',
|
|
@@ -1120,19 +702,9 @@ const STEP_PLAN = [
|
|
|
1120
702
|
];
|
|
1121
703
|
|
|
1122
704
|
/**
|
|
1123
|
-
*
|
|
1124
|
-
* a
|
|
1125
|
-
*
|
|
1126
|
-
* - `kind: 'npm-update'` — bump the dependency via the `npmUpdate` seam.
|
|
1127
|
-
* - `kind: 'spawn'` — spawn `phase`/`args` from the new binary; a
|
|
1128
|
-
* non-zero exit is fatal and throws `failMessage`.
|
|
1129
|
-
* - `kind: 'doctor'` — spawn `doctor` from the new binary; a non-zero
|
|
1130
|
-
* exit is *soft* (maps to `action: 'doctor-failed'`
|
|
1131
|
-
* + exit 1), so it carries no `failMessage`.
|
|
1132
|
-
*
|
|
1133
|
-
* `label` is the name pushed into the run's `stepsRun[]` (the external return
|
|
1134
|
-
* contract). `migrate` is the only phase whose argv depends on the version
|
|
1135
|
-
* 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.
|
|
1136
708
|
*
|
|
1137
709
|
* @param {string} current
|
|
1138
710
|
* @param {string} target
|
|
@@ -1162,10 +734,7 @@ function fullUpgradeSteps(current, target) {
|
|
|
1162
734
|
'Run `npm run sync:commands` manually to restore.',
|
|
1163
735
|
},
|
|
1164
736
|
{
|
|
1165
|
-
//
|
|
1166
|
-
// role-agent tree at all — only the bootstrap path did. Added alongside
|
|
1167
|
-
// sync-commands so `.claude/agents/` materializes here too, which is
|
|
1168
|
-
// what makes the tightened `agents-in-sync` doctor check satisfiable.
|
|
737
|
+
// Required for the `agents-in-sync` doctor check to be satisfiable.
|
|
1169
738
|
kind: 'spawn',
|
|
1170
739
|
phase: 'sync-agents',
|
|
1171
740
|
args: [],
|
|
@@ -1190,10 +759,7 @@ function fullUpgradeSteps(current, target) {
|
|
|
1190
759
|
}
|
|
1191
760
|
|
|
1192
761
|
/**
|
|
1193
|
-
*
|
|
1194
|
-
* `.agents/` is stale). No npm-update, no migrations, no doctor — only the
|
|
1195
|
-
* three sync phases re-materialize the payload from the already-installed
|
|
1196
|
-
* binary.
|
|
762
|
+
* Drift-heal phases: the sync phases only, from the installed binary.
|
|
1197
763
|
*
|
|
1198
764
|
* @returns {Array<{ kind: 'spawn', phase: string, args: string[], label: string, failMessage: string }>}
|
|
1199
765
|
*/
|
|
@@ -1220,7 +786,6 @@ function driftHealSteps() {
|
|
|
1220
786
|
'Run `npm run sync:commands` manually to restore.',
|
|
1221
787
|
},
|
|
1222
788
|
{
|
|
1223
|
-
// Story #4528/#4530: see the matching entry in fullUpgradeSteps().
|
|
1224
789
|
kind: 'spawn',
|
|
1225
790
|
phase: 'sync-agents',
|
|
1226
791
|
args: [],
|
|
@@ -1234,25 +799,7 @@ function driftHealSteps() {
|
|
|
1234
799
|
}
|
|
1235
800
|
|
|
1236
801
|
/**
|
|
1237
|
-
* Pure
|
|
1238
|
-
* inputs and the two flags, decide which of the four actions to take and the
|
|
1239
|
-
* ordered phase plan for that action. **No I/O** — no filesystem, child
|
|
1240
|
-
* process, network, `write`, or `exit`. This isolates the scheduler-style
|
|
1241
|
-
* branch-selection and step-sequencing logic (the surface under review in
|
|
1242
|
-
* Story #4182 / audit::architecture) so it can be exercised as a table over
|
|
1243
|
-
* plain inputs rather than by running the whole async orchestration with every
|
|
1244
|
-
* seam stubbed.
|
|
1245
|
-
*
|
|
1246
|
-
* The four actions:
|
|
1247
|
-
*
|
|
1248
|
-
* - `up-to-date` — version is current and no drift. True no-op; `steps: []`.
|
|
1249
|
-
* - `dry-run` — `dryRun` is set. `steps: []` (nothing is executed); the
|
|
1250
|
-
* `variant` distinguishes the drift-heal preview from the
|
|
1251
|
-
* full-upgrade preview so the executor prints the right plan.
|
|
1252
|
-
* - `resynced` — version is current but drift detected. Heal via the two
|
|
1253
|
-
* sync phases (`driftHealSteps()`).
|
|
1254
|
-
* - `updated` — a newer version is available. Full upgrade
|
|
1255
|
-
* (`fullUpgradeSteps(current, target)`).
|
|
802
|
+
* Pure (no I/O) choice of action and phase plan.
|
|
1256
803
|
*
|
|
1257
804
|
* @param {{ current: string, target: string, dryRun: boolean, hasDrift: boolean }} input
|
|
1258
805
|
* @returns {{
|
|
@@ -1265,7 +812,6 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
|
|
|
1265
812
|
const versionCurrent = compareVersions(target, current) <= 0;
|
|
1266
813
|
|
|
1267
814
|
if (versionCurrent) {
|
|
1268
|
-
// Version is already newest. The only remaining question is drift.
|
|
1269
815
|
if (!hasDrift) {
|
|
1270
816
|
return { action: 'up-to-date', steps: [] };
|
|
1271
817
|
}
|
|
@@ -1275,8 +821,7 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
|
|
|
1275
821
|
return { action: 'resynced', steps: driftHealSteps() };
|
|
1276
822
|
}
|
|
1277
823
|
|
|
1278
|
-
//
|
|
1279
|
-
// post-upgrade doctor phase re-checks materialization).
|
|
824
|
+
// Drift is irrelevant here: the post-upgrade doctor re-checks it.
|
|
1280
825
|
if (dryRun) {
|
|
1281
826
|
return { action: 'dry-run', steps: [], variant: 'full-upgrade' };
|
|
1282
827
|
}
|
|
@@ -1284,14 +829,8 @@ export function planUpdate({ current, target, dryRun, hasDrift }) {
|
|
|
1284
829
|
}
|
|
1285
830
|
|
|
1286
831
|
/**
|
|
1287
|
-
*
|
|
1288
|
-
*
|
|
1289
|
-
* following token group) and the `=` form (`--install-cmd="<cmd>"`). Returns
|
|
1290
|
-
* `undefined` when the flag is absent so the default package manager is used.
|
|
1291
|
-
*
|
|
1292
|
-
* The argv tokenizer hands us a pre-split array; with the space form the shell
|
|
1293
|
-
* has already collapsed a quoted value into one element, so the immediate next
|
|
1294
|
-
* 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.
|
|
1295
834
|
*
|
|
1296
835
|
* @param {string[]} argv
|
|
1297
836
|
* @returns {string | undefined}
|
|
@@ -1310,12 +849,6 @@ function parseInstallCmdFlag(argv) {
|
|
|
1310
849
|
}
|
|
1311
850
|
|
|
1312
851
|
/**
|
|
1313
|
-
* Resolve the drift signal for the no-op short-circuit. Prefers the injected
|
|
1314
|
-
* `checkDrift` seam (unit-test friendly); falls back to the production
|
|
1315
|
-
* `runAgentsDrift` helper. Only consulted when the installed version is already
|
|
1316
|
-
* the newest (Story #4065) — a real version bump skips drift entirely (the
|
|
1317
|
-
* post-upgrade doctor phase re-checks materialization).
|
|
1318
|
-
*
|
|
1319
852
|
* @param {(() => boolean | Promise<boolean>) | undefined} checkDrift
|
|
1320
853
|
* @returns {Promise<boolean>}
|
|
1321
854
|
*/
|
|
@@ -1326,12 +859,8 @@ async function resolveDrift(checkDrift) {
|
|
|
1326
859
|
}
|
|
1327
860
|
|
|
1328
861
|
/**
|
|
1329
|
-
*
|
|
1330
|
-
*
|
|
1331
|
-
* `npmUpdate` seam call, the `spawnPhase` re-exec boundary, the per-step
|
|
1332
|
-
* `stepsRun` accounting, and the soft doctor-fail (`exit(1)` +
|
|
1333
|
-
* `action: 'doctor-failed'`). The branch-selection logic that produced `steps`
|
|
1334
|
-
* lives in the pure `planUpdate`.
|
|
862
|
+
* Drive `planUpdate`'s steps. A doctor failure still surfaces the changelog,
|
|
863
|
+
* then exits 1.
|
|
1335
864
|
*
|
|
1336
865
|
* @param {{
|
|
1337
866
|
* steps: Array<{ kind: 'npm-update' | 'spawn' | 'doctor', label: string, phase?: string, args?: string[], failMessage?: string }>,
|
|
@@ -1364,11 +893,8 @@ async function executePlan({
|
|
|
1364
893
|
const stepsRun = [];
|
|
1365
894
|
let doctorOk = true;
|
|
1366
895
|
|
|
1367
|
-
//
|
|
1368
|
-
//
|
|
1369
|
-
// seam surfaces its own clear error first, and (b) resolution reflects the
|
|
1370
|
-
// just-installed package. The `mandrel` package directory is version-stable,
|
|
1371
|
-
// 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.
|
|
1372
898
|
let binPath;
|
|
1373
899
|
const binScript = () => {
|
|
1374
900
|
if (binPath === undefined) binPath = resolveBinScript(projectRoot);
|
|
@@ -1377,8 +903,6 @@ async function executePlan({
|
|
|
1377
903
|
|
|
1378
904
|
for (const step of steps) {
|
|
1379
905
|
if (step.kind === 'npm-update') {
|
|
1380
|
-
// Bump the dependency. The lockfile change is left STAGED on disk; this
|
|
1381
|
-
// module never commits.
|
|
1382
906
|
if (typeof npmUpdate !== 'function') {
|
|
1383
907
|
throw new Error(
|
|
1384
908
|
'mandrel update: npmUpdate seam is required to bump the dependency',
|
|
@@ -1390,9 +914,6 @@ async function executePlan({
|
|
|
1390
914
|
continue;
|
|
1391
915
|
}
|
|
1392
916
|
|
|
1393
|
-
// Both 'spawn' and 'doctor' kinds run a post-install phase from the
|
|
1394
|
-
// newly-installed binary (the Story #4034 re-exec boundary), so the new
|
|
1395
|
-
// package's module code — not the old loaded module — executes.
|
|
1396
917
|
// eslint-disable-next-line no-await-in-loop
|
|
1397
918
|
const result = await spawnPhase(step.phase, step.args, {
|
|
1398
919
|
binPath: binScript(),
|
|
@@ -1403,18 +924,12 @@ async function executePlan({
|
|
|
1403
924
|
stepsRun.push(step.label);
|
|
1404
925
|
|
|
1405
926
|
if (step.kind === 'doctor') {
|
|
1406
|
-
// Doctor failure is SOFT: record it, keep going to surface the changelog,
|
|
1407
|
-
// then map to exit(1) + doctor-failed by the caller.
|
|
1408
927
|
doctorOk = result.ok;
|
|
1409
928
|
} else if (!result.ok) {
|
|
1410
|
-
// sync / sync-commands / migrate failures are FATAL.
|
|
1411
929
|
throw new Error(step.failMessage);
|
|
1412
930
|
}
|
|
1413
931
|
}
|
|
1414
932
|
|
|
1415
|
-
// Surface the target changelog (best-effort; optional seam). Runs even when
|
|
1416
|
-
// doctor failed, so the operator still sees the changelog for the version
|
|
1417
|
-
// that landed on disk.
|
|
1418
933
|
if (typeof surfaceChangelog === 'function') {
|
|
1419
934
|
await surfaceChangelog(target);
|
|
1420
935
|
}
|
|
@@ -1431,14 +946,6 @@ async function executePlan({
|
|
|
1431
946
|
}
|
|
1432
947
|
|
|
1433
948
|
/**
|
|
1434
|
-
* Run the `mandrel update` orchestration cycle.
|
|
1435
|
-
*
|
|
1436
|
-
* The cycle is split into a pure decision (`planUpdate`) and a side-effecting
|
|
1437
|
-
* shell (this function + `executePlan`). `runUpdate` resolves the inputs
|
|
1438
|
-
* (current / target / drift) through the injectable seams, calls `planUpdate`
|
|
1439
|
-
* to select the action and its ordered phase plan, then drives the plan through
|
|
1440
|
-
* the `spawnPhase` / `write` / `exit` shell.
|
|
1441
|
-
*
|
|
1442
949
|
* @param {{
|
|
1443
950
|
* argv?: string[],
|
|
1444
951
|
* currentVersion?: string | (() => string),
|
|
@@ -1495,9 +1002,7 @@ export async function runUpdate({
|
|
|
1495
1002
|
}
|
|
1496
1003
|
const target = String(await resolveTargetVersion());
|
|
1497
1004
|
|
|
1498
|
-
//
|
|
1499
|
-
// it solely on that branch so a real version bump never calls the drift seam
|
|
1500
|
-
// (Story #4065).
|
|
1005
|
+
// Probe drift only when already newest; a real bump never calls the seam.
|
|
1501
1006
|
const hasDrift =
|
|
1502
1007
|
compareVersions(target, current) <= 0
|
|
1503
1008
|
? await resolveDrift(checkDrift)
|
|
@@ -1505,7 +1010,6 @@ export async function runUpdate({
|
|
|
1505
1010
|
|
|
1506
1011
|
const plan = planUpdate({ current, target, dryRun, hasDrift });
|
|
1507
1012
|
|
|
1508
|
-
// --- up-to-date: true no-op ----------------------------------------------
|
|
1509
1013
|
if (plan.action === 'up-to-date') {
|
|
1510
1014
|
write(`✅ Already up to date (v${current} is the newest version).\n`);
|
|
1511
1015
|
return {
|
|
@@ -1518,7 +1022,6 @@ export async function runUpdate({
|
|
|
1518
1022
|
};
|
|
1519
1023
|
}
|
|
1520
1024
|
|
|
1521
|
-
// --- dry-run: print the plan, execute nothing -----------------------------
|
|
1522
1025
|
if (plan.action === 'dry-run') {
|
|
1523
1026
|
if (plan.variant === 'drift-heal') {
|
|
1524
1027
|
write(
|
|
@@ -1551,7 +1054,6 @@ export async function runUpdate({
|
|
|
1551
1054
|
};
|
|
1552
1055
|
}
|
|
1553
1056
|
|
|
1554
|
-
// --- resynced / updated: execute the phase plan ---------------------------
|
|
1555
1057
|
const projectRoot = cwd();
|
|
1556
1058
|
|
|
1557
1059
|
if (plan.action === 'resynced') {
|
|
@@ -1577,10 +1079,7 @@ export async function runUpdate({
|
|
|
1577
1079
|
});
|
|
1578
1080
|
|
|
1579
1081
|
if (plan.action === 'resynced') {
|
|
1580
|
-
//
|
|
1581
|
-
// so the payload IS the diff. A consumer that tracks that tree used to be
|
|
1582
|
-
// told nothing at all about it (Story #5364), the same silent-diff hazard
|
|
1583
|
-
// the staging report exists to close, one branch earlier.
|
|
1082
|
+
// No dependency is bumped here, so the re-materialized payload IS the diff.
|
|
1584
1083
|
const payloadLine = resolveStagingReport({
|
|
1585
1084
|
gitStatus,
|
|
1586
1085
|
detectLockfile,
|
|
@@ -1601,7 +1100,6 @@ export async function runUpdate({
|
|
|
1601
1100
|
};
|
|
1602
1101
|
}
|
|
1603
1102
|
|
|
1604
|
-
// plan.action === 'updated'
|
|
1605
1103
|
if (!doctorOk) {
|
|
1606
1104
|
return {
|
|
1607
1105
|
ok: false,
|
|
@@ -1613,9 +1111,6 @@ export async function runUpdate({
|
|
|
1613
1111
|
};
|
|
1614
1112
|
}
|
|
1615
1113
|
|
|
1616
|
-
// Report the real index state rather than asserting one (Story #5339). Both
|
|
1617
|
-
// seams are read-only and failure-tolerant: a probe that cannot answer yields
|
|
1618
|
-
// the neutral line, and the update still reports success with a zero exit.
|
|
1619
1114
|
write(
|
|
1620
1115
|
`✅ Updated to v${target}. ${resolveStagingReport({ gitStatus, detectLockfile, projectRoot })}\n`,
|
|
1621
1116
|
);
|
|
@@ -1630,46 +1125,10 @@ export async function runUpdate({
|
|
|
1630
1125
|
}
|
|
1631
1126
|
|
|
1632
1127
|
/**
|
|
1633
|
-
*
|
|
1634
|
-
*
|
|
1635
|
-
* Wires the production-default seams that `runUpdate` leaves injectable:
|
|
1636
|
-
* - `resolveTargetVersion` always probes the registry via `isStale` with
|
|
1637
|
-
* `bypassCache: true` — the 24h cache is overridden for explicit update
|
|
1638
|
-
* calls so the resolved version is always fresh (Story #4046 A1b). The
|
|
1639
|
-
* cache is still written so the `version-current` doctor advisory reads
|
|
1640
|
-
* a current baseline after the upgrade.
|
|
1641
|
-
* - `npmUpdate` runs the install command — auto-detected from the project
|
|
1642
|
-
* lockfile (`pnpm`/`yarn`/`npm`), or the `--install-cmd` override —
|
|
1643
|
-
* through the shared `runInstallCommand` helper — no git mutation; the
|
|
1644
|
-
* resulting index state is reported by the staging probe, not asserted.
|
|
1645
|
-
* - `spawnPhase` is wired to `defaultSpawnPhase`, which spawns each
|
|
1646
|
-
* post-install phase (sync, sync-commands, migrate, doctor) as
|
|
1647
|
-
* `node <packageRoot>/bin/mandrel.js …` (Story #4613 — the resolved bin
|
|
1648
|
-
* script, not the `node_modules/.bin` shim). This is the
|
|
1649
|
-
* Story #4034 fix: the new bin loads the new package's module code and
|
|
1650
|
-
* resolves paths against the new install dir, so these phases can never
|
|
1651
|
-
* observe the old payload.
|
|
1652
|
-
* - `surfaceChangelog` prints the relevant `docs/CHANGELOG.md` section(s)
|
|
1653
|
-
* for the applied range. Reads from the packaged file first; falls back to
|
|
1654
|
-
* a GitHub raw-content fetch via the injectable `fetchChangelog` seam when
|
|
1655
|
-
* the packaged file is absent; emits an actionable link to the GitHub
|
|
1656
|
-
* Releases page when both sources fail (Story #4035).
|
|
1657
|
-
*
|
|
1658
|
-
* Every seam stays injectable on `runUpdate`; these are merely the
|
|
1659
|
-
* no-seam-provided fallbacks, so the existing seam-driven tests stay green.
|
|
1660
|
-
* `--dry-run` / `--install-cmd` are parsed from `argv` by
|
|
1661
|
-
* `runUpdate` itself.
|
|
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.
|
|
1662
1130
|
*
|
|
1663
|
-
*
|
|
1664
|
-
* defaults shell out across (`versionRunner` = `npm view`, `runInstall` =
|
|
1665
|
-
* the install spawn, `spawnFn` = the phase-spawn boundary) plus `fs` /
|
|
1666
|
-
* `cachePath` / `now`, so the entrypoint can be driven end-to-end with the
|
|
1667
|
-
* network/npm boundary stubbed and no real I/O.
|
|
1668
|
-
* `bin/mandrel.js` calls `run(argv)` with no `deps`, getting the production
|
|
1669
|
-
* wiring; tests pass fakes. The `deps` surface is NOT part of the public
|
|
1670
|
-
* subcommand contract — `bin/mandrel.js` only ever supplies `argv`.
|
|
1671
|
-
*
|
|
1672
|
-
* @param {string[]} argv - Subcommand arguments (after `mandrel update`).
|
|
1131
|
+
* @param {string[]} argv
|
|
1673
1132
|
* @param {{
|
|
1674
1133
|
* currentVersion?: string,
|
|
1675
1134
|
* cachePath?: string,
|
|
@@ -1717,18 +1176,9 @@ export default async function run(argv = [], deps = {}) {
|
|
|
1717
1176
|
|
|
1718
1177
|
const cwdFn = typeof cwd === 'function' ? cwd : () => process.cwd();
|
|
1719
1178
|
|
|
1720
|
-
// Story #4525/#4530: prefer the consumer's declared dependency pin over
|
|
1721
|
-
// the pre-#4525 self-referential read — see resolveCurrentVersionForUpdate.
|
|
1722
1179
|
const current =
|
|
1723
1180
|
deps.currentVersion ?? resolveCurrentVersionForUpdate(cwdFn(), fs);
|
|
1724
1181
|
|
|
1725
|
-
// The production spawnPhase: spawn each post-install phase as
|
|
1726
|
-
// `node <packageRoot>/bin/mandrel.js …` (the newly-installed bin script,
|
|
1727
|
-
// resolved layout-agnostically per Story #4613 — not the node_modules/.bin
|
|
1728
|
-
// shim). This is the sole post-install execution path (No-Shim — Story #4182
|
|
1729
|
-
// retired the in-process runSync/runMigrations/runDoctor seam set). spawnFn
|
|
1730
|
-
// is injectable so tests can stub the spawn boundary without running a real
|
|
1731
|
-
// child process.
|
|
1732
1182
|
const productionSpawnPhase = (phase, args, opts) =>
|
|
1733
1183
|
defaultSpawnPhase(phase, args, {
|
|
1734
1184
|
...opts,
|
|
@@ -1738,8 +1188,7 @@ export default async function run(argv = [], deps = {}) {
|
|
|
1738
1188
|
await runUpdateImpl({
|
|
1739
1189
|
argv,
|
|
1740
1190
|
currentVersion: current,
|
|
1741
|
-
//
|
|
1742
|
-
// resolved target is fresh from the registry (Story #4046 A1b).
|
|
1191
|
+
// An explicit update always bypasses the 24h cache.
|
|
1743
1192
|
resolveTargetVersion: () =>
|
|
1744
1193
|
defaultResolveTargetVersion({
|
|
1745
1194
|
cachePath:
|
|
@@ -1757,9 +1206,6 @@ export default async function run(argv = [], deps = {}) {
|
|
|
1757
1206
|
fs,
|
|
1758
1207
|
}),
|
|
1759
1208
|
...(checkDrift ? { checkDrift } : {}),
|
|
1760
|
-
// Story #5339 — the staging probe. Production leaves both undefined so
|
|
1761
|
-
// runUpdate applies the real read-only git seam and the lockfile detection
|
|
1762
|
-
// that mirrors the install seam's; tests stub them for a fake tree.
|
|
1763
1209
|
...(gitStatus ? { gitStatus } : {}),
|
|
1764
1210
|
...(detectLockfile
|
|
1765
1211
|
? { detectLockfile }
|
|
@@ -1778,8 +1224,6 @@ export default async function run(argv = [], deps = {}) {
|
|
|
1778
1224
|
writeErr,
|
|
1779
1225
|
exit,
|
|
1780
1226
|
cwd: cwdFn,
|
|
1781
|
-
// Pass through undefined in production so runUpdate applies its default
|
|
1782
|
-
// resolver (resolveNewBinScriptPath); tests inject a stub for a fake root.
|
|
1783
1227
|
resolveBinScript,
|
|
1784
1228
|
});
|
|
1785
1229
|
}
|