@ionivetech/mugiwara 0.6.2 → 0.6.4

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 (73) 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-plan.md +1 -1
  10. package/.opencode/commands/mugiwara-review.md +1 -1
  11. package/.opencode/commands/mugiwara-security.md +1 -1
  12. package/.opencode/commands/mugiwara-ship.md +1 -1
  13. package/.opencode/mugiwara-helpers.mjs +1 -1
  14. package/.opencode/plugins/mugiwara.mjs +62 -20
  15. package/AGENTS.md +16 -5
  16. package/README.md +67 -15
  17. package/content/agents/brook-healing.md +1 -1
  18. package/content/agents/chopper-checkpoint.md +1 -1
  19. package/content/agents/eval-runner.md +1 -1
  20. package/content/agents/franky-gates.md +1 -1
  21. package/content/agents/jinbe-security.md +1 -1
  22. package/content/agents/memory-keeper.md +1 -1
  23. package/content/agents/nami-planner.md +2 -2
  24. package/content/agents/resume-coordinator.md +11 -11
  25. package/content/agents/robin-reviewer.md +1 -1
  26. package/content/agents/sanji-quality.md +1 -1
  27. package/content/agents/skeptic-verifier.md +1 -1
  28. package/content/agents/usopp-brainstorm.md +2 -2
  29. package/content/agents/zoro-execution.md +3 -3
  30. package/content/skills/mugiwara-agent-security/SKILL.md +1 -18
  31. package/content/skills/mugiwara-agent-security/references/checklist.md +20 -0
  32. package/content/skills/mugiwara-brainstorm/SKILL.md +7 -1
  33. package/content/skills/mugiwara-execution/SKILL.md +14 -13
  34. package/content/skills/mugiwara-execution/references/resume-batching.md +4 -4
  35. package/content/skills/mugiwara-healing/SKILL.md +2 -37
  36. package/content/skills/mugiwara-healing/references/workers.md +38 -0
  37. package/content/skills/mugiwara-orchestration/SKILL.md +11 -8
  38. package/content/skills/mugiwara-orchestration/references/check-ins.md +26 -7
  39. package/content/skills/mugiwara-orchestration/references/closure.md +9 -0
  40. package/content/skills/mugiwara-orchestration/references/triage-escalation.md +9 -12
  41. package/content/skills/mugiwara-planning/SKILL.md +7 -11
  42. package/content/skills/mugiwara-planning/references/plan-template.md +4 -4
  43. package/content/skills/mugiwara-pr/SKILL.md +11 -19
  44. package/content/skills/mugiwara-pr/references/verdict-format.md +31 -0
  45. package/content/skills/mugiwara-resume/SKILL.md +37 -22
  46. package/content/skills/mugiwara-ship/SKILL.md +1 -23
  47. package/content/skills/mugiwara-ship/references/cleanup.md +25 -0
  48. package/content/skills/mugiwara-workflow/SKILL.md +7 -3
  49. package/content/skills/mugiwara-workflow/references/workspace-layout.md +11 -3
  50. package/content/skills/using-mugiwara/SKILL.md +1 -1
  51. package/dist/mugiwara.js +138 -42
  52. package/gemini-extension.json +1 -1
  53. package/hooks/session-start.ts +113 -2
  54. package/package.json +3 -3
  55. package/plugin.json +1 -1
  56. package/references/multi-actor.md +39 -14
  57. package/references/skill-versioning.md +3 -3
  58. package/references/token-budget.md +1 -1
  59. package/references/wave-banners.md +75 -0
  60. package/scripts/evidence.sh +9 -0
  61. package/scripts/gate-selftest.ts +154 -0
  62. package/scripts/lane-base.ts +114 -0
  63. package/scripts/lane.sh +5 -3
  64. package/scripts/lib/lane-base.sh +19 -0
  65. package/scripts/lib/patterns.sh +21 -0
  66. package/scripts/mission-report.sh +21 -5
  67. package/scripts/savepoint.sh +170 -56
  68. package/scripts/setup-fixtures.ts +108 -0
  69. package/scripts/validate-content.ts +61 -0
  70. package/src/cli.ts +5 -1
  71. package/src/installer.ts +70 -8
  72. package/src/mission.ts +37 -19
  73. package/src/targets/opencode.ts +34 -6
package/src/mission.ts CHANGED
@@ -4,12 +4,23 @@ import { existsSync, rmSync, readFileSync, readdirSync, mkdirSync, appendFileSyn
4
4
  import { join } from 'node:path';
5
5
 
6
6
  function activeActor(projectDir: string): string | null {
7
- const stateFile = join(projectDir, '.mugiwara', 'state.json');
8
- if (!existsSync(stateFile)) return null;
9
- try {
10
- const state = JSON.parse(readFileSync(stateFile, 'utf8'));
11
- return state.actor || null;
12
- } catch { return null; }
7
+ // state now lives at .mugiwara/state/<mission>/[member].json — scan the
8
+ // latest state file for its actor
9
+ const stateDir = join(projectDir, '.mugiwara', 'state');
10
+ if (!existsSync(stateDir)) return null;
11
+ const missions = readdirSync(stateDir, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name);
12
+ let latest: { actor: string; updated: number } | null = null;
13
+ for (const mission of missions) {
14
+ const d = join(stateDir, mission);
15
+ for (const f of readdirSync(d).filter(f => f.endsWith('.json'))) {
16
+ try {
17
+ const s = JSON.parse(readFileSync(join(d, f), 'utf8'));
18
+ const t = Date.parse(s.updated_at || '') || 0;
19
+ if (!latest || t > latest.updated) latest = { actor: s.actor || null, updated: t };
20
+ } catch { /* corrupt — skip */ }
21
+ }
22
+ }
23
+ return latest ? latest.actor : null;
13
24
  }
14
25
 
15
26
  export function resetMission(projectDir: string, keepLogs: boolean, force?: boolean): { removed: string[]; kept: string[]; blocked?: string } {
@@ -30,7 +41,12 @@ export function resetMission(projectDir: string, keepLogs: boolean, force?: bool
30
41
  const p = join(root, dir);
31
42
  if (existsSync(p)) { rmSync(p, { recursive: true, force: true }); removed.push(dir); }
32
43
  }
33
- // mission state files — state.json + branch-specific state-*.json
44
+ // mission state + continue folders — state/<mission>/, continue/<mission>/
45
+ for (const dir of ['state', 'continue']) {
46
+ const p = join(root, dir);
47
+ if (existsSync(p)) { rmSync(p, { recursive: true, force: true }); removed.push(dir); }
48
+ }
49
+ // legacy flat state files — state.json + branch-specific state-*.json
34
50
  for (const f of readdirSync(root)) {
35
51
  if (/^state(-.+)?\.json$/.test(f)) {
36
52
  const p = join(root, f);
@@ -81,7 +97,8 @@ export function archiveMission(projectDir: string, mission: string, opts: { dryR
81
97
  }
82
98
 
83
99
  // step results 01..05 + todos.md are evidence — kept; archive removes
84
- // only spec/review/issues/logs/continue.md
100
+ // step results 01..05 + todos.md are evidence — kept; archive removes
101
+ // only spec/review/issues/logs + continue/<mission>/ + state/<mission>/
85
102
  const resultsDir = join(root, 'results', mission);
86
103
  if (existsSync(resultsDir)) {
87
104
  for (const f of readdirSync(resultsDir)) {
@@ -121,21 +138,22 @@ export function archiveMission(projectDir: string, mission: string, opts: { dryR
121
138
  }
122
139
  }
123
140
 
124
- // continue.md is a session handoff — only remove it if it belongs to THIS
125
- // mission (its content references the mission name); otherwise leave it.
126
- const cont = join(root, 'continue.md');
127
- if (existsSync(cont)) {
128
- try {
129
- if (readFileSync(cont, 'utf8').includes(mission)) {
130
- if (!dryRun) rmSync(cont);
131
- removed.push('continue.md');
132
- }
133
- } catch { /* unreadable leave it */ }
141
+ // continue/<mission>/ is a session handoff — remove this mission's folder
142
+ const contDir = join(root, 'continue', mission);
143
+ if (existsSync(contDir)) {
144
+ if (!dryRun) rmSync(contDir, { recursive: true, force: true });
145
+ removed.push(join('continue', mission));
146
+ }
147
+ // state/<mission>/ — remove this mission's computed state (archived)
148
+ const stateDir = join(root, 'state', mission);
149
+ if (existsSync(stateDir)) {
150
+ if (!dryRun) rmSync(stateDir, { recursive: true, force: true });
151
+ removed.push(join('state', mission));
134
152
  }
135
153
 
136
154
  // kept: report + the audit-trail survivors
137
155
  if (report) kept.push(report);
138
- for (const k of ['plans', 'config', 'state.json', join('logs', 'lessons.md')]) {
156
+ for (const k of ['plans', 'config', join('logs', 'lessons.md')]) {
139
157
  if (existsSync(join(root, k))) kept.push(k);
140
158
  }
141
159
 
@@ -1,5 +1,5 @@
1
1
  // src/targets/opencode.ts
2
- import { existsSync, readdirSync, copyFileSync, mkdirSync } from 'node:fs';
2
+ import { existsSync, readdirSync, copyFileSync, mkdirSync, readFileSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { stringifyFrontmatter, type FrontmatterData } from '../frontmatter.ts';
@@ -7,6 +7,7 @@ import type { Target } from '../installer.ts';
7
7
 
8
8
  const here = dirname(fileURLToPath(import.meta.url));
9
9
  const COMMANDS_SRC = join(here, '..', '..', '.opencode', 'commands');
10
+ const BANNER_TABLE = join(here, '..', '..', 'references', 'wave-banners.md');
10
11
 
11
12
  type CrewConfig = {
12
13
  color: string;
@@ -15,23 +16,46 @@ type CrewConfig = {
15
16
  permission?: Record<string, string>;
16
17
  };
17
18
 
19
+ // Colors are fallbacks — the wave-banners table is the source of truth.
20
+ // Temperature/steps stay here (runtime tuning, not banner material).
18
21
  const CREW: Record<string, CrewConfig> = {
19
22
  'luffy-orchestrator': { color: '#ef4444', temperature: 0.2, steps: 15 },
20
- 'usopp-brainstorm': { color: '#f59e0b', temperature: 0.6, steps: 15 },
23
+ 'usopp-brainstorm': { color: '#b45309', temperature: 0.6, steps: 15 },
21
24
  'nami-planner': { color: '#f97316', temperature: 0.2, steps: 15 },
22
25
  'zoro-execution': { color: '#22c55e', temperature: 0.1, steps: 30 },
23
- 'chopper-checkpoint': { color: '#3b82f6', temperature: 0.1, steps: 15 },
24
- 'sanji-quality': { color: '#a855f7', temperature: 0.1, steps: 10 },
26
+ 'chopper-checkpoint': { color: '#60a5fa', temperature: 0.1, steps: 15 },
27
+ 'sanji-quality': { color: '#facc15', temperature: 0.1, steps: 10 },
25
28
  'franky-gates': { color: '#06b6d4', temperature: 0.1, steps: 10 },
26
29
  'robin-reviewer': { color: '#8b5cf6', temperature: 0.2, steps: 15 },
27
30
  'jinbe-security': { color: '#6366f1', temperature: 0.2, steps: 15 },
28
- 'brook-healing': { color: '#ec4899', temperature: 0.1, steps: 20 },
31
+ 'brook-healing': { color: '#2dd4bf', temperature: 0.1, steps: 20 },
29
32
  'skeptic-verifier': { color: '#64748b', temperature: 0.1, steps: 12 },
30
33
  'eval-runner': { color: '#14b8a6', temperature: 0.2, steps: 15 },
31
34
  'resume-coordinator': { color: '#d97706', temperature: 0.2, steps: 10 },
32
35
  'memory-keeper': { color: '#d946ef', temperature: 0.2, steps: 8 },
33
36
  };
34
37
 
38
+ // Crew colors from the wave-banners table (single source of truth). Returns
39
+ // {} on any failure — callers fall back to the CREW map. Same table shape as
40
+ // the opencode plugin parses: | agent-id | role | hex | ansi-256 | emoji |
41
+ function readBannerColors(): Record<string, string> {
42
+ try {
43
+ if (!existsSync(BANNER_TABLE)) return {};
44
+ const text = readFileSync(BANNER_TABLE, 'utf8');
45
+ // null-prototype + CRLF-tolerant: agent ids are trusted repo content, but
46
+ // a future `__proto__` id must never write the object's prototype
47
+ const colors: Record<string, string> = Object.create(null);
48
+ for (const m of text.matchAll(/^\| ([\w-]+) \| [^|]+ \| (#[0-9a-f]{6}) \| (\d+) \| (\S+) \|\r?$/gm)) {
49
+ colors[m[1]] = m[2];
50
+ }
51
+ return colors;
52
+ } catch {
53
+ return {};
54
+ }
55
+ }
56
+
57
+ const BANNER_COLORS = readBannerColors();
58
+
35
59
  // write-scope is the single source of truth (content/agents/*.md frontmatter),
36
60
  // but it is a RULE for user-facing crew agents: they run inline in the main
37
61
  // thread, so a runtime permission bound to the active-agent identity would
@@ -50,8 +74,12 @@ function permissionFromScope(scope: string | undefined): Record<string, string |
50
74
  function agentFrontmatter(name: string, description: string, internal: boolean, writeScope?: string) {
51
75
  const crew = CREW[name];
52
76
  const lines = [`description: ${description}`, `mode: all`];
77
+ // color: wave-banners table wins; CREW fallback for agents the table lacks.
78
+ // temperature/steps only exist for CREW members (runtime tuning).
79
+ const color = BANNER_COLORS[name] ?? crew?.color;
80
+ if (color) lines.push(`color: '${color}'`);
53
81
  if (crew) {
54
- lines.push(`color: '${crew.color}'`, `temperature: ${crew.temperature}`, `steps: ${crew.steps}`);
82
+ lines.push(`temperature: ${crew.temperature}`, `steps: ${crew.steps}`);
55
83
  const perm = internal ? permissionFromScope(writeScope) : undefined;
56
84
  if (perm) {
57
85
  lines.push('permission:');