claude-raid 0.1.2 → 0.1.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.
- package/README.md +295 -223
- package/bin/cli.js +16 -2
- package/package.json +1 -1
- package/src/descriptions.js +57 -0
- package/src/doctor.js +4 -29
- package/src/init.js +142 -7
- package/src/merge-settings.js +1 -1
- package/src/setup.js +45 -3
- package/src/ui.js +76 -5
- package/template/.claude/agents/archer.md +30 -0
- package/template/.claude/agents/rogue.md +30 -0
- package/template/.claude/agents/warrior.md +30 -0
- package/template/.claude/agents/wizard.md +68 -14
- package/template/.claude/hooks/raid-lib.sh +73 -55
- package/template/.claude/hooks/raid-session-end.sh +1 -1
- package/template/.claude/hooks/raid-session-start.sh +5 -8
- package/template/.claude/hooks/raid-stop.sh +11 -16
- package/template/.claude/hooks/raid-task-completed.sh +4 -0
- package/template/.claude/hooks/raid-teammate-idle.sh +9 -2
- package/template/.claude/hooks/validate-commit.sh +5 -1
- package/template/.claude/hooks/validate-dungeon.sh +11 -12
- package/template/.claude/skills/raid-design/SKILL.md +8 -3
- package/template/.claude/skills/raid-implementation/SKILL.md +24 -6
- package/template/.claude/skills/raid-protocol/SKILL.md +28 -6
package/bin/cli.js
CHANGED
|
@@ -11,12 +11,26 @@ const showUpdateNotice = versionCheck.start();
|
|
|
11
11
|
|
|
12
12
|
const COMMANDS = {
|
|
13
13
|
// Primary commands
|
|
14
|
-
summon: () =>
|
|
14
|
+
summon: () => {
|
|
15
|
+
if (process.argv.includes('--dry-run')) {
|
|
16
|
+
console.log('\n' + banner());
|
|
17
|
+
console.log(require('../src/init').dryRun(process.cwd()));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
return require('../src/init').run();
|
|
21
|
+
},
|
|
15
22
|
update: () => require('../src/update').run(),
|
|
16
23
|
dismantle: () => require('../src/remove').run(),
|
|
17
24
|
heal: () => require('../src/doctor').run(),
|
|
18
25
|
// Aliases (backward compat)
|
|
19
|
-
init: () =>
|
|
26
|
+
init: () => {
|
|
27
|
+
if (process.argv.includes('--dry-run')) {
|
|
28
|
+
console.log('\n' + banner());
|
|
29
|
+
console.log(require('../src/init').dryRun(process.cwd()));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
return require('../src/init').run();
|
|
33
|
+
},
|
|
20
34
|
remove: () => require('../src/remove').run(),
|
|
21
35
|
doctor: () => require('../src/doctor').run(),
|
|
22
36
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Single source of truth for file descriptions used by dry-run and install output.
|
|
4
|
+
// Keep descriptions under 80 chars — they appear in terminal columns.
|
|
5
|
+
|
|
6
|
+
const AGENTS = {
|
|
7
|
+
'wizard.md': 'Dungeon master — opens phases, dispatches team, closes with rulings',
|
|
8
|
+
'warrior.md': 'Stress-tester — breaks things under load, edge cases, pressure',
|
|
9
|
+
'archer.md': 'Pattern-seeker — traces ripple effects, naming drift, contract violations',
|
|
10
|
+
'rogue.md': 'Assumption-destroyer — thinks like a failing system, malicious input',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const HOOKS = {
|
|
14
|
+
lifecycle: [
|
|
15
|
+
{ name: 'raid-lib.sh', desc: 'Shared config — reads raid.json, exports session state' },
|
|
16
|
+
{ name: 'raid-session-start.sh', desc: 'Activates Raid workflow when session begins' },
|
|
17
|
+
{ name: 'raid-session-end.sh', desc: 'Archives Dungeon, cleans up when session ends' },
|
|
18
|
+
{ name: 'raid-stop.sh', desc: 'Backs up Dungeon on phase transitions' },
|
|
19
|
+
{ name: 'raid-pre-compact.sh', desc: 'Backs up Dungeon before message compaction' },
|
|
20
|
+
{ name: 'raid-task-created.sh', desc: 'Validates task subjects are meaningful' },
|
|
21
|
+
{ name: 'raid-task-completed.sh', desc: 'Blocks task completion without test evidence' },
|
|
22
|
+
{ name: 'raid-teammate-idle.sh', desc: 'Nudges idle agents to participate' },
|
|
23
|
+
],
|
|
24
|
+
gates: [
|
|
25
|
+
{ name: 'validate-commit.sh', desc: 'Enforces conventional commits + test gate' },
|
|
26
|
+
{ name: 'validate-write-gate.sh', desc: 'Blocks implementation before design doc exists' },
|
|
27
|
+
{ name: 'validate-file-naming.sh', desc: 'Enforces naming convention (kebab-case, etc.)' },
|
|
28
|
+
{ name: 'validate-no-placeholders.sh', desc: 'Blocks TBD/TODO in specs and plans' },
|
|
29
|
+
{ name: 'validate-dungeon.sh', desc: 'Requires multi-agent verification on pins' },
|
|
30
|
+
{ name: 'validate-browser-tests-exist.sh', desc: 'Checks Playwright tests exist before commits' },
|
|
31
|
+
{ name: 'validate-browser-cleanup.sh', desc: 'Verifies browser processes cleaned up properly' },
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const SKILLS = {
|
|
36
|
+
'raid-protocol': 'Session lifecycle and team rules',
|
|
37
|
+
'raid-design': 'Phase 1: adversarial exploration',
|
|
38
|
+
'raid-implementation-plan': 'Phase 2: task decomposition',
|
|
39
|
+
'raid-implementation': 'Phase 3: TDD with direct challenge',
|
|
40
|
+
'raid-review': 'Phase 4: independent review + fighting',
|
|
41
|
+
'raid-finishing': 'Completeness debate + merge options',
|
|
42
|
+
'raid-tdd': 'RED-GREEN-REFACTOR enforcement',
|
|
43
|
+
'raid-debugging': 'Root-cause investigation',
|
|
44
|
+
'raid-verification': 'Evidence-before-claims gate',
|
|
45
|
+
'raid-git-worktrees': 'Isolated workspace creation',
|
|
46
|
+
'raid-browser': 'Browser startup discovery',
|
|
47
|
+
'raid-browser-playwright': 'Playwright test authoring',
|
|
48
|
+
'raid-browser-chrome': 'Live browser inspection',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const CONFIG = {
|
|
52
|
+
'raid.json': 'Project settings (editable)',
|
|
53
|
+
'raid-rules.md': '17 team rules (editable)',
|
|
54
|
+
'settings.json': 'Hooks merged into existing (backup created)',
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
module.exports = { AGENTS, HOOKS, SKILLS, CONFIG };
|
package/src/doctor.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { runChecks, runSetup } = require('./setup');
|
|
4
|
-
const { banner,
|
|
4
|
+
const { banner, header } = require('./ui');
|
|
5
5
|
|
|
6
6
|
function diagnose(opts) {
|
|
7
7
|
return runChecks(opts);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
async function run() {
|
|
11
|
+
const { referenceCard } = require('./ui');
|
|
12
|
+
|
|
11
13
|
console.log('\n' + banner());
|
|
12
14
|
console.log(header('Diagnosing Wounds...') + '\n');
|
|
13
15
|
|
|
@@ -17,34 +19,7 @@ async function run() {
|
|
|
17
19
|
process.exitCode = 1;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
' In-process mode (any terminal):',
|
|
22
|
-
' ' + colors.bold('claude --agent wizard'),
|
|
23
|
-
'',
|
|
24
|
-
' Split-pane mode (tmux):',
|
|
25
|
-
' ' + colors.bold('tmux new-session -s raid'),
|
|
26
|
-
' ' + colors.bold('claude --agent wizard --teammate-mode tmux'),
|
|
27
|
-
]);
|
|
28
|
-
console.log('\n' + quickStart);
|
|
29
|
-
|
|
30
|
-
const controls = box('Controls', [
|
|
31
|
-
' ' + colors.bold('Shift+Down') + ' Cycle through teammates',
|
|
32
|
-
' ' + colors.bold('Enter') + ' View a teammate\'s session',
|
|
33
|
-
' ' + colors.bold('Escape') + ' Interrupt a teammate\'s turn',
|
|
34
|
-
' ' + colors.bold('Ctrl+T') + ' Toggle the shared task list',
|
|
35
|
-
' ' + colors.bold('Click pane') + ' Interact directly (split-pane)',
|
|
36
|
-
]);
|
|
37
|
-
console.log('\n' + controls);
|
|
38
|
-
|
|
39
|
-
const modes = box('Raid Modes', [
|
|
40
|
-
' ' + colors.bold('Full Raid') + ' Warrior + Archer + Rogue (3 agents)',
|
|
41
|
-
' ' + colors.bold('Skirmish') + ' 2 agents, lightweight',
|
|
42
|
-
' ' + colors.bold('Scout') + ' Wizard solo review',
|
|
43
|
-
'',
|
|
44
|
-
' The Wizard recommends a mode based on task',
|
|
45
|
-
' complexity. You confirm before agents spawn.',
|
|
46
|
-
]);
|
|
47
|
-
console.log('\n' + modes + '\n');
|
|
22
|
+
console.log('\n' + referenceCard() + '\n');
|
|
48
23
|
}
|
|
49
24
|
|
|
50
25
|
module.exports = { diagnose, run };
|
package/src/init.js
CHANGED
|
@@ -6,6 +6,7 @@ const { detectProject } = require('./detect-project');
|
|
|
6
6
|
const { mergeSettings } = require('./merge-settings');
|
|
7
7
|
const { runSetup } = require('./setup');
|
|
8
8
|
const { banner, header, colors } = require('./ui');
|
|
9
|
+
const { AGENTS, HOOKS, SKILLS, CONFIG } = require('./descriptions');
|
|
9
10
|
|
|
10
11
|
const TEMPLATE_DIR = path.join(__dirname, '..', 'template', '.claude');
|
|
11
12
|
|
|
@@ -60,6 +61,16 @@ function install(cwd) {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
// Count copied files by category
|
|
65
|
+
const agentsDir = path.join(claudeDir, 'agents');
|
|
66
|
+
const hooksDir2 = path.join(claudeDir, 'hooks');
|
|
67
|
+
const skillsDir = path.join(claudeDir, 'skills');
|
|
68
|
+
result.counts = {
|
|
69
|
+
agents: fs.existsSync(agentsDir) ? fs.readdirSync(agentsDir).filter(f => f.endsWith('.md')).length : 0,
|
|
70
|
+
hooks: fs.existsSync(hooksDir2) ? fs.readdirSync(hooksDir2).filter(f => f.endsWith('.sh')).length : 0,
|
|
71
|
+
skills: fs.existsSync(skillsDir) ? fs.readdirSync(skillsDir).filter(f => !f.startsWith('.')).length : 0,
|
|
72
|
+
};
|
|
73
|
+
|
|
63
74
|
// Generate raid.json (skip if it already exists to preserve user config)
|
|
64
75
|
const raidConfigPath = path.join(claudeDir, 'raid.json');
|
|
65
76
|
if (!fs.existsSync(raidConfigPath)) {
|
|
@@ -139,7 +150,8 @@ function install(cwd) {
|
|
|
139
150
|
];
|
|
140
151
|
if (fs.existsSync(gitignorePath)) {
|
|
141
152
|
let content = fs.readFileSync(gitignorePath, 'utf8');
|
|
142
|
-
const
|
|
153
|
+
const lines = content.split('\n').map(l => l.trim());
|
|
154
|
+
const toAdd = ignoreEntries.filter(e => !lines.includes(e.trim()));
|
|
143
155
|
if (toAdd.length > 0) {
|
|
144
156
|
const sep = content.endsWith('\n') ? '' : '\n';
|
|
145
157
|
fs.appendFileSync(gitignorePath, sep + toAdd.join('\n') + '\n');
|
|
@@ -153,26 +165,149 @@ function install(cwd) {
|
|
|
153
165
|
|
|
154
166
|
async function run() {
|
|
155
167
|
const cwd = process.cwd();
|
|
168
|
+
const { bold, dim } = colors;
|
|
169
|
+
|
|
156
170
|
console.log('\n' + banner());
|
|
157
171
|
console.log(header('Summoning the Party...') + '\n');
|
|
158
172
|
|
|
159
173
|
const result = install(cwd);
|
|
160
174
|
|
|
161
175
|
if (result.alreadyInstalled) {
|
|
162
|
-
console.log(' The party is already here. Use ' +
|
|
176
|
+
console.log(' The party is already here. Use ' + bold('claude-raid update') + ' to reforge.');
|
|
163
177
|
console.log(' Proceeding with re-summon...\n');
|
|
164
178
|
}
|
|
165
179
|
|
|
166
|
-
|
|
180
|
+
// Detection summary
|
|
181
|
+
console.log(' Realm detected: ' + bold(result.detected.language));
|
|
167
182
|
if (result.detected.testCommand) {
|
|
168
|
-
console.log('
|
|
183
|
+
console.log(' Test command: ' + bold(result.detected.testCommand));
|
|
184
|
+
}
|
|
185
|
+
if (result.detected.lintCommand) {
|
|
186
|
+
console.log(' Lint command: ' + bold(result.detected.lintCommand));
|
|
169
187
|
}
|
|
188
|
+
|
|
189
|
+
// Agents
|
|
190
|
+
console.log('');
|
|
191
|
+
console.log(' ' + header('Agents') + dim(` ${result.counts.agents} files`));
|
|
192
|
+
console.log(' Copied wizard.md, warrior.md, archer.md, rogue.md');
|
|
193
|
+
console.log(dim(' Each agent gets its own tmux pane. Start with:'));
|
|
194
|
+
console.log(dim(' tmux new-session -s raid && claude --agent wizard'));
|
|
195
|
+
|
|
196
|
+
// Hooks
|
|
197
|
+
console.log('');
|
|
198
|
+
console.log(' ' + header('Hooks') + dim(` ${result.counts.hooks} files`));
|
|
199
|
+
console.log(' Copied ' + bold(`${HOOKS.lifecycle.length} lifecycle hooks`) + ' + ' + bold(`${HOOKS.gates.length} quality gates`));
|
|
200
|
+
console.log(dim(' Lifecycle hooks manage session state automatically.'));
|
|
201
|
+
console.log(dim(' Quality gates block bad commits, missing tests, and'));
|
|
202
|
+
console.log(dim(' placeholder text \u2014 only active during Raid sessions.'));
|
|
203
|
+
|
|
204
|
+
// Skills
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log(' ' + header('Skills') + dim(` ${result.counts.skills} folders`));
|
|
207
|
+
const skillNames = Object.keys(SKILLS).join(', ');
|
|
208
|
+
console.log(' ' + dim(skillNames));
|
|
209
|
+
console.log(dim(' Phase-specific workflows that guide agent behavior.'));
|
|
210
|
+
|
|
211
|
+
// Config
|
|
212
|
+
console.log('');
|
|
213
|
+
console.log(' ' + header('Config'));
|
|
214
|
+
console.log(' Generated ' + bold('raid.json') + ' ' + dim('Project settings (editable)'));
|
|
215
|
+
console.log(' Copied ' + bold('raid-rules.md') + ' ' + dim('17 team rules (editable)'));
|
|
216
|
+
console.log(' Merged ' + bold('settings.json') + ' ' + dim('Backup at .pre-raid-backup'));
|
|
217
|
+
|
|
218
|
+
// Skipped files
|
|
170
219
|
if (result.skipped.length > 0) {
|
|
171
|
-
console.log('
|
|
172
|
-
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log(' ' + dim('Preserved existing scrolls:'));
|
|
222
|
+
result.skipped.forEach(f => console.log(' ' + dim('\u2192 ' + path.relative(cwd, f))));
|
|
173
223
|
}
|
|
174
224
|
|
|
225
|
+
// Setup wizard
|
|
175
226
|
await runSetup();
|
|
227
|
+
|
|
228
|
+
// Reference card
|
|
229
|
+
const { referenceCard } = require('./ui');
|
|
230
|
+
console.log('\n' + referenceCard() + '\n');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function dryRun(cwd) {
|
|
234
|
+
const detected = detectProject(cwd);
|
|
235
|
+
const lines = [];
|
|
236
|
+
|
|
237
|
+
lines.push(header('Dry Run — nothing will be written') + '\n');
|
|
238
|
+
|
|
239
|
+
// Realm
|
|
240
|
+
lines.push(' Realm detected: ' + colors.bold(detected.language));
|
|
241
|
+
if (detected.testCommand) {
|
|
242
|
+
lines.push(' Test command: ' + colors.bold(detected.testCommand));
|
|
243
|
+
}
|
|
244
|
+
if (detected.lintCommand) {
|
|
245
|
+
lines.push(' Lint command: ' + colors.bold(detected.lintCommand));
|
|
246
|
+
}
|
|
247
|
+
lines.push('');
|
|
248
|
+
|
|
249
|
+
// Helper: check if a file already exists
|
|
250
|
+
const claudeDir = path.join(cwd, '.claude');
|
|
251
|
+
function tag(relPath) {
|
|
252
|
+
const full = path.join(claudeDir, relPath);
|
|
253
|
+
return fs.existsSync(full) ? ' ' + colors.dim('(preserved)') : '';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Agents
|
|
257
|
+
lines.push(header('Agents') + '\n');
|
|
258
|
+
for (const [file, desc] of Object.entries(AGENTS)) {
|
|
259
|
+
lines.push(' ' + colors.bold(file.padEnd(14)) + desc + tag('agents/' + file));
|
|
260
|
+
}
|
|
261
|
+
lines.push('');
|
|
262
|
+
|
|
263
|
+
// Hooks — Lifecycle
|
|
264
|
+
lines.push(header('Hooks — Lifecycle') + '\n');
|
|
265
|
+
for (const h of HOOKS.lifecycle) {
|
|
266
|
+
lines.push(' ' + colors.bold(h.name.padEnd(28)) + h.desc + tag('hooks/' + h.name));
|
|
267
|
+
}
|
|
268
|
+
lines.push('');
|
|
269
|
+
|
|
270
|
+
// Hooks — Quality Gates
|
|
271
|
+
lines.push(header('Hooks — Quality Gates') + '\n');
|
|
272
|
+
for (const h of HOOKS.gates) {
|
|
273
|
+
lines.push(' ' + colors.bold(h.name.padEnd(36)) + h.desc + tag('hooks/' + h.name));
|
|
274
|
+
}
|
|
275
|
+
lines.push('');
|
|
276
|
+
|
|
277
|
+
// Skills
|
|
278
|
+
lines.push(header('Skills') + '\n');
|
|
279
|
+
for (const [folder, desc] of Object.entries(SKILLS)) {
|
|
280
|
+
lines.push(' ' + colors.bold(folder.padEnd(28)) + desc + tag('skills/' + folder));
|
|
281
|
+
}
|
|
282
|
+
lines.push('');
|
|
283
|
+
|
|
284
|
+
// Config
|
|
285
|
+
lines.push(header('Config') + '\n');
|
|
286
|
+
for (const [file, desc] of Object.entries(CONFIG)) {
|
|
287
|
+
lines.push(' ' + colors.bold(file.padEnd(20)) + desc + tag(file));
|
|
288
|
+
}
|
|
289
|
+
lines.push('');
|
|
290
|
+
|
|
291
|
+
// .gitignore
|
|
292
|
+
lines.push(header('.gitignore entries') + '\n');
|
|
293
|
+
const ignoreEntries = [
|
|
294
|
+
'.claude/raid-last-test-run',
|
|
295
|
+
'.claude/raid-session',
|
|
296
|
+
'.claude/raid-dungeon.md',
|
|
297
|
+
'.claude/raid-dungeon-phase-*',
|
|
298
|
+
'.claude/raid-dungeon-backup.md',
|
|
299
|
+
'.claude/raid-dungeon-phase-*-backup.md',
|
|
300
|
+
'.claude/vault/.draft/',
|
|
301
|
+
'.env.raid',
|
|
302
|
+
];
|
|
303
|
+
for (const entry of ignoreEntries) {
|
|
304
|
+
lines.push(' ' + colors.dim(entry));
|
|
305
|
+
}
|
|
306
|
+
lines.push('');
|
|
307
|
+
|
|
308
|
+
lines.push(' Run without --dry-run to install.');
|
|
309
|
+
|
|
310
|
+
return lines.join('\n');
|
|
176
311
|
}
|
|
177
312
|
|
|
178
|
-
module.exports = { install, run };
|
|
313
|
+
module.exports = { install, run, dryRun };
|
package/src/merge-settings.js
CHANGED
|
@@ -126,7 +126,7 @@ function mergeSettings(cwd) {
|
|
|
126
126
|
if (!existing.hooks) existing.hooks = {};
|
|
127
127
|
|
|
128
128
|
for (const [event, raidEntries] of Object.entries(RAID_HOOKS)) {
|
|
129
|
-
if (!existing.hooks[event]) {
|
|
129
|
+
if (!Array.isArray(existing.hooks[event])) {
|
|
130
130
|
existing.hooks[event] = [];
|
|
131
131
|
}
|
|
132
132
|
existing.hooks[event] = existing.hooks[event].filter(entry => !isRaidHookEntry(entry));
|
package/src/setup.js
CHANGED
|
@@ -34,7 +34,7 @@ function versionGte(v, min) {
|
|
|
34
34
|
const MIN_NODE = { major: 18, minor: 0, patch: 0 };
|
|
35
35
|
const MIN_CLAUDE = { major: 2, minor: 1, patch: 32 };
|
|
36
36
|
const VALID_TEAMMATE_MODES = ['tmux', 'in-process', 'auto'];
|
|
37
|
-
const REQUIRED_IDS = ['node', 'claude'];
|
|
37
|
+
const REQUIRED_IDS = ['node', 'claude', 'jq'];
|
|
38
38
|
|
|
39
39
|
// --- Check functions (private) ---
|
|
40
40
|
|
|
@@ -166,11 +166,10 @@ function checkPlaywright(exec, cwd) {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// Determine exec command prefix (e.g. "npx" or "pnpm dlx")
|
|
169
|
-
const execCommand = raidConfig.execCommand || 'npx';
|
|
169
|
+
const execCommand = (raidConfig.project && raidConfig.project.execCommand) || 'npx';
|
|
170
170
|
|
|
171
171
|
// Check if playwright config file exists
|
|
172
172
|
const configFile = (raidConfig.browser && raidConfig.browser.playwrightConfig) || 'playwright.config.ts';
|
|
173
|
-
const configExists = cwd && fs.existsSync(path.join(cwd, configFile));
|
|
174
173
|
|
|
175
174
|
// Check playwright version
|
|
176
175
|
const raw = exec(`${execCommand} playwright --version`);
|
|
@@ -197,6 +196,45 @@ function checkPlaywright(exec, cwd) {
|
|
|
197
196
|
};
|
|
198
197
|
}
|
|
199
198
|
|
|
199
|
+
function checkJq(exec) {
|
|
200
|
+
const found = exec('command -v jq');
|
|
201
|
+
if (found) {
|
|
202
|
+
return {
|
|
203
|
+
id: 'jq',
|
|
204
|
+
ok: true,
|
|
205
|
+
label: 'jq',
|
|
206
|
+
detail: 'installed',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
id: 'jq',
|
|
211
|
+
ok: false,
|
|
212
|
+
label: 'jq',
|
|
213
|
+
detail: 'not found',
|
|
214
|
+
hint: process.platform === 'darwin'
|
|
215
|
+
? 'Install: brew install jq'
|
|
216
|
+
: 'Install jq via your package manager (apt, dnf, brew, etc.)',
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function checkPlatform(platform) {
|
|
221
|
+
if (platform === 'win32') {
|
|
222
|
+
return {
|
|
223
|
+
id: 'platform',
|
|
224
|
+
ok: false,
|
|
225
|
+
label: 'Platform',
|
|
226
|
+
detail: 'Windows is not supported — hooks require POSIX bash',
|
|
227
|
+
hint: 'Use WSL2 (Windows Subsystem for Linux) for full compatibility',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
id: 'platform',
|
|
232
|
+
ok: true,
|
|
233
|
+
label: 'Platform',
|
|
234
|
+
detail: platform,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
200
238
|
function checkSplitPane(exec) {
|
|
201
239
|
const tmux = exec('command -v tmux');
|
|
202
240
|
const it2 = exec('command -v it2');
|
|
@@ -273,9 +311,13 @@ function runChecks(opts = {}) {
|
|
|
273
311
|
|
|
274
312
|
const nodeVersion = opts.nodeVersion || undefined;
|
|
275
313
|
|
|
314
|
+
const platform = opts.platform || process.platform;
|
|
315
|
+
|
|
276
316
|
const checks = [
|
|
317
|
+
checkPlatform(platform),
|
|
277
318
|
checkNode(nodeVersion),
|
|
278
319
|
checkClaude(exec),
|
|
320
|
+
checkJq(exec),
|
|
279
321
|
checkTeammateMode(homedir),
|
|
280
322
|
checkSplitPane(exec),
|
|
281
323
|
checkPlaywright(exec, cwd),
|
package/src/ui.js
CHANGED
|
@@ -16,6 +16,7 @@ const colors = {
|
|
|
16
16
|
bold: wrap('1'),
|
|
17
17
|
boldAmber: wrap('1;33'),
|
|
18
18
|
boldRed: wrap('1;31'),
|
|
19
|
+
dimRed: wrap('2;31'),
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
function stripAnsi(str) {
|
|
@@ -23,7 +24,7 @@ function stripAnsi(str) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
function banner() {
|
|
26
|
-
const { amber, boldAmber, boldRed, dim } = colors;
|
|
27
|
+
const { amber, boldAmber, boldRed, red, dimRed, dim } = colors;
|
|
27
28
|
const rule = amber(' ⚔ ═══════════════════════════════════════════════════════ ⚔');
|
|
28
29
|
|
|
29
30
|
const claudeArt = [
|
|
@@ -44,14 +45,30 @@ function banner() {
|
|
|
44
45
|
' ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═════╝ ',
|
|
45
46
|
];
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
// 5-tone vertical gradient: boldAmber -> amber -> boldRed -> red -> dimRed
|
|
49
|
+
// Lines 1-2 (CLAUDE top): boldAmber
|
|
50
|
+
// Lines 3-5 (CLAUDE bottom + transition): amber
|
|
51
|
+
// Line 6 (CLAUDE/RAID boundary): boldRed
|
|
52
|
+
// Lines 7-8 (RAID top): boldRed
|
|
53
|
+
// Lines 9-11 (RAID middle): red
|
|
54
|
+
// Line 12 (RAID bottom): dimRed
|
|
55
|
+
const gradientColors = [
|
|
56
|
+
boldAmber, boldAmber, // claudeArt[0-1]
|
|
57
|
+
amber, amber, amber, // claudeArt[2-4]
|
|
58
|
+
boldRed, // claudeArt[5]
|
|
59
|
+
boldRed, boldRed, // raidArt[0-1]
|
|
60
|
+
red, red, red, // raidArt[2-4]
|
|
61
|
+
dimRed, // raidArt[5]
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const allArt = [...claudeArt, ...raidArt];
|
|
65
|
+
const tagline = ' Adversarial multi-agent development for Claude Code';
|
|
48
66
|
|
|
49
67
|
const lines = [
|
|
50
68
|
'',
|
|
51
69
|
rule,
|
|
52
70
|
'',
|
|
53
|
-
...
|
|
54
|
-
...raidArt.map(l => boldRed(l)),
|
|
71
|
+
...allArt.map((l, i) => gradientColors[i](l)),
|
|
55
72
|
'',
|
|
56
73
|
dim(tagline),
|
|
57
74
|
'',
|
|
@@ -100,4 +117,58 @@ function header(text) {
|
|
|
100
117
|
return ` ${amber(bold(`\u2694 ${text}`))}`;
|
|
101
118
|
}
|
|
102
119
|
|
|
103
|
-
|
|
120
|
+
function referenceCard() {
|
|
121
|
+
const howItWorks = box('How It Works', [
|
|
122
|
+
' You describe a task. The Wizard assesses complexity and',
|
|
123
|
+
' recommends a mode:',
|
|
124
|
+
'',
|
|
125
|
+
' ' + colors.bold('Full Raid') + ' 3 agents attack from competing angles',
|
|
126
|
+
' ' + colors.bold('Skirmish') + ' 2 agents, lighter process',
|
|
127
|
+
' ' + colors.bold('Scout') + ' 1 agent + Wizard review',
|
|
128
|
+
'',
|
|
129
|
+
' Every task flows through 4 phases:',
|
|
130
|
+
'',
|
|
131
|
+
' 1. ' + colors.bold('Design') + ' Agents explore and challenge the approach',
|
|
132
|
+
' 2. ' + colors.bold('Plan') + ' Agents decompose into testable tasks',
|
|
133
|
+
' 3. ' + colors.bold('Implement') + ' One builds (TDD), others attack',
|
|
134
|
+
' 4. ' + colors.bold('Review') + ' Independent reviews, fight over findings',
|
|
135
|
+
'',
|
|
136
|
+
' Hooks enforce discipline automatically:',
|
|
137
|
+
' ' + colors.dim('\u2022') + ' No implementation without a design doc',
|
|
138
|
+
' ' + colors.dim('\u2022') + ' No commits without passing tests',
|
|
139
|
+
' ' + colors.dim('\u2022') + ' No completion claims without fresh test evidence',
|
|
140
|
+
' ' + colors.dim('\u2022') + ' Conventional commit messages required',
|
|
141
|
+
'',
|
|
142
|
+
' ' + colors.dim('Hooks only activate during Raid sessions \u2014 they won\'t'),
|
|
143
|
+
' ' + colors.dim('interfere with normal coding outside of a Raid.'),
|
|
144
|
+
'',
|
|
145
|
+
' Config: ' + colors.bold('.claude/raid.json') + ' ' + colors.dim('project settings'),
|
|
146
|
+
' Rules: ' + colors.bold('.claude/raid-rules.md') + ' ' + colors.dim('editable team rules'),
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
const nextStep = box('Next Step', [
|
|
150
|
+
' ' + colors.bold('tmux new-session -s raid'),
|
|
151
|
+
' ' + colors.bold('claude --agent wizard'),
|
|
152
|
+
'',
|
|
153
|
+
' ' + colors.dim('Start tmux first, then the Wizard inside it.'),
|
|
154
|
+
' ' + colors.dim('Each agent gets its own tmux pane automatically.'),
|
|
155
|
+
' ' + colors.dim('Click any pane to talk to that agent directly.'),
|
|
156
|
+
'',
|
|
157
|
+
' ' + colors.dim('Tip: start with a small task (bugfix, config change) to'),
|
|
158
|
+
' ' + colors.dim('see the workflow before tackling something complex.'),
|
|
159
|
+
'',
|
|
160
|
+
' ' + colors.bold('Controls') + ' ' + colors.dim('(tmux)'),
|
|
161
|
+
' Click pane Switch to an agent',
|
|
162
|
+
' Ctrl+B + arrow Navigate between panes',
|
|
163
|
+
'',
|
|
164
|
+
' ' + colors.bold('Controls') + ' ' + colors.dim('(in-process, no tmux)'),
|
|
165
|
+
' Shift+Down Cycle through teammates',
|
|
166
|
+
' Ctrl+T Toggle the shared task list',
|
|
167
|
+
'',
|
|
168
|
+
' Review this anytime: ' + colors.bold('claude-raid heal'),
|
|
169
|
+
]);
|
|
170
|
+
|
|
171
|
+
return howItWorks + '\n' + nextStep;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = { colors, banner, box, header, stripAnsi, referenceCard };
|
|
@@ -6,6 +6,7 @@ description: >
|
|
|
6
6
|
verifies every claim. Zero trust in reports — reads code, traces chains. Zero ego —
|
|
7
7
|
concedes with evidence, moves on. Collaborates through rigor, not agreement.
|
|
8
8
|
model: claude-opus-4-6
|
|
9
|
+
tools: SendMessage, TaskCreate, TaskUpdate, Read, Grep, Glob, Bash, Write, Edit
|
|
9
10
|
effort: max
|
|
10
11
|
color: green
|
|
11
12
|
memory: project
|
|
@@ -100,6 +101,35 @@ Signals are shorthand for scanning, not ceremony:
|
|
|
100
101
|
- `DUNGEON:` — pinning a finding that survived challenge from at least two agents
|
|
101
102
|
- `WIZARD:` — you need project-level context or are genuinely stuck
|
|
102
103
|
|
|
104
|
+
## Team Communication
|
|
105
|
+
|
|
106
|
+
You are a team member. Your teammates are in separate tmux panes.
|
|
107
|
+
|
|
108
|
+
**Messaging teammates:**
|
|
109
|
+
- `SendMessage(to="wizard", message="...")` — escalate to the Wizard
|
|
110
|
+
- `SendMessage(to="warrior", message="...")` — challenge or build on Warrior's work
|
|
111
|
+
- `SendMessage(to="rogue", message="...")` — challenge or build on Rogue's work
|
|
112
|
+
|
|
113
|
+
Messages are delivered automatically. Idle teammates wake up when they receive a message.
|
|
114
|
+
|
|
115
|
+
**Discovering teammates:** Read the team config at `~/.claude/teams/{team_name}/config.json` to see your teammates' names.
|
|
116
|
+
|
|
117
|
+
**Task coordination:**
|
|
118
|
+
- `TaskCreate(subject="...", description="...")` — create a new task for discovered work
|
|
119
|
+
- `TaskUpdate(taskId="...", owner="archer")` — claim a task
|
|
120
|
+
- `TaskUpdate(taskId="...", status="completed")` — mark a task done
|
|
121
|
+
- Check `TaskList` after completing each task to find next available work
|
|
122
|
+
|
|
123
|
+
**The Dungeon is still your knowledge artifact.** Pin verified findings there via Write tool. Use SendMessage for real-time conversation and challenges. Both systems coexist.
|
|
124
|
+
|
|
125
|
+
## User Direct Access
|
|
126
|
+
|
|
127
|
+
The user can talk to you directly in your tmux pane. Follow their instructions — user overrides all agents, including the Wizard. If the user gives you a protocol-level instruction (skip a phase, change mode, implement something directly), follow it and notify the Wizard:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
SendMessage(to="wizard", message="User directed me to [X]. Proceeding.")
|
|
131
|
+
```
|
|
132
|
+
|
|
103
133
|
## Standards
|
|
104
134
|
|
|
105
135
|
- Every finding includes the exact location and the exact consequence.
|
|
@@ -6,6 +6,7 @@ description: >
|
|
|
6
6
|
claim. Zero trust in reports — reads code, constructs attacks. Zero ego — concedes
|
|
7
7
|
with evidence, moves on. Collaborates through rigor, not agreement.
|
|
8
8
|
model: claude-opus-4-6
|
|
9
|
+
tools: SendMessage, TaskCreate, TaskUpdate, Read, Grep, Glob, Bash, Write, Edit
|
|
9
10
|
effort: max
|
|
10
11
|
color: orange
|
|
11
12
|
memory: project
|
|
@@ -100,6 +101,35 @@ Signals are shorthand for scanning, not ceremony:
|
|
|
100
101
|
- `DUNGEON:` — pinning a finding that survived challenge from at least two agents
|
|
101
102
|
- `WIZARD:` — you need project-level context or are genuinely stuck
|
|
102
103
|
|
|
104
|
+
## Team Communication
|
|
105
|
+
|
|
106
|
+
You are a team member. Your teammates are in separate tmux panes.
|
|
107
|
+
|
|
108
|
+
**Messaging teammates:**
|
|
109
|
+
- `SendMessage(to="wizard", message="...")` — escalate to the Wizard
|
|
110
|
+
- `SendMessage(to="warrior", message="...")` — challenge or build on Warrior's work
|
|
111
|
+
- `SendMessage(to="archer", message="...")` — challenge or build on Archer's work
|
|
112
|
+
|
|
113
|
+
Messages are delivered automatically. Idle teammates wake up when they receive a message.
|
|
114
|
+
|
|
115
|
+
**Discovering teammates:** Read the team config at `~/.claude/teams/{team_name}/config.json` to see your teammates' names.
|
|
116
|
+
|
|
117
|
+
**Task coordination:**
|
|
118
|
+
- `TaskCreate(subject="...", description="...")` — create a new task for discovered work
|
|
119
|
+
- `TaskUpdate(taskId="...", owner="rogue")` — claim a task
|
|
120
|
+
- `TaskUpdate(taskId="...", status="completed")` — mark a task done
|
|
121
|
+
- Check `TaskList` after completing each task to find next available work
|
|
122
|
+
|
|
123
|
+
**The Dungeon is still your knowledge artifact.** Pin verified findings there via Write tool. Use SendMessage for real-time conversation and challenges. Both systems coexist.
|
|
124
|
+
|
|
125
|
+
## User Direct Access
|
|
126
|
+
|
|
127
|
+
The user can talk to you directly in your tmux pane. Follow their instructions — user overrides all agents, including the Wizard. If the user gives you a protocol-level instruction (skip a phase, change mode, implement something directly), follow it and notify the Wizard:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
SendMessage(to="wizard", message="User directed me to [X]. Proceeding.")
|
|
131
|
+
```
|
|
132
|
+
|
|
103
133
|
## Standards
|
|
104
134
|
|
|
105
135
|
- Every finding includes a concrete attack scenario or failure sequence.
|