Skip to content

Node

Terminal window
npm install @six5536/agent-harness-adapter-node

Prebuilt for Linux (glibc; x64, arm64), macOS (x64, arm64) and Windows (x64). API reference.

A manifest is a file’s path, an object, or null for the repository’s config.

install.mjs
import aha from "@six5536/agent-harness-adapter-node";
const mytool = {
version: 1,
name: "mytool",
instructions: "Run `mytool check` before you finish.\n",
skills: [{ name: "mytool", description: "Check the project with mytool.", body: "Run `mytool check`; fix what it reports.\n" }],
hooks: [{ event: "pre-tool", tools: "shell", command: "mytool hook {harness} {event}" }],
allow_commands: ["mytool"],
};
const result = aha.install(mytool, ["claude", "codex", "gemini"]);
for (const h of result.harnesses) {
console.log(h.harness, h.parts.map((p) => `${p.state} ${p.path}`));
}

The third argument takes options: scope ("project", "user" or "local"), root (the project directory), home, and for install force and without (InstallOptions). uninstall and status take the same arguments. Results are the JSON of agent-harness-adapter install --json.

With command = "mytool hook {harness} {event}", runHook reads the harness’s input as Claude Code’s and writes your answer in the harness’s form.

hook.mjs
// mytool hook <harness> <event>: Claude Code's input in, its answer out,
// translated for whichever agent ran the hook.
import aha from "@six5536/agent-harness-adapter-node";
const [harness, event] = process.argv.slice(2);
const code = await aha.runHook(harness, event, (hook) =>
String(hook.tool_input?.command ?? "").includes("rm -rf")
? { decision: "block", reason: "mytool: no rm -rf here" }
: null,
);
process.exit(code);

parseHook and answerHook do the two halves separately. Errors throw with the library’s message.