@ionivetech/mugiwara 0.6.1 → 0.6.3

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 (83) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  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 +42 -10
  7. package/.opencode/commands/mugiwara-execute.md +1 -1
  8. package/.opencode/commands/mugiwara-heal.md +1 -1
  9. package/.opencode/commands/mugiwara-onboard.md +15 -0
  10. package/.opencode/commands/mugiwara-plan.md +1 -1
  11. package/.opencode/commands/mugiwara-review.md +1 -1
  12. package/.opencode/commands/mugiwara-security.md +1 -1
  13. package/.opencode/commands/mugiwara-ship.md +1 -1
  14. package/.opencode/mugiwara-helpers.mjs +0 -1
  15. package/.opencode/plugins/mugiwara.mjs +33 -22
  16. package/AGENTS.md +16 -5
  17. package/README.md +80 -27
  18. package/content/agents/brook-healing.md +1 -1
  19. package/content/agents/chopper-checkpoint.md +1 -1
  20. package/content/agents/eval-runner.md +1 -1
  21. package/content/agents/franky-gates.md +1 -1
  22. package/content/agents/jinbe-security.md +1 -1
  23. package/content/agents/luffy-orchestrator.md +14 -4
  24. package/content/agents/memory-keeper.md +1 -1
  25. package/content/agents/nami-planner.md +2 -2
  26. package/content/agents/onboarding-guide.md +24 -45
  27. package/content/agents/resume-coordinator.md +11 -11
  28. package/content/agents/robin-reviewer.md +1 -1
  29. package/content/agents/sanji-quality.md +1 -1
  30. package/content/agents/skeptic-verifier.md +1 -1
  31. package/content/agents/usopp-brainstorm.md +2 -2
  32. package/content/agents/zoro-execution.md +6 -2
  33. package/content/skills/mugiwara-agent-security/SKILL.md +1 -18
  34. package/content/skills/mugiwara-agent-security/references/checklist.md +20 -0
  35. package/content/skills/mugiwara-backend/SKILL.md +1 -1
  36. package/content/skills/mugiwara-brainstorm/SKILL.md +7 -1
  37. package/content/skills/mugiwara-checkpoint/SKILL.md +1 -1
  38. package/content/skills/mugiwara-claim-audit/SKILL.md +1 -1
  39. package/content/skills/mugiwara-execution/SKILL.md +22 -22
  40. package/content/skills/mugiwara-execution/references/dispatch.md +4 -3
  41. package/content/skills/mugiwara-execution/references/resume-batching.md +4 -4
  42. package/content/skills/mugiwara-frontend/SKILL.md +1 -1
  43. package/content/skills/mugiwara-healing/SKILL.md +3 -38
  44. package/content/skills/mugiwara-healing/references/workers.md +38 -0
  45. package/content/skills/mugiwara-orchestration/SKILL.md +30 -33
  46. package/content/skills/mugiwara-orchestration/references/check-ins.md +42 -0
  47. package/content/skills/mugiwara-orchestration/references/closure.md +1 -1
  48. package/content/skills/mugiwara-orchestration/references/triage-escalation.md +9 -12
  49. package/content/skills/mugiwara-planning/SKILL.md +7 -11
  50. package/content/skills/mugiwara-planning/references/plan-template.md +4 -4
  51. package/content/skills/mugiwara-pr/SKILL.md +11 -9
  52. package/content/skills/mugiwara-resume/SKILL.md +37 -22
  53. package/content/skills/mugiwara-ship/SKILL.md +2 -24
  54. package/content/skills/mugiwara-ship/references/cleanup.md +25 -0
  55. package/content/skills/mugiwara-sunset/SKILL.md +1 -1
  56. package/content/skills/mugiwara-workflow/SKILL.md +4 -2
  57. package/content/skills/mugiwara-workflow/references/workspace-layout.md +16 -7
  58. package/content/skills/using-mugiwara/SKILL.md +5 -1
  59. package/dist/mugiwara.js +107 -37
  60. package/gemini-extension.json +1 -1
  61. package/hooks/session-start.ts +113 -2
  62. package/package.json +3 -3
  63. package/plugin.json +1 -1
  64. package/references/multi-actor.md +39 -14
  65. package/references/skill-versioning.md +3 -3
  66. package/references/token-budget.md +1 -1
  67. package/scripts/evidence.sh +9 -0
  68. package/scripts/gate-selftest.ts +123 -0
  69. package/scripts/lane-base.ts +114 -0
  70. package/scripts/lane.sh +4 -2
  71. package/scripts/lib/lane-base.sh +19 -0
  72. package/scripts/lib/patterns.sh +15 -0
  73. package/scripts/mission-report.sh +21 -5
  74. package/scripts/onboard.ts +3 -29
  75. package/scripts/release-notes.ts +152 -75
  76. package/scripts/savepoint.sh +178 -56
  77. package/scripts/setup-fixtures.ts +108 -0
  78. package/scripts/validate-content.ts +61 -0
  79. package/src/cli.ts +5 -1
  80. package/src/installer.ts +70 -8
  81. package/src/mission.ts +39 -24
  82. package/src/targets/claude.ts +5 -3
  83. package/src/targets/opencode.ts +12 -8
@@ -89,6 +89,129 @@ if (!existsSync(join(root, 'test', 'savepoint.test.ts'))) {
89
89
  }
90
90
  }
91
91
 
92
+ // --- D1 mutation: break LANE_PREV resolve (require-style) → lane-integrity red ---
93
+ console.log('\nD1 — LANE_PREV resolve mutation');
94
+ const savepointFile = join(root, 'scripts', 'savepoint.sh');
95
+ if (!existsSync(savepointFile)) {
96
+ console.log(' ⚠ savepoint.sh not found, skipping');
97
+ } else {
98
+ const original = readFileSync(savepointFile, 'utf8');
99
+ try {
100
+ // reintroduce the D1 defect: read lane_prev with require() of a relative path
101
+ const broken = original.replace(
102
+ /PREV_JSON=\$\(node -e "try\{const fs=require\('fs'\);const s=JSON\.parse\(fs\.readFileSync\(process\.argv\[1\],'utf8'\)\);process\.stdout\.write\(JSON\.stringify\(\{mission:s\.mission\|\|'',lane:s\.lane\|\|'',peak:s\.lane_peak\|\|''\}\)\)\}catch\(e\)\{process\.stdout\.write\('\{\}'\)\}" "\$STATE_FILE" 2>\/dev\/null \|\| true\)/,
103
+ "PREV_JSON=$(node -e \"try{const s=require(process.argv[1]);process.stdout.write(JSON.stringify({mission:s.mission||'',lane:s.lane||'',peak:s.lane_peak||''}))}catch(e){process.stdout.write('{}')}\" \"$STATE_FILE\" 2>/dev/null || true)"
104
+ );
105
+ if (broken === original) {
106
+ console.log(' ⚠ D1 mutation pattern not found — skipping');
107
+ } else {
108
+ writeFileSync(savepointFile, broken);
109
+ assert('broken LANE_PREV resolve → lane-integrity fails', false, () => run('D1', 'bun run test -- lane-integrity -t "lane_prev"'));
110
+ }
111
+ } finally {
112
+ writeFileSync(savepointFile, original);
113
+ assert('restored → lane-integrity passes', true, () => run('D1', 'bun run test -- lane-integrity -t "lane_prev"'));
114
+ }
115
+ }
116
+
117
+ // --- D2 mutation: break monotonic clamp (drop lane_rank) → red ---
118
+ console.log('\nD2 — monotonic clamp mutation');
119
+ if (!existsSync(savepointFile)) {
120
+ console.log(' ⚠ savepoint.sh not found, skipping');
121
+ } else {
122
+ const original = readFileSync(savepointFile, 'utf8');
123
+ try {
124
+ // neuter the clamp: make lane_rank always return 0 so a drop never holds
125
+ const broken = original.replace(
126
+ /lane_rank\(\) \{\n case "\$1" in\n direct\) echo 0 ;;[\s\S]*?\n esac\n\}/,
127
+ 'lane_rank() {\n echo 0\n}'
128
+ );
129
+ if (broken === original) {
130
+ console.log(' ⚠ D2 mutation pattern not found — skipping');
131
+ } else {
132
+ writeFileSync(savepointFile, broken);
133
+ assert('broken clamp → lane-integrity fails', false, () => run('D2', 'bun run test -- lane-integrity -t "clamp"'));
134
+ }
135
+ } finally {
136
+ writeFileSync(savepointFile, original);
137
+ assert('restored → lane-integrity passes', true, () => run('D2', 'bun run test -- lane-integrity -t "clamp"'));
138
+ }
139
+ }
140
+
141
+ // --- D3 mutation: empty SENSITIVE_PATS → lane-integrity red ---
142
+ console.log('\nD3 — sensitive patterns mutation');
143
+ const patternsFile = join(root, 'scripts', 'lib', 'patterns.sh');
144
+ if (!existsSync(patternsFile)) {
145
+ console.log(' ⚠ patterns.sh not found, skipping');
146
+ } else {
147
+ const original = readFileSync(patternsFile, 'utf8');
148
+ try {
149
+ // reintroduce the D3 defect: singular-only list (no payments/, migrations/)
150
+ const broken = original.replace(
151
+ /SENSITIVE_PATS=.*/,
152
+ 'SENSITIVE_PATS="auth/|payment/|billing/|crypto/|secrets/|\\.env$|config/.*key|migration/|\\.sql$|schema\\.|\\.prisma$|\\.terraform|\\.tf$"'
153
+ );
154
+ if (broken === original) {
155
+ console.log(' ⚠ D3 mutation pattern not found — skipping');
156
+ } else {
157
+ writeFileSync(patternsFile, broken);
158
+ assert('singular sensitive patterns → lane-integrity fails', false, () => run('D3', 'bun run test -- lane-integrity -t "payments"'));
159
+ }
160
+ } finally {
161
+ writeFileSync(patternsFile, original);
162
+ assert('restored → lane-integrity passes', true, () => run('D3', 'bun run test -- lane-integrity -t "payments"'));
163
+ }
164
+ }
165
+
166
+ // --- D4 mutation: zero LOC_TOKENS → lane-integrity red ---
167
+ console.log('\nD4 — churn token mutation');
168
+ if (!existsSync(savepointFile)) {
169
+ console.log(' ⚠ savepoint.sh not found, skipping');
170
+ } else {
171
+ const original = readFileSync(savepointFile, 'utf8');
172
+ try {
173
+ // revert to delta-based (0 on deletions/refactors)
174
+ const broken = original.replace(
175
+ /LOC_TOKENS=\$\(\( LOC_CHURN \* 12 \)\)/,
176
+ 'LOC_TOKENS=$(( LOC_DELTA > 0 ? LOC_DELTA * 12 : 0 ))'
177
+ );
178
+ if (broken === original) {
179
+ console.log(' ⚠ D4 mutation pattern not found — skipping');
180
+ } else {
181
+ writeFileSync(savepointFile, broken);
182
+ assert('zero churn tokens → lane-integrity fails', false, () => run('D4', 'bun run test -- lane-integrity -t "churn"'));
183
+ }
184
+ } finally {
185
+ writeFileSync(savepointFile, original);
186
+ assert('restored → lane-integrity passes', true, () => run('D4', 'bun run test -- lane-integrity -t "churn"'));
187
+ }
188
+ }
189
+
190
+ // --- D10 mutation: break continue writer → savepoint test red ---
191
+ console.log('\nD10 — continue writer mutation');
192
+ if (!existsSync(savepointFile)) {
193
+ console.log(' ⚠ savepoint.sh not found, skipping');
194
+ } else {
195
+ const original = readFileSync(savepointFile, 'utf8');
196
+ try {
197
+ // silently drop the continue writer block (make it a no-op). Anchor on the
198
+ // D10 header comment so the regex hits the writer, not the STATE_FILE if.
199
+ const broken = original.replace(
200
+ /# --- continue\/<mission>\/<member>\.json \(D10\): machine-written resume point ---[\s\S]*?\nfi\n\n/,
201
+ '# --- continue writer disabled (D10) ---\n\n'
202
+ );
203
+ if (broken === original) {
204
+ console.log(' ⚠ D10 mutation pattern not found — skipping');
205
+ } else {
206
+ writeFileSync(savepointFile, broken);
207
+ assert('broken continue writer → savepoint fails', false, () => run('D10', 'bun run test -- savepoint -t "D10"'));
208
+ }
209
+ } finally {
210
+ writeFileSync(savepointFile, original);
211
+ assert('restored → savepoint passes', true, () => run('D10', 'bun run test -- savepoint -t "D10"'));
212
+ }
213
+ }
214
+
92
215
  // --- Retrieval eval ratchet ---
93
216
  console.log('\nRetrieval eval ratchet');
94
217
  if (!existsSync(join(root, 'scripts', 'retrieval-eval.ts'))) {
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env bun
2
+ // scripts/lane-base.ts — G5: validate LANE_BASE constants against measured
3
+ // content instruction load. Each lane loads a fixed set of skills + agents
4
+ // (wave owners from docs/concepts/workflow.md). The honest base is the sum of
5
+ // their body word-sums × 1.35 tokens/word. A constant that drifts more than
6
+ // the tolerance from that load fails — content growth must be reflected in
7
+ // lane-base.sh, or the estimate is a lie (D5).
8
+ //
9
+ // Run: bun scripts/lane-base.ts — validate (CI gate)
10
+ // bun scripts/lane-base.ts --show — print computed load per lane
11
+
12
+ import { readFileSync, existsSync, readdirSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+
15
+ const root = join(import.meta.dirname, '..');
16
+ const skills = join(root, 'content', 'skills');
17
+ const agents = join(root, 'content', 'agents');
18
+
19
+ // wave → owning skill + agent (workflow.md rows)
20
+ const WAVE_OWNER: Record<string, [string, string]> = {
21
+ '0': ['mugiwara-orchestration', 'luffy-orchestrator'],
22
+ '1': ['mugiwara-brainstorm', 'usopp-brainstorm'],
23
+ '2': ['mugiwara-planning', 'nami-planner'],
24
+ '3': ['mugiwara-execution', 'zoro-execution'],
25
+ '4': ['mugiwara-checkpoint', 'chopper-checkpoint'],
26
+ '4.5': ['mugiwara-claim-audit', 'skeptic-verifier'],
27
+ '5': ['mugiwara-quality', 'sanji-quality'],
28
+ '6': ['mugiwara-gates', 'franky-gates'],
29
+ '7': ['mugiwara-review', 'robin-reviewer'],
30
+ '8': ['mugiwara-healing', 'brook-healing'],
31
+ };
32
+
33
+ // skills always loaded regardless of lane: none — resume/lessons only load
34
+ // mid-mission on interruption, too cheap to count as a lane base.
35
+ const ALWAYS: Array<[string, string]> = [];
36
+
37
+ const LANE_WAVES: Record<string, string[]> = {
38
+ direct: [],
39
+ lean: ['0', '3', '5'],
40
+ standard: ['0', '2', '3', '4', '7'],
41
+ full: ['0', '1', '2', '3', '4', '4.5', '5', '6', '7', '8'],
42
+ spike: ['0', '1'],
43
+ };
44
+
45
+ const TOKENS_PER_WORD = 1.35;
46
+ const TOLERANCE = 0.2; // ±20% — a real drift, not rounding
47
+
48
+ function wordCount(file: string): number {
49
+ if (!existsSync(file)) return 0;
50
+ return readFileSync(file, 'utf8').split(/\s+/).filter(Boolean).length;
51
+ }
52
+
53
+ function laneLoad(lane: string): number {
54
+ const waves = LANE_WAVES[lane] ?? [];
55
+ const owners: Array<[string, string]> = [...ALWAYS];
56
+ for (const w of waves) {
57
+ if (WAVE_OWNER[w]) owners.push(WAVE_OWNER[w]);
58
+ }
59
+ let words = 0;
60
+ for (const [skill, agent] of owners) {
61
+ words += wordCount(join(skills, skill, 'SKILL.md'));
62
+ words += wordCount(join(agents, agent + '.md'));
63
+ }
64
+ return Math.round(words * TOKENS_PER_WORD);
65
+ }
66
+
67
+ // constants live in scripts/lib/lane-base.sh — parse them, not hardcode
68
+ function parseConstants(): Record<string, { base: number; budget: number }> {
69
+ const text = readFileSync(join(root, 'scripts', 'lib', 'lane-base.sh'), 'utf8');
70
+ const out: Record<string, { base: number; budget: number }> = {};
71
+ for (const lane of ['lean', 'standard', 'full', 'spike']) {
72
+ const base = text.match(new RegExp(`LANE_BASE_${lane}=(\\d+)`));
73
+ const budget = text.match(new RegExp(`BUDGET_${lane}=(\\d+)`));
74
+ out[lane] = {
75
+ base: base ? parseInt(base[1], 10) : 0,
76
+ budget: budget ? parseInt(budget[1], 10) : 0,
77
+ };
78
+ }
79
+ return out;
80
+ }
81
+
82
+ const SHOW = process.argv.includes('--show');
83
+ const lanes = ['direct', 'lean', 'standard', 'full', 'spike'];
84
+ let failures = 0;
85
+
86
+ const constants = parseConstants();
87
+ for (const lane of lanes) {
88
+ const computed = laneLoad(lane);
89
+ const declared = constants[lane]?.base ?? 0;
90
+ if (SHOW) {
91
+ console.log(`${lane.padEnd(9)} computed=${computed.toString().padEnd(6)} declared=${declared} budget=${constants[lane]?.budget ?? 0}`);
92
+ }
93
+ // direct runs no pipeline — base 0 is correct by design, skip
94
+ if (lane === 'direct') continue;
95
+ // spike is a deliberate floor (resize lane, brainstorm only) — not
96
+ // content-derived, never validated
97
+ if (lane === 'spike') continue;
98
+ if (declared === 0) {
99
+ console.log(` ✗ ${lane}: LANE_BASE is 0 — missing constant in lane-base.sh`);
100
+ failures++;
101
+ continue;
102
+ }
103
+ const drift = Math.abs(computed - declared) / declared;
104
+ if (drift > TOLERANCE) {
105
+ console.log(` ✗ ${lane}: LANE_BASE ${declared} drifts ${(drift * 100).toFixed(0)}% from computed load ${computed} — update lane-base.sh`);
106
+ failures++;
107
+ }
108
+ }
109
+
110
+ if (failures > 0) {
111
+ console.log(`\nlane-base: ${failures} constant(s) drifted from content load`);
112
+ process.exit(1);
113
+ }
114
+ console.log('\nlane-base: constants match content load');
package/scripts/lane.sh CHANGED
@@ -3,6 +3,10 @@
3
3
  # Usage: lane.sh [base-ref] [--json]
4
4
  set -u
5
5
 
6
+ # shared path patterns — single source of truth (D3)
7
+ # shellcheck source=scripts/lib/patterns.sh
8
+ source "$(dirname "$0")/lib/patterns.sh"
9
+
6
10
  BASE="${1:-main}"
7
11
  JSON_OUT=0
8
12
  [ "${2:-}" = "--json" ] && JSON_OUT=1
@@ -19,7 +23,6 @@ CHANGED=$(git diff --name-only "$BASE"..HEAD 2>/dev/null || git diff --name-only
19
23
  FILE_COUNT=0
20
24
  [ -n "$CHANGED" ] && FILE_COUNT=$(echo "$CHANGED" | wc -l | tr -d ' ')
21
25
 
22
- SENSITIVE_PATS="auth/|payment/|billing/|crypto/|secrets/|\.env$|config/.*key|migration/|\.sql$|schema\.|\.prisma$|\.terraform|\.tf$"
23
26
  SENSITIVE=$(echo "$CHANGED" | grep -E "$SENSITIVE_PATS" 2>/dev/null | head -5 | tr '\n' ',' | sed 's/,$//' || true)
24
27
  HAS_SENSITIVE=0
25
28
  [ -n "$SENSITIVE" ] && HAS_SENSITIVE=1
@@ -64,7 +67,6 @@ fi
64
67
  # code surface actually touched. Sensitive-path escalation above still wins.
65
68
  # Product surface for mugiwara: content/, src/, scripts/, test/, hooks/,
66
69
  # .opencode/, .claude/, evals/ — everything else is docs/config/asset.
67
- PRODUCT_PAT="^content/|^src/|^scripts/|^test/|^hooks/|^\.opencode/|^\.claude/|^evals/"
68
70
  if [ "$LANE" = "full" ] && [ "$HAS_SENSITIVE" -eq 0 ] && [ -n "$CHANGED" ]; then
69
71
  CODE_COUNT=$(echo "$CHANGED" | grep -E "$PRODUCT_PAT" 2>/dev/null | grep -c . || true)
70
72
  if [ -z "$CODE_COUNT" ] || [ "$CODE_COUNT" -eq 0 ] 2>/dev/null; then
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/lib/lane-base.sh — LANE_BASE + BUDGET constants, single source of
3
+ # truth. Validated by scripts/lane-base.ts against measured content word-sums.
4
+ # Change a constant here → run `bun scripts/lane-base.ts`; the gate fails if
5
+ # the constant drifts from the honest instruction load (D5).
6
+
7
+ # LANE_BASE: token estimate for skills/agents loaded in this lane. Derived
8
+ # from content word-sums × 1.35 (see scripts/lane-base.ts). spike is a
9
+ # resize, not a rise — small base, tiny budget.
10
+ LANE_BASE_lean=7000
11
+ LANE_BASE_standard=13000
12
+ LANE_BASE_full=23000
13
+ LANE_BASE_spike=1000
14
+
15
+ # BUDGET: warn at 1.5×, stop at 3×.
16
+ BUDGET_lean=12000
17
+ BUDGET_standard=25000
18
+ BUDGET_full=50000
19
+ BUDGET_spike=3000
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/lib/patterns.sh — single source of truth for lane path patterns.
3
+ # Sourced by lane.sh and savepoint.sh. Extend here, never in the consumers.
4
+
5
+ # Sensitive-path escalation patterns. A changed file matching any of these
6
+ # always escalates to Lane 3 (Full), regardless of file count. Plural forms
7
+ # included (payments/, migrations/) — the singular-only list missed them (D3).
8
+ # Deliberately NOT matched: package.json (dependency churn is policy-as-code,
9
+ # not a sensitive lane trigger), authors/ (contains "auth" but never auth/).
10
+ SENSITIVE_PATS="auth/|payment/|payments/|billing/|crypto/|secrets/|\.env$|config/.*key|migration/|migrations/|\.sql$|schema\.|\.prisma$|\.terraform|\.tf$"
11
+
12
+ # Product surface: paths that count toward file-based lane sizing. Changes
13
+ # outside this surface are docs/config/asset and never escalate to full on
14
+ # count alone (path-weighted sizing).
15
+ PRODUCT_PAT="^content/|^src/|^scripts/|^test/|^hooks/|^\.opencode/|^\.claude/|^evals/"
@@ -12,25 +12,38 @@ MISSION="${1:-}"
12
12
  case "$MISSION" in
13
13
  *[!a-zA-Z0-9._-]*) echo "mission-report: invalid mission name \"$MISSION\" (allowlist: [a-zA-Z0-9._-])" >&2; exit 1 ;;
14
14
  esac
15
+ if [[ "$MISSION" =~ ^\.+$ ]]; then
16
+ echo "mission-report: invalid mission name \"$MISSION\" (allowlist: [a-zA-Z0-9._-], not a dot-path)" >&2; exit 1
17
+ fi
15
18
 
16
19
  MUGIWARA_DIR="${MUGIWARA_DIR:-.mugiwara}"
17
- STATE_FILE="$MUGIWARA_DIR/state.json"
20
+ # state lives per (mission, member): solo = state/<mission>/state.json;
21
+ # MEMBER env selects a team member's state, else the solo state.
22
+ MEMBER="${MEMBER:-}"
23
+ case "$MEMBER" in
24
+ *[!a-zA-Z0-9._-]*) echo "mission-report: invalid member \"$MEMBER\" (allowlist: [a-zA-Z0-9._-])" >&2; exit 1 ;;
25
+ esac
26
+ if [ -n "$MEMBER" ]; then
27
+ STATE_FILE="$MUGIWARA_DIR/state/$MISSION/$MEMBER.json"
28
+ else
29
+ STATE_FILE="$MUGIWARA_DIR/state/$MISSION/state.json"
30
+ fi
18
31
  REPORT_DIR="$MUGIWARA_DIR/reports"
19
32
  RESULTS_DIR="$MUGIWARA_DIR/results/$MISSION"
20
33
  REVIEW_DIR="$MUGIWARA_DIR/review"
21
34
  ISSUES_DIR="$MUGIWARA_DIR/issues"
22
35
 
23
- export STATE_FILE REPORT_DIR RESULTS_DIR REVIEW_DIR ISSUES_DIR MISSION
36
+ export STATE_FILE REPORT_DIR RESULTS_DIR REVIEW_DIR ISSUES_DIR MISSION MUGIWARA_DIR
24
37
  node << 'NODE'
25
38
  const fs = require('fs');
26
39
  const path = require('path');
27
40
 
28
- const stateFile = process.env.STATE_FILE || ".mugiwara/state.json";
41
+ const stateFile = process.env.STATE_FILE || ".mugiwara/state/unknown/state.json";
29
42
  const reportDir = process.env.REPORT_DIR || ".mugiwara/reports";
30
43
  const resultsDir = process.env.RESULTS_DIR || ".mugiwara/results/unknown";
31
44
  const reviewDir = process.env.REVIEW_DIR || ".mugiwara/review";
32
45
  const issuesDir = process.env.ISSUES_DIR || ".mugiwara/issues";
33
- const mugiDir = path.dirname(stateFile);
46
+ const mugiDir = process.env.MUGIWARA_DIR || ".mugiwara";
34
47
  const mission = process.env.MISSION || "unknown";
35
48
 
36
49
  fs.mkdirSync(reportDir, { recursive: true });
@@ -56,6 +69,7 @@ const val = (obj, key, dflt) => {
56
69
  const now = new Date().toISOString().slice(0, 10);
57
70
  const reportFile = path.join(reportDir, now + "-" + mission + ".md");
58
71
  const lane = val(s, 'lane', 'n/a');
72
+ const lanePeak = val(s, 'lane_peak', 'n/a');
59
73
  const laneReason = val(s, 'lane_reason', 'n/a');
60
74
  const mode = val(s, 'mode', 'n/a');
61
75
  const actor = val(s, 'actor', 'n/a');
@@ -63,6 +77,7 @@ const branch = val(s, 'branch', 'n/a');
63
77
  const wave = val(s, 'wave', 'n/a');
64
78
  const filesTouched = val(s, 'files_touched', 0);
65
79
  const locDelta = val(s, 'loc_delta', 0);
80
+ const locChurn = val(s, 'loc_churn', 0);
66
81
  const sensitive = s ? (s.sensitive_paths || []) : [];
67
82
  const tasks = s ? (s.tasks || {}) : {};
68
83
  const blockers = val(s, 'blockers_open', 0);
@@ -213,6 +228,7 @@ report += "## Mission header\n\n| Field | Value |\n|-------|-------|\n";
213
228
  report += "| Mission | " + mission + " |\n";
214
229
  report += "| Branch | " + branch + " |\n";
215
230
  report += "| Lane | " + lane + " |\n";
231
+ report += "| Lane peak | " + lanePeak + " |\n";
216
232
  report += "| Lane reason | " + laneReason + " |\n";
217
233
  report += "| Mode | " + mode + " |\n";
218
234
  report += "| Wave | " + wave + " |\n";
@@ -232,7 +248,7 @@ report += "| Total | " + tasksTotal + " |\n";
232
248
  report += "| Source | " + tasksSource + " |\n\n";
233
249
 
234
250
  report += "## What changed\n\n";
235
- report += filesTouched + " files, +" + locDelta + " LOC";
251
+ report += filesTouched + " files, +" + locDelta + " LOC (" + locChurn + " churn)";
236
252
  if (sensitive.length) report += "\nSensitive paths: " + sensitive.join(", ");
237
253
  report += "\n\n";
238
254
 
@@ -7,7 +7,6 @@ import { createInterface } from "node:readline";
7
7
  const root = join(import.meta.dirname, "..");
8
8
  const mugiwaraDir = join(root, ".mugiwara");
9
9
  const configPath = join(mugiwaraDir, "config");
10
- const onboardPath = join(mugiwaraDir, "onboard.json");
11
10
 
12
11
  function ask(rl: import("node:readline").Interface, prompt: string): Promise<string> {
13
12
  return new Promise((resolve) => {
@@ -38,7 +37,7 @@ async function main() {
38
37
  if (process.argv.includes("--help") || process.argv.includes("-h")) {
39
38
  console.log("Usage: bun scripts/onboard.ts");
40
39
  console.log("Runs the Mugiwara onboarding wizard (10 fixed questions).");
41
- console.log("Writes .mugiwara/config and .mugiwara/onboard.json.");
40
+ console.log("Writes .mugiwara/config.");
42
41
  process.exit(0);
43
42
  }
44
43
 
@@ -64,10 +63,6 @@ async function main() {
64
63
  console.log("");
65
64
  }
66
65
 
67
- const answers: Record<string, unknown> = {
68
- started_at: new Date().toISOString(),
69
- };
70
-
71
66
  // ---- Phase 1: Project Context ----
72
67
  console.log("── Phase 1: Project Context ──");
73
68
  console.log("");
@@ -80,7 +75,6 @@ async function main() {
80
75
  "Backend service / API",
81
76
  "Other",
82
77
  ]);
83
- answers.project_type = q1;
84
78
  console.log("");
85
79
 
86
80
  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 > ", [
@@ -92,7 +86,6 @@ async function main() {
92
86
  "Java",
93
87
  "Other",
94
88
  ]);
95
- answers.primary_language = q2;
96
89
  console.log("");
97
90
 
98
91
  const q3 = await pick(rl, "Q3 — Team size:\n [1] Solo\n [2] 2–5\n [3] 6–15\n [4] 16+\n > ", [
@@ -101,7 +94,6 @@ async function main() {
101
94
  "6–15",
102
95
  "16+",
103
96
  ]);
104
- answers.team_size = q3;
105
97
  console.log("");
106
98
 
107
99
  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 > ", [
@@ -110,7 +102,6 @@ async function main() {
110
102
  "github-flow",
111
103
  "other",
112
104
  ]);
113
- answers.git_workflow = q4;
114
105
  console.log("");
115
106
 
116
107
  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 > ", [
@@ -121,7 +112,6 @@ async function main() {
121
112
  "None / manual",
122
113
  "Other",
123
114
  ]);
124
- answers.ci_cd = q5;
125
115
  console.log("");
126
116
 
127
117
  // ---- Phase 2: Mugiwara Preferences ----
@@ -133,27 +123,20 @@ async function main() {
133
123
  "semi",
134
124
  "auto",
135
125
  ]);
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
126
  console.log("");
142
127
 
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 > ", [
128
+ const q8a = await pick(rl, "Q7 — 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
129
  "full",
145
130
  "standard",
146
131
  "quick",
147
132
  ]);
148
- answers.review_depth = q8a;
149
133
  console.log("");
150
134
 
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 > ", [
135
+ const q8b = await pick(rl, "Q8 — Quality check depth:\n [1] full — format, lint, typecheck, test, build\n [2] standard — lint, typecheck, test\n [3] quick — test only\n > ", [
152
136
  "full",
153
137
  "standard",
154
138
  "quick",
155
139
  ]);
156
- answers.quality_depth = q8b;
157
140
  console.log("");
158
141
 
159
142
  console.log("Q9 — Test coverage threshold:");
@@ -180,9 +163,6 @@ async function main() {
180
163
  coverageNew = 0;
181
164
  coverageModified = 0;
182
165
  }
183
- answers.coverage_threshold = q9;
184
- answers.coverage_new = coverageNew;
185
- answers.coverage_modified = coverageModified;
186
166
  console.log("");
187
167
 
188
168
  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 > ", [
@@ -190,8 +170,6 @@ async function main() {
190
170
  "semantic",
191
171
  "free-form",
192
172
  ]);
193
- answers.commit_style = q10;
194
- answers.completed_at = new Date().toISOString();
195
173
  console.log("");
196
174
 
197
175
  // ---- Branch name format ----
@@ -230,11 +208,9 @@ async function main() {
230
208
  commit,
231
209
  review_depth: reviewDepth,
232
210
  quality_depth: qualityDepth,
233
- enabled_agents: answers.enabled_agents,
234
211
  };
235
212
 
236
213
  writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
237
- writeFileSync(onboardPath, JSON.stringify(answers, null, 2) + "\n");
238
214
 
239
215
  // ---- Summary ----
240
216
  const qLabels: Record<string, Record<number, string>> = {
@@ -275,14 +251,12 @@ async function main() {
275
251
  console.log(" Git workflow: ", qLabels.git_workflow[q4]);
276
252
  console.log(" CI/CD: ", qLabels.ci_cd[q5]);
277
253
  console.log(" Autonomy mode: ", qLabels.autonomy_mode[q6]);
278
- console.log(" Enabled agents: ", answers.enabled_agents);
279
254
  console.log(" Review depth: ", qLabels.review_depth[q8a]);
280
255
  console.log(" Quality depth: ", qLabels.quality_depth[q8b]);
281
256
  console.log(" Coverage: ", q9 === 3 ? `${coverageNew}/${coverageModified}` : qLabels.coverage_threshold[q9]);
282
257
  console.log(" Commit style: ", qLabels.commit_style[q10]);
283
258
  console.log("");
284
259
  console.log(` Config written: ${configPath}`);
285
- console.log(` Audit trail: ${onboardPath}`);
286
260
  console.log("");
287
261
  } finally {
288
262
  rl.close();