@ionivetech/mugiwara 0.5.5 → 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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +3 -2
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.kimi-plugin/plugin.json +1 -1
- package/.opencode/commands/mugiwara-continue.md +16 -0
- package/.opencode/commands/mugiwara-execute.md +6 -4
- package/.opencode/commands/mugiwara-heal.md +6 -4
- package/.opencode/commands/mugiwara-plan.md +6 -4
- package/.opencode/commands/mugiwara-review.md +6 -4
- package/.opencode/commands/mugiwara-security.md +6 -4
- package/.opencode/commands/mugiwara-ship.md +6 -4
- package/.opencode/commands/mugiwara.md +4 -2
- package/.opencode/commands/using-mugiwara.md +7 -13
- package/.opencode/plugins/mugiwara.mjs +18 -6
- package/AGENTS.md +1 -1
- package/GEMINI.md +1 -1
- package/README.md +251 -120
- package/content/agents/brook-healing.md +17 -3
- package/content/agents/chopper-checkpoint.md +17 -3
- package/content/agents/eval-runner.md +17 -2
- package/content/agents/franky-gates.md +19 -5
- package/content/agents/jinbe-security.md +19 -3
- package/content/agents/luffy-orchestrator.md +6 -3
- package/content/agents/memory-keeper.md +15 -0
- package/content/agents/nami-planner.md +21 -6
- package/content/agents/onboarding-guide.md +145 -0
- package/content/agents/resume-coordinator.md +21 -5
- package/content/agents/robin-reviewer.md +18 -3
- package/content/agents/sanji-quality.md +19 -4
- package/content/agents/skeptic-verifier.md +18 -3
- package/content/agents/usopp-brainstorm.md +17 -3
- package/content/agents/zoro-execution.md +18 -3
- package/content/skills/mugiwara-backend/SKILL.md +1 -1
- package/content/skills/mugiwara-brainstorm/SKILL.md +5 -1
- package/content/skills/mugiwara-checkpoint/SKILL.md +2 -2
- package/content/skills/mugiwara-contract-first/SKILL.md +2 -36
- package/content/skills/mugiwara-contract-first/references/process.md +37 -0
- package/content/skills/mugiwara-execution/SKILL.md +30 -13
- package/content/skills/mugiwara-execution/references/resume-batching.md +32 -0
- package/content/skills/mugiwara-frontend/SKILL.md +1 -1
- package/content/skills/mugiwara-gates/SKILL.md +32 -30
- package/content/skills/mugiwara-healing/SKILL.md +1 -1
- package/content/skills/mugiwara-orchestration/SKILL.md +41 -25
- package/content/skills/mugiwara-orchestration/references/delegation-pillars.md +40 -0
- package/content/skills/mugiwara-orchestration/references/triage-escalation.md +96 -0
- package/content/skills/mugiwara-planning/SKILL.md +23 -27
- package/content/skills/mugiwara-planning/references/plan-template.md +42 -0
- package/content/skills/mugiwara-pr/SKILL.md +2 -2
- package/content/skills/mugiwara-quality/SKILL.md +10 -6
- package/content/skills/mugiwara-resume/SKILL.md +7 -2
- package/content/skills/mugiwara-review/SKILL.md +20 -1
- package/content/skills/mugiwara-review/references/code-attributes.md +38 -0
- package/content/skills/mugiwara-root-cause/SKILL.md +3 -30
- package/content/skills/mugiwara-root-cause/references/process.md +35 -0
- package/content/skills/mugiwara-security/SKILL.md +17 -32
- package/content/skills/mugiwara-security/references/owasp-top10.md +12 -0
- package/content/skills/mugiwara-security/references/rationalizations.md +8 -0
- package/content/skills/mugiwara-ship/SKILL.md +5 -4
- package/content/skills/mugiwara-workflow/SKILL.md +74 -77
- package/content/skills/mugiwara-workflow/references/workspace-layout.md +50 -0
- package/content/skills/using-mugiwara/SKILL.md +8 -19
- package/dist/mugiwara.js +107 -41
- package/gemini-extension.json +1 -1
- package/hooks/session-start.ts +1 -1
- package/package.json +11 -4
- package/plugin.json +1 -1
- package/references/multi-actor.md +1 -1
- package/scripts/evidence.sh +12 -5
- package/scripts/gate-selftest.ts +266 -0
- package/scripts/initiative.ts +248 -0
- package/scripts/lane.sh +15 -0
- package/scripts/mission-report.sh +90 -12
- package/scripts/onboard.ts +292 -0
- package/scripts/release-notes.ts +42 -13
- package/scripts/savepoint.sh +97 -50
- package/scripts/validate-content.ts +77 -0
- package/scripts/verify-install.ts +72 -0
- package/src/installer.ts +34 -7
- package/src/mission.ts +7 -5
- package/src/targets/claude.ts +20 -3
- package/src/targets/generic.ts +1 -1
- package/src/targets/opencode.ts +31 -10
- package/docs/concepts/agents.md +0 -53
- package/docs/concepts/audit-trail.md +0 -65
- package/docs/concepts/comparison.md +0 -58
- package/docs/concepts/config.md +0 -55
- package/docs/concepts/cost.md +0 -45
- package/docs/concepts/execution-model.md +0 -92
- package/docs/concepts/git-strategy.md +0 -62
- package/docs/concepts/lanes.md +0 -82
- package/docs/concepts/modes.md +0 -73
- package/docs/concepts/pr-summary.md +0 -54
- package/docs/concepts/skills.md +0 -55
- package/docs/concepts/workflow.md +0 -89
- package/docs/getting-started.md +0 -158
- package/docs/index.md +0 -56
- package/docs/install/antigravity.md +0 -45
- package/docs/install/claude.md +0 -77
- package/docs/install/cli.md +0 -115
- package/docs/install/codex.md +0 -44
- package/docs/install/copilot.md +0 -45
- package/docs/install/cursor.md +0 -45
- package/docs/install/gemini.md +0 -44
- package/docs/install/index.md +0 -53
- package/docs/install/kimi.md +0 -45
- package/docs/install/opencode.md +0 -143
- package/docs/install/pi.md +0 -46
- package/docs/reference/adoption-guide.md +0 -72
- package/docs/reference/agent-anatomy.md +0 -72
- package/docs/reference/compliance-matrix.md +0 -81
- package/docs/reference/developer-onboarding.md +0 -89
- package/docs/reference/enforcement.md +0 -35
- package/docs/reference/harness-matrix.md +0 -41
- package/docs/reference/skill-anatomy.md +0 -71
- package/docs/troubleshooting.md +0 -91
- package/evals/cases/_no-skill.json +0 -16
- package/evals/cases/adversarial-pressure-fake-pass.json +0 -25
- package/evals/cases/adversarial-pressure-skip-review.json +0 -25
- package/evals/cases/lane-exploratory-vague.json +0 -24
- package/evals/cases/lane-sensitivity-payment.json +0 -24
- package/evals/cases/positive-refactor-existing-tests.json +0 -25
- package/evals/cases/positive-resume-mid-mission.json +0 -24
- package/evals/cases/routing-agent-security.json +0 -25
- package/evals/cases/routing-auth-feature.json +0 -26
- package/evals/cases/routing-backend.json +0 -25
- package/evals/cases/routing-bug-one-file.json +0 -26
- package/evals/cases/routing-claim-audit.json +0 -25
- package/evals/cases/routing-context-budget.json +0 -25
- package/evals/cases/routing-contract-first.json +0 -25
- package/evals/cases/routing-execution.json +0 -25
- package/evals/cases/routing-frontend.json +0 -26
- package/evals/cases/routing-gates.json +0 -25
- package/evals/cases/routing-git.json +0 -25
- package/evals/cases/routing-healing.json +0 -25
- package/evals/cases/routing-lessons.json +0 -25
- package/evals/cases/routing-orchestration.json +0 -25
- package/evals/cases/routing-planning.json +0 -26
- package/evals/cases/routing-pr.json +0 -25
- package/evals/cases/routing-quality.json +0 -25
- package/evals/cases/routing-ship.json +0 -26
- package/evals/cases/routing-sunset.json +0 -25
- package/evals/cases/routing-using-mugiwara.json +0 -25
- package/evals/cases/routing-workflow.json +0 -25
- package/evals/floor.json +0 -6
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# scripts/mission-report.sh — generate
|
|
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
|
-
|
|
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
|
-
export STATE_FILE REPORT_DIR MISSION
|
|
22
|
+
export STATE_FILE REPORT_DIR RESULTS_DIR REVIEW_DIR ISSUES_DIR MISSION
|
|
17
23
|
node << 'NODE'
|
|
18
24
|
const fs = require('fs');
|
|
19
25
|
const path = require('path');
|
|
20
26
|
|
|
21
27
|
const stateFile = process.env.STATE_FILE || ".mugiwara/state.json";
|
|
22
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";
|
|
23
32
|
const mission = process.env.MISSION || "unknown";
|
|
24
33
|
|
|
25
34
|
if (!fs.existsSync(stateFile)) {
|
|
@@ -30,8 +39,8 @@ if (!fs.existsSync(stateFile)) {
|
|
|
30
39
|
fs.mkdirSync(reportDir, { recursive: true });
|
|
31
40
|
const s = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
32
41
|
|
|
33
|
-
const reportFile = path.join(reportDir, mission + ".md");
|
|
34
42
|
const now = new Date().toISOString().slice(0, 10);
|
|
43
|
+
const reportFile = path.join(reportDir, now + "-" + mission + ".md");
|
|
35
44
|
const lane = s.lane || "unknown";
|
|
36
45
|
const mode = s.mode || "guided";
|
|
37
46
|
const actor = s.actor || "unknown";
|
|
@@ -48,19 +57,84 @@ const budget = s.budget || 0;
|
|
|
48
57
|
const evidence = s.evidence || [];
|
|
49
58
|
const updated = s.updated_at || new Date().toISOString();
|
|
50
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
|
+
|
|
51
120
|
let report = "# Mission: " + mission + " . " + now + "\n\n";
|
|
52
121
|
report += "**Lane** " + lane + " . **Mode** " + mode + " . **Actor** " + actor + " . **Branch** " + branch + "\n\n";
|
|
122
|
+
|
|
53
123
|
report += "## What changed\n\n";
|
|
54
124
|
report += filesTouched + " files, +" + locDelta + " LOC";
|
|
125
|
+
if (sensitive.length) report += "\nSensitive paths: " + sensitive.join(", ");
|
|
55
126
|
|
|
56
|
-
|
|
57
|
-
|
|
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 + "`) | | |";
|
|
58
132
|
}
|
|
59
133
|
|
|
60
|
-
report += "\n\n##
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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") : "");
|
|
64
138
|
|
|
65
139
|
report += "\n\n## State\n\n| Field | Value |\n|-------|-------|\n";
|
|
66
140
|
report += "| Wave | " + wave + " |\n";
|
|
@@ -68,7 +142,11 @@ report += "| Tasks | " + (tasks.done || 0) + "/" + (tasks.total || 0) + " done |
|
|
|
68
142
|
report += "| Blockers open | " + blockers + " |\n";
|
|
69
143
|
report += "| Heal cycles | " + healCycle + " |\n";
|
|
70
144
|
report += "| Tokens used | " + tokens.toLocaleString() + " / " + budget.toLocaleString() + " |\n\n";
|
|
71
|
-
|
|
145
|
+
|
|
146
|
+
report += "## Evidence\n\n";
|
|
147
|
+
for (const e of evidence) report += "- " + e + "\n";
|
|
148
|
+
|
|
149
|
+
report += "\n## Updated\n\n" + updated + "\n";
|
|
72
150
|
|
|
73
151
|
fs.writeFileSync(reportFile, report);
|
|
74
152
|
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();
|
package/scripts/release-notes.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
// scripts/release-notes.ts — generates a
|
|
3
|
-
// from git history between the previous tag and HEAD.
|
|
4
|
-
// conventional-commit
|
|
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
|
|
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
|
|
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
|
-
|
|
42
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|