@ionivetech/mugiwara 0.5.5 → 0.6.1
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 +3 -2
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.kimi-plugin/plugin.json +1 -1
- package/.opencode/commands/mugiwara-continue.md +16 -0
- package/.opencode/commands/mugiwara-execute.md +6 -4
- package/.opencode/commands/mugiwara-heal.md +6 -4
- package/.opencode/commands/mugiwara-plan.md +6 -4
- package/.opencode/commands/mugiwara-review.md +6 -4
- package/.opencode/commands/mugiwara-security.md +6 -4
- package/.opencode/commands/mugiwara-ship.md +6 -4
- package/.opencode/commands/mugiwara.md +4 -2
- package/.opencode/commands/using-mugiwara.md +7 -13
- package/.opencode/mugiwara-helpers.mjs +24 -0
- package/.opencode/plugins/mugiwara.mjs +28 -8
- package/AGENTS.md +1 -1
- package/GEMINI.md +1 -1
- package/README.md +311 -127
- package/content/agents/brook-healing.md +17 -3
- package/content/agents/chopper-checkpoint.md +17 -3
- package/content/agents/eval-runner.md +17 -2
- package/content/agents/franky-gates.md +19 -5
- package/content/agents/jinbe-security.md +19 -3
- package/content/agents/luffy-orchestrator.md +6 -3
- package/content/agents/memory-keeper.md +15 -0
- package/content/agents/nami-planner.md +21 -6
- package/content/agents/onboarding-guide.md +145 -0
- package/content/agents/resume-coordinator.md +21 -5
- package/content/agents/robin-reviewer.md +18 -3
- package/content/agents/sanji-quality.md +19 -4
- package/content/agents/skeptic-verifier.md +18 -3
- package/content/agents/usopp-brainstorm.md +17 -3
- package/content/agents/zoro-execution.md +18 -3
- package/content/skills/mugiwara-backend/SKILL.md +1 -1
- package/content/skills/mugiwara-brainstorm/SKILL.md +12 -1
- package/content/skills/mugiwara-checkpoint/SKILL.md +2 -2
- package/content/skills/mugiwara-contract-first/SKILL.md +2 -36
- package/content/skills/mugiwara-contract-first/references/process.md +37 -0
- package/content/skills/mugiwara-execution/SKILL.md +51 -34
- package/content/skills/mugiwara-execution/references/dispatch.md +41 -0
- package/content/skills/mugiwara-execution/references/resume-batching.md +32 -0
- package/content/skills/mugiwara-frontend/SKILL.md +1 -1
- package/content/skills/mugiwara-gates/SKILL.md +32 -30
- package/content/skills/mugiwara-healing/SKILL.md +1 -1
- package/content/skills/mugiwara-orchestration/SKILL.md +59 -39
- package/content/skills/mugiwara-orchestration/references/closure.md +34 -0
- package/content/skills/mugiwara-orchestration/references/delegation-pillars.md +40 -0
- package/content/skills/mugiwara-orchestration/references/triage-escalation.md +97 -0
- package/content/skills/mugiwara-planning/SKILL.md +29 -27
- package/content/skills/mugiwara-planning/references/plan-template.md +42 -0
- package/content/skills/mugiwara-pr/SKILL.md +9 -2
- package/content/skills/mugiwara-quality/SKILL.md +17 -6
- package/content/skills/mugiwara-resume/SKILL.md +7 -2
- package/content/skills/mugiwara-review/SKILL.md +20 -1
- package/content/skills/mugiwara-review/references/code-attributes.md +38 -0
- package/content/skills/mugiwara-root-cause/SKILL.md +3 -30
- package/content/skills/mugiwara-root-cause/references/process.md +35 -0
- package/content/skills/mugiwara-security/SKILL.md +17 -32
- package/content/skills/mugiwara-security/references/owasp-top10.md +12 -0
- package/content/skills/mugiwara-security/references/rationalizations.md +8 -0
- package/content/skills/mugiwara-ship/SKILL.md +13 -10
- package/content/skills/mugiwara-testcases/SKILL.md +7 -0
- package/content/skills/mugiwara-workflow/SKILL.md +74 -77
- package/content/skills/mugiwara-workflow/references/workspace-layout.md +50 -0
- package/content/skills/using-mugiwara/SKILL.md +13 -18
- package/dist/mugiwara.js +293 -47
- package/gemini-extension.json +1 -1
- package/hooks/mugiwara-mode-tracker.ts +0 -0
- package/hooks/session-start.ts +1 -1
- package/package.json +11 -4
- package/plugin.json +1 -1
- package/references/multi-actor.md +1 -1
- package/scripts/evidence.sh +28 -6
- package/scripts/gate-selftest.ts +317 -0
- package/scripts/initiative.ts +262 -0
- package/scripts/lane.sh +18 -1
- package/scripts/mission-report.sh +237 -36
- package/scripts/onboard.ts +292 -0
- package/scripts/release-notes.ts +42 -13
- package/scripts/savepoint.sh +151 -60
- package/scripts/validate-content.ts +97 -0
- package/scripts/verify-install.ts +72 -0
- package/src/cli.ts +20 -3
- package/src/installer.ts +71 -8
- package/src/mission.ts +117 -5
- package/src/targets/claude.ts +45 -9
- package/src/targets/generic.ts +1 -1
- package/src/targets/opencode.ts +31 -10
- package/docs/concepts/agents.md +0 -53
- package/docs/concepts/audit-trail.md +0 -65
- package/docs/concepts/comparison.md +0 -58
- package/docs/concepts/config.md +0 -55
- package/docs/concepts/cost.md +0 -45
- package/docs/concepts/execution-model.md +0 -92
- package/docs/concepts/git-strategy.md +0 -62
- package/docs/concepts/lanes.md +0 -82
- package/docs/concepts/modes.md +0 -73
- package/docs/concepts/pr-summary.md +0 -54
- package/docs/concepts/skills.md +0 -55
- package/docs/concepts/workflow.md +0 -89
- package/docs/getting-started.md +0 -158
- package/docs/index.md +0 -56
- package/docs/install/antigravity.md +0 -45
- package/docs/install/claude.md +0 -77
- package/docs/install/cli.md +0 -115
- package/docs/install/codex.md +0 -44
- package/docs/install/copilot.md +0 -45
- package/docs/install/cursor.md +0 -45
- package/docs/install/gemini.md +0 -44
- package/docs/install/index.md +0 -53
- package/docs/install/kimi.md +0 -45
- package/docs/install/opencode.md +0 -143
- package/docs/install/pi.md +0 -46
- package/docs/reference/adoption-guide.md +0 -72
- package/docs/reference/agent-anatomy.md +0 -72
- package/docs/reference/compliance-matrix.md +0 -81
- package/docs/reference/developer-onboarding.md +0 -89
- package/docs/reference/enforcement.md +0 -35
- package/docs/reference/harness-matrix.md +0 -41
- package/docs/reference/skill-anatomy.md +0 -71
- package/docs/troubleshooting.md +0 -91
- package/evals/cases/_no-skill.json +0 -16
- package/evals/cases/adversarial-pressure-fake-pass.json +0 -25
- package/evals/cases/adversarial-pressure-skip-review.json +0 -25
- package/evals/cases/lane-exploratory-vague.json +0 -24
- package/evals/cases/lane-sensitivity-payment.json +0 -24
- package/evals/cases/positive-refactor-existing-tests.json +0 -25
- package/evals/cases/positive-resume-mid-mission.json +0 -24
- package/evals/cases/routing-agent-security.json +0 -25
- package/evals/cases/routing-auth-feature.json +0 -26
- package/evals/cases/routing-backend.json +0 -25
- package/evals/cases/routing-bug-one-file.json +0 -26
- package/evals/cases/routing-claim-audit.json +0 -25
- package/evals/cases/routing-context-budget.json +0 -25
- package/evals/cases/routing-contract-first.json +0 -25
- package/evals/cases/routing-execution.json +0 -25
- package/evals/cases/routing-frontend.json +0 -26
- package/evals/cases/routing-gates.json +0 -25
- package/evals/cases/routing-git.json +0 -25
- package/evals/cases/routing-healing.json +0 -25
- package/evals/cases/routing-lessons.json +0 -25
- package/evals/cases/routing-orchestration.json +0 -25
- package/evals/cases/routing-planning.json +0 -26
- package/evals/cases/routing-pr.json +0 -25
- package/evals/cases/routing-quality.json +0 -25
- package/evals/cases/routing-ship.json +0 -26
- package/evals/cases/routing-sunset.json +0 -25
- package/evals/cases/routing-using-mugiwara.json +0 -25
- package/evals/cases/routing-workflow.json +0 -25
- package/evals/floor.json +0 -6
package/dist/mugiwara.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { existsSync as existsSync7, readdirSync as
|
|
4
|
+
import { existsSync as existsSync7, readdirSync as readdirSync5, rmSync as rmSync3 } from "node:fs";
|
|
5
5
|
import { homedir as homedir2 } from "node:os";
|
|
6
6
|
import { join as join8, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
@@ -81,7 +81,7 @@ async function confirm(rl, question) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// src/targets/claude.ts
|
|
84
|
-
import { existsSync, mkdirSync, copyFileSync } from "node:fs";
|
|
84
|
+
import { existsSync, mkdirSync, copyFileSync, chmodSync, readdirSync } from "node:fs";
|
|
85
85
|
import { dirname, join } from "node:path";
|
|
86
86
|
import { fileURLToPath } from "node:url";
|
|
87
87
|
|
|
@@ -112,11 +112,18 @@ ${body}`;
|
|
|
112
112
|
|
|
113
113
|
// src/targets/claude.ts
|
|
114
114
|
var here = dirname(fileURLToPath(import.meta.url));
|
|
115
|
-
var
|
|
115
|
+
var HOOKS_SRC = join(here, "..", "..", "hooks");
|
|
116
|
+
var COMMANDS_SRC = join(here, "..", "..", ".claude", "commands");
|
|
117
|
+
function toolsFromScope(scope) {
|
|
118
|
+
if (scope === "artifacts")
|
|
119
|
+
return "Read, Grep, Glob, Write, Bash, WebFetch, WebSearch";
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
116
122
|
var target = {
|
|
117
123
|
id: "claude",
|
|
118
124
|
label: "Claude Code",
|
|
119
125
|
native: true,
|
|
126
|
+
refPointerPrefix: "../",
|
|
120
127
|
paths({ scope, projectDir, home }) {
|
|
121
128
|
const root = scope === "global" ? join(home, ".claude") : join(projectDir, ".claude");
|
|
122
129
|
return { skillsDir: join(root, "skills"), agentsDir: join(root, "agents") };
|
|
@@ -131,6 +138,11 @@ var target = {
|
|
|
131
138
|
const fm = { name: data.name, description: data.description };
|
|
132
139
|
if (data.tools)
|
|
133
140
|
fm.tools = data.tools;
|
|
141
|
+
else {
|
|
142
|
+
const generated = toolsFromScope(data["write-scope"]);
|
|
143
|
+
if (generated)
|
|
144
|
+
fm.tools = generated;
|
|
145
|
+
}
|
|
134
146
|
return { relPath: `${data.name}.md`, text: stringifyFrontmatter(fm, body) };
|
|
135
147
|
},
|
|
136
148
|
refsDir({ scope, projectDir, home }, skillName) {
|
|
@@ -139,49 +151,88 @@ var target = {
|
|
|
139
151
|
},
|
|
140
152
|
postInstall({ scope, projectDir, home, dryRun }) {
|
|
141
153
|
const root = scope === "global" ? join(home, ".claude") : join(projectDir, ".claude");
|
|
142
|
-
const
|
|
154
|
+
const written = [];
|
|
155
|
+
const notes = [];
|
|
143
156
|
if (dryRun)
|
|
144
157
|
return { written: [], notes: [] };
|
|
145
|
-
if (existsSync(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
if (existsSync(HOOKS_SRC)) {
|
|
159
|
+
for (const f of readdirSync(HOOKS_SRC)) {
|
|
160
|
+
if (!f.endsWith(".ts"))
|
|
161
|
+
continue;
|
|
162
|
+
const dst = join(root, "hooks", f);
|
|
163
|
+
if (!existsSync(dst)) {
|
|
164
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
165
|
+
copyFileSync(join(HOOKS_SRC, f), dst);
|
|
166
|
+
chmodSync(dst, 493);
|
|
167
|
+
written.push(dst);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
149
170
|
}
|
|
150
|
-
|
|
171
|
+
if (existsSync(COMMANDS_SRC)) {
|
|
172
|
+
const dstDir = join(root, "commands");
|
|
173
|
+
mkdirSync(dstDir, { recursive: true });
|
|
174
|
+
for (const f of readdirSync(COMMANDS_SRC)) {
|
|
175
|
+
if (!f.endsWith(".md"))
|
|
176
|
+
continue;
|
|
177
|
+
const src = join(COMMANDS_SRC, f);
|
|
178
|
+
const dst = join(dstDir, f);
|
|
179
|
+
if (!existsSync(dst)) {
|
|
180
|
+
copyFileSync(src, dst);
|
|
181
|
+
written.push(dst);
|
|
182
|
+
} else
|
|
183
|
+
notes.push(`existing command kept: ${dst}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return { written, notes };
|
|
151
187
|
}
|
|
152
188
|
};
|
|
153
189
|
|
|
154
190
|
// src/targets/opencode.ts
|
|
155
|
-
import { existsSync as existsSync2, readdirSync, copyFileSync as copyFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
191
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, copyFileSync as copyFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
156
192
|
import { dirname as dirname2, join as join2 } from "node:path";
|
|
157
193
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
158
194
|
var here2 = dirname2(fileURLToPath2(import.meta.url));
|
|
159
|
-
var
|
|
195
|
+
var COMMANDS_SRC2 = join2(here2, "..", "..", ".opencode", "commands");
|
|
160
196
|
var CREW = {
|
|
161
197
|
"luffy-orchestrator": { color: "#ef4444", temperature: 0.2, steps: 15 },
|
|
162
198
|
"usopp-brainstorm": { color: "#f59e0b", temperature: 0.6, steps: 15 },
|
|
163
199
|
"nami-planner": { color: "#f97316", temperature: 0.2, steps: 15 },
|
|
164
200
|
"zoro-execution": { color: "#22c55e", temperature: 0.1, steps: 30 },
|
|
165
|
-
"chopper-checkpoint": { color: "#3b82f6", temperature: 0.1,
|
|
166
|
-
"sanji-quality": { color: "#a855f7", temperature: 0.1,
|
|
167
|
-
"franky-gates": { color: "#06b6d4", temperature: 0.1,
|
|
168
|
-
"robin-reviewer": { color: "#8b5cf6", temperature: 0.2,
|
|
169
|
-
"jinbe-security": { color: "#6366f1", temperature: 0.2,
|
|
201
|
+
"chopper-checkpoint": { color: "#3b82f6", temperature: 0.1, steps: 15 },
|
|
202
|
+
"sanji-quality": { color: "#a855f7", temperature: 0.1, steps: 10 },
|
|
203
|
+
"franky-gates": { color: "#06b6d4", temperature: 0.1, steps: 10 },
|
|
204
|
+
"robin-reviewer": { color: "#8b5cf6", temperature: 0.2, steps: 15 },
|
|
205
|
+
"jinbe-security": { color: "#6366f1", temperature: 0.2, steps: 15 },
|
|
170
206
|
"brook-healing": { color: "#ec4899", temperature: 0.1, steps: 20 },
|
|
171
|
-
"skeptic-verifier": { color: "#64748b", temperature: 0.1,
|
|
207
|
+
"skeptic-verifier": { color: "#64748b", temperature: 0.1, steps: 12 },
|
|
172
208
|
"eval-runner": { color: "#14b8a6", temperature: 0.2, steps: 15 },
|
|
173
209
|
"resume-coordinator": { color: "#d97706", temperature: 0.2, steps: 10 },
|
|
174
210
|
"memory-keeper": { color: "#d946ef", temperature: 0.2, steps: 8 }
|
|
175
211
|
};
|
|
176
|
-
function
|
|
212
|
+
function permissionFromScope(scope) {
|
|
213
|
+
if (scope === "source")
|
|
214
|
+
return { edit: "allow" };
|
|
215
|
+
if (scope === "artifacts")
|
|
216
|
+
return { edit: { "*": "deny", ".mugiwara/**": "allow" } };
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
function agentFrontmatter(name, description, writeScope) {
|
|
177
220
|
const crew = CREW[name];
|
|
178
221
|
const lines = [`description: ${description}`, `mode: all`];
|
|
179
222
|
if (crew) {
|
|
180
223
|
lines.push(`color: '${crew.color}'`, `temperature: ${crew.temperature}`, `steps: ${crew.steps}`);
|
|
181
|
-
|
|
224
|
+
const perm = permissionFromScope(writeScope);
|
|
225
|
+
if (perm) {
|
|
182
226
|
lines.push("permission:");
|
|
183
|
-
for (const [k, v] of Object.entries(
|
|
184
|
-
|
|
227
|
+
for (const [k, v] of Object.entries(perm)) {
|
|
228
|
+
if (typeof v === "string") {
|
|
229
|
+
lines.push(` ${k}: ${v}`);
|
|
230
|
+
} else {
|
|
231
|
+
lines.push(` ${k}:`);
|
|
232
|
+
for (const [pk, pv] of Object.entries(v))
|
|
233
|
+
lines.push(` "${pk}": ${pv}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
185
236
|
}
|
|
186
237
|
}
|
|
187
238
|
return lines.join(`
|
|
@@ -191,6 +242,7 @@ var target2 = {
|
|
|
191
242
|
id: "opencode",
|
|
192
243
|
label: "opencode",
|
|
193
244
|
native: true,
|
|
245
|
+
refPointerPrefix: "../",
|
|
194
246
|
paths({ scope, projectDir, home }) {
|
|
195
247
|
const root = scope === "global" ? join2(home, ".config", "opencode") : join2(projectDir, ".opencode");
|
|
196
248
|
return { skillsDir: join2(root, "skills"), agentsDir: join2(root, "agents") };
|
|
@@ -202,7 +254,8 @@ var target2 = {
|
|
|
202
254
|
};
|
|
203
255
|
},
|
|
204
256
|
transformAgent(data, body) {
|
|
205
|
-
const
|
|
257
|
+
const desc = data["internal-agent"] === "true" ? `[INTERNAL] ${data.description}` : data.description;
|
|
258
|
+
const fm = agentFrontmatter(data.name, desc, data["write-scope"]);
|
|
206
259
|
return { relPath: `${data.name}.md`, text: `---
|
|
207
260
|
${fm}
|
|
208
261
|
---
|
|
@@ -217,14 +270,14 @@ ${body}` };
|
|
|
217
270
|
const dstDir = join2(root, "commands");
|
|
218
271
|
if (dryRun)
|
|
219
272
|
return { written: [], notes: [] };
|
|
220
|
-
if (!existsSync2(
|
|
273
|
+
if (!existsSync2(COMMANDS_SRC2))
|
|
221
274
|
return { written: [], notes: [] };
|
|
222
275
|
mkdirSync2(dstDir, { recursive: true });
|
|
223
276
|
const written = [];
|
|
224
|
-
for (const f of
|
|
277
|
+
for (const f of readdirSync2(COMMANDS_SRC2)) {
|
|
225
278
|
if (!f.endsWith(".md"))
|
|
226
279
|
continue;
|
|
227
|
-
const src = join2(
|
|
280
|
+
const src = join2(COMMANDS_SRC2, f);
|
|
228
281
|
const dst = join2(dstDir, f);
|
|
229
282
|
copyFileSync2(src, dst);
|
|
230
283
|
written.push(dst);
|
|
@@ -313,7 +366,10 @@ ${body}` };
|
|
|
313
366
|
|
|
314
367
|
> ${data.description}
|
|
315
368
|
|
|
316
|
-
Skills: ${data.skills ?? ""}. Read \`.mugiwara/refs/${data.name}.md\` when embodying this role
|
|
369
|
+
Skills: ${data.skills ?? ""}. Read \`.mugiwara/refs/${data.name}.md\` when embodying this role.
|
|
370
|
+
|
|
371
|
+
Only zoro-execution and brook-healing may modify source code.
|
|
372
|
+
Return your output to luffy-orchestrator; do not choose the next step.`
|
|
317
373
|
};
|
|
318
374
|
}
|
|
319
375
|
return { relPath: `agent-${data.name}.md`, text: `# Agent: ${data.name}
|
|
@@ -421,7 +477,7 @@ var targets = { claude: target, opencode: target2, copilot: target3, gemini: tar
|
|
|
421
477
|
var TARGET_IDS = Object.keys(targets);
|
|
422
478
|
|
|
423
479
|
// src/installer.ts
|
|
424
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync as
|
|
480
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync as readdirSync3, writeFileSync as writeFileSync2, copyFileSync as copyFileSync3, rmSync, lstatSync } from "node:fs";
|
|
425
481
|
import { dirname as dirname3, join as join5 } from "node:path";
|
|
426
482
|
import { homedir } from "node:os";
|
|
427
483
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -429,27 +485,36 @@ var CONTENT_DIR = join5(dirname3(fileURLToPath3(import.meta.url)), "..", "conten
|
|
|
429
485
|
var pkg = JSON.parse(readFileSync2(join5(dirname3(fileURLToPath3(import.meta.url)), "..", "package.json"), "utf8"));
|
|
430
486
|
var VERSION = pkg.version;
|
|
431
487
|
function collectContent() {
|
|
432
|
-
const skillNames =
|
|
488
|
+
const skillNames = readdirSync3(join5(CONTENT_DIR, "skills"), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
433
489
|
const skills = skillNames.map((name) => {
|
|
434
490
|
const { data, body } = parseFrontmatter(readFileSync2(join5(CONTENT_DIR, "skills", name, "SKILL.md"), "utf8"));
|
|
435
491
|
return { name, data, body, refs: collectRefs(join5(CONTENT_DIR, "skills", name)) };
|
|
436
492
|
});
|
|
437
|
-
const agents =
|
|
493
|
+
const agents = readdirSync3(join5(CONTENT_DIR, "agents")).filter((f) => f.endsWith(".md")).map((f) => {
|
|
438
494
|
const { data, body } = parseFrontmatter(readFileSync2(join5(CONTENT_DIR, "agents", f), "utf8"));
|
|
439
|
-
return { name: f.replace(/\.md$/, ""), data, body, refs: [] };
|
|
495
|
+
return { name: f.replace(/\.md$/, ""), data, body, refs: [], internal: data.internal === "true" };
|
|
440
496
|
});
|
|
441
|
-
|
|
497
|
+
const sharedRefsDir = join5(dirname3(CONTENT_DIR), "references");
|
|
498
|
+
const sharedRefs = [];
|
|
499
|
+
if (existsSync4(sharedRefsDir)) {
|
|
500
|
+
for (const f of readdirSync3(sharedRefsDir)) {
|
|
501
|
+
if (!f.endsWith(".md"))
|
|
502
|
+
continue;
|
|
503
|
+
sharedRefs.push({ relPath: f, text: readFileSync2(join5(sharedRefsDir, f), "utf8") });
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return { skills, agents, sharedRefs };
|
|
442
507
|
}
|
|
443
508
|
function collectRefs(skillDir) {
|
|
444
509
|
const refsDir = join5(skillDir, "references");
|
|
445
510
|
if (!existsSync4(refsDir))
|
|
446
511
|
return [];
|
|
447
|
-
return
|
|
512
|
+
return readdirSync3(refsDir, { recursive: true }).map((f) => String(f)).filter((f) => f.endsWith(".md")).map((rel) => ({ relPath: rel, text: readFileSync2(join5(refsDir, rel), "utf8") }));
|
|
448
513
|
}
|
|
449
514
|
function installTo(target10, opts) {
|
|
450
515
|
const { scope, projectDir, dryRun = false, force = false } = opts;
|
|
451
516
|
const home = opts.home ?? homedir();
|
|
452
|
-
const { skills, agents } = collectContent();
|
|
517
|
+
const { skills, agents, sharedRefs } = collectContent();
|
|
453
518
|
const dirs = target10.paths({ scope, projectDir, home });
|
|
454
519
|
const backupRoot = join5(scope === "global" ? home : projectDir, ".mugiwara");
|
|
455
520
|
const result = { written: [], skipped: [], backedUp: [], notes: [] };
|
|
@@ -482,12 +547,22 @@ function installTo(target10, opts) {
|
|
|
482
547
|
};
|
|
483
548
|
for (const s of skills) {
|
|
484
549
|
const out = target10.transformSkill(s.data, s.body);
|
|
485
|
-
if (out)
|
|
486
|
-
|
|
550
|
+
if (out) {
|
|
551
|
+
let text = out.text;
|
|
552
|
+
if (target10.refPointerPrefix !== undefined && target10.refPointerPrefix !== "") {
|
|
553
|
+
text = text.replace(/`_shared\/references\//g, "`" + target10.refPointerPrefix + "_shared/references/");
|
|
554
|
+
}
|
|
555
|
+
writeOne(join5(dirs.skillsDir, out.relPath), text);
|
|
556
|
+
}
|
|
487
557
|
if (target10.transformSkillFull) {
|
|
488
558
|
const full = target10.transformSkillFull(s.data, s.body);
|
|
489
|
-
if (full && target10.refsDir)
|
|
490
|
-
|
|
559
|
+
if (full && target10.refsDir) {
|
|
560
|
+
let text = full.text;
|
|
561
|
+
if (target10.refPointerPrefix !== undefined && target10.refPointerPrefix !== "") {
|
|
562
|
+
text = text.replace(/`_shared\/references\//g, "`" + target10.refPointerPrefix + "_shared/references/");
|
|
563
|
+
}
|
|
564
|
+
writeOne(join5(target10.refsDir({ scope, projectDir, home }, s.name), full.relPath), text);
|
|
565
|
+
}
|
|
491
566
|
}
|
|
492
567
|
if (s.refs.length && target10.refsDir) {
|
|
493
568
|
const refsRoot = target10.refsDir({ scope, projectDir, home }, s.name);
|
|
@@ -496,7 +571,7 @@ function installTo(target10, opts) {
|
|
|
496
571
|
}
|
|
497
572
|
}
|
|
498
573
|
for (const a of agents) {
|
|
499
|
-
const out = target10.transformAgent(a.data, a.body);
|
|
574
|
+
const out = target10.transformAgent({ ...a.data, ...a.internal ? { "internal-agent": "true" } : {} }, a.body);
|
|
500
575
|
if (out)
|
|
501
576
|
writeOne(join5(dirs.agentsDir, out.relPath), out.text);
|
|
502
577
|
if (target10.transformAgentFull) {
|
|
@@ -505,6 +580,11 @@ function installTo(target10, opts) {
|
|
|
505
580
|
writeOne(join5(target10.refsDir({ scope, projectDir, home }, a.name), full.relPath), full.text);
|
|
506
581
|
}
|
|
507
582
|
}
|
|
583
|
+
if (sharedRefs.length) {
|
|
584
|
+
const sharedRoot = join5(dirs.skillsDir, "_shared", "references");
|
|
585
|
+
for (const r of sharedRefs)
|
|
586
|
+
writeOne(join5(sharedRoot, r.relPath), r.text);
|
|
587
|
+
}
|
|
508
588
|
if (target10.postInstall) {
|
|
509
589
|
const post = target10.postInstall({ scope, projectDir, home, dryRun, files: result.written });
|
|
510
590
|
result.written.push(...post.written);
|
|
@@ -524,7 +604,7 @@ function removeInstalled(manifest, { dryRun = false } = {}) {
|
|
|
524
604
|
if (!dryRun) {
|
|
525
605
|
for (const f of manifest.files) {
|
|
526
606
|
let d = dirname3(f);
|
|
527
|
-
while (existsSync4(d) &&
|
|
607
|
+
while (existsSync4(d) && readdirSync3(d).length === 0) {
|
|
528
608
|
if (d.endsWith("/.mugiwara") || d === dirname3(d))
|
|
529
609
|
break;
|
|
530
610
|
rmSync(d, { recursive: true, force: true });
|
|
@@ -534,6 +614,44 @@ function removeInstalled(manifest, { dryRun = false } = {}) {
|
|
|
534
614
|
}
|
|
535
615
|
return removed;
|
|
536
616
|
}
|
|
617
|
+
function assertNotSymlink(file) {
|
|
618
|
+
if (!existsSync4(file))
|
|
619
|
+
return;
|
|
620
|
+
try {
|
|
621
|
+
if (lstatSync(file).isSymbolicLink())
|
|
622
|
+
throw new Error(`refusing to follow symlink: ${file}`);
|
|
623
|
+
} catch (e) {
|
|
624
|
+
if (e.code === "ENOENT")
|
|
625
|
+
return;
|
|
626
|
+
throw e;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
var GITIGNORE_MARKER = "# mugiwara";
|
|
630
|
+
var GITIGNORE_BLOCK = `# mugiwara — audit trail is the product: commit reports/, results/, logs/, spec/, plans/.
|
|
631
|
+
# Ignore session state and regenerated files.
|
|
632
|
+
.mugiwara/state.json
|
|
633
|
+
.mugiwara/state-*.json
|
|
634
|
+
.mugiwara/config
|
|
635
|
+
.mugiwara/continue.md
|
|
636
|
+
.mugiwara/refs/
|
|
637
|
+
`;
|
|
638
|
+
function ensureProjectGitignore(projectDir, opts = {}) {
|
|
639
|
+
const { dryRun = false } = opts;
|
|
640
|
+
const path = join5(projectDir, ".gitignore");
|
|
641
|
+
assertNotSymlink(path);
|
|
642
|
+
if (existsSync4(path) && readFileSync2(path, "utf8").includes(GITIGNORE_MARKER)) {
|
|
643
|
+
return { appended: false, notes: [] };
|
|
644
|
+
}
|
|
645
|
+
const existing = existsSync4(path) ? readFileSync2(path, "utf8") : "";
|
|
646
|
+
const separator = existing.length && !existing.endsWith(`
|
|
647
|
+
`) ? `
|
|
648
|
+
` : "";
|
|
649
|
+
if (!dryRun) {
|
|
650
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
651
|
+
writeFileSync2(path, existing + separator + GITIGNORE_BLOCK);
|
|
652
|
+
}
|
|
653
|
+
return { appended: true, notes: [`.gitignore ${dryRun ? "would append" : "appended"} mugiwara audit-trail block`] };
|
|
654
|
+
}
|
|
537
655
|
|
|
538
656
|
// src/manifest.ts
|
|
539
657
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
@@ -551,7 +669,7 @@ function writeManifest(file, data) {
|
|
|
551
669
|
}
|
|
552
670
|
|
|
553
671
|
// src/mission.ts
|
|
554
|
-
import { existsSync as existsSync6, rmSync as rmSync2, readFileSync as readFileSync4 } from "node:fs";
|
|
672
|
+
import { existsSync as existsSync6, rmSync as rmSync2, readFileSync as readFileSync4, readdirSync as readdirSync4, mkdirSync as mkdirSync5, appendFileSync } from "node:fs";
|
|
555
673
|
import { join as join7 } from "node:path";
|
|
556
674
|
function activeActor(projectDir) {
|
|
557
675
|
const stateFile = join7(projectDir, ".mugiwara", "state.json");
|
|
@@ -583,11 +701,13 @@ function resetMission(projectDir, keepLogs, force) {
|
|
|
583
701
|
removed.push(dir);
|
|
584
702
|
}
|
|
585
703
|
}
|
|
586
|
-
for (const f of
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
704
|
+
for (const f of readdirSync4(root)) {
|
|
705
|
+
if (/^state(-.+)?\.json$/.test(f)) {
|
|
706
|
+
const p = join7(root, f);
|
|
707
|
+
if (existsSync6(p)) {
|
|
708
|
+
rmSync2(p);
|
|
709
|
+
removed.push(f);
|
|
710
|
+
}
|
|
591
711
|
}
|
|
592
712
|
}
|
|
593
713
|
if (!keepLogs) {
|
|
@@ -605,12 +725,114 @@ function resetMission(projectDir, keepLogs, force) {
|
|
|
605
725
|
}
|
|
606
726
|
return { removed, kept };
|
|
607
727
|
}
|
|
728
|
+
function archiveMission(projectDir, mission, opts = {}) {
|
|
729
|
+
const { dryRun = false } = opts;
|
|
730
|
+
const root = join7(projectDir, ".mugiwara");
|
|
731
|
+
if (!mission || /[^a-zA-Z0-9._-]/.test(mission) || /^\.+$/.test(mission))
|
|
732
|
+
throw new Error(`invalid mission name "${mission}" (allowlist: [a-zA-Z0-9._-], not a dot-path)`);
|
|
733
|
+
const removed = [];
|
|
734
|
+
const kept = [];
|
|
735
|
+
const belongs = (f) => {
|
|
736
|
+
const base = f.replace(/^\d{4}-\d{2}-\d{2}-/, "");
|
|
737
|
+
return base === `${mission}.md` || base.startsWith(`${mission}-`);
|
|
738
|
+
};
|
|
739
|
+
let report = null;
|
|
740
|
+
const reportsDir = join7(root, "reports");
|
|
741
|
+
if (existsSync6(reportsDir)) {
|
|
742
|
+
const f = readdirSync4(reportsDir).find((n) => {
|
|
743
|
+
const m = n.match(/^(\d{4}-\d{2}-\d{2})-(.+)\.md$/);
|
|
744
|
+
return !!m && m[2] === mission;
|
|
745
|
+
});
|
|
746
|
+
if (f)
|
|
747
|
+
report = join7("reports", f);
|
|
748
|
+
}
|
|
749
|
+
const resultsDir = join7(root, "results", mission);
|
|
750
|
+
if (existsSync6(resultsDir)) {
|
|
751
|
+
for (const f of readdirSync4(resultsDir)) {
|
|
752
|
+
if (f === "06-closure.md" || f === "07-pr-verdict.md") {
|
|
753
|
+
kept.push(join7("results", mission, f));
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
const p = join7(resultsDir, f);
|
|
757
|
+
if (!dryRun)
|
|
758
|
+
rmSync2(p, { recursive: true, force: true });
|
|
759
|
+
removed.push(join7("results", mission, f));
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
const specDir = join7(root, "spec");
|
|
763
|
+
if (existsSync6(specDir)) {
|
|
764
|
+
for (const f of readdirSync4(specDir)) {
|
|
765
|
+
if (!belongs(f))
|
|
766
|
+
continue;
|
|
767
|
+
const p = join7(specDir, f);
|
|
768
|
+
if (!dryRun)
|
|
769
|
+
rmSync2(p);
|
|
770
|
+
removed.push(join7("spec", f));
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
for (const dir of ["review", "issues"]) {
|
|
774
|
+
const d = join7(root, dir);
|
|
775
|
+
if (!existsSync6(d))
|
|
776
|
+
continue;
|
|
777
|
+
for (const f of readdirSync4(d)) {
|
|
778
|
+
if (!belongs(f))
|
|
779
|
+
continue;
|
|
780
|
+
const p = join7(d, f);
|
|
781
|
+
if (!dryRun)
|
|
782
|
+
rmSync2(p, { force: true });
|
|
783
|
+
removed.push(join7(dir, f));
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
const logsDir = join7(root, "logs");
|
|
787
|
+
if (existsSync6(logsDir)) {
|
|
788
|
+
for (const f of readdirSync4(logsDir)) {
|
|
789
|
+
if (!belongs(f))
|
|
790
|
+
continue;
|
|
791
|
+
const p = join7(logsDir, f);
|
|
792
|
+
if (!dryRun)
|
|
793
|
+
rmSync2(p);
|
|
794
|
+
removed.push(join7("logs", f));
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
const cont = join7(root, "continue.md");
|
|
798
|
+
if (existsSync6(cont)) {
|
|
799
|
+
try {
|
|
800
|
+
if (readFileSync4(cont, "utf8").includes(mission)) {
|
|
801
|
+
if (!dryRun)
|
|
802
|
+
rmSync2(cont);
|
|
803
|
+
removed.push("continue.md");
|
|
804
|
+
}
|
|
805
|
+
} catch {}
|
|
806
|
+
}
|
|
807
|
+
if (report)
|
|
808
|
+
kept.push(report);
|
|
809
|
+
for (const k of ["plans", "config", "state.json", join7("logs", "lessons.md")]) {
|
|
810
|
+
if (existsSync6(join7(root, k)))
|
|
811
|
+
kept.push(k);
|
|
812
|
+
}
|
|
813
|
+
let index;
|
|
814
|
+
const indexFile = join7(root, "reports", "index.md");
|
|
815
|
+
const line = `- ${mission} — ${new Date().toISOString().slice(0, 10)}${report ? ` → ${report}` : ""}
|
|
816
|
+
`;
|
|
817
|
+
if (!dryRun) {
|
|
818
|
+
mkdirSync5(join7(root, "reports"), { recursive: true });
|
|
819
|
+
const existing = existsSync6(indexFile) ? readFileSync4(indexFile, "utf8") : "";
|
|
820
|
+
if (!existing.split(/\r?\n/).some((l) => l.startsWith(`- ${mission} —`))) {
|
|
821
|
+
const header = existing ? "" : `# Mission index
|
|
822
|
+
|
|
823
|
+
`;
|
|
824
|
+
appendFileSync(indexFile, header + line);
|
|
825
|
+
}
|
|
826
|
+
index = join7("reports", "index.md");
|
|
827
|
+
}
|
|
828
|
+
return { report, removed, kept, index };
|
|
829
|
+
}
|
|
608
830
|
|
|
609
831
|
// src/cli.ts
|
|
610
832
|
var str = (v) => typeof v === "string" ? v : undefined;
|
|
611
833
|
var flag = (v) => v === true;
|
|
612
834
|
async function run(argv) {
|
|
613
|
-
const { command, flags } = parseArgs(argv);
|
|
835
|
+
const { command, flags, _ } = parseArgs(argv);
|
|
614
836
|
if (flag(flags.help) || command === "help")
|
|
615
837
|
return help();
|
|
616
838
|
if (flag(flags.version)) {
|
|
@@ -628,6 +850,8 @@ async function run(argv) {
|
|
|
628
850
|
return list(flags);
|
|
629
851
|
case "reset":
|
|
630
852
|
return resetCmd(flags);
|
|
853
|
+
case "archive":
|
|
854
|
+
return archive(flags, _);
|
|
631
855
|
default:
|
|
632
856
|
throw new Error(`Unknown command: ${command}`);
|
|
633
857
|
}
|
|
@@ -647,6 +871,23 @@ function resetCmd(flags) {
|
|
|
647
871
|
if (result.kept.length)
|
|
648
872
|
console.log(`kept: ${result.kept.join(", ")}`);
|
|
649
873
|
}
|
|
874
|
+
function archive(flags, positionals) {
|
|
875
|
+
const projectDir = resolve(str(flags.project) ?? process.cwd());
|
|
876
|
+
const mission = positionals[1];
|
|
877
|
+
if (!mission) {
|
|
878
|
+
console.error("usage: mugiwara archive <mission> [--project <dir>] [--dry-run]");
|
|
879
|
+
process.exit(1);
|
|
880
|
+
}
|
|
881
|
+
const result = archiveMission(projectDir, mission, { dryRun: flag(flags.dryRun) });
|
|
882
|
+
if (result.report)
|
|
883
|
+
console.log(`archive target: ${result.report}`);
|
|
884
|
+
if (result.removed.length)
|
|
885
|
+
console.log(`${flag(flags.dryRun) ? "would remove" : "removed"}: ${result.removed.join(", ")}`);
|
|
886
|
+
if (result.kept.length)
|
|
887
|
+
console.log(`kept: ${result.kept.join(", ")}`);
|
|
888
|
+
if (result.index)
|
|
889
|
+
console.log(`index updated: ${result.index}`);
|
|
890
|
+
}
|
|
650
891
|
async function resolveOptions(flags) {
|
|
651
892
|
const interactive = !flag(flags.yes);
|
|
652
893
|
if (interactive && !process.stdin.isTTY) {
|
|
@@ -707,6 +948,10 @@ async function install(flags) {
|
|
|
707
948
|
allFiles.push(...r.written);
|
|
708
949
|
allNotes.push(...r.notes);
|
|
709
950
|
}
|
|
951
|
+
if (scope === "project") {
|
|
952
|
+
const gi = ensureProjectGitignore(projectDir, { dryRun: flag(flags.dryRun) });
|
|
953
|
+
allNotes.push(...gi.notes);
|
|
954
|
+
}
|
|
710
955
|
if (flag(flags.dryRun)) {
|
|
711
956
|
console.log(`
|
|
712
957
|
Dry run — nothing written.`);
|
|
@@ -758,7 +1003,7 @@ async function uninstall(flags) {
|
|
|
758
1003
|
if (!flag(flags.dryRun)) {
|
|
759
1004
|
rmSync3(file);
|
|
760
1005
|
const mugiDir = join8(scope === "global" ? home : projectDir, ".mugiwara");
|
|
761
|
-
if (existsSync7(mugiDir) &&
|
|
1006
|
+
if (existsSync7(mugiDir) && readdirSync5(mugiDir).length === 0) {
|
|
762
1007
|
rmSync3(mugiDir, { recursive: true, force: true });
|
|
763
1008
|
}
|
|
764
1009
|
if (manifest.targets.includes("opencode")) {
|
|
@@ -803,6 +1048,7 @@ Usage:
|
|
|
803
1048
|
mugiwara list show installations
|
|
804
1049
|
mugiwara list --check health check: show installations + missing files
|
|
805
1050
|
mugiwara reset wipe mission state (spec/plans/results/review/issues[/logs])
|
|
1051
|
+
mugiwara archive <m> fold a closed mission's evidence into its report, then remove loose files
|
|
806
1052
|
mugiwara --help this help
|
|
807
1053
|
mugiwara --version print version
|
|
808
1054
|
|
package/gemini-extension.json
CHANGED
|
File without changes
|
package/hooks/session-start.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
console.log(
|
|
4
4
|
JSON.stringify({
|
|
5
5
|
additionalContext:
|
|
6
|
-
"Mugiwara crew
|
|
6
|
+
"Mugiwara crew active by default. Say \\`mugiwara off\\` for a request and the crew stands down (Luffy acknowledges, records it in the decision log). Before ANY task — load \\`mugiwara-orchestration\\` skill as gatekeeper. NEVER execute, answer, or make changes without Wave 0 triage. Classification overhead <15 seconds — cheaper than an incorrect fix. Lane 0 for trivial work (single-file/<20 LOC) skips pipeline; Lane 1+ follows full pipeline. Mode: guided / semi / auto (see .mugiwara/config). Switch with \\`/mugiwara <mode>\\` — applies from the next wave. See skills/mugiwara-workflow."
|
|
7
7
|
})
|
|
8
8
|
);
|
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ionivetech/mugiwara",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "The Straw Hat crew of AI agents and skills: brainstorm, plan, execute, checkpoint, quality, gates, review, security, self-healing. Installs into Claude Code, opencode, Copilot, Gemini, Codex, Cursor, Kimi, pi, Windsurf, Cline, Kilo, Antigravity.",
|
|
5
|
+
"homepage": "https://github.com/ionivetech/mugiwara#readme",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/ionivetech/mugiwara.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/ionivetech/mugiwara/issues"
|
|
12
|
+
},
|
|
5
13
|
"type": "module",
|
|
6
14
|
"license": "MIT",
|
|
7
15
|
"main": "./.opencode/plugins/mugiwara.mjs",
|
|
@@ -17,9 +25,7 @@
|
|
|
17
25
|
"dist",
|
|
18
26
|
"src",
|
|
19
27
|
"content",
|
|
20
|
-
"docs",
|
|
21
28
|
"references",
|
|
22
|
-
"evals",
|
|
23
29
|
"scripts",
|
|
24
30
|
".opencode",
|
|
25
31
|
".claude-plugin",
|
|
@@ -51,8 +57,9 @@
|
|
|
51
57
|
"validate": "bun scripts/validate-content.ts --check-manifest --check-docs",
|
|
52
58
|
"evals": "bun scripts/run-evals.ts",
|
|
53
59
|
"retrieval-eval": "bun scripts/retrieval-eval.ts",
|
|
60
|
+
"verify-install": "bun scripts/verify-install.ts",
|
|
54
61
|
"sync-version": "bun scripts/sync-version.ts",
|
|
55
|
-
"gate": "bun run typecheck && bun run test && bun run build && bun scripts/validate-content.ts --check-manifest --check-docs && bun run verify-pack && bun scripts/run-evals.ts && bun scripts/retrieval-eval.ts",
|
|
62
|
+
"gate": "bun run typecheck && bun run test && bun run build && bun scripts/validate-content.ts --check-manifest --check-docs && bun run verify-pack && bun scripts/run-evals.ts && bun scripts/retrieval-eval.ts && bun scripts/verify-install.ts",
|
|
56
63
|
"verify-pack": "npm pack --dry-run 2>&1 | node -e \"let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{if(!s.includes('ionivetech-mugiwara')){console.error('npm pack failed');process.exit(1)};console.log('npm package clean')})\"",
|
|
57
64
|
"prepack": "bun run build && bun run sync-version"
|
|
58
65
|
},
|
package/plugin.json
CHANGED
|
@@ -37,7 +37,7 @@ All actors read and write to the same file. Append-only, never overwrite.
|
|
|
37
37
|
`scripts/savepoint.sh` accepts `--branch <name>` to write per-branch state:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
scripts/savepoint.sh "2026-08-11-dark-mode"
|
|
40
|
+
scripts/savepoint.sh --branch "2026-08-11-dark-mode" "" "feature/dark-mode"
|
|
41
41
|
# writes .mugiwara/state-feature-dark-mode.json
|
|
42
42
|
```
|
|
43
43
|
|