mandrel 1.59.0 → 1.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 +86 -44
- package/.agents/docs/SDLC.md +135 -141
- package/.agents/docs/configuration.md +77 -20
- package/.agents/docs/quality-gates.md +796 -0
- package/.agents/docs/workflows.md +6 -9
- 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/runtime-deps.json +2 -2
- package/.agents/schemas/agentrc.schema.json +3 -3
- 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 +2 -2
- package/.agents/scripts/acceptance-eval.js +1 -1
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/agents-bootstrap-github.js +23 -119
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/check-doc-links.js +2 -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 +6 -6
- package/.agents/scripts/epic-deliver-prepare.js +1 -1
- package/.agents/scripts/epic-execute-record-wave.js +4 -4
- 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 +1 -1
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +7 -11
- 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/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 +47 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +7 -9
- package/.agents/scripts/lib/bootstrap/manifest.js +21 -1
- package/.agents/scripts/lib/bootstrap/merge-methods.js +31 -16
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +32 -11
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- 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/detect-package-manager.js +72 -0
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/errors/index.js +4 -4
- 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/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +5 -10
- package/.agents/scripts/lib/onboard/detect-stack.js +10 -10
- package/.agents/scripts/lib/onboard/init-tail.js +218 -0
- package/.agents/scripts/lib/onboard/scaffold-docs.js +18 -3
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
- package/.agents/scripts/lib/orchestration/code-review.js +5 -5
- 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 +9 -25
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +8 -8
- 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-lease-guard.js +26 -13
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +1 -1
- 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 +2 -2
- 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 +6 -21
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +8 -8
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +4 -4
- 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 +2 -2
- 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 +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +1 -1
- 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 +1 -1
- 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/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/preflight-cache.js +1 -1
- 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/single-story-close/phases/wrong-tree-guard.js +1 -1
- 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/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +2 -2
- 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 +2 -2
- 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 +5 -12
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +8 -8
- package/.agents/scripts/lib/orchestration/ticketing/state.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +2 -2
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +1 -1
- 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/runtime-deps/preflight.js +6 -6
- 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-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 +8 -8
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/wave-runner/tick.js +1 -1
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +5 -2
- 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 +4 -4
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +1 -1
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +5 -5
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +13 -16
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +1 -1
- 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/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +16 -31
- 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 +2 -2
- 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/agents-sync-config.md +3 -2
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +43 -43
- 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 +13 -13
- 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 +11 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/plan.md +131 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +19 -39
- package/bin/mandrel.js +235 -16
- package/docs/CHANGELOG.md +1173 -0
- package/lib/cli/doctor.js +45 -3
- package/lib/cli/init.js +97 -36
- package/lib/cli/registry.js +41 -145
- package/lib/cli/sync.js +122 -23
- package/lib/cli/uninstall.js +42 -7
- package/lib/cli/update.js +524 -210
- package/lib/cli/version-helpers.js +59 -0
- package/package.json +7 -6
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/workflows/onboard.md +0 -208
- package/lib/cli/__tests__/migrate.test.js +0 -268
- package/lib/cli/__tests__/sync-local-zone.test.js +0 -247
- package/lib/cli/__tests__/sync.test.js +0 -372
- package/lib/cli/__tests__/update-major.test.js +0 -217
- package/lib/cli/__tests__/update.test.js +0 -696
- package/lib/cli/__tests__/version-check.test.js +0 -398
- package/lib/migrations/__tests__/index.test.js +0 -216
package/lib/cli/doctor.js
CHANGED
|
@@ -17,11 +17,15 @@
|
|
|
17
17
|
* Exit code 0 = all pass, 1 = any fail.
|
|
18
18
|
*
|
|
19
19
|
* Injectable seams (used by tests):
|
|
20
|
-
* - `checks`
|
|
21
|
-
* - `write`
|
|
22
|
-
* - `exit`
|
|
20
|
+
* - `checks` — replaces the default registry array
|
|
21
|
+
* - `write` — replaces process.stdout.write
|
|
22
|
+
* - `exit` — replaces process.exit
|
|
23
|
+
* - `writeResultCache` — replaces the temp/doctor-result.json writer
|
|
23
24
|
*/
|
|
24
25
|
|
|
26
|
+
import nodeFs from 'node:fs';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
|
|
25
29
|
import { registry } from './registry.js';
|
|
26
30
|
|
|
27
31
|
// ---------------------------------------------------------------------------
|
|
@@ -74,6 +78,40 @@ function formatSummary(passed, total) {
|
|
|
74
78
|
return `❌ Not ready (${failed}/${total} checks failed)\n`;
|
|
75
79
|
}
|
|
76
80
|
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Result cache
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Best-effort write of the doctor verdict to `temp/doctor-result.json` under
|
|
87
|
+
* the consumer root so downstream workflows (e.g. the `/plan` first-run
|
|
88
|
+
* preflight) can read the last recorded verdict without re-running doctor.
|
|
89
|
+
* `temp/` is the gitignored scratch root, so neither git nor the sync prune
|
|
90
|
+
* pass ever sees the cache. Any write failure is swallowed — the cache is
|
|
91
|
+
* advisory, never a gate.
|
|
92
|
+
*
|
|
93
|
+
* Exported for testing.
|
|
94
|
+
*
|
|
95
|
+
* @param {'ready'|'unready'} verdict
|
|
96
|
+
* @param {{ fs?: typeof nodeFs, cwd?: () => string }} [opts]
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
99
|
+
export function writeDoctorResultCache(
|
|
100
|
+
verdict,
|
|
101
|
+
{ fs = nodeFs, cwd = process.cwd } = {},
|
|
102
|
+
) {
|
|
103
|
+
try {
|
|
104
|
+
const dir = path.join(cwd(), 'temp');
|
|
105
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
106
|
+
fs.writeFileSync(
|
|
107
|
+
path.join(dir, 'doctor-result.json'),
|
|
108
|
+
`${JSON.stringify({ verdict, checkedAt: new Date().toISOString() }, null, 2)}\n`,
|
|
109
|
+
);
|
|
110
|
+
} catch {
|
|
111
|
+
// Best-effort only — an unwritable temp/ must never fail the doctor run.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
77
115
|
// ---------------------------------------------------------------------------
|
|
78
116
|
// Doctor runner (exported for testing)
|
|
79
117
|
// ---------------------------------------------------------------------------
|
|
@@ -85,6 +123,7 @@ function formatSummary(passed, total) {
|
|
|
85
123
|
* checks?: Array<{ name: string, run(opts?: unknown): { ok: boolean, detail: string, remedy?: string } }>,
|
|
86
124
|
* write?: (s: string) => void,
|
|
87
125
|
* exit?: (code: number) => void,
|
|
126
|
+
* writeResultCache?: (verdict: 'ready'|'unready') => void,
|
|
88
127
|
* }} [opts]
|
|
89
128
|
* @returns {void}
|
|
90
129
|
*/
|
|
@@ -92,6 +131,7 @@ export async function runDoctor({
|
|
|
92
131
|
checks = registry,
|
|
93
132
|
write = (s) => process.stdout.write(s),
|
|
94
133
|
exit = (code) => process.exit(code),
|
|
134
|
+
writeResultCache = writeDoctorResultCache,
|
|
95
135
|
} = {}) {
|
|
96
136
|
let passed = 0;
|
|
97
137
|
|
|
@@ -104,6 +144,8 @@ export async function runDoctor({
|
|
|
104
144
|
const total = checks.length;
|
|
105
145
|
write(formatSummary(passed, total));
|
|
106
146
|
|
|
147
|
+
writeResultCache(passed === total ? 'ready' : 'unready');
|
|
148
|
+
|
|
107
149
|
if (passed < total) {
|
|
108
150
|
exit(1);
|
|
109
151
|
}
|
package/lib/cli/init.js
CHANGED
|
@@ -20,13 +20,15 @@
|
|
|
20
20
|
* `init` goes straight to the prompt — the one subcommand is idempotent
|
|
21
21
|
* across both the cold-start and post-install entry points.
|
|
22
22
|
*
|
|
23
|
-
* 2. **
|
|
24
|
-
* `node .agents/scripts/bootstrap.js`, forwarding every
|
|
25
|
-
* unchanged) or stop at "just the files" (
|
|
26
|
-
* and exit 0).
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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.
|
|
30
32
|
*
|
|
31
33
|
* ## Cold-start provenance
|
|
32
34
|
*
|
|
@@ -48,8 +50,9 @@
|
|
|
48
50
|
* for `./.agents/` in the cwd
|
|
49
51
|
* - `runStep` — `(cmd, args) => { status }`; runs one install/sync/bootstrap
|
|
50
52
|
* step. Defaults to a `spawnSync` runner with `stdio: inherit`.
|
|
51
|
-
* - `confirm` — `() =>
|
|
52
|
-
* Defaults to a synchronous stdin readline
|
|
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.
|
|
53
56
|
* - `stdout` — `(s) => void`; defaults to `process.stdout.write`.
|
|
54
57
|
* - `isTTY` — boolean; defaults to `process.stdin.isTTY`.
|
|
55
58
|
* - `exit` — `(code) => void`; defaults to `process.exit`.
|
|
@@ -63,6 +66,30 @@
|
|
|
63
66
|
import { spawnSync } from 'node:child_process';
|
|
64
67
|
import fs from 'node:fs';
|
|
65
68
|
import path from 'node:path';
|
|
69
|
+
import { pathToFileURL } from 'node:url';
|
|
70
|
+
|
|
71
|
+
// Lazily resolved at runtime so cold-start `npx mandrel init` (where
|
|
72
|
+
// `.agents/` is absent) does not try to import from a directory that does not
|
|
73
|
+
// exist yet. The import is performed after bootstrap succeeds and `.agents/`
|
|
74
|
+
// is guaranteed to be materialised.
|
|
75
|
+
let _runInitTail = null;
|
|
76
|
+
async function getRunInitTail(projectRoot) {
|
|
77
|
+
if (_runInitTail) return _runInitTail;
|
|
78
|
+
const tailPath = path.join(
|
|
79
|
+
projectRoot,
|
|
80
|
+
'.agents',
|
|
81
|
+
'scripts',
|
|
82
|
+
'lib',
|
|
83
|
+
'onboard',
|
|
84
|
+
'init-tail.js',
|
|
85
|
+
);
|
|
86
|
+
// pathToFileURL handles Windows drive letters correctly (a raw
|
|
87
|
+
// `file://C:\…` template treats the drive letter as a URL host — same
|
|
88
|
+
// fix as commit 2e3d210b in lib/transpile.js).
|
|
89
|
+
const mod = await import(pathToFileURL(tailPath).href);
|
|
90
|
+
_runInitTail = mod.runInitTail;
|
|
91
|
+
return _runInitTail;
|
|
92
|
+
}
|
|
66
93
|
|
|
67
94
|
/**
|
|
68
95
|
* Hardcoded build-time package name. NEVER read from argv or env — cold-start
|
|
@@ -92,12 +119,11 @@ const BOOTSTRAP_SCRIPT = path.join('.agents', 'scripts', 'bootstrap.js');
|
|
|
92
119
|
const SYNC_BIN = path.join('node_modules', PACKAGE_NAME, 'bin', 'mandrel.js');
|
|
93
120
|
|
|
94
121
|
const PROMPT_TEXT =
|
|
95
|
-
'Mandrel
|
|
96
|
-
'
|
|
97
|
-
|
|
98
|
-
'Choice [1/2]: ';
|
|
122
|
+
'The Mandrel .agents package has been copied to your directory.\n' +
|
|
123
|
+
'Would you like to begin the interactive process to setup your local and ' +
|
|
124
|
+
'github environments now? [Y/n]: ';
|
|
99
125
|
|
|
100
|
-
const FILES_ONLY_HINT = 'Configure any time with: mandrel init\n';
|
|
126
|
+
const FILES_ONLY_HINT = 'Configure any time with: npx mandrel init\n';
|
|
101
127
|
|
|
102
128
|
// On win32, `npm` resolves to a `.cmd` shim that Node refuses to spawn without
|
|
103
129
|
// a shell after the CVE-2024-27980 hardening; mirror update.js and set
|
|
@@ -149,24 +175,26 @@ function buildBootstrapArgs(argv, assumeYes) {
|
|
|
149
175
|
* argv?: string[],
|
|
150
176
|
* exists?: (relPath: string) => boolean,
|
|
151
177
|
* runStep?: (cmd: string, args: string[]) => { status: number | null },
|
|
152
|
-
* confirm?: () =>
|
|
178
|
+
* confirm?: () => boolean,
|
|
153
179
|
* stdout?: (s: string) => void,
|
|
154
180
|
* isTTY?: boolean,
|
|
181
|
+
* afterBootstrap?: (root: string) => Promise<{ ok?: boolean } | void> | { ok?: boolean } | void,
|
|
155
182
|
* }} [opts]
|
|
156
|
-
* @returns {{
|
|
183
|
+
* @returns {Promise<{
|
|
157
184
|
* installed: boolean,
|
|
158
185
|
* ranBootstrap: boolean,
|
|
159
186
|
* steps: Array<{ cmd: string, args: string[] }>,
|
|
160
187
|
* exitCode: number,
|
|
161
|
-
* }}
|
|
188
|
+
* }>}
|
|
162
189
|
*/
|
|
163
|
-
export function planInit({
|
|
190
|
+
export async function planInit({
|
|
164
191
|
argv = [],
|
|
165
192
|
exists,
|
|
166
193
|
runStep,
|
|
167
194
|
confirm,
|
|
168
195
|
stdout = (s) => process.stdout.write(s),
|
|
169
196
|
isTTY,
|
|
197
|
+
afterBootstrap,
|
|
170
198
|
} = {}) {
|
|
171
199
|
const steps = [];
|
|
172
200
|
|
|
@@ -224,32 +252,58 @@ export function planInit({
|
|
|
224
252
|
// Decide the outcome: configure (run bootstrap) vs. files-only.
|
|
225
253
|
// - `--assume-yes` → configure, prompt skipped entirely.
|
|
226
254
|
// - non-TTY without `--assume-yes` → files-only (never provision unattended).
|
|
227
|
-
// - TTY → consult the confirm seam for the
|
|
228
|
-
let
|
|
255
|
+
// - TTY → consult the confirm seam for the yes/no answer (yes = configure).
|
|
256
|
+
let proceed;
|
|
229
257
|
if (assumeYes) {
|
|
230
|
-
|
|
258
|
+
proceed = true;
|
|
231
259
|
} else if (!isTTY) {
|
|
232
|
-
|
|
260
|
+
proceed = false;
|
|
233
261
|
} else {
|
|
234
262
|
stdout(PROMPT_TEXT);
|
|
235
|
-
|
|
263
|
+
proceed = confirm();
|
|
236
264
|
}
|
|
237
265
|
|
|
238
|
-
if (
|
|
266
|
+
if (proceed) {
|
|
239
267
|
const bootstrapArgs = buildBootstrapArgs(argv, assumeYes);
|
|
240
268
|
const bootstrapStatus = step(process.execPath, [
|
|
241
269
|
BOOTSTRAP_SCRIPT,
|
|
242
270
|
...bootstrapArgs,
|
|
243
271
|
]);
|
|
272
|
+
if (bootstrapStatus !== 0) {
|
|
273
|
+
return {
|
|
274
|
+
installed,
|
|
275
|
+
ranBootstrap: true,
|
|
276
|
+
steps,
|
|
277
|
+
exitCode: bootstrapStatus,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Bootstrap succeeded — run the onboarding tail: stack detection, docs
|
|
282
|
+
// scaffolding offer, doctor gate, and /plan handoff. A tail that reports
|
|
283
|
+
// `ok: false` (the doctor gate failed) makes the whole init exit
|
|
284
|
+
// non-zero; the tail already printed its own remediation message, and the
|
|
285
|
+
// earlier install/sync/bootstrap phases' results stand as completed.
|
|
286
|
+
if (afterBootstrap) {
|
|
287
|
+
const tail = await afterBootstrap(process.cwd());
|
|
288
|
+
if (tail && tail.ok === false) {
|
|
289
|
+
return {
|
|
290
|
+
installed,
|
|
291
|
+
ranBootstrap: true,
|
|
292
|
+
steps,
|
|
293
|
+
exitCode: 1,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
244
298
|
return {
|
|
245
299
|
installed,
|
|
246
300
|
ranBootstrap: true,
|
|
247
301
|
steps,
|
|
248
|
-
exitCode:
|
|
302
|
+
exitCode: 0,
|
|
249
303
|
};
|
|
250
304
|
}
|
|
251
305
|
|
|
252
|
-
//
|
|
306
|
+
// Declined (files-only): print the re-run hint and exit cleanly.
|
|
253
307
|
stdout(FILES_ONLY_HINT);
|
|
254
308
|
return { installed, ranBootstrap: false, steps, exitCode: 0 };
|
|
255
309
|
}
|
|
@@ -284,32 +338,32 @@ function defaultRunStep(cmd, args) {
|
|
|
284
338
|
}
|
|
285
339
|
|
|
286
340
|
/**
|
|
287
|
-
* Default `confirm` seam — synchronous
|
|
288
|
-
*
|
|
289
|
-
* (including bare Enter
|
|
290
|
-
* `
|
|
341
|
+
* Default `confirm` seam — synchronous yes/no prompt. Reads one line from stdin
|
|
342
|
+
* and normalizes it to a boolean; any input other than an explicit "no"
|
|
343
|
+
* (`n`/`no`, case-insensitive) — including bare Enter — defaults to `true`
|
|
344
|
+
* (configure), matching the `[Y/n]` convention where yes is the default.
|
|
291
345
|
*
|
|
292
|
-
* @returns {
|
|
346
|
+
* @returns {boolean}
|
|
293
347
|
*/
|
|
294
348
|
function defaultConfirm() {
|
|
295
349
|
let answer = '';
|
|
296
350
|
try {
|
|
297
351
|
const buf = fs.readFileSync(0, 'utf8');
|
|
298
|
-
answer = buf.split('\n', 1)[0].trim();
|
|
352
|
+
answer = buf.split('\n', 1)[0].trim().toLowerCase();
|
|
299
353
|
} catch {
|
|
300
354
|
// No readable line (e.g. stdin closed) → fall through to the default.
|
|
301
355
|
answer = '';
|
|
302
356
|
}
|
|
303
|
-
return answer
|
|
357
|
+
return answer !== 'n' && answer !== 'no';
|
|
304
358
|
}
|
|
305
359
|
|
|
306
360
|
/**
|
|
307
361
|
* Default export consumed by `bin/mandrel.js`.
|
|
308
362
|
*
|
|
309
363
|
* @param {string[]} [argv] - Subcommand arguments (after `mandrel init`).
|
|
310
|
-
* @returns {void}
|
|
364
|
+
* @returns {Promise<void>}
|
|
311
365
|
*/
|
|
312
|
-
export default function run(argv = []) {
|
|
366
|
+
export default async function run(argv = []) {
|
|
313
367
|
if (argv.includes('--help') || argv.includes('-h')) {
|
|
314
368
|
process.stdout.write(
|
|
315
369
|
'Usage: mandrel init [bootstrap flags]\n\n' +
|
|
@@ -322,12 +376,19 @@ export default function run(argv = []) {
|
|
|
322
376
|
return;
|
|
323
377
|
}
|
|
324
378
|
|
|
325
|
-
const result = planInit({
|
|
379
|
+
const result = await planInit({
|
|
326
380
|
argv,
|
|
327
381
|
exists: defaultExists,
|
|
328
382
|
runStep: defaultRunStep,
|
|
329
383
|
confirm: defaultConfirm,
|
|
330
384
|
isTTY: Boolean(process.stdin.isTTY),
|
|
385
|
+
afterBootstrap: async (projectRoot) => {
|
|
386
|
+
const runInitTail = await getRunInitTail(projectRoot);
|
|
387
|
+
return runInitTail({
|
|
388
|
+
root: projectRoot,
|
|
389
|
+
isTTY: Boolean(process.stdin.isTTY),
|
|
390
|
+
});
|
|
391
|
+
},
|
|
331
392
|
});
|
|
332
393
|
|
|
333
394
|
if (result.exitCode !== 0) {
|
package/lib/cli/registry.js
CHANGED
|
@@ -26,7 +26,17 @@ import { createRequire } from 'node:module';
|
|
|
26
26
|
import path from 'node:path';
|
|
27
27
|
import { fileURLToPath } from 'node:url';
|
|
28
28
|
|
|
29
|
+
import {
|
|
30
|
+
REQUIRED_NODE_CEILING_MAJOR,
|
|
31
|
+
REQUIRED_NODE_FLOOR,
|
|
32
|
+
satisfiesNodeEngine,
|
|
33
|
+
} from '../../.agents/scripts/lib/bootstrap/project-bootstrap.js';
|
|
34
|
+
import {
|
|
35
|
+
defaultResolvePackageRoot,
|
|
36
|
+
listFiles as listPayloadFiles,
|
|
37
|
+
} from './sync.js';
|
|
29
38
|
import { readCache } from './version-check.js';
|
|
39
|
+
import { compareVersions as compareSemver } from './version-helpers.js';
|
|
30
40
|
|
|
31
41
|
// ---------------------------------------------------------------------------
|
|
32
42
|
// Internal helpers
|
|
@@ -53,32 +63,6 @@ function spawn(cmd, args) {
|
|
|
53
63
|
// check: node-version
|
|
54
64
|
// ---------------------------------------------------------------------------
|
|
55
65
|
|
|
56
|
-
/**
|
|
57
|
-
* The minimum Node version the framework requires (`engines.node`).
|
|
58
|
-
* Mirrors `lib/bootstrap/project-bootstrap.js#REQUIRED_NODE_FLOOR`.
|
|
59
|
-
*/
|
|
60
|
-
const REQUIRED_NODE_FLOOR = '22.22.1';
|
|
61
|
-
const REQUIRED_NODE_CEILING_MAJOR = 25;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Return true when `version` satisfies `>=22.22.1 <25`.
|
|
65
|
-
*
|
|
66
|
-
* @param {string} version
|
|
67
|
-
* @returns {boolean}
|
|
68
|
-
*/
|
|
69
|
-
function satisfiesNodeEngine(version) {
|
|
70
|
-
const [majorRaw, minorRaw, patchRaw] = String(version).split('.');
|
|
71
|
-
const major = Number.parseInt(majorRaw, 10) || 0;
|
|
72
|
-
const minor = Number.parseInt(minorRaw, 10) || 0;
|
|
73
|
-
const patch = Number.parseInt(patchRaw, 10) || 0;
|
|
74
|
-
if (major >= REQUIRED_NODE_CEILING_MAJOR) return false;
|
|
75
|
-
if (major > 22) return true;
|
|
76
|
-
if (major < 22) return false;
|
|
77
|
-
if (minor > 22) return true;
|
|
78
|
-
if (minor < 22) return false;
|
|
79
|
-
return patch >= 1;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
66
|
/**
|
|
83
67
|
* @param {{ nodeVersion?: string }} [opts]
|
|
84
68
|
* @returns {{ ok: boolean, detail: string, remedy?: string }}
|
|
@@ -235,19 +219,6 @@ function runGhAuth({ runner = spawn, env = process.env } = {}) {
|
|
|
235
219
|
// check: commands-in-sync
|
|
236
220
|
// ---------------------------------------------------------------------------
|
|
237
221
|
|
|
238
|
-
/**
|
|
239
|
-
* Resolve the project root — the directory that contains `.agents/` and
|
|
240
|
-
* `.claude/`. Walks up from this file's own location.
|
|
241
|
-
*
|
|
242
|
-
* @returns {string}
|
|
243
|
-
*/
|
|
244
|
-
function resolveProjectRoot() {
|
|
245
|
-
// lib/cli/registry.js lives at <root>/lib/cli/registry.js, so walk up two
|
|
246
|
-
// levels from __dirname to reach the project root.
|
|
247
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
248
|
-
return path.resolve(here, '..', '..');
|
|
249
|
-
}
|
|
250
|
-
|
|
251
222
|
/**
|
|
252
223
|
* Dry-run the sync-claude-commands logic: compare `.agents/workflows/*.md`
|
|
253
224
|
* sources to the generated flat command tree `.claude/commands/*.md`
|
|
@@ -324,6 +295,14 @@ function runCommandsInSync({ projectRoot, cwd, readDir } = {}) {
|
|
|
324
295
|
* Verify that the framework's required runtime dependencies are resolvable
|
|
325
296
|
* from the project's node_modules.
|
|
326
297
|
*
|
|
298
|
+
* `projectRoot` defaults to `process.cwd()` (the consumer project root) so
|
|
299
|
+
* the manifest path and the require-resolution context mirror the context in
|
|
300
|
+
* which the framework scripts actually run (Story #4046 A2). Using
|
|
301
|
+
* `resolveProjectRoot()` (the package root inside `node_modules/mandrel/`)
|
|
302
|
+
* breaks the check under pnpm isolated-mode layouts where the consumer's
|
|
303
|
+
* node_modules are hoisted above `node_modules/mandrel/` and are invisible
|
|
304
|
+
* from the package root's resolver.
|
|
305
|
+
*
|
|
327
306
|
* Injectable seams:
|
|
328
307
|
* - `resolve(dep)` — replaces the real `require.resolve`; throws when a dep
|
|
329
308
|
* is missing.
|
|
@@ -338,10 +317,15 @@ function runRuntimeDeps({
|
|
|
338
317
|
resolve: resolveSeam,
|
|
339
318
|
manifestRequired,
|
|
340
319
|
} = {}) {
|
|
320
|
+
// Anchor at process.cwd() (the consumer root), not resolveProjectRoot() (the
|
|
321
|
+
// package root). Under pnpm isolated-mode the consumer's node_modules are not
|
|
322
|
+
// visible from inside node_modules/mandrel/, so the old anchor produced false
|
|
323
|
+
// "missing" reports on a clean install (Story #4046 A2).
|
|
324
|
+
const root = projectRoot ?? process.cwd();
|
|
325
|
+
|
|
341
326
|
let required = manifestRequired;
|
|
342
327
|
if (!required) {
|
|
343
328
|
try {
|
|
344
|
-
const root = projectRoot ?? resolveProjectRoot();
|
|
345
329
|
const manifestPath = path.join(root, '.agents', 'runtime-deps.json');
|
|
346
330
|
const raw = fs.readFileSync(manifestPath, 'utf8');
|
|
347
331
|
const parsed = JSON.parse(raw);
|
|
@@ -366,9 +350,11 @@ function runRuntimeDeps({
|
|
|
366
350
|
}
|
|
367
351
|
}
|
|
368
352
|
} else {
|
|
369
|
-
// Anchor resolution to the project root so it mirrors the context
|
|
370
|
-
// the framework scripts run (they free-ride on the consumer's
|
|
371
|
-
|
|
353
|
+
// Anchor resolution to the consumer project root so it mirrors the context
|
|
354
|
+
// in which the framework scripts run (they free-ride on the consumer's
|
|
355
|
+
// node_modules). Under pnpm isolated-mode the consumer's node_modules are
|
|
356
|
+
// not reachable from inside node_modules/mandrel/; anchoring at process.cwd()
|
|
357
|
+
// finds them correctly.
|
|
372
358
|
const req = createRequire(path.join(root, 'package.json'));
|
|
373
359
|
for (const dep of required) {
|
|
374
360
|
try {
|
|
@@ -467,68 +453,9 @@ function runAgentsMaterialized({ cwd, existsSync, resolvePackage } = {}) {
|
|
|
467
453
|
// check: agents-drift
|
|
468
454
|
// ---------------------------------------------------------------------------
|
|
469
455
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
*/
|
|
474
|
-
const PACKAGE_NAME = 'mandrel';
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Top-level directory name (relative to `.agents/`) reserved as the
|
|
478
|
-
* sync-exempt local-additions zone (Story #3498, f-drift-local-zone).
|
|
479
|
-
*
|
|
480
|
-
* `.agents/local/` is consumer-owned space that `mandrel sync` never
|
|
481
|
-
* materializes nor prunes, so it is excluded from the drift comparison —
|
|
482
|
-
* a hand-authored file under `.agents/local/` is sanctioned, not drift.
|
|
483
|
-
* Mirrors `lib/cli/sync.js#LOCAL_ZONE_DIR`.
|
|
484
|
-
*/
|
|
485
|
-
const LOCAL_ZONE_DIR = 'local';
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Default resolver: locate the installed `mandrel` package root by
|
|
489
|
-
* resolving its `package.json` and returning the directory that contains it.
|
|
490
|
-
* Mirrors `lib/cli/sync.js#defaultResolvePackageRoot` so the drift baseline
|
|
491
|
-
* is exactly the payload that `mandrel sync` would copy.
|
|
492
|
-
*
|
|
493
|
-
* @param {string} fromDir - Directory to resolve from (the consumer project).
|
|
494
|
-
* @returns {string} Absolute path to the package root.
|
|
495
|
-
*/
|
|
496
|
-
function defaultResolvePackageRoot(fromDir) {
|
|
497
|
-
const requireFrom = createRequire(path.join(fromDir, 'noop.js'));
|
|
498
|
-
const pkgJsonPath = requireFrom.resolve(`${PACKAGE_NAME}/package.json`);
|
|
499
|
-
return path.dirname(pkgJsonPath);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* Recursively enumerate every regular file under `dir`, returning paths
|
|
504
|
-
* relative to `dir` using OS separators. The top-level `local/` subtree is
|
|
505
|
-
* skipped entirely — it is the consumer-owned local-additions zone and is
|
|
506
|
-
* never part of the package payload (Story #3498). Mirrors
|
|
507
|
-
* `lib/cli/sync.js#listFiles` so source enumeration matches the materializer.
|
|
508
|
-
*
|
|
509
|
-
* @param {string} dir - Absolute directory to walk.
|
|
510
|
-
* @param {typeof fs} fsImpl
|
|
511
|
-
* @param {string} [prefix] - Accumulated relative prefix (internal).
|
|
512
|
-
* @returns {string[]} Relative file paths.
|
|
513
|
-
*/
|
|
514
|
-
function listPayloadFiles(dir, fsImpl, prefix = '') {
|
|
515
|
-
const out = [];
|
|
516
|
-
for (const ent of fsImpl.readdirSync(dir, { withFileTypes: true })) {
|
|
517
|
-
// Scope the local-zone skip to the top-level `.agents/local/` only; a
|
|
518
|
-
// deeper directory that happens to be named `local` stays in scope.
|
|
519
|
-
if (prefix === '' && ent.name === LOCAL_ZONE_DIR && ent.isDirectory()) {
|
|
520
|
-
continue;
|
|
521
|
-
}
|
|
522
|
-
const rel = prefix ? path.join(prefix, ent.name) : ent.name;
|
|
523
|
-
const abs = path.join(dir, ent.name);
|
|
524
|
-
if (ent.isDirectory()) {
|
|
525
|
-
out.push(...listPayloadFiles(abs, fsImpl, rel));
|
|
526
|
-
} else {
|
|
527
|
-
out.push(rel);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
return out;
|
|
531
|
-
}
|
|
456
|
+
// defaultResolvePackageRoot and listPayloadFiles (re-exported as `listFiles`
|
|
457
|
+
// from sync.js) are imported from `lib/cli/sync.js` at the top of this file
|
|
458
|
+
// (Story #4048 B3 — no mirror copies).
|
|
532
459
|
|
|
533
460
|
/**
|
|
534
461
|
* Compare the consumer's materialized `./.agents/<f>` bytes against the
|
|
@@ -630,39 +557,9 @@ function runAgentsDrift({ cwd, fsImpl = fs, resolvePackageRoot } = {}) {
|
|
|
630
557
|
// check: version-current
|
|
631
558
|
// ---------------------------------------------------------------------------
|
|
632
559
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
* Mirrors `lib/cli/update.js#parseVersion`.
|
|
637
|
-
*
|
|
638
|
-
* @param {string} version
|
|
639
|
-
* @returns {[number, number, number]}
|
|
640
|
-
*/
|
|
641
|
-
function parseVersionTuple(version) {
|
|
642
|
-
const [major, minor, patch] = String(version).split('.');
|
|
643
|
-
return [
|
|
644
|
-
Number.parseInt(major, 10) || 0,
|
|
645
|
-
Number.parseInt(minor, 10) || 0,
|
|
646
|
-
Number.parseInt(patch, 10) || 0,
|
|
647
|
-
];
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Compare two version strings. Negative when `a < b`, zero when equal,
|
|
652
|
-
* positive when `a > b`. Mirrors `lib/cli/update.js#compareVersions`.
|
|
653
|
-
*
|
|
654
|
-
* @param {string} a
|
|
655
|
-
* @param {string} b
|
|
656
|
-
* @returns {number}
|
|
657
|
-
*/
|
|
658
|
-
function compareSemver(a, b) {
|
|
659
|
-
const pa = parseVersionTuple(a);
|
|
660
|
-
const pb = parseVersionTuple(b);
|
|
661
|
-
for (let i = 0; i < 3; i += 1) {
|
|
662
|
-
if (pa[i] !== pb[i]) return pa[i] - pb[i];
|
|
663
|
-
}
|
|
664
|
-
return 0;
|
|
665
|
-
}
|
|
560
|
+
// parseVersion and compareVersions are imported from `lib/cli/version-helpers.js`
|
|
561
|
+
// at the top of this file (Story #4048 B3 — no mirror copies). `compareSemver`
|
|
562
|
+
// is the registry-local alias for the imported `compareVersions`.
|
|
666
563
|
|
|
667
564
|
/**
|
|
668
565
|
* Resolve the installed `mandrel` version from this package's own
|
|
@@ -685,17 +582,16 @@ function defaultInstalledVersion(fsImpl) {
|
|
|
685
582
|
const DEFAULT_VERSION_CACHE_FILENAME = 'version-check.json';
|
|
686
583
|
|
|
687
584
|
/**
|
|
688
|
-
* Default cache path: `<
|
|
689
|
-
*
|
|
585
|
+
* Default cache path: `<consumerRoot>/temp/version-check.json`, anchored at
|
|
586
|
+
* `process.cwd()` (the consumer project root) so the cache file survives
|
|
587
|
+
* npm/pnpm reinstalls that may replace `node_modules/` entirely (Story #4046
|
|
588
|
+
* A2). Mirrors `commands-in-sync` / `agents-materialized` / `agents-drift`
|
|
589
|
+
* which all anchor at `process.cwd()`.
|
|
690
590
|
*
|
|
691
591
|
* @returns {string}
|
|
692
592
|
*/
|
|
693
593
|
function defaultVersionCachePath() {
|
|
694
|
-
return path.join(
|
|
695
|
-
resolveProjectRoot(),
|
|
696
|
-
'temp',
|
|
697
|
-
DEFAULT_VERSION_CACHE_FILENAME,
|
|
698
|
-
);
|
|
594
|
+
return path.join(process.cwd(), 'temp', DEFAULT_VERSION_CACHE_FILENAME);
|
|
699
595
|
}
|
|
700
596
|
|
|
701
597
|
/**
|