@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.
Files changed (151) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +3 -2
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor-plugin/plugin.json +1 -1
  5. package/.kimi-plugin/plugin.json +1 -1
  6. package/.opencode/commands/mugiwara-continue.md +16 -0
  7. package/.opencode/commands/mugiwara-execute.md +6 -4
  8. package/.opencode/commands/mugiwara-heal.md +6 -4
  9. package/.opencode/commands/mugiwara-plan.md +6 -4
  10. package/.opencode/commands/mugiwara-review.md +6 -4
  11. package/.opencode/commands/mugiwara-security.md +6 -4
  12. package/.opencode/commands/mugiwara-ship.md +6 -4
  13. package/.opencode/commands/mugiwara.md +4 -2
  14. package/.opencode/commands/using-mugiwara.md +7 -13
  15. package/.opencode/mugiwara-helpers.mjs +24 -0
  16. package/.opencode/plugins/mugiwara.mjs +28 -8
  17. package/AGENTS.md +1 -1
  18. package/GEMINI.md +1 -1
  19. package/README.md +311 -127
  20. package/content/agents/brook-healing.md +17 -3
  21. package/content/agents/chopper-checkpoint.md +17 -3
  22. package/content/agents/eval-runner.md +17 -2
  23. package/content/agents/franky-gates.md +19 -5
  24. package/content/agents/jinbe-security.md +19 -3
  25. package/content/agents/luffy-orchestrator.md +6 -3
  26. package/content/agents/memory-keeper.md +15 -0
  27. package/content/agents/nami-planner.md +21 -6
  28. package/content/agents/onboarding-guide.md +145 -0
  29. package/content/agents/resume-coordinator.md +21 -5
  30. package/content/agents/robin-reviewer.md +18 -3
  31. package/content/agents/sanji-quality.md +19 -4
  32. package/content/agents/skeptic-verifier.md +18 -3
  33. package/content/agents/usopp-brainstorm.md +17 -3
  34. package/content/agents/zoro-execution.md +18 -3
  35. package/content/skills/mugiwara-backend/SKILL.md +1 -1
  36. package/content/skills/mugiwara-brainstorm/SKILL.md +12 -1
  37. package/content/skills/mugiwara-checkpoint/SKILL.md +2 -2
  38. package/content/skills/mugiwara-contract-first/SKILL.md +2 -36
  39. package/content/skills/mugiwara-contract-first/references/process.md +37 -0
  40. package/content/skills/mugiwara-execution/SKILL.md +51 -34
  41. package/content/skills/mugiwara-execution/references/dispatch.md +41 -0
  42. package/content/skills/mugiwara-execution/references/resume-batching.md +32 -0
  43. package/content/skills/mugiwara-frontend/SKILL.md +1 -1
  44. package/content/skills/mugiwara-gates/SKILL.md +32 -30
  45. package/content/skills/mugiwara-healing/SKILL.md +1 -1
  46. package/content/skills/mugiwara-orchestration/SKILL.md +59 -39
  47. package/content/skills/mugiwara-orchestration/references/closure.md +34 -0
  48. package/content/skills/mugiwara-orchestration/references/delegation-pillars.md +40 -0
  49. package/content/skills/mugiwara-orchestration/references/triage-escalation.md +97 -0
  50. package/content/skills/mugiwara-planning/SKILL.md +29 -27
  51. package/content/skills/mugiwara-planning/references/plan-template.md +42 -0
  52. package/content/skills/mugiwara-pr/SKILL.md +9 -2
  53. package/content/skills/mugiwara-quality/SKILL.md +17 -6
  54. package/content/skills/mugiwara-resume/SKILL.md +7 -2
  55. package/content/skills/mugiwara-review/SKILL.md +20 -1
  56. package/content/skills/mugiwara-review/references/code-attributes.md +38 -0
  57. package/content/skills/mugiwara-root-cause/SKILL.md +3 -30
  58. package/content/skills/mugiwara-root-cause/references/process.md +35 -0
  59. package/content/skills/mugiwara-security/SKILL.md +17 -32
  60. package/content/skills/mugiwara-security/references/owasp-top10.md +12 -0
  61. package/content/skills/mugiwara-security/references/rationalizations.md +8 -0
  62. package/content/skills/mugiwara-ship/SKILL.md +13 -10
  63. package/content/skills/mugiwara-testcases/SKILL.md +7 -0
  64. package/content/skills/mugiwara-workflow/SKILL.md +74 -77
  65. package/content/skills/mugiwara-workflow/references/workspace-layout.md +50 -0
  66. package/content/skills/using-mugiwara/SKILL.md +13 -18
  67. package/dist/mugiwara.js +293 -47
  68. package/gemini-extension.json +1 -1
  69. package/hooks/mugiwara-mode-tracker.ts +0 -0
  70. package/hooks/session-start.ts +1 -1
  71. package/package.json +11 -4
  72. package/plugin.json +1 -1
  73. package/references/multi-actor.md +1 -1
  74. package/scripts/evidence.sh +28 -6
  75. package/scripts/gate-selftest.ts +317 -0
  76. package/scripts/initiative.ts +262 -0
  77. package/scripts/lane.sh +18 -1
  78. package/scripts/mission-report.sh +237 -36
  79. package/scripts/onboard.ts +292 -0
  80. package/scripts/release-notes.ts +42 -13
  81. package/scripts/savepoint.sh +151 -60
  82. package/scripts/validate-content.ts +97 -0
  83. package/scripts/verify-install.ts +72 -0
  84. package/src/cli.ts +20 -3
  85. package/src/installer.ts +71 -8
  86. package/src/mission.ts +117 -5
  87. package/src/targets/claude.ts +45 -9
  88. package/src/targets/generic.ts +1 -1
  89. package/src/targets/opencode.ts +31 -10
  90. package/docs/concepts/agents.md +0 -53
  91. package/docs/concepts/audit-trail.md +0 -65
  92. package/docs/concepts/comparison.md +0 -58
  93. package/docs/concepts/config.md +0 -55
  94. package/docs/concepts/cost.md +0 -45
  95. package/docs/concepts/execution-model.md +0 -92
  96. package/docs/concepts/git-strategy.md +0 -62
  97. package/docs/concepts/lanes.md +0 -82
  98. package/docs/concepts/modes.md +0 -73
  99. package/docs/concepts/pr-summary.md +0 -54
  100. package/docs/concepts/skills.md +0 -55
  101. package/docs/concepts/workflow.md +0 -89
  102. package/docs/getting-started.md +0 -158
  103. package/docs/index.md +0 -56
  104. package/docs/install/antigravity.md +0 -45
  105. package/docs/install/claude.md +0 -77
  106. package/docs/install/cli.md +0 -115
  107. package/docs/install/codex.md +0 -44
  108. package/docs/install/copilot.md +0 -45
  109. package/docs/install/cursor.md +0 -45
  110. package/docs/install/gemini.md +0 -44
  111. package/docs/install/index.md +0 -53
  112. package/docs/install/kimi.md +0 -45
  113. package/docs/install/opencode.md +0 -143
  114. package/docs/install/pi.md +0 -46
  115. package/docs/reference/adoption-guide.md +0 -72
  116. package/docs/reference/agent-anatomy.md +0 -72
  117. package/docs/reference/compliance-matrix.md +0 -81
  118. package/docs/reference/developer-onboarding.md +0 -89
  119. package/docs/reference/enforcement.md +0 -35
  120. package/docs/reference/harness-matrix.md +0 -41
  121. package/docs/reference/skill-anatomy.md +0 -71
  122. package/docs/troubleshooting.md +0 -91
  123. package/evals/cases/_no-skill.json +0 -16
  124. package/evals/cases/adversarial-pressure-fake-pass.json +0 -25
  125. package/evals/cases/adversarial-pressure-skip-review.json +0 -25
  126. package/evals/cases/lane-exploratory-vague.json +0 -24
  127. package/evals/cases/lane-sensitivity-payment.json +0 -24
  128. package/evals/cases/positive-refactor-existing-tests.json +0 -25
  129. package/evals/cases/positive-resume-mid-mission.json +0 -24
  130. package/evals/cases/routing-agent-security.json +0 -25
  131. package/evals/cases/routing-auth-feature.json +0 -26
  132. package/evals/cases/routing-backend.json +0 -25
  133. package/evals/cases/routing-bug-one-file.json +0 -26
  134. package/evals/cases/routing-claim-audit.json +0 -25
  135. package/evals/cases/routing-context-budget.json +0 -25
  136. package/evals/cases/routing-contract-first.json +0 -25
  137. package/evals/cases/routing-execution.json +0 -25
  138. package/evals/cases/routing-frontend.json +0 -26
  139. package/evals/cases/routing-gates.json +0 -25
  140. package/evals/cases/routing-git.json +0 -25
  141. package/evals/cases/routing-healing.json +0 -25
  142. package/evals/cases/routing-lessons.json +0 -25
  143. package/evals/cases/routing-orchestration.json +0 -25
  144. package/evals/cases/routing-planning.json +0 -26
  145. package/evals/cases/routing-pr.json +0 -25
  146. package/evals/cases/routing-quality.json +0 -25
  147. package/evals/cases/routing-ship.json +0 -26
  148. package/evals/cases/routing-sunset.json +0 -25
  149. package/evals/cases/routing-using-mugiwara.json +0 -25
  150. package/evals/cases/routing-workflow.json +0 -25
  151. 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 readdirSync3, rmSync as rmSync3 } from "node:fs";
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 HOOK_SRC = join(here, "..", "..", "hooks", "session-start.ts");
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 hookFile = join(root, "hooks", "session-start.ts");
154
+ const written = [];
155
+ const notes = [];
143
156
  if (dryRun)
144
157
  return { written: [], notes: [] };
145
- if (existsSync(HOOK_SRC) && !existsSync(hookFile)) {
146
- mkdirSync(dirname(hookFile), { recursive: true });
147
- copyFileSync(HOOK_SRC, hookFile);
148
- return { written: [hookFile], notes: [] };
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
- return { written: [], notes: [] };
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 COMMANDS_SRC = join2(here2, "..", "..", ".opencode", "commands");
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, permission: { edit: "deny" }, steps: 15 },
166
- "sanji-quality": { color: "#a855f7", temperature: 0.1, permission: { edit: "deny" }, steps: 10 },
167
- "franky-gates": { color: "#06b6d4", temperature: 0.1, permission: { edit: "deny" }, steps: 10 },
168
- "robin-reviewer": { color: "#8b5cf6", temperature: 0.2, permission: { edit: "deny" }, steps: 15 },
169
- "jinbe-security": { color: "#6366f1", temperature: 0.2, permission: { edit: "deny" }, steps: 15 },
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, permission: { edit: "deny" }, steps: 12 },
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 agentFrontmatter(name, description) {
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
- if (crew.permission) {
224
+ const perm = permissionFromScope(writeScope);
225
+ if (perm) {
182
226
  lines.push("permission:");
183
- for (const [k, v] of Object.entries(crew.permission))
184
- lines.push(` ${k}: ${v}`);
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 fm = agentFrontmatter(data.name, data.description);
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(COMMANDS_SRC))
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 readdirSync(COMMANDS_SRC)) {
277
+ for (const f of readdirSync2(COMMANDS_SRC2)) {
225
278
  if (!f.endsWith(".md"))
226
279
  continue;
227
- const src = join2(COMMANDS_SRC, f);
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 readdirSync2, writeFileSync as writeFileSync2, copyFileSync as copyFileSync3, rmSync } from "node:fs";
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 = readdirSync2(join5(CONTENT_DIR, "skills"), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
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 = readdirSync2(join5(CONTENT_DIR, "agents")).filter((f) => f.endsWith(".md")).map((f) => {
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
- return { skills, agents };
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 readdirSync2(refsDir, { recursive: true }).map((f) => String(f)).filter((f) => f.endsWith(".md")).map((rel) => ({ relPath: rel, text: readFileSync2(join5(refsDir, rel), "utf8") }));
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
- writeOne(join5(dirs.skillsDir, out.relPath), out.text);
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
- writeOne(join5(target10.refsDir({ scope, projectDir, home }, s.name), full.relPath), full.text);
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) && readdirSync2(d).length === 0) {
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 ["state.json"]) {
587
- const p = join7(root, f);
588
- if (existsSync6(p)) {
589
- rmSync2(p);
590
- removed.push(f);
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) && readdirSync3(mugiDir).length === 0) {
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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mugiwara",
3
- "version": "0.5.5",
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, healing.",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }
File without changes
@@ -3,6 +3,6 @@
3
3
  console.log(
4
4
  JSON.stringify({
5
5
  additionalContext:
6
- "Mugiwara crew available. The workflow auto-activates for non-trivial requests (no need to call `/using-mugiwara` at session start; it is optional and routes to the right crew member). The crew runs inline in the main thread Never Task-dispatch a crew member. Subagents only for [PARALLEL] task batches, concurrent review/security, and independent re-run checks. Checkpoint reports at wave/stage boundaries. Mode: guided / semi / auto (see .mugiwara/config). Switch with `/mugiwara <mode>`. See skills/mugiwara-workflow."
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.5.5",
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mugiwara",
3
3
  "description": "The Straw Hat crew of AI agents and skills: brainstorm, plan, execute, checkpoint, quality, gates, review, security, healing.",
4
- "version": "0.5.5",
4
+ "version": "0.6.1",
5
5
  "author": {
6
6
  "name": "ionivetech"
7
7
  },
@@ -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" --branch "feature/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