@ionivetech/mugiwara 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +4 -3
  3. package/.codex-plugin/plugin.json +2 -2
  4. package/.cursor-plugin/plugin.json +2 -2
  5. package/.kimi-plugin/plugin.json +2 -2
  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/plugins/mugiwara.mjs +18 -6
  16. package/AGENTS.md +2 -2
  17. package/GEMINI.md +1 -1
  18. package/README.md +240 -217
  19. package/content/agents/brook-healing.md +17 -3
  20. package/content/agents/chopper-checkpoint.md +17 -3
  21. package/content/agents/eval-runner.md +17 -2
  22. package/content/agents/franky-gates.md +19 -5
  23. package/content/agents/jinbe-security.md +19 -3
  24. package/content/agents/luffy-orchestrator.md +6 -3
  25. package/content/agents/memory-keeper.md +15 -0
  26. package/content/agents/nami-planner.md +21 -6
  27. package/content/agents/onboarding-guide.md +145 -0
  28. package/content/agents/resume-coordinator.md +21 -5
  29. package/content/agents/robin-reviewer.md +18 -3
  30. package/content/agents/sanji-quality.md +19 -4
  31. package/content/agents/skeptic-verifier.md +18 -3
  32. package/content/agents/usopp-brainstorm.md +17 -3
  33. package/content/agents/zoro-execution.md +19 -5
  34. package/content/skills/mugiwara-backend/SKILL.md +1 -1
  35. package/content/skills/mugiwara-brainstorm/SKILL.md +5 -1
  36. package/content/skills/mugiwara-checkpoint/SKILL.md +2 -2
  37. package/content/skills/mugiwara-contract-first/SKILL.md +2 -36
  38. package/content/skills/mugiwara-contract-first/references/process.md +37 -0
  39. package/content/skills/mugiwara-execution/SKILL.md +30 -13
  40. package/content/skills/mugiwara-execution/references/resume-batching.md +32 -0
  41. package/content/skills/mugiwara-frontend/SKILL.md +1 -1
  42. package/content/skills/mugiwara-gates/SKILL.md +32 -30
  43. package/content/skills/mugiwara-healing/SKILL.md +1 -1
  44. package/content/skills/mugiwara-orchestration/SKILL.md +41 -25
  45. package/content/skills/mugiwara-orchestration/references/delegation-pillars.md +40 -0
  46. package/content/skills/mugiwara-orchestration/references/triage-escalation.md +96 -0
  47. package/content/skills/mugiwara-planning/SKILL.md +23 -27
  48. package/content/skills/mugiwara-planning/references/plan-template.md +42 -0
  49. package/content/skills/mugiwara-pr/SKILL.md +2 -2
  50. package/content/skills/mugiwara-quality/SKILL.md +10 -6
  51. package/content/skills/mugiwara-resume/SKILL.md +7 -2
  52. package/content/skills/mugiwara-review/SKILL.md +20 -1
  53. package/content/skills/mugiwara-review/references/code-attributes.md +38 -0
  54. package/content/skills/mugiwara-root-cause/SKILL.md +3 -30
  55. package/content/skills/mugiwara-root-cause/references/process.md +35 -0
  56. package/content/skills/mugiwara-security/SKILL.md +17 -32
  57. package/content/skills/mugiwara-security/references/owasp-top10.md +12 -0
  58. package/content/skills/mugiwara-security/references/rationalizations.md +8 -0
  59. package/content/skills/mugiwara-ship/SKILL.md +5 -4
  60. package/content/skills/mugiwara-workflow/SKILL.md +74 -77
  61. package/content/skills/mugiwara-workflow/references/workspace-layout.md +50 -0
  62. package/content/skills/using-mugiwara/SKILL.md +8 -19
  63. package/dist/mugiwara.js +206 -87
  64. package/gemini-extension.json +1 -1
  65. package/hooks/session-start.ts +1 -1
  66. package/package.json +12 -5
  67. package/plugin.json +2 -2
  68. package/references/multi-actor.md +1 -1
  69. package/scripts/evidence.sh +13 -6
  70. package/scripts/gate-selftest.ts +266 -0
  71. package/scripts/initiative.ts +248 -0
  72. package/scripts/lane.sh +15 -0
  73. package/scripts/mission-report.sh +141 -76
  74. package/scripts/onboard.ts +292 -0
  75. package/scripts/release-notes.ts +42 -13
  76. package/scripts/retrieval-eval.ts +1 -2
  77. package/scripts/run-evals.ts +1 -1
  78. package/scripts/savepoint.sh +97 -50
  79. package/scripts/validate-content.ts +84 -7
  80. package/scripts/verify-install.ts +72 -0
  81. package/src/args.ts +1 -1
  82. package/src/cli.ts +49 -22
  83. package/src/frontmatter.ts +3 -3
  84. package/src/installer.ts +44 -15
  85. package/src/mission.ts +7 -5
  86. package/src/targets/claude.ts +20 -3
  87. package/src/targets/generic.ts +1 -1
  88. package/src/targets/opencode.ts +53 -11
  89. package/docs/adoption-guide.md +0 -72
  90. package/docs/agent-anatomy.md +0 -72
  91. package/docs/agents.md +0 -53
  92. package/docs/audit-trail.md +0 -65
  93. package/docs/claude-setup.md +0 -43
  94. package/docs/codex-setup.md +0 -26
  95. package/docs/comparison.md +0 -58
  96. package/docs/compliance-matrix.md +0 -81
  97. package/docs/config.md +0 -55
  98. package/docs/copilot-setup.md +0 -29
  99. package/docs/cost.md +0 -45
  100. package/docs/cursor-setup.md +0 -25
  101. package/docs/developer-onboarding.md +0 -89
  102. package/docs/enforcement.md +0 -35
  103. package/docs/execution-model.md +0 -92
  104. package/docs/gemini-setup.md +0 -27
  105. package/docs/getting-started.md +0 -158
  106. package/docs/git-strategy.md +0 -62
  107. package/docs/harness-matrix.md +0 -41
  108. package/docs/index.md +0 -56
  109. package/docs/install-antigravity.md +0 -45
  110. package/docs/install-claude.md +0 -75
  111. package/docs/install-cli.md +0 -103
  112. package/docs/install-codex.md +0 -44
  113. package/docs/install-copilot.md +0 -45
  114. package/docs/install-cursor.md +0 -45
  115. package/docs/install-gemini.md +0 -44
  116. package/docs/install-kimi.md +0 -45
  117. package/docs/install-opencode.md +0 -129
  118. package/docs/install-pi.md +0 -46
  119. package/docs/install.md +0 -53
  120. package/docs/lanes.md +0 -82
  121. package/docs/modes.md +0 -74
  122. package/docs/opencode-setup.md +0 -54
  123. package/docs/pr-summary.md +0 -54
  124. package/docs/rule-based-setup.md +0 -31
  125. package/docs/skill-anatomy.md +0 -71
  126. package/docs/skills.md +0 -55
  127. package/docs/troubleshooting.md +0 -91
  128. package/docs/windsurf-setup.md +0 -18
  129. package/docs/workflow.md +0 -89
  130. package/evals/cases/_no-skill.json +0 -16
  131. package/evals/cases/adversarial-pressure-fake-pass.json +0 -25
  132. package/evals/cases/adversarial-pressure-skip-review.json +0 -25
  133. package/evals/cases/lane-exploratory-vague.json +0 -24
  134. package/evals/cases/lane-sensitivity-payment.json +0 -24
  135. package/evals/cases/positive-refactor-existing-tests.json +0 -25
  136. package/evals/cases/positive-resume-mid-mission.json +0 -24
  137. package/evals/cases/routing-agent-security.json +0 -25
  138. package/evals/cases/routing-auth-feature.json +0 -26
  139. package/evals/cases/routing-backend.json +0 -25
  140. package/evals/cases/routing-bug-one-file.json +0 -26
  141. package/evals/cases/routing-claim-audit.json +0 -25
  142. package/evals/cases/routing-context-budget.json +0 -25
  143. package/evals/cases/routing-contract-first.json +0 -25
  144. package/evals/cases/routing-execution.json +0 -25
  145. package/evals/cases/routing-frontend.json +0 -26
  146. package/evals/cases/routing-gates.json +0 -25
  147. package/evals/cases/routing-git.json +0 -25
  148. package/evals/cases/routing-healing.json +0 -25
  149. package/evals/cases/routing-lessons.json +0 -25
  150. package/evals/cases/routing-orchestration.json +0 -25
  151. package/evals/cases/routing-planning.json +0 -26
  152. package/evals/cases/routing-pr.json +0 -25
  153. package/evals/cases/routing-quality.json +0 -25
  154. package/evals/cases/routing-ship.json +0 -26
  155. package/evals/cases/routing-sunset.json +0 -25
  156. package/evals/cases/routing-using-mugiwara.json +0 -25
  157. package/evals/cases/routing-workflow.json +0 -25
  158. package/evals/floor.json +0 -6
package/dist/mugiwara.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { existsSync as existsSync6, realpathSync, 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
- import { resolve } from "node:path";
7
- import { pathToFileURL } from "node:url";
6
+ import { join as join8, resolve } from "node:path";
7
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
8
8
 
9
9
  // src/args.ts
10
10
  var VALUE_FLAGS = { "--project": "project", "--target": "target" };
@@ -15,6 +15,7 @@ var BOOL_FLAGS = {
15
15
  "--force": "force",
16
16
  "--dry-run": "dryRun",
17
17
  "--keep-logs": "keepLogs",
18
+ "--check": "check",
18
19
  "--help": "help",
19
20
  "-h": "help",
20
21
  "--version": "version",
@@ -80,7 +81,7 @@ async function confirm(rl, question) {
80
81
  }
81
82
 
82
83
  // src/targets/claude.ts
83
- import { existsSync, mkdirSync, copyFileSync } from "node:fs";
84
+ import { existsSync, mkdirSync, copyFileSync, chmodSync, readdirSync } from "node:fs";
84
85
  import { dirname, join } from "node:path";
85
86
  import { fileURLToPath } from "node:url";
86
87
 
@@ -93,10 +94,10 @@ function parseFrontmatter(text) {
93
94
  for (const line of m[1].split(/\r?\n/)) {
94
95
  if (!line.trim())
95
96
  continue;
96
- const i = line.indexOf(":");
97
+ const i = line.indexOf(": ");
97
98
  if (i === -1)
98
- throw new Error(`Bad frontmatter line: ${line}`);
99
- data[line.slice(0, i).trim()] = line.slice(i + 1).trim();
99
+ throw new Error(`Bad frontmatter line (missing ': ' separator): ${line}`);
100
+ data[line.slice(0, i).trim()] = line.slice(i + 2).trim();
100
101
  }
101
102
  return { data, body: text.slice(m[0].length) };
102
103
  }
@@ -112,10 +113,12 @@ ${body}`;
112
113
  // src/targets/claude.ts
113
114
  var here = dirname(fileURLToPath(import.meta.url));
114
115
  var HOOK_SRC = join(here, "..", "..", "hooks", "session-start.ts");
116
+ var COMMANDS_SRC = join(here, "..", "..", ".claude", "commands");
115
117
  var target = {
116
118
  id: "claude",
117
119
  label: "Claude Code",
118
120
  native: true,
121
+ refPointerPrefix: "../",
119
122
  paths({ scope, projectDir, home }) {
120
123
  const root = scope === "global" ? join(home, ".claude") : join(projectDir, ".claude");
121
124
  return { skillsDir: join(root, "skills"), agentsDir: join(root, "agents") };
@@ -139,44 +142,81 @@ var target = {
139
142
  postInstall({ scope, projectDir, home, dryRun }) {
140
143
  const root = scope === "global" ? join(home, ".claude") : join(projectDir, ".claude");
141
144
  const hookFile = join(root, "hooks", "session-start.ts");
145
+ const written = [];
146
+ const notes = [];
142
147
  if (dryRun)
143
148
  return { written: [], notes: [] };
144
149
  if (existsSync(HOOK_SRC) && !existsSync(hookFile)) {
145
150
  mkdirSync(dirname(hookFile), { recursive: true });
146
151
  copyFileSync(HOOK_SRC, hookFile);
147
- return { written: [hookFile], notes: [] };
152
+ chmodSync(hookFile, 493);
153
+ written.push(hookFile);
148
154
  }
149
- return { written: [], notes: [] };
155
+ if (existsSync(COMMANDS_SRC)) {
156
+ const dstDir = join(root, "commands");
157
+ mkdirSync(dstDir, { recursive: true });
158
+ for (const f of readdirSync(COMMANDS_SRC)) {
159
+ if (!f.endsWith(".md"))
160
+ continue;
161
+ const src = join(COMMANDS_SRC, f);
162
+ const dst = join(dstDir, f);
163
+ if (!existsSync(dst)) {
164
+ copyFileSync(src, dst);
165
+ written.push(dst);
166
+ } else
167
+ notes.push(`existing command kept: ${dst}`);
168
+ }
169
+ }
170
+ return { written, notes };
150
171
  }
151
172
  };
152
173
 
153
174
  // src/targets/opencode.ts
154
- import { join as join2 } from "node:path";
175
+ import { existsSync as existsSync2, readdirSync as readdirSync2, copyFileSync as copyFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
176
+ import { dirname as dirname2, join as join2 } from "node:path";
177
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
178
+ var here2 = dirname2(fileURLToPath2(import.meta.url));
179
+ var COMMANDS_SRC2 = join2(here2, "..", "..", ".opencode", "commands");
155
180
  var CREW = {
156
181
  "luffy-orchestrator": { color: "#ef4444", temperature: 0.2, steps: 15 },
157
182
  "usopp-brainstorm": { color: "#f59e0b", temperature: 0.6, steps: 15 },
158
183
  "nami-planner": { color: "#f97316", temperature: 0.2, steps: 15 },
159
184
  "zoro-execution": { color: "#22c55e", temperature: 0.1, steps: 30 },
160
- "chopper-checkpoint": { color: "#3b82f6", temperature: 0.1, permission: { edit: "deny" }, steps: 15 },
161
- "sanji-quality": { color: "#a855f7", temperature: 0.1, permission: { edit: "deny" }, steps: 10 },
162
- "franky-gates": { color: "#06b6d4", temperature: 0.1, permission: { edit: "deny" }, steps: 10 },
163
- "robin-reviewer": { color: "#8b5cf6", temperature: 0.2, permission: { edit: "deny" }, steps: 15 },
164
- "jinbe-security": { color: "#6366f1", temperature: 0.2, permission: { edit: "deny" }, steps: 15 },
185
+ "chopper-checkpoint": { color: "#3b82f6", temperature: 0.1, steps: 15 },
186
+ "sanji-quality": { color: "#a855f7", temperature: 0.1, steps: 10 },
187
+ "franky-gates": { color: "#06b6d4", temperature: 0.1, steps: 10 },
188
+ "robin-reviewer": { color: "#8b5cf6", temperature: 0.2, steps: 15 },
189
+ "jinbe-security": { color: "#6366f1", temperature: 0.2, steps: 15 },
165
190
  "brook-healing": { color: "#ec4899", temperature: 0.1, steps: 20 },
166
- "skeptic-verifier": { color: "#64748b", temperature: 0.1, permission: { edit: "deny" }, steps: 12 },
191
+ "skeptic-verifier": { color: "#64748b", temperature: 0.1, steps: 12 },
167
192
  "eval-runner": { color: "#14b8a6", temperature: 0.2, steps: 15 },
168
193
  "resume-coordinator": { color: "#d97706", temperature: 0.2, steps: 10 },
169
194
  "memory-keeper": { color: "#d946ef", temperature: 0.2, steps: 8 }
170
195
  };
171
- function agentFrontmatter(name, description) {
196
+ function permissionFromScope(scope) {
197
+ if (scope === "source")
198
+ return { edit: "allow" };
199
+ if (scope === "artifacts")
200
+ return { edit: { "*": "deny", ".mugiwara/**": "allow" } };
201
+ return;
202
+ }
203
+ function agentFrontmatter(name, description, writeScope) {
172
204
  const crew = CREW[name];
173
205
  const lines = [`description: ${description}`, `mode: all`];
174
206
  if (crew) {
175
207
  lines.push(`color: '${crew.color}'`, `temperature: ${crew.temperature}`, `steps: ${crew.steps}`);
176
- if (crew.permission) {
208
+ const perm = permissionFromScope(writeScope);
209
+ if (perm) {
177
210
  lines.push("permission:");
178
- for (const [k, v] of Object.entries(crew.permission))
179
- lines.push(` ${k}: ${v}`);
211
+ for (const [k, v] of Object.entries(perm)) {
212
+ if (typeof v === "string") {
213
+ lines.push(` ${k}: ${v}`);
214
+ } else {
215
+ lines.push(` ${k}:`);
216
+ for (const [pk, pv] of Object.entries(v))
217
+ lines.push(` "${pk}": ${pv}`);
218
+ }
219
+ }
180
220
  }
181
221
  }
182
222
  return lines.join(`
@@ -186,6 +226,7 @@ var target2 = {
186
226
  id: "opencode",
187
227
  label: "opencode",
188
228
  native: true,
229
+ refPointerPrefix: "../",
189
230
  paths({ scope, projectDir, home }) {
190
231
  const root = scope === "global" ? join2(home, ".config", "opencode") : join2(projectDir, ".opencode");
191
232
  return { skillsDir: join2(root, "skills"), agentsDir: join2(root, "agents") };
@@ -197,7 +238,8 @@ var target2 = {
197
238
  };
198
239
  },
199
240
  transformAgent(data, body) {
200
- const fm = agentFrontmatter(data.name, data.description);
241
+ const desc = data["internal-agent"] === "true" ? `[INTERNAL] ${data.description}` : data.description;
242
+ const fm = agentFrontmatter(data.name, desc, data["write-scope"]);
201
243
  return { relPath: `${data.name}.md`, text: `---
202
244
  ${fm}
203
245
  ---
@@ -206,6 +248,25 @@ ${body}` };
206
248
  refsDir({ scope, projectDir, home }, skillName) {
207
249
  const root = scope === "global" ? join2(home, ".config", "opencode") : join2(projectDir, ".opencode");
208
250
  return join2(root, "skills", skillName, "references");
251
+ },
252
+ postInstall({ scope, projectDir, home, dryRun }) {
253
+ const root = scope === "global" ? join2(home, ".config", "opencode") : join2(projectDir, ".opencode");
254
+ const dstDir = join2(root, "commands");
255
+ if (dryRun)
256
+ return { written: [], notes: [] };
257
+ if (!existsSync2(COMMANDS_SRC2))
258
+ return { written: [], notes: [] };
259
+ mkdirSync2(dstDir, { recursive: true });
260
+ const written = [];
261
+ for (const f of readdirSync2(COMMANDS_SRC2)) {
262
+ if (!f.endsWith(".md"))
263
+ continue;
264
+ const src = join2(COMMANDS_SRC2, f);
265
+ const dst = join2(dstDir, f);
266
+ copyFileSync2(src, dst);
267
+ written.push(dst);
268
+ }
269
+ return { written, notes: [] };
209
270
  }
210
271
  };
211
272
 
@@ -237,7 +298,7 @@ var target3 = {
237
298
  };
238
299
 
239
300
  // src/targets/generic.ts
240
- import { existsSync as existsSync2, writeFileSync } from "node:fs";
301
+ import { existsSync as existsSync3, writeFileSync } from "node:fs";
241
302
  import { join as join4 } from "node:path";
242
303
  function makeGeneric(opts) {
243
304
  const { id, label, rulesDir, bootstrapFile, bootstrapPointer, tier = bootstrapPointer ? 2 : 3 } = opts;
@@ -289,7 +350,10 @@ ${body}` };
289
350
 
290
351
  > ${data.description}
291
352
 
292
- Skills: ${data.skills ?? ""}. Read \`.mugiwara/refs/${data.name}.md\` when embodying this role.`
353
+ Skills: ${data.skills ?? ""}. Read \`.mugiwara/refs/${data.name}.md\` when embodying this role.
354
+
355
+ Only zoro-execution and brook-healing may modify source code.
356
+ Return your output to luffy-orchestrator; do not choose the next step.`
293
357
  };
294
358
  }
295
359
  return { relPath: `agent-${data.name}.md`, text: `# Agent: ${data.name}
@@ -320,7 +384,7 @@ ${body}` };
320
384
  const notes = [];
321
385
  const written = [];
322
386
  const file = join4(projectDir, bootstrapFile);
323
- if (!existsSync2(file)) {
387
+ if (!existsSync3(file)) {
324
388
  if (!dryRun)
325
389
  writeFileSync(file, `${bootstrapPointer}
326
390
  `);
@@ -397,39 +461,49 @@ var targets = { claude: target, opencode: target2, copilot: target3, gemini: tar
397
461
  var TARGET_IDS = Object.keys(targets);
398
462
 
399
463
  // src/installer.ts
400
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, writeFileSync as writeFileSync2, copyFileSync as copyFileSync2, rmSync } from "node:fs";
401
- import { dirname as dirname2, join as join5 } from "node:path";
464
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync as readdirSync3, writeFileSync as writeFileSync2, copyFileSync as copyFileSync3, rmSync } from "node:fs";
465
+ import { dirname as dirname3, join as join5 } from "node:path";
402
466
  import { homedir } from "node:os";
403
- var CONTENT_DIR = join5(import.meta.dirname, "..", "content");
404
- var pkg = JSON.parse(readFileSync2(join5(import.meta.dirname, "..", "package.json"), "utf8"));
467
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
468
+ var CONTENT_DIR = join5(dirname3(fileURLToPath3(import.meta.url)), "..", "content");
469
+ var pkg = JSON.parse(readFileSync2(join5(dirname3(fileURLToPath3(import.meta.url)), "..", "package.json"), "utf8"));
405
470
  var VERSION = pkg.version;
406
471
  function collectContent() {
407
- const skillNames = readdirSync(join5(CONTENT_DIR, "skills"), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
472
+ const skillNames = readdirSync3(join5(CONTENT_DIR, "skills"), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
408
473
  const skills = skillNames.map((name) => {
409
474
  const { data, body } = parseFrontmatter(readFileSync2(join5(CONTENT_DIR, "skills", name, "SKILL.md"), "utf8"));
410
475
  return { name, data, body, refs: collectRefs(join5(CONTENT_DIR, "skills", name)) };
411
476
  });
412
- const agents = readdirSync(join5(CONTENT_DIR, "agents")).filter((f) => f.endsWith(".md")).map((f) => {
477
+ const agents = readdirSync3(join5(CONTENT_DIR, "agents")).filter((f) => f.endsWith(".md")).map((f) => {
413
478
  const { data, body } = parseFrontmatter(readFileSync2(join5(CONTENT_DIR, "agents", f), "utf8"));
414
- return { name: f.replace(/\.md$/, ""), data, body, refs: [] };
479
+ return { name: f.replace(/\.md$/, ""), data, body, refs: [], internal: data.internal === "true" };
415
480
  });
416
- return { skills, agents };
481
+ const sharedRefsDir = join5(dirname3(CONTENT_DIR), "references");
482
+ const sharedRefs = [];
483
+ if (existsSync4(sharedRefsDir)) {
484
+ for (const f of readdirSync3(sharedRefsDir)) {
485
+ if (!f.endsWith(".md"))
486
+ continue;
487
+ sharedRefs.push({ relPath: f, text: readFileSync2(join5(sharedRefsDir, f), "utf8") });
488
+ }
489
+ }
490
+ return { skills, agents, sharedRefs };
417
491
  }
418
492
  function collectRefs(skillDir) {
419
493
  const refsDir = join5(skillDir, "references");
420
- if (!existsSync3(refsDir))
494
+ if (!existsSync4(refsDir))
421
495
  return [];
422
- return readdirSync(refsDir, { recursive: true }).map((f) => String(f)).filter((f) => f.endsWith(".md")).map((rel) => ({ relPath: rel, text: readFileSync2(join5(refsDir, rel), "utf8") }));
496
+ return readdirSync3(refsDir, { recursive: true }).map((f) => String(f)).filter((f) => f.endsWith(".md")).map((rel) => ({ relPath: rel, text: readFileSync2(join5(refsDir, rel), "utf8") }));
423
497
  }
424
498
  function installTo(target10, opts) {
425
499
  const { scope, projectDir, dryRun = false, force = false } = opts;
426
500
  const home = opts.home ?? homedir();
427
- const { skills, agents } = collectContent();
501
+ const { skills, agents, sharedRefs } = collectContent();
428
502
  const dirs = target10.paths({ scope, projectDir, home });
429
503
  const backupRoot = join5(scope === "global" ? home : projectDir, ".mugiwara");
430
504
  const result = { written: [], skipped: [], backedUp: [], notes: [] };
431
505
  const writeOne = (absPath, text) => {
432
- if (existsSync3(absPath)) {
506
+ if (existsSync4(absPath)) {
433
507
  if (readFileSync2(absPath, "utf8") === text) {
434
508
  result.skipped.push(absPath);
435
509
  return;
@@ -440,27 +514,39 @@ function installTo(target10, opts) {
440
514
  return;
441
515
  }
442
516
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
443
- const backupFile = join5(backupRoot, "backup", ts, absPath.replace(/[^a-zA-Z0-9]+/g, "_"));
517
+ const fileName = absPath.replace(/[^a-zA-Z0-9]+/g, "_");
518
+ const backupDir = join5(backupRoot, "backup", `${ts}-${target10.id}`);
519
+ const backupFile = join5(backupDir, fileName);
444
520
  if (!dryRun) {
445
- mkdirSync2(dirname2(backupFile), { recursive: true });
446
- copyFileSync2(absPath, backupFile);
521
+ mkdirSync3(backupDir, { recursive: true });
522
+ copyFileSync3(absPath, backupFile);
447
523
  }
448
524
  result.backedUp.push(absPath);
449
525
  }
450
526
  if (!dryRun) {
451
- mkdirSync2(dirname2(absPath), { recursive: true });
527
+ mkdirSync3(dirname3(absPath), { recursive: true });
452
528
  writeFileSync2(absPath, text);
453
529
  }
454
530
  result.written.push(absPath);
455
531
  };
456
532
  for (const s of skills) {
457
533
  const out = target10.transformSkill(s.data, s.body);
458
- if (out)
459
- writeOne(join5(dirs.skillsDir, out.relPath), out.text);
534
+ if (out) {
535
+ let text = out.text;
536
+ if (target10.refPointerPrefix !== undefined && target10.refPointerPrefix !== "") {
537
+ text = text.replace(/`_shared\/references\//g, "`" + target10.refPointerPrefix + "_shared/references/");
538
+ }
539
+ writeOne(join5(dirs.skillsDir, out.relPath), text);
540
+ }
460
541
  if (target10.transformSkillFull) {
461
542
  const full = target10.transformSkillFull(s.data, s.body);
462
- if (full && target10.refsDir)
463
- writeOne(join5(target10.refsDir({ scope, projectDir, home }, s.name), full.relPath), full.text);
543
+ if (full && target10.refsDir) {
544
+ let text = full.text;
545
+ if (target10.refPointerPrefix !== undefined && target10.refPointerPrefix !== "") {
546
+ text = text.replace(/`_shared\/references\//g, "`" + target10.refPointerPrefix + "_shared/references/");
547
+ }
548
+ writeOne(join5(target10.refsDir({ scope, projectDir, home }, s.name), full.relPath), text);
549
+ }
464
550
  }
465
551
  if (s.refs.length && target10.refsDir) {
466
552
  const refsRoot = target10.refsDir({ scope, projectDir, home }, s.name);
@@ -469,7 +555,7 @@ function installTo(target10, opts) {
469
555
  }
470
556
  }
471
557
  for (const a of agents) {
472
- const out = target10.transformAgent(a.data, a.body);
558
+ const out = target10.transformAgent({ ...a.data, ...a.internal ? { "internal-agent": "true" } : {} }, a.body);
473
559
  if (out)
474
560
  writeOne(join5(dirs.agentsDir, out.relPath), out.text);
475
561
  if (target10.transformAgentFull) {
@@ -478,6 +564,11 @@ function installTo(target10, opts) {
478
564
  writeOne(join5(target10.refsDir({ scope, projectDir, home }, a.name), full.relPath), full.text);
479
565
  }
480
566
  }
567
+ if (sharedRefs.length) {
568
+ const sharedRoot = join5(dirs.skillsDir, "_shared", "references");
569
+ for (const r of sharedRefs)
570
+ writeOne(join5(sharedRoot, r.relPath), r.text);
571
+ }
481
572
  if (target10.postInstall) {
482
573
  const post = target10.postInstall({ scope, projectDir, home, dryRun, files: result.written });
483
574
  result.written.push(...post.written);
@@ -488,7 +579,7 @@ function installTo(target10, opts) {
488
579
  function removeInstalled(manifest, { dryRun = false } = {}) {
489
580
  const removed = [];
490
581
  for (const f of manifest.files) {
491
- if (existsSync3(f)) {
582
+ if (existsSync4(f)) {
492
583
  if (!dryRun)
493
584
  rmSync(f);
494
585
  removed.push(f);
@@ -496,13 +587,12 @@ function removeInstalled(manifest, { dryRun = false } = {}) {
496
587
  }
497
588
  if (!dryRun) {
498
589
  for (const f of manifest.files) {
499
- let d = dirname2(f);
500
- while (existsSync3(d) && readdirSync(d).length === 0) {
501
- rmSync(d, { recursive: true });
502
- const parent = dirname2(d);
503
- if (parent === d)
590
+ let d = dirname3(f);
591
+ while (existsSync4(d) && readdirSync3(d).length === 0) {
592
+ if (d.endsWith("/.mugiwara") || d === dirname3(d))
504
593
  break;
505
- d = parent;
594
+ rmSync(d, { recursive: true, force: true });
595
+ d = dirname3(d);
506
596
  }
507
597
  }
508
598
  }
@@ -510,26 +600,26 @@ function removeInstalled(manifest, { dryRun = false } = {}) {
510
600
  }
511
601
 
512
602
  // src/manifest.ts
513
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
514
- import { dirname as dirname3, join as join6 } from "node:path";
603
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
604
+ import { dirname as dirname4, join as join6 } from "node:path";
515
605
  function manifestPath({ scope, projectDir, home }) {
516
606
  return scope === "global" ? join6(home, ".mugiwara", "manifest.json") : join6(projectDir, ".mugiwara", "manifest.json");
517
607
  }
518
608
  function readManifest(file) {
519
- return existsSync4(file) ? JSON.parse(readFileSync3(file, "utf8")) : null;
609
+ return existsSync5(file) ? JSON.parse(readFileSync3(file, "utf8")) : null;
520
610
  }
521
611
  function writeManifest(file, data) {
522
- mkdirSync3(dirname3(file), { recursive: true });
612
+ mkdirSync4(dirname4(file), { recursive: true });
523
613
  writeFileSync3(file, JSON.stringify(data, null, 2) + `
524
614
  `);
525
615
  }
526
616
 
527
617
  // src/mission.ts
528
- import { existsSync as existsSync5, rmSync as rmSync2, readFileSync as readFileSync4 } from "node:fs";
618
+ import { existsSync as existsSync6, rmSync as rmSync2, readFileSync as readFileSync4, readdirSync as readdirSync4 } from "node:fs";
529
619
  import { join as join7 } from "node:path";
530
620
  function activeActor(projectDir) {
531
621
  const stateFile = join7(projectDir, ".mugiwara", "state.json");
532
- if (!existsSync5(stateFile))
622
+ if (!existsSync6(stateFile))
533
623
  return null;
534
624
  try {
535
625
  const state = JSON.parse(readFileSync4(stateFile, "utf8"));
@@ -540,7 +630,7 @@ function activeActor(projectDir) {
540
630
  }
541
631
  function resetMission(projectDir, keepLogs, force) {
542
632
  const root = join7(projectDir, ".mugiwara");
543
- if (!existsSync5(root))
633
+ if (!existsSync6(root))
544
634
  return { removed: [], kept: [] };
545
635
  if (!force) {
546
636
  const actor = activeActor(projectDir);
@@ -552,29 +642,31 @@ function resetMission(projectDir, keepLogs, force) {
552
642
  const kept = [];
553
643
  for (const dir of ["spec", "plans", "results", "review", "issues", "reports"]) {
554
644
  const p = join7(root, dir);
555
- if (existsSync5(p)) {
645
+ if (existsSync6(p)) {
556
646
  rmSync2(p, { recursive: true, force: true });
557
647
  removed.push(dir);
558
648
  }
559
649
  }
560
- for (const f of ["state.json"]) {
561
- const p = join7(root, f);
562
- if (existsSync5(p)) {
563
- rmSync2(p);
564
- removed.push(f);
650
+ for (const f of readdirSync4(root)) {
651
+ if (/^state(-.+)?\.json$/.test(f)) {
652
+ const p = join7(root, f);
653
+ if (existsSync6(p)) {
654
+ rmSync2(p);
655
+ removed.push(f);
656
+ }
565
657
  }
566
658
  }
567
659
  if (!keepLogs) {
568
660
  const p = join7(root, "logs");
569
- if (existsSync5(p)) {
661
+ if (existsSync6(p)) {
570
662
  rmSync2(p, { recursive: true, force: true });
571
663
  removed.push("logs");
572
664
  }
573
- } else if (existsSync5(join7(root, "logs"))) {
665
+ } else if (existsSync6(join7(root, "logs"))) {
574
666
  kept.push("logs");
575
667
  }
576
668
  for (const f of ["config", "manifest.json", "backup"]) {
577
- if (existsSync5(join7(root, f)))
669
+ if (existsSync6(join7(root, f)))
578
670
  kept.push(f);
579
671
  }
580
672
  return { removed, kept };
@@ -623,25 +715,31 @@ function resetCmd(flags) {
623
715
  }
624
716
  async function resolveOptions(flags) {
625
717
  const interactive = !flag(flags.yes);
718
+ if (interactive && !process.stdin.isTTY) {
719
+ throw new Error("Not a terminal. Run with --yes and --global or --project <dir> --target <ids|all>");
720
+ }
626
721
  const rl = interactive ? createRl() : null;
627
722
  try {
628
723
  let scope = flag(flags.global) ? "global" : str(flags.project) ? "project" : null;
629
724
  if (!scope) {
630
- if (!interactive)
631
- throw new Error("Specify --global or --project <dir> with --yes");
632
- scope = await choose(rl, "Install scope?", ["global (user-wide)", "project (this repo)"]) === 0 ? "global" : "project";
725
+ if (!interactive) {
726
+ scope = "project";
727
+ } else
728
+ scope = await choose(rl, "Install scope?", ["global (user-wide)", "project (this repo)"]) === 0 ? "global" : "project";
633
729
  }
634
730
  const projectDir = resolve(str(flags.project) ?? process.cwd());
635
- if (scope === "project" && !existsSync6(projectDir))
731
+ if (scope === "project" && !existsSync7(projectDir))
636
732
  throw new Error(`Project dir not found: ${projectDir}`);
637
733
  let targetIds = str(flags.target)?.split(",").map((s) => s.trim()) ?? null;
638
734
  if (targetIds && targetIds.includes("all"))
639
735
  targetIds = [...TARGET_IDS];
640
736
  if (!targetIds) {
641
- if (!interactive)
642
- throw new Error("Specify --target <ids|all> with --yes");
643
- const idx = await multiChoose(rl, "Target AI agents?", ["all targets", ...TARGET_IDS]);
644
- targetIds = idx.includes(0) ? [...TARGET_IDS] : idx.map((i) => TARGET_IDS[i - 1]);
737
+ if (!interactive) {
738
+ targetIds = [...TARGET_IDS];
739
+ } else {
740
+ const idx = await multiChoose(rl, "Target AI agents?", ["all targets", ...TARGET_IDS]);
741
+ targetIds = idx.includes(0) ? [...TARGET_IDS] : idx.map((i) => TARGET_IDS[i - 1]);
742
+ }
645
743
  }
646
744
  for (const id of targetIds) {
647
745
  if (!targets[id])
@@ -701,6 +799,14 @@ async function uninstall(flags) {
701
799
  const file = manifestPath({ scope, projectDir, home });
702
800
  const manifest = readManifest(file);
703
801
  if (!manifest) {
802
+ if (scope === "project") {
803
+ const globalFile = manifestPath({ scope: "global", projectDir, home });
804
+ if (existsSync7(globalFile)) {
805
+ console.log(`No project manifest found, but a global install exists. Try:
806
+ mugiwara uninstall --global`);
807
+ return;
808
+ }
809
+ }
704
810
  console.log("Nothing installed (no manifest found).");
705
811
  return;
706
812
  }
@@ -715,8 +821,20 @@ async function uninstall(flags) {
715
821
  }
716
822
  }
717
823
  const removed = removeInstalled(manifest, { dryRun: flag(flags.dryRun) });
718
- if (!flag(flags.dryRun))
824
+ if (!flag(flags.dryRun)) {
719
825
  rmSync3(file);
826
+ const mugiDir = join8(scope === "global" ? home : projectDir, ".mugiwara");
827
+ if (existsSync7(mugiDir) && readdirSync5(mugiDir).length === 0) {
828
+ rmSync3(mugiDir, { recursive: true, force: true });
829
+ }
830
+ if (manifest.targets.includes("opencode")) {
831
+ const opencodeCache = join8(home, ".cache", "opencode", "packages", "@ionivetech");
832
+ if (existsSync7(opencodeCache)) {
833
+ rmSync3(opencodeCache, { recursive: true, force: true });
834
+ console.log(" cleared opencode npm cache (stale plugin versions)");
835
+ }
836
+ }
837
+ }
720
838
  console.log(`OK removed ${removed.length} files`);
721
839
  }
722
840
  function list(flags) {
@@ -731,7 +849,12 @@ function list(flags) {
731
849
  if (!m)
732
850
  continue;
733
851
  found = true;
734
- console.log(`${label}: v${m.version} targets=${m.targets.join(",")} files=${m.files.length} installed=${m.installedAt}`);
852
+ if (flag(flags.check)) {
853
+ const missing = m.files.filter((f) => !existsSync7(f));
854
+ console.log(`${label}: v${m.version} targets=${m.targets.join(",")} files=${m.files.length} missing=${missing.length} installed=${m.installedAt}`);
855
+ } else {
856
+ console.log(`${label}: v${m.version} targets=${m.targets.join(",")} files=${m.files.length} installed=${m.installedAt}`);
857
+ }
735
858
  }
736
859
  if (!found)
737
860
  console.log("No mugiwara installation found.");
@@ -740,10 +863,11 @@ function help() {
740
863
  console.log(`mugiwara ${VERSION} — the Straw Hat crew for AI agents
741
864
 
742
865
  Usage:
743
- mugiwara [install] install the crew (default; wizard when flags missing)
866
+ mugiwara [install] install the crew (wizard; with --yes uses project + all)
744
867
  mugiwara update replace existing files (backs up differences first)
745
868
  mugiwara uninstall remove installed files via manifest
746
869
  mugiwara list show installations
870
+ mugiwara list --check health check: show installations + missing files
747
871
  mugiwara reset wipe mission state (spec/plans/results/review/issues[/logs])
748
872
  mugiwara --help this help
749
873
  mugiwara --version print version
@@ -752,18 +876,13 @@ Flags:
752
876
  --global user-wide install
753
877
  --project <dir> project install (default: cwd)
754
878
  --target <ids|all> comma-separated: ${TARGET_IDS.join(", ")}
755
- --yes, -y non-interactive (needs --global/--project, --target)
879
+ --yes, -y non-interactive (defaults: project, all targets)
756
880
  --force overwrite differing files (with backup)
757
881
  --dry-run print actions without writing
882
+ --check with list: report missing files (health check)
758
883
  --keep-logs with reset: keep .mugiwara/logs (lessons ledger survives)`);
759
884
  }
760
- var entry = process.argv[1] !== undefined ? resolve(process.argv[1]) : undefined;
761
- if (entry !== undefined) {
762
- try {
763
- entry = realpathSync(entry);
764
- } catch {}
765
- }
766
- var isMain = entry !== undefined && import.meta.url === pathToFileURL(entry).href;
885
+ var isMain = process.argv[1] !== undefined && fileURLToPath4(import.meta.url) === resolve(process.argv[1]);
767
886
  if (isMain) {
768
887
  run(process.argv.slice(2)).catch((err) => {
769
888
  console.error(`mugiwara: ${err.message}`);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mugiwara",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
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
  }
@@ -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.4",
3
+ "version": "0.6.0",
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,9 +57,10 @@
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",
56
- "verify-pack": "npm pack --dry-run 2>&1 | grep -q 'ionivetech-mugiwara' && echo 'npm package clean' || (echo 'npm pack failed' && exit 1)",
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",
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
  },
59
66
  "keywords": [