@ionivetech/mugiwara 0.6.4 → 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 +48 -2
- package/.cursor-plugin/plugin.json +48 -2
- package/.kimi-plugin/plugin.json +48 -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/mugiwara-helpers.mjs +3 -0
- package/.opencode/plugins/mugiwara.mjs +1 -1
- package/AGENTS.md +12 -2
- package/README.md +53 -32
- 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 +14 -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 +27 -27
- 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 +9 -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-lessons/SKILL.md +1 -0
- package/content/skills/mugiwara-orchestration/SKILL.md +34 -35
- package/content/skills/mugiwara-orchestration/references/check-ins.md +17 -16
- package/content/skills/mugiwara-orchestration/references/closure.md +5 -5
- package/content/skills/mugiwara-orchestration/references/output-contract.md +77 -0
- 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 +8 -7
- package/content/skills/mugiwara-resume/SKILL.md +19 -20
- package/content/skills/mugiwara-review/SKILL.md +6 -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 +38 -39
- package/content/skills/mugiwara-workflow/references/workspace-layout.md +15 -15
- package/content/skills/using-mugiwara/SKILL.md +5 -5
- package/dist/mugiwara.js +807 -31
- 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 +21 -31
- package/scripts/build-hooks.ts +56 -0
- package/scripts/conformance.ts +232 -0
- package/scripts/coverage-gate.ts +179 -0
- package/scripts/evidence.sh +17 -4
- package/scripts/gate-selftest.ts +14 -0
- package/scripts/initiative.ts +51 -17
- 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 +152 -37
- package/scripts/validate-content.ts +22 -2
- 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/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
|
|
|
@@ -318,7 +431,17 @@ var target3 = {
|
|
|
318
431
|
transformSkill(data, body) {
|
|
319
432
|
return {
|
|
320
433
|
relPath: `${data.name}.instructions.md`,
|
|
321
|
-
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)
|
|
322
445
|
};
|
|
323
446
|
},
|
|
324
447
|
transformAgent(data, body) {
|
|
@@ -333,7 +456,7 @@ var target3 = {
|
|
|
333
456
|
};
|
|
334
457
|
|
|
335
458
|
// src/targets/generic.ts
|
|
336
|
-
import { existsSync as existsSync3, writeFileSync } from "node:fs";
|
|
459
|
+
import { existsSync as existsSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
337
460
|
import { join as join4 } from "node:path";
|
|
338
461
|
function makeGeneric(opts) {
|
|
339
462
|
const { id, label, rulesDir, bootstrapFile, bootstrapPointer, tier = bootstrapPointer ? 2 : 3 } = opts;
|
|
@@ -359,7 +482,7 @@ function makeGeneric(opts) {
|
|
|
359
482
|
|
|
360
483
|
## Skip when
|
|
361
484
|
|
|
362
|
-
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.`
|
|
363
486
|
};
|
|
364
487
|
}
|
|
365
488
|
return { relPath: `${data.name}.md`, text: `# ${data.name}
|
|
@@ -385,7 +508,7 @@ ${body}` };
|
|
|
385
508
|
|
|
386
509
|
> ${data.description}
|
|
387
510
|
|
|
388
|
-
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.
|
|
389
512
|
|
|
390
513
|
Only zoro-execution and brook-healing may modify source code.
|
|
391
514
|
Return your output to luffy-orchestrator; do not choose the next step.`
|
|
@@ -410,8 +533,8 @@ Skills used: ${data.skills ?? ""}
|
|
|
410
533
|
|
|
411
534
|
${body}` };
|
|
412
535
|
},
|
|
413
|
-
refsDir({ projectDir }) {
|
|
414
|
-
return join4(projectDir, ".mugiwara", "refs");
|
|
536
|
+
refsDir({ projectDir }, skillName) {
|
|
537
|
+
return join4(projectDir, ".mugiwara", "refs", skillName);
|
|
415
538
|
},
|
|
416
539
|
postInstall({ projectDir, dryRun }) {
|
|
417
540
|
if (!bootstrapFile)
|
|
@@ -421,7 +544,7 @@ ${body}` };
|
|
|
421
544
|
const file = join4(projectDir, bootstrapFile);
|
|
422
545
|
if (!existsSync3(file)) {
|
|
423
546
|
if (!dryRun)
|
|
424
|
-
|
|
547
|
+
writeFileSync2(file, `${bootstrapPointer}
|
|
425
548
|
`);
|
|
426
549
|
written.push(file);
|
|
427
550
|
} else {
|
|
@@ -496,7 +619,7 @@ var targets = { claude: target, opencode: target2, copilot: target3, gemini: tar
|
|
|
496
619
|
var TARGET_IDS = Object.keys(targets);
|
|
497
620
|
|
|
498
621
|
// src/installer.ts
|
|
499
|
-
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";
|
|
500
623
|
import { dirname as dirname3, join as join5 } from "node:path";
|
|
501
624
|
import { homedir } from "node:os";
|
|
502
625
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -560,7 +683,7 @@ function installTo(target10, opts) {
|
|
|
560
683
|
}
|
|
561
684
|
if (!dryRun) {
|
|
562
685
|
mkdirSync3(dirname3(absPath), { recursive: true });
|
|
563
|
-
|
|
686
|
+
writeFileSync3(absPath, text);
|
|
564
687
|
}
|
|
565
688
|
result.written.push(absPath);
|
|
566
689
|
};
|
|
@@ -609,6 +732,38 @@ function installTo(target10, opts) {
|
|
|
609
732
|
result.written.push(...post.written);
|
|
610
733
|
result.notes.push(...post.notes);
|
|
611
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
|
+
}
|
|
612
767
|
return result;
|
|
613
768
|
}
|
|
614
769
|
function removeInstalled(manifest, { dryRun = false } = {}) {
|
|
@@ -676,7 +831,7 @@ function ensureProjectGitignore(projectDir, opts = {}) {
|
|
|
676
831
|
` : "";
|
|
677
832
|
if (!dryRun) {
|
|
678
833
|
mkdirSync3(dirname3(path), { recursive: true });
|
|
679
|
-
|
|
834
|
+
writeFileSync3(path, existing + separator + GITIGNORE_BLOCK);
|
|
680
835
|
}
|
|
681
836
|
return { appended: true, notes: [`.gitignore ${dryRun ? "would upgrade+append" : "upgraded"} mugiwara audit-trail block`] };
|
|
682
837
|
}
|
|
@@ -723,12 +878,12 @@ function removeProjectGitignore(projectDir, { dryRun = false } = {}) {
|
|
|
723
878
|
if (cleaned === current)
|
|
724
879
|
return { removed: false, notes: [] };
|
|
725
880
|
if (!dryRun)
|
|
726
|
-
|
|
881
|
+
writeFileSync3(path, cleaned);
|
|
727
882
|
return { removed: true, notes: [`.gitignore ${dryRun ? "would strip" : "stripped"} mugiwara block`] };
|
|
728
883
|
}
|
|
729
884
|
|
|
730
885
|
// src/manifest.ts
|
|
731
|
-
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";
|
|
732
887
|
import { dirname as dirname4, join as join6 } from "node:path";
|
|
733
888
|
function manifestPath({ scope, projectDir, home }) {
|
|
734
889
|
return scope === "global" ? join6(home, ".mugiwara", "manifest.json") : join6(projectDir, ".mugiwara", "manifest.json");
|
|
@@ -738,7 +893,7 @@ function readManifest(file) {
|
|
|
738
893
|
}
|
|
739
894
|
function writeManifest(file, data) {
|
|
740
895
|
mkdirSync4(dirname4(file), { recursive: true });
|
|
741
|
-
|
|
896
|
+
writeFileSync4(file, JSON.stringify(data, null, 2) + `
|
|
742
897
|
`);
|
|
743
898
|
}
|
|
744
899
|
|
|
@@ -912,6 +1067,417 @@ function archiveMission(projectDir, mission, opts = {}) {
|
|
|
912
1067
|
return { report, removed, kept, index };
|
|
913
1068
|
}
|
|
914
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
|
+
|
|
915
1481
|
// src/cli.ts
|
|
916
1482
|
var str = (v) => typeof v === "string" ? v : undefined;
|
|
917
1483
|
var flag = (v) => v === true;
|
|
@@ -936,12 +1502,26 @@ async function run(argv) {
|
|
|
936
1502
|
return resetCmd(flags);
|
|
937
1503
|
case "archive":
|
|
938
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, _);
|
|
939
1519
|
default:
|
|
940
1520
|
throw new Error(`Unknown command: ${command}`);
|
|
941
1521
|
}
|
|
942
1522
|
}
|
|
943
1523
|
function resetCmd(flags) {
|
|
944
|
-
const projectDir =
|
|
1524
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
945
1525
|
const force = flag(flags.force);
|
|
946
1526
|
const result = resetMission(projectDir, flag(flags.keepLogs), force);
|
|
947
1527
|
if (result.blocked) {
|
|
@@ -956,7 +1536,7 @@ function resetCmd(flags) {
|
|
|
956
1536
|
console.log(`kept: ${result.kept.join(", ")}`);
|
|
957
1537
|
}
|
|
958
1538
|
function archive(flags, positionals) {
|
|
959
|
-
const projectDir =
|
|
1539
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
960
1540
|
const mission = positionals[1];
|
|
961
1541
|
if (!mission) {
|
|
962
1542
|
console.error("usage: mugiwara archive <mission> [--project <dir>] [--dry-run]");
|
|
@@ -972,6 +1552,12 @@ function archive(flags, positionals) {
|
|
|
972
1552
|
if (result.index)
|
|
973
1553
|
console.log(`index updated: ${result.index}`);
|
|
974
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
|
+
}
|
|
975
1561
|
async function resolveOptions(flags) {
|
|
976
1562
|
const interactive = !flag(flags.yes);
|
|
977
1563
|
if (interactive && !process.stdin.isTTY) {
|
|
@@ -986,8 +1572,8 @@ async function resolveOptions(flags) {
|
|
|
986
1572
|
} else
|
|
987
1573
|
scope = await choose(rl, "Install scope?", ["global (user-wide)", "project (this repo)"]) === 0 ? "global" : "project";
|
|
988
1574
|
}
|
|
989
|
-
const projectDir =
|
|
990
|
-
if (scope === "project" && !
|
|
1575
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1576
|
+
if (scope === "project" && !existsSync10(projectDir))
|
|
991
1577
|
throw new Error(`Project dir not found: ${projectDir}`);
|
|
992
1578
|
let targetIds = str(flags.target)?.split(",").map((s) => s.trim()) ?? null;
|
|
993
1579
|
if (targetIds && targetIds.includes("all"))
|
|
@@ -1054,17 +1640,34 @@ Dry run — nothing written.`);
|
|
|
1054
1640
|
OK mugiwara ${VERSION} installed (manifest: ${file})`);
|
|
1055
1641
|
if (allNotes.length)
|
|
1056
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
|
+
}
|
|
1057
1660
|
}
|
|
1058
1661
|
async function uninstall(flags) {
|
|
1059
1662
|
const scope = flag(flags.global) ? "global" : "project";
|
|
1060
|
-
const projectDir =
|
|
1663
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1061
1664
|
const home = homedir2();
|
|
1062
1665
|
const file = manifestPath({ scope, projectDir, home });
|
|
1063
1666
|
const manifest = readManifest(file);
|
|
1064
1667
|
if (!manifest) {
|
|
1065
1668
|
if (scope === "project") {
|
|
1066
1669
|
const globalFile = manifestPath({ scope: "global", projectDir, home });
|
|
1067
|
-
if (
|
|
1670
|
+
if (existsSync10(globalFile)) {
|
|
1068
1671
|
console.log(`No project manifest found, but a global install exists. Try:
|
|
1069
1672
|
mugiwara uninstall --global`);
|
|
1070
1673
|
return;
|
|
@@ -1084,6 +1687,16 @@ async function uninstall(flags) {
|
|
|
1084
1687
|
}
|
|
1085
1688
|
}
|
|
1086
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
|
+
}
|
|
1087
1700
|
if (!flag(flags.dryRun)) {
|
|
1088
1701
|
if (scope === "project") {
|
|
1089
1702
|
const gi = removeProjectGitignore(projectDir);
|
|
@@ -1091,13 +1704,13 @@ async function uninstall(flags) {
|
|
|
1091
1704
|
console.log(` note: ${n}`);
|
|
1092
1705
|
}
|
|
1093
1706
|
rmSync3(file);
|
|
1094
|
-
const mugiDir =
|
|
1095
|
-
if (
|
|
1707
|
+
const mugiDir = join11(scope === "global" ? home : projectDir, ".mugiwara");
|
|
1708
|
+
if (existsSync10(mugiDir) && readdirSync7(mugiDir).length === 0) {
|
|
1096
1709
|
rmSync3(mugiDir, { recursive: true, force: true });
|
|
1097
1710
|
}
|
|
1098
1711
|
if (manifest.targets.includes("opencode")) {
|
|
1099
|
-
const opencodeCache =
|
|
1100
|
-
if (
|
|
1712
|
+
const opencodeCache = join11(home, ".cache", "opencode", "packages", "@ionivetech");
|
|
1713
|
+
if (existsSync10(opencodeCache)) {
|
|
1101
1714
|
rmSync3(opencodeCache, { recursive: true, force: true });
|
|
1102
1715
|
console.log(" cleared opencode npm cache (stale plugin versions)");
|
|
1103
1716
|
}
|
|
@@ -1107,7 +1720,7 @@ async function uninstall(flags) {
|
|
|
1107
1720
|
}
|
|
1108
1721
|
function list(flags) {
|
|
1109
1722
|
const home = homedir2();
|
|
1110
|
-
const projectDir =
|
|
1723
|
+
const projectDir = resolve2(str(flags.project) ?? process.cwd());
|
|
1111
1724
|
let found = false;
|
|
1112
1725
|
for (const [label, file] of [
|
|
1113
1726
|
["project", manifestPath({ scope: "project", projectDir, home })],
|
|
@@ -1118,7 +1731,7 @@ function list(flags) {
|
|
|
1118
1731
|
continue;
|
|
1119
1732
|
found = true;
|
|
1120
1733
|
if (flag(flags.check)) {
|
|
1121
|
-
const missing = m.files.filter((f) => !
|
|
1734
|
+
const missing = m.files.filter((f) => !existsSync10(f));
|
|
1122
1735
|
console.log(`${label}: v${m.version} targets=${m.targets.join(",")} files=${m.files.length} missing=${missing.length} installed=${m.installedAt}`);
|
|
1123
1736
|
} else {
|
|
1124
1737
|
console.log(`${label}: v${m.version} targets=${m.targets.join(",")} files=${m.files.length} installed=${m.installedAt}`);
|
|
@@ -1127,6 +1740,155 @@ function list(flags) {
|
|
|
1127
1740
|
if (!found)
|
|
1128
1741
|
console.log("No mugiwara installation found.");
|
|
1129
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
|
+
}
|
|
1130
1892
|
function help() {
|
|
1131
1893
|
console.log(`mugiwara ${VERSION} — the Straw Hat crew for AI agents
|
|
1132
1894
|
|
|
@@ -1138,6 +1900,19 @@ Usage:
|
|
|
1138
1900
|
mugiwara list --check health check: show installations + missing files
|
|
1139
1901
|
mugiwara reset wipe mission state (spec/plans/results/review/issues[/logs])
|
|
1140
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)
|
|
1141
1916
|
mugiwara --help this help
|
|
1142
1917
|
mugiwara --version print version
|
|
1143
1918
|
|
|
@@ -1149,9 +1924,10 @@ Flags:
|
|
|
1149
1924
|
--force overwrite differing files (with backup)
|
|
1150
1925
|
--dry-run print actions without writing
|
|
1151
1926
|
--check with list: report missing files (health check)
|
|
1927
|
+
--all with continue/status: every actor, not just yours
|
|
1152
1928
|
--keep-logs with reset: keep .mugiwara/logs (lessons ledger survives)`);
|
|
1153
1929
|
}
|
|
1154
|
-
var isMain = process.argv[1] !== undefined &&
|
|
1930
|
+
var isMain = process.argv[1] !== undefined && fileURLToPath5(import.meta.url) === resolve2(process.argv[1]);
|
|
1155
1931
|
if (isMain) {
|
|
1156
1932
|
run(process.argv.slice(2)).catch((err) => {
|
|
1157
1933
|
console.error(`mugiwara: ${err.message}`);
|