maestro-flow-one 0.1.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.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/bin/maestro-flow.js +730 -0
  4. package/claude/maestro-flow/SKILL.md +239 -0
  5. package/claude/maestro-flow/chains/templates.json +256 -0
  6. package/claude/maestro-flow/commands/learn/decompose.md +176 -0
  7. package/claude/maestro-flow/commands/learn/follow.md +167 -0
  8. package/claude/maestro-flow/commands/learn/investigate.md +221 -0
  9. package/claude/maestro-flow/commands/learn/retro.md +303 -0
  10. package/claude/maestro-flow/commands/learn/second-opinion.md +167 -0
  11. package/claude/maestro-flow/commands/lifecycle/amend.md +300 -0
  12. package/claude/maestro-flow/commands/lifecycle/analyze.md +126 -0
  13. package/claude/maestro-flow/commands/lifecycle/brainstorm.md +100 -0
  14. package/claude/maestro-flow/commands/lifecycle/composer.md +354 -0
  15. package/claude/maestro-flow/commands/lifecycle/execute.md +114 -0
  16. package/claude/maestro-flow/commands/lifecycle/fork.md +86 -0
  17. package/claude/maestro-flow/commands/lifecycle/init.md +78 -0
  18. package/claude/maestro-flow/commands/lifecycle/learn.md +140 -0
  19. package/claude/maestro-flow/commands/lifecycle/link-coordinate.md +71 -0
  20. package/claude/maestro-flow/commands/lifecycle/merge.md +61 -0
  21. package/claude/maestro-flow/commands/lifecycle/overlay.md +178 -0
  22. package/claude/maestro-flow/commands/lifecycle/plan.md +138 -0
  23. package/claude/maestro-flow/commands/lifecycle/player.md +404 -0
  24. package/claude/maestro-flow/commands/lifecycle/quick.md +56 -0
  25. package/claude/maestro-flow/commands/lifecycle/roadmap.md +164 -0
  26. package/claude/maestro-flow/commands/lifecycle/ui-design.md +93 -0
  27. package/claude/maestro-flow/commands/lifecycle/update.md +176 -0
  28. package/claude/maestro-flow/commands/lifecycle/verify.md +90 -0
  29. package/claude/maestro-flow/commands/manage/codebase-rebuild.md +75 -0
  30. package/claude/maestro-flow/commands/manage/codebase-refresh.md +57 -0
  31. package/claude/maestro-flow/commands/manage/harvest.md +94 -0
  32. package/claude/maestro-flow/commands/manage/issue-discover.md +77 -0
  33. package/claude/maestro-flow/commands/manage/issue.md +73 -0
  34. package/claude/maestro-flow/commands/manage/knowhow-capture.md +193 -0
  35. package/claude/maestro-flow/commands/manage/knowhow.md +77 -0
  36. package/claude/maestro-flow/commands/manage/learn.md +67 -0
  37. package/claude/maestro-flow/commands/manage/status.md +51 -0
  38. package/claude/maestro-flow/commands/manage/wiki.md +62 -0
  39. package/claude/maestro-flow/commands/milestone/audit.md +68 -0
  40. package/claude/maestro-flow/commands/milestone/complete.md +75 -0
  41. package/claude/maestro-flow/commands/milestone/release.md +96 -0
  42. package/claude/maestro-flow/commands/quality/auto-test.md +124 -0
  43. package/claude/maestro-flow/commands/quality/debug.md +115 -0
  44. package/claude/maestro-flow/commands/quality/refactor.md +55 -0
  45. package/claude/maestro-flow/commands/quality/retrospective.md +78 -0
  46. package/claude/maestro-flow/commands/quality/review.md +108 -0
  47. package/claude/maestro-flow/commands/quality/sync.md +51 -0
  48. package/claude/maestro-flow/commands/quality/test.md +103 -0
  49. package/claude/maestro-flow/commands/spec/add.md +49 -0
  50. package/claude/maestro-flow/commands/spec/load.md +51 -0
  51. package/claude/maestro-flow/commands/spec/remove.md +51 -0
  52. package/claude/maestro-flow/commands/spec/setup.md +51 -0
  53. package/claude/maestro-flow/commands/wiki/connect.md +62 -0
  54. package/claude/maestro-flow/commands/wiki/digest.md +69 -0
  55. package/codex/maestro-flow/SKILL.md +505 -0
  56. package/codex/maestro-flow/chains/templates.json +256 -0
  57. package/codex/maestro-flow/commands/learn/decompose.md +113 -0
  58. package/codex/maestro-flow/commands/learn/follow.md +83 -0
  59. package/codex/maestro-flow/commands/learn/investigate.md +83 -0
  60. package/codex/maestro-flow/commands/learn/retro.md +83 -0
  61. package/codex/maestro-flow/commands/learn/second-opinion.md +86 -0
  62. package/codex/maestro-flow/commands/lifecycle/amend.md +300 -0
  63. package/codex/maestro-flow/commands/lifecycle/analyze.md +483 -0
  64. package/codex/maestro-flow/commands/lifecycle/brainstorm.md +397 -0
  65. package/codex/maestro-flow/commands/lifecycle/composer.md +213 -0
  66. package/codex/maestro-flow/commands/lifecycle/execute.md +318 -0
  67. package/codex/maestro-flow/commands/lifecycle/fork.md +98 -0
  68. package/codex/maestro-flow/commands/lifecycle/init.md +134 -0
  69. package/codex/maestro-flow/commands/lifecycle/learn.md +80 -0
  70. package/codex/maestro-flow/commands/lifecycle/link-coordinate.md +257 -0
  71. package/codex/maestro-flow/commands/lifecycle/merge.md +69 -0
  72. package/codex/maestro-flow/commands/lifecycle/overlay.md +119 -0
  73. package/codex/maestro-flow/commands/lifecycle/plan.md +460 -0
  74. package/codex/maestro-flow/commands/lifecycle/player.md +323 -0
  75. package/codex/maestro-flow/commands/lifecycle/quick.md +124 -0
  76. package/codex/maestro-flow/commands/lifecycle/roadmap.md +468 -0
  77. package/codex/maestro-flow/commands/lifecycle/ui-design.md +135 -0
  78. package/codex/maestro-flow/commands/lifecycle/update.md +176 -0
  79. package/codex/maestro-flow/commands/lifecycle/verify.md +468 -0
  80. package/codex/maestro-flow/commands/manage/codebase-rebuild.md +347 -0
  81. package/codex/maestro-flow/commands/manage/codebase-refresh.md +66 -0
  82. package/codex/maestro-flow/commands/manage/harvest.md +91 -0
  83. package/codex/maestro-flow/commands/manage/issue-discover.md +431 -0
  84. package/codex/maestro-flow/commands/manage/issue.md +75 -0
  85. package/codex/maestro-flow/commands/manage/knowhow-capture.md +110 -0
  86. package/codex/maestro-flow/commands/manage/knowhow.md +95 -0
  87. package/codex/maestro-flow/commands/manage/learn.md +137 -0
  88. package/codex/maestro-flow/commands/manage/status.md +76 -0
  89. package/codex/maestro-flow/commands/manage/wiki.md +55 -0
  90. package/codex/maestro-flow/commands/milestone/audit.md +87 -0
  91. package/codex/maestro-flow/commands/milestone/complete.md +91 -0
  92. package/codex/maestro-flow/commands/milestone/release.md +70 -0
  93. package/codex/maestro-flow/commands/quality/auto-test.md +547 -0
  94. package/codex/maestro-flow/commands/quality/debug.md +334 -0
  95. package/codex/maestro-flow/commands/quality/refactor.md +151 -0
  96. package/codex/maestro-flow/commands/quality/retrospective.md +292 -0
  97. package/codex/maestro-flow/commands/quality/review.md +364 -0
  98. package/codex/maestro-flow/commands/quality/sync.md +111 -0
  99. package/codex/maestro-flow/commands/quality/test.md +498 -0
  100. package/codex/maestro-flow/commands/spec/add.md +101 -0
  101. package/codex/maestro-flow/commands/spec/load.md +77 -0
  102. package/codex/maestro-flow/commands/spec/remove.md +69 -0
  103. package/codex/maestro-flow/commands/spec/setup.md +75 -0
  104. package/codex/maestro-flow/commands/wiki/connect.md +73 -0
  105. package/codex/maestro-flow/commands/wiki/digest.md +87 -0
  106. package/package.json +24 -0
@@ -0,0 +1,730 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+
7
+ // --- Path resolution ---
8
+ // Package root: ../ relative to this script
9
+ const PKG_ROOT = path.join(__dirname, "..");
10
+ const VARIANTS = {
11
+ codex: path.join(PKG_ROOT, "codex", "maestro-flow"),
12
+ claude: path.join(PKG_ROOT, "claude", "maestro-flow"),
13
+ };
14
+ const DEFAULT_VARIANT = "codex";
15
+
16
+ // Resolve active variant: check installed first (project -> global), then package
17
+ function getSkillRoot(variant) {
18
+ if (variant && VARIANTS[variant]) return VARIANTS[variant];
19
+
20
+ const home = process.env.HOME || process.env.USERPROFILE;
21
+ // Check installed locations: project first, then global
22
+ const candidates = [
23
+ path.join(process.cwd(), ".codex", "skills", "maestro-flow"),
24
+ path.join(process.cwd(), ".claude", "skills", "maestro-flow"),
25
+ path.join(home, ".codex", "skills", "maestro-flow"),
26
+ path.join(home, ".claude", "skills", "maestro-flow"),
27
+ ];
28
+ for (const c of candidates) {
29
+ if (fs.existsSync(path.join(c, "SKILL.md"))) return c;
30
+ }
31
+ return VARIANTS[DEFAULT_VARIANT];
32
+ }
33
+
34
+ let SKILL_ROOT = getSkillRoot();
35
+ let COMMANDS_DIR = path.join(SKILL_ROOT, "commands");
36
+ let CHAINS_FILE = path.join(SKILL_ROOT, "chains", "templates.json");
37
+
38
+ function setVariant(variant) {
39
+ SKILL_ROOT = getSkillRoot(variant);
40
+ COMMANDS_DIR = path.join(SKILL_ROOT, "commands");
41
+ CHAINS_FILE = path.join(SKILL_ROOT, "chains", "templates.json");
42
+ }
43
+
44
+ // --- Name mapping ---
45
+ const PREFIX_CATEGORY = [
46
+ ["maestro-milestone-", "milestone"],
47
+ ["maestro-ralph-", "lifecycle"],
48
+ ["maestro-", "lifecycle"],
49
+ ["quality-", "quality"],
50
+ ["manage-", "manage"],
51
+ ["learn-", "learn"],
52
+ ["spec-", "spec"],
53
+ ["wiki-", "wiki"],
54
+ ];
55
+
56
+ // --- Helpers ---
57
+
58
+ function parseFrontmatter(filepath) {
59
+ const text = fs.readFileSync(filepath, "utf-8");
60
+ const match = text.match(/^---\s*\n([\s\S]*?)\n---/);
61
+ if (!match) return {};
62
+ const fm = {};
63
+ for (const line of match[1].split("\n")) {
64
+ const trimmed = line.trim();
65
+ if (trimmed.startsWith("-") || trimmed.startsWith("#")) continue;
66
+ const idx = trimmed.indexOf(":");
67
+ if (idx > 0) {
68
+ const key = trimmed.slice(0, idx).trim();
69
+ const val = trimmed.slice(idx + 1).trim().replace(/^["']|["']$/g, "");
70
+ fm[key] = val;
71
+ }
72
+ }
73
+ return fm;
74
+ }
75
+
76
+ function extractPurpose(filepath) {
77
+ const text = fs.readFileSync(filepath, "utf-8");
78
+ const match = text.match(/<purpose>\s*\n([\s\S]*?)<\/purpose>/);
79
+ return match ? match[1].trim().slice(0, 200) : "";
80
+ }
81
+
82
+ function resolveCommand(name) {
83
+ for (const [prefix, category] of PREFIX_CATEGORY) {
84
+ if (name.startsWith(prefix)) {
85
+ let filename = name.slice(prefix.length) + ".md";
86
+ if (!filename || filename === ".md") filename = name + ".md";
87
+ const p = path.join(COMMANDS_DIR, category, filename);
88
+ if (fs.existsSync(p)) return p;
89
+ }
90
+ }
91
+ // Fallback: scan all categories
92
+ if (fs.existsSync(COMMANDS_DIR)) {
93
+ for (const cat of fs.readdirSync(COMMANDS_DIR)) {
94
+ const catDir = path.join(COMMANDS_DIR, cat);
95
+ if (!fs.statSync(catDir).isDirectory()) continue;
96
+ for (const f of fs.readdirSync(catDir).filter((x) => x.endsWith(".md"))) {
97
+ const fm = parseFrontmatter(path.join(catDir, f));
98
+ if (fm.name === name) return path.join(catDir, f);
99
+ }
100
+ }
101
+ }
102
+ return null;
103
+ }
104
+
105
+ function loadChains() {
106
+ if (!fs.existsSync(CHAINS_FILE)) return {};
107
+ return JSON.parse(fs.readFileSync(CHAINS_FILE, "utf-8"));
108
+ }
109
+
110
+ function findSessions(workDir, all) {
111
+ const maestroDir = path.join(workDir, ".workflow", ".maestro");
112
+ if (!fs.existsSync(maestroDir)) return [];
113
+ const sessions = [];
114
+ for (const d of fs.readdirSync(maestroDir).sort().reverse()) {
115
+ const statusFile = path.join(maestroDir, d, "status.json");
116
+ if (!fs.existsSync(statusFile)) continue;
117
+ const data = JSON.parse(fs.readFileSync(statusFile, "utf-8"));
118
+ data._path = statusFile;
119
+ if (data.source === "flow" || all) sessions.push(data);
120
+ }
121
+ return sessions;
122
+ }
123
+
124
+ function writeSession(session) {
125
+ const p = session._path;
126
+ const data = { ...session };
127
+ delete data._path;
128
+ data.updated_at = new Date().toISOString();
129
+ fs.writeFileSync(p, JSON.stringify(data, null, 2), "utf-8");
130
+ }
131
+
132
+ // --- Commands ---
133
+
134
+ function cmdList(args) {
135
+ const categoryFilter = args.category;
136
+ if (!fs.existsSync(COMMANDS_DIR)) {
137
+ console.log("Commands directory not found:", COMMANDS_DIR);
138
+ return;
139
+ }
140
+ const categories = fs
141
+ .readdirSync(COMMANDS_DIR)
142
+ .filter((d) => fs.statSync(path.join(COMMANDS_DIR, d)).isDirectory())
143
+ .sort()
144
+ .filter((c) => !categoryFilter || c === categoryFilter);
145
+
146
+ let total = 0;
147
+ console.log(
148
+ `${"Category".padEnd(14)} ${"Command".padEnd(30)} Description`
149
+ );
150
+ console.log(`${"-".repeat(13)} ${"-".repeat(29)} ${"-".repeat(40)}`);
151
+
152
+ for (const cat of categories) {
153
+ const catDir = path.join(COMMANDS_DIR, cat);
154
+ const files = fs.readdirSync(catDir).filter((f) => f.endsWith(".md")).sort();
155
+ for (const f of files) {
156
+ const fm = parseFrontmatter(path.join(catDir, f));
157
+ const name = fm.name || f.replace(".md", "");
158
+ const desc = (fm.description || "").slice(0, 50);
159
+ console.log(`${cat.padEnd(14)} ${name.padEnd(30)} ${desc}`);
160
+ total++;
161
+ }
162
+ }
163
+ console.log(`\nTotal: ${total} commands`);
164
+ }
165
+
166
+ function cmdShow(args) {
167
+ const cmdPath = resolveCommand(args.name);
168
+ if (!cmdPath) {
169
+ console.log(`Command not found: ${args.name}`);
170
+ process.exit(1);
171
+ }
172
+ const fm = parseFrontmatter(cmdPath);
173
+ const purpose = extractPurpose(cmdPath);
174
+ const relPath = path.relative(SKILL_ROOT, cmdPath);
175
+
176
+ console.log(`+${"-".repeat(50)}+`);
177
+ console.log(`| ${(fm.name || args.name).padEnd(48)} |`);
178
+ console.log(`| ${(fm.description || "").slice(0, 48).padEnd(48)} |`);
179
+ console.log(`+${"-".repeat(50)}+`);
180
+ if (fm["argument-hint"]) {
181
+ console.log(`| Args: ${fm["argument-hint"].slice(0, 42).padEnd(42)} |`);
182
+ }
183
+ console.log(`| File: ${relPath.slice(0, 42).padEnd(42)} |`);
184
+ if (purpose) {
185
+ console.log(`+${"-".repeat(50)}+`);
186
+ // Simple word wrap
187
+ const words = purpose.split(/\s+/);
188
+ let line = "";
189
+ for (const w of words) {
190
+ if (line.length + w.length + 1 > 48) {
191
+ console.log(`| ${line.padEnd(48)} |`);
192
+ line = w;
193
+ } else {
194
+ line = line ? line + " " + w : w;
195
+ }
196
+ }
197
+ if (line) console.log(`| ${line.padEnd(48)} |`);
198
+ }
199
+ console.log(`+${"-".repeat(50)}+`);
200
+ }
201
+
202
+ function cmdChains(args) {
203
+ const data = loadChains();
204
+ let templates = data.templates || {};
205
+ if (args.category) {
206
+ templates = Object.fromEntries(
207
+ Object.entries(templates).filter(([, v]) => v.category === args.category)
208
+ );
209
+ }
210
+ console.log(
211
+ `${"Template".padEnd(25)} ${"Category".padEnd(16)} ${"Steps".padStart(5)} Description`
212
+ );
213
+ console.log(
214
+ `${"-".repeat(24)} ${"-".repeat(15)} ${"-".repeat(5)} ${"-".repeat(35)}`
215
+ );
216
+ for (const [name, tmpl] of Object.entries(templates)) {
217
+ const steps = (tmpl.steps || []).length;
218
+ const desc = (tmpl.description || "").slice(0, 35);
219
+ const cat = tmpl.category || "";
220
+ console.log(
221
+ `${name.padEnd(25)} ${cat.padEnd(16)} ${String(steps).padStart(5)} ${desc}`
222
+ );
223
+ }
224
+ console.log(`\nTotal: ${Object.keys(templates).length} templates`);
225
+ }
226
+
227
+ function cmdChain(args) {
228
+ const data = loadChains();
229
+ const tmpl = (data.templates || {})[args.name];
230
+ if (!tmpl) {
231
+ console.log(`Chain template not found: ${args.name}`);
232
+ process.exit(1);
233
+ }
234
+ console.log(`\n Chain: ${args.name}`);
235
+ console.log(` Category: ${tmpl.category || ""}`);
236
+ console.log(` Description: ${tmpl.description || ""}`);
237
+ console.log(` Triggers: ${(tmpl.triggers || []).join(", ")}`);
238
+ console.log(`\n Steps:`);
239
+ for (let i = 0; i < (tmpl.steps || []).length; i++) {
240
+ const s = tmpl.steps[i];
241
+ const badge =
242
+ s.type === "external" ? "*" : s.type === "decision" ? ">" : " ";
243
+ console.log(
244
+ ` ${i}. ${badge} ${s.cmd} ${s.args || ""} [${s.type}]`
245
+ );
246
+ }
247
+ console.log();
248
+ }
249
+
250
+ function cmdSuggest(args) {
251
+ const intent = args.intent.toLowerCase();
252
+ const data = loadChains();
253
+ const matches = [];
254
+
255
+ for (const [name, tmpl] of Object.entries(data.templates || {})) {
256
+ let score = 0;
257
+ const matched = [];
258
+ for (const trigger of tmpl.triggers || []) {
259
+ if (intent.includes(trigger.toLowerCase())) {
260
+ score += trigger.length;
261
+ matched.push(trigger);
262
+ }
263
+ }
264
+ if (score > 0) matches.push({ score, name, tmpl, matched });
265
+ }
266
+
267
+ matches.sort((a, b) => b.score - a.score);
268
+
269
+ if (!matches.length) {
270
+ // Check single_commands
271
+ for (const [key, cmd] of Object.entries(data.single_commands || {})) {
272
+ if (intent.includes(key)) {
273
+ console.log(`\nSuggested single command:`);
274
+ console.log(` ${cmd} (match: ${key})`);
275
+ return;
276
+ }
277
+ }
278
+ console.log("No matching chain found. Try: maestro-flow chains");
279
+ return;
280
+ }
281
+
282
+ console.log(`\nSuggested chains for: "${args.intent}"`);
283
+ for (let i = 0; i < Math.min(3, matches.length); i++) {
284
+ const m = matches[i];
285
+ const steps = (m.tmpl.steps || []).length;
286
+ console.log(
287
+ ` ${i + 1}. ${m.name.padEnd(25)} ${(m.tmpl.description || "").padEnd(35)} (${steps} steps, match: ${m.matched.join(", ")})`
288
+ );
289
+ }
290
+ }
291
+
292
+ function cmdResolve(args) {
293
+ const p = resolveCommand(args.name);
294
+ if (p) {
295
+ console.log(p);
296
+ } else {
297
+ console.error(`NOT_FOUND: ${args.name}`);
298
+ process.exit(1);
299
+ }
300
+ }
301
+
302
+ function cmdStatus(args) {
303
+ const sessions = findSessions(process.cwd(), true);
304
+ const filtered = args.sessionId
305
+ ? sessions.filter((s) => s.session_id === args.sessionId)
306
+ : sessions.filter((s) => s.status === "running");
307
+ const list = filtered.length ? filtered : sessions;
308
+
309
+ if (!list.length) {
310
+ console.log("No flow sessions found.");
311
+ return;
312
+ }
313
+
314
+ const session = list[0];
315
+ const steps = session.steps || [];
316
+ const total = steps.length;
317
+ const completed = steps.filter((s) => s.status === "completed").length;
318
+
319
+ console.log(`\n Session: ${session.session_id || "?"}`);
320
+ console.log(` Status: ${session.status || "?"}`);
321
+ console.log(` Source: ${session.source || "?"}`);
322
+ console.log(` Chain: ${session.chain_name || "?"} (${total} steps)`);
323
+ console.log(` Intent: ${(session.intent || "").slice(0, 60)}`);
324
+ if (session.phase) console.log(` Phase: ${session.phase}`);
325
+ console.log(
326
+ ` Progress: ${completed}/${total} (${total ? Math.round((completed / total) * 100) : 0}%)`
327
+ );
328
+ console.log(`\n Steps:`);
329
+
330
+ for (const step of steps) {
331
+ const icon = { completed: "done", running: " >> ", failed: "FAIL", skipped: "skip", pending: " " }[step.status] || " ";
332
+ const badge = step.type === "external" ? "*" : step.type === "decision" ? ">" : " ";
333
+ console.log(
334
+ ` [${icon}] ${step.index}. ${badge} ${step.skill} ${step.args || ""} [${step.type}]`
335
+ );
336
+ }
337
+ console.log();
338
+ }
339
+
340
+ function cmdSessions(args) {
341
+ const sessions = findSessions(process.cwd(), !!args.all);
342
+ if (!sessions.length) {
343
+ console.log("No sessions found.");
344
+ return;
345
+ }
346
+ console.log(
347
+ `${"Session ID".padEnd(30)} ${"Status".padEnd(12)} ${"Source".padEnd(8)} ${"Chain".padEnd(20)} Intent`
348
+ );
349
+ console.log(
350
+ `${"-".repeat(29)} ${"-".repeat(11)} ${"-".repeat(7)} ${"-".repeat(19)} ${"-".repeat(30)}`
351
+ );
352
+ for (const s of sessions.slice(0, 20)) {
353
+ console.log(
354
+ `${(s.session_id || "?").padEnd(30)} ${(s.status || "?").padEnd(12)} ${(s.source || "?").padEnd(8)} ${(s.chain_name || "?").slice(0, 19).padEnd(20)} ${(s.intent || "").slice(0, 30)}`
355
+ );
356
+ }
357
+ }
358
+
359
+ function cmdStep(args) {
360
+ const sessions = findSessions(process.cwd(), true);
361
+ const session = sessions.find((s) => s.session_id === args.sessionId);
362
+ if (!session) {
363
+ console.log(`Session not found: ${args.sessionId}`);
364
+ process.exit(1);
365
+ }
366
+ const idx = parseInt(args.index);
367
+ const steps = session.steps || [];
368
+ if (idx < 0 || idx >= steps.length) {
369
+ console.log(`Invalid step index: ${idx} (0-${steps.length - 1})`);
370
+ process.exit(1);
371
+ }
372
+ const valid = ["pending", "running", "completed", "failed", "skipped"];
373
+ if (!valid.includes(args.status)) {
374
+ console.log(`Invalid status: ${args.status}. Use: ${valid.join(", ")}`);
375
+ process.exit(1);
376
+ }
377
+ steps[idx].status = args.status;
378
+ const now = new Date().toISOString();
379
+ if (args.status === "running") steps[idx].started_at = now;
380
+ else if (["completed", "failed", "skipped"].includes(args.status))
381
+ steps[idx].completed_at = now;
382
+
383
+ writeSession(session);
384
+ console.log(`Step ${idx} -> ${args.status}`);
385
+ }
386
+
387
+ function cmdNext(args) {
388
+ const sessions = findSessions(process.cwd(), true);
389
+ const filtered = args.sessionId
390
+ ? sessions.filter((s) => s.session_id === args.sessionId)
391
+ : sessions.filter((s) => s.status === "running");
392
+
393
+ if (!filtered.length) {
394
+ console.log("NO_SESSION");
395
+ return;
396
+ }
397
+
398
+ const session = filtered[0];
399
+ const steps = session.steps || [];
400
+ const total = steps.length;
401
+
402
+ const pending = steps.find((s) => s.status === "pending");
403
+ if (!pending) {
404
+ session.status = "completed";
405
+ writeSession(session);
406
+ console.log("SESSION_COMPLETE");
407
+ return;
408
+ }
409
+
410
+ const idx = pending.index;
411
+ pending.status = "running";
412
+ pending.started_at = new Date().toISOString();
413
+ session.current_step = idx;
414
+ writeSession(session);
415
+
416
+ console.log(`STEP: ${idx}/${total - 1}`);
417
+ console.log(`TYPE: ${pending.type}`);
418
+ console.log(`SKILL: ${pending.skill}`);
419
+ console.log(`ARGS: ${pending.args || ""}`);
420
+
421
+ if (pending.type === "decision") {
422
+ console.log(`DECISION: ${pending.decision || ""}`);
423
+ console.log(`RETRY: ${pending.retry_count || 0}/${pending.max_retries || 2}`);
424
+ console.log("---COMMAND---");
425
+ console.log("(decision node - evaluate via Agent)");
426
+ return;
427
+ }
428
+
429
+ const cmdPath = resolveCommand(pending.skill);
430
+ if (!cmdPath) {
431
+ console.log(`RESOLVE_FAILED: ${pending.skill}`);
432
+ return;
433
+ }
434
+
435
+ console.log(`PATH: ${cmdPath}`);
436
+ console.log("---COMMAND---");
437
+ console.log(fs.readFileSync(cmdPath, "utf-8"));
438
+ }
439
+
440
+ function cmdDone(args) {
441
+ const sessions = findSessions(process.cwd(), true);
442
+ const filtered = args.sessionId
443
+ ? sessions.filter((s) => s.session_id === args.sessionId)
444
+ : sessions.filter((s) => s.status === "running");
445
+
446
+ if (!filtered.length) {
447
+ console.log("NO_SESSION");
448
+ return;
449
+ }
450
+
451
+ const session = filtered[0];
452
+ const steps = session.steps || [];
453
+
454
+ const running = steps.find((s) => s.status === "running");
455
+ if (!running) {
456
+ console.log("NO_RUNNING_STEP");
457
+ return;
458
+ }
459
+
460
+ running.status = "completed";
461
+ running.completed_at = new Date().toISOString();
462
+ writeSession(session);
463
+
464
+ console.log(`COMPLETED: ${running.index} ${running.skill || "?"}`);
465
+
466
+ const nextPending = steps.find((s) => s.status === "pending");
467
+ if (!nextPending) {
468
+ // Reload and mark session complete
469
+ const data = JSON.parse(fs.readFileSync(session._path, "utf-8"));
470
+ data.status = "completed";
471
+ data.updated_at = new Date().toISOString();
472
+ fs.writeFileSync(session._path, JSON.stringify(data, null, 2), "utf-8");
473
+ console.log("SESSION_COMPLETE");
474
+ } else {
475
+ console.log(
476
+ `NEXT: ${nextPending.index} ${nextPending.skill || "?"} [${nextPending.type || "internal"}]`
477
+ );
478
+ }
479
+ }
480
+
481
+ function cmdReset(args) {
482
+ const sessions = findSessions(process.cwd(), true);
483
+ const session = sessions.find((s) => s.session_id === args.sessionId);
484
+ if (!session) {
485
+ console.log(`Session not found: ${args.sessionId}`);
486
+ process.exit(1);
487
+ }
488
+ let count = 0;
489
+ for (const step of session.steps || []) {
490
+ if (step.status === "failed" || step.status === "running") {
491
+ step.status = "pending";
492
+ delete step.error;
493
+ delete step.started_at;
494
+ delete step.completed_at;
495
+ count++;
496
+ }
497
+ }
498
+ session.status = "running";
499
+ writeSession(session);
500
+ console.log(`Reset ${count} steps to pending. Session status -> running.`);
501
+ }
502
+
503
+ function copyDir(src, dest) {
504
+ fs.mkdirSync(dest, { recursive: true });
505
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
506
+ const srcPath = path.join(src, entry.name);
507
+ const destPath = path.join(dest, entry.name);
508
+ if (entry.isDirectory()) copyDir(srcPath, destPath);
509
+ else fs.copyFileSync(srcPath, destPath);
510
+ }
511
+ }
512
+
513
+ function countMd(dir) {
514
+ let n = 0;
515
+ if (!fs.existsSync(dir)) return 0;
516
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
517
+ if (entry.isDirectory()) n += countMd(path.join(dir, entry.name));
518
+ else if (entry.name.endsWith(".md")) n++;
519
+ }
520
+ return n;
521
+ }
522
+
523
+ function rmDir(dir) {
524
+ if (!fs.existsSync(dir)) return;
525
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
526
+ const p = path.join(dir, entry.name);
527
+ if (entry.isDirectory()) rmDir(p);
528
+ else fs.unlinkSync(p);
529
+ }
530
+ fs.rmdirSync(dir);
531
+ }
532
+
533
+ // Resolve target skills directory per variant and scope
534
+ // codex -> .codex/skills/ claude -> .claude/skills/
535
+ function resolveSkillsDir(variant, args) {
536
+ const dotDir = variant === "codex" ? ".codex" : ".claude";
537
+ if (args.project) {
538
+ return path.join(path.resolve(args.project), dotDir, "skills");
539
+ }
540
+ const home = process.env.HOME || process.env.USERPROFILE;
541
+ return path.join(home, dotDir, "skills");
542
+ }
543
+
544
+ function cmdInstall(args) {
545
+ const variant = args.variant || "all";
546
+ const validVariants = ["codex", "claude", "all"];
547
+
548
+ if (!validVariants.includes(variant)) {
549
+ console.log(`Invalid variant: ${variant}. Use: ${validVariants.join(", ")}`);
550
+ process.exit(1);
551
+ }
552
+
553
+ const scope = args.project ? "project" : "global";
554
+ console.log(`Install scope: ${scope}`);
555
+ console.log();
556
+
557
+ const toInstall = variant === "all" ? ["codex", "claude"] : [variant];
558
+
559
+ for (const v of toInstall) {
560
+ const source = VARIANTS[v];
561
+ const skillsDir = resolveSkillsDir(v, args);
562
+ const skillName = "maestro-flow";
563
+ const skillTarget = path.join(skillsDir, skillName);
564
+
565
+ console.log(`Installing [${v}] -> ${skillName}`);
566
+ console.log(` Source: ${source}`);
567
+ console.log(` Target: ${skillTarget}`);
568
+
569
+ if (!fs.existsSync(path.join(source, "SKILL.md"))) {
570
+ console.error(` Error: SKILL.md not found in ${source}`);
571
+ continue;
572
+ }
573
+
574
+ copyDir(source, skillTarget);
575
+
576
+ const cmdCount = countMd(path.join(skillTarget, "commands"));
577
+ console.log(` Commands: ${cmdCount}`);
578
+ console.log();
579
+ }
580
+
581
+ console.log("Installation complete!");
582
+ if (variant === "all") {
583
+ console.log(" .codex/skills/maestro-flow/ -> codex (spawn_agents_on_csv)");
584
+ console.log(" .claude/skills/maestro-flow/ -> claude (Skill + delegate)");
585
+ } else {
586
+ const dotDir = variant === "codex" ? ".codex" : ".claude";
587
+ console.log(` ${dotDir}/skills/maestro-flow/ -> ${variant}`);
588
+ }
589
+ console.log();
590
+ console.log("Usage:");
591
+ console.log(' /maestro-flow "your intent"');
592
+ console.log(" maestro-flow list");
593
+ }
594
+
595
+ function cmdUninstall(args) {
596
+ const variant = args.variant || "all";
597
+ const toUninstall = variant === "all" ? ["codex", "claude"] : [variant];
598
+ const scope = args.project ? "project" : "global";
599
+
600
+ let removed = 0;
601
+ for (const v of toUninstall) {
602
+ const skillsDir = resolveSkillsDir(v, args);
603
+ const skillTarget = path.join(skillsDir, "maestro-flow");
604
+ if (!fs.existsSync(skillTarget)) continue;
605
+ const cmdCount = countMd(path.join(skillTarget, "commands"));
606
+ const dotDir = v === "codex" ? ".codex" : ".claude";
607
+ rmDir(skillTarget);
608
+ console.log(`Uninstalled ${dotDir}/skills/maestro-flow (${cmdCount} commands) [${scope}]`);
609
+ removed++;
610
+ }
611
+
612
+ if (!removed) {
613
+ console.log(`Maestro Flow not found [${scope}]`);
614
+ } else {
615
+ console.log();
616
+ console.log("Global CLI still available. Remove: npm uninstall -g maestro-flow-one");
617
+ }
618
+ }
619
+
620
+ // --- CLI Parser ---
621
+
622
+ function parseArgs() {
623
+ const args = process.argv.slice(2);
624
+ if (!args.length) {
625
+ printHelp();
626
+ return;
627
+ }
628
+
629
+ const cmd = args[0];
630
+ const rest = args.slice(1);
631
+
632
+ // Parse flags
633
+ const flags = {};
634
+ const positional = [];
635
+ for (let i = 0; i < rest.length; i++) {
636
+ if (rest[i] === "--category" || rest[i] === "-c") {
637
+ flags.category = rest[++i];
638
+ } else if (rest[i] === "--all" || rest[i] === "-a") {
639
+ flags.all = true;
640
+ } else if (rest[i] === "--project" || rest[i] === "-p") {
641
+ flags.project = rest[++i];
642
+ } else if (rest[i] === "--variant" || rest[i] === "-v") {
643
+ flags.variant = rest[++i];
644
+ } else {
645
+ positional.push(rest[i]);
646
+ }
647
+ }
648
+
649
+ // Apply variant for query commands
650
+ if (flags.variant) setVariant(flags.variant);
651
+
652
+ switch (cmd) {
653
+ case "list":
654
+ return cmdList({ category: flags.category });
655
+ case "show":
656
+ if (!positional[0]) { console.log("Usage: maestro-flow show <command-name>"); return; }
657
+ return cmdShow({ name: positional[0] });
658
+ case "chains":
659
+ return cmdChains({ category: flags.category });
660
+ case "chain":
661
+ if (!positional[0]) { console.log("Usage: maestro-flow chain <template-name>"); return; }
662
+ return cmdChain({ name: positional[0] });
663
+ case "suggest":
664
+ if (!positional[0]) { console.log("Usage: maestro-flow suggest <intent-text>"); return; }
665
+ return cmdSuggest({ intent: positional.join(" ") });
666
+ case "resolve":
667
+ if (!positional[0]) { console.log("Usage: maestro-flow resolve <command-name>"); return; }
668
+ return cmdResolve({ name: positional[0] });
669
+ case "status":
670
+ return cmdStatus({ sessionId: positional[0] });
671
+ case "sessions":
672
+ return cmdSessions({ all: flags.all });
673
+ case "step":
674
+ if (positional.length < 3) { console.log("Usage: maestro-flow step <session-id> <index> <status>"); return; }
675
+ return cmdStep({ sessionId: positional[0], index: positional[1], status: positional[2] });
676
+ case "next":
677
+ return cmdNext({ sessionId: positional[0] });
678
+ case "done":
679
+ return cmdDone({ sessionId: positional[0] });
680
+ case "reset":
681
+ if (!positional[0]) { console.log("Usage: maestro-flow reset <session-id>"); return; }
682
+ return cmdReset({ sessionId: positional[0] });
683
+ case "install":
684
+ return cmdInstall({ project: flags.project || positional[0], variant: flags.variant });
685
+ case "uninstall":
686
+ return cmdUninstall({ project: flags.project || positional[0], variant: flags.variant });
687
+ case "help":
688
+ case "--help":
689
+ case "-h":
690
+ return printHelp();
691
+ default:
692
+ console.log(`Unknown command: ${cmd}`);
693
+ printHelp();
694
+ }
695
+ }
696
+
697
+ function printHelp() {
698
+ console.log(`
699
+ Maestro Flow CLI - command discovery, chain management, session tracking
700
+
701
+ Usage: maestro-flow <command> [options]
702
+
703
+ Commands:
704
+ list [--category <cat>] List available commands
705
+ show <command-name> Display command details
706
+ chains [--category <cat>] List chain templates
707
+ chain <template-name> Show chain details
708
+ suggest <intent-text> Suggest chain for intent
709
+ resolve <command-name> Resolve command name to file path
710
+ status [session-id] Show session status
711
+ sessions [--all] List sessions
712
+ next [session-id] Load next pending step
713
+ done [session-id] Complete current step
714
+ step <session-id> <index> <status> Update step status
715
+ reset <session-id> Reset failed session
716
+ install [--variant codex|claude|all] Install skill (default: all)
717
+ uninstall [--variant codex|claude|all] Remove skill
718
+
719
+ Options:
720
+ --variant, -v <name> Select variant: codex, claude, all (default: all)
721
+ codex -> .codex/skills/maestro-flow/
722
+ claude -> .claude/skills/maestro-flow/
723
+ all -> both
724
+ --project, -p <dir> Install to project dir (default: global ~/.claude|.codex)
725
+ --category, -c <cat> Filter by category (list, chains)
726
+ --all, -a Show all sessions (sessions)
727
+ `);
728
+ }
729
+
730
+ parseArgs();