@ionivetech/mugiwara 0.2.0 → 0.4.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/.opencode/commands/mugiwara-execute.md +11 -0
- package/.opencode/commands/mugiwara-heal.md +11 -0
- package/.opencode/commands/mugiwara-mode.md +6 -0
- package/.opencode/commands/mugiwara-plan.md +15 -0
- package/.opencode/commands/mugiwara-review.md +11 -0
- package/.opencode/commands/mugiwara-security.md +11 -0
- package/.opencode/commands/mugiwara-ship.md +11 -0
- package/.opencode/commands/mugiwara.md +11 -0
- package/.opencode/plugins/mugiwara.mjs +126 -7
- package/README.md +338 -355
- package/content/agents/brook-healing.md +2 -2
- package/content/agents/luffy-orchestrator.md +3 -2
- package/content/agents/robin-reviewer.md +1 -1
- package/content/agents/skeptic-verifier.md +1 -1
- package/content/agents/using-mugiwara.md +5 -1
- package/content/agents/usopp-brainstorm.md +1 -1
- package/content/agents/zoro-execution.md +1 -1
- package/content/skills/mugiwara-agent-security/SKILL.md +5 -0
- package/content/skills/mugiwara-api-and-interface-design/SKILL.md +92 -0
- package/content/skills/mugiwara-backend/SKILL.md +5 -0
- package/content/skills/mugiwara-brainstorm/SKILL.md +5 -0
- package/content/skills/mugiwara-checkpoint/SKILL.md +5 -0
- package/content/skills/mugiwara-context-engineering/SKILL.md +64 -0
- package/content/skills/mugiwara-deprecation/SKILL.md +5 -0
- package/content/skills/mugiwara-doubt-driven-development/SKILL.md +70 -0
- package/content/skills/mugiwara-dynamic-workflow/SKILL.md +5 -0
- package/content/skills/mugiwara-eval/SKILL.md +5 -0
- package/content/skills/mugiwara-execution/SKILL.md +9 -0
- package/content/skills/mugiwara-frontend/SKILL.md +61 -59
- package/content/skills/mugiwara-frontend/references/checklist.md +37 -0
- package/content/skills/mugiwara-gates/SKILL.md +9 -0
- package/content/skills/mugiwara-git/SKILL.md +5 -0
- package/content/skills/mugiwara-git-worktrees/SKILL.md +67 -0
- package/content/skills/mugiwara-healing/SKILL.md +17 -0
- package/content/skills/mugiwara-lessons/SKILL.md +5 -0
- package/content/skills/mugiwara-mode/SKILL.md +18 -4
- package/content/skills/mugiwara-observability/SKILL.md +5 -0
- package/content/skills/mugiwara-orchestration/SKILL.md +32 -1
- package/content/skills/mugiwara-planning/SKILL.md +25 -26
- package/content/skills/mugiwara-pr/SKILL.md +22 -6
- package/content/skills/mugiwara-quality/SKILL.md +15 -0
- package/content/skills/mugiwara-resume/SKILL.md +5 -0
- package/content/skills/mugiwara-review/SKILL.md +5 -0
- package/content/skills/mugiwara-security/SKILL.md +51 -14
- package/content/skills/mugiwara-ship/SKILL.md +29 -1
- package/content/skills/mugiwara-systematic-debugging/SKILL.md +82 -0
- package/content/skills/mugiwara-test-driven-development/SKILL.md +89 -0
- package/content/skills/mugiwara-testcases/SKILL.md +5 -0
- package/content/skills/mugiwara-workflow/SKILL.md +14 -3
- package/content/skills/mugiwara-writing-skills/SKILL.md +65 -0
- package/dist/mugiwara.js +175 -51
- package/docs/adoption-guide.md +3 -3
- package/docs/agents.md +2 -2
- package/docs/claude-setup.md +9 -4
- package/docs/codex-setup.md +3 -1
- package/docs/comparison.md +166 -44
- package/docs/config.md +53 -0
- package/docs/copilot-setup.md +3 -1
- package/docs/cursor-setup.md +3 -1
- package/docs/developer-onboarding.md +17 -13
- package/docs/enforcement.md +38 -0
- package/docs/execution-model.md +33 -0
- package/docs/gemini-setup.md +4 -1
- package/docs/getting-started.md +16 -4
- package/docs/index.md +14 -3
- package/docs/lanes.md +40 -0
- package/docs/modes.md +22 -12
- package/docs/opencode-setup.md +9 -2
- package/docs/pr-summary.md +54 -0
- package/docs/skill-anatomy.md +16 -6
- package/docs/skills.md +17 -5
- package/docs/troubleshooting.md +91 -0
- package/docs/windsurf-setup.md +3 -1
- package/docs/workflow.md +15 -6
- package/evals/cases/adversarial-pressure-fake-pass.json +12 -0
- package/evals/cases/adversarial-pressure-skip-review.json +13 -0
- package/evals/cases/lane-exploratory-vague.json +12 -0
- package/evals/cases/lane-sensitivity-payment.json +12 -0
- package/evals/cases/negative-secrets-typo.json +12 -0
- package/evals/cases/negative-security-docs-change.json +12 -0
- package/evals/cases/positive-refactor-existing-tests.json +11 -0
- package/evals/cases/positive-resume-mid-mission.json +11 -0
- package/evals/cases/routing-auth-feature.json +13 -0
- package/evals/cases/routing-bug-one-file.json +13 -0
- package/evals/cases/routing-typo.json +13 -0
- package/hooks/hooks.json +15 -0
- package/hooks/session-start.ts +8 -0
- package/package.json +3 -1
- package/src/args.ts +2 -1
- package/src/cli.ts +16 -20
- package/src/installer.ts +31 -3
- package/src/mission.ts +25 -0
- package/src/targets/claude.ts +22 -1
- package/src/targets/codex.ts +1 -1
- package/src/targets/copilot.ts +4 -0
- package/src/targets/gemini.ts +1 -1
- package/src/targets/generic.ts +33 -1
- package/src/targets/kilo.ts +1 -0
- package/src/targets/opencode.ts +4 -0
package/src/cli.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// src/cli.ts
|
|
3
|
-
import { existsSync, readFileSync,
|
|
3
|
+
import { existsSync, readFileSync, realpathSync, rmSync } from 'node:fs';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
-
import {
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
7
|
import { parseArgs, type FlagValue, type Args } from './args.ts';
|
|
8
8
|
import { createRl, choose, multiChoose, confirm } from './prompt.ts';
|
|
9
9
|
import { targets, TARGET_IDS } from './targets/index.ts';
|
|
10
|
-
import { installTo, removeInstalled, VERSION
|
|
10
|
+
import { installTo, removeInstalled, VERSION } from './installer.ts';
|
|
11
11
|
import { manifestPath, readManifest, writeManifest, type Scope } from './manifest.ts';
|
|
12
|
-
import {
|
|
12
|
+
import { resetMission } from './mission.ts';
|
|
13
13
|
|
|
14
14
|
const str = (v: FlagValue): string | undefined => (typeof v === 'string' ? v : undefined);
|
|
15
15
|
const flag = (v: FlagValue): boolean => v === true;
|
|
@@ -23,11 +23,19 @@ export async function run(argv: string[]): Promise<void> {
|
|
|
23
23
|
case 'update': return install({ ...flags, force: true });
|
|
24
24
|
case 'uninstall': return uninstall(flags);
|
|
25
25
|
case 'list': return list(flags);
|
|
26
|
-
case '
|
|
26
|
+
case 'reset': return resetCmd(flags);
|
|
27
27
|
default: throw new Error(`Unknown command: ${command}`);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function resetCmd(flags: Args['flags']): void {
|
|
32
|
+
const projectDir = resolve(str(flags.project) ?? process.cwd());
|
|
33
|
+
const { removed, kept } = resetMission(projectDir, flag(flags.keepLogs));
|
|
34
|
+
if (removed.length) console.log(`removed: ${removed.join(', ')}`);
|
|
35
|
+
else console.log('nothing to remove.');
|
|
36
|
+
if (kept.length) console.log(`kept: ${kept.join(', ')}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
async function resolveOptions(flags: Args['flags']): Promise<{ scope: Scope; projectDir: string; targetIds: string[] }> {
|
|
32
40
|
const interactive = !flag(flags.yes);
|
|
33
41
|
const rl = interactive ? createRl() : null;
|
|
@@ -126,19 +134,6 @@ function list(flags: Args['flags']): void {
|
|
|
126
134
|
if (!found) console.log('No mugiwara installation found.');
|
|
127
135
|
}
|
|
128
136
|
|
|
129
|
-
function skills(): void {
|
|
130
|
-
const dir = join(CONTENT_DIR, 'skills');
|
|
131
|
-
const names = readdirSync(dir, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name).sort();
|
|
132
|
-
const rows = names.map(name => {
|
|
133
|
-
const { data } = parseFrontmatter(readFileSync(join(dir, name, 'SKILL.md'), 'utf8'));
|
|
134
|
-
return [name, data.description ?? ''] as const;
|
|
135
|
-
});
|
|
136
|
-
const w = Math.max(...rows.map(r => r[0].length)) + 2;
|
|
137
|
-
console.log(`mugiwara ${VERSION} — ${rows.length} skills (agentskills.io format):\n`);
|
|
138
|
-
for (const [name, description] of rows) console.log(` ${name.padEnd(w)}${description}`);
|
|
139
|
-
console.log(`\nInstall skills into any agent via skills.sh:\n npx skills add ionivetech/mugiwara`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
137
|
function help(): void {
|
|
143
138
|
console.log(`mugiwara ${VERSION} — the Straw Hat crew for AI agents
|
|
144
139
|
|
|
@@ -147,7 +142,7 @@ Usage:
|
|
|
147
142
|
mugiwara update replace existing files (backs up differences first)
|
|
148
143
|
mugiwara uninstall remove installed files via manifest
|
|
149
144
|
mugiwara list show installations
|
|
150
|
-
mugiwara
|
|
145
|
+
mugiwara reset wipe mission state (spec/plans/results/review/issues[/logs])
|
|
151
146
|
mugiwara --help this help
|
|
152
147
|
mugiwara --version print version
|
|
153
148
|
|
|
@@ -157,7 +152,8 @@ Flags:
|
|
|
157
152
|
--target <ids|all> comma-separated: ${TARGET_IDS.join(', ')}
|
|
158
153
|
--yes, -y non-interactive (needs --global/--project, --target)
|
|
159
154
|
--force overwrite differing files (with backup)
|
|
160
|
-
--dry-run print actions without writing
|
|
155
|
+
--dry-run print actions without writing
|
|
156
|
+
--keep-logs with reset: keep .mugiwara/logs (lessons ledger survives)`);
|
|
161
157
|
}
|
|
162
158
|
|
|
163
159
|
let entry = process.argv[1] !== undefined ? resolve(process.argv[1]) : undefined;
|
package/src/installer.ts
CHANGED
|
@@ -5,7 +5,12 @@ import { homedir } from 'node:os';
|
|
|
5
5
|
import { parseFrontmatter, type FrontmatterData } from './frontmatter.ts';
|
|
6
6
|
import type { Scope } from './manifest.ts';
|
|
7
7
|
|
|
8
|
-
export type ContentItem = {
|
|
8
|
+
export type ContentItem = {
|
|
9
|
+
name: string;
|
|
10
|
+
data: FrontmatterData;
|
|
11
|
+
body: string;
|
|
12
|
+
refs: { relPath: string; text: string }[];
|
|
13
|
+
};
|
|
9
14
|
|
|
10
15
|
export type InstallOptions = {
|
|
11
16
|
scope: Scope;
|
|
@@ -28,9 +33,13 @@ export interface Target {
|
|
|
28
33
|
id: string;
|
|
29
34
|
label: string;
|
|
30
35
|
native: boolean;
|
|
36
|
+
tier?: 1 | 2 | 3;
|
|
31
37
|
paths(opts: { scope: Scope; projectDir: string; home: string }): { skillsDir: string; agentsDir: string };
|
|
32
38
|
transformSkill(data: FrontmatterData, body: string): TransformOut;
|
|
33
39
|
transformAgent(data: FrontmatterData, body: string): TransformOut;
|
|
40
|
+
refsDir?(opts: { scope: Scope; projectDir: string; home: string }, skillName: string): string;
|
|
41
|
+
transformSkillFull?(data: FrontmatterData, body: string): TransformOut | null;
|
|
42
|
+
transformAgentFull?(data: FrontmatterData, body: string): TransformOut | null;
|
|
34
43
|
postInstall?(opts: { scope: Scope; projectDir: string; home: string; dryRun: boolean; files: string[] }): { written: string[]; notes: string[] };
|
|
35
44
|
}
|
|
36
45
|
|
|
@@ -43,17 +52,24 @@ export function collectContent(): { skills: ContentItem[]; agents: ContentItem[]
|
|
|
43
52
|
.filter(e => e.isDirectory()).map(e => e.name);
|
|
44
53
|
const skills = skillNames.map(name => {
|
|
45
54
|
const { data, body } = parseFrontmatter(readFileSync(join(CONTENT_DIR, 'skills', name, 'SKILL.md'), 'utf8'));
|
|
46
|
-
return { name, data, body };
|
|
55
|
+
return { name, data, body, refs: collectRefs(join(CONTENT_DIR, 'skills', name)) };
|
|
47
56
|
});
|
|
48
57
|
const agents = readdirSync(join(CONTENT_DIR, 'agents'))
|
|
49
58
|
.filter(f => f.endsWith('.md'))
|
|
50
59
|
.map(f => {
|
|
51
60
|
const { data, body } = parseFrontmatter(readFileSync(join(CONTENT_DIR, 'agents', f), 'utf8'));
|
|
52
|
-
return { name: f.replace(/\.md$/, ''), data, body };
|
|
61
|
+
return { name: f.replace(/\.md$/, ''), data, body, refs: [] as { relPath: string; text: string }[] };
|
|
53
62
|
});
|
|
54
63
|
return { skills, agents };
|
|
55
64
|
}
|
|
56
65
|
|
|
66
|
+
function collectRefs(skillDir: string): { relPath: string; text: string }[] {
|
|
67
|
+
const refsDir = join(skillDir, 'references');
|
|
68
|
+
if (!existsSync(refsDir)) return [];
|
|
69
|
+
return readdirSync(refsDir, { recursive: true }).map(f => String(f)).filter(f => f.endsWith('.md'))
|
|
70
|
+
.map(rel => ({ relPath: rel, text: readFileSync(join(refsDir, rel), 'utf8') }));
|
|
71
|
+
}
|
|
72
|
+
|
|
57
73
|
export function installTo(target: Target, opts: InstallOptions): InstallResult {
|
|
58
74
|
const { scope, projectDir, dryRun = false, force = false } = opts;
|
|
59
75
|
const home = opts.home ?? homedir();
|
|
@@ -82,10 +98,22 @@ export function installTo(target: Target, opts: InstallOptions): InstallResult {
|
|
|
82
98
|
for (const s of skills) {
|
|
83
99
|
const out = target.transformSkill(s.data, s.body);
|
|
84
100
|
if (out) writeOne(join(dirs.skillsDir, out.relPath), out.text);
|
|
101
|
+
if (target.transformSkillFull) {
|
|
102
|
+
const full = target.transformSkillFull(s.data, s.body);
|
|
103
|
+
if (full && target.refsDir) writeOne(join(target.refsDir({ scope, projectDir, home }, s.name), full.relPath), full.text);
|
|
104
|
+
}
|
|
105
|
+
if (s.refs.length && target.refsDir) {
|
|
106
|
+
const refsRoot = target.refsDir({ scope, projectDir, home }, s.name);
|
|
107
|
+
for (const r of s.refs) writeOne(join(refsRoot, r.relPath), r.text);
|
|
108
|
+
}
|
|
85
109
|
}
|
|
86
110
|
for (const a of agents) {
|
|
87
111
|
const out = target.transformAgent(a.data, a.body);
|
|
88
112
|
if (out) writeOne(join(dirs.agentsDir, out.relPath), out.text);
|
|
113
|
+
if (target.transformAgentFull) {
|
|
114
|
+
const full = target.transformAgentFull(a.data, a.body);
|
|
115
|
+
if (full && target.refsDir) writeOne(join(target.refsDir({ scope, projectDir, home }, a.name), full.relPath), full.text);
|
|
116
|
+
}
|
|
89
117
|
}
|
|
90
118
|
|
|
91
119
|
if (target.postInstall) {
|
package/src/mission.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/mission.ts
|
|
2
|
+
// Mission-state helpers for the mugiwara CLI (installer + reset only).
|
|
3
|
+
import { existsSync, rmSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
export function resetMission(projectDir: string, keepLogs: boolean): { removed: string[]; kept: string[] } {
|
|
7
|
+
const root = join(projectDir, '.mugiwara');
|
|
8
|
+
if (!existsSync(root)) return { removed: [], kept: [] };
|
|
9
|
+
const removed: string[] = [];
|
|
10
|
+
const kept: string[] = [];
|
|
11
|
+
for (const dir of ['spec', 'plans', 'results', 'review', 'issues']) {
|
|
12
|
+
const p = join(root, dir);
|
|
13
|
+
if (existsSync(p)) { rmSync(p, { recursive: true, force: true }); removed.push(dir); }
|
|
14
|
+
}
|
|
15
|
+
if (!keepLogs) {
|
|
16
|
+
const p = join(root, 'logs');
|
|
17
|
+
if (existsSync(p)) { rmSync(p, { recursive: true, force: true }); removed.push('logs'); }
|
|
18
|
+
} else if (existsSync(join(root, 'logs'))) {
|
|
19
|
+
kept.push('logs');
|
|
20
|
+
}
|
|
21
|
+
for (const f of ['config', 'manifest.json', 'backup']) {
|
|
22
|
+
if (existsSync(join(root, f))) kept.push(f);
|
|
23
|
+
}
|
|
24
|
+
return { removed, kept };
|
|
25
|
+
}
|
package/src/targets/claude.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
// src/targets/claude.ts
|
|
2
|
-
import {
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
3
5
|
import { stringifyFrontmatter, type FrontmatterData } from '../frontmatter.ts';
|
|
4
6
|
import type { Target } from '../installer.ts';
|
|
5
7
|
|
|
8
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const HOOK_SRC = join(here, '..', '..', 'hooks', 'session-start.ts');
|
|
10
|
+
|
|
6
11
|
export const target: Target = {
|
|
7
12
|
id: 'claude',
|
|
8
13
|
label: 'Claude Code',
|
|
@@ -22,4 +27,20 @@ export const target: Target = {
|
|
|
22
27
|
if (data.tools) fm.tools = data.tools;
|
|
23
28
|
return { relPath: `${data.name}.md`, text: stringifyFrontmatter(fm, body) };
|
|
24
29
|
},
|
|
30
|
+
refsDir({ scope, projectDir, home }, skillName: string) {
|
|
31
|
+
const root = scope === 'global' ? join(home, '.claude') : join(projectDir, '.claude');
|
|
32
|
+
return join(root, 'skills', skillName, 'references');
|
|
33
|
+
},
|
|
34
|
+
postInstall({ scope, projectDir, home, dryRun }) {
|
|
35
|
+
// Wire the SessionStart hook (inline doctrine) into the installed .claude dir.
|
|
36
|
+
const root = scope === 'global' ? join(home, '.claude') : join(projectDir, '.claude');
|
|
37
|
+
const hookFile = join(root, 'hooks', 'session-start.ts');
|
|
38
|
+
if (dryRun) return { written: [], notes: [] };
|
|
39
|
+
if (existsSync(HOOK_SRC) && !existsSync(hookFile)) {
|
|
40
|
+
mkdirSync(dirname(hookFile), { recursive: true });
|
|
41
|
+
copyFileSync(HOOK_SRC, hookFile);
|
|
42
|
+
return { written: [hookFile], notes: [] };
|
|
43
|
+
}
|
|
44
|
+
return { written: [], notes: [] };
|
|
45
|
+
},
|
|
25
46
|
};
|
package/src/targets/codex.ts
CHANGED
|
@@ -6,5 +6,5 @@ export const target = makeGeneric({
|
|
|
6
6
|
label: 'Codex',
|
|
7
7
|
rulesDir: '.codex/mugiwara',
|
|
8
8
|
bootstrapFile: 'AGENTS.md',
|
|
9
|
-
bootstrapPointer: 'Mugiwara crew installed in .codex/mugiwara/ — read .codex/mugiwara/mugiwara-workflow.md to run the pipeline.',
|
|
9
|
+
bootstrapPointer: 'Mugiwara crew installed in .codex/mugiwara/ — read .codex/mugiwara/mugiwara-workflow.md to run the pipeline inline in the main conversation.',
|
|
10
10
|
});
|
package/src/targets/copilot.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/targets/copilot.ts
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { stringifyFrontmatter, type FrontmatterData } from '../frontmatter.ts';
|
|
4
|
+
import type { Scope } from '../manifest.ts';
|
|
4
5
|
import type { Target } from '../installer.ts';
|
|
5
6
|
|
|
6
7
|
export const target: Target = {
|
|
@@ -23,4 +24,7 @@ export const target: Target = {
|
|
|
23
24
|
text: stringifyFrontmatter({ name: data.name, description: data.description }, body),
|
|
24
25
|
};
|
|
25
26
|
},
|
|
27
|
+
refsDir(_opts: { scope: Scope; projectDir: string; home: string }, skillName: string) {
|
|
28
|
+
return join(_opts.projectDir, '.mugiwara', 'refs', skillName);
|
|
29
|
+
},
|
|
26
30
|
};
|
package/src/targets/gemini.ts
CHANGED
|
@@ -6,5 +6,5 @@ export const target = makeGeneric({
|
|
|
6
6
|
label: 'Gemini',
|
|
7
7
|
rulesDir: '.gemini/mugiwara',
|
|
8
8
|
bootstrapFile: 'GEMINI.md',
|
|
9
|
-
bootstrapPointer: 'Mugiwara crew installed in .gemini/mugiwara/ — read .gemini/mugiwara/mugiwara-workflow.md to run the pipeline.',
|
|
9
|
+
bootstrapPointer: 'Mugiwara crew installed in .gemini/mugiwara/ — read .gemini/mugiwara/mugiwara-workflow.md to run the pipeline inline in the main conversation.',
|
|
10
10
|
});
|
package/src/targets/generic.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/targets/generic.ts
|
|
2
2
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import type { Scope } from '../manifest.ts';
|
|
4
5
|
import type { Target } from '../installer.ts';
|
|
5
6
|
|
|
6
7
|
export function makeGeneric(opts: {
|
|
@@ -9,23 +10,54 @@ export function makeGeneric(opts: {
|
|
|
9
10
|
rulesDir: string;
|
|
10
11
|
bootstrapFile: string | null;
|
|
11
12
|
bootstrapPointer: string | null;
|
|
13
|
+
tier?: 2 | 3;
|
|
12
14
|
}): Target {
|
|
13
|
-
const { id, label, rulesDir, bootstrapFile, bootstrapPointer } = opts;
|
|
15
|
+
const { id, label, rulesDir, bootstrapFile, bootstrapPointer, tier = bootstrapPointer ? 2 : 3 } = opts;
|
|
16
|
+
const stubOnly = tier === 3;
|
|
14
17
|
return {
|
|
15
18
|
id,
|
|
16
19
|
label,
|
|
17
20
|
native: false,
|
|
21
|
+
tier,
|
|
18
22
|
paths({ scope, projectDir }: { scope: 'global' | 'project'; projectDir: string; home: string }) {
|
|
19
23
|
if (scope === 'global') throw new Error(`${label} supports project scope only`);
|
|
20
24
|
const dir = join(projectDir, rulesDir);
|
|
21
25
|
return { skillsDir: dir, agentsDir: dir };
|
|
22
26
|
},
|
|
23
27
|
transformSkill(data, body) {
|
|
28
|
+
if (stubOnly) {
|
|
29
|
+
// Stub: routing + pointer only. Full body lands in .mugiwara/refs/
|
|
30
|
+
// (outside the rules glob) so glob-loading harnesses stop eating ~40k
|
|
31
|
+
// tokens at session start. The agent reads the full file on demand.
|
|
32
|
+
return {
|
|
33
|
+
relPath: `${data.name}.md`,
|
|
34
|
+
text: `# ${data.name}\n\n> ${data.description}\n\n## Skip when\n\nFull skill: \`${data.name}\` — read \`.mugiwara/refs/${data.name}.md\` when the crew invokes this role.`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return { relPath: `${data.name}.md`, text: `# ${data.name}\n\n> ${data.description}\n\n${body}` };
|
|
38
|
+
},
|
|
39
|
+
transformSkillFull(data, body) {
|
|
40
|
+
if (!stubOnly) return null;
|
|
24
41
|
return { relPath: `${data.name}.md`, text: `# ${data.name}\n\n> ${data.description}\n\n${body}` };
|
|
25
42
|
},
|
|
26
43
|
transformAgent(data, body) {
|
|
44
|
+
if (stubOnly) {
|
|
45
|
+
return {
|
|
46
|
+
relPath: `agent-${data.name}.md`,
|
|
47
|
+
text: `# Agent: ${data.name}\n\n> ${data.description}\n\nSkills: ${data.skills ?? ''}. Read \`.mugiwara/refs/${data.name}.md\` when embodying this role.`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
27
50
|
return { relPath: `agent-${data.name}.md`, text: `# Agent: ${data.name}\n\n> ${data.description}\n\nSkills used: ${data.skills ?? ''}\n\n${body}` };
|
|
28
51
|
},
|
|
52
|
+
transformAgentFull(data, body) {
|
|
53
|
+
if (!stubOnly) return null;
|
|
54
|
+
return { relPath: `${data.name}.md`, text: `# Agent: ${data.name}\n\n> ${data.description}\n\nSkills used: ${data.skills ?? ''}\n\n${body}` };
|
|
55
|
+
},
|
|
56
|
+
refsDir({ projectDir }: { scope: Scope; projectDir: string; home: string }) {
|
|
57
|
+
// Keep reference detail outside the rules dir so the harness never
|
|
58
|
+
// glob-loads it; the skill names the path and the agent reads on demand.
|
|
59
|
+
return join(projectDir, '.mugiwara', 'refs');
|
|
60
|
+
},
|
|
29
61
|
postInstall({ projectDir, dryRun }) {
|
|
30
62
|
if (!bootstrapFile) return { written: [], notes: [] };
|
|
31
63
|
const notes: string[] = [];
|
package/src/targets/kilo.ts
CHANGED
package/src/targets/opencode.ts
CHANGED
|
@@ -22,4 +22,8 @@ export const target: Target = {
|
|
|
22
22
|
if (data.tools) fm.tools = data.tools;
|
|
23
23
|
return { relPath: `${data.name}.md`, text: stringifyFrontmatter(fm, body) };
|
|
24
24
|
},
|
|
25
|
+
refsDir({ scope, projectDir, home }, skillName: string) {
|
|
26
|
+
const root = scope === 'global' ? join(home, '.config', 'opencode') : join(projectDir, '.opencode');
|
|
27
|
+
return join(root, 'skills', skillName, 'references');
|
|
28
|
+
},
|
|
25
29
|
};
|