@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
@@ -1,74 +1,275 @@
1
1
  #!/usr/bin/env bash
2
- # scripts/mission-report.sh — generate human-readable mission report from state.json
3
- # Usage: mission-report.sh <mission>
2
+ # scripts/mission-report.sh — generate aggregate mission report from state.json
3
+ # + per-mission wave artifacts. Usage: mission-report.sh <mission>
4
+ # Output: .mugiwara/reports/YYYY-MM-DD-<mission>.md — one-file summary of all waves.
5
+ # Enriched: mission header, token budget (WARN/STOP flag), tasks (state or plan
6
+ # doc fallback), gate/quality excerpt from 06-closure.md, evidence file list.
7
+ # Every section degrades to n/a when state.json or results/ artifacts are absent.
4
8
  set -u
5
9
 
6
10
  MISSION="${1:-}"
7
11
  [ -z "$MISSION" ] && { echo "usage: mission-report.sh <mission>" >&2; exit 1; }
12
+ case "$MISSION" in
13
+ *[!a-zA-Z0-9._-]*) echo "mission-report: invalid mission name \"$MISSION\" (allowlist: [a-zA-Z0-9._-])" >&2; exit 1 ;;
14
+ esac
8
15
 
9
16
  MUGIWARA_DIR="${MUGIWARA_DIR:-.mugiwara}"
10
17
  STATE_FILE="$MUGIWARA_DIR/state.json"
11
18
  REPORT_DIR="$MUGIWARA_DIR/reports"
12
- REPORT_FILE="$REPORT_DIR/${MISSION}.md"
19
+ RESULTS_DIR="$MUGIWARA_DIR/results/$MISSION"
20
+ REVIEW_DIR="$MUGIWARA_DIR/review"
21
+ ISSUES_DIR="$MUGIWARA_DIR/issues"
13
22
 
14
- [ -f "$STATE_FILE" ] || { echo "mission-report: $STATE_FILE not found" >&2; exit 1; }
15
-
16
- export STATE_FILE REPORT_DIR MISSION
23
+ export STATE_FILE REPORT_DIR RESULTS_DIR REVIEW_DIR ISSUES_DIR MISSION
17
24
  node << 'NODE'
18
25
  const fs = require('fs');
19
26
  const path = require('path');
20
27
 
21
28
  const stateFile = process.env.STATE_FILE || ".mugiwara/state.json";
22
29
  const reportDir = process.env.REPORT_DIR || ".mugiwara/reports";
30
+ const resultsDir = process.env.RESULTS_DIR || ".mugiwara/results/unknown";
31
+ const reviewDir = process.env.REVIEW_DIR || ".mugiwara/review";
32
+ const issuesDir = process.env.ISSUES_DIR || ".mugiwara/issues";
33
+ const mugiDir = path.dirname(stateFile);
23
34
  const mission = process.env.MISSION || "unknown";
24
35
 
25
- if (!fs.existsSync(stateFile)) {
26
- console.error("mission-report: " + stateFile + " not found");
27
- process.exit(1);
36
+ fs.mkdirSync(reportDir, { recursive: true });
37
+
38
+ // state.json is optional — degrade gracefully, never hard-exit
39
+ let s = null;
40
+ if (fs.existsSync(stateFile)) {
41
+ try {
42
+ s = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
43
+ } catch (e) {
44
+ console.error("mission-report: warning: " + stateFile + " unreadable — degraded report");
45
+ }
46
+ } else {
47
+ console.error("mission-report: warning: " + stateFile + " not found — degraded report");
28
48
  }
29
49
 
30
- fs.mkdirSync(reportDir, { recursive: true });
31
- const s = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
50
+ const val = (obj, key, dflt) => {
51
+ if (!s || !obj) return dflt;
52
+ const v = obj[key];
53
+ return (v === undefined || v === null || v === "") ? dflt : v;
54
+ };
32
55
 
33
- const reportFile = path.join(reportDir, mission + ".md");
34
56
  const now = new Date().toISOString().slice(0, 10);
35
- const lane = s.lane || "unknown";
36
- const mode = s.mode || "guided";
37
- const actor = s.actor || "unknown";
38
- const branch = s.branch || "unknown";
39
- const wave = s.wave || 0;
40
- const filesTouched = s.files_touched || 0;
41
- const locDelta = s.loc_delta || 0;
42
- const sensitive = s.sensitive_paths || [];
43
- const tasks = s.tasks || {};
44
- const blockers = s.blockers_open || 0;
45
- const healCycle = s.heal_cycle || 1;
46
- const tokens = s.tokens_est || 0;
47
- const budget = s.budget || 0;
48
- const evidence = s.evidence || [];
49
- const updated = s.updated_at || new Date().toISOString();
57
+ const reportFile = path.join(reportDir, now + "-" + mission + ".md");
58
+ const lane = val(s, 'lane', 'n/a');
59
+ const laneReason = val(s, 'lane_reason', 'n/a');
60
+ const mode = val(s, 'mode', 'n/a');
61
+ const actor = val(s, 'actor', 'n/a');
62
+ const branch = val(s, 'branch', 'n/a');
63
+ const wave = val(s, 'wave', 'n/a');
64
+ const filesTouched = val(s, 'files_touched', 0);
65
+ const locDelta = val(s, 'loc_delta', 0);
66
+ const sensitive = s ? (s.sensitive_paths || []) : [];
67
+ const tasks = s ? (s.tasks || {}) : {};
68
+ const blockers = val(s, 'blockers_open', 0);
69
+ const healCycle = val(s, 'heal_cycle', 1);
70
+ const tokens = Number(val(s, 'tokens_est', 0)) || 0;
71
+ const tokensSource = val(s, 'tokens_source', 'n/a');
72
+ const budget = Number(val(s, 'budget', 0)) || 0;
73
+ const budgetStatus = val(s, 'budget_status', 'n/a');
74
+ const evidence = s ? (s.evidence || []) : [];
75
+ const updated = val(s, 'updated_at', now);
76
+
77
+ // --- wave artifact scan: results/<mission>/NN-*.md with verdict sniffing ---
78
+ const WAVE_LABEL = {
79
+ "01-execution": "Execute (Wave 3)",
80
+ "02-audit": "Checkpoint (Wave 4)",
81
+ "03-quality": "Quality (Wave 5)",
82
+ "04-gates": "Gates (Wave 6)",
83
+ "05-healing": "Healing (Wave 8)",
84
+ "06-closure": "Closure (Wave 9)",
85
+ "07-pr-verdict": "PR material",
86
+ };
87
+
88
+ const WAVE_FILES = new Set(Object.keys(WAVE_LABEL));
89
+
90
+ function sniffVerdict(file) {
91
+ const text = fs.readFileSync(file, 'utf8');
92
+ const lines = text.split(/\r?\n/).filter(l => /pass|fail|✅|❌|verdict|go|no-go/i.test(l));
93
+ for (const l of lines) {
94
+ const m = l.match(/(NO-GO|GO|PASS|FAIL|✅|❌)/i);
95
+ if (m) return m[1].toUpperCase();
96
+ }
97
+ return "?";
98
+ }
99
+
100
+ const waveRows = [];
101
+ if (fs.existsSync(resultsDir)) {
102
+ const files = fs.readdirSync(resultsDir).filter(f => f.endsWith('.md')).sort();
103
+ for (const f of files) {
104
+ const base = f.replace(/\.md$/, '');
105
+ if (!WAVE_FILES.has(base)) continue; // support files (todos.md, resume.md, eval.md, evidence logs) not in waves table
106
+ const label = WAVE_LABEL[base];
107
+ const verdict = sniffVerdict(path.join(resultsDir, f));
108
+ waveRows.push({ label, file: f, verdict });
109
+ }
110
+ }
111
+
112
+ // --- review + issues scan: aggregate findings count + open blockers ---
113
+ let reviewFiles = [];
114
+ let reviewFindings = 0;
115
+ if (fs.existsSync(reviewDir)) {
116
+ reviewFiles = fs.readdirSync(reviewDir).filter(f => (f.startsWith(mission + "-") || f.startsWith(mission + "/")) && f.endsWith('.md'));
117
+ for (const f of reviewFiles) {
118
+ const text = fs.readFileSync(path.join(reviewDir, f), 'utf8');
119
+ reviewFindings += (text.match(/^\s*(?:#+\s*)?(?:🔴|🟠|🟡|⚪)(?:\s*\/\s*(?:🔴|🟠|🟡|⚪))?\s+.*/gm) || []).length;
120
+ }
121
+ }
122
+
123
+ let issueRows = [];
124
+ if (fs.existsSync(issuesDir)) {
125
+ const issueFiles = fs.readdirSync(issuesDir).filter(f => (f.startsWith(mission + "-") || f.startsWith(mission + "/")) && f.endsWith('.md'));
126
+ for (const f of issueFiles) {
127
+ const text = fs.readFileSync(path.join(issuesDir, f), 'utf8');
128
+ for (const line of text.split(/\r?\n/)) {
129
+ if (line.trim().startsWith('|') && line.includes('|') && !line.includes('---')) {
130
+ const cells = line.split('|').map(c => c.trim()).filter(Boolean);
131
+ if (cells.length >= 5 && !/^wave$/i.test(cells[0])) issueRows.push(cells.join(" | "));
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ // --- tasks: state.json counts, plan-doc fallback (savepoint-style counting) ---
138
+ function findPlan(mDir, m) {
139
+ const plansDir = path.join(mDir, 'plans');
140
+ if (!fs.existsSync(plansDir)) return null;
141
+ const direct = path.join(plansDir, m + '.md');
142
+ if (fs.existsSync(direct)) return direct;
143
+ const matches = fs.readdirSync(plansDir).filter(f => f.endsWith('-' + m + '.md'));
144
+ return matches.length ? path.join(plansDir, matches.sort()[0]) : null;
145
+ }
146
+
147
+ let tasksDone = tasks.done || 0;
148
+ let tasksTotal = tasks.total || 0;
149
+ let tasksSource = "state.json";
150
+ if (tasksTotal === 0) {
151
+ const plan = findPlan(mugiDir, mission);
152
+ if (plan) {
153
+ const lines = fs.readFileSync(plan, 'utf8').split(/\r?\n/);
154
+ const done = lines.filter(l => l.includes('[x]')).length;
155
+ const total = done + lines.filter(l => l.includes('[ ]')).length;
156
+ if (total > 0) {
157
+ tasksDone = done;
158
+ tasksTotal = total;
159
+ tasksSource = "plan doc";
160
+ }
161
+ }
162
+ }
163
+ if (tasksDone === 0 && tasksTotal === 0) { tasksDone = 'n/a'; tasksTotal = 'n/a'; }
164
+
165
+ // --- token budget flag: WARN/STOP line when status is not ok ---
166
+ let budgetFlag = "";
167
+ if (budgetStatus === "warn" || budgetStatus === "stop") {
168
+ const warnAt = Math.floor(budget * 3 / 2);
169
+ const stopAt = budget * 3;
170
+ if (budgetStatus === "stop") {
171
+ budgetFlag = "🛑 STOP: tokens " + tokens.toLocaleString() + " ≥ 3× budget " + budget.toLocaleString() + " (stop at " + stopAt.toLocaleString() + ") — halt; escalate to Luffy";
172
+ } else {
173
+ budgetFlag = "⚠ WARN: tokens " + tokens.toLocaleString() + " ≥ 1.5× budget " + budget.toLocaleString() + " (warn at " + warnAt.toLocaleString() + ") — checkpoint before continuing";
174
+ }
175
+ }
176
+
177
+ // --- gate/quality excerpt: headings + body from 06-closure.md, as-is ---
178
+ function sectionExcerpt(file, heading) {
179
+ if (!fs.existsSync(file)) return null;
180
+ const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
181
+ let collecting = false;
182
+ let content = 0;
183
+ const out = [];
184
+ for (const l of lines) {
185
+ if (/^##\s/.test(l)) {
186
+ if (collecting) break;
187
+ if (l.trim() === heading) { collecting = true; out.push(l); continue; }
188
+ } else if (collecting) {
189
+ if (l.trim()) {
190
+ out.push(l);
191
+ content++;
192
+ if (content >= 20) break; // cap excerpt
193
+ }
194
+ }
195
+ }
196
+ return collecting ? out : null;
197
+ }
50
198
 
199
+ const closureFile = path.join(resultsDir, "06-closure.md");
200
+ const gateSec = sectionExcerpt(closureFile, "## Gate verdicts");
201
+ const testsSec = sectionExcerpt(closureFile, "## Tests");
202
+
203
+ // --- evidence file list: all .md artifacts in results/<mission>/ ---
204
+ let evidenceFiles = [];
205
+ if (fs.existsSync(resultsDir)) {
206
+ evidenceFiles = fs.readdirSync(resultsDir).filter(f => f.endsWith('.md')).sort();
207
+ }
208
+
209
+ // --- assemble report ---
51
210
  let report = "# Mission: " + mission + " . " + now + "\n\n";
52
- report += "**Lane** " + lane + " . **Mode** " + mode + " . **Actor** " + actor + " . **Branch** " + branch + "\n\n";
211
+
212
+ report += "## Mission header\n\n| Field | Value |\n|-------|-------|\n";
213
+ report += "| Mission | " + mission + " |\n";
214
+ report += "| Branch | " + branch + " |\n";
215
+ report += "| Lane | " + lane + " |\n";
216
+ report += "| Lane reason | " + laneReason + " |\n";
217
+ report += "| Mode | " + mode + " |\n";
218
+ report += "| Wave | " + wave + " |\n";
219
+ report += "| Actor | " + actor + " |\n\n";
220
+
221
+ report += "## Token budget\n\n| Field | Value |\n|-------|-------|\n";
222
+ report += "| Tokens (est) | " + tokens.toLocaleString() + " |\n";
223
+ report += "| Tokens source | " + tokensSource + " |\n";
224
+ report += "| Budget | " + budget.toLocaleString() + " |\n";
225
+ report += "| Budget status | " + budgetStatus + " |\n";
226
+ if (budgetFlag) report += "\n" + budgetFlag;
227
+ report += "\n\n";
228
+
229
+ report += "## Tasks\n\n| Field | Value |\n|-------|-------|\n";
230
+ report += "| Done | " + tasksDone + " |\n";
231
+ report += "| Total | " + tasksTotal + " |\n";
232
+ report += "| Source | " + tasksSource + " |\n\n";
233
+
53
234
  report += "## What changed\n\n";
54
235
  report += filesTouched + " files, +" + locDelta + " LOC";
236
+ if (sensitive.length) report += "\nSensitive paths: " + sensitive.join(", ");
237
+ report += "\n\n";
55
238
 
56
- if (sensitive.length) {
57
- report += "\nSensitive paths: " + sensitive.join(", ");
239
+ report += "## Waves\n\n| Wave | Artifact | Verdict |\n|------|----------|---------|";
240
+ if (waveRows.length) {
241
+ for (const r of waveRows) report += "\n| " + r.label + " | `" + r.file + "` | " + r.verdict + " |";
242
+ } else {
243
+ report += "\n| (no wave artifacts found in `" + resultsDir + "`) | | |";
58
244
  }
245
+ report += "\n\n";
59
246
 
60
- report += "\n\n## Gates\n\n| Gate | Verdict | Evidence |\n|------|---------|----------|";
61
- for (const e of evidence) {
62
- report += "\n| \u2014 | PASS | " + e + " |";
63
- }
247
+ report += "## Gate & quality\n\n";
248
+ if (gateSec) report += gateSec.join("\n") + "\n";
249
+ if (testsSec) report += (gateSec ? "\n" : "") + testsSec.join("\n") + "\n";
250
+ if (!gateSec && !testsSec) report += "n/a\n";
251
+ report += "\n";
252
+
253
+ report += "## Evidence files\n\n";
254
+ report += evidenceFiles.length ? evidenceFiles.map(f => "- " + f).join("\n") : "n/a";
255
+ report += "\n\n";
64
256
 
65
- report += "\n\n## State\n\n| Field | Value |\n|-------|-------|\n";
257
+ report += "## Review & blockers\n\n";
258
+ report += "Review + security files: " + (reviewFiles.length ? reviewFiles.join(", ") : "none") + "\n";
259
+ report += "Findings: " + reviewFindings + "\n";
260
+ report += "Blocker ledger rows: " + issueRows.length + (issueRows.length ? "\n" + issueRows.map(r => "- " + r).join("\n") : "");
261
+ report += "\n\n";
262
+
263
+ report += "## State\n\n| Field | Value |\n|-------|-------|\n";
66
264
  report += "| Wave | " + wave + " |\n";
67
265
  report += "| Tasks | " + (tasks.done || 0) + "/" + (tasks.total || 0) + " done |\n";
68
266
  report += "| Blockers open | " + blockers + " |\n";
69
267
  report += "| Heal cycles | " + healCycle + " |\n";
70
268
  report += "| Tokens used | " + tokens.toLocaleString() + " / " + budget.toLocaleString() + " |\n\n";
71
- report += "## Updated\n\n" + updated + "\n";
269
+
270
+ report += "## Evidence\n\n";
271
+ report += evidence.length ? evidence.map(e => "- " + e).join("\n") : "n/a";
272
+ report += "\n\n## Updated\n\n" + updated + "\n";
72
273
 
73
274
  fs.writeFileSync(reportFile, report);
74
275
  console.log("\u2713 mission report: " + reportFile);
@@ -0,0 +1,292 @@
1
+ #!/usr/bin/env bun
2
+ // scripts/onboard.ts — Mugiwara onboarding wizard (10 fixed questions, no network)
3
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { createInterface } from "node:readline";
6
+
7
+ const root = join(import.meta.dirname, "..");
8
+ const mugiwaraDir = join(root, ".mugiwara");
9
+ const configPath = join(mugiwaraDir, "config");
10
+ const onboardPath = join(mugiwaraDir, "onboard.json");
11
+
12
+ function ask(rl: import("node:readline").Interface, prompt: string): Promise<string> {
13
+ return new Promise((resolve) => {
14
+ rl.question(prompt, (answer) => resolve(answer.trim()));
15
+ });
16
+ }
17
+
18
+ async function confirm(rl: import("node:readline").Interface, prompt: string): Promise<boolean> {
19
+ const a = await ask(rl, prompt);
20
+ return a.toLowerCase() === "y" || a.toLowerCase() === "yes";
21
+ }
22
+
23
+ async function pick(
24
+ rl: import("node:readline").Interface,
25
+ prompt: string,
26
+ options: string[],
27
+ ): Promise<number> {
28
+ while (true) {
29
+ const a = await ask(rl, prompt);
30
+ const n = Number.parseInt(a, 10);
31
+ if (!Number.isNaN(n) && n >= 1 && n <= options.length) return n;
32
+ console.log(` Enter a number 1–${options.length}`);
33
+ }
34
+ }
35
+
36
+ async function main() {
37
+ // --help / no-tty guard
38
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
39
+ console.log("Usage: bun scripts/onboard.ts");
40
+ console.log("Runs the Mugiwara onboarding wizard (10 fixed questions).");
41
+ console.log("Writes .mugiwara/config and .mugiwara/onboard.json.");
42
+ process.exit(0);
43
+ }
44
+
45
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
46
+
47
+ try {
48
+ console.log("");
49
+ console.log("╔══════════════════════════════════════╗");
50
+ console.log("║ Mugiwara Onboarding Wizard ║");
51
+ console.log("╚══════════════════════════════════════╝");
52
+ console.log("");
53
+
54
+ // Check for existing config
55
+ if (existsSync(configPath)) {
56
+ const redo = await confirm(
57
+ rl,
58
+ "Existing .mugiwara/config found. Re-onboard? (y/n): ",
59
+ );
60
+ if (!redo) {
61
+ console.log("Onboarding cancelled. Config preserved.");
62
+ process.exit(0);
63
+ }
64
+ console.log("");
65
+ }
66
+
67
+ const answers: Record<string, unknown> = {
68
+ started_at: new Date().toISOString(),
69
+ };
70
+
71
+ // ---- Phase 1: Project Context ----
72
+ console.log("── Phase 1: Project Context ──");
73
+ console.log("");
74
+
75
+ const q1 = await pick(rl, "Q1 — Project type:\n [1] Web application\n [2] Mobile application\n [3] CLI tool\n [4] Library/SDK\n [5] Backend service / API\n [6] Other\n > ", [
76
+ "Web application",
77
+ "Mobile application",
78
+ "CLI tool",
79
+ "Library/SDK",
80
+ "Backend service / API",
81
+ "Other",
82
+ ]);
83
+ answers.project_type = q1;
84
+ console.log("");
85
+
86
+ const q2 = await pick(rl, "Q2 — Primary language:\n [1] TypeScript\n [2] JavaScript\n [3] Python\n [4] Go\n [5] Rust\n [6] Java\n [7] Other\n > ", [
87
+ "TypeScript",
88
+ "JavaScript",
89
+ "Python",
90
+ "Go",
91
+ "Rust",
92
+ "Java",
93
+ "Other",
94
+ ]);
95
+ answers.primary_language = q2;
96
+ console.log("");
97
+
98
+ const q3 = await pick(rl, "Q3 — Team size:\n [1] Solo\n [2] 2–5\n [3] 6–15\n [4] 16+\n > ", [
99
+ "Solo",
100
+ "2–5",
101
+ "6–15",
102
+ "16+",
103
+ ]);
104
+ answers.team_size = q3;
105
+ console.log("");
106
+
107
+ const q4 = await pick(rl, "Q4 — Git workflow:\n [1] Trunk-based (feature/{type}-{issue}-{slug})\n [2] GitFlow (feature/{slug})\n [3] GitHub Flow (feat/{slug})\n [4] Other (feature/{slug})\n > ", [
108
+ "trunk-based",
109
+ "gitflow",
110
+ "github-flow",
111
+ "other",
112
+ ]);
113
+ answers.git_workflow = q4;
114
+ console.log("");
115
+
116
+ const q5 = await pick(rl, "Q5 — CI/CD platform:\n [1] GitHub Actions\n [2] GitLab CI\n [3] CircleCI\n [4] Jenkins\n [5] None / manual\n [6] Other\n > ", [
117
+ "GitHub Actions",
118
+ "GitLab CI",
119
+ "CircleCI",
120
+ "Jenkins",
121
+ "None / manual",
122
+ "Other",
123
+ ]);
124
+ answers.ci_cd = q5;
125
+ console.log("");
126
+
127
+ // ---- Phase 2: Mugiwara Preferences ----
128
+ console.log("── Phase 2: Mugiwara Preferences ──");
129
+ console.log("");
130
+
131
+ const q6 = await pick(rl, "Q6 — Autonomy mode:\n [1] guided — ask before every wave transition\n [2] semi — auto-advance through waves, pause on failures\n [3] auto — full auto-pilot\n > ", [
132
+ "guided",
133
+ "semi",
134
+ "auto",
135
+ ]);
136
+ answers.autonomy_mode = q6;
137
+ console.log("");
138
+
139
+ const q7 = await ask(rl, "Q7 — Agents to enable (comma-separated or 'all'):\n Available: brainstorm, plan, execute, checkpoint, quality, gates, review, security, healing\n Default: all\n > ");
140
+ answers.enabled_agents = q7 || "all";
141
+ console.log("");
142
+
143
+ const q8a = await pick(rl, "Q8a — Code review depth:\n [1] full — breaking-change map, five-axis review, ≤3 cycles\n [2] standard — five-axis review, 1 cycle\n [3] quick — diff-only, no caller-map\n > ", [
144
+ "full",
145
+ "standard",
146
+ "quick",
147
+ ]);
148
+ answers.review_depth = q8a;
149
+ console.log("");
150
+
151
+ const q8b = await pick(rl, "Q8b — Quality check depth:\n [1] full — format, lint, typecheck, test, build\n [2] standard — lint, typecheck, test\n [3] quick — test only\n > ", [
152
+ "full",
153
+ "standard",
154
+ "quick",
155
+ ]);
156
+ answers.quality_depth = q8b;
157
+ console.log("");
158
+
159
+ console.log("Q9 — Test coverage threshold:");
160
+ const q9 = await pick(rl, " [1] 90/80 — new code 90%, modified 80%\n [2] 80/70 — new code 80%, modified 70%\n [3] custom — enter your own values\n [4] none — 0/0, no coverage enforcement\n > ", [
161
+ "90/80",
162
+ "80/70",
163
+ "custom",
164
+ "none",
165
+ ]);
166
+ let coverageNew = 90;
167
+ let coverageModified = 80;
168
+ if (q9 === 1) {
169
+ coverageNew = 90;
170
+ coverageModified = 80;
171
+ } else if (q9 === 2) {
172
+ coverageNew = 80;
173
+ coverageModified = 70;
174
+ } else if (q9 === 3) {
175
+ const cNew = await ask(rl, " Enter coverage threshold for new code (%): ");
176
+ const cMod = await ask(rl, " Enter coverage threshold for modified code (%): ");
177
+ coverageNew = Number.parseInt(cNew, 10) || 0;
178
+ coverageModified = Number.parseInt(cMod, 10) || 0;
179
+ } else {
180
+ coverageNew = 0;
181
+ coverageModified = 0;
182
+ }
183
+ answers.coverage_threshold = q9;
184
+ answers.coverage_new = coverageNew;
185
+ answers.coverage_modified = coverageModified;
186
+ console.log("");
187
+
188
+ const q10 = await pick(rl, "Q10 — Commit style:\n [1] Conventional Commits (feat:, fix:, chore:, docs:)\n [2] Semantic (type(scope): message)\n [3] Free-form\n > ", [
189
+ "conventional",
190
+ "semantic",
191
+ "free-form",
192
+ ]);
193
+ answers.commit_style = q10;
194
+ answers.completed_at = new Date().toISOString();
195
+ console.log("");
196
+
197
+ // ---- Branch name format ----
198
+ const branchFormats: Record<number, string> = {
199
+ 1: "feature/{type}-{issue}-{slug}",
200
+ 2: "feature/{slug}",
201
+ 3: "feat/{slug}",
202
+ 4: "feature/{slug}",
203
+ };
204
+ const branch = branchFormats[q4] || "feature/{slug}";
205
+
206
+ // ---- Autonomy mode ----
207
+ const modes: Record<number, string> = { 1: "guided", 2: "semi", 3: "auto" };
208
+ const mode = modes[q6] || "semi";
209
+
210
+ // ---- Commit style ----
211
+ const commitStyles: Record<number, string> = { 1: "conventional", 2: "semantic", 3: "free-form" };
212
+ const commit = commitStyles[q10] || "conventional";
213
+
214
+ // ---- Review depth ----
215
+ const reviewDepths: Record<number, string> = { 1: "full", 2: "standard", 3: "quick" };
216
+ const reviewDepth = reviewDepths[q8a] || "standard";
217
+
218
+ // ---- Quality depth ----
219
+ const qualityDepths: Record<number, string> = { 1: "full", 2: "standard", 3: "quick" };
220
+ const qualityDepth = qualityDepths[q8b] || "standard";
221
+
222
+ // ---- Write config ----
223
+ if (!existsSync(mugiwaraDir)) mkdirSync(mugiwaraDir, { recursive: true });
224
+
225
+ const config = {
226
+ mode,
227
+ branch,
228
+ coverage_new: coverageNew,
229
+ coverage_modified: coverageModified,
230
+ commit,
231
+ review_depth: reviewDepth,
232
+ quality_depth: qualityDepth,
233
+ enabled_agents: answers.enabled_agents,
234
+ };
235
+
236
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
237
+ writeFileSync(onboardPath, JSON.stringify(answers, null, 2) + "\n");
238
+
239
+ // ---- Summary ----
240
+ const qLabels: Record<string, Record<number, string>> = {
241
+ project_type: {
242
+ 1: "Web application",
243
+ 2: "Mobile application",
244
+ 3: "CLI tool",
245
+ 4: "Library/SDK",
246
+ 5: "Backend service / API",
247
+ 6: "Other",
248
+ },
249
+ primary_language: {
250
+ 1: "TypeScript",
251
+ 2: "JavaScript",
252
+ 3: "Python",
253
+ 4: "Go",
254
+ 5: "Rust",
255
+ 6: "Java",
256
+ 7: "Other",
257
+ },
258
+ team_size: { 1: "Solo", 2: "2–5", 3: "6–15", 4: "16+" },
259
+ git_workflow: { 1: "Trunk-based", 2: "GitFlow", 3: "GitHub Flow", 4: "Other" },
260
+ ci_cd: { 1: "GitHub Actions", 2: "GitLab CI", 3: "CircleCI", 4: "Jenkins", 5: "None", 6: "Other" },
261
+ autonomy_mode: { 1: "guided", 2: "semi", 3: "auto" },
262
+ review_depth: { 1: "full", 2: "standard", 3: "quick" },
263
+ quality_depth: { 1: "full", 2: "standard", 3: "quick" },
264
+ coverage_threshold: { 1: "90/80", 2: "80/70", 3: "custom", 4: "none" },
265
+ commit_style: { 1: "Conventional", 2: "Semantic", 3: "Free-form" },
266
+ };
267
+
268
+ console.log("╔══════════════════════════════════════╗");
269
+ console.log("║ Onboarding Complete ║");
270
+ console.log("╚══════════════════════════════════════╝");
271
+ console.log("");
272
+ console.log(" Project type: ", qLabels.project_type[q1]);
273
+ console.log(" Primary language: ", qLabels.primary_language[q2]);
274
+ console.log(" Team size: ", qLabels.team_size[q3]);
275
+ console.log(" Git workflow: ", qLabels.git_workflow[q4]);
276
+ console.log(" CI/CD: ", qLabels.ci_cd[q5]);
277
+ console.log(" Autonomy mode: ", qLabels.autonomy_mode[q6]);
278
+ console.log(" Enabled agents: ", answers.enabled_agents);
279
+ console.log(" Review depth: ", qLabels.review_depth[q8a]);
280
+ console.log(" Quality depth: ", qLabels.quality_depth[q8b]);
281
+ console.log(" Coverage: ", q9 === 3 ? `${coverageNew}/${coverageModified}` : qLabels.coverage_threshold[q9]);
282
+ console.log(" Commit style: ", qLabels.commit_style[q10]);
283
+ console.log("");
284
+ console.log(` Config written: ${configPath}`);
285
+ console.log(` Audit trail: ${onboardPath}`);
286
+ console.log("");
287
+ } finally {
288
+ rl.close();
289
+ }
290
+ }
291
+
292
+ main();