mandrel 1.58.0 → 1.60.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 +100 -98
- package/.agents/docs/SDLC.md +140 -141
- package/.agents/docs/configuration.md +16 -16
- package/.agents/docs/workflows.md +7 -8
- package/.agents/instructions.md +12 -11
- package/.agents/personas/architect.md +1 -1
- package/.agents/personas/product.md +1 -1
- package/.agents/personas/project-manager.md +14 -14
- package/.agents/personas/technical-writer.md +1 -1
- package/.agents/rules/changelog-style.md +5 -5
- package/.agents/rules/git-conventions.md +3 -3
- package/.agents/schemas/agentrc.schema.json +3 -3
- package/.agents/schemas/audit-rules.json +20 -0
- package/.agents/schemas/dispatch-manifest.json +4 -4
- package/.agents/schemas/epic-spec.schema.json +15 -45
- package/.agents/schemas/lifecycle/README.md +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/README.md +1 -1
- package/.agents/scripts/acceptance-eval.js +21 -4
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/assert-branch.js +1 -3
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/bootstrap.js +1 -1
- package/.agents/scripts/check-arch-cycles.js +360 -0
- package/.agents/scripts/check-doc-links.js +2 -3
- package/.agents/scripts/coverage-capture.js +24 -3
- package/.agents/scripts/diagnose-friction.js +1 -1
- package/.agents/scripts/dispatcher.js +2 -2
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/epic-audit-prepare.js +3 -3
- package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
- package/.agents/scripts/epic-deliver-preflight.js +11 -9
- package/.agents/scripts/epic-deliver-prepare.js +13 -5
- package/.agents/scripts/epic-execute-record-wave.js +5 -5
- package/.agents/scripts/epic-plan-healthcheck.js +6 -10
- package/.agents/scripts/epic-plan-spec-validate.js +1 -1
- package/.agents/scripts/epic-reconcile.js +11 -29
- package/.agents/scripts/evidence-gate.js +2 -2
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/git-rebase-and-resolve.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +40 -24
- package/.agents/scripts/lib/ITicketingProvider.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
- package/.agents/scripts/lib/baseline-snapshot.js +7 -7
- package/.agents/scripts/lib/baselines/kinds/coverage.js +33 -149
- package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
- package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
- package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
- package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
- package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
- package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
- package/.agents/scripts/lib/baselines/writer.js +1 -1
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +1 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/close-validation/commands.js +188 -0
- package/.agents/scripts/lib/close-validation/gates.js +235 -0
- package/.agents/scripts/lib/close-validation/process.js +101 -0
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
- package/.agents/scripts/lib/close-validation/runner.js +325 -0
- package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/quality.js +6 -6
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-resolver.js +2 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
- package/.agents/scripts/lib/config-settings-schema.js +3 -3
- package/.agents/scripts/lib/coverage-capture.js +147 -4
- package/.agents/scripts/lib/cpu-pool.js +14 -0
- package/.agents/scripts/lib/crap-utils.js +6 -11
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +10 -10
- package/.agents/scripts/lib/git-utils.js +24 -22
- package/.agents/scripts/lib/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +3 -8
- package/.agents/scripts/lib/maintainability-engine.js +1 -1
- package/.agents/scripts/lib/maintainability-utils.js +4 -187
- package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +81 -7
- package/.agents/scripts/lib/orchestration/code-review.js +95 -82
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
- package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +14 -37
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +22 -22
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +206 -58
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +27 -3
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +28 -8
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +13 -41
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +2 -3
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -8
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +10 -10
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +8 -20
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
- package/.agents/scripts/lib/orchestration/file-assumptions.js +6 -5
- package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +144 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +8 -8
- package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
- package/.agents/scripts/lib/orchestration/preflight-cache.js +36 -13
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
- package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
- package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +5 -60
- package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
- package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +451 -503
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +32 -5
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
- package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
- package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
- package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +4 -35
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
- package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +44 -73
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +16 -7
- package/.agents/scripts/lib/orchestration/ticketing/state.js +53 -439
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +2 -8
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +1 -1
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
- package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
- package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
- package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
- package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
- package/.agents/scripts/lib/project-root.js +17 -0
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/spec/index.js +1 -1
- package/.agents/scripts/lib/spec/loader.js +2 -2
- package/.agents/scripts/lib/spec/state.js +7 -16
- package/.agents/scripts/lib/story-adjacency.js +76 -0
- package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
- package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
- package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
- package/.agents/scripts/lib/story-lifecycle.js +9 -9
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/transpile.js +93 -0
- package/.agents/scripts/lib/wave-runner/tick.js +4 -153
- package/.agents/scripts/lib/workers/crap-worker.js +1 -1
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
- package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
- package/.agents/scripts/lifecycle-emit.js +1 -1
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/mappers.js +2 -2
- package/.agents/scripts/providers/github/tickets.js +114 -10
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +10 -1
- package/.agents/scripts/run-tests.js +24 -4
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +13 -10
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +162 -26
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +2 -2
- package/.agents/scripts/validate-docs-freshness.js +1 -1
- package/.agents/scripts/wave-tick.js +1 -1
- package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
- package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
- package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
- package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
- package/.agents/skills/core/scope-triage/SKILL.md +9 -10
- package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
- package/.agents/skills/skills.index.json +7 -7
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +2 -2
- package/.agents/workflows/audit-architecture.md +2 -2
- package/.agents/workflows/audit-clean-code.md +2 -2
- package/.agents/workflows/audit-dependencies.md +1 -1
- package/.agents/workflows/audit-devops.md +1 -1
- package/.agents/workflows/audit-documentation.md +226 -0
- package/.agents/workflows/audit-lighthouse.md +1 -1
- package/.agents/workflows/audit-performance.md +2 -2
- package/.agents/workflows/audit-privacy.md +1 -1
- package/.agents/workflows/audit-quality.md +2 -2
- package/.agents/workflows/audit-security.md +2 -2
- package/.agents/workflows/audit-seo.md +1 -1
- package/.agents/workflows/audit-sre.md +1 -1
- package/.agents/workflows/audit-to-stories.md +10 -10
- package/.agents/workflows/audit-ux-ui.md +1 -1
- package/.agents/workflows/deliver.md +85 -0
- package/.agents/workflows/explain.md +3 -3
- package/.agents/workflows/git-merge-pr.md +1 -1
- package/.agents/workflows/git-pr-all.md +13 -10
- package/.agents/workflows/git-push.md +6 -3
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +59 -66
- package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
- package/.agents/workflows/helpers/diagnose.md +1 -1
- package/.agents/workflows/helpers/epic-audit.md +6 -6
- package/.agents/workflows/helpers/epic-deliver-story.md +28 -39
- package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
- package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
- package/.agents/workflows/helpers/epic-testing.md +3 -3
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
- package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
- package/.agents/workflows/helpers/signals.md +1 -1
- package/.agents/workflows/helpers/single-story-deliver.md +12 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/onboard.md +21 -20
- package/.agents/workflows/plan.md +89 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +17 -20
- package/docs/CHANGELOG.md +1149 -0
- package/lib/cli/__tests__/update-changelog-surface.test.js +357 -0
- package/lib/cli/__tests__/update-reexec.test.js +513 -0
- package/lib/cli/init.js +338 -0
- package/lib/cli/update.js +413 -52
- package/package.json +3 -1
- package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
- package/.agents/scripts/lib/close-validation.js +0 -897
- package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
- package/.agents/scripts/lib/task-utils.js +0 -26
- package/.agents/scripts/story-deliver-prepare.js +0 -267
package/lib/cli/init.js
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
// lib/cli/init.js
|
|
2
|
+
/**
|
|
3
|
+
* `mandrel init` subcommand — one-command cold start (Story #3975).
|
|
4
|
+
*
|
|
5
|
+
* Folds the former `create-mandrel` launcher logic into a subcommand of the
|
|
6
|
+
* single published `mandrel` package. `npx mandrel init` (≡ `npm exec --
|
|
7
|
+
* mandrel init`, or `mandrel init` once installed) takes a project from a
|
|
8
|
+
* blank folder to a configured Mandrel environment in one command:
|
|
9
|
+
*
|
|
10
|
+
* 1. **Install-if-absent.** When `./.agents/` is missing in the cwd, install
|
|
11
|
+
* the framework deterministically — `npm install mandrel --ignore-scripts`
|
|
12
|
+
* followed by an explicit sync run against the freshly installed bin
|
|
13
|
+
* (`node ./node_modules/mandrel/bin/mandrel.js sync`, NOT a bare `mandrel`
|
|
14
|
+
* on `PATH` — see `SYNC_BIN` below). The `--ignore-scripts` install
|
|
15
|
+
* skips the package's best-effort `postinstall` sync so the explicit
|
|
16
|
+
* `sync` is the single, deterministic materialization (review rec D.3 —
|
|
17
|
+
* no postinstall-then-init double-sync, and no arbitrary install
|
|
18
|
+
* lifecycle scripts during cold start). When `./.agents/` already exists
|
|
19
|
+
* (the operator ran `npm install mandrel` first), step 1 is skipped and
|
|
20
|
+
* `init` goes straight to the prompt — the one subcommand is idempotent
|
|
21
|
+
* across both the cold-start and post-install entry points.
|
|
22
|
+
*
|
|
23
|
+
* 2. **Yes/no prompt.** Ask whether to begin the interactive setup now
|
|
24
|
+
* (yes → run `node .agents/scripts/bootstrap.js`, forwarding every
|
|
25
|
+
* passthrough flag unchanged) or stop at "just the files" (no → print a
|
|
26
|
+
* re-run hint and exit 0). Yes is the default, so a bare Enter configures
|
|
27
|
+
* (mirrors the `[Y/n]` convention in bootstrap.js). `--assume-yes` skips
|
|
28
|
+
* the prompt and configures (the flag is also forwarded to bootstrap for
|
|
29
|
+
* its own non-interactive run). A non-TTY stdin without `--assume-yes`
|
|
30
|
+
* defaults to no (files-only) so the side-effecting GitHub provisioning
|
|
31
|
+
* never runs unattended.
|
|
32
|
+
*
|
|
33
|
+
* ## Cold-start provenance
|
|
34
|
+
*
|
|
35
|
+
* The installed package name is the **hardcoded build-time constant**
|
|
36
|
+
* `PACKAGE_NAME` below — it is NEVER read from argv or the environment. A
|
|
37
|
+
* cold start fetches `mandrel` from npx's temp cache and runs this bin; the
|
|
38
|
+
* package it then installs into the project must be the same `mandrel`, not an
|
|
39
|
+
* attacker-influenced name supplied on the command line. The forwarded
|
|
40
|
+
* passthrough flags reach `bootstrap.js` (the sole bootstrap orchestrator),
|
|
41
|
+
* which owns its own summary + confirm loop and validates its own inputs.
|
|
42
|
+
*
|
|
43
|
+
* ## Injectable seams (used by tests/cli/init.test.js)
|
|
44
|
+
*
|
|
45
|
+
* The plan/decision logic is a pure function (`planInit`) over injected
|
|
46
|
+
* boundaries so the suite is hermetic — no real TTY, npm, or network:
|
|
47
|
+
*
|
|
48
|
+
* - `argv` — subcommand args (after `mandrel init`)
|
|
49
|
+
* - `exists` — `(path) => boolean`; defaults to an `fs.existsSync` probe
|
|
50
|
+
* for `./.agents/` in the cwd
|
|
51
|
+
* - `runStep` — `(cmd, args) => { status }`; runs one install/sync/bootstrap
|
|
52
|
+
* step. Defaults to a `spawnSync` runner with `stdio: inherit`.
|
|
53
|
+
* - `confirm` — `() => boolean`; reads the operator's yes/no answer (true =
|
|
54
|
+
* configure now). Defaults to a synchronous stdin readline
|
|
55
|
+
* prompt with yes as the default.
|
|
56
|
+
* - `stdout` — `(s) => void`; defaults to `process.stdout.write`.
|
|
57
|
+
* - `isTTY` — boolean; defaults to `process.stdin.isTTY`.
|
|
58
|
+
* - `exit` — `(code) => void`; defaults to `process.exit`.
|
|
59
|
+
*
|
|
60
|
+
* Security (security-baseline § 5/6): logs only flag/step descriptions and
|
|
61
|
+
* never reads or echoes tokens, credentials, or env values. The package name
|
|
62
|
+
* is a hardcoded constant rather than interpolated input, and the step runner
|
|
63
|
+
* passes argv as an array (no shell-string concatenation of user input).
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
import { spawnSync } from 'node:child_process';
|
|
67
|
+
import fs from 'node:fs';
|
|
68
|
+
import path from 'node:path';
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Hardcoded build-time package name. NEVER read from argv or env — cold-start
|
|
72
|
+
* provenance requires the install target to be the same package this bin
|
|
73
|
+
* shipped from. See the module header.
|
|
74
|
+
*/
|
|
75
|
+
const PACKAGE_NAME = 'mandrel';
|
|
76
|
+
|
|
77
|
+
// The `mandrel sync` and `node .agents/scripts/bootstrap.js` invocations both
|
|
78
|
+
// resolve relative to the consumer's cwd at run time (the materialized
|
|
79
|
+
// `.agents/` lives there), so they are expressed as cwd-relative steps rather
|
|
80
|
+
// than against PROJECT_ROOT (which, under npx, is npx's throwaway cache).
|
|
81
|
+
const BOOTSTRAP_SCRIPT = path.join('.agents', 'scripts', 'bootstrap.js');
|
|
82
|
+
|
|
83
|
+
// The `sync` step is dispatched against the **locally installed** Mandrel bin
|
|
84
|
+
// rather than a bare `mandrel` on `PATH`. A bare `spawnSync('mandrel', …)` only
|
|
85
|
+
// resolves while npx keeps its throwaway `.bin` on `PATH`; reached any other
|
|
86
|
+
// way (a documented `npm install mandrel` then `mandrel init`, or a plain
|
|
87
|
+
// `node bin/mandrel.js init`), the freshly installed binary lives at
|
|
88
|
+
// `./node_modules/mandrel/bin/mandrel.js` — off `PATH` — and a bare spawn dies
|
|
89
|
+
// with ENOENT, leaving `.agents/` un-materialized. Spawning `process.execPath`
|
|
90
|
+
// against the package entrypoint resolves identically under npx, under a
|
|
91
|
+
// post-install invocation, and in tests — and, by going through `node`, also
|
|
92
|
+
// sidesteps the win32 `.cmd`-shim concern entirely (no `shell: true` needed for
|
|
93
|
+
// this step). The path is cwd-relative because the package is installed into
|
|
94
|
+
// the consumer's cwd (the same reason BOOTSTRAP_SCRIPT is cwd-relative).
|
|
95
|
+
const SYNC_BIN = path.join('node_modules', PACKAGE_NAME, 'bin', 'mandrel.js');
|
|
96
|
+
|
|
97
|
+
const PROMPT_TEXT =
|
|
98
|
+
'The Mandrel .agents package has been copied to your directory.\n' +
|
|
99
|
+
'Would you like to begin the interactive process to setup your local and ' +
|
|
100
|
+
'github environments now? [Y/n]: ';
|
|
101
|
+
|
|
102
|
+
const FILES_ONLY_HINT = 'Configure any time with: npx mandrel init\n';
|
|
103
|
+
|
|
104
|
+
// On win32, `npm` resolves to a `.cmd` shim that Node refuses to spawn without
|
|
105
|
+
// a shell after the CVE-2024-27980 hardening; mirror update.js and set
|
|
106
|
+
// `shell: true` only there. Off win32, never shell out — array argv stays
|
|
107
|
+
// injection-proof. (The `sync` step no longer hits a `.cmd` shim at all: it is
|
|
108
|
+
// dispatched as `process.execPath` + `SYNC_BIN`, a plain `node` spawn — but the
|
|
109
|
+
// `npm install` step still needs the shim handling, so `NEEDS_SHELL` stays.)
|
|
110
|
+
const NEEDS_SHELL = process.platform === 'win32';
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Strip the `--assume-yes` flag from a passthrough argv, returning the
|
|
114
|
+
* remaining flags. `--assume-yes` is consumed by `init` to skip the prompt but
|
|
115
|
+
* is ALSO forwarded to bootstrap (see `buildBootstrapArgs`), so this helper
|
|
116
|
+
* exists only to detect its presence, not to drop it from the forward set.
|
|
117
|
+
*
|
|
118
|
+
* @param {string[]} argv
|
|
119
|
+
* @returns {boolean} whether `--assume-yes` is present
|
|
120
|
+
*/
|
|
121
|
+
function hasAssumeYes(argv) {
|
|
122
|
+
return argv.includes('--assume-yes');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Build the forwarded argv for the bootstrap step. Every passthrough flag is
|
|
127
|
+
* forwarded unchanged; `--assume-yes` is appended when chosen but absent so
|
|
128
|
+
* bootstrap runs non-interactively without the operator having typed it.
|
|
129
|
+
*
|
|
130
|
+
* @param {string[]} argv
|
|
131
|
+
* @param {boolean} assumeYes
|
|
132
|
+
* @returns {string[]}
|
|
133
|
+
*/
|
|
134
|
+
function buildBootstrapArgs(argv, assumeYes) {
|
|
135
|
+
if (assumeYes && !argv.includes('--assume-yes')) {
|
|
136
|
+
return [...argv, '--assume-yes'];
|
|
137
|
+
}
|
|
138
|
+
return [...argv];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Decide and execute the cold-start plan over injected boundaries.
|
|
143
|
+
*
|
|
144
|
+
* This is the testable core: it consults `exists` to decide whether to run the
|
|
145
|
+
* install + sync steps, resolves the prompt outcome from `isTTY` / `--assume-yes`
|
|
146
|
+
* / `confirm`, and either runs the bootstrap step or prints the files-only hint.
|
|
147
|
+
* Every effectful boundary is injected so the suite never touches a real TTY,
|
|
148
|
+
* npm, or the network.
|
|
149
|
+
*
|
|
150
|
+
* @param {{
|
|
151
|
+
* argv?: string[],
|
|
152
|
+
* exists?: (relPath: string) => boolean,
|
|
153
|
+
* runStep?: (cmd: string, args: string[]) => { status: number | null },
|
|
154
|
+
* confirm?: () => boolean,
|
|
155
|
+
* stdout?: (s: string) => void,
|
|
156
|
+
* isTTY?: boolean,
|
|
157
|
+
* }} [opts]
|
|
158
|
+
* @returns {{
|
|
159
|
+
* installed: boolean,
|
|
160
|
+
* ranBootstrap: boolean,
|
|
161
|
+
* steps: Array<{ cmd: string, args: string[] }>,
|
|
162
|
+
* exitCode: number,
|
|
163
|
+
* }}
|
|
164
|
+
*/
|
|
165
|
+
export function planInit({
|
|
166
|
+
argv = [],
|
|
167
|
+
exists,
|
|
168
|
+
runStep,
|
|
169
|
+
confirm,
|
|
170
|
+
stdout = (s) => process.stdout.write(s),
|
|
171
|
+
isTTY,
|
|
172
|
+
} = {}) {
|
|
173
|
+
const steps = [];
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Run one step through the injected runner and record it. A non-zero status
|
|
177
|
+
* short-circuits the plan with that exit code (the runner inherits stdio, so
|
|
178
|
+
* the failing tool's own output already reached the terminal).
|
|
179
|
+
*
|
|
180
|
+
* @param {string} cmd
|
|
181
|
+
* @param {string[]} args
|
|
182
|
+
* @returns {number} the step's exit code (0 on success)
|
|
183
|
+
*/
|
|
184
|
+
const step = (cmd, args) => {
|
|
185
|
+
steps.push({ cmd, args });
|
|
186
|
+
const result = runStep(cmd, args);
|
|
187
|
+
return result?.status ?? 1;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// --- Step 1: install-if-absent ------------------------------------------
|
|
191
|
+
// When `./.agents/` is missing, materialize the framework deterministically:
|
|
192
|
+
// `npm install <pkg> --ignore-scripts` then explicit `mandrel sync`. When it
|
|
193
|
+
// is present, skip straight to the prompt (idempotent post-install path).
|
|
194
|
+
const agentsPresent = exists('.agents');
|
|
195
|
+
if (!agentsPresent) {
|
|
196
|
+
const installStatus = step('npm', [
|
|
197
|
+
'install',
|
|
198
|
+
PACKAGE_NAME,
|
|
199
|
+
'--ignore-scripts',
|
|
200
|
+
]);
|
|
201
|
+
if (installStatus !== 0) {
|
|
202
|
+
return {
|
|
203
|
+
installed: false,
|
|
204
|
+
ranBootstrap: false,
|
|
205
|
+
steps,
|
|
206
|
+
exitCode: installStatus,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const syncStatus = step(process.execPath, [SYNC_BIN, 'sync']);
|
|
211
|
+
if (syncStatus !== 0) {
|
|
212
|
+
return {
|
|
213
|
+
installed: true,
|
|
214
|
+
ranBootstrap: false,
|
|
215
|
+
steps,
|
|
216
|
+
exitCode: syncStatus,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const installed = !agentsPresent;
|
|
222
|
+
|
|
223
|
+
// --- Step 2: configure-or-files prompt ----------------------------------
|
|
224
|
+
const assumeYes = hasAssumeYes(argv);
|
|
225
|
+
|
|
226
|
+
// Decide the outcome: configure (run bootstrap) vs. files-only.
|
|
227
|
+
// - `--assume-yes` → configure, prompt skipped entirely.
|
|
228
|
+
// - non-TTY without `--assume-yes` → files-only (never provision unattended).
|
|
229
|
+
// - TTY → consult the confirm seam for the yes/no answer (yes = configure).
|
|
230
|
+
let proceed;
|
|
231
|
+
if (assumeYes) {
|
|
232
|
+
proceed = true;
|
|
233
|
+
} else if (!isTTY) {
|
|
234
|
+
proceed = false;
|
|
235
|
+
} else {
|
|
236
|
+
stdout(PROMPT_TEXT);
|
|
237
|
+
proceed = confirm();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (proceed) {
|
|
241
|
+
const bootstrapArgs = buildBootstrapArgs(argv, assumeYes);
|
|
242
|
+
const bootstrapStatus = step(process.execPath, [
|
|
243
|
+
BOOTSTRAP_SCRIPT,
|
|
244
|
+
...bootstrapArgs,
|
|
245
|
+
]);
|
|
246
|
+
return {
|
|
247
|
+
installed,
|
|
248
|
+
ranBootstrap: true,
|
|
249
|
+
steps,
|
|
250
|
+
exitCode: bootstrapStatus,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Declined (files-only): print the re-run hint and exit cleanly.
|
|
255
|
+
stdout(FILES_ONLY_HINT);
|
|
256
|
+
return { installed, ranBootstrap: false, steps, exitCode: 0 };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Default `exists` seam — probe for `./.agents/` in the consumer's cwd.
|
|
261
|
+
*
|
|
262
|
+
* @param {string} relPath
|
|
263
|
+
* @returns {boolean}
|
|
264
|
+
*/
|
|
265
|
+
function defaultExists(relPath) {
|
|
266
|
+
return fs.existsSync(path.resolve(process.cwd(), relPath));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Default step runner — spawn the tool synchronously, inheriting stdio so its
|
|
271
|
+
* output streams to the terminal. Sets `shell: true` only on win32 so the
|
|
272
|
+
* `npm.cmd` shim resolves under CVE-2024-27980 hardening. The `sync` and
|
|
273
|
+
* bootstrap steps spawn `process.execPath` (a plain `node`), which needs no
|
|
274
|
+
* shell on any platform.
|
|
275
|
+
*
|
|
276
|
+
* @param {string} cmd
|
|
277
|
+
* @param {string[]} args
|
|
278
|
+
* @returns {{ status: number | null }}
|
|
279
|
+
*/
|
|
280
|
+
function defaultRunStep(cmd, args) {
|
|
281
|
+
return spawnSync(cmd, args, {
|
|
282
|
+
stdio: 'inherit',
|
|
283
|
+
env: process.env,
|
|
284
|
+
shell: NEEDS_SHELL,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Default `confirm` seam — synchronous yes/no prompt. Reads one line from stdin
|
|
290
|
+
* and normalizes it to a boolean; any input other than an explicit "no"
|
|
291
|
+
* (`n`/`no`, case-insensitive) — including bare Enter — defaults to `true`
|
|
292
|
+
* (configure), matching the `[Y/n]` convention where yes is the default.
|
|
293
|
+
*
|
|
294
|
+
* @returns {boolean}
|
|
295
|
+
*/
|
|
296
|
+
function defaultConfirm() {
|
|
297
|
+
let answer = '';
|
|
298
|
+
try {
|
|
299
|
+
const buf = fs.readFileSync(0, 'utf8');
|
|
300
|
+
answer = buf.split('\n', 1)[0].trim().toLowerCase();
|
|
301
|
+
} catch {
|
|
302
|
+
// No readable line (e.g. stdin closed) → fall through to the default.
|
|
303
|
+
answer = '';
|
|
304
|
+
}
|
|
305
|
+
return answer !== 'n' && answer !== 'no';
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Default export consumed by `bin/mandrel.js`.
|
|
310
|
+
*
|
|
311
|
+
* @param {string[]} [argv] - Subcommand arguments (after `mandrel init`).
|
|
312
|
+
* @returns {void}
|
|
313
|
+
*/
|
|
314
|
+
export default function run(argv = []) {
|
|
315
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
316
|
+
process.stdout.write(
|
|
317
|
+
'Usage: mandrel init [bootstrap flags]\n\n' +
|
|
318
|
+
' One-command cold start: install Mandrel (if absent), then prompt to\n' +
|
|
319
|
+
' configure now or stop at the files.\n\n' +
|
|
320
|
+
' --assume-yes Skip the prompt and configure non-interactively\n' +
|
|
321
|
+
' (forwarded to bootstrap.js). All other flags are\n' +
|
|
322
|
+
' forwarded to bootstrap.js unchanged.\n',
|
|
323
|
+
);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const result = planInit({
|
|
328
|
+
argv,
|
|
329
|
+
exists: defaultExists,
|
|
330
|
+
runStep: defaultRunStep,
|
|
331
|
+
confirm: defaultConfirm,
|
|
332
|
+
isTTY: Boolean(process.stdin.isTTY),
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
if (result.exitCode !== 0) {
|
|
336
|
+
process.exit(result.exitCode);
|
|
337
|
+
}
|
|
338
|
+
}
|