@limina-labs/momentum 0.30.2

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.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +214 -0
  3. package/adapters/antigravity/adapter.js +196 -0
  4. package/adapters/antigravity/hooks.json +39 -0
  5. package/adapters/antigravity/instructions/AGENTS.md +638 -0
  6. package/adapters/antigravity/instructions/header.md +3 -0
  7. package/adapters/antigravity/instructions/surfaces.md +153 -0
  8. package/adapters/antigravity/instructions/vars.json +1 -0
  9. package/adapters/antigravity/scripts/antigravity-hook-adapter.sh +147 -0
  10. package/adapters/antigravity/skills/momentum-orient/SKILL.md +29 -0
  11. package/adapters/antigravity/skills/momentum-reviewer-architecture/SKILL.md +39 -0
  12. package/adapters/antigravity/skills/momentum-reviewer-qa/SKILL.md +38 -0
  13. package/adapters/antigravity/skills/momentum-reviewer-security/SKILL.md +42 -0
  14. package/adapters/antigravity/skills/swarm-supervisor/SKILL.md +75 -0
  15. package/adapters/antigravity/workflows/continue.md +39 -0
  16. package/adapters/antigravity/workflows/dispatch.md +49 -0
  17. package/adapters/antigravity/workflows/handoff.md +38 -0
  18. package/adapters/antigravity/workflows/review-code.md +84 -0
  19. package/adapters/antigravity/workflows/scout.md +42 -0
  20. package/adapters/antigravity/workflows/swarm.md +203 -0
  21. package/adapters/claude-code/adapter.js +148 -0
  22. package/adapters/claude-code/commands/continue.md +82 -0
  23. package/adapters/claude-code/commands/dispatch.md +154 -0
  24. package/adapters/claude-code/commands/handoff.md +85 -0
  25. package/adapters/claude-code/commands/review-code.md +194 -0
  26. package/adapters/claude-code/commands/scout.md +119 -0
  27. package/adapters/claude-code/commands/swarm.md +242 -0
  28. package/adapters/claude-code/instructions/header.md +3 -0
  29. package/adapters/claude-code/instructions/vars.json +1 -0
  30. package/adapters/claude-code/settings.json +36 -0
  31. package/adapters/codex/adapter.js +225 -0
  32. package/adapters/codex/agents/momentum-reviewer-architecture.toml +57 -0
  33. package/adapters/codex/agents/momentum-reviewer-qa.toml +52 -0
  34. package/adapters/codex/agents/momentum-reviewer-security.toml +57 -0
  35. package/adapters/codex/agents/swarm-supervisor.toml +76 -0
  36. package/adapters/codex/commands/continue.md +40 -0
  37. package/adapters/codex/commands/dispatch.md +111 -0
  38. package/adapters/codex/commands/handoff.md +57 -0
  39. package/adapters/codex/commands/scout.md +90 -0
  40. package/adapters/codex/commands/swarm.md +334 -0
  41. package/adapters/codex/hooks.json +36 -0
  42. package/adapters/codex/instructions/AGENTS.md +691 -0
  43. package/adapters/codex/instructions/header.md +3 -0
  44. package/adapters/codex/instructions/surfaces.md +206 -0
  45. package/adapters/codex/instructions/vars.json +1 -0
  46. package/adapters/codex/skills/momentum-orient/SKILL.md +29 -0
  47. package/adapters/opencode/adapter.js +224 -0
  48. package/adapters/opencode/agents/momentum-reviewer-architecture.md +54 -0
  49. package/adapters/opencode/agents/momentum-reviewer-qa.md +48 -0
  50. package/adapters/opencode/agents/momentum-reviewer-security.md +48 -0
  51. package/adapters/opencode/agents/swarm-supervisor.md +51 -0
  52. package/adapters/opencode/commands/continue.md +40 -0
  53. package/adapters/opencode/commands/dispatch.md +111 -0
  54. package/adapters/opencode/commands/handoff.md +57 -0
  55. package/adapters/opencode/commands/review-code.md +194 -0
  56. package/adapters/opencode/commands/scout.md +90 -0
  57. package/adapters/opencode/commands/swarm.md +334 -0
  58. package/adapters/opencode/instructions/AGENTS.md +604 -0
  59. package/adapters/opencode/instructions/header.md +3 -0
  60. package/adapters/opencode/instructions/surfaces.md +119 -0
  61. package/adapters/opencode/instructions/vars.json +1 -0
  62. package/adapters/opencode/plugins/momentum.js +219 -0
  63. package/adapters/opencode/skills/momentum-lanes/SKILL.md +30 -0
  64. package/adapters/opencode/skills/momentum-orient/SKILL.md +29 -0
  65. package/adapters/opencode/skills/momentum-track/SKILL.md +28 -0
  66. package/adapters/opencode/skills/momentum-validate/SKILL.md +31 -0
  67. package/bin/antigravity.js +112 -0
  68. package/bin/ecosystem.js +1302 -0
  69. package/bin/lanes.js +300 -0
  70. package/bin/momentum.js +1857 -0
  71. package/bin/okf.js +72 -0
  72. package/bin/orchestration-commands.js +305 -0
  73. package/bin/state-commands.js +218 -0
  74. package/bin/swarm.js +1104 -0
  75. package/bin/waves.js +147 -0
  76. package/core/adapter-capabilities.md +112 -0
  77. package/core/adapter-parity-matrix.md +170 -0
  78. package/core/commands/brainstorm-idea.md +98 -0
  79. package/core/commands/brainstorm-phase.md +129 -0
  80. package/core/commands/complete-phase.md +130 -0
  81. package/core/commands/ecosystem.md +161 -0
  82. package/core/commands/hotfix.md +64 -0
  83. package/core/commands/initiative.md +119 -0
  84. package/core/commands/lanes.md +94 -0
  85. package/core/commands/log.md +45 -0
  86. package/core/commands/migrate.md +70 -0
  87. package/core/commands/review.md +31 -0
  88. package/core/commands/session.md +79 -0
  89. package/core/commands/start-phase.md +161 -0
  90. package/core/commands/start-project.md +142 -0
  91. package/core/commands/sync-docs.md +81 -0
  92. package/core/commands/systematic-debug.md +46 -0
  93. package/core/commands/track.md +54 -0
  94. package/core/commands/validate.md +96 -0
  95. package/core/ecosystem/layout.md +128 -0
  96. package/core/ecosystem/lib/index.js +245 -0
  97. package/core/ecosystem/lib/initiative.js +258 -0
  98. package/core/ecosystem/lib/pointer.js +198 -0
  99. package/core/ecosystem/lib/state.js +295 -0
  100. package/core/ecosystem/schema/ecosystem.schema.json +85 -0
  101. package/core/ecosystem/schema/initiative.schema.json +61 -0
  102. package/core/ecosystem/scripts/session-append.sh +167 -0
  103. package/core/ecosystem/templates/ecosystem-agents.md +79 -0
  104. package/core/ecosystem/templates/ecosystem-claude.md +79 -0
  105. package/core/ecosystem/templates/ecosystem-settings-claude.json +14 -0
  106. package/core/ecosystem/templates/initiative-template.md +50 -0
  107. package/core/engines/subagent-dispatch.md +80 -0
  108. package/core/git-hooks/commit-msg +14 -0
  109. package/core/git-hooks/contract.js +176 -0
  110. package/core/git-hooks/pre-push +15 -0
  111. package/core/git-hooks/run-check.js +160 -0
  112. package/core/instructions/legacy-project-md-hashes.json +12 -0
  113. package/core/instructions/navigation.md +14 -0
  114. package/core/instructions/rules-body.md +454 -0
  115. package/core/lanes/lib/board.js +161 -0
  116. package/core/lanes/lib/land.js +301 -0
  117. package/core/lanes/lib/signals.js +207 -0
  118. package/core/lanes/lib/state.js +325 -0
  119. package/core/lib/frontmatter.js +163 -0
  120. package/core/lib/okf-migrate.js +374 -0
  121. package/core/lib/okf-types.js +61 -0
  122. package/core/lifecycle-contract.md +59 -0
  123. package/core/orchestration/capability-routing.js +120 -0
  124. package/core/orchestration/continue.js +102 -0
  125. package/core/orchestration/dispatch.js +366 -0
  126. package/core/orchestration/events.js +197 -0
  127. package/core/orchestration/handoff.js +317 -0
  128. package/core/orchestration/index.js +50 -0
  129. package/core/orchestration/run-artifact.js +93 -0
  130. package/core/orchestration/scout.js +327 -0
  131. package/core/orchestration/session-log.js +123 -0
  132. package/core/orchestration/tracking.js +180 -0
  133. package/core/orchestration/types.js +131 -0
  134. package/core/scripts/brainstorm-gate.sh +157 -0
  135. package/core/scripts/check-history-reminder.sh +110 -0
  136. package/core/scripts/sessionstart-handoff.sh +180 -0
  137. package/core/specs-templates/CLAUDE.md +484 -0
  138. package/core/specs-templates/README.md +32 -0
  139. package/core/specs-templates/specs/README.md +30 -0
  140. package/core/specs-templates/specs/adhoc/README.md +31 -0
  141. package/core/specs-templates/specs/adhoc/_TEMPLATE.md +29 -0
  142. package/core/specs-templates/specs/architecture/ecosystem.md +214 -0
  143. package/core/specs-templates/specs/backlog/backlog.md +46 -0
  144. package/core/specs-templates/specs/backlog/details/.gitkeep +0 -0
  145. package/core/specs-templates/specs/changelog/.gitkeep +0 -0
  146. package/core/specs-templates/specs/decisions/0000-template.md +33 -0
  147. package/core/specs-templates/specs/decisions/README.md +29 -0
  148. package/core/specs-templates/specs/decisions/impact-map.md +14 -0
  149. package/core/specs-templates/specs/decisions/index.md +4 -0
  150. package/core/specs-templates/specs/index.md +16 -0
  151. package/core/specs-templates/specs/phases/README.md +41 -0
  152. package/core/specs-templates/specs/phases/index.md +6 -0
  153. package/core/specs-templates/specs/planning/roadmap.md +23 -0
  154. package/core/specs-templates/specs/status.md +70 -0
  155. package/core/specs-templates/specs/vision/principles.md +16 -0
  156. package/core/specs-templates/specs/vision/project-charter.md +35 -0
  157. package/core/specs-templates/specs/vision/success-criteria.md +19 -0
  158. package/core/swarm/absorb.js +294 -0
  159. package/core/swarm/conductor.js +499 -0
  160. package/core/swarm/focus.js +151 -0
  161. package/core/swarm/inbox.js +222 -0
  162. package/core/swarm/join.js +138 -0
  163. package/core/swarm/lib/board.js +178 -0
  164. package/core/swarm/lib/brief.js +157 -0
  165. package/core/swarm/lib/git-sha-cache.js +71 -0
  166. package/core/swarm/lib/incremental-log.js +117 -0
  167. package/core/swarm/lib/manifest.js +578 -0
  168. package/core/swarm/lib/pre-merge.js +112 -0
  169. package/core/swarm/lib/saga.js +146 -0
  170. package/core/swarm/lib/sessions.js +83 -0
  171. package/core/swarm/lib/tokens.js +255 -0
  172. package/core/swarm/lib/wave-ordering.js +55 -0
  173. package/core/swarm/schema/board.schema.json +54 -0
  174. package/core/swarm/schema/contract.schema.json +56 -0
  175. package/core/swarm/schema/dispatch-run.schema.json +70 -0
  176. package/core/swarm/schema/manifest.schema.json +234 -0
  177. package/core/swarm/schema/signal.schema.json +61 -0
  178. package/core/swarm/signals.js +296 -0
  179. package/core/swarm/supervise.md +81 -0
  180. package/core/waves/lib/plan-graph.js +175 -0
  181. package/core/waves/lib/waves.js +83 -0
  182. package/package.json +46 -0
package/bin/lanes.js ADDED
@@ -0,0 +1,300 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * `momentum lanes` — single-repo lane registry CLI (Phase 21b, FEAT-026/027).
5
+ *
6
+ * Subcommand modules load lazily so groups of Phase 21b could ship them
7
+ * independently (board → core/lanes/lib/board.js, signals →
8
+ * core/lanes/lib/signals.js, land → core/lanes/lib/land.js); a missing
9
+ * module reports "not shipped yet" instead of crashing the dispatcher.
10
+ *
11
+ * State: <git-common-dir>/momentum/lanes (see core/lanes/lib/state.js,
12
+ * ADR-0002). No daemon; every command computes from files.
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { spawnSync } = require('child_process');
18
+
19
+ const MOMENTUM_ROOT = path.resolve(__dirname, '..');
20
+ const state = require(path.join(MOMENTUM_ROOT, 'core', 'lanes', 'lib', 'state'));
21
+
22
+ // ─── small helpers ───────────────────────────────────────────────────────
23
+
24
+ function git(cwd, ...args) {
25
+ return spawnSync('git', args, { cwd, encoding: 'utf8' });
26
+ }
27
+
28
+ let failed = false;
29
+
30
+ function fail(msg) {
31
+ console.error(`✗ ${msg}`);
32
+ failed = true;
33
+ return null;
34
+ }
35
+
36
+ function parseFlags(argv) {
37
+ const flags = {};
38
+ const positional = [];
39
+ for (let i = 0; i < argv.length; i++) {
40
+ const a = argv[i];
41
+ if (a === '--no-worktree' || a === '--rm-worktree' || a === '--execute' ||
42
+ a === '--force' || a === '--json' || a === '--ack-all') {
43
+ flags[a.slice(2)] = true;
44
+ } else if (a.startsWith('--')) {
45
+ flags[a.slice(2)] = argv[++i];
46
+ } else {
47
+ positional.push(a);
48
+ }
49
+ }
50
+ return { flags, positional };
51
+ }
52
+
53
+ function requireAnchor(cwd) {
54
+ const anchor = state.resolveAnchor(cwd);
55
+ if (!anchor) return fail('not inside a git repository');
56
+ return anchor;
57
+ }
58
+
59
+ function printOverlaps(warnings) {
60
+ for (const w of warnings) {
61
+ console.log(`⚠ touch overlap with lane '${w.laneId}': ${w.mine} ↔ ${w.theirs} (advisory — coordinate before landing)`);
62
+ }
63
+ }
64
+
65
+ // ─── open / done / close ─────────────────────────────────────────────────
66
+
67
+ function branchExists(cwd, branch) {
68
+ return git(cwd, 'rev-parse', '--verify', '--quiet', `refs/heads/${branch}`).status === 0;
69
+ }
70
+
71
+ /** Path of the worktree that already has `branch` checked out, or null. */
72
+ function worktreeFor(cwd, branch) {
73
+ const out = git(cwd, 'worktree', 'list', '--porcelain');
74
+ if (out.status !== 0) return null;
75
+ let current = null;
76
+ for (const line of out.stdout.split('\n')) {
77
+ if (line.startsWith('worktree ')) current = line.slice('worktree '.length);
78
+ if (line === `branch refs/heads/${branch}` && current) return current;
79
+ }
80
+ return null;
81
+ }
82
+
83
+ /** Fresh-worktree preflight (21a trial learnings) — warnings only. */
84
+ function preflight(worktree) {
85
+ const warnings = [];
86
+ const modes = git(worktree, 'ls-files', '-s');
87
+ if (modes.status === 0) {
88
+ const bad = modes.stdout.split('\n')
89
+ .filter((l) => l.trim().endsWith('.sh') && !l.startsWith('100755'));
90
+ if (bad.length) {
91
+ warnings.push(`${bad.length} committed *.sh without the exec bit — hook scripts will silently no-op in this worktree (BUG-012 class)`);
92
+ }
93
+ }
94
+ try {
95
+ const pkg = JSON.parse(fs.readFileSync(path.join(worktree, 'package.json'), 'utf8'));
96
+ const wanted = pkg.engines && pkg.engines.node;
97
+ if (wanted) {
98
+ const major = Number(process.versions.node.split('.')[0]);
99
+ const minMajor = Number((wanted.match(/(\d+)/) || [])[1]);
100
+ if (minMajor && major < minMajor) {
101
+ warnings.push(`node ${process.versions.node} < engines.node ${wanted}`);
102
+ }
103
+ }
104
+ } catch { /* no package.json — nothing to check */ }
105
+ return warnings;
106
+ }
107
+
108
+ function cmdOpen(cwd, argv) {
109
+ const { flags, positional } = parseFlags(argv);
110
+ const branch = positional[0];
111
+ if (!branch) return fail('usage: momentum lanes open <branch> [--path P] [--grade phase|quick-task|spike] [--touches a,b] [--from ref] [--no-worktree] [--note s]');
112
+
113
+ const anchor = requireAnchor(cwd);
114
+ if (!anchor) return;
115
+ const repoRoot = state.worktreeRoot(cwd);
116
+ const id = state.laneId(branch);
117
+ const planNode = state.inferPlanNode(branch, repoRoot);
118
+ const grade = flags.grade || state.defaultGrade(planNode);
119
+ const touches = flags.touches ? flags.touches.split(',').map((s) => s.trim()).filter(Boolean) : [];
120
+
121
+ // Substrate: reuse an existing checkout, else create a plain git worktree.
122
+ let worktree = null;
123
+ if (!flags['no-worktree']) {
124
+ worktree = worktreeFor(cwd, branch);
125
+ if (!worktree) {
126
+ const home = flags.path ||
127
+ path.join(path.dirname(repoRoot), `${path.basename(repoRoot)}.lanes`, id);
128
+ // ENH-050: a NEW lane branch bases on the integration branch (main)
129
+ // by default — basing on the invoking checkout's HEAD silently stacked
130
+ // lanes on whatever branch happened to be checked out. `--from HEAD`
131
+ // (or any explicit ref) remains available for deliberate stacking.
132
+ const preExisting = branchExists(cwd, branch);
133
+ let base = flags.from;
134
+ if (!base && !preExisting) {
135
+ const headRef = git(cwd, 'symbolic-ref', '--short', 'refs/remotes/origin/HEAD');
136
+ base = headRef.status === 0 && headRef.stdout.trim()
137
+ ? headRef.stdout.trim().replace(/^origin\//, '')
138
+ : (git(cwd, 'rev-parse', '--verify', 'main').status === 0 ? 'main' : 'HEAD');
139
+ }
140
+ const args = preExisting
141
+ ? ['worktree', 'add', home, branch]
142
+ : ['worktree', 'add', home, '-b', branch, base];
143
+ const res = git(cwd, ...args);
144
+ if (res.status !== 0) return fail(`git worktree add failed:\n${res.stderr}`);
145
+ worktree = home;
146
+ console.log(`✓ worktree created at ${home}${preExisting ? '' : ` (branched from ${base})`}`);
147
+ } else {
148
+ console.log(`✓ reusing existing checkout at ${worktree}`);
149
+ }
150
+ }
151
+
152
+ let lane;
153
+ try {
154
+ lane = state.createLane(anchor, {
155
+ id, branch, planNode, grade, touches,
156
+ worktree, note: flags.note || null,
157
+ });
158
+ } catch (err) {
159
+ return fail(err.message);
160
+ }
161
+
162
+ console.log(`✓ lane '${lane.id}' open — branch ${branch}, plan node ${planNode.type}${planNode.ref ? `:${planNode.ref}` : ''}, grade ${grade}`);
163
+ if (planNode.type === 'phase' && planNode.dirExists === false) {
164
+ console.log(` note: specs/phases/${branch}/ does not exist yet — sessions will fall back to status.md until it does`);
165
+ }
166
+
167
+ printOverlaps(state.overlapWarnings(anchor, id, touches));
168
+
169
+ if (worktree) {
170
+ for (const w of preflight(worktree)) console.log(`⚠ preflight: ${w}`);
171
+ }
172
+
173
+ // Substrate hints — detection only, never a dependency.
174
+ if (spawnSync('sh', ['-c', 'command -v treehouse'], { encoding: 'utf8' }).status === 0) {
175
+ console.log('ℹ treehouse detected — its worktree pools can make lane opens near-instant (optional)');
176
+ }
177
+ }
178
+
179
+ function cmdDone(cwd, argv) {
180
+ const { positional } = parseFlags(argv);
181
+ const id = positional[0];
182
+ if (!id) return fail('usage: momentum lanes done <lane-id>');
183
+ const anchor = requireAnchor(cwd);
184
+ if (!anchor) return;
185
+ try {
186
+ state.updateLane(anchor, id, { status: 'done', doneAt: new Date().toISOString() });
187
+ } catch (err) {
188
+ return fail(err.message);
189
+ }
190
+ const queue = state.listLanes(anchor)
191
+ .filter((l) => l.status === 'done')
192
+ .sort((a, b) => String(a.doneAt).localeCompare(String(b.doneAt)));
193
+ const pos = queue.findIndex((l) => l.id === id) + 1;
194
+ console.log(`✓ lane '${id}' marked done — position ${pos} of ${queue.length} in the landing queue`);
195
+ }
196
+
197
+ function cmdClose(cwd, argv) {
198
+ const { flags, positional } = parseFlags(argv);
199
+ const id = positional[0];
200
+ if (!id) return fail('usage: momentum lanes close <lane-id> [--rm-worktree]');
201
+ const anchor = requireAnchor(cwd);
202
+ if (!anchor) return;
203
+ let lane;
204
+ try {
205
+ lane = state.updateLane(anchor, id, { status: 'closed' });
206
+ } catch (err) {
207
+ return fail(err.message);
208
+ }
209
+ if (flags['rm-worktree'] && lane.worktree) {
210
+ const res = git(cwd, 'worktree', 'remove', lane.worktree, '--force');
211
+ if (res.status === 0) console.log(`✓ worktree removed: ${lane.worktree}`);
212
+ else console.log(`⚠ could not remove worktree (${res.stderr.trim()}) — remove manually`);
213
+ }
214
+ console.log(`✓ lane '${id}' closed`);
215
+ }
216
+
217
+ // ─── lazy subcommand modules ─────────────────────────────────────────────
218
+
219
+ function lazy(module, exportName) {
220
+ const file = path.join(MOMENTUM_ROOT, 'core', 'lanes', 'lib', `${module}.js`);
221
+ if (!fs.existsSync(file)) {
222
+ return () => fail(`'momentum lanes' subcommand backed by core/lanes/lib/${module}.js is not shipped yet`);
223
+ }
224
+ return require(file)[exportName];
225
+ }
226
+
227
+ // ─── help + dispatch ─────────────────────────────────────────────────────
228
+
229
+ const HELP = `momentum lanes — concurrent workstreams in one repo (Rule 15 mechanism)
230
+
231
+ Usage:
232
+ momentum lanes Board: every lane + queue pressure
233
+ momentum lanes open <branch> [flags] Register a lane (creates a git worktree
234
+ by default at ../<repo>.lanes/<id>)
235
+ --path <dir> worktree location --grade phase|quick-task|spike
236
+ --touches <a,b> intended touch paths (overlap warnings, advisory)
237
+ --from <ref> base for a new branch --no-worktree --note <s>
238
+ momentum lanes done <id> Mark ready to land (enters the queue)
239
+ momentum lanes close <id> [--rm-worktree]
240
+ momentum lanes queue Landing order + gate grades
241
+ momentum lanes signal <id> <pause|resume|redirect|kill|message> [text]
242
+ momentum lanes inbox <id> [--ack <n>|--ack-all]
243
+ momentum lanes land <id> [--into <ref>] [--execute] [--force] [--mark-landed]
244
+ --mark-landed bookkeeping only: record an out-of-band merge that
245
+ already happened (done + merged lane → landed)
246
+
247
+ State lives at <git-common-dir>/momentum/lanes — shared by all worktrees,
248
+ untracked, no daemon. Substrate: plain git worktrees by default; treehouse
249
+ pools are detected and suggested; GitButler virtual branches work too (skip
250
+ worktrees with --no-worktree). Landing follows the Rule 6 Landing Order with
251
+ Rule-14-graded evidence gates.`;
252
+
253
+ /** Dispatch a `momentum lanes` invocation. Returns the exit code. */
254
+ function runLanes(argv, cwd = process.cwd()) {
255
+ failed = false;
256
+ dispatch(argv, cwd);
257
+ return failed ? 1 : 0;
258
+ }
259
+
260
+ function dispatch(argv, cwd) {
261
+ const sub = argv[0];
262
+ const rest = argv.slice(1);
263
+ // Lazy subcommand modules signal failure by RETURNING 1 (they print
264
+ // their own message); inline commands use fail().
265
+ const delegate = (module, exportName, args) => {
266
+ if (lazy(module, exportName)(cwd, args) === 1) failed = true;
267
+ };
268
+ // A leading flag (e.g. `momentum lanes --json`) means the default board.
269
+ if (sub && sub.startsWith('--') && sub !== '--help') {
270
+ return delegate('board', 'cmdBoard', argv);
271
+ }
272
+ switch (sub) {
273
+ case undefined:
274
+ case 'board':
275
+ return delegate('board', 'cmdBoard', sub === 'board' ? rest : argv);
276
+ case 'queue':
277
+ return delegate('board', 'cmdQueue', rest);
278
+ case 'open':
279
+ return cmdOpen(cwd, rest);
280
+ case 'done':
281
+ return cmdDone(cwd, rest);
282
+ case 'close':
283
+ return cmdClose(cwd, rest);
284
+ case 'signal':
285
+ return delegate('signals', 'cmdSignal', rest);
286
+ case 'inbox':
287
+ return delegate('signals', 'cmdInbox', rest);
288
+ case 'land':
289
+ return delegate('land', 'cmdLand', rest);
290
+ case '--help':
291
+ case '-h':
292
+ case 'help':
293
+ console.log(HELP);
294
+ return;
295
+ default:
296
+ return fail(`unknown lanes subcommand: '${sub}' (see: momentum lanes help)`);
297
+ }
298
+ }
299
+
300
+ module.exports = { runLanes };