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

269 lines (221 loc) 7.67 kB
#!/usr/bin/env node /** * CLI tool to add a behavior to an existing addon or framework * Usage: aiwg add-behavior <name> [options] * * Behaviors are reactive capabilities with scripts + hooks + structured inputs. * They extend beyond skills by subscribing to system events. */ import { parseArgs, formatName, ensureDir, writeFileIfNotExists, printSuccess, printError, printInfo, printHeader, } from './utils.mjs'; import { existsSync, writeFileSync, chmodSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const repoRoot = join(__dirname, '..', '..'); const { positional, flags } = parseArgs(process.argv); const name = positional[0]; const behaviorDescription = flags.description || flags.d || `${formatName(name || 'behavior').title} behavior`; const hooks = flags.hooks || 'on_file_write'; const category = flags.category || 'general'; const dryRun = flags['dry-run'] || flags.n; const help = flags.help || flags.h; function printHelp() { console.log(` Usage: aiwg add-behavior <name> [options] Add a new behavior to the cross-framework behaviors directory. Behaviors are reactive capabilities with scripts + hooks + structured inputs. They go beyond skills by subscribing to system events (file writes, deploys, schedules) and reacting automatically. Arguments: name Behavior name (kebab-case recommended) Options: --description, -d Behavior description --hooks Comma-separated hook events (default: on_file_write) Available: on_file_write, on_tool_complete, on_schedule, on_commit, on_pr_open, on_deploy, on_session_start, on_session_end --category Behavior category (default: general) --dry-run, -n Preview what would be created --help, -h Show this help Examples: aiwg add-behavior security-scanner aiwg add-behavior test-watcher --hooks on_file_write,on_schedule --category testing aiwg add-behavior deploy-guard --hooks on_deploy --description "Pre-deploy validation" `); } function generateHooksYaml(hooksStr) { const hookList = hooksStr.split(',').map(h => h.trim()); const lines = []; for (const hook of hookList) { lines.push(` ${hook}:`); switch (hook) { case 'on_file_write': lines.push(' - filter: "**/*"'); lines.push(' action: run_script'); lines.push(' script: scripts/main.sh'); break; case 'on_tool_complete': lines.push(' - tool: build'); lines.push(' action: run_script'); lines.push(' script: scripts/main.sh'); break; case 'on_schedule': lines.push(' - cron: "0 */4 * * *"'); lines.push(' action: run_script'); lines.push(' script: scripts/main.sh'); break; case 'on_deploy': lines.push(' - action: run_script'); lines.push(' script: scripts/main.sh'); break; case 'on_commit': lines.push(' - action: run_script'); lines.push(' script: scripts/main.sh'); break; case 'on_pr_open': lines.push(' - action: run_script'); lines.push(' script: scripts/main.sh'); break; default: lines.push(' - action: run_script'); lines.push(' script: scripts/main.sh'); } } return lines.join('\n'); } function generateBehaviorMd(name, options) { const { kebab, title } = formatName(name); const hooksYaml = generateHooksYaml(options.hooks); return `--- name: ${kebab} version: 1.0.0 description: ${options.description} platforms: [openclaw, claude-code] triggers: - "TODO: add NLP trigger phrase" inputs: - name: target type: string required: false description: TODO: describe input hooks: ${hooksYaml} scripts: main: scripts/main.sh manifest: category: ${options.category} requires: bins: [] outputs: - type: report path: .aiwg/reports/ --- # ${title} ${options.description} ## When Triggered via NLP TODO: describe what happens when a user says the trigger phrase. ## When Triggered via Hooks TODO: describe what happens for each hook event. `; } function generateMainScript(name) { const { kebab } = formatName(name); return `#!/usr/bin/env bash # ${kebab} — main entry point # # Invoked when the behavior is triggered via NLP or hooks. # # Environment variables (set by the platform): # BEHAVIOR_NAME — "${kebab}" # BEHAVIOR_TRIGGER — The trigger phrase (NLP) or hook event # HOOK_EVENT — Hook event type (if triggered by hook) # HOOK_FILE_PATH — Changed file path (for on_file_write) # HOOK_TOOL — Tool name (for on_tool_complete) # PROJECT_ROOT — Project root directory # INPUT_* — Structured inputs from BEHAVIOR.md set -euo pipefail PROJECT="\${PROJECT_ROOT:-.}" echo "${kebab}: running" # TODO: implement behavior logic echo "${kebab}: complete" `; } async function main() { if (help) { printHelp(); process.exit(0); } if (!name) { printError('Behavior name is required.'); printHelp(); process.exit(1); } const { kebab, title } = formatName(name); // Behaviors go in the cross-framework behaviors directory const behaviorsDir = join(repoRoot, 'agentic', 'code', 'behaviors'); const behaviorDir = join(behaviorsDir, kebab); const behaviorMdPath = join(behaviorDir, 'BEHAVIOR.md'); const scriptsDir = join(behaviorDir, 'scripts'); const mainScriptPath = join(scriptsDir, 'main.sh'); // Check if behavior already exists if (existsSync(behaviorDir)) { printError(`Behavior already exists: ${behaviorDir}`); process.exit(1); } printHeader(`Adding Behavior: ${title}`); // Generate content const behaviorMdContent = generateBehaviorMd(name, { description: behaviorDescription, hooks, category, }); const mainScriptContent = generateMainScript(name); if (dryRun) { console.log('\n[DRY RUN] Would create:\n'); console.log(` ${behaviorDir}/`); console.log(` ${behaviorMdPath}`); console.log(` ${scriptsDir}/`); console.log(` ${mainScriptPath}`); console.log('\nBEHAVIOR.md content preview:'); console.log('\u2500'.repeat(40)); console.log(behaviorMdContent.slice(0, 600) + '...'); console.log('\nRun without --dry-run to create.'); process.exit(0); } // Create directory structure ensureDir(behaviorDir); ensureDir(scriptsDir); printSuccess(`Created ${behaviorDir}/`); // Write BEHAVIOR.md writeFileIfNotExists(behaviorMdPath, behaviorMdContent, { force: true }); printSuccess(`Created ${behaviorMdPath}`); // Write main.sh writeFileIfNotExists(mainScriptPath, mainScriptContent, { force: true }); try { chmodSync(mainScriptPath, 0o755); } catch { /* ignore on platforms without chmod */ } printSuccess(`Created ${mainScriptPath}`); // Summary printHeader('Behavior Added Successfully'); printInfo(`Behavior: ${kebab}`); printInfo(`Location: ${behaviorDir}/`); printInfo(''); printInfo('Next steps:'); printInfo(' 1. Edit BEHAVIOR.md — set triggers, inputs, and hooks'); printInfo(' 2. Implement scripts/main.sh'); printInfo(' 3. Add additional scripts for each hook event'); printInfo(' 4. Deploy: aiwg use sdlc --provider openclaw'); printInfo(' 5. Test: openclaw skills list | grep ' + kebab); console.log(''); } main().catch(err => { printError(err.message); process.exit(1); });