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

89 lines 3.75 kB
/** * Hook bridge shim — env-var contract substitution + canonical schemas. * * Per ADR-3 §2: hook authors write canonical $AIWG_* env vars. Translators * substitute them with the provider-native equivalents at deploy time so * authors don't need to maintain N copies of the same hook. */ import { AIWG_ENV_VARS, NATIVE_ENV_VAR_MAP } from './types.js'; /** * Substitute canonical AIWG env vars with the provider's native equivalents. * * Example for codex: * "$AIWG_PROJECT_DIR/scripts/scan.sh" → "$CODEX_WORKSPACE/scripts/scan.sh" * * When a provider has no native equivalent for a given canonical var, the * canonical name is preserved. Hook authors who need the value despite the * gap can rely on the runtime shim setting $AIWG_PROJECT_DIR (etc.) before * invoking the command — that's the per-ADR-3-§2 fallback. */ export function substituteEnvVars(input, provider) { const mapping = NATIVE_ENV_VAR_MAP[provider]; if (!mapping) return input; let out = input; for (const [canonicalKey, canonicalRef] of Object.entries(AIWG_ENV_VARS)) { const native = mapping[canonicalKey]; if (!native) continue; // Replace both $VAR and ${VAR} forms. const escaped = canonicalRef.replace(/\$/g, '\\$'); out = out.replace(new RegExp(escaped + '\\b', 'g'), native); out = out.replace(new RegExp('\\${' + canonicalRef.slice(1) + '}', 'g'), native); } return out; } /** * Canonical exit-code mapping per ADR-3 §4. * * AIWG canonical: * 0 = allow * 1 = block * 2 = warn-and-continue * * Provider mapping returns the native exit code(s) that mean each AIWG * semantic. Used by the wrapper shim to translate hook exit codes back to * what the provider expects. */ export const EXIT_CODE_MAP = { claude: { allow: 0, block: 1, warn: 0 /* warn surfaces via stderr only */ }, codex: { allow: 0, block: 1, warn: 2 }, copilot: { allow: 0, block: 1, warn: 0 }, factory: { allow: 0, block: 1, warn: 0 }, hermes: { allow: 0, block: 1, warn: 2 }, openclaw: { allow: 0, block: 1, warn: 2 }, }; /** * Generate a small bash shim that: * 1. exports $AIWG_PROJECT_DIR + $AIWG_TOOL_NAME from native env vars * 2. reads native stdin, transforms to AIWG canonical JSON * 3. pipes to the hook command * 4. translates the exit code back per ADR-3 §4 * * The shim is what gets installed as the actual hook artifact on the * provider; the AIWG hook command runs inside it. */ export function generateShimBash(provider, hookCommand, args = []) { const native = NATIVE_ENV_VAR_MAP[provider] || {}; const projectDirSrc = native.PROJECT_DIR || '$PWD'; const toolNameSrc = native.TOOL_NAME || '"${AIWG_TOOL_NAME:-unknown}"'; // Substitute canonical env vars in the command + args ahead of shim emission // so the shim invocation references native names where available. const subCommand = substituteEnvVars(hookCommand, provider); const subArgs = args.map((a) => substituteEnvVars(a, provider)); return [ '#!/usr/bin/env bash', `# AIWG hook bridge shim (ADR-3) — provider: ${provider}`, 'set -e', '', `export AIWG_PROJECT_DIR="${projectDirSrc}"`, `export AIWG_TOOL_NAME=${toolNameSrc}`, `export AIWG_HOOK_EVENT="${'${AIWG_HOOK_EVENT:-unknown}'}"`, '', '# Forward stdin verbatim. Hook command can re-parse if it needs the', '# canonical AIWG schema; the env-var contract above is the primary', '# integration surface for cross-provider portability.', `${subCommand}${subArgs.length ? ' ' + subArgs.map((a) => `"${a}"`).join(' ') : ''}`, ].join('\n'); } //# sourceMappingURL=shim.js.map