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/bin/mandrel.js
CHANGED
|
@@ -2,35 +2,252 @@
|
|
|
2
2
|
// bin/mandrel.js — mandrel CLI entry point
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Allowlist-based subcommand dispatcher.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Only modules listed in SUBCOMMANDS are dispatchable. Each entry declares the
|
|
8
|
+
* name, a one-line description for help output, and the set of known flags so
|
|
9
|
+
* the dispatcher can reject unknown flags before loading the subcommand.
|
|
10
|
+
*
|
|
11
|
+
* Supported top-level flags:
|
|
12
|
+
* --help / -h Print subcommand list and exit 0.
|
|
13
|
+
* --version Print installed version and exit 0.
|
|
14
|
+
*
|
|
15
|
+
* Each subcommand module must export a default function `run(argv)`.
|
|
10
16
|
*/
|
|
11
17
|
|
|
18
|
+
import { createRequire } from 'node:module';
|
|
12
19
|
import path from 'node:path';
|
|
13
20
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
14
21
|
|
|
15
22
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Subcommand registry — the ONLY allowed dispatch targets
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {{ description: string, knownFlags: Set<string> }} SubcommandMeta
|
|
30
|
+
* @type {Map<string, SubcommandMeta>}
|
|
31
|
+
*/
|
|
32
|
+
const SUBCOMMANDS = new Map([
|
|
33
|
+
[
|
|
34
|
+
'init',
|
|
35
|
+
{
|
|
36
|
+
description: 'install and configure mandrel in the current project',
|
|
37
|
+
knownFlags: new Set([
|
|
38
|
+
'--assume-yes',
|
|
39
|
+
'--skip-github',
|
|
40
|
+
'--owner',
|
|
41
|
+
'--repo',
|
|
42
|
+
'--base-branch',
|
|
43
|
+
'--project-number',
|
|
44
|
+
'--operator-handle',
|
|
45
|
+
'--dry-run',
|
|
46
|
+
]),
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
'sync',
|
|
51
|
+
{
|
|
52
|
+
description: 'materialize .agents/ payload from installed package',
|
|
53
|
+
knownFlags: new Set(['--dry-run']),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
[
|
|
57
|
+
'sync-commands',
|
|
58
|
+
{
|
|
59
|
+
description: 'regenerate .claude/commands/ from .agents/workflows/',
|
|
60
|
+
knownFlags: new Set(['--dry-run']),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
'doctor',
|
|
65
|
+
{
|
|
66
|
+
description: 'run readiness checks and report remedies',
|
|
67
|
+
knownFlags: new Set([]),
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
'update',
|
|
72
|
+
{
|
|
73
|
+
description: 'upgrade mandrel to the newest published version',
|
|
74
|
+
knownFlags: new Set(['--dry-run', '--install-cmd']),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
'migrate',
|
|
79
|
+
{
|
|
80
|
+
description: 'apply version-keyed migrations for a version range',
|
|
81
|
+
knownFlags: new Set(['--from', '--to', '--dry-run']),
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
[
|
|
85
|
+
'explain',
|
|
86
|
+
{
|
|
87
|
+
description: 'print resolved config values and their sources',
|
|
88
|
+
knownFlags: new Set(['--json']),
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
'uninstall',
|
|
93
|
+
{
|
|
94
|
+
description: 'reverse a recorded install using the install ledger',
|
|
95
|
+
knownFlags: new Set(['--include-github', '--dry-run']),
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Helpers
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Read the installed mandrel version from the root package.json.
|
|
106
|
+
*
|
|
107
|
+
* @returns {string}
|
|
108
|
+
*/
|
|
109
|
+
function installedVersion() {
|
|
110
|
+
const req = createRequire(import.meta.url);
|
|
111
|
+
const manifest = req('../package.json');
|
|
112
|
+
return String(manifest.version);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Print the help screen listing all subcommands with descriptions.
|
|
117
|
+
*
|
|
118
|
+
* @param {(s: string) => void} [write]
|
|
119
|
+
*/
|
|
120
|
+
function printHelp(write = (s) => process.stdout.write(s)) {
|
|
121
|
+
const lines = ['Usage: mandrel <subcommand> [args]\n', '\nSubcommands:\n'];
|
|
122
|
+
for (const [name, meta] of SUBCOMMANDS) {
|
|
123
|
+
const pad = ' '.repeat(Math.max(1, 16 - name.length));
|
|
124
|
+
lines.push(` ${name}${pad}${meta.description}\n`);
|
|
125
|
+
}
|
|
126
|
+
lines.push(
|
|
127
|
+
'\nFlags:\n',
|
|
128
|
+
' --help, -h Print this help message\n',
|
|
129
|
+
' --version Print the installed version\n',
|
|
130
|
+
'\nRun `mandrel <subcommand> --help` for subcommand-specific flags.\n',
|
|
131
|
+
);
|
|
132
|
+
write(lines.join(''));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Suggest the closest known subcommand name for a typo (Levenshtein-1).
|
|
137
|
+
*
|
|
138
|
+
* @param {string} input
|
|
139
|
+
* @returns {string | undefined}
|
|
140
|
+
*/
|
|
141
|
+
function suggest(input) {
|
|
142
|
+
for (const name of SUBCOMMANDS.keys()) {
|
|
143
|
+
if (levenshtein(input, name) <= 2) return name;
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Compute Levenshtein edit distance between two strings (capped at 3 for
|
|
150
|
+
* performance — we only care about small distances).
|
|
151
|
+
*
|
|
152
|
+
* @param {string} a
|
|
153
|
+
* @param {string} b
|
|
154
|
+
* @returns {number}
|
|
155
|
+
*/
|
|
156
|
+
function levenshtein(a, b) {
|
|
157
|
+
if (a === b) return 0;
|
|
158
|
+
if (Math.abs(a.length - b.length) > 3) return 4;
|
|
159
|
+
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
160
|
+
for (let i = 0; i < a.length; i++) {
|
|
161
|
+
const curr = [i + 1];
|
|
162
|
+
for (let j = 0; j < b.length; j++) {
|
|
163
|
+
curr[j + 1] = Math.min(
|
|
164
|
+
curr[j] + 1,
|
|
165
|
+
prev[j + 1] + 1,
|
|
166
|
+
prev[j] + (a[i] === b[j] ? 0 : 1),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
prev.splice(0, prev.length, ...curr);
|
|
170
|
+
}
|
|
171
|
+
return prev[b.length];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Universal flags that all subcommands accept and that bypass per-subcommand
|
|
176
|
+
* flag validation. Subcommands may handle these themselves internally.
|
|
177
|
+
*/
|
|
178
|
+
const UNIVERSAL_FLAGS = new Set(['--help', '-h']);
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Validate the argv array against the known flags for a subcommand.
|
|
182
|
+
* Returns null when all flags are known; an error message string when an
|
|
183
|
+
* unknown flag is detected. Values following `=` or the next positional are
|
|
184
|
+
* allowed — only the flag name prefix is checked.
|
|
185
|
+
*
|
|
186
|
+
* Universal flags (--help, -h) are always allowed and bypass this check.
|
|
187
|
+
*
|
|
188
|
+
* @param {string} subName
|
|
189
|
+
* @param {Set<string>} knownFlags
|
|
190
|
+
* @param {string[]} argv
|
|
191
|
+
* @returns {string | null}
|
|
192
|
+
*/
|
|
193
|
+
function findUnknownFlag(subName, knownFlags, argv) {
|
|
194
|
+
for (const arg of argv) {
|
|
195
|
+
if (!arg.startsWith('-')) continue;
|
|
196
|
+
// Strip value portion for `--flag=value` form.
|
|
197
|
+
const flagName = arg.includes('=') ? arg.slice(0, arg.indexOf('=')) : arg;
|
|
198
|
+
if (UNIVERSAL_FLAGS.has(flagName)) continue;
|
|
199
|
+
if (!knownFlags.has(flagName)) {
|
|
200
|
+
const names = [...knownFlags].sort().join(', ');
|
|
201
|
+
const hint = names.length > 0 ? ` Known flags: ${names}\n` : '';
|
|
202
|
+
return (
|
|
203
|
+
`mandrel: unknown flag '${flagName}' for subcommand '${subName}'\n` +
|
|
204
|
+
hint
|
|
205
|
+
);
|
|
206
|
+
}
|
|
24
207
|
}
|
|
208
|
+
return null;
|
|
25
209
|
}
|
|
26
210
|
|
|
27
|
-
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
// Dispatch
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
const args = process.argv.slice(2);
|
|
216
|
+
const sub = args[0];
|
|
217
|
+
|
|
218
|
+
// --- Top-level flags (no subcommand) ---
|
|
219
|
+
if (!sub || sub === '--help' || sub === '-h') {
|
|
220
|
+
printHelp();
|
|
221
|
+
process.exit(0);
|
|
222
|
+
}
|
|
28
223
|
|
|
29
|
-
if (
|
|
30
|
-
|
|
224
|
+
if (sub === '--version') {
|
|
225
|
+
process.stdout.write(`${installedVersion()}\n`);
|
|
226
|
+
process.exit(0);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// --- Subcommand lookup ---
|
|
230
|
+
const meta = SUBCOMMANDS.get(sub);
|
|
231
|
+
if (!meta) {
|
|
232
|
+
const subcommandList = [...SUBCOMMANDS.keys()].join(', ');
|
|
233
|
+
const hint = suggest(sub);
|
|
234
|
+
const didYouMean = hint ? `\n Did you mean '${hint}'?` : '';
|
|
235
|
+
process.stderr.write(
|
|
236
|
+
`mandrel: unknown subcommand '${sub}'${didYouMean}\n` +
|
|
237
|
+
` Available subcommands: ${subcommandList}\n`,
|
|
238
|
+
);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// --- Unknown-flag rejection ---
|
|
243
|
+
const subArgv = args.slice(1);
|
|
244
|
+
const unknownFlagError = findUnknownFlag(sub, meta.knownFlags, subArgv);
|
|
245
|
+
if (unknownFlagError) {
|
|
246
|
+
process.stderr.write(unknownFlagError);
|
|
31
247
|
process.exit(1);
|
|
32
248
|
}
|
|
33
249
|
|
|
250
|
+
// --- Load and dispatch ---
|
|
34
251
|
const subFile = path.resolve(__dirname, '..', 'lib', 'cli', `${sub}.js`);
|
|
35
252
|
const subFileUrl = pathToFileURL(subFile).href;
|
|
36
253
|
|
|
@@ -39,7 +256,9 @@ try {
|
|
|
39
256
|
mod = await import(subFileUrl);
|
|
40
257
|
} catch (err) {
|
|
41
258
|
if (err.code === 'ERR_MODULE_NOT_FOUND' && err.message.includes(subFile)) {
|
|
42
|
-
|
|
259
|
+
process.stderr.write(
|
|
260
|
+
`mandrel: subcommand '${sub}' module not found — this is a bug\n`,
|
|
261
|
+
);
|
|
43
262
|
process.exit(1);
|
|
44
263
|
}
|
|
45
264
|
// Re-throw broken-module errors so they are visible rather than masked.
|
|
@@ -53,4 +272,4 @@ if (typeof mod.default !== 'function') {
|
|
|
53
272
|
process.exit(1);
|
|
54
273
|
}
|
|
55
274
|
|
|
56
|
-
await mod.default(
|
|
275
|
+
await mod.default(subArgv);
|