@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
@@ -1,88 +1,153 @@
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.
4
5
  set -u
5
6
 
6
7
  MISSION="${1:-}"
7
8
  [ -z "$MISSION" ] && { echo "usage: mission-report.sh <mission>" >&2; exit 1; }
9
+ case "$MISSION" in
10
+ *[!a-zA-Z0-9._-]*) echo "mission-report: invalid mission name \"$MISSION\" (allowlist: [a-zA-Z0-9._-])" >&2; exit 1 ;;
11
+ esac
8
12
 
9
13
  MUGIWARA_DIR="${MUGIWARA_DIR:-.mugiwara}"
10
14
  STATE_FILE="$MUGIWARA_DIR/state.json"
11
15
  REPORT_DIR="$MUGIWARA_DIR/reports"
12
- REPORT_FILE="$REPORT_DIR/${MISSION}.md"
16
+ RESULTS_DIR="$MUGIWARA_DIR/results/$MISSION"
17
+ REVIEW_DIR="$MUGIWARA_DIR/review"
18
+ ISSUES_DIR="$MUGIWARA_DIR/issues"
13
19
 
14
20
  [ -f "$STATE_FILE" ] || { echo "mission-report: $STATE_FILE not found" >&2; exit 1; }
15
21
 
16
- python3 <<PY
17
- import json, os, sys
18
- from datetime import datetime
19
-
20
- state_file = "$STATE_FILE"
21
- report_file = "$REPORT_FILE"
22
- os.makedirs(os.path.dirname(report_file), exist_ok=True)
23
-
24
- with open(state_file) as f:
25
- s = json.load(f)
26
-
27
- mission = s.get("mission", "$MISSION")
28
- actor = s.get("actor", "unknown")
29
- branch = s.get("branch", "unknown")
30
- lane = s.get("lane", "unknown")
31
- lane_reason = s.get("lane_reason", "")
32
- mode = s.get("mode", "guided")
33
- wave = s.get("wave", 0)
34
- files_touched = s.get("files_touched", 0)
35
- loc_delta = s.get("loc_delta", 0)
36
- sensitive = s.get("sensitive_paths", [])
37
- tasks = s.get("tasks", {})
38
- blockers = s.get("blockers_open", 0)
39
- heal_cycle = s.get("heal_cycle", 1)
40
- tokens = s.get("tokens_est", 0)
41
- budget = s.get("budget", 0)
42
- evidence = s.get("evidence", [])
43
- updated = s.get("updated_at", datetime.utcnow().isoformat() + "Z")
44
-
45
- now = datetime.utcnow().strftime("%Y-%m-%d")
46
- report = f"""# Mission: {mission} · {now}
47
-
48
- **Lane** {lane} · **Mode** {mode} · **Actor** {actor} · **Branch** {branch}
49
-
50
- ## What changed
51
-
52
- {files_touched} files, +{loc_delta if loc_delta >= 0 else loc_delta} LOC"""
53
-
54
- if sensitive:
55
- report += "\nSensitive paths: " + ", ".join(sensitive)
56
-
57
- report += f"""
58
-
59
- ## Gates
60
-
61
- | Gate | Verdict | Evidence |
62
- |------|---------|----------|"""
63
-
64
- for e in evidence:
65
- report += f"\n| | PASS | {e} |"
66
-
67
- report += f"""
68
-
69
- ## State
70
-
71
- | Field | Value |
72
- |-------|-------|
73
- | Wave | {wave} |
74
- | Tasks | {tasks.get('done', 0)}/{tasks.get('total', 0)} done |
75
- | Blockers open | {blockers} |
76
- | Heal cycles | {heal_cycle} |
77
- | Tokens used | {tokens:,} / {budget:,} |
78
-
79
- ## Updated
80
-
81
- {updated}
82
- """
83
-
84
- with open(report_file, 'w') as f:
85
- f.write(report)
86
-
87
- print(f"✓ mission report: {report_file}")
88
- PY
22
+ export STATE_FILE REPORT_DIR RESULTS_DIR REVIEW_DIR ISSUES_DIR MISSION
23
+ node << 'NODE'
24
+ const fs = require('fs');
25
+ const path = require('path');
26
+
27
+ const stateFile = process.env.STATE_FILE || ".mugiwara/state.json";
28
+ const reportDir = process.env.REPORT_DIR || ".mugiwara/reports";
29
+ const resultsDir = process.env.RESULTS_DIR || ".mugiwara/results/unknown";
30
+ const reviewDir = process.env.REVIEW_DIR || ".mugiwara/review";
31
+ const issuesDir = process.env.ISSUES_DIR || ".mugiwara/issues";
32
+ const mission = process.env.MISSION || "unknown";
33
+
34
+ if (!fs.existsSync(stateFile)) {
35
+ console.error("mission-report: " + stateFile + " not found");
36
+ process.exit(1);
37
+ }
38
+
39
+ fs.mkdirSync(reportDir, { recursive: true });
40
+ const s = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
41
+
42
+ const now = new Date().toISOString().slice(0, 10);
43
+ const reportFile = path.join(reportDir, now + "-" + mission + ".md");
44
+ const lane = s.lane || "unknown";
45
+ const mode = s.mode || "guided";
46
+ const actor = s.actor || "unknown";
47
+ const branch = s.branch || "unknown";
48
+ const wave = s.wave || 0;
49
+ const filesTouched = s.files_touched || 0;
50
+ const locDelta = s.loc_delta || 0;
51
+ const sensitive = s.sensitive_paths || [];
52
+ const tasks = s.tasks || {};
53
+ const blockers = s.blockers_open || 0;
54
+ const healCycle = s.heal_cycle || 1;
55
+ const tokens = s.tokens_est || 0;
56
+ const budget = s.budget || 0;
57
+ const evidence = s.evidence || [];
58
+ const updated = s.updated_at || new Date().toISOString();
59
+
60
+ // --- wave artifact scan: results/<mission>/NN-*.md with verdict sniffing ---
61
+ const WAVE_LABEL = {
62
+ "01-execution": "Execute (Wave 3)",
63
+ "02-audit": "Checkpoint (Wave 4)",
64
+ "03-quality": "Quality (Wave 5)",
65
+ "04-gates": "Gates (Wave 6)",
66
+ "05-healing": "Healing (Wave 8)",
67
+ "06-closure": "Closure (Wave 9)",
68
+ "07-pr-verdict": "PR material",
69
+ };
70
+
71
+ const WAVE_FILES = new Set(Object.keys(WAVE_LABEL));
72
+
73
+ function sniffVerdict(file) {
74
+ const text = fs.readFileSync(file, 'utf8');
75
+ const lines = text.split(/\r?\n/).filter(l => /pass|fail|✅|❌|verdict|go|no-go/i.test(l));
76
+ for (const l of lines) {
77
+ const m = l.match(/(NO-GO|GO|PASS|FAIL|✅|❌)/i);
78
+ if (m) return m[1].toUpperCase();
79
+ }
80
+ return "?";
81
+ }
82
+
83
+ const waveRows = [];
84
+ if (fs.existsSync(resultsDir)) {
85
+ const files = fs.readdirSync(resultsDir).filter(f => f.endsWith('.md')).sort();
86
+ for (const f of files) {
87
+ const base = f.replace(/\.md$/, '');
88
+ if (!WAVE_FILES.has(base)) continue; // support files (todos.md, resume.md, eval.md, evidence logs) not in waves table
89
+ const label = WAVE_LABEL[base];
90
+ const verdict = sniffVerdict(path.join(resultsDir, f));
91
+ waveRows.push({ label, file: f, verdict });
92
+ }
93
+ }
94
+
95
+ // --- review + issues scan: aggregate findings count + open blockers ---
96
+ let reviewFiles = [];
97
+ let reviewFindings = 0;
98
+ if (fs.existsSync(reviewDir)) {
99
+ reviewFiles = fs.readdirSync(reviewDir).filter(f => (f.startsWith(mission + "-") || f.startsWith(mission + "/")) && f.endsWith('.md'));
100
+ for (const f of reviewFiles) {
101
+ const text = fs.readFileSync(path.join(reviewDir, f), 'utf8');
102
+ reviewFindings += (text.match(/^\s*(?:#+\s*)?(?:🔴|🟠|🟡|⚪)(?:\s*\/\s*(?:🔴|🟠|🟡|⚪))?\s+.*/gm) || []).length;
103
+ }
104
+ }
105
+
106
+ let issueRows = [];
107
+ if (fs.existsSync(issuesDir)) {
108
+ const issueFiles = fs.readdirSync(issuesDir).filter(f => (f.startsWith(mission + "-") || f.startsWith(mission + "/")) && f.endsWith('.md'));
109
+ for (const f of issueFiles) {
110
+ const text = fs.readFileSync(path.join(issuesDir, f), 'utf8');
111
+ for (const line of text.split(/\r?\n/)) {
112
+ if (line.trim().startsWith('|') && line.includes('|') && !line.includes('---')) {
113
+ const cells = line.split('|').map(c => c.trim()).filter(Boolean);
114
+ if (cells.length >= 5 && !/^wave$/i.test(cells[0])) issueRows.push(cells.join(" | "));
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ let report = "# Mission: " + mission + " . " + now + "\n\n";
121
+ report += "**Lane** " + lane + " . **Mode** " + mode + " . **Actor** " + actor + " . **Branch** " + branch + "\n\n";
122
+
123
+ report += "## What changed\n\n";
124
+ report += filesTouched + " files, +" + locDelta + " LOC";
125
+ if (sensitive.length) report += "\nSensitive paths: " + sensitive.join(", ");
126
+
127
+ report += "\n\n## Waves\n\n| Wave | Artifact | Verdict |\n|------|----------|---------|";
128
+ if (waveRows.length) {
129
+ for (const r of waveRows) report += "\n| " + r.label + " | `" + r.file + "` | " + r.verdict + " |";
130
+ } else {
131
+ report += "\n| (no wave artifacts found in `" + resultsDir + "`) | | |";
132
+ }
133
+
134
+ report += "\n\n## Review & blockers\n\n";
135
+ report += "Review + security files: " + (reviewFiles.length ? reviewFiles.join(", ") : "none") + "\n";
136
+ report += "Findings: " + reviewFindings + "\n";
137
+ report += "Blocker ledger rows: " + issueRows.length + (issueRows.length ? "\n" + issueRows.map(r => "- " + r).join("\n") : "");
138
+
139
+ report += "\n\n## State\n\n| Field | Value |\n|-------|-------|\n";
140
+ report += "| Wave | " + wave + " |\n";
141
+ report += "| Tasks | " + (tasks.done || 0) + "/" + (tasks.total || 0) + " done |\n";
142
+ report += "| Blockers open | " + blockers + " |\n";
143
+ report += "| Heal cycles | " + healCycle + " |\n";
144
+ report += "| Tokens used | " + tokens.toLocaleString() + " / " + budget.toLocaleString() + " |\n\n";
145
+
146
+ report += "## Evidence\n\n";
147
+ for (const e of evidence) report += "- " + e + "\n";
148
+
149
+ report += "\n## Updated\n\n" + updated + "\n";
150
+
151
+ fs.writeFileSync(reportFile, report);
152
+ console.log("\u2713 mission report: " + reportFile);
153
+ NODE
@@ -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();
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env bun
2
- // scripts/release-notes.ts — generates a human-readable release description
3
- // from git history between the previous tag and HEAD. Grouped by change type,
4
- // conventional-commit aware, no gh dependency.
2
+ // scripts/release-notes.ts — generates a detailed GitHub Release description
3
+ // from git history between the previous tag and HEAD. Pulls the full commit
4
+ // message (subject + body), groups by conventional-commit type, flags breaking
5
+ // changes, and lists the affected scopes. No gh dependency.
5
6
  //
6
- // bun scripts/release-notes.ts notes since the last tag → stdout
7
+ // bun scripts/release-notes.ts notes since the last tag → stdout
7
8
  // bun scripts/release-notes.ts --since v0.3.0 notes from that tag → stdout
8
9
  import { execFileSync } from 'node:child_process';
9
10
 
@@ -22,15 +23,22 @@ if (!since) {
22
23
  }
23
24
 
24
25
  const range = since ? `${since}..HEAD` : '';
25
- const raw = execFileSync('git', ['log', '--pretty=%s', ...(range ? [range] : [])], { encoding: 'utf8' });
26
- const commits = raw.split(/\r?\n/).filter(Boolean);
26
+ const raw = execFileSync('git', ['log', '--pretty=%H%n%s%n%n%b%n__END__', ...(range ? [range] : [])], { encoding: 'utf8' });
27
+ const commits = raw
28
+ .split('__END__\n')
29
+ .map(c => c.trim())
30
+ .filter(Boolean)
31
+ .map(c => {
32
+ const [sha, subject, ...rest] = c.split('\n');
33
+ return { sha: sha.slice(0, 7), subject: subject ?? '', body: rest.join('\n').trim() };
34
+ });
27
35
 
28
36
  const groups: Record<string, { label: string; items: string[] }> = {
29
37
  feat: { label: 'New', items: [] },
30
38
  fix: { label: 'Fixed', items: [] },
31
- docs: { label: 'Docs', items: [] },
32
39
  refactor: { label: 'Refactored', items: [] },
33
40
  perf: { label: 'Performance', items: [] },
41
+ docs: { label: 'Docs', items: [] },
34
42
  chore: { label: 'Housekeeping', items: [] },
35
43
  test: { label: 'Housekeeping', items: [] },
36
44
  ci: { label: 'Housekeeping', items: [] },
@@ -38,12 +46,33 @@ const groups: Record<string, { label: string; items: string[] }> = {
38
46
 
39
47
  const order = ['feat', 'fix', 'refactor', 'perf', 'docs', 'chore'];
40
48
 
41
- for (const c of commits) {
42
- const m = /^(\w+)(?:\(.*\))?!?: (.*)/.exec(c);
49
+ // strip signature trailers (Co-authored-by, Signed-off-by, review notes)
50
+ const TRAILER = /^(Co-authored-by|Signed-off-by|Reviewed-by|Helped-by|Reported-by|Tested-by|Acked-by):/i;
51
+
52
+ for (const commit of commits) {
53
+ const m = /^(\w+)(?:\((.*?)\))?!?: (.*)/.exec(commit.subject);
43
54
  const type = m ? m[1] : 'chore';
44
- const text = m ? m[2] : c;
55
+ const scope = m?.[2] || '';
56
+ const text = m ? m[3] : commit.subject;
57
+ const breaking = Boolean(m?.[0]?.includes('!')) || /^BREAKING CHANGE:/m.test(commit.body);
58
+
59
+ const title = text.charAt(0).toUpperCase() + text.slice(1);
60
+ const scopeTag = scope ? ` \`${scope}\`` : '';
61
+ const breakingTag = breaking ? ' ⚠️ **BREAKING**' : '';
62
+
63
+ const bodyLines = commit.body
64
+ .split(/\r?\n/)
65
+ .map(l => l.trim())
66
+ .filter(l => l && !TRAILER.test(l) && l !== '---')
67
+ // the release-version marker commit repeats the subject as body — drop it
68
+ .filter(l => l !== text);
69
+
70
+ let entry = `- ${title}${scopeTag}${breakingTag} \`${commit.sha}\``;
71
+ if (bodyLines.length) {
72
+ entry += '\n' + bodyLines.map(l => ` - ${l}`).join('\n');
73
+ }
45
74
  const g = groups[type] ?? groups.chore;
46
- g.items.push(text.charAt(0).toUpperCase() + text.slice(1));
75
+ g.items.push(entry);
47
76
  }
48
77
 
49
78
  const lines: string[] = [];
@@ -52,11 +81,11 @@ for (const t of order) {
52
81
  if (!g.items.length) continue;
53
82
  lines.push(`## ${g.label}`);
54
83
  lines.push('');
55
- for (const item of g.items) lines.push(`- ${item}`);
84
+ for (const item of g.items) lines.push(item);
56
85
  lines.push('');
57
86
  }
58
87
 
59
- const bumpIdx = commits.findIndex(c => /chore: release|chore: bump/.test(c));
88
+ const bumpIdx = commits.findIndex(c => /chore: release|chore: bump/.test(c.subject));
60
89
  const body = commits.slice(0, bumpIdx === -1 ? commits.length : bumpIdx);
61
90
  const count = body.length || commits.length;
62
91
 
@@ -3,7 +3,7 @@
3
3
  // Builds TF-IDF index over skill descriptions, scores prompts, reports rank-1 + top_k.
4
4
  // No model needed — pure string matching. Outputs JSON.
5
5
 
6
- import { readdirSync, readFileSync, existsSync, statSync } from 'node:fs';
6
+ import { readdirSync, readFileSync, existsSync, statSync, writeFileSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
8
  import { parseFrontmatter } from '../src/frontmatter.ts';
9
9
 
@@ -256,6 +256,5 @@ if (ciArg !== -1) {
256
256
  }
257
257
 
258
258
  function writeJson(path: string, obj: object) {
259
- const { writeFileSync } = require('node:fs');
260
259
  writeFileSync(path, JSON.stringify(obj, null, 2) + '\n');
261
260
  }
@@ -9,6 +9,7 @@
9
9
  // keyword match, then reports pass/fail per case and a rank-1 style score.
10
10
  // The command defaults to "claude -p" — override with MUGIWARA_EVAL_CMD.
11
11
  import { readdirSync, readFileSync, existsSync } from 'node:fs';
12
+ import { execFileSync } from 'node:child_process';
12
13
  import { join } from 'node:path';
13
14
 
14
15
  const root = join(import.meta.dirname, '..');
@@ -136,7 +137,6 @@ async function runCases(env: { execFileSync: typeof import('node:child_process')
136
137
 
137
138
  const runArg = process.argv.indexOf('--run');
138
139
  if (runArg !== -1) {
139
- const { execFileSync } = require('node:child_process');
140
140
  const cmd = process.env.MUGIWARA_EVAL_CMD ?? 'claude -p';
141
141
  const [bin, ...pre] = cmd.split(' ');
142
142
  runCases({ execFileSync, bin, pre });