ktg-plugin-marketplace/plugins/llm-security-copilot/hooks/scripts/lib/copilot-adapter.mjs
Kjell Tore Guttormsen f418a8fe08 feat(llm-security-copilot): port llm-security v5.1.0 to GitHub Copilot CLI
Full port of llm-security plugin for internal use on Windows with GitHub
Copilot CLI. Protocol translation layer (copilot-hook-runner.mjs)
normalizes Copilot camelCase I/O to Claude Code snake_case format — all
original hook scripts run unmodified.

- 8 hooks with protocol translation (stdin/stdout/exit code)
- 18 SKILL.md skills (Agent Skills Open Standard)
- 6 .agent.md agent definitions
- 20 scanners + 14 scanner lib modules (unchanged)
- 14 knowledge files (unchanged)
- 39 test files including copilot-port-verify.mjs (17 tests)
- Windows-ready: node:path, os.tmpdir(), process.execPath, no bash

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 21:56:10 +02:00

73 lines
1.8 KiB
JavaScript

#!/usr/bin/env node
// copilot-adapter.mjs — Normalizes GitHub Copilot hook I/O to internal format.
//
// Copilot CLI hooks receive JSON on stdin with camelCase field names
// (toolName, toolArgs, toolResult) and signal blocking via exit code 2
// or stdout JSON with permissionDecision: "deny".
//
// This adapter provides a consistent interface so hook logic stays
// platform-agnostic. If Copilot changes field names, only this file
// needs updating.
import { readFileSync } from 'node:fs';
/**
* Parse hook input from stdin. Returns normalized object or null on failure.
* Supports both Copilot (camelCase) and Claude Code (snake_case) field names.
*/
export function parseInput() {
try {
const raw = readFileSync(0, 'utf-8');
const input = JSON.parse(raw);
return {
toolName: input.toolName ?? input.tool_name ?? '',
toolInput: input.toolArgs ?? input.tool_input ?? {},
toolOutput: input.toolResult ?? input.tool_output ?? '',
message: input.message ?? {},
sessionId: input.sessionId ?? input.session_id ?? '',
raw: input,
};
} catch {
return null;
}
}
/**
* Block the tool call with a reason message.
*/
export function block(reason) {
process.stderr.write(reason + '\n');
process.stdout.write(JSON.stringify({
permissionDecision: 'deny',
reason,
}));
process.exit(2);
}
/**
* Allow the tool call, optionally with an advisory message.
*/
export function allow(message) {
if (message) {
process.stdout.write(JSON.stringify({
permissionDecision: 'allow',
message,
}));
}
process.exit(0);
}
/**
* Emit a warning to stderr without blocking. Exit 0.
*/
export function warn(message) {
process.stderr.write(message + '\n');
process.exit(0);
}
/**
* Fail-open: exit 0 silently. Used when input can't be parsed.
*/
export function failOpen() {
process.exit(0);
}