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
88 lines (82 loc) • 3.14 kB
JavaScript
/**
* Hermes hook translator — emits a Python plugin stub at
* ~/.hermes/plugins/<id>.py that registers itself with Hermes' Python
* plugin system and shells out to the AIWG hook command.
*
* Hermes' hook surface per the parity assessment is Python-plugin-based;
* shelling out from Python is a common pattern, so the emitted plugin is
* a thin wrapper that subprocess.run()s the canonical AIWG command.
*/
import { promises as fs } from 'node:fs';
import * as path from 'node:path';
import { homedir } from 'node:os';
import { substituteEnvVars } from './shim.js';
const HERMES_EVENT_MAP = {
PreToolUse: 'pre_tool',
PostToolUse: 'post_tool',
UserPromptSubmit: 'pre_prompt',
SessionStart: 'session_start',
SessionEnd: 'session_end',
Stop: 'session_end',
};
function renderHermesPlugin(source) {
const events = source.events
.map((e) => HERMES_EVENT_MAP[e])
.filter((e) => Boolean(e));
const subCommand = substituteEnvVars(source.command, 'hermes');
const subArgs = (source.args || []).map((a) => substituteEnvVars(a, 'hermes'));
return `# AIWG-managed hook plugin (ADR-3 / PUW-018)
# Hook id: ${source.id}
# Description: ${source.description}
# Generated by AIWG; do not edit manually.
from hermes.plugin import register_hook
import os
import subprocess
EVENTS = ${JSON.stringify(events)}
COMMAND = ${JSON.stringify(subCommand)}
ARGS = ${JSON.stringify(subArgs)}
AIWG_ID = ${JSON.stringify(source.id)}
SAFETY_CRITICAL = ${source.safetyCritical ? 'True' : 'False'}
def _run(payload=None):
env = dict(os.environ)
env.setdefault('AIWG_PROJECT_DIR', os.environ.get('HERMES_PROJECT_DIR', os.getcwd()))
env['AIWG_HOOK_EVENT'] = payload.get('event', 'unknown') if payload else 'unknown'
cmd = [COMMAND] + ARGS if not COMMAND.startswith('#!') else [COMMAND, *ARGS]
return subprocess.run(cmd, env=env, check=False).returncode
for _event in EVENTS:
register_hook(_event, _run, aiwg_managed=True, aiwg_id=AIWG_ID,
safety_critical=SAFETY_CRITICAL)
`;
}
export async function translateForHermes(source, options) {
const result = {
provider: 'hermes',
emittedPaths: [],
warnings: [],
skipped: false,
};
if (source.degradeOn?.includes('hermes')) {
result.skipped = true;
result.skipReason = 'degrade-on declared hermes';
return result;
}
const events = source.events
.map((e) => HERMES_EVENT_MAP[e])
.filter((e) => Boolean(e));
if (events.length === 0) {
result.skipped = true;
result.skipReason = 'no Hermes-mapped events';
return result;
}
const pluginDir = path.join(homedir(), '.hermes', 'plugins');
const pluginPath = path.join(pluginDir, `${source.id}.py`);
if (options.dryRun) {
result.emittedPaths.push(pluginPath + ' (dry-run)');
return result;
}
await fs.mkdir(pluginDir, { recursive: true });
await fs.writeFile(pluginPath, renderHermesPlugin(source), 'utf8');
result.emittedPaths.push(pluginPath);
return result;
}
//# sourceMappingURL=hermes-translator.js.map