Last active
February 2, 2026 15:15
-
-
Save syabro/0ca27bdfebf2ca1592808e0617a1ae92 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env node | |
| /** | |
| * Claude Code Swarm Mode Patcher | |
| * | |
| * Enables native multi-agent features (TeammateTool, delegate mode, swarm spawning) | |
| * by patching the tengu_brass_pebble statsig gate to always return true. | |
| * | |
| * Updated for claude-code v2.1.29+ (new gate function structure). | |
| * | |
| * Usage: | |
| * node patch-swarm.mjs | |
| */ | |
| import fs from 'node:fs'; | |
| import path from 'node:path'; | |
| import { execSync } from 'node:child_process'; | |
| import readline from 'node:readline'; | |
| // === Swarm Mode Patch Logic === | |
| const SWARM_GATE_MARKER = /tengu_brass_pebble/; | |
| // Regex for the unpatched function in newer versions (v2.1.29+) | |
| // Matches: function i8(){if(sY(process.env.CLAUDE_CODE_AGENT_SWARMS))return!1;if(!J7("tengu_brass_pebble",!1))return!1; | |
| const SWARM_GATE_RE = /function\s+([a-zA-Z_$][\w$]*)\(\)\{if\([\w$]+\(process\.env\.CLAUDE_CODE_AGENT_SWARMS\)\)return!1;if\(![\w$]+\("tengu_brass_pebble",!1\)\)return!1;/; | |
| // Regex for the patched function (we inject return!0; at the start) | |
| // Matches: function i8(){return!0;if(sY(process.env.CLAUDE_CODE_AGENT_SWARMS))return!1;if(!J7("tengu_brass_pebble",!1))return!1; | |
| const SWARM_GATE_PATCHED_RE = /function\s+([a-zA-Z_$][\w$]*)\(\)\{return!0;if\([\w$]+\(process\.env\.CLAUDE_CODE_AGENT_SWARMS\)\)return!1;if\(![\w$]+\("tengu_brass_pebble",!1\)\)return!1;/; | |
| // Legacy regex for older versions (optional, kept for backward compatibility if structure matches) | |
| const LEGACY_SWARM_GATE_RE = /function\s+([a-zA-Z_$][\w$]*)\(\)\{if\([\w$]+\(process\.env\.CLAUDE_CODE_AGENT_SWARMS\)\)return!1;return\s*[\w$]+\("tengu_brass_pebble",!1\)\}/; | |
| const findSwarmGateFunction = (content) => { | |
| // 1. Check for new version unpatched | |
| let match = content.match(SWARM_GATE_RE); | |
| if (match) return { fnName: match[1], fullMatch: match[0], patched: false, type: 'new' }; | |
| // 2. Check for new version patched | |
| match = content.match(SWARM_GATE_PATCHED_RE); | |
| if (match) return { fnName: match[1], fullMatch: match[0], patched: true, type: 'new' }; | |
| // 3. Check for legacy version unpatched | |
| match = content.match(LEGACY_SWARM_GATE_RE); | |
| if (match) return { fnName: match[1], fullMatch: match[0], patched: false, type: 'legacy' }; | |
| // Note: Legacy patched detection is hard because we replaced the whole body. | |
| // If we can't find the gate but marker exists, we assume unknown or custom patched. | |
| return null; | |
| }; | |
| const detectSwarmModeState = (content) => { | |
| const gate = findSwarmGateFunction(content); | |
| if (gate) { | |
| return gate.patched ? 'enabled' : 'disabled'; | |
| } | |
| if (!SWARM_GATE_MARKER.test(content)) { | |
| // If marker is gone, maybe it's fully enabled/removed? | |
| const hasSwarmCode = /TeammateTool|teammate_mailbox|launchSwarm/.test(content); | |
| if (hasSwarmCode) return 'enabled'; | |
| return 'unknown'; | |
| } | |
| // Marker exists but regex didn't match -> Unknown structure | |
| return 'unknown'; | |
| }; | |
| const setSwarmModeEnabled = (content) => { | |
| const gate = findSwarmGateFunction(content); | |
| if (!gate) { | |
| const currentState = detectSwarmModeState(content); | |
| return { content, changed: false, state: currentState }; | |
| } | |
| if (gate.patched) { | |
| return { content, changed: false, state: 'enabled' }; | |
| } | |
| let patchedContent; | |
| if (gate.type === 'legacy') { | |
| // Legacy: Replace entire function body | |
| patchedContent = content.replace(gate.fullMatch, `function ${gate.fnName}(){return!0}`); | |
| } else { | |
| // New: Inject return!0; at the start | |
| // gate.fullMatch is "function name(){if(..." | |
| // We want "function name(){return!0;if(..." | |
| const prefix = `function ${gate.fnName}(){`; | |
| const replacement = prefix + "return!0;" + gate.fullMatch.substring(prefix.length); | |
| patchedContent = content.replace(gate.fullMatch, replacement); | |
| } | |
| return { content: patchedContent, changed: true, state: 'enabled' }; | |
| }; | |
| // === CLI Discovery === | |
| const findClaudePath = () => { | |
| try { | |
| const claudePath = execSync('which claude', { encoding: 'utf8' }).trim(); | |
| if (!claudePath) return null; | |
| return fs.realpathSync(claudePath); | |
| } catch { | |
| return null; | |
| } | |
| }; | |
| const findCliJs = (claudePath) => { | |
| // claude binary -> resolve symlinks -> find cli.js in the package | |
| let dir = path.dirname(claudePath); | |
| // Walk up to find node_modules/@anthropic-ai/claude-code/cli.js | |
| for (let i = 0; i < 10; i++) { | |
| const cliJs = path.join(dir, 'cli.js'); | |
| if (fs.existsSync(cliJs)) { | |
| const content = fs.readFileSync(cliJs, 'utf8'); | |
| if (content.includes('@anthropic-ai/claude-code') || content.includes('TeammateTool') || content.includes('tengu_brass_pebble')) { | |
| return cliJs; | |
| } | |
| } | |
| // Check if we're in node_modules structure | |
| const packageJson = path.join(dir, 'package.json'); | |
| if (fs.existsSync(packageJson)) { | |
| try { | |
| const pkg = JSON.parse(fs.readFileSync(packageJson, 'utf8')); | |
| if (pkg.name === '@anthropic-ai/claude-code') { | |
| const cliPath = path.join(dir, 'cli.js'); | |
| if (fs.existsSync(cliPath)) return cliPath; | |
| } | |
| } catch {} | |
| } | |
| const parent = path.dirname(dir); | |
| if (parent === dir) break; | |
| dir = parent; | |
| } | |
| return null; | |
| }; | |
| // === User Interaction === | |
| const askQuestion = (question) => { | |
| const rl = readline.createInterface({ | |
| input: process.stdin, | |
| output: process.stdout, | |
| }); | |
| return new Promise((resolve) => { | |
| rl.question(question, (answer) => { | |
| rl.close(); | |
| resolve(answer.trim().toLowerCase()); | |
| }); | |
| }); | |
| }; | |
| // === Main === | |
| const main = async () => { | |
| console.log('Claude Code Swarm Mode Patcher\n'); | |
| // Step 1: Find claude | |
| console.log('Looking for claude...'); | |
| const claudePath = findClaudePath(); | |
| if (!claudePath) { | |
| console.error('Error: claude not found. Make sure it\'s installed and in PATH.'); | |
| process.exit(1); | |
| } | |
| console.log(`Found claude: ${claudePath}`); | |
| // Step 2: Find cli.js | |
| console.log('\nLooking for cli.js...'); | |
| const cliJsPath = findCliJs(claudePath); | |
| if (!cliJsPath) { | |
| console.error('Error: cli.js not found in claude installation.'); | |
| process.exit(1); | |
| } | |
| console.log(`Found cli.js: ${cliJsPath}`); | |
| // Step 3: Check current state | |
| console.log('\nAnalyzing swarm mode state...'); | |
| const content = fs.readFileSync(cliJsPath, 'utf8'); | |
| const state = detectSwarmModeState(content); | |
| console.log(`Current state: ${state}`); | |
| if (state === 'enabled') { | |
| console.log('\nSwarm mode is already enabled. Nothing to do.'); | |
| process.exit(0); | |
| } | |
| if (state === 'unknown') { | |
| console.error('\nError: Cannot detect swarm gate. The file structure might have changed.'); | |
| process.exit(1); | |
| } | |
| // Step 4: Ask user | |
| console.log('\nSwarm mode enables:'); | |
| console.log(' - TeammateTool for team coordination'); | |
| console.log(' - Delegate mode for Task tool'); | |
| console.log(' - Swarm spawning via ExitPlanMode'); | |
| console.log(' - Teammate mailbox/messaging'); | |
| console.log(' - Task ownership and claiming\n'); | |
| const answer = await askQuestion('Patch cli.js to enable swarm mode? (y/N): '); | |
| if (answer !== 'y' && answer !== 'yes') { | |
| console.log('Aborted.'); | |
| process.exit(0); | |
| } | |
| // Step 5: Create backup and patch | |
| const backupPath = `${cliJsPath}.backup`; | |
| if (!fs.existsSync(backupPath)) { | |
| console.log(`\nCreating backup: ${backupPath}`); | |
| fs.copyFileSync(cliJsPath, backupPath); | |
| } | |
| console.log('Applying patch...'); | |
| const result = setSwarmModeEnabled(content); | |
| if (!result.changed) { | |
| console.error('Error: Patch failed - no changes made.'); | |
| process.exit(1); | |
| } | |
| fs.writeFileSync(cliJsPath, result.content); | |
| // Step 6: Verify | |
| const verifyContent = fs.readFileSync(cliJsPath, 'utf8'); | |
| const verifyState = detectSwarmModeState(verifyContent); | |
| if (verifyState !== 'enabled') { | |
| console.error('Error: Patch verification failed.'); | |
| process.exit(1); | |
| } | |
| console.log('\nSwarm mode enabled successfully!'); | |
| console.log(`Backup saved at: ${backupPath}`); | |
| }; | |
| main().catch((err) => { | |
| console.error('Error:', err.message); | |
| process.exit(1); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment