@ionivetech/mugiwara 0.6.5 → 0.6.6
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -3
- package/.codex-plugin/plugin.json +1 -2
- package/.cursor-plugin/plugin.json +1 -2
- package/.kimi-plugin/plugin.json +1 -2
- package/.opencode/commands/mugiwara-continue.md +44 -31
- package/.opencode/commands/mugiwara-execute.md +1 -1
- package/.opencode/commands/mugiwara-heal.md +1 -1
- package/.opencode/commands/mugiwara-onboard.md +8 -9
- package/.opencode/commands/mugiwara-plan.md +1 -1
- package/.opencode/commands/mugiwara-review.md +1 -1
- package/.opencode/commands/mugiwara-security.md +1 -1
- package/.opencode/commands/mugiwara-ship.md +1 -1
- package/.opencode/commands/mugiwara.md +2 -2
- package/.opencode/plugins/mugiwara.mjs +1 -2
- package/AGENTS.md +1 -2
- package/README.md +33 -31
- package/content/agents/brook-healing.md +5 -8
- package/content/agents/chopper-checkpoint.md +8 -11
- package/content/agents/eval-runner.md +2 -5
- package/content/agents/franky-gates.md +4 -7
- package/content/agents/jinbe-security.md +4 -7
- package/content/agents/luffy-orchestrator.md +13 -13
- package/content/agents/memory-keeper.md +5 -8
- package/content/agents/nami-planner.md +4 -7
- package/content/agents/resume-coordinator.md +7 -10
- package/content/agents/robin-reviewer.md +6 -9
- package/content/agents/sanji-quality.md +7 -10
- package/content/agents/skeptic-verifier.md +5 -8
- package/content/agents/usopp-brainstorm.md +3 -6
- package/content/agents/zoro-execution.md +7 -10
- package/content/skills/mugiwara-backend/SKILL.md +2 -0
- package/content/skills/mugiwara-checkpoint/SKILL.md +13 -11
- package/content/skills/mugiwara-checkpoint/references/ledger-format.md +4 -4
- package/content/skills/mugiwara-context-budget/SKILL.md +5 -3
- package/content/skills/mugiwara-contract-first/SKILL.md +2 -0
- package/content/skills/mugiwara-execution/SKILL.md +20 -20
- package/content/skills/mugiwara-execution/references/dispatch.md +2 -2
- package/content/skills/mugiwara-execution/references/resume-batching.md +1 -1
- package/content/skills/mugiwara-frontend/SKILL.md +2 -4
- package/content/skills/mugiwara-gates/SKILL.md +8 -7
- package/content/skills/mugiwara-git/SKILL.md +7 -2
- package/content/skills/mugiwara-healing/SKILL.md +6 -4
- package/content/skills/mugiwara-healing/references/workers.md +2 -2
- package/content/skills/mugiwara-orchestration/SKILL.md +26 -24
- package/content/skills/mugiwara-orchestration/references/check-ins.md +16 -15
- package/content/skills/mugiwara-orchestration/references/closure.md +5 -5
- package/content/skills/mugiwara-orchestration/references/output-contract.md +9 -9
- package/content/skills/mugiwara-orchestration/references/triage-escalation.md +22 -22
- package/content/skills/mugiwara-planning/SKILL.md +3 -1
- package/content/skills/mugiwara-planning/references/plan-template.md +2 -2
- package/content/skills/mugiwara-pr/SKILL.md +6 -6
- package/content/skills/mugiwara-pr/references/verdict-format.md +3 -3
- package/content/skills/mugiwara-quality/SKILL.md +7 -7
- package/content/skills/mugiwara-resume/SKILL.md +18 -20
- package/content/skills/mugiwara-review/SKILL.md +5 -1
- package/content/skills/mugiwara-review/references/five-axis-worksheet.md +1 -1
- package/content/skills/mugiwara-root-cause/SKILL.md +2 -0
- package/content/skills/mugiwara-security/SKILL.md +5 -1
- package/content/skills/mugiwara-ship/SKILL.md +1 -1
- package/content/skills/mugiwara-ship/references/cleanup.md +1 -1
- package/content/skills/mugiwara-testcases/SKILL.md +5 -3
- package/content/skills/mugiwara-workflow/SKILL.md +27 -27
- package/content/skills/mugiwara-workflow/references/workspace-layout.md +15 -15
- package/content/skills/using-mugiwara/SKILL.md +5 -5
- package/dist/mugiwara.js +808 -33
- package/gemini-extension.json +1 -1
- package/hooks/auto-savepoint.js +106 -0
- package/hooks/auto-savepoint.ts +102 -0
- package/hooks/engagement-marker.js +59 -0
- package/hooks/engagement-marker.ts +97 -0
- package/hooks/hooks.json +41 -2
- package/hooks/mugiwara-mode-tracker.js +79 -0
- package/hooks/pipeline-guard.js +182 -0
- package/hooks/pipeline-guard.ts +267 -0
- package/hooks/session-start.js +106 -0
- package/hooks/session-start.ts +44 -32
- package/package.json +7 -3
- package/plugin.json +1 -1
- package/references/agent-protocol.md +15 -0
- package/references/complexity.md +54 -0
- package/references/definition-of-done.md +1 -1
- package/references/multi-actor.md +5 -5
- package/references/skill-versioning.md +1 -1
- package/references/token-budget.md +30 -8
- package/references/wave-banners.md +16 -16
- package/scripts/build-hooks.ts +56 -0
- package/scripts/conformance.ts +17 -0
- package/scripts/coverage-gate.ts +179 -0
- package/scripts/install.ps1 +5 -4
- package/scripts/install.sh +4 -4
- package/scripts/lane.sh +6 -2
- package/scripts/lib/patterns.sh +84 -0
- package/scripts/mission-report.sh +12 -11
- package/scripts/savepoint.sh +141 -37
- package/scripts/verify-install.ts +154 -44
- package/src/args.ts +1 -1
- package/src/cli.ts +217 -0
- package/src/continue.ts +243 -0
- package/src/installer.ts +39 -0
- package/src/onboard.ts +207 -0
- package/src/run.ts +82 -0
- package/src/targets/claude.ts +140 -2
- package/src/targets/copilot.ts +20 -1
- package/src/targets/generic.ts +9 -4
- package/src/targets/opencode.ts +0 -1
- package/content/agents/onboarding-guide.md +0 -124
- package/content/skills/mugiwara-security/references/owasp-mapping.md +0 -30
- package/scripts/onboard.ts +0 -266
- package/scripts/probe.ts +0 -40
package/dist/mugiwara.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { existsSync as
|
|
4
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, readdirSync as readdirSync7, rmSync as rmSync3 } from "node:fs";
|
|
5
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
5
6
|
import { homedir as homedir2 } from "node:os";
|
|
6
|
-
import { join as
|
|
7
|
-
import { fileURLToPath as
|
|
7
|
+
import { join as join11, resolve as resolve2 } from "node:path";
|
|
8
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
8
9
|
|
|
9
10
|
// src/args.ts
|
|
10
11
|
var VALUE_FLAGS = { "--project": "project", "--target": "target" };
|
|
@@ -16,6 +17,7 @@ var BOOL_FLAGS = {
|
|
|
16
17
|
"--dry-run": "dryRun",
|
|
17
18
|
"--keep-logs": "keepLogs",
|
|
18
19
|
"--check": "check",
|
|
20
|
+
"--all": "all",
|
|
19
21
|
"--help": "help",
|
|
20
22
|
"-h": "help",
|
|
21
23
|
"--version": "version",
|
|
@@ -81,7 +83,7 @@ async function confirm(rl, question) {
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
// src/targets/claude.ts
|
|
84
|
-
import { existsSync, mkdirSync, copyFileSync, chmodSync, readdirSync } from "node:fs";
|
|
86
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, chmodSync, readdirSync } from "node:fs";
|
|
85
87
|
import { dirname, join } from "node:path";
|
|
86
88
|
import { fileURLToPath } from "node:url";
|
|
87
89
|
|
|
@@ -119,6 +121,107 @@ function toolsFromScope(scope) {
|
|
|
119
121
|
return "Read, Grep, Glob, Write, Bash, WebFetch, WebSearch";
|
|
120
122
|
return;
|
|
121
123
|
}
|
|
124
|
+
var SCRIPTS_SRC = join(here, "..", "..", "scripts");
|
|
125
|
+
function copyScripts(dstDir) {
|
|
126
|
+
if (!existsSync(SCRIPTS_SRC))
|
|
127
|
+
return [];
|
|
128
|
+
const written = [];
|
|
129
|
+
const copyDir = (src, dst) => {
|
|
130
|
+
if (!existsSync(src))
|
|
131
|
+
return;
|
|
132
|
+
for (const f of readdirSync(src)) {
|
|
133
|
+
if (!f.endsWith(".sh") && f !== "initiative.ts" || f === "install.sh")
|
|
134
|
+
continue;
|
|
135
|
+
const to = join(dst, f);
|
|
136
|
+
mkdirSync(dst, { recursive: true });
|
|
137
|
+
copyFileSync(join(src, f), to);
|
|
138
|
+
chmodSync(to, 493);
|
|
139
|
+
written.push(to);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
copyDir(SCRIPTS_SRC, dstDir);
|
|
143
|
+
copyDir(join(SCRIPTS_SRC, "lib"), join(dstDir, "lib"));
|
|
144
|
+
return written;
|
|
145
|
+
}
|
|
146
|
+
function wireSettings(root, hooksDir) {
|
|
147
|
+
const file = join(root, "settings.json");
|
|
148
|
+
const events = {
|
|
149
|
+
SessionStart: { file: "session-start.js", timeout: 10 },
|
|
150
|
+
UserPromptSubmit: { file: "mugiwara-mode-tracker.js", timeout: 5 },
|
|
151
|
+
Stop: { file: "auto-savepoint.js", timeout: 20 },
|
|
152
|
+
SubagentStop: { file: "pipeline-guard.js", timeout: 15 },
|
|
153
|
+
PostToolUse: { file: "engagement-marker.js", timeout: 5, matcher: "Task|Skill" }
|
|
154
|
+
};
|
|
155
|
+
let settings = {};
|
|
156
|
+
if (existsSync(file)) {
|
|
157
|
+
try {
|
|
158
|
+
settings = JSON.parse(readFileSync(file, "utf8"));
|
|
159
|
+
} catch {
|
|
160
|
+
return { written: [], notes: [`${file} is not valid JSON — add the mugiwara hooks manually`] };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const hooks = settings.hooks ?? {};
|
|
164
|
+
let changed = false;
|
|
165
|
+
for (const [event, spec] of Object.entries(events)) {
|
|
166
|
+
const command = join(hooksDir, spec.file);
|
|
167
|
+
if (!existsSync(command))
|
|
168
|
+
continue;
|
|
169
|
+
const groups = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
170
|
+
const already = groups.some((g) => g.hooks?.some((h) => h.command?.includes(spec.file)));
|
|
171
|
+
if (already)
|
|
172
|
+
continue;
|
|
173
|
+
const group = { hooks: [{ type: "command", command: JSON.stringify(command), timeout: spec.timeout }] };
|
|
174
|
+
if (spec.matcher)
|
|
175
|
+
group.matcher = spec.matcher;
|
|
176
|
+
groups.push(group);
|
|
177
|
+
hooks[event] = groups;
|
|
178
|
+
changed = true;
|
|
179
|
+
}
|
|
180
|
+
if (!changed)
|
|
181
|
+
return { written: [], notes: [] };
|
|
182
|
+
settings.hooks = hooks;
|
|
183
|
+
writeFileSync(file, JSON.stringify(settings, null, 2) + `
|
|
184
|
+
`);
|
|
185
|
+
return { written: [], notes: [`hooks registered in ${file}`] };
|
|
186
|
+
}
|
|
187
|
+
function unwireSettings(root) {
|
|
188
|
+
const file = join(root, "settings.json");
|
|
189
|
+
if (!existsSync(file))
|
|
190
|
+
return { changed: [], notes: [] };
|
|
191
|
+
let settings;
|
|
192
|
+
try {
|
|
193
|
+
settings = JSON.parse(readFileSync(file, "utf8"));
|
|
194
|
+
} catch {
|
|
195
|
+
return { changed: [], notes: [`${file} is not valid JSON — left untouched`] };
|
|
196
|
+
}
|
|
197
|
+
const hooks = settings.hooks;
|
|
198
|
+
if (!hooks)
|
|
199
|
+
return { changed: [], notes: [] };
|
|
200
|
+
const ours = new Set(["session-start.js", "mugiwara-mode-tracker.js", "auto-savepoint.js", "pipeline-guard.js", "engagement-marker.js"]);
|
|
201
|
+
const isOurs = (h) => [...ours].some((f) => h.command?.includes(join("hooks", f)) || h.command?.includes(`hooks/${f}`));
|
|
202
|
+
let changed = false;
|
|
203
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
204
|
+
if (!Array.isArray(groups))
|
|
205
|
+
continue;
|
|
206
|
+
const kept = groups.map((g) => ({ ...g, hooks: (g.hooks ?? []).filter((h) => !isOurs(h)) })).filter((g) => g.hooks.length > 0);
|
|
207
|
+
if (kept.length !== groups.length || kept.some((g, i) => g.hooks.length !== groups[i]?.hooks?.length)) {
|
|
208
|
+
changed = true;
|
|
209
|
+
}
|
|
210
|
+
if (kept.length)
|
|
211
|
+
hooks[event] = kept;
|
|
212
|
+
else {
|
|
213
|
+
delete hooks[event];
|
|
214
|
+
changed = true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (!changed)
|
|
218
|
+
return { changed: [], notes: [] };
|
|
219
|
+
if (!Object.keys(hooks).length)
|
|
220
|
+
delete settings.hooks;
|
|
221
|
+
writeFileSync(file, JSON.stringify(settings, null, 2) + `
|
|
222
|
+
`);
|
|
223
|
+
return { changed: [file], notes: [] };
|
|
224
|
+
}
|
|
122
225
|
var target = {
|
|
123
226
|
id: "claude",
|
|
124
227
|
label: "Claude Code",
|
|
@@ -157,7 +260,7 @@ var target = {
|
|
|
157
260
|
return { written: [], notes: [] };
|
|
158
261
|
if (existsSync(HOOKS_SRC)) {
|
|
159
262
|
for (const f of readdirSync(HOOKS_SRC)) {
|
|
160
|
-
if (
|
|
263
|
+
if (!/\.(ts|js|json)$/.test(f))
|
|
161
264
|
continue;
|
|
162
265
|
const dst = join(root, "hooks", f);
|
|
163
266
|
if (!existsSync(dst)) {
|
|
@@ -167,6 +270,10 @@ var target = {
|
|
|
167
270
|
written.push(dst);
|
|
168
271
|
}
|
|
169
272
|
}
|
|
273
|
+
written.push(...copyScripts(join(root, "scripts")));
|
|
274
|
+
const wired = wireSettings(root, join(root, "hooks"));
|
|
275
|
+
written.push(...wired.written);
|
|
276
|
+
notes.push(...wired.notes);
|
|
170
277
|
}
|
|
171
278
|
if (existsSync(COMMANDS_SRC)) {
|
|
172
279
|
const dstDir = join(root, "commands");
|
|
@@ -184,6 +291,12 @@ var target = {
|
|
|
184
291
|
}
|
|
185
292
|
}
|
|
186
293
|
return { written, notes };
|
|
294
|
+
},
|
|
295
|
+
postUninstall({ scope, projectDir, home, dryRun }) {
|
|
296
|
+
if (dryRun)
|
|
297
|
+
return { changed: [], notes: [] };
|
|
298
|
+
const root = scope === "global" ? join(home, ".claude") : join(projectDir, ".claude");
|
|
299
|
+
return unwireSettings(root);
|
|
187
300
|
}
|
|
188
301
|
};
|
|
189
302
|
|
|
@@ -208,8 +321,7 @@ var CREW = {
|
|
|
208
321
|
"skeptic-verifier": { color: "#64748b", temperature: 0.1, steps: 12 },
|
|
209
322
|
"eval-runner": { color: "#14b8a6", temperature: 0.2, steps: 15 },
|
|
210
323
|
"resume-coordinator": { color: "#d97706", temperature: 0.2, steps: 10 },
|
|
211
|
-
"memory-keeper": { color: "#d946ef", temperature: 0.2, steps: 8 }
|
|
212
|
-
"onboarding-guide": { color: "#0ea5e9", temperature: 0.3, steps: 15 }
|
|
324
|
+
"memory-keeper": { color: "#d946ef", temperature: 0.2, steps: 8 }
|
|
213
325
|
};
|
|
214
326
|
function readBannerColors() {
|
|
215
327
|
try {
|
|
@@ -319,7 +431,17 @@ var target3 = {
|
|
|
319
431
|
transformSkill(data, body) {
|
|
320
432
|
return {
|
|
321
433
|
relPath: `${data.name}.instructions.md`,
|
|
322
|
-
text: stringifyFrontmatter({ description: data.description, applyTo: "**/*" },
|
|
434
|
+
text: stringifyFrontmatter({ description: data.description, applyTo: "**/*" }, `# ${data.name}
|
|
435
|
+
|
|
436
|
+
> ${data.description}
|
|
437
|
+
|
|
438
|
+
Full skill: read \`.mugiwara/refs/${data.name}/${data.name}.md\` when the crew invokes this role.`)
|
|
439
|
+
};
|
|
440
|
+
},
|
|
441
|
+
transformSkillFull(data, body) {
|
|
442
|
+
return {
|
|
443
|
+
relPath: `${data.name}.md`,
|
|
444
|
+
text: stringifyFrontmatter({ description: data.description }, body)
|
|
323
445
|
};
|
|
324
446
|
},
|
|
325
447
|
transformAgent(data, body) {
|
|
@@ -334,7 +456,7 @@ var target3 = {
|
|
|
334
456
|
};
|
|
335
457
|
|
|
336
458
|
// src/targets/generic.ts
|
|
337
|
-
import { existsSync as existsSync3, writeFileSync } from "node:fs";
|
|
459
|
+
import { existsSync as existsSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
338
460
|
import { join as join4 } from "node:path";
|
|
339
461
|
function makeGeneric(opts) {
|
|
340
462
|
const { id, label, rulesDir, bootstrapFile, bootstrapPointer, tier = bootstrapPointer ? 2 : 3 } = opts;
|
|
@@ -360,7 +482,7 @@ function makeGeneric(opts) {
|
|
|
360
482
|
|
|
361
483
|
## Skip when
|
|
362
484
|
|
|
363
|
-
Full skill: \`${data.name}\` — read \`.mugiwara/refs/${data.name}.md\` when the crew invokes this role.`
|
|
485
|
+
Full skill: \`${data.name}\` — read \`.mugiwara/refs/${data.name}/${data.name}.md\` when the crew invokes this role.`
|
|
364
486
|
};
|
|
365
487
|
}
|
|
366
488
|
return { relPath: `${data.name}.md`, text: `# ${data.name}
|
|
@@ -386,7 +508,7 @@ ${body}` };
|
|
|
386
508
|
|
|
387
509
|
> ${data.description}
|
|
388
510
|
|
|
389
|
-
Skills: ${data.skills ?? ""}. Read \`.mugiwara/refs/${data.name}.md\` when embodying this role.
|
|
511
|
+
Skills: ${data.skills ?? ""}. Read \`.mugiwara/refs/${data.name}/${data.name}.md\` when embodying this role.
|
|
390
512
|
|
|
391
513
|
Only zoro-execution and brook-healing may modify source code.
|
|
392
514
|
Return your output to luffy-orchestrator; do not choose the next step.`
|
|
@@ -411,8 +533,8 @@ Skills used: ${data.skills ?? ""}
|
|
|
411
533
|
|
|
412
534
|
${body}` };
|
|
413
535
|
},
|
|
414
|
-
refsDir({ projectDir }) {
|
|
415
|
-
return join4(projectDir, ".mugiwara", "refs");
|
|
536
|
+
refsDir({ projectDir }, skillName) {
|
|
537
|
+
return join4(projectDir, ".mugiwara", "refs", skillName);
|
|
416
538
|
},
|
|
417
539
|
postInstall({ projectDir, dryRun }) {
|
|
418
540
|
if (!bootstrapFile)
|
|
@@ -422,7 +544,7 @@ ${body}` };
|
|
|
422
544
|
const file = join4(projectDir, bootstrapFile);
|
|
423
545
|
if (!existsSync3(file)) {
|
|
424
546
|
if (!dryRun)
|
|
425
|
-
|
|
547
|
+
writeFileSync2(file, `${bootstrapPointer}
|
|
426
548
|
`);
|
|
427
549
|
written.push(file);
|
|
428
550
|
} else {
|
|
@@ -497,7 +619,7 @@ var targets = { claude: target, opencode: target2, copilot: target3, gemini: tar
|
|
|
497
619
|
var TARGET_IDS = Object.keys(targets);
|
|
498
620
|
|
|
499
621
|
// src/installer.ts
|
|
500
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync3, writeFileSync as
|
|
622
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync3, writeFileSync as writeFileSync3, copyFileSync as copyFileSync3, rmSync, lstatSync } from "node:fs";
|
|
501
623
|
import { dirname as dirname3, join as join5 } from "node:path";
|
|
502
624
|
import { homedir } from "node:os";
|
|
503
625
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -561,7 +683,7 @@ function installTo(target10, opts) {
|
|
|
561
683
|
}
|
|
562
684
|
if (!dryRun) {
|
|
563
685
|
mkdirSync3(dirname3(absPath), { recursive: true });
|
|
564
|
-
|
|
686
|
+
writeFileSync3(absPath, text);
|
|
565
687
|
}
|
|
566
688
|
result.written.push(absPath);
|
|
567
689
|
};
|
|
@@ -610,6 +732,38 @@ function installTo(target10, opts) {
|
|
|
610
732
|
result.written.push(...post.written);
|
|
611
733
|
result.notes.push(...post.notes);
|
|
612
734
|
}
|
|
735
|
+
if (scope === "project") {
|
|
736
|
+
const configPath = join5(projectDir, ".mugiwara", "config");
|
|
737
|
+
let configExists = false;
|
|
738
|
+
try {
|
|
739
|
+
configExists = lstatSync(configPath).isFile() || lstatSync(configPath).isSymbolicLink();
|
|
740
|
+
} catch {
|
|
741
|
+
configExists = false;
|
|
742
|
+
}
|
|
743
|
+
if (!configExists) {
|
|
744
|
+
const body = [
|
|
745
|
+
"mode=guided",
|
|
746
|
+
"branch=feature/{type}-{issue}-{slug}",
|
|
747
|
+
"commit=conventional",
|
|
748
|
+
"auto_commit=on",
|
|
749
|
+
"coverage_new=90",
|
|
750
|
+
"coverage_modified=80",
|
|
751
|
+
"review_depth=full",
|
|
752
|
+
"quality_depth=full",
|
|
753
|
+
"delegate_threshold=60",
|
|
754
|
+
"heal_max_cycles=3",
|
|
755
|
+
"verbosity=normal"
|
|
756
|
+
].join(`
|
|
757
|
+
`) + `
|
|
758
|
+
`;
|
|
759
|
+
if (!dryRun) {
|
|
760
|
+
mkdirSync3(dirname3(configPath), { recursive: true });
|
|
761
|
+
writeFileSync3(configPath, body);
|
|
762
|
+
}
|
|
763
|
+
result.written.push(configPath);
|
|
764
|
+
result.notes.push(`default config written: ${configPath} (run \`mugiwara onboard\` to customise)`);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
613
767
|
return result;
|
|
614
768
|
}
|
|
615
769
|
function removeInstalled(manifest, { dryRun = false } = {}) {
|
|
@@ -677,7 +831,7 @@ function ensureProjectGitignore(projectDir, opts = {}) {
|
|
|
677
831
|
` : "";
|
|
678
832
|
if (!dryRun) {
|
|
679
833
|
mkdirSync3(dirname3(path), { recursive: true });
|
|
680
|
-
|
|
834
|
+
writeFileSync3(path, existing + separator + GITIGNORE_BLOCK);
|
|
681
835
|
}
|
|
682
836
|
return { appended: true, notes: [`.gitignore ${dryRun ? "would upgrade+append" : "upgraded"} mugiwara audit-trail block`] };
|
|
683
837
|
}
|
|
@@ -724,12 +878,12 @@ function removeProjectGitignore(projectDir, { dryRun = false } = {}) {
|
|
|
724
878
|
if (cleaned === current)
|
|
725
879
|
return { removed: false, notes: [] };
|
|
726
880
|
if (!dryRun)
|
|
727
|
-
|
|
881
|
+
writeFileSync3(path, cleaned);
|
|
728
882
|
return { removed: true, notes: [`.gitignore ${dryRun ? "would strip" : "stripped"} mugiwara block`] };
|
|
729
883
|
}
|
|
730
884
|
|
|
731
885
|
// src/manifest.ts
|
|
732
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as
|
|
886
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
733
887
|
import { dirname as dirname4, join as join6 } from "node:path";
|
|
734
888
|
function manifestPath({ scope, projectDir, home }) {
|
|
735
889
|
return scope === "global" ? join6(home, ".mugiwara", "manifest.json") : join6(projectDir, ".mugiwara", "manifest.json");
|
|
@@ -739,7 +893,7 @@ function readManifest(file) {
|
|
|
739
893
|
}
|
|
740
894
|
function writeManifest(file, data) {
|
|
741
895
|
mkdirSync4(dirname4(file), { recursive: true });
|
|
742
|
-
|
|
896
|
+
writeFileSync4(file, JSON.stringify(data, null, 2) + `
|
|
743
897
|
`);
|
|
744
898
|
}
|
|
745
899
|
|
|
@@ -913,6 +1067,417 @@ function archiveMission(projectDir, mission, opts = {}) {
|
|
|
913
1067
|
return { report, removed, kept, index };
|
|
914
1068
|
}
|
|
915
1069
|
|
|
1070
|
+
// src/onboard.ts
|
|
1071
|
+
import { existsSync as existsSync7, lstatSync as lstatSync2, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1072
|
+
import { join as join8 } from "node:path";
|
|
1073
|
+
var DEFAULT_ORDER = [
|
|
1074
|
+
"mode",
|
|
1075
|
+
"branch",
|
|
1076
|
+
"commit",
|
|
1077
|
+
"auto_commit",
|
|
1078
|
+
"coverage_new",
|
|
1079
|
+
"coverage_modified",
|
|
1080
|
+
"review_depth",
|
|
1081
|
+
"quality_depth",
|
|
1082
|
+
"delegate_threshold",
|
|
1083
|
+
"heal_max_cycles",
|
|
1084
|
+
"verbosity"
|
|
1085
|
+
];
|
|
1086
|
+
var DEFAULTS = {
|
|
1087
|
+
auto_commit: "on",
|
|
1088
|
+
delegate_threshold: "60",
|
|
1089
|
+
heal_max_cycles: "3",
|
|
1090
|
+
verbosity: "normal"
|
|
1091
|
+
};
|
|
1092
|
+
var BRANCH_PRESETS = {
|
|
1093
|
+
1: "feature/{type}-{issue}-{slug}",
|
|
1094
|
+
2: "feature/{slug}",
|
|
1095
|
+
3: "feat/{slug}"
|
|
1096
|
+
};
|
|
1097
|
+
var COMMIT_PRESETS = {
|
|
1098
|
+
1: "conventional",
|
|
1099
|
+
2: "gitmoji",
|
|
1100
|
+
3: "plain"
|
|
1101
|
+
};
|
|
1102
|
+
function parseConfig(lines) {
|
|
1103
|
+
const map = new Map;
|
|
1104
|
+
for (const line of lines) {
|
|
1105
|
+
const t = line.trim();
|
|
1106
|
+
if (!t || t.startsWith("#"))
|
|
1107
|
+
continue;
|
|
1108
|
+
const eq = t.indexOf("=");
|
|
1109
|
+
if (eq === -1)
|
|
1110
|
+
continue;
|
|
1111
|
+
const k = t.slice(0, eq).trim();
|
|
1112
|
+
const v = t.slice(eq + 1).trim();
|
|
1113
|
+
if (!map.has(k))
|
|
1114
|
+
map.set(k, v);
|
|
1115
|
+
}
|
|
1116
|
+
return map;
|
|
1117
|
+
}
|
|
1118
|
+
function buildConfig(existing, updates) {
|
|
1119
|
+
const merged = new Map(existing);
|
|
1120
|
+
for (const [k, v] of Object.entries(updates))
|
|
1121
|
+
merged.set(k, v);
|
|
1122
|
+
const lines = [];
|
|
1123
|
+
for (const k of DEFAULT_ORDER) {
|
|
1124
|
+
const v = merged.get(k) ?? DEFAULTS[k];
|
|
1125
|
+
if (v === undefined)
|
|
1126
|
+
continue;
|
|
1127
|
+
lines.push(`${k}=${v}`);
|
|
1128
|
+
merged.delete(k);
|
|
1129
|
+
}
|
|
1130
|
+
for (const [k, v] of merged)
|
|
1131
|
+
lines.push(`${k}=${v}`);
|
|
1132
|
+
return lines.join(`
|
|
1133
|
+
`) + `
|
|
1134
|
+
`;
|
|
1135
|
+
}
|
|
1136
|
+
async function askFreeText(rl, prompt) {
|
|
1137
|
+
for (;; ) {
|
|
1138
|
+
const v = (await rl.question(prompt)).trim();
|
|
1139
|
+
if (v)
|
|
1140
|
+
return v;
|
|
1141
|
+
console.log(" Cannot be empty.");
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
function assertNotSymlink2(file) {
|
|
1145
|
+
if (!existsSync7(file))
|
|
1146
|
+
return;
|
|
1147
|
+
try {
|
|
1148
|
+
if (lstatSync2(file).isSymbolicLink())
|
|
1149
|
+
throw new Error(`refusing to follow symlink: ${file}`);
|
|
1150
|
+
} catch (e) {
|
|
1151
|
+
if (e.code === "ENOENT")
|
|
1152
|
+
return;
|
|
1153
|
+
throw e;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
async function runOnboard(projectDir) {
|
|
1157
|
+
if (!process.stdin.isTTY) {
|
|
1158
|
+
throw new Error("Not a terminal. Run `mugiwara onboard` in a terminal, or edit .mugiwara/config directly.");
|
|
1159
|
+
}
|
|
1160
|
+
const mugiwaraDir = join8(projectDir, ".mugiwara");
|
|
1161
|
+
const configPath = join8(mugiwaraDir, "config");
|
|
1162
|
+
assertNotSymlink2(configPath);
|
|
1163
|
+
const existing = parseConfig(existsSync7(configPath) ? readFileSync6(configPath, "utf8").split(/\r?\n/) : []);
|
|
1164
|
+
const rl = createRl();
|
|
1165
|
+
try {
|
|
1166
|
+
console.log("");
|
|
1167
|
+
console.log("╔══════════════════════════════════════╗");
|
|
1168
|
+
console.log("║ Mugiwara Onboarding Wizard ║");
|
|
1169
|
+
console.log("╚══════════════════════════════════════╝");
|
|
1170
|
+
console.log("");
|
|
1171
|
+
if (existing.size > 0) {
|
|
1172
|
+
const redo = await confirm(rl, "Existing .mugiwara/config found. Re-onboard?");
|
|
1173
|
+
if (!redo) {
|
|
1174
|
+
console.log("Onboarding cancelled. Config preserved.");
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
console.log("");
|
|
1178
|
+
}
|
|
1179
|
+
const branchChoice = await choose(rl, "Q1 — Branch naming pattern:", [
|
|
1180
|
+
"Trunk-based — feature/{type}-{issue}-{slug}",
|
|
1181
|
+
"GitFlow — feature/{slug}",
|
|
1182
|
+
"GitHub Flow — feat/{slug}",
|
|
1183
|
+
"Custom — enter your own pattern"
|
|
1184
|
+
]);
|
|
1185
|
+
let branch;
|
|
1186
|
+
if (branchChoice === 3) {
|
|
1187
|
+
branch = await askFreeText(rl, " Branch pattern (placeholders: {type} {issue} {slug}, e.g. {issue}-{slug}): ");
|
|
1188
|
+
} else {
|
|
1189
|
+
branch = BRANCH_PRESETS[branchChoice + 1];
|
|
1190
|
+
}
|
|
1191
|
+
const modeIdx = await choose(rl, "Q2 — Autonomy mode:", [
|
|
1192
|
+
"guided — ask before every wave transition",
|
|
1193
|
+
"semi — auto-advance through waves, pause on failures",
|
|
1194
|
+
"auto — full auto-pilot"
|
|
1195
|
+
]);
|
|
1196
|
+
const mode = ["guided", "semi", "auto"][modeIdx];
|
|
1197
|
+
const reviewIdx = await choose(rl, "Q3 — Code review depth:", [
|
|
1198
|
+
"full — breaking-change map, five-axis review, <=3 cycles",
|
|
1199
|
+
"standard — five-axis review, 1 cycle",
|
|
1200
|
+
"quick — diff-only, no caller-map"
|
|
1201
|
+
]);
|
|
1202
|
+
const reviewDepth = ["full", "standard", "quick"][reviewIdx];
|
|
1203
|
+
const qualityIdx = await choose(rl, "Q4 — Quality check depth:", [
|
|
1204
|
+
"full — format, lint, typecheck, test, build",
|
|
1205
|
+
"standard — lint, typecheck, test",
|
|
1206
|
+
"quick — test only"
|
|
1207
|
+
]);
|
|
1208
|
+
const qualityDepth = ["full", "standard", "quick"][qualityIdx];
|
|
1209
|
+
const covIdx = await choose(rl, "Q5 — Test coverage threshold:", [
|
|
1210
|
+
"90/80 — new code 90%, modified 80%",
|
|
1211
|
+
"80/70 — new code 80%, modified 70%",
|
|
1212
|
+
"custom — enter your own values",
|
|
1213
|
+
"none — 0/0, no coverage enforcement"
|
|
1214
|
+
]);
|
|
1215
|
+
let coverageNew = 90;
|
|
1216
|
+
let coverageModified = 80;
|
|
1217
|
+
if (covIdx === 1) {
|
|
1218
|
+
coverageNew = 80;
|
|
1219
|
+
coverageModified = 70;
|
|
1220
|
+
} else if (covIdx === 2) {
|
|
1221
|
+
coverageNew = Number.parseInt(await askFreeText(rl, " Coverage for new code (%): "), 10) || 0;
|
|
1222
|
+
coverageModified = Number.parseInt(await askFreeText(rl, " Coverage for modified code (%): "), 10) || 0;
|
|
1223
|
+
} else if (covIdx === 3) {
|
|
1224
|
+
coverageNew = 0;
|
|
1225
|
+
coverageModified = 0;
|
|
1226
|
+
}
|
|
1227
|
+
const commitChoice = await choose(rl, "Q6 — Commit style:", [
|
|
1228
|
+
"Conventional Commits — feat:, fix:, chore:, docs:",
|
|
1229
|
+
"Gitmoji — emoji-prefixed conventional",
|
|
1230
|
+
"Plain — no prefix, imperative sentence",
|
|
1231
|
+
"Custom template — enter your own (placeholders: {type} {issue} {title})"
|
|
1232
|
+
]);
|
|
1233
|
+
let commit;
|
|
1234
|
+
if (commitChoice === 3) {
|
|
1235
|
+
commit = await askFreeText(rl, " Commit template (e.g. {issue}: {title}): ");
|
|
1236
|
+
} else {
|
|
1237
|
+
commit = COMMIT_PRESETS[commitChoice + 1];
|
|
1238
|
+
}
|
|
1239
|
+
if (!existsSync7(mugiwaraDir))
|
|
1240
|
+
mkdirSync6(mugiwaraDir, { recursive: true });
|
|
1241
|
+
const body = buildConfig(existing, {
|
|
1242
|
+
mode,
|
|
1243
|
+
branch,
|
|
1244
|
+
commit,
|
|
1245
|
+
coverage_new: String(coverageNew),
|
|
1246
|
+
coverage_modified: String(coverageModified),
|
|
1247
|
+
review_depth: reviewDepth,
|
|
1248
|
+
quality_depth: qualityDepth
|
|
1249
|
+
});
|
|
1250
|
+
writeFileSync5(configPath, `# .mugiwara/config — written by mugiwara onboard on ${new Date().toISOString().slice(0, 10)}
|
|
1251
|
+
${body}`);
|
|
1252
|
+
console.log("╔══════════════════════════════════════╗");
|
|
1253
|
+
console.log("║ Onboarding Complete ║");
|
|
1254
|
+
console.log("╚══════════════════════════════════════╝");
|
|
1255
|
+
console.log("");
|
|
1256
|
+
console.log(` Branch pattern: ${branch}`);
|
|
1257
|
+
console.log(` Autonomy mode: ${mode}`);
|
|
1258
|
+
console.log(` Review depth: ${reviewDepth}`);
|
|
1259
|
+
console.log(` Quality depth: ${qualityDepth}`);
|
|
1260
|
+
console.log(` Coverage: ${coverageNew}/${coverageModified}`);
|
|
1261
|
+
console.log(` Commit style: ${commit}`);
|
|
1262
|
+
console.log("");
|
|
1263
|
+
console.log(` Config written: ${configPath}`);
|
|
1264
|
+
console.log("");
|
|
1265
|
+
} finally {
|
|
1266
|
+
rl.close();
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// src/run.ts
|
|
1271
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
1272
|
+
import { spawnSync } from "node:child_process";
|
|
1273
|
+
import { dirname as dirname5, join as join9, resolve } from "node:path";
|
|
1274
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
1275
|
+
var here3 = dirname5(fileURLToPath4(import.meta.url));
|
|
1276
|
+
var SCRIPTS_DIR = join9(here3, "..", "scripts");
|
|
1277
|
+
var RUNNABLE = ["savepoint.sh", "lane.sh", "evidence.sh", "mission-report.sh"];
|
|
1278
|
+
function findBash() {
|
|
1279
|
+
const explicit = process.env.MUGIWARA_BASH?.trim();
|
|
1280
|
+
if (explicit)
|
|
1281
|
+
return existsSync8(explicit) ? explicit : null;
|
|
1282
|
+
const candidates = process.platform === "win32" ? [
|
|
1283
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
1284
|
+
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
1285
|
+
join9(process.env.LOCALAPPDATA ?? "", "Programs", "Git", "bin", "bash.exe")
|
|
1286
|
+
] : ["/bin/bash", "/usr/bin/bash", "/usr/local/bin/bash", "/opt/homebrew/bin/bash"];
|
|
1287
|
+
for (const p of candidates) {
|
|
1288
|
+
if (p && existsSync8(p))
|
|
1289
|
+
return p;
|
|
1290
|
+
}
|
|
1291
|
+
const probe = spawnSync(process.platform === "win32" ? "where" : "which", ["bash"], {
|
|
1292
|
+
encoding: "utf8"
|
|
1293
|
+
});
|
|
1294
|
+
const first = probe.stdout?.split(/\r?\n/).find((l) => l.trim());
|
|
1295
|
+
return first?.trim() || null;
|
|
1296
|
+
}
|
|
1297
|
+
function noBashMessage() {
|
|
1298
|
+
const remedy = process.platform === "win32" ? "Install Git for Windows (which ships bash)" : "Install bash with your package manager (e.g. `apk add bash`, `apt install bash`)";
|
|
1299
|
+
return `no bash found. ${remedy}, or set MUGIWARA_BASH to a bash executable.`;
|
|
1300
|
+
}
|
|
1301
|
+
function runScript(name, args, projectDir) {
|
|
1302
|
+
if (!/^[a-z0-9-]+\.sh$/.test(name)) {
|
|
1303
|
+
throw new Error(`invalid script name "${name}" (expected one of: ${RUNNABLE.join(", ")})`);
|
|
1304
|
+
}
|
|
1305
|
+
const script = join9(SCRIPTS_DIR, name);
|
|
1306
|
+
if (!existsSync8(script)) {
|
|
1307
|
+
const have = existsSync8(SCRIPTS_DIR) ? readdirSync5(SCRIPTS_DIR).filter((f) => f.endsWith(".sh")) : [];
|
|
1308
|
+
throw new Error(`script not found: ${name}${have.length ? ` (available: ${have.join(", ")})` : ""}`);
|
|
1309
|
+
}
|
|
1310
|
+
const bash = findBash();
|
|
1311
|
+
if (!bash)
|
|
1312
|
+
throw new Error(noBashMessage());
|
|
1313
|
+
const r = spawnSync(bash, [script, ...args], {
|
|
1314
|
+
cwd: resolve(projectDir),
|
|
1315
|
+
stdio: "inherit",
|
|
1316
|
+
env: process.env
|
|
1317
|
+
});
|
|
1318
|
+
if (r.error)
|
|
1319
|
+
throw r.error;
|
|
1320
|
+
return r.status ?? 1;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// src/continue.ts
|
|
1324
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, readdirSync as readdirSync6 } from "node:fs";
|
|
1325
|
+
import { execFileSync } from "node:child_process";
|
|
1326
|
+
import { join as join10 } from "node:path";
|
|
1327
|
+
var SAFE = /^[A-Za-z0-9._-]+$/;
|
|
1328
|
+
var isSafeKey = (s) => SAFE.test(s) && !/^\.+$/.test(s);
|
|
1329
|
+
var num = (v) => {
|
|
1330
|
+
const n = Number(v);
|
|
1331
|
+
return Number.isFinite(n) ? n : 0;
|
|
1332
|
+
};
|
|
1333
|
+
var text = (v) => typeof v === "string" ? v : "";
|
|
1334
|
+
function gitActor(cwd) {
|
|
1335
|
+
const env = (process.env.STATE_ACTOR ?? "").trim();
|
|
1336
|
+
if (env)
|
|
1337
|
+
return env;
|
|
1338
|
+
const author = (process.env.GIT_AUTHOR_NAME ?? "").trim();
|
|
1339
|
+
if (author)
|
|
1340
|
+
return author;
|
|
1341
|
+
const git = (args) => {
|
|
1342
|
+
try {
|
|
1343
|
+
return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
1344
|
+
} catch {
|
|
1345
|
+
return "";
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
const name = git(["config", "user.name"]);
|
|
1349
|
+
const email = git(["config", "user.email"]);
|
|
1350
|
+
if (name && email)
|
|
1351
|
+
return `${name} <${email}>`;
|
|
1352
|
+
return name || process.env.USER || process.env.USERNAME || "";
|
|
1353
|
+
}
|
|
1354
|
+
function scan(projectDir, root, map) {
|
|
1355
|
+
const base = join10(projectDir, ".mugiwara", root);
|
|
1356
|
+
if (!existsSync9(base))
|
|
1357
|
+
return [];
|
|
1358
|
+
const out = [];
|
|
1359
|
+
const missions = readdirSync6(base, { withFileTypes: true }).filter((e) => e.isDirectory() && isSafeKey(e.name)).map((e) => e.name).sort();
|
|
1360
|
+
for (const mission of missions) {
|
|
1361
|
+
const dir = join10(base, mission);
|
|
1362
|
+
for (const f of readdirSync6(dir).filter((n) => n.endsWith(".json")).sort()) {
|
|
1363
|
+
const stem = f.slice(0, -".json".length);
|
|
1364
|
+
const member = stem === "state" ? null : stem;
|
|
1365
|
+
if (member !== null && !isSafeKey(member))
|
|
1366
|
+
continue;
|
|
1367
|
+
try {
|
|
1368
|
+
const raw = JSON.parse(readFileSync7(join10(dir, f), "utf8"));
|
|
1369
|
+
if (text(raw.mission) !== mission)
|
|
1370
|
+
continue;
|
|
1371
|
+
out.push(map(raw, member));
|
|
1372
|
+
} catch {}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return out;
|
|
1376
|
+
}
|
|
1377
|
+
function readContinue(projectDir) {
|
|
1378
|
+
return scan(projectDir, "continue", (r, member) => ({
|
|
1379
|
+
mission: text(r.mission),
|
|
1380
|
+
member,
|
|
1381
|
+
actor: text(r.actor),
|
|
1382
|
+
branch: text(r.branch),
|
|
1383
|
+
flow: num(r.flow ?? r.wave),
|
|
1384
|
+
mode: text(r.mode) || "guided",
|
|
1385
|
+
tasks_done: num(r.tasks_done),
|
|
1386
|
+
tasks_total: num(r.tasks_total),
|
|
1387
|
+
lane: text(r.lane) || "direct",
|
|
1388
|
+
next_action: text(r.next_action),
|
|
1389
|
+
next_session_prompt: text(r.next_session_prompt),
|
|
1390
|
+
updated_at: text(r.updated_at)
|
|
1391
|
+
}));
|
|
1392
|
+
}
|
|
1393
|
+
function readState(projectDir) {
|
|
1394
|
+
return scan(projectDir, "state", (r, member) => {
|
|
1395
|
+
const tasks = r.tasks ?? {};
|
|
1396
|
+
return {
|
|
1397
|
+
mission: text(r.mission),
|
|
1398
|
+
member,
|
|
1399
|
+
actor: text(r.actor),
|
|
1400
|
+
branch: text(r.branch),
|
|
1401
|
+
flow: num(r.flow ?? r.wave),
|
|
1402
|
+
mode: text(r.mode) || "guided",
|
|
1403
|
+
tasks_done: num(tasks.done),
|
|
1404
|
+
tasks_total: num(tasks.total),
|
|
1405
|
+
lane: text(r.lane) || "direct",
|
|
1406
|
+
lane_reason: text(r.lane_reason),
|
|
1407
|
+
lane_rose: r.lane_rose === true,
|
|
1408
|
+
lane_prev: text(r.lane_prev),
|
|
1409
|
+
lane_peak: text(r.lane_peak),
|
|
1410
|
+
next_action: "",
|
|
1411
|
+
next_session_prompt: "",
|
|
1412
|
+
updated_at: text(r.updated_at),
|
|
1413
|
+
blockers_open: num(r.blockers_open),
|
|
1414
|
+
heal_cycle: num(r.heal_cycle),
|
|
1415
|
+
heal_max_cycles: num(r.heal_max_cycles) || 3,
|
|
1416
|
+
heal_halt: r.heal_halt === true,
|
|
1417
|
+
delegate_threshold: num(r.delegate_threshold) || 60,
|
|
1418
|
+
delegate_due: r.delegate_due === true,
|
|
1419
|
+
tokens_est: num(r.tokens_est),
|
|
1420
|
+
budget: num(r.budget),
|
|
1421
|
+
budget_status: text(r.budget_status) || "ok",
|
|
1422
|
+
files_touched: num(r.files_touched),
|
|
1423
|
+
evidence: Array.isArray(r.evidence) ? r.evidence.map(text).filter(Boolean) : []
|
|
1424
|
+
};
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
function resolveContinue(entries, mission, member) {
|
|
1428
|
+
if (!entries.length)
|
|
1429
|
+
return { kind: "none" };
|
|
1430
|
+
if (!mission) {
|
|
1431
|
+
const missions = [...new Set(entries.map((e) => e.mission))];
|
|
1432
|
+
if (missions.length === 1)
|
|
1433
|
+
return resolveContinue(entries, missions[0], member);
|
|
1434
|
+
return { kind: "missions", entries };
|
|
1435
|
+
}
|
|
1436
|
+
const inMission = entries.filter((e) => e.mission === mission);
|
|
1437
|
+
if (!inMission.length) {
|
|
1438
|
+
return { kind: "unknown-mission", mission, known: [...new Set(entries.map((e) => e.mission))] };
|
|
1439
|
+
}
|
|
1440
|
+
if (member) {
|
|
1441
|
+
const hit = inMission.find((e) => e.member === member);
|
|
1442
|
+
if (!hit) {
|
|
1443
|
+
return {
|
|
1444
|
+
kind: "unknown-member",
|
|
1445
|
+
mission,
|
|
1446
|
+
member,
|
|
1447
|
+
known: inMission.map((e) => e.member ?? "(solo)")
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
return { kind: "resume", entry: hit };
|
|
1451
|
+
}
|
|
1452
|
+
const solo = inMission.find((e) => e.member === null);
|
|
1453
|
+
if (solo && inMission.length === 1)
|
|
1454
|
+
return { kind: "resume", entry: solo };
|
|
1455
|
+
return { kind: "members", mission, entries: inMission };
|
|
1456
|
+
}
|
|
1457
|
+
function pad(s, n) {
|
|
1458
|
+
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
1459
|
+
}
|
|
1460
|
+
function formatTable(entries) {
|
|
1461
|
+
const rows = entries.map((e) => [
|
|
1462
|
+
e.mission,
|
|
1463
|
+
e.member ?? "—",
|
|
1464
|
+
String(e.flow),
|
|
1465
|
+
`${e.tasks_done}/${e.tasks_total}`,
|
|
1466
|
+
e.lane,
|
|
1467
|
+
e.mode
|
|
1468
|
+
]);
|
|
1469
|
+
const head = ["MISSION", "MEMBER", "FLOW", "TASKS", "LANE", "MODE"];
|
|
1470
|
+
const widths = head.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)));
|
|
1471
|
+
const line = (cells) => " " + cells.map((c, i) => pad(c, widths[i])).join(" ").trimEnd();
|
|
1472
|
+
return [line(head), ...rows.map(line)].join(`
|
|
1473
|
+
`);
|
|
1474
|
+
}
|
|
1475
|
+
function formatResume(e) {
|
|
1476
|
+
const scope = e.member ? ` [${e.member}]` : "";
|
|
1477
|
+
const next = e.next_session_prompt || "(no next_session_prompt recorded)";
|
|
1478
|
+
return `Resumed: ${e.mission}${scope}, Flow ${e.flow}, ${e.tasks_done}/${e.tasks_total} tasks — next_action: ${e.next_action} — run: ${next}`;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
916
1481
|
// src/cli.ts
|
|
917
1482
|
var str = (v) => typeof v === "string" ? v : undefined;
|
|
918
1483
|
var flag = (v) => v === true;
|
|
@@ -937,12 +1502,26 @@ async function run(argv) {
|
|
|
937
1502
|
return resetCmd(flags);
|
|
938
1503
|
case "archive":
|
|
939
1504
|
return archive(flags, _);
|
|
1505
|
+
case "onboard":
|
|
1506
|
+
return onboardCmd(flags);
|
|
1507
|
+
case "continue":
|
|
1508
|
+
return continueCmd(flags, _);
|
|
1509
|
+
case "status":
|
|
1510
|
+
return statusCmd(flags);
|
|
1511
|
+
case "run":
|
|
1512
|
+
return runCmd(flags, _);
|
|
1513
|
+
case "savepoint":
|
|
1514
|
+
return runCmd(flags, ["run", "savepoint.sh", ..._.slice(1)]);
|
|
1515
|
+
case "initiative":
|
|
1516
|
+
return initiativeCmd(flags, _);
|
|
1517
|
+
case "start":
|
|
1518
|
+
return startCmd(flags, _);
|
|
940
1519
|
default:
|
|
941
1520
|
throw new Error(`Unknown command: ${command}`);
|
|
942
1521
|
}
|
|
943
1522
|
}
|
|
944
1523
|
function resetCmd(flags) {
|
|
945
|
-
const projectDir =
|
|
1524
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
946
1525
|
const force = flag(flags.force);
|
|
947
1526
|
const result = resetMission(projectDir, flag(flags.keepLogs), force);
|
|
948
1527
|
if (result.blocked) {
|
|
@@ -957,7 +1536,7 @@ function resetCmd(flags) {
|
|
|
957
1536
|
console.log(`kept: ${result.kept.join(", ")}`);
|
|
958
1537
|
}
|
|
959
1538
|
function archive(flags, positionals) {
|
|
960
|
-
const projectDir =
|
|
1539
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
961
1540
|
const mission = positionals[1];
|
|
962
1541
|
if (!mission) {
|
|
963
1542
|
console.error("usage: mugiwara archive <mission> [--project <dir>] [--dry-run]");
|
|
@@ -973,6 +1552,12 @@ function archive(flags, positionals) {
|
|
|
973
1552
|
if (result.index)
|
|
974
1553
|
console.log(`index updated: ${result.index}`);
|
|
975
1554
|
}
|
|
1555
|
+
async function onboardCmd(flags) {
|
|
1556
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1557
|
+
if (!existsSync10(projectDir))
|
|
1558
|
+
throw new Error(`Project dir not found: ${projectDir}`);
|
|
1559
|
+
await runOnboard(projectDir);
|
|
1560
|
+
}
|
|
976
1561
|
async function resolveOptions(flags) {
|
|
977
1562
|
const interactive = !flag(flags.yes);
|
|
978
1563
|
if (interactive && !process.stdin.isTTY) {
|
|
@@ -987,8 +1572,8 @@ async function resolveOptions(flags) {
|
|
|
987
1572
|
} else
|
|
988
1573
|
scope = await choose(rl, "Install scope?", ["global (user-wide)", "project (this repo)"]) === 0 ? "global" : "project";
|
|
989
1574
|
}
|
|
990
|
-
const projectDir =
|
|
991
|
-
if (scope === "project" && !
|
|
1575
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1576
|
+
if (scope === "project" && !existsSync10(projectDir))
|
|
992
1577
|
throw new Error(`Project dir not found: ${projectDir}`);
|
|
993
1578
|
let targetIds = str(flags.target)?.split(",").map((s) => s.trim()) ?? null;
|
|
994
1579
|
if (targetIds && targetIds.includes("all"))
|
|
@@ -1055,17 +1640,34 @@ Dry run — nothing written.`);
|
|
|
1055
1640
|
OK mugiwara ${VERSION} installed (manifest: ${file})`);
|
|
1056
1641
|
if (allNotes.length)
|
|
1057
1642
|
console.log(`${allNotes.length} note(s) above may need attention.`);
|
|
1643
|
+
if (scope === "project") {
|
|
1644
|
+
const cfg = join11(projectDir, ".mugiwara", "config");
|
|
1645
|
+
let mode = "guided", enforce = "block";
|
|
1646
|
+
if (existsSync10(cfg)) {
|
|
1647
|
+
for (const line of readFileSync8(cfg, "utf8").split(/\r?\n/)) {
|
|
1648
|
+
const [k, v] = line.split("=").map((s) => s.trim());
|
|
1649
|
+
if (k === "mode")
|
|
1650
|
+
mode = v;
|
|
1651
|
+
if (k === "enforce")
|
|
1652
|
+
enforce = v;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
console.log(`
|
|
1656
|
+
Next: run \`mugiwara onboard\` to customise (mode=${mode}, enforce=${enforce} now).`);
|
|
1657
|
+
} else {
|
|
1658
|
+
console.log("\nNext: run `mugiwara onboard` in a project to write its .mugiwara/config.");
|
|
1659
|
+
}
|
|
1058
1660
|
}
|
|
1059
1661
|
async function uninstall(flags) {
|
|
1060
1662
|
const scope = flag(flags.global) ? "global" : "project";
|
|
1061
|
-
const projectDir =
|
|
1663
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1062
1664
|
const home = homedir2();
|
|
1063
1665
|
const file = manifestPath({ scope, projectDir, home });
|
|
1064
1666
|
const manifest = readManifest(file);
|
|
1065
1667
|
if (!manifest) {
|
|
1066
1668
|
if (scope === "project") {
|
|
1067
1669
|
const globalFile = manifestPath({ scope: "global", projectDir, home });
|
|
1068
|
-
if (
|
|
1670
|
+
if (existsSync10(globalFile)) {
|
|
1069
1671
|
console.log(`No project manifest found, but a global install exists. Try:
|
|
1070
1672
|
mugiwara uninstall --global`);
|
|
1071
1673
|
return;
|
|
@@ -1085,6 +1687,16 @@ async function uninstall(flags) {
|
|
|
1085
1687
|
}
|
|
1086
1688
|
}
|
|
1087
1689
|
const removed = removeInstalled(manifest, { dryRun: flag(flags.dryRun) });
|
|
1690
|
+
for (const id of manifest.targets) {
|
|
1691
|
+
const t = targets[id];
|
|
1692
|
+
if (!t?.postUninstall)
|
|
1693
|
+
continue;
|
|
1694
|
+
const post = t.postUninstall({ scope, projectDir, home, dryRun: flag(flags.dryRun) });
|
|
1695
|
+
for (const f of post.changed)
|
|
1696
|
+
console.log(` unwired mugiwara hooks from ${f}`);
|
|
1697
|
+
for (const n of post.notes)
|
|
1698
|
+
console.log(` note: ${n}`);
|
|
1699
|
+
}
|
|
1088
1700
|
if (!flag(flags.dryRun)) {
|
|
1089
1701
|
if (scope === "project") {
|
|
1090
1702
|
const gi = removeProjectGitignore(projectDir);
|
|
@@ -1092,13 +1704,13 @@ async function uninstall(flags) {
|
|
|
1092
1704
|
console.log(` note: ${n}`);
|
|
1093
1705
|
}
|
|
1094
1706
|
rmSync3(file);
|
|
1095
|
-
const mugiDir =
|
|
1096
|
-
if (
|
|
1707
|
+
const mugiDir = join11(scope === "global" ? home : projectDir, ".mugiwara");
|
|
1708
|
+
if (existsSync10(mugiDir) && readdirSync7(mugiDir).length === 0) {
|
|
1097
1709
|
rmSync3(mugiDir, { recursive: true, force: true });
|
|
1098
1710
|
}
|
|
1099
1711
|
if (manifest.targets.includes("opencode")) {
|
|
1100
|
-
const opencodeCache =
|
|
1101
|
-
if (
|
|
1712
|
+
const opencodeCache = join11(home, ".cache", "opencode", "packages", "@ionivetech");
|
|
1713
|
+
if (existsSync10(opencodeCache)) {
|
|
1102
1714
|
rmSync3(opencodeCache, { recursive: true, force: true });
|
|
1103
1715
|
console.log(" cleared opencode npm cache (stale plugin versions)");
|
|
1104
1716
|
}
|
|
@@ -1108,7 +1720,7 @@ async function uninstall(flags) {
|
|
|
1108
1720
|
}
|
|
1109
1721
|
function list(flags) {
|
|
1110
1722
|
const home = homedir2();
|
|
1111
|
-
const projectDir =
|
|
1723
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1112
1724
|
let found = false;
|
|
1113
1725
|
for (const [label, file] of [
|
|
1114
1726
|
["project", manifestPath({ scope: "project", projectDir, home })],
|
|
@@ -1119,7 +1731,7 @@ function list(flags) {
|
|
|
1119
1731
|
continue;
|
|
1120
1732
|
found = true;
|
|
1121
1733
|
if (flag(flags.check)) {
|
|
1122
|
-
const missing = m.files.filter((f) => !
|
|
1734
|
+
const missing = m.files.filter((f) => !existsSync10(f));
|
|
1123
1735
|
console.log(`${label}: v${m.version} targets=${m.targets.join(",")} files=${m.files.length} missing=${missing.length} installed=${m.installedAt}`);
|
|
1124
1736
|
} else {
|
|
1125
1737
|
console.log(`${label}: v${m.version} targets=${m.targets.join(",")} files=${m.files.length} installed=${m.installedAt}`);
|
|
@@ -1128,6 +1740,155 @@ function list(flags) {
|
|
|
1128
1740
|
if (!found)
|
|
1129
1741
|
console.log("No mugiwara installation found.");
|
|
1130
1742
|
}
|
|
1743
|
+
function continueCmd(flags, positionals) {
|
|
1744
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1745
|
+
const [mission, member] = positionals.slice(1);
|
|
1746
|
+
let entries = readContinue(projectDir);
|
|
1747
|
+
if (!flag(flags.all)) {
|
|
1748
|
+
const actor = gitActor(projectDir);
|
|
1749
|
+
const mine = entries.filter((e) => e.actor === actor);
|
|
1750
|
+
if (mine.length)
|
|
1751
|
+
entries = mine;
|
|
1752
|
+
}
|
|
1753
|
+
const r = resolveContinue(entries, mission, member);
|
|
1754
|
+
if (r.kind === "resume") {
|
|
1755
|
+
console.log(formatResume(r.entry));
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
if (r.kind === "none") {
|
|
1759
|
+
console.log("No mission in flight. Start one with Flow 0 triage (mugiwara-orchestration).");
|
|
1760
|
+
} else if (r.kind === "missions") {
|
|
1761
|
+
console.log(`${new Set(r.entries.map((e) => e.mission)).size} missions in flight:
|
|
1762
|
+
`);
|
|
1763
|
+
console.log(formatTable(r.entries));
|
|
1764
|
+
console.log(`
|
|
1765
|
+
Pick one: mugiwara continue <mission> [member]`);
|
|
1766
|
+
} else if (r.kind === "members") {
|
|
1767
|
+
console.log(`Mission "${r.mission}" has ${r.entries.length} members in flight:
|
|
1768
|
+
`);
|
|
1769
|
+
console.log(formatTable(r.entries));
|
|
1770
|
+
console.log(`
|
|
1771
|
+
Pick one: mugiwara continue ${r.mission} <member>`);
|
|
1772
|
+
} else if (r.kind === "unknown-mission") {
|
|
1773
|
+
console.error(`No in-flight mission "${r.mission}". Known: ${r.known.join(", ") || "(none)"}`);
|
|
1774
|
+
} else {
|
|
1775
|
+
console.error(`Mission "${r.mission}" has no member "${r.member}". Known: ${r.known.join(", ")}`);
|
|
1776
|
+
}
|
|
1777
|
+
process.exit(2);
|
|
1778
|
+
}
|
|
1779
|
+
function statusCmd(flags) {
|
|
1780
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1781
|
+
const states = readState(projectDir);
|
|
1782
|
+
if (!states.length) {
|
|
1783
|
+
console.log("No mission state on disk.");
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
const actor = flag(flags.all) ? null : gitActor(projectDir);
|
|
1787
|
+
const rows = actor ? states.filter((s) => s.actor === actor).length ? states.filter((s) => s.actor === actor) : states : states;
|
|
1788
|
+
for (const s of rows) {
|
|
1789
|
+
const scope = s.member ? ` [${s.member}]` : "";
|
|
1790
|
+
console.log(`${s.mission}${scope}`);
|
|
1791
|
+
console.log(` flow ${s.flow} · ${s.tasks_done}/${s.tasks_total} tasks · lane ${s.lane}${s.lane_rose ? " ⬆ ROSE" : ""}${s.lane_reason ? ` (${s.lane_reason})` : ""} · mode ${s.mode}`);
|
|
1792
|
+
console.log(` blockers ${s.blockers_open} · heal cycle ${s.heal_cycle}/${s.heal_max_cycles}${s.heal_halt ? " — HALT" : ""} · files touched ${s.files_touched}`);
|
|
1793
|
+
if (s.budget)
|
|
1794
|
+
console.log(` tokens ${s.tokens_est}/${s.budget} (${s.budget_status})${s.delegate_due ? " · delegate due" : ""}`);
|
|
1795
|
+
console.log(` branch ${s.branch} · updated ${s.updated_at}`);
|
|
1796
|
+
if (s.evidence.length)
|
|
1797
|
+
console.log(` evidence: ${s.evidence.join(", ")}`);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
function runCmd(flags, positionals) {
|
|
1801
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1802
|
+
const name = positionals[1];
|
|
1803
|
+
if (!name) {
|
|
1804
|
+
console.error(`usage: mugiwara run <script> [args...]
|
|
1805
|
+
scripts: ${RUNNABLE.join(", ")}`);
|
|
1806
|
+
process.exit(1);
|
|
1807
|
+
}
|
|
1808
|
+
const code = runScript(name, positionals.slice(2), projectDir);
|
|
1809
|
+
if (code !== 0)
|
|
1810
|
+
process.exit(code);
|
|
1811
|
+
}
|
|
1812
|
+
function initiativeCmd(flags, positionals) {
|
|
1813
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1814
|
+
const sub = positionals[1];
|
|
1815
|
+
if (!sub) {
|
|
1816
|
+
console.error("usage: mugiwara initiative <status|conflict-check|set-status> <plan-file> [--id <id> --status <s>]");
|
|
1817
|
+
process.exit(1);
|
|
1818
|
+
}
|
|
1819
|
+
const script = join11(import.meta.dirname, "..", "scripts", "initiative.ts");
|
|
1820
|
+
const bun = process.platform === "win32" ? "bun.exe" : "bun";
|
|
1821
|
+
const r = spawnSync2(bun, [script, ...positionals.slice(1)], {
|
|
1822
|
+
cwd: projectDir,
|
|
1823
|
+
stdio: "inherit",
|
|
1824
|
+
env: process.env
|
|
1825
|
+
});
|
|
1826
|
+
if (r.error)
|
|
1827
|
+
throw r.error;
|
|
1828
|
+
if (r.status)
|
|
1829
|
+
process.exit(r.status);
|
|
1830
|
+
}
|
|
1831
|
+
function startCmd(flags, positionals) {
|
|
1832
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1833
|
+
const mission = positionals[1];
|
|
1834
|
+
if (!mission) {
|
|
1835
|
+
console.error("usage: mugiwara start <mission> [member]");
|
|
1836
|
+
process.exit(1);
|
|
1837
|
+
}
|
|
1838
|
+
const plansDir = join11(projectDir, ".mugiwara", "plans");
|
|
1839
|
+
let planFile = "";
|
|
1840
|
+
if (existsSync10(plansDir)) {
|
|
1841
|
+
const bare = join11(plansDir, `${mission}.md`);
|
|
1842
|
+
if (existsSync10(bare))
|
|
1843
|
+
planFile = bare;
|
|
1844
|
+
else {
|
|
1845
|
+
const dated = readdirSync7(plansDir).filter((f) => f.endsWith(`-${mission}.md`)).sort().pop();
|
|
1846
|
+
if (dated)
|
|
1847
|
+
planFile = join11(plansDir, dated);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
if (!planFile) {
|
|
1851
|
+
console.error(`mugiwara start: no plan found for "${mission}" in ${plansDir}`);
|
|
1852
|
+
process.exit(1);
|
|
1853
|
+
}
|
|
1854
|
+
const plan = readFileSync8(planFile, "utf8");
|
|
1855
|
+
const subMatch = plan.match(/##\s*Sub-missions/i);
|
|
1856
|
+
let member = positionals[2] ?? "";
|
|
1857
|
+
if (subMatch && !member) {
|
|
1858
|
+
const actor = gitActor(projectDir);
|
|
1859
|
+
const name = actor.split("<")[0].trim();
|
|
1860
|
+
const rowRe = /^\|\s*([\w.-]+)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|/gm;
|
|
1861
|
+
let m;
|
|
1862
|
+
let found = "";
|
|
1863
|
+
while (m = rowRe.exec(plan)) {
|
|
1864
|
+
const assignee = m[3].trim();
|
|
1865
|
+
if (assignee === name || assignee === actor) {
|
|
1866
|
+
found = m[1].trim();
|
|
1867
|
+
break;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
if (!found) {
|
|
1871
|
+
console.error(`mugiwara start: no sub-mission assigned to "${name}" in ${planFile}. Pass a member explicitly: mugiwara start ${mission} <member>`);
|
|
1872
|
+
process.exit(1);
|
|
1873
|
+
}
|
|
1874
|
+
member = found;
|
|
1875
|
+
}
|
|
1876
|
+
const code = runScript("savepoint.sh", [mission, member, "0", "auto", "full"], projectDir);
|
|
1877
|
+
if (code !== 0)
|
|
1878
|
+
process.exit(code);
|
|
1879
|
+
if (subMatch && member) {
|
|
1880
|
+
const init = join11(import.meta.dirname, "..", "scripts", "initiative.ts");
|
|
1881
|
+
const bun = process.platform === "win32" ? "bun.exe" : "bun";
|
|
1882
|
+
const r = spawnSync2(bun, [init, "set-status", planFile, "--id", member, "--status", "in-progress"], {
|
|
1883
|
+
cwd: projectDir,
|
|
1884
|
+
stdio: "inherit",
|
|
1885
|
+
env: process.env
|
|
1886
|
+
});
|
|
1887
|
+
if (r.status)
|
|
1888
|
+
process.exit(r.status);
|
|
1889
|
+
}
|
|
1890
|
+
console.log(`mugiwara start: ${mission}${member ? ` [${member}]` : ""} begun at Flow 0. Resume anytime with: mugiwara continue ${mission}${member ? ` ${member}` : ""}`);
|
|
1891
|
+
}
|
|
1131
1892
|
function help() {
|
|
1132
1893
|
console.log(`mugiwara ${VERSION} — the Straw Hat crew for AI agents
|
|
1133
1894
|
|
|
@@ -1139,6 +1900,19 @@ Usage:
|
|
|
1139
1900
|
mugiwara list --check health check: show installations + missing files
|
|
1140
1901
|
mugiwara reset wipe mission state (spec/plans/results/review/issues[/logs])
|
|
1141
1902
|
mugiwara archive <m> fold a closed mission's evidence into its report, then remove loose files
|
|
1903
|
+
mugiwara onboard run the zero-LLM onboarding wizard (writes .mugiwara/config)
|
|
1904
|
+
mugiwara continue list in-flight missions (exit 2 = pick one, nothing resumed)
|
|
1905
|
+
mugiwara continue <m> [member]
|
|
1906
|
+
print the exact resume point for that mission/member
|
|
1907
|
+
mugiwara status computed mission state: wave, tasks, lane, blockers, budget
|
|
1908
|
+
mugiwara run <script> [args...]
|
|
1909
|
+
run a bundled harness script here (${RUNNABLE.join(", ")})
|
|
1910
|
+
mugiwara savepoint <mission> [member] [wave] [mode]
|
|
1911
|
+
shorthand for: mugiwara run savepoint.sh ...
|
|
1912
|
+
mugiwara initiative <status|conflict-check|set-status> <plan> [args]
|
|
1913
|
+
team sub-mission coordination (status / conflict-check / set-status)
|
|
1914
|
+
mugiwara start <mission> [member]
|
|
1915
|
+
begin a mission from its plan (solo, or team sub-mission by git actor)
|
|
1142
1916
|
mugiwara --help this help
|
|
1143
1917
|
mugiwara --version print version
|
|
1144
1918
|
|
|
@@ -1150,9 +1924,10 @@ Flags:
|
|
|
1150
1924
|
--force overwrite differing files (with backup)
|
|
1151
1925
|
--dry-run print actions without writing
|
|
1152
1926
|
--check with list: report missing files (health check)
|
|
1927
|
+
--all with continue/status: every actor, not just yours
|
|
1153
1928
|
--keep-logs with reset: keep .mugiwara/logs (lessons ledger survives)`);
|
|
1154
1929
|
}
|
|
1155
|
-
var isMain = process.argv[1] !== undefined &&
|
|
1930
|
+
var isMain = process.argv[1] !== undefined && fileURLToPath5(import.meta.url) === resolve2(process.argv[1]);
|
|
1156
1931
|
if (isMain) {
|
|
1157
1932
|
run(process.argv.slice(2)).catch((err) => {
|
|
1158
1933
|
console.error(`mugiwara: ${err.message}`);
|