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
175 lines (152 loc) • 4.9 kB
JavaScript
/**
* Behavior management CLI
*
* Manage behavior YAML bundles that bind directives and toolsets to agent types.
* Usage: aiwg behavior <list|info|apply|remove> [name] [options]
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { createClient } from './ipc-client.mjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.join(__dirname, '..', '..');
const SOCKET_PATH = '.aiwg/daemon/daemon.sock';
const args = process.argv.slice(2);
const subcommand = args[0];
const name = args[1];
function getBehaviorDirs() {
const dirs = [];
// Cross-framework behaviors
const globalDir = path.join(repoRoot, 'agentic', 'code', 'behaviors');
if (fs.existsSync(globalDir)) {
for (const entry of fs.readdirSync(globalDir, { withFileTypes: true })) {
if (entry.isDirectory() && fs.existsSync(path.join(globalDir, entry.name, 'BEHAVIOR.md'))) {
dirs.push({ name: entry.name, path: path.join(globalDir, entry.name), scope: 'global' });
}
}
}
// Per-framework behaviors
const frameworksDir = path.join(repoRoot, 'agentic', 'code', 'frameworks');
if (fs.existsSync(frameworksDir)) {
for (const fw of fs.readdirSync(frameworksDir, { withFileTypes: true })) {
if (!fw.isDirectory()) continue;
const fwBehaviorsDir = path.join(frameworksDir, fw.name, 'behaviors');
if (!fs.existsSync(fwBehaviorsDir)) continue;
for (const entry of fs.readdirSync(fwBehaviorsDir, { withFileTypes: true })) {
if (entry.isDirectory() && fs.existsSync(path.join(fwBehaviorsDir, entry.name, 'BEHAVIOR.md'))) {
dirs.push({ name: entry.name, path: path.join(fwBehaviorsDir, entry.name), scope: fw.name });
}
}
}
}
return dirs;
}
function listBehaviors() {
const behaviors = getBehaviorDirs();
if (behaviors.length === 0) {
console.log('No behaviors found.');
return;
}
console.log(`\nBehaviors (${behaviors.length}):\n`);
for (const b of behaviors) {
console.log(` ${b.name} (${b.scope})`);
}
console.log('');
}
function infoBehavior(behaviorName) {
if (!behaviorName) {
console.error('Usage: aiwg behavior info <name>');
process.exit(1);
}
const behaviors = getBehaviorDirs();
const found = behaviors.find(b => b.name === behaviorName);
if (!found) {
console.error(`Behavior not found: ${behaviorName}`);
process.exit(1);
}
const content = fs.readFileSync(path.join(found.path, 'BEHAVIOR.md'), 'utf-8');
console.log(content);
}
function printHelp() {
console.log(`
Usage: aiwg behavior <subcommand> [name] [options]
Subcommands:
list List all available behaviors
info <name> Show behavior details (BEHAVIOR.md)
apply <name> Apply a behavior to the daemon (not yet implemented)
remove <name> Remove a behavior from the daemon (not yet implemented)
Examples:
aiwg behavior list
aiwg behavior info security-sentinel
`);
}
async function applyBehavior(behaviorName) {
if (!behaviorName) {
console.error('Usage: aiwg behavior apply <name>');
process.exit(1);
}
let client;
try {
client = await createClient(SOCKET_PATH);
const result = await client.call('behaviors.apply', { name: behaviorName });
console.log(`Behavior applied: ${result.name}`);
} catch (err) {
if (err.message && err.message.includes('not running')) {
console.error('Daemon is not running. Start it with: aiwg daemon start');
} else {
console.error(`Failed to apply behavior '${behaviorName}': ${err.message}`);
}
process.exit(1);
} finally {
client?.disconnect();
}
}
async function removeBehavior(behaviorName) {
if (!behaviorName) {
console.error('Usage: aiwg behavior remove <name>');
process.exit(1);
}
let client;
try {
client = await createClient(SOCKET_PATH);
const result = await client.call('behaviors.remove', { name: behaviorName });
if (result.removed) {
console.log(`Behavior removed: ${result.name}`);
} else {
console.log(`Behavior '${behaviorName}' was not active`);
}
} catch (err) {
if (err.message && err.message.includes('not running')) {
console.error('Daemon is not running. Start it with: aiwg daemon start');
} else {
console.error(`Failed to remove behavior '${behaviorName}': ${err.message}`);
}
process.exit(1);
} finally {
client?.disconnect();
}
}
switch (subcommand) {
case 'list':
listBehaviors();
break;
case 'info':
infoBehavior(name);
break;
case 'apply':
applyBehavior(name);
break;
case 'remove':
removeBehavior(name);
break;
case '--help':
case '-h':
case undefined:
printHelp();
break;
default:
console.error(`Unknown subcommand: ${subcommand}`);
printHelp();
process.exit(1);
}