@ionivetech/mugiwara 0.5.1 → 0.5.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.
- package/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +18 -0
- package/.claude-plugin/plugin.json +64 -0
- package/.claude-plugin/sync.sh +18 -0
- package/.codex-plugin/plugin.json +18 -0
- package/.cursor-plugin/plugin.json +19 -0
- package/.kimi-plugin/plugin.json +18 -0
- package/.opencode/commands/mugiwara.md +13 -7
- package/.opencode/commands/using-mugiwara.md +26 -0
- package/.opencode/plugins/mugiwara-helpers.mjs +90 -0
- package/.opencode/plugins/mugiwara.mjs +10 -107
- package/AGENTS.md +183 -0
- package/GEMINI.md +12 -0
- package/README.md +86 -56
- package/content/agents/luffy-orchestrator.md +1 -1
- package/content/agents/zoro-execution.md +1 -1
- package/content/skills/mugiwara-execution/SKILL.md +2 -5
- package/content/skills/{mugiwara-proof-order/SKILL.md → mugiwara-execution/references/tdd.md} +4 -7
- package/content/skills/using-mugiwara/SKILL.md +52 -0
- package/dist/mugiwara.js +35 -4
- package/docs/adoption-guide.md +1 -1
- package/docs/agents.md +5 -3
- package/docs/claude-setup.md +1 -1
- package/docs/developer-onboarding.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/index.md +1 -1
- package/docs/install-antigravity.md +45 -0
- package/docs/install-claude.md +75 -0
- package/docs/install-cli.md +103 -0
- package/docs/install-codex.md +44 -0
- package/docs/install-copilot.md +45 -0
- package/docs/install-cursor.md +45 -0
- package/docs/install-gemini.md +44 -0
- package/docs/install-kimi.md +45 -0
- package/docs/install-opencode.md +129 -0
- package/docs/install-pi.md +46 -0
- package/docs/install.md +53 -0
- package/docs/opencode-setup.md +1 -1
- package/docs/skills.md +1 -1
- package/evals/cases/routing-claim-audit.json +1 -1
- package/evals/cases/routing-using-mugiwara.json +25 -0
- package/evals/floor.json +1 -1
- package/gemini-extension.json +6 -0
- package/hooks/hooks.json +11 -0
- package/hooks/mugiwara-mode-tracker.ts +80 -0
- package/hooks/session-start.ts +1 -1
- package/package.json +13 -2
- package/plugin.json +15 -0
- package/src/targets/opencode.ts +39 -3
- package/.opencode/commands/mugiwara-mode.md +0 -6
- package/content/agents/using-mugiwara.md +0 -44
- package/content/skills/mugiwara-proof-order/references/proof-order-examples.md +0 -62
- package/evals/cases/routing-proof-order.json +0 -25
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mugiwara",
|
|
3
|
+
"interface": {
|
|
4
|
+
"displayName": "Mugiwara"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "mugiwara",
|
|
9
|
+
"source": {
|
|
10
|
+
"source": "url",
|
|
11
|
+
"url": "./"
|
|
12
|
+
},
|
|
13
|
+
"policy": {
|
|
14
|
+
"installation": "AVAILABLE",
|
|
15
|
+
"authentication": "ON_INSTALL"
|
|
16
|
+
},
|
|
17
|
+
"category": "Developer Tools"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mugiwara",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "ionive"
|
|
5
|
+
},
|
|
6
|
+
"metadata": {
|
|
7
|
+
"description": "The Straw Hat crew for AI agents",
|
|
8
|
+
"version": "0.5.3"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "mugiwara",
|
|
13
|
+
"description": "The Straw Hat crew of AI agents and skills: brainstorm, plan, execute, checkpoint, quality, gates, review, security, healing.",
|
|
14
|
+
"version": "0.5.3",
|
|
15
|
+
"source": "./"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mugiwara",
|
|
3
|
+
"displayName": "Mugiwara",
|
|
4
|
+
"version": "0.5.3",
|
|
5
|
+
"description": "The Straw Hat crew of AI agents and skills: brainstorm, plan, execute, checkpoint, quality, gates, review, security, healing.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "ionive"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"homepage": "https://github.com/ionivetech/mugiwara",
|
|
11
|
+
"repository": "https://github.com/ionivetech/mugiwara",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"agents",
|
|
14
|
+
"skills",
|
|
15
|
+
"workflow",
|
|
16
|
+
"multi-agent"
|
|
17
|
+
],
|
|
18
|
+
"metadata": {
|
|
19
|
+
"agents": [
|
|
20
|
+
"luffy-orchestrator",
|
|
21
|
+
"usopp-brainstorm",
|
|
22
|
+
"nami-planner",
|
|
23
|
+
"zoro-execution",
|
|
24
|
+
"chopper-checkpoint",
|
|
25
|
+
"sanji-quality",
|
|
26
|
+
"franky-gates",
|
|
27
|
+
"robin-reviewer",
|
|
28
|
+
"jinbe-security",
|
|
29
|
+
"brook-healing",
|
|
30
|
+
"skeptic-verifier",
|
|
31
|
+
"eval-runner",
|
|
32
|
+
"resume-coordinator",
|
|
33
|
+
"memory-keeper"
|
|
34
|
+
],
|
|
35
|
+
"skills": [
|
|
36
|
+
"mugiwara-agent-security",
|
|
37
|
+
"mugiwara-backend",
|
|
38
|
+
"mugiwara-brainstorm",
|
|
39
|
+
"mugiwara-checkpoint",
|
|
40
|
+
"mugiwara-claim-audit",
|
|
41
|
+
"mugiwara-context-budget",
|
|
42
|
+
"mugiwara-contract-first",
|
|
43
|
+
"mugiwara-execution",
|
|
44
|
+
"mugiwara-frontend",
|
|
45
|
+
"mugiwara-gates",
|
|
46
|
+
"mugiwara-git",
|
|
47
|
+
"mugiwara-healing",
|
|
48
|
+
"mugiwara-lessons",
|
|
49
|
+
"mugiwara-orchestration",
|
|
50
|
+
"mugiwara-planning",
|
|
51
|
+
"mugiwara-pr",
|
|
52
|
+
"mugiwara-quality",
|
|
53
|
+
"mugiwara-resume",
|
|
54
|
+
"mugiwara-review",
|
|
55
|
+
"mugiwara-root-cause",
|
|
56
|
+
"mugiwara-security",
|
|
57
|
+
"mugiwara-ship",
|
|
58
|
+
"mugiwara-sunset",
|
|
59
|
+
"mugiwara-testcases",
|
|
60
|
+
"mugiwara-workflow",
|
|
61
|
+
"using-mugiwara"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# content/ is the single source of truth. Root agents/ and skills/ are
|
|
3
|
+
# symlinks to it so the Claude Code marketplace plugin (which reads from the
|
|
4
|
+
# plugin root) and the npm package consumers see the same files.
|
|
5
|
+
set -eu
|
|
6
|
+
ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
|
|
7
|
+
|
|
8
|
+
for pair in "agents:content/agents" "skills:content/skills"; do
|
|
9
|
+
link="${pair%%:*}"
|
|
10
|
+
target="${pair#*:}"
|
|
11
|
+
if [ -L "$ROOT/$link" ]; then
|
|
12
|
+
echo "ok $link -> $target"
|
|
13
|
+
else
|
|
14
|
+
rm -rf "$ROOT/$link"
|
|
15
|
+
ln -s "$target" "$ROOT/$link"
|
|
16
|
+
echo "linked $link -> $target"
|
|
17
|
+
fi
|
|
18
|
+
done
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mugiwara",
|
|
3
|
+
"version": "0.5.3",
|
|
4
|
+
"description": "The Straw Hat crew of AI agents and skills: brainstorm, plan, execute, checkpoint, quality, gates, review, security, healing.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "ionive"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/ionivetech/mugiwara",
|
|
9
|
+
"repository": "https://github.com/ionivetech/mugiwara",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"agents",
|
|
13
|
+
"skills",
|
|
14
|
+
"workflow",
|
|
15
|
+
"multi-agent"
|
|
16
|
+
],
|
|
17
|
+
"skills": "./content/skills/"
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mugiwara",
|
|
3
|
+
"displayName": "Mugiwara",
|
|
4
|
+
"description": "The Straw Hat crew of AI agents and skills: brainstorm, plan, execute, checkpoint, quality, gates, review, security, healing.",
|
|
5
|
+
"version": "0.5.3",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "ionive"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/ionivetech/mugiwara",
|
|
10
|
+
"repository": "https://github.com/ionivetech/mugiwara",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"agents",
|
|
14
|
+
"skills",
|
|
15
|
+
"workflow",
|
|
16
|
+
"multi-agent"
|
|
17
|
+
],
|
|
18
|
+
"skills": "./content/skills/"
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mugiwara",
|
|
3
|
+
"version": "0.5.3",
|
|
4
|
+
"description": "The Straw Hat crew of AI agents and skills: brainstorm, plan, execute, checkpoint, quality, gates, review, security, healing.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "ionive"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/ionivetech/mugiwara",
|
|
9
|
+
"repository": "https://github.com/ionivetech/mugiwara",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"agents",
|
|
13
|
+
"skills",
|
|
14
|
+
"workflow",
|
|
15
|
+
"multi-agent"
|
|
16
|
+
],
|
|
17
|
+
"skills": "./content/skills/"
|
|
18
|
+
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Run the mugiwara crew workflow
|
|
2
|
+
description: Run the mugiwara crew workflow or switch runtime mode (guided | semi | auto)
|
|
3
3
|
---
|
|
4
|
-
Mugiwara
|
|
4
|
+
Mugiwara mode: $ARGUMENTS
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
2. The pipeline flows: Luffy triage -> Usopp brainstorm (when needed) -> Nami planning -> Zoro execution -> Chopper checkpoint -> Sanji quality -> Franky gates -> Robin/Jinbe review -> Brook healing -> Luffy closure.
|
|
8
|
-
3. Subagents are used only for parallel work (Zoro's [PARALLEL] workers, Robin + Jinbe concurrent review, independent re-run checks).
|
|
9
|
-
4. Switch runtime mode with `/mugiwara-mode` (guided | semi | auto). Default is guided.
|
|
6
|
+
Run the crew pipeline inline. To switch autonomy mode:
|
|
10
7
|
|
|
11
|
-
|
|
8
|
+
```
|
|
9
|
+
/mugiwara guided # human decides every GO
|
|
10
|
+
/mugiwara semi # auto branch + commit, plan needs GO
|
|
11
|
+
/mugiwara auto # hands-off except high-risk
|
|
12
|
+
/mugiwara # show current mode
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The flip applies from the next wave. Valid modes: guided, semi, auto.
|
|
16
|
+
|
|
17
|
+
For workflow: embody `using-mugiwara` inline to classify and route. See skills/mugiwara-workflow for the full pipeline.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Front-door router: explain how mugiwara works, classify a mission, or route to the right crew member
|
|
3
|
+
---
|
|
4
|
+
Using Mugiwara: $ARGUMENTS
|
|
5
|
+
|
|
6
|
+
Mugiwara crew available. The workflow auto-activates for non-trivial requests.
|
|
7
|
+
|
|
8
|
+
## How it works
|
|
9
|
+
|
|
10
|
+
- 14 agents: Luffy (triage), Usopp (brainstorm), Nami (plan), Zoro (execute), Chopper (audit), Sanji (quality), Franky (gates), Robin (review), Jinbe (security), Brook (heal), Skeptic (verify), Eval Runner, Resume, Memory
|
|
11
|
+
- 26 skills — one per crew role + domain skills (frontend, backend, git, security)
|
|
12
|
+
- 9-wave pipeline runs inline in the main conversation
|
|
13
|
+
- Evidence over claims at every wave
|
|
14
|
+
- Autonomy modes: guided, semi, auto
|
|
15
|
+
|
|
16
|
+
## What to do
|
|
17
|
+
|
|
18
|
+
1. If the user asks how mugiwara works — summarize in 3 lines.
|
|
19
|
+
2. If the user gives a task — classify (Trivial/Explicit/Exploratory/Open-ended/Ambiguous) and route:
|
|
20
|
+
- Clear, small → route to nami-planner or zoro-execution directly.
|
|
21
|
+
- Vague, needs direction → route to usopp-brainstorm.
|
|
22
|
+
- Anything else → route to luffy-orchestrator (full triage).
|
|
23
|
+
- Specialized: review → robin-reviewer, security → jinbe-security, etc.
|
|
24
|
+
3. Record the route in `.mugiwara/logs/`.
|
|
25
|
+
|
|
26
|
+
Skills: mugiwara-workflow, mugiwara-orchestration. See skills/mugiwara-workflow for the full pipeline.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// mugiwara helpers — extracted from mugiwara.mjs so the plugin module has a
|
|
2
|
+
// single export. OpenCode's legacy loader calls every exported function as a
|
|
3
|
+
// plugin; keeping helpers in a separate module avoids them being invoked with
|
|
4
|
+
// the plugin context as first argument (same pattern ponytail uses).
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, appendFileSync, lstatSync, renameSync } from 'node:fs';
|
|
7
|
+
import { homedir, tmpdir } from 'node:os';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
|
|
10
|
+
export const VALID_MODES = new Set(['guided', 'semi', 'auto']);
|
|
11
|
+
|
|
12
|
+
export function readMode({ projectDir = process.cwd(), home = homedir() } = {}) {
|
|
13
|
+
const readValue = (dir) => {
|
|
14
|
+
const file = join(dir, '.mugiwara', 'config');
|
|
15
|
+
if (!existsSync(file)) return undefined;
|
|
16
|
+
for (const line of readFileSync(file, 'utf8').split(/\r?\n/)) {
|
|
17
|
+
const t = line.trim();
|
|
18
|
+
if (!t || t.startsWith('#')) continue;
|
|
19
|
+
const [k, v] = line.split('=').map((s) => s.trim());
|
|
20
|
+
if (k !== 'mode') continue;
|
|
21
|
+
return VALID_MODES.has(v) ? v : 'INVALID';
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
};
|
|
25
|
+
const proj = readValue(projectDir);
|
|
26
|
+
if (proj === 'INVALID') return 'guided';
|
|
27
|
+
if (proj) return proj;
|
|
28
|
+
const glob = readValue(home);
|
|
29
|
+
if (glob === 'INVALID') return 'guided';
|
|
30
|
+
if (glob) return glob;
|
|
31
|
+
return 'guided';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function parseModeChange(promptRaw) {
|
|
35
|
+
if (typeof promptRaw !== 'string') return null;
|
|
36
|
+
let prompt = promptRaw.trim();
|
|
37
|
+
const wrapped = /^(["'`])([\s\S]*)\1$/.exec(prompt);
|
|
38
|
+
if (wrapped) prompt = wrapped[2].trim();
|
|
39
|
+
prompt = prompt.toLowerCase();
|
|
40
|
+
if (!prompt) return null;
|
|
41
|
+
|
|
42
|
+
const tplSet = /^(?:set |)mugiwara mode:[ \t]*(\S*)/.exec(prompt);
|
|
43
|
+
if (tplSet && VALID_MODES.has(tplSet[1])) return tplSet[1];
|
|
44
|
+
|
|
45
|
+
const slash = /^\/(?:mugiwara[-\s]?)?mode[ \t]+(\S*)/.exec(prompt);
|
|
46
|
+
if (slash && VALID_MODES.has(slash[1])) return slash[1];
|
|
47
|
+
|
|
48
|
+
const slashMain = /^\/mugiwara[ \t]+(\S*)/.exec(prompt);
|
|
49
|
+
if (slashMain && VALID_MODES.has(slashMain[1])) return slashMain[1];
|
|
50
|
+
|
|
51
|
+
const natural = /^mugiwara mode[ \t]+(\S*)/.exec(prompt);
|
|
52
|
+
if (natural && VALID_MODES.has(natural[1])) return natural[1];
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function assertNotSymlink(file) {
|
|
58
|
+
if (!existsSync(file)) return;
|
|
59
|
+
try {
|
|
60
|
+
const st = lstatSync(file);
|
|
61
|
+
if (st.isSymbolicLink()) throw new Error(`refusing to follow symlink: ${file}`);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
if (e.code === 'ENOENT') return;
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function applyModeChange(mode, { projectDir = process.cwd(), home = homedir() } = {}) {
|
|
69
|
+
if (!VALID_MODES.has(mode)) return;
|
|
70
|
+
const dir = join(projectDir, '.mugiwara');
|
|
71
|
+
const file = join(dir, 'config');
|
|
72
|
+
assertNotSymlink(file);
|
|
73
|
+
mkdirSync(dir, { recursive: true });
|
|
74
|
+
const lines = [];
|
|
75
|
+
if (existsSync(file)) {
|
|
76
|
+
for (const line of readFileSync(file, 'utf8').split(/\r?\n/)) {
|
|
77
|
+
const t = line.trim();
|
|
78
|
+
if (t.startsWith('mode=')) continue;
|
|
79
|
+
lines.push(line);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
lines.push(`mode=${mode}`);
|
|
83
|
+
const body = lines.filter((l, i) => !(l === '' && (i === lines.length - 1 || i === 0))).join('\n') + '\n';
|
|
84
|
+
const tmp = join(tmpdir(), `mugiwara-config-${process.pid}-${Date.now()}.tmp`);
|
|
85
|
+
writeFileSync(tmp, body);
|
|
86
|
+
renameSync(tmp, file);
|
|
87
|
+
const log = join(dir, 'logs', `${new Date().toISOString().slice(0, 10)}-mode-flip.md`);
|
|
88
|
+
mkdirSync(dirname(log), { recursive: true });
|
|
89
|
+
appendFileSync(log, `| ${new Date().toISOString()} | mode flip | guided/semi/auto -> ${mode} | user |\n`);
|
|
90
|
+
}
|
|
@@ -1,59 +1,31 @@
|
|
|
1
1
|
// mugiwara — OpenCode plugin.
|
|
2
2
|
//
|
|
3
|
-
// Registers
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// Registers skills/agents via config hook (superpowers pattern: one plugin,
|
|
4
|
+
// zero file-copy), announces the crew at session start, and handles runtime
|
|
5
|
+
// mode switching. Helpers live in mugiwara-helpers.mjs — this module has a
|
|
6
|
+
// single export because OpenCode's legacy loader calls every exported function
|
|
7
|
+
// as a plugin (same constraint ponytail documents).
|
|
6
8
|
//
|
|
7
9
|
// Install: add to opencode.json
|
|
8
10
|
// { "plugin": ["@ionivetech/mugiwara"] }
|
|
9
11
|
// or from the git repo:
|
|
10
12
|
// { "plugin": ["mugiwara@git+https://github.com/ionivetech/mugiwara.git"] }
|
|
11
13
|
|
|
12
|
-
import { existsSync, readdirSync, readFileSync
|
|
13
|
-
import { homedir, tmpdir } from 'node:os';
|
|
14
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
14
15
|
import { dirname, join } from 'node:path';
|
|
15
16
|
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { readMode, parseModeChange, applyModeChange } from './mugiwara-helpers.mjs';
|
|
16
18
|
|
|
17
19
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
20
|
|
|
19
|
-
const VALID_MODES = new Set(['guided', 'semi', 'auto']);
|
|
20
|
-
|
|
21
|
-
// Read the runtime mode: `.mugiwara/config` (project) wins over
|
|
22
|
-
// `~/.mugiwara/config` (global) per key; a missing key or a value outside the
|
|
23
|
-
// enum falls back to `guided`. Never creates files on read.
|
|
24
|
-
export function readMode({ projectDir = process.cwd(), home = homedir() } = {}) {
|
|
25
|
-
const readValue = (dir) => {
|
|
26
|
-
const file = join(dir, '.mugiwara', 'config');
|
|
27
|
-
if (!existsSync(file)) return undefined;
|
|
28
|
-
for (const line of readFileSync(file, 'utf8').split(/\r?\n/)) {
|
|
29
|
-
const t = line.trim();
|
|
30
|
-
if (!t || t.startsWith('#')) continue;
|
|
31
|
-
const [k, v] = t.split('=').map((s) => s.trim());
|
|
32
|
-
if (k !== 'mode') continue;
|
|
33
|
-
return VALID_MODES.has(v) ? v : 'INVALID';
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
};
|
|
37
|
-
const proj = readValue(projectDir);
|
|
38
|
-
if (proj === 'INVALID') return 'guided';
|
|
39
|
-
if (proj) return proj;
|
|
40
|
-
const glob = readValue(home);
|
|
41
|
-
if (glob === 'INVALID') return 'guided';
|
|
42
|
-
if (glob) return glob;
|
|
43
|
-
return 'guided';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
21
|
const contentDir = join(__dirname, '..', '..', 'content');
|
|
47
22
|
const skillsDir = join(contentDir, 'skills');
|
|
48
23
|
const agentsDir = join(contentDir, 'agents');
|
|
49
24
|
|
|
50
25
|
const ANNOUNCE =
|
|
51
|
-
"Mugiwara crew available. The workflow auto-activates for non-trivial requests — no need to call `/using-mugiwara` at session start (it
|
|
26
|
+
"Mugiwara crew available. The workflow auto-activates for non-trivial requests — no need to call `/using-mugiwara` at session start (it is an optional router). Run the crew pipeline inline in the main conversation: embody ONE crew role at a time using its skill, wait for its report, then move to the next. Never Task-dispatch a crew member — the crew runs in the main thread; subagents only for [PARALLEL] task batches, concurrent review/security, and independent re-run checks. Progress shows as checkpoint reports at wave/stage boundaries, pausing on failure or risk. Switch mode with `/mugiwara` (guided|semi|auto). See skills/mugiwara-workflow.";
|
|
52
27
|
|
|
53
|
-
// OpenCode per-agent tuning. Content stays portable markdown; these knobs
|
|
54
|
-
// (color, temperature, permission, steps) are opencode-only.
|
|
55
28
|
const CREW = {
|
|
56
|
-
'using-mugiwara': { color: '#84cc16', temperature: 0.2, steps: 10 },
|
|
57
29
|
'luffy-orchestrator': { color: '#ef4444', temperature: 0.2, steps: 15 },
|
|
58
30
|
'usopp-brainstorm': { color: '#f59e0b', temperature: 0.6, steps: 15 },
|
|
59
31
|
'nami-planner': { color: '#f97316', temperature: 0.2, steps: 15 },
|
|
@@ -106,73 +78,9 @@ function readAgents() {
|
|
|
106
78
|
return agents;
|
|
107
79
|
}
|
|
108
80
|
|
|
109
|
-
// Parse a user prompt for a mugiwara mode change. Returns the new mode or null.
|
|
110
|
-
// Mirrors the caveman plugin pattern: opencode expands `/mugiwara-mode <level>`
|
|
111
|
-
// into the command file's body ("Set mugiwara mode: <level>...") before
|
|
112
|
-
// chat.message fires, so parse the template's first line too. The slash command
|
|
113
|
-
// and natural-language phrase must sit at the START of the prompt — a bare
|
|
114
|
-
// mention mid-message (pasted untrusted content) never flips autonomy.
|
|
115
|
-
export function parseModeChange(promptRaw) {
|
|
116
|
-
if (typeof promptRaw !== 'string') return null;
|
|
117
|
-
let prompt = promptRaw.trim();
|
|
118
|
-
const wrapped = /^(["'`])([\s\S]*)\1$/.exec(prompt);
|
|
119
|
-
if (wrapped) prompt = wrapped[2].trim();
|
|
120
|
-
prompt = prompt.toLowerCase();
|
|
121
|
-
if (!prompt) return null;
|
|
122
|
-
|
|
123
|
-
const tpl = /^set mugiwara mode:[ \t]*(\S*)/.exec(prompt);
|
|
124
|
-
if (tpl && VALID_MODES.has(tpl[1])) return tpl[1];
|
|
125
|
-
|
|
126
|
-
const slash = /^\/(mugiwara[-\s]?)mode[ \t]+(\S*)/.exec(prompt);
|
|
127
|
-
if (slash && VALID_MODES.has(slash[2])) return slash[2];
|
|
128
|
-
|
|
129
|
-
const natural = /^mugiwara mode[ \t]+(\S*)/.exec(prompt);
|
|
130
|
-
if (natural && VALID_MODES.has(natural[1])) return natural[1];
|
|
131
|
-
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Refuse to follow a symlinked .mugiwara/config — a malicious repo could point
|
|
136
|
-
// it at ~/.bashrc and any mode flip would overwrite the target.
|
|
137
|
-
function assertNotSymlink(file) {
|
|
138
|
-
if (!existsSync(file)) return;
|
|
139
|
-
try {
|
|
140
|
-
const st = lstatSync(file);
|
|
141
|
-
if (st.isSymbolicLink()) throw new Error(`refusing to follow symlink: ${file}`);
|
|
142
|
-
} catch (e) {
|
|
143
|
-
if (e.code === 'ENOENT') return;
|
|
144
|
-
throw e;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Write `.mugiwara/config` (project) and append a decision-log row.
|
|
149
|
-
// Atomic: write a temp file next to the target, then rename over it, so a
|
|
150
|
-
// crash mid-write never truncates the config (keeps branch/commit keys).
|
|
151
|
-
export function applyModeChange(mode, { projectDir = process.cwd(), home = homedir() } = {}) {
|
|
152
|
-
if (!VALID_MODES.has(mode)) return;
|
|
153
|
-
const dir = join(projectDir, '.mugiwara');
|
|
154
|
-
const file = join(dir, 'config');
|
|
155
|
-
assertNotSymlink(file);
|
|
156
|
-
mkdirSync(dir, { recursive: true });
|
|
157
|
-
const lines = [];
|
|
158
|
-
if (existsSync(file)) {
|
|
159
|
-
for (const line of readFileSync(file, 'utf8').split(/\r?\n/)) {
|
|
160
|
-
const t = line.trim();
|
|
161
|
-
if (t.startsWith('mode=')) continue;
|
|
162
|
-
lines.push(line);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
lines.push(`mode=${mode}`);
|
|
166
|
-
const body = lines.filter((l, i) => !(l === '' && (i === lines.length - 1 || i === 0))).join('\n') + '\n';
|
|
167
|
-
const tmp = join(tmpdir(), `mugiwara-config-${process.pid}-${Date.now()}.tmp`);
|
|
168
|
-
writeFileSync(tmp, body);
|
|
169
|
-
renameSync(tmp, file);
|
|
170
|
-
const log = join(dir, 'logs', `${new Date().toISOString().slice(0, 10)}-mode-flip.md`);
|
|
171
|
-
mkdirSync(dirname(log), { recursive: true });
|
|
172
|
-
appendFileSync(log, `| ${new Date().toISOString()} | mode flip | guided/semi/auto -> ${mode} | user |\n`);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
81
|
export default async () => ({
|
|
82
|
+
dispose: () => {},
|
|
83
|
+
|
|
176
84
|
config: (config) => {
|
|
177
85
|
config.skills = config.skills || {};
|
|
178
86
|
config.skills.paths = config.skills.paths || [];
|
|
@@ -185,8 +93,6 @@ export default async () => ({
|
|
|
185
93
|
}
|
|
186
94
|
},
|
|
187
95
|
|
|
188
|
-
// Intercept user messages to detect `/mugiwara-mode` and natural-language
|
|
189
|
-
// mode toggles; write `.mugiwara/config` so the next wave reads the new level.
|
|
190
96
|
'chat.message': async (_input, output) => {
|
|
191
97
|
if (!output) return;
|
|
192
98
|
if (typeof output === 'string') {
|
|
@@ -204,9 +110,6 @@ export default async () => ({
|
|
|
204
110
|
}
|
|
205
111
|
},
|
|
206
112
|
|
|
207
|
-
// ponytail-proven hook; appends the announce string to the system prompt.
|
|
208
|
-
// Dedupes: repeated transforms (model switch/compaction) must not grow the
|
|
209
|
-
// prompt unbounded — only append once per string content.
|
|
210
113
|
'experimental.chat.system.transform': async (_input, output) => {
|
|
211
114
|
if (!output.system.some((s) => s.includes('Mugiwara crew available'))) {
|
|
212
115
|
if (output.system.length > 0) {
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Mugiwara Development Standards
|
|
2
|
+
|
|
3
|
+
Rules for working on this repo. Follow these or validation fails.
|
|
4
|
+
|
|
5
|
+
## Language
|
|
6
|
+
|
|
7
|
+
**All files must be English.** Skills, agents, docs, scripts, references — no
|
|
8
|
+
exceptions. Comments in source code should be English. This conversation is the
|
|
9
|
+
only place Indonesian is acceptable.
|
|
10
|
+
|
|
11
|
+
## Trunk-based branching strategy
|
|
12
|
+
|
|
13
|
+
`main` is always stable and releasable. No commits directly to `main`.
|
|
14
|
+
|
|
15
|
+
| Branch | Purpose | Lifetime |
|
|
16
|
+
|--------|---------|----------|
|
|
17
|
+
| `main` | Source of truth. Always passes CI. | Permanent |
|
|
18
|
+
| `feat/*`, `fix/*`, `chore/*`, `docs/*`, `refactor/*` | Feature work from main, PR to main | Short-lived, delete after merge |
|
|
19
|
+
| `release/vX.Y.Z` | Created from main for publishing. Archival. | Short-lived, delete after tag |
|
|
20
|
+
|
|
21
|
+
### Flow
|
|
22
|
+
|
|
23
|
+
1. Branch from `main`
|
|
24
|
+
2. Work + validate locally
|
|
25
|
+
3. PR to `main` — CI must pass (all gates)
|
|
26
|
+
4. Merge to `main`
|
|
27
|
+
5. Run "Manual Release" workflow on GitHub Actions → creates tag + npm publish + GitHub Release
|
|
28
|
+
6. Release branch (`release/vX.Y.Z`) created from the release commit — archival
|
|
29
|
+
|
|
30
|
+
### Branch naming
|
|
31
|
+
|
|
32
|
+
| Type | Pattern | Example |
|
|
33
|
+
|------|---------|---------|
|
|
34
|
+
| Feature | `feat/<description>` | `feat/add-mcp-audit-skill` |
|
|
35
|
+
| Fix | `fix/<description>` | `fix/token-budget-overflow` |
|
|
36
|
+
| Chore | `chore/<description>` | `chore/update-deps` |
|
|
37
|
+
| Docs | `docs/<description>` | `docs/rewrite-readme` |
|
|
38
|
+
| Refactor | `refactor/<description>` | `refactor/split-checkpoint` |
|
|
39
|
+
| Release | `release/vX.Y.Z` | `release/v0.5.0` |
|
|
40
|
+
|
|
41
|
+
### Version bumping
|
|
42
|
+
|
|
43
|
+
| Trigger | Bump |
|
|
44
|
+
|---------|------|
|
|
45
|
+
| Bug fix, no new features | `patch` |
|
|
46
|
+
| New feature, backward-compatible | `minor` |
|
|
47
|
+
| Breaking change | `major` |
|
|
48
|
+
|
|
49
|
+
All version bumps via the Manual Release workflow — never manually edited.
|
|
50
|
+
|
|
51
|
+
## Validation before commit (CI gates)
|
|
52
|
+
|
|
53
|
+
All must pass on every PR. Run locally with:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
bun run gate # everything CI runs on a PR
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Per-PR gates
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
bun run typecheck # TypeScript
|
|
63
|
+
bun run test # 70 tests
|
|
64
|
+
bun run build # dist/
|
|
65
|
+
bun scripts/validate-content.ts --check-manifest --check-docs # content + manifest + docs drift + budget + section length + description hygiene
|
|
66
|
+
bun scripts/run-evals.ts # behavioral evals
|
|
67
|
+
bun scripts/retrieval-eval.ts # retrieval ranking + floor ratchet
|
|
68
|
+
bun scripts/verify-install.ts # G1: resolve all references/*.md pointers after install
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Pre-release / weekly gates
|
|
72
|
+
|
|
73
|
+
| Gate | Script | Purpose |
|
|
74
|
+
|------|--------|---------|
|
|
75
|
+
| G3 meta-eval | `bun scripts/gate-selftest.ts` | Prove every gate can fail — catch rotted gates |
|
|
76
|
+
|
|
77
|
+
### Where each gate runs
|
|
78
|
+
|
|
79
|
+
| Gate | Pre-commit | Every PR | Pre-release | Weekly |
|
|
80
|
+
|------|:---:|:---:|:---:|:---:|
|
|
81
|
+
| typecheck, test, build | ✅ | ✅ | ✅ | |
|
|
82
|
+
| validate-content (+manifest, docs, sections, descriptions) | ✅ | ✅ | ✅ | |
|
|
83
|
+
| run-evals | | ✅ | ✅ | |
|
|
84
|
+
| retrieval-eval + ratchet | | ✅ | ✅ | |
|
|
85
|
+
| **G1 verify-install** | | ✅ | ✅ | |
|
|
86
|
+
| **G2 computed fixtures** | ✅ | ✅ | ✅ | |
|
|
87
|
+
| **G3 gate-selftest** | | | ✅ | ✅ |
|
|
88
|
+
|
|
89
|
+
### Gate design
|
|
90
|
+
|
|
91
|
+
**A gate that cannot fail is not a gate.** Every gate must have a corresponding
|
|
92
|
+
mutation in `scripts/gate-selftest.ts` proving it goes red. Adding a gate
|
|
93
|
+
without adding its mutation is an incomplete change.
|
|
94
|
+
|
|
95
|
+
**Every production defect adds a gate before the fix merges.** Record the defect
|
|
96
|
+
class, not just the instance. The fix closes one instance — the gate closes the
|
|
97
|
+
class.
|
|
98
|
+
|
|
99
|
+
No PR merges without all green CI.
|
|
100
|
+
|
|
101
|
+
## Skill standards
|
|
102
|
+
|
|
103
|
+
Every skill is `content/skills/<name>/SKILL.md`:
|
|
104
|
+
|
|
105
|
+
| Rule | Limit | Validated |
|
|
106
|
+
|------|-------|:---:|
|
|
107
|
+
| Frontmatter `name` matches directory | exact match | ✅ |
|
|
108
|
+
| `description` length | 20–220 chars | ✅ |
|
|
109
|
+
| `description` content | trigger keywords + disambiguators only, no procedure | ✅ |
|
|
110
|
+
| Body lines | ≤120 | ✅ |
|
|
111
|
+
| `## Skip when` block | 1–4 bullets, numeric threshold | ✅ |
|
|
112
|
+
| Sections >15–20 lines | move to `references/<topic>.md`, one-line pointer in body | ✅ |
|
|
113
|
+
| Language | **English only** | ✅ |
|
|
114
|
+
|
|
115
|
+
## Agent standards
|
|
116
|
+
|
|
117
|
+
Every agent is `content/agents/<name>.md`:
|
|
118
|
+
|
|
119
|
+
| Rule | Requirement |
|
|
120
|
+
|------|------------|
|
|
121
|
+
| Frontmatter `name` matches filename | exact match (without `.md`) |
|
|
122
|
+
| `description` | pointer line: "Persona for <skill>. <role summary>." |
|
|
123
|
+
| `skills` | list of held skills, comma-separated |
|
|
124
|
+
| `permissions` | optional: `read-only`, `can-write`, `no-network` |
|
|
125
|
+
| Language | **English only** |
|
|
126
|
+
|
|
127
|
+
## Reference files
|
|
128
|
+
|
|
129
|
+
Two locations:
|
|
130
|
+
- `references/` — shared, linked from multiple skills. Installed to `_shared/references/` (tier 1) or `.mugiwara/refs/_shared/` (tier 2/3).
|
|
131
|
+
- `content/skills/<name>/references/` — skill-specific
|
|
132
|
+
|
|
133
|
+
Any `references/*.md` pointer in a skill body must resolve **after install**,
|
|
134
|
+
verified by `scripts/verify-install.ts`. A pointer correct in the repo but
|
|
135
|
+
broken after install is a defect, not a docs nit.
|
|
136
|
+
|
|
137
|
+
Body → one-line pointer: "Full checklist: `references/checklist.md` — 37 items;
|
|
138
|
+
unchecked boxes are not done." Never just a bare filename.
|
|
139
|
+
|
|
140
|
+
## State fields
|
|
141
|
+
|
|
142
|
+
`state.json` is the audit trail. Every field in `state.json` is computed, never
|
|
143
|
+
model-supplied. Every field has a fixture assertion in `test/savepoint.test.ts`
|
|
144
|
+
with a non-trivial expected value. A field that can silently read `0` is worse
|
|
145
|
+
than an absent field — absence is visible, a wrong zero is not.
|
|
146
|
+
|
|
147
|
+
## Index budget
|
|
148
|
+
|
|
149
|
+
Combined skill + agent description chars must be ≤5500. Enforced by validator.
|
|
150
|
+
Trimming descriptions = always drop procedure, keep trigger vocabulary.
|
|
151
|
+
|
|
152
|
+
## Manifest sync
|
|
153
|
+
|
|
154
|
+
`.claude-plugin/plugin.json` `metadata.skills` and `metadata.agents` must
|
|
155
|
+
set-equal `content/skills/` and `content/agents/`. Enforced by
|
|
156
|
+
`--check-manifest`. If you add/remove/rename a skill or agent, update the
|
|
157
|
+
manifest.
|
|
158
|
+
|
|
159
|
+
## Docs drift
|
|
160
|
+
|
|
161
|
+
`docs/skills.md` must mention every skill directory. `docs/agents.md` must
|
|
162
|
+
mention every agent file. Enforced by `--check-docs`.
|
|
163
|
+
|
|
164
|
+
## Skill count
|
|
165
|
+
|
|
166
|
+
26 is the ceiling. A new skill replaces an old one — never add to grow.
|
|
167
|
+
|
|
168
|
+
## Commit style
|
|
169
|
+
|
|
170
|
+
Conventional Commits: `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`.
|
|
171
|
+
|
|
172
|
+
## Directory map
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
content/skills/ — 26 skill dirs, each with SKILL.md + optional references/
|
|
176
|
+
content/agents/ — 15 agent .md files
|
|
177
|
+
references/ — shared reference files (definition-of-done, source-grounding, etc.)
|
|
178
|
+
docs/ — user-facing documentation
|
|
179
|
+
scripts/ — validation + tooling scripts
|
|
180
|
+
src/ — CLI TypeScript source
|
|
181
|
+
test/ — vitest tests
|
|
182
|
+
evals/cases/ — eval case JSON files
|
|
183
|
+
```
|