@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/src/installer.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/installer.ts
2
- import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, copyFileSync, rmSync } from 'node:fs';
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, copyFileSync, rmSync, lstatSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { homedir } from 'node:os';
5
5
  import { fileURLToPath } from 'node:url';
@@ -11,6 +11,7 @@ export type ContentItem = {
11
11
  data: FrontmatterData;
12
12
  body: string;
13
13
  refs: { relPath: string; text: string }[];
14
+ internal?: boolean;
14
15
  };
15
16
 
16
17
  export type InstallOptions = {
@@ -35,6 +36,7 @@ export interface Target {
35
36
  label: string;
36
37
  native: boolean;
37
38
  tier?: 1 | 2 | 3;
39
+ refPointerPrefix?: string;
38
40
  paths(opts: { scope: Scope; projectDir: string; home: string }): { skillsDir: string; agentsDir: string };
39
41
  transformSkill(data: FrontmatterData, body: string): TransformOut;
40
42
  transformAgent(data: FrontmatterData, body: string): TransformOut;
@@ -48,7 +50,7 @@ export const CONTENT_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', '
48
50
  const pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8')) as { version: string };
49
51
  export const VERSION = pkg.version;
50
52
 
51
- export function collectContent(): { skills: ContentItem[]; agents: ContentItem[] } {
53
+ export function collectContent(): { skills: ContentItem[]; agents: ContentItem[]; sharedRefs: { relPath: string; text: string }[] } {
52
54
  const skillNames = readdirSync(join(CONTENT_DIR, 'skills'), { withFileTypes: true })
53
55
  .filter(e => e.isDirectory()).map(e => e.name);
54
56
  const skills = skillNames.map(name => {
@@ -59,9 +61,17 @@ export function collectContent(): { skills: ContentItem[]; agents: ContentItem[]
59
61
  .filter(f => f.endsWith('.md'))
60
62
  .map(f => {
61
63
  const { data, body } = parseFrontmatter(readFileSync(join(CONTENT_DIR, 'agents', f), 'utf8'));
62
- return { name: f.replace(/\.md$/, ''), data, body, refs: [] as { relPath: string; text: string }[] };
64
+ return { name: f.replace(/\.md$/, ''), data, body, refs: [] as { relPath: string; text: string }[], internal: data.internal === 'true' };
63
65
  });
64
- return { skills, agents };
66
+ const sharedRefsDir = join(dirname(CONTENT_DIR), 'references');
67
+ const sharedRefs: { relPath: string; text: string }[] = [];
68
+ if (existsSync(sharedRefsDir)) {
69
+ for (const f of readdirSync(sharedRefsDir)) {
70
+ if (!f.endsWith('.md')) continue;
71
+ sharedRefs.push({ relPath: f, text: readFileSync(join(sharedRefsDir, f), 'utf8') });
72
+ }
73
+ }
74
+ return { skills, agents, sharedRefs };
65
75
  }
66
76
 
67
77
  function collectRefs(skillDir: string): { relPath: string; text: string }[] {
@@ -74,7 +84,7 @@ function collectRefs(skillDir: string): { relPath: string; text: string }[] {
74
84
  export function installTo(target: Target, opts: InstallOptions): InstallResult {
75
85
  const { scope, projectDir, dryRun = false, force = false } = opts;
76
86
  const home = opts.home ?? homedir();
77
- const { skills, agents } = collectContent();
87
+ const { skills, agents, sharedRefs } = collectContent();
78
88
  const dirs = target.paths({ scope, projectDir, home });
79
89
  const backupRoot = join(scope === 'global' ? home : projectDir, '.mugiwara');
80
90
  const result: InstallResult = { written: [], skipped: [], backedUp: [], notes: [] };
@@ -100,10 +110,22 @@ export function installTo(target: Target, opts: InstallOptions): InstallResult {
100
110
 
101
111
  for (const s of skills) {
102
112
  const out = target.transformSkill(s.data, s.body);
103
- if (out) writeOne(join(dirs.skillsDir, out.relPath), out.text);
113
+ if (out) {
114
+ let text = out.text;
115
+ if (target.refPointerPrefix !== undefined && target.refPointerPrefix !== '') {
116
+ text = text.replace(/`_shared\/references\//g, '`' + target.refPointerPrefix + '_shared/references/');
117
+ }
118
+ writeOne(join(dirs.skillsDir, out.relPath), text);
119
+ }
104
120
  if (target.transformSkillFull) {
105
121
  const full = target.transformSkillFull(s.data, s.body);
106
- if (full && target.refsDir) writeOne(join(target.refsDir({ scope, projectDir, home }, s.name), full.relPath), full.text);
122
+ if (full && target.refsDir) {
123
+ let text = full.text;
124
+ if (target.refPointerPrefix !== undefined && target.refPointerPrefix !== '') {
125
+ text = text.replace(/`_shared\/references\//g, '`' + target.refPointerPrefix + '_shared/references/');
126
+ }
127
+ writeOne(join(target.refsDir({ scope, projectDir, home }, s.name), full.relPath), text);
128
+ }
107
129
  }
108
130
  if (s.refs.length && target.refsDir) {
109
131
  const refsRoot = target.refsDir({ scope, projectDir, home }, s.name);
@@ -111,7 +133,7 @@ export function installTo(target: Target, opts: InstallOptions): InstallResult {
111
133
  }
112
134
  }
113
135
  for (const a of agents) {
114
- const out = target.transformAgent(a.data, a.body);
136
+ const out = target.transformAgent({ ...a.data, ...(a.internal ? { 'internal-agent': 'true' } : {}) }, a.body);
115
137
  if (out) writeOne(join(dirs.agentsDir, out.relPath), out.text);
116
138
  if (target.transformAgentFull) {
117
139
  const full = target.transformAgentFull(a.data, a.body);
@@ -119,6 +141,11 @@ export function installTo(target: Target, opts: InstallOptions): InstallResult {
119
141
  }
120
142
  }
121
143
 
144
+ if (sharedRefs.length) {
145
+ const sharedRoot = join(dirs.skillsDir, '_shared', 'references');
146
+ for (const r of sharedRefs) writeOne(join(sharedRoot, r.relPath), r.text);
147
+ }
148
+
122
149
  if (target.postInstall) {
123
150
  const post = target.postInstall({ scope, projectDir, home, dryRun, files: result.written });
124
151
  result.written.push(...post.written);
@@ -144,3 +171,39 @@ export function removeInstalled(manifest: { files: string[] }, { dryRun = false
144
171
  }
145
172
  return removed;
146
173
  }
174
+
175
+ function assertNotSymlink(file: string): void {
176
+ if (!existsSync(file)) return;
177
+ try {
178
+ if (lstatSync(file).isSymbolicLink()) throw new Error(`refusing to follow symlink: ${file}`);
179
+ } catch (e) {
180
+ if ((e as { code?: string }).code === 'ENOENT') return;
181
+ throw e;
182
+ }
183
+ }
184
+
185
+ const GITIGNORE_MARKER = '# mugiwara';
186
+ const GITIGNORE_BLOCK = `# mugiwara — audit trail is the product: commit reports/, results/, logs/, spec/, plans/.
187
+ # Ignore session state and regenerated files.
188
+ .mugiwara/state.json
189
+ .mugiwara/state-*.json
190
+ .mugiwara/config
191
+ .mugiwara/continue.md
192
+ .mugiwara/refs/
193
+ `;
194
+
195
+ export function ensureProjectGitignore(projectDir: string, opts: { dryRun?: boolean } = {}): { appended: boolean; notes: string[] } {
196
+ const { dryRun = false } = opts;
197
+ const path = join(projectDir, '.gitignore');
198
+ assertNotSymlink(path);
199
+ if (existsSync(path) && readFileSync(path, 'utf8').includes(GITIGNORE_MARKER)) {
200
+ return { appended: false, notes: [] };
201
+ }
202
+ const existing = existsSync(path) ? readFileSync(path, 'utf8') : '';
203
+ const separator = existing.length && !existing.endsWith('\n') ? '\n' : '';
204
+ if (!dryRun) {
205
+ mkdirSync(dirname(path), { recursive: true });
206
+ writeFileSync(path, existing + separator + GITIGNORE_BLOCK);
207
+ }
208
+ return { appended: true, notes: [`.gitignore ${dryRun ? 'would append' : 'appended'} mugiwara audit-trail block`] };
209
+ }
package/src/mission.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/mission.ts
2
2
  // Mission-state helpers for the mugiwara CLI (installer + reset only).
3
- import { existsSync, rmSync, readFileSync } from 'node:fs';
3
+ import { existsSync, rmSync, readFileSync, readdirSync, mkdirSync, appendFileSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
5
 
6
6
  function activeActor(projectDir: string): string | null {
@@ -30,10 +30,12 @@ export function resetMission(projectDir: string, keepLogs: boolean, force?: bool
30
30
  const p = join(root, dir);
31
31
  if (existsSync(p)) { rmSync(p, { recursive: true, force: true }); removed.push(dir); }
32
32
  }
33
- // mission state files
34
- for (const f of ['state.json']) {
35
- const p = join(root, f);
36
- if (existsSync(p)) { rmSync(p); removed.push(f); }
33
+ // mission state files — state.json + branch-specific state-*.json
34
+ for (const f of readdirSync(root)) {
35
+ if (/^state(-.+)?\.json$/.test(f)) {
36
+ const p = join(root, f);
37
+ if (existsSync(p)) { rmSync(p); removed.push(f); }
38
+ }
37
39
  }
38
40
  if (!keepLogs) {
39
41
  const p = join(root, 'logs');
@@ -46,3 +48,113 @@ export function resetMission(projectDir: string, keepLogs: boolean, force?: bool
46
48
  }
47
49
  return { removed, kept };
48
50
  }
51
+
52
+ export function archiveMission(projectDir: string, mission: string, opts: { dryRun?: boolean } = {}): { report: string | null; removed: string[]; kept: string[]; index?: string } {
53
+ const { dryRun = false } = opts;
54
+ const root = join(projectDir, '.mugiwara');
55
+ // mission allowlist — same as savepoint.sh / mission-report.sh. Dot-only
56
+ // names (".", "..") would resolve upward through join(...,"..") and let
57
+ // rmSync reach state.json/config outside the mission dir.
58
+ if (!mission || /[^a-zA-Z0-9._-]/.test(mission) || /^\.+$/.test(mission)) throw new Error(`invalid mission name "${mission}" (allowlist: [a-zA-Z0-9._-], not a dot-path)`);
59
+ const removed: string[] = [];
60
+ const kept: string[] = [];
61
+
62
+ // A file belongs to this mission when stripping the optional YYYY-MM-DD-
63
+ // prefix leaves `<mission>.md` or `<mission>-<suffix>.md`. Covers both the
64
+ // bare names and the date-prefixed names the prose writes (audit-trail.md).
65
+ const belongs = (f: string): boolean => {
66
+ const base = f.replace(/^\d{4}-\d{2}-\d{2}-/, '');
67
+ return base === `${mission}.md` || base.startsWith(`${mission}-`);
68
+ };
69
+
70
+ // locate the report (the archive target that must survive). Reports are
71
+ // date-prefixed (`reports/YYYY-MM-DD-<mission>.md`); compare the stripped
72
+ // mission name so `bar-foo.md` is not mistaken for mission `foo`.
73
+ let report: string | null = null;
74
+ const reportsDir = join(root, 'reports');
75
+ if (existsSync(reportsDir)) {
76
+ const f = readdirSync(reportsDir).find(n => {
77
+ const m = n.match(/^(\d{4}-\d{2}-\d{2})-(.+)\.md$/);
78
+ return !!m && m[2] === mission;
79
+ });
80
+ if (f) report = join('reports', f);
81
+ }
82
+
83
+ // remove per-mission wave intermediates from results/<mission>/, EXCEPT
84
+ // 06-closure.md and 07-pr-verdict.md (PR material + closure stay)
85
+ const resultsDir = join(root, 'results', mission);
86
+ if (existsSync(resultsDir)) {
87
+ for (const f of readdirSync(resultsDir)) {
88
+ if (f === '06-closure.md' || f === '07-pr-verdict.md') { kept.push(join('results', mission, f)); continue; }
89
+ const p = join(resultsDir, f);
90
+ if (!dryRun) rmSync(p, { recursive: true, force: true });
91
+ removed.push(join('results', mission, f));
92
+ }
93
+ }
94
+
95
+ // spec, review, issues, per-mission decision log — bare + date-prefixed
96
+ const specDir = join(root, 'spec');
97
+ if (existsSync(specDir)) {
98
+ for (const f of readdirSync(specDir)) {
99
+ if (!belongs(f)) continue;
100
+ const p = join(specDir, f);
101
+ if (!dryRun) rmSync(p);
102
+ removed.push(join('spec', f));
103
+ }
104
+ }
105
+
106
+ for (const dir of ['review', 'issues']) {
107
+ const d = join(root, dir);
108
+ if (!existsSync(d)) continue;
109
+ for (const f of readdirSync(d)) {
110
+ if (!belongs(f)) continue;
111
+ const p = join(d, f);
112
+ if (!dryRun) rmSync(p, { force: true });
113
+ removed.push(join(dir, f));
114
+ }
115
+ }
116
+
117
+ const logsDir = join(root, 'logs');
118
+ if (existsSync(logsDir)) {
119
+ for (const f of readdirSync(logsDir)) {
120
+ if (!belongs(f)) continue;
121
+ const p = join(logsDir, f);
122
+ if (!dryRun) rmSync(p);
123
+ removed.push(join('logs', f));
124
+ }
125
+ }
126
+
127
+ // continue.md is a session handoff — only remove it if it belongs to THIS
128
+ // mission (its content references the mission name); otherwise leave it.
129
+ const cont = join(root, 'continue.md');
130
+ if (existsSync(cont)) {
131
+ try {
132
+ if (readFileSync(cont, 'utf8').includes(mission)) {
133
+ if (!dryRun) rmSync(cont);
134
+ removed.push('continue.md');
135
+ }
136
+ } catch { /* unreadable — leave it */ }
137
+ }
138
+
139
+ // kept: report + the audit-trail survivors
140
+ if (report) kept.push(report);
141
+ for (const k of ['plans', 'config', 'state.json', join('logs', 'lessons.md')]) {
142
+ if (existsSync(join(root, k))) kept.push(k);
143
+ }
144
+
145
+ // summary index: append one line per archived mission (retention aid),
146
+ // idempotently — never duplicate a line for an already-indexed mission.
147
+ let index: string | undefined;
148
+ const indexFile = join(root, 'reports', 'index.md');
149
+ const line = `- ${mission} — ${new Date().toISOString().slice(0, 10)}${report ? ` → ${report}` : ''}\n`;
150
+ if (!dryRun) {
151
+ mkdirSync(join(root, 'reports'), { recursive: true });
152
+ const existing = existsSync(indexFile) ? readFileSync(indexFile, 'utf8') : '';
153
+ if (!existing.split(/\r?\n/).some(l => l.startsWith(`- ${mission} —`))) {
154
+ const header = existing ? '' : '# Mission index\n\n';
155
+ appendFileSync(indexFile, header + line);
156
+ }
157
+ index = join('reports', 'index.md');
158
+ }
159
+ return { report, removed, kept, index };
160
+ }
@@ -1,17 +1,27 @@
1
1
  // src/targets/claude.ts
2
- import { existsSync, readFileSync, mkdirSync, copyFileSync } from 'node:fs';
2
+ import { existsSync, readFileSync, mkdirSync, copyFileSync, chmodSync, readdirSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { stringifyFrontmatter, type FrontmatterData } from '../frontmatter.ts';
6
6
  import type { Target } from '../installer.ts';
7
7
 
8
8
  const here = dirname(fileURLToPath(import.meta.url));
9
- const HOOK_SRC = join(here, '..', '..', 'hooks', 'session-start.ts');
9
+ const HOOKS_SRC = join(here, '..', '..', 'hooks');
10
+ const COMMANDS_SRC = join(here, '..', '..', '.claude', 'commands');
11
+
12
+ // Claude Code has no path-scoped permission. write-scope maps to a partial
13
+ // `tools:` list: artifacts agents lose Edit (cannot modify existing source)
14
+ // but keep Write (must create .mugiwara/**); source agents get the default set.
15
+ function toolsFromScope(scope?: string): string | undefined {
16
+ if (scope === 'artifacts') return 'Read, Grep, Glob, Write, Bash, WebFetch, WebSearch';
17
+ return undefined;
18
+ }
10
19
 
11
20
  export const target: Target = {
12
21
  id: 'claude',
13
22
  label: 'Claude Code',
14
23
  native: true,
24
+ refPointerPrefix: '../',
15
25
  paths({ scope, projectDir, home }) {
16
26
  const root = scope === 'global' ? join(home, '.claude') : join(projectDir, '.claude');
17
27
  return { skillsDir: join(root, 'skills'), agentsDir: join(root, 'agents') };
@@ -25,6 +35,10 @@ export const target: Target = {
25
35
  transformAgent(data: FrontmatterData, body: string) {
26
36
  const fm: FrontmatterData = { name: data.name, description: data.description };
27
37
  if (data.tools) fm.tools = data.tools;
38
+ else {
39
+ const generated = toolsFromScope(data['write-scope']);
40
+ if (generated) fm.tools = generated;
41
+ }
28
42
  return { relPath: `${data.name}.md`, text: stringifyFrontmatter(fm, body) };
29
43
  },
30
44
  refsDir({ scope, projectDir, home }, skillName: string) {
@@ -32,15 +46,37 @@ export const target: Target = {
32
46
  return join(root, 'skills', skillName, 'references');
33
47
  },
34
48
  postInstall({ scope, projectDir, home, dryRun }) {
35
- // Wire the SessionStart hook (inline doctrine) into the installed .claude dir.
49
+ // Wire hook scripts (SessionStart + UserPromptSubmit) into the installed .claude dir.
36
50
  const root = scope === 'global' ? join(home, '.claude') : join(projectDir, '.claude');
37
- const hookFile = join(root, 'hooks', 'session-start.ts');
51
+ const written: string[] = [];
52
+ const notes: string[] = [];
38
53
  if (dryRun) return { written: [], notes: [] };
39
- if (existsSync(HOOK_SRC) && !existsSync(hookFile)) {
40
- mkdirSync(dirname(hookFile), { recursive: true });
41
- copyFileSync(HOOK_SRC, hookFile);
42
- return { written: [hookFile], notes: [] };
54
+ if (existsSync(HOOKS_SRC)) {
55
+ for (const f of readdirSync(HOOKS_SRC)) {
56
+ if (!f.endsWith('.ts')) continue;
57
+ const dst = join(root, 'hooks', f);
58
+ if (!existsSync(dst)) {
59
+ mkdirSync(dirname(dst), { recursive: true });
60
+ copyFileSync(join(HOOKS_SRC, f), dst);
61
+ // /bin/sh executes hooks via shebang — a non-executable copy is a
62
+ // "Permission denied" at first user prompt. chmod every hook file.
63
+ chmodSync(dst, 0o755);
64
+ written.push(dst);
65
+ }
66
+ }
67
+ }
68
+ // Port the /mugiwara commands into the installed .claude dir.
69
+ if (existsSync(COMMANDS_SRC)) {
70
+ const dstDir = join(root, 'commands');
71
+ mkdirSync(dstDir, { recursive: true });
72
+ for (const f of readdirSync(COMMANDS_SRC)) {
73
+ if (!f.endsWith('.md')) continue;
74
+ const src = join(COMMANDS_SRC, f);
75
+ const dst = join(dstDir, f);
76
+ if (!existsSync(dst)) { copyFileSync(src, dst); written.push(dst); }
77
+ else notes.push(`existing command kept: ${dst}`);
78
+ }
43
79
  }
44
- return { written: [], notes: [] };
80
+ return { written, notes };
45
81
  },
46
82
  };
@@ -44,7 +44,7 @@ export function makeGeneric(opts: {
44
44
  if (stubOnly) {
45
45
  return {
46
46
  relPath: `agent-${data.name}.md`,
47
- text: `# Agent: ${data.name}\n\n> ${data.description}\n\nSkills: ${data.skills ?? ''}. Read \`.mugiwara/refs/${data.name}.md\` when embodying this role.`,
47
+ text: `# Agent: ${data.name}\n\n> ${data.description}\n\nSkills: ${data.skills ?? ''}. Read \`.mugiwara/refs/${data.name}.md\` when embodying this role.\n\nOnly zoro-execution and brook-healing may modify source code.\nReturn your output to luffy-orchestrator; do not choose the next step.`,
48
48
  };
49
49
  }
50
50
  return { relPath: `agent-${data.name}.md`, text: `# Agent: ${data.name}\n\n> ${data.description}\n\nSkills used: ${data.skills ?? ''}\n\n${body}` };
@@ -20,26 +20,45 @@ const CREW: Record<string, CrewConfig> = {
20
20
  'usopp-brainstorm': { color: '#f59e0b', temperature: 0.6, steps: 15 },
21
21
  'nami-planner': { color: '#f97316', temperature: 0.2, steps: 15 },
22
22
  'zoro-execution': { color: '#22c55e', temperature: 0.1, steps: 30 },
23
- 'chopper-checkpoint': { color: '#3b82f6', temperature: 0.1, permission: { edit: 'deny' }, steps: 15 },
24
- 'sanji-quality': { color: '#a855f7', temperature: 0.1, permission: { edit: 'deny' }, steps: 10 },
25
- 'franky-gates': { color: '#06b6d4', temperature: 0.1, permission: { edit: 'deny' }, steps: 10 },
26
- 'robin-reviewer': { color: '#8b5cf6', temperature: 0.2, permission: { edit: 'deny' }, steps: 15 },
27
- 'jinbe-security': { color: '#6366f1', temperature: 0.2, permission: { edit: 'deny' }, steps: 15 },
23
+ 'chopper-checkpoint': { color: '#3b82f6', temperature: 0.1, steps: 15 },
24
+ 'sanji-quality': { color: '#a855f7', temperature: 0.1, steps: 10 },
25
+ 'franky-gates': { color: '#06b6d4', temperature: 0.1, steps: 10 },
26
+ 'robin-reviewer': { color: '#8b5cf6', temperature: 0.2, steps: 15 },
27
+ 'jinbe-security': { color: '#6366f1', temperature: 0.2, steps: 15 },
28
28
  'brook-healing': { color: '#ec4899', temperature: 0.1, steps: 20 },
29
- 'skeptic-verifier': { color: '#64748b', temperature: 0.1, permission: { edit: 'deny' }, steps: 12 },
29
+ 'skeptic-verifier': { color: '#64748b', temperature: 0.1, steps: 12 },
30
30
  'eval-runner': { color: '#14b8a6', temperature: 0.2, steps: 15 },
31
31
  'resume-coordinator': { color: '#d97706', temperature: 0.2, steps: 10 },
32
32
  'memory-keeper': { color: '#d946ef', temperature: 0.2, steps: 8 },
33
33
  };
34
34
 
35
- function agentFrontmatter(name: string, description: string) {
35
+ // write-scope is the single source of truth (content/agents/*.md frontmatter).
36
+ // The path boundary (artifacts vs source) IS expressible in opencode:
37
+ // permission.edit accepts glob/pattern -> action, last match wins. Artifacts
38
+ // agents get deny-all-edit except .mugiwara/**; source agents (zoro, brook)
39
+ // get full edit allow. Derived at install time from the frontmatter field.
40
+ function permissionFromScope(scope: string | undefined): Record<string, string | Record<string, string>> | undefined {
41
+ if (scope === 'source') return { edit: 'allow' };
42
+ if (scope === 'artifacts') return { edit: { '*': 'deny', '.mugiwara/**': 'allow' } };
43
+ return undefined;
44
+ }
45
+
46
+ function agentFrontmatter(name: string, description: string, writeScope?: string) {
36
47
  const crew = CREW[name];
37
48
  const lines = [`description: ${description}`, `mode: all`];
38
49
  if (crew) {
39
50
  lines.push(`color: '${crew.color}'`, `temperature: ${crew.temperature}`, `steps: ${crew.steps}`);
40
- if (crew.permission) {
51
+ const perm = permissionFromScope(writeScope);
52
+ if (perm) {
41
53
  lines.push('permission:');
42
- for (const [k, v] of Object.entries(crew.permission)) lines.push(` ${k}: ${v}`);
54
+ for (const [k, v] of Object.entries(perm)) {
55
+ if (typeof v === 'string') {
56
+ lines.push(` ${k}: ${v}`);
57
+ } else {
58
+ lines.push(` ${k}:`);
59
+ for (const [pk, pv] of Object.entries(v)) lines.push(` "${pk}": ${pv}`);
60
+ }
61
+ }
43
62
  }
44
63
  }
45
64
  return lines.join('\n');
@@ -49,6 +68,7 @@ export const target: Target = {
49
68
  id: 'opencode',
50
69
  label: 'opencode',
51
70
  native: true,
71
+ refPointerPrefix: '../',
52
72
  paths({ scope, projectDir, home }) {
53
73
  const root = scope === 'global' ? join(home, '.config', 'opencode') : join(projectDir, '.opencode');
54
74
  return { skillsDir: join(root, 'skills'), agentsDir: join(root, 'agents') };
@@ -60,7 +80,8 @@ export const target: Target = {
60
80
  };
61
81
  },
62
82
  transformAgent(data: FrontmatterData, body: string) {
63
- const fm = agentFrontmatter(data.name, data.description);
83
+ const desc = data['internal-agent'] === 'true' ? `[INTERNAL] ${data.description}` : data.description;
84
+ const fm = agentFrontmatter(data.name, desc, data['write-scope']);
64
85
  return { relPath: `${data.name}.md`, text: `---\n${fm}\n---\n${body}` };
65
86
  },
66
87
  refsDir({ scope, projectDir, home }, skillName: string) {
@@ -1,53 +0,0 @@
1
- # The Crew — 14 Agents
2
-
3
- Every agent is a focused specialist. The main thread embodies each role inline
4
- using its skill; you can also summon any member directly. "Dispatch" below
5
- means "route the mission to this role."
6
-
7
- The front door is the `using-mugiwara` skill — load it for crew overview.
8
- Slash command: `/using-mugiwara`.
9
-
10
- | Agent | Crew member | Role | Summon for |
11
- |-------|-------------|------|------------|
12
- | `luffy-orchestrator` | Luffy | Captain — 5-way triage, check-ins, decisions, closure | mission start, wave boundaries, escalations |
13
- | `usopp-brainstorm` | Usopp | Critical friend — interrogates, researches, no rubber-stamps | vague ideas, direction, options |
14
- | `nami-planner` | Nami | Planner — interview-first, full-context scan, scaled plans | turning an idea into an execution plan |
15
- | `zoro-execution` | Zoro | Executor — inline sequential tasks, parallel worker batches, evidence per task | executing an approved plan |
16
- | `chopper-checkpoint` | Chopper | Auditor — verify-everything, deduped re-runs, failure ledger | auditing a wave's results |
17
- | `sanji-quality` | Sanji | Quality — discover real tooling, format/lint/test | after checkpoint passes |
18
- | `franky-gates` | Franky | Gates — coverage, build, Definition of Done, binary verdicts | after quality checks |
19
- | `robin-reviewer` | Robin | Reviewer — doubt-driven diff review, breaking-change map first | after gates pass |
20
- | `jinbe-security` | Jinbe | Security — STRIDE, OWASP, secrets, injection, dependencies | security audit of a diff |
21
- | `brook-healing` | Brook | Healer — reads the ledger, root-cause fixes, ≤3 cycles | any wave produced failures |
22
- | `skeptic-verifier` | Skeptic | Adversarial verifier — doubts every output, does NOT validate | high-stakes verdicts, plans, reviews |
23
- | `eval-runner` | Eval Runner | Harness tester — task suites, judge-agent comparison | verifying mugiwara itself works |
24
- | `resume-coordinator` | Resume Coordinator | Resumer — rebuilds state from `.mugiwara/`, continues never restarts | context loss, new session mid-mission |
25
- | `memory-keeper` | Memory Keeper | Institutional memory — surfaces past lessons, captures new ones | mission start + closure |
26
-
27
- ## How to summon
28
-
29
- Say a crew member's name in your request:
30
-
31
- ```
32
- > Chopper, audit the last wave against the plan
33
- > Nami, plan this out
34
- ```
35
-
36
- Luffy still records the route and its reason, and direct calls do not skip
37
- check-ins. The harness stays coherent either way.
38
-
39
- ## Who never does what
40
-
41
- - **Luffy** never implements code.
42
- - **Chopper** never fixes findings — reports them.
43
- - **Skeptic** never validates — doubts.
44
- - **Robin/Jinbe** never implement — findings to Brook.
45
- - Crew members never dispatch each other. Workers are subagents, never crew.
46
-
47
- ## The crew ships whole
48
-
49
- Every install gets all 14 agents and all 26 skills. No project-type selection —
50
- the harness routes each task to the right specialist.
51
-
52
- See [skills.md](skills.md) for the 26 techniques, or
53
- [workflow.md](workflow.md) for the pipeline.
@@ -1,65 +0,0 @@
1
- # Audit Trail
2
-
3
- Every artifact the Mugiwara crew produces — what writes it, when, and how to
4
- read it as a reviewer.
5
-
6
- ## The artifacts
7
-
8
- | Artifact | Path | Written by | When | For |
9
- |----------|------|-----------|------|-----|
10
- | **Plan doc** | `.mugiwara/plans/YYYY-MM-DD-<mission>.md` | Nami (planning) | Wave 2 | Zero-context executor — waves, tasks, criteria, risk |
11
- | **Spec** | `.mugiwara/spec/YYYY-MM-DD-<mission>.md` | Usopp (brainstorm) / Luffy (spec bridge) | Wave 0–1 | Bridge from idea to plan — goal, acceptance, constraints |
12
- | **State** | `.mugiwara/state.json` | `scripts/savepoint.sh` | Every wave boundary | Computed mission state: lane, wave, files, blockers, token budget, evidence paths |
13
- | **Decision log** | `.mugiwara/logs/YYYY-MM-DD-<mission>.md` | Luffy (orchestrator) | Every wave | Route reason, check-in verdicts, mode flips, decisions |
14
- | **Blocker ledger** | `.mugiwara/issues/YYYY-MM-DD-<mission>-blockers.md` | Any agent | On blocker hit | Row per blocker: wave, task, symptom, attempted, help-needed |
15
- | **Todo list** | `.mugiwara/results/<mission>-todos.md` | Zoro (execution) | Wave 3 | Checkbox per task, checked with evidence pointer |
16
- | **Audit report** | `.mugiwara/results/YYYY-MM-DD-<mission>-audit.md` | Chopper (checkpoint) | Wave 4 | Per-task evidence, commit hygiene, parallel-conflict, honest classification |
17
- | **Quality report** | `.mugiwara/results/YYYY-MM-DD-<mission>-quality.md` | Sanji (quality) | Wave 5 | Formatter/linter/unit/user-test results |
18
- | **Gate verdict** | `.mugiwara/results/YYYY-MM-DD-<mission>-gates.md` | Franky (gates) | Wave 6 | Coverage thresholds from config, build exit, DoD verdict |
19
- | **Review findings** | `.mugiwara/review/YYYY-MM-DD-<mission>-review.md` | Robin (review) | Wave 7 | Severity-tagged: path:line → problem → fix |
20
- | **Security report** | `.mugiwara/review/YYYY-MM-DD-<mission>-security.md` | Jinbe (security) | Wave 7 | STRIDE, OWASP mapping, checklist, CVSS severity |
21
- | **Heal report** | `.mugiwara/results/YYYY-MM-DD-<mission>-heal.md` | Brook (healing) | Wave 8 | Fixed list, escalated list, updated ledger |
22
- | **Closure report** | `.mugiwara/results/YYYY-MM-DD-<mission>-closure.md` | Luffy (orchestrator) | Wave 9 | Mission summary, per-wave outcomes, deferred items, lessons |
23
- | **Mission report** | `.mugiwara/reports/YYYY-MM-DD-<mission>.md` | `scripts/mission-report.sh` | Wave 9 | Human-readable summary: what changed, gates, state, token cost |
24
- | **PR verdict** | `.mugiwara/results/YYYY-MM-DD-<mission>-pr-verdict.md` | Luffy (orchestrator) | Wave 9 | Ready PR summary block for the user to open the PR |
25
- | **Trace** | `.mugiwara/results/<mission>-trace.md` | Resume coordinator | Every dispatch | Dispatch → outcome — drives resume (legacy; state.json preferred) |
26
- | **Lessons ledger** | `.mugiwara/logs/lessons.md` | Memory Keeper | Cross-mission | One row per real lesson, append-only, all actors share |
27
- | **Evidence logs** | `.mugiwara/results/<label>-<hash>.log` | `scripts/evidence.sh` | On demand | Command stdout/stderr capture with timestamp and exit code |
28
-
29
- ## How to read as a reviewer
30
-
31
- 1. **Start with the mission report** (`reports/`) — one file, what changed, gates, token cost.
32
- 2. **Check the gate verdict** (`results/<mission>-gates.md`) — coverage from config, build, DoD. Any FAIL needs explanation.
33
- 3. **Spot-check the audit report** (`results/<mission>-audit.md`) — did Chopper re-run checks or accept claims? Every criterion gets a command run + evidence row.
34
- 4. **Review findings count** (`review/`) — how many blocker/major/minor? Were they healed? Check the heal report for closure.
35
- 5. **State.json** for raw numbers — lane, wave, files, blockers open, heal cycle, token budget status (ok/warn/stop).
36
-
37
- ## What stays after cleanup
38
-
39
- After Wave 9 closure (run via `mugiwara-ship` cleanup procedure):
40
-
41
- **Kept** (audit trail + PR material):
42
- - `config` — runtime config
43
- - `plans/<mission>.md` — clean plan doc
44
- - `results/<mission>-closure.md` — closure report
45
- - `results/<mission>-pr-verdict.md` — PR material
46
- - `reports/<mission>.md` — mission report
47
- - `state.json` — final mission state (unless another actor's mission is active)
48
- - `logs/lessons.md` — cross-mission lessons
49
- - `backup/`, `manifest.json` — harness config
50
-
51
- **Deleted** (consumed/superseded):
52
- - `spec/<mission>.md` — consumed by planning
53
- - `results/<mission>-todos.md` — consumed
54
- - `results/<mission>-audit.md` — superseded by closure
55
- - `results/<mission>-quality.md` — consumed
56
- - `results/<mission>-gates.md` — consumed
57
- - `results/<mission>-heal.md` — consumed
58
- - `results/<mission>-trace.md` — consumed (state.json is canonical)
59
- - `review/<mission>-review.md` — consumed
60
- - `review/<mission>-security.md` — consumed
61
- - `issues/<mission>-blockers.md` — consumed
62
- - `logs/<mission>.md` — decision log, per-mission
63
-
64
- Cleanup lists candidates first (dry-run), then deletes. A mission is only
65
- closed after cleanup runs.
@@ -1,58 +0,0 @@
1
- # Positioning
2
-
3
- What mugiwara is and when to use something else.
4
-
5
- ## What mugiwara is
6
-
7
- **Mugiwara is the governance layer for AI-assisted engineering work.** Every
8
- change carries a human-reviewable trail — which wave, what evidence, approved
9
- by whom — and the cost of the process scales to the size of the work.
10
-
11
- - **Lane sizing** — process scales from zero waves (typo) to nine (auth
12
- migration), computed from the diff by `scripts/lane.sh`, not guessed.
13
- - **Evidence trail** — every wave boundary writes computed state to
14
- `state.json`. No wave passes on a spoken claim. `scripts/evidence.sh` wraps
15
- check commands and captures output.
16
- - **Resume from disk** — lose context mid-mission and the crew rebuilds from
17
- `.mugiwara/` instead of restarting.
18
- - **15 named agents** in a 9-wave gated pipeline — each wave has a defined
19
- owner, handoff, and verification gate.
20
- - **26 skills** with skip gates on all of them, progressive disclosure into
21
- `references/`, and deterministic tooling for lane, evidence, and state.
22
-
23
- ## What mugiwara refuses
24
-
25
- - **A runtime or daemon.** Orchestration stays in the harness. Pure markdown.
26
- - **Auto-merge or auto-deploy.** Human review at the PR is the terminal gate.
27
- - **Unattended marathon mode.** Mugiwara runs inline by default — you watch
28
- every wave. Subagents only for parallel work.
29
- - **Skill-count growth.** 26 is the ceiling; a new skill replaces an old one.
30
- - **Head-to-head feature scorecards.** The compliance matrix replaces them.
31
-
32
- ## When to use something else
33
-
34
- - **Deep autonomous marathon runs.** If you want an agent to disappear for
35
- hours on subagent-driven builds with minimal visibility, superpowers'
36
- `subagent-driven-development` is built for exactly that.
37
- - **Reference-encyclopedia depth.** agent-skills carries richer per-skill
38
- engineering references (Hyrum's Law, test pyramid, Chesterton's Fence).
39
- - **A runtime service.** If you need API-driven, deployable agent crews, use a
40
- framework (LangGraph, CrewAI).
41
- - **A single mega-prompt.** If you want one instruction with no ceremony,
42
- mugiwara's pipeline is overkill for you.
43
-
44
- ## Reproducible facts
45
-
46
- Generated from `content/` and `scripts/validate-content.ts`:
47
-
48
- | Metric | Value |
49
- |--------|:-----:|
50
- | Skills | 26 |
51
- | Agents | 15 |
52
- | Index (all descriptions) | ~10k chars (~2.5k tokens) |
53
- | Avg skill size | ~1.2k tokens |
54
- | Skills with skip gate | 26/26 |
55
- | References/ files | growing |
56
- | Evals | 11 cases |
57
-
58
- *Figures from Aug 2026. Recalculate with `bun scripts/validate-content.ts --check-manifest`.*