UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

178 lines 6.89 kB
/** * Agent Spawn Utility * * Centralized utility for spawning agentic CLI tools across providers. * Handles --dangerous, --provider, and --params passthrough flags uniformly * across all commands that invoke an underlying agent system. * * Usage pattern: * const { opts, remaining } = parseAgentSpawnFlags(ctx.args); * const config = getProviderConfig(opts.provider); * const spawnArgs = buildAgentArgs(prompt, opts); * spawn(config.binary, spawnArgs, { stdio: 'inherit' }); */ export const PROVIDER_CONFIGS = { claude: { binary: 'claude', dangerousFlag: '--dangerously-skip-permissions', name: 'Claude Code', }, opencode: { binary: 'opencode', // OpenCode uses `opencode run "<prompt>"` for non-interactive execution. // Dangerous mode flag not yet confirmed; omit rather than guess. promptPrefix: ['run'], dangerousFlag: null, name: 'OpenCode', }, codex: { binary: 'codex', // Codex supports --full-auto (no approval prompts) and --approval-mode full-auto dangerousFlag: '--full-auto', name: 'OpenAI Codex', }, hermes: { // Hermes is a model series (NousResearch), not a confirmed standalone CLI. // Treat as IDE/runtime-integrated until a CLI is confirmed. binary: null, dangerousFlag: null, name: 'Hermes', guidanceMessage: 'Hermes does not have a confirmed standalone CLI.\n' + 'Run via Ollama (`ollama run hermes3`) or through your configured MCP sidecar.', }, copilot: { binary: null, dangerousFlag: null, name: 'GitHub Copilot', guidanceMessage: 'GitHub Copilot is IDE-integrated and cannot be spawned from the CLI.\n' + 'Open your IDE, navigate to the Copilot chat panel, and run the skill manually.', }, cursor: { binary: null, dangerousFlag: null, name: 'Cursor', guidanceMessage: 'Cursor is IDE-integrated and cannot be spawned from the CLI.\n' + 'For unrestricted AIWG tool access, use the MCP sidecar:\n' + ' aiwg mcp install cursor && aiwg mcp serve\n' + 'See: docs/integrations/cursor-mcp-sidecar.md', }, factory: { binary: null, dangerousFlag: null, name: 'Factory AI', guidanceMessage: 'Factory AI is cloud-based and cannot be spawned from the CLI.\n' + 'Use the Factory AI dashboard to dispatch the workflow.', }, warp: { binary: null, dangerousFlag: null, name: 'Warp Terminal', guidanceMessage: 'Warp Terminal cannot be spawned programmatically.\n' + 'For unrestricted AIWG tool access, use the MCP sidecar:\n' + ' aiwg mcp install warp && aiwg mcp serve\n' + 'See: docs/integrations/warp-mcp-sidecar.md', }, windsurf: { binary: null, dangerousFlag: null, name: 'Windsurf', guidanceMessage: 'Windsurf is IDE-integrated and cannot be spawned from the CLI.\n' + 'For unrestricted AIWG tool access, use the MCP sidecar:\n' + ' aiwg mcp install windsurf && aiwg mcp serve\n' + 'See: docs/integrations/windsurf-mcp-sidecar.md', }, }; /** * Extract --provider, --dangerous, and --params from an args array. * Returns the parsed options and the remaining args with those flags removed. * * This is non-destructive — the original array is not modified. */ export function parseAgentSpawnFlags(args) { const opts = {}; const remaining = []; for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg === '--provider' && i + 1 < args.length) { opts.provider = args[++i]; } else if (arg === '--dangerous') { opts.dangerous = true; } else if (arg === '--params' && i + 1 < args.length) { opts.params = args[++i]; } else { remaining.push(arg); } } return { opts, remaining }; } // ── Arg construction ────────────────────────────────────────── /** * Build the final args array for spawning a provider binary. * * Order: [prompt, dangerous-flag?, ...raw-params] */ export function buildAgentArgs(prompt, opts) { const config = getProviderConfig(opts.provider ?? 'claude'); const args = []; // Dangerous flag must precede the prompt — it is a CLI flag to the binary, // not content to pass to the agent. e.g. `claude --dangerously-skip-permissions "<prompt>"` if (opts.dangerous && config.dangerousFlag) { args.push(config.dangerousFlag); // If provider has no dangerous flag, silently ignored — // callers should warn via dangerousWarning(). } // Some providers require a subcommand before the prompt (e.g. `opencode run "<prompt>"`) args.push(...(config.promptPrefix ?? [])); // The prompt args.push(prompt); // User passthrough params — appended after the prompt verbatim if (opts.params) { args.push(...splitParams(opts.params)); } return args; } /** * Split a params string into individual args, respecting double/single quotes. * "hello world" → ['hello world'] * --flag value → ['--flag', 'value'] */ export function splitParams(params) { const result = []; const re = /(?:[^\s"']+|"[^"]*"|'[^']*')+/g; let match; while ((match = re.exec(params)) !== null) { // Strip surrounding quotes from individual tokens result.push(match[0].replace(/^["']|["']$/g, '')); } return result; } // ── Provider helpers ────────────────────────────────────────── /** Get config for a provider, falling back to claude for unknown values. */ export function getProviderConfig(provider) { return PROVIDER_CONFIGS[provider] ?? PROVIDER_CONFIGS['claude']; } /** Returns true if the provider has a CLI binary that can be spawned. */ export function isSpawnableProvider(provider) { return getProviderConfig(provider).binary !== null; } /** * Warn string if --dangerous was requested but the provider has no * corresponding flag. Returns null if the combination is valid. */ export function dangerousWarning(provider) { const config = getProviderConfig(provider); if (!config.dangerousFlag) { return `--dangerous is not supported for provider '${config.name}' and will be ignored.`; } return null; } /** List all spawnable provider names. */ export function spawnableProviders() { return Object.entries(PROVIDER_CONFIGS) .filter(([, c]) => c.binary !== null) .map(([k]) => k); } //# sourceMappingURL=agent-spawn.js.map