polyhook

agent
Guvenlik Denetimi
Uyari
Health Uyari
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Gecti
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

Write AI coding agent hooks once. Run them everywhere. Multi-language SDK (TS/Rust/Go/Python/.NET) over a shared WASM core — supports Claude Code, Cursor, Windsurf, Cline, and Amp.

README.md

polyhook

polyhook

Write AI coding agent hooks once. Run them everywhere.

polyhook is a multi-language SDK for AI coding agent hooks. Instead of parsing each tool's bespoke stdin/stdout format, you call polyhook — it detects which tool invoked your binary, deserializes the event into a normalized struct, and serializes your response back in the format that tool expects.

Your hook binary runs unchanged whether Claude Code, Cursor, Windsurf, Cline, Amp, or Pi invoked it.


Architecture

Rust core compiled to WASM. Every language SDK is a thin shim over the same binary — no logic re-implemented per language. See ARCHITECTURE.md.


The Problem

Every AI coding tool triggers hooks differently. The same logical event — "bash command about to run" — arrives in a different shape and expects a different response format:

Claude Code  →  stdin: { "tool_name": "Bash", "tool_input": { "command": "..." } }
                stdout: { "decision": "block", "reason": "..." }

Cursor       →  stdin: { "type": "BeforeToolCall", "toolCall": { "name": "run_terminal_cmd", "args": {...} } }
                stdout: { "action": "deny", "message": "..." }

Windsurf     →  stdin: { "event": "pre_tool", "tool": "run_command", "parameters": {...} }
                stdout: { "allow": false, "reason": "..." }

Without polyhook you write a parser and serializer for each tool. With polyhook you call read() and respond().


How It Works

AI tool  ──stdin──▶  your binary
                         │
                    polyhook.read()        ← WASM: detects tool, parses format
                         │
                    your hook logic        ← tool-agnostic
                         │
                    polyhook.respond()     ← WASM: serializes to tool's format
                         │
your binary  ──stdout──▶  AI tool

Normalized Types

Types are auto-generated in every SDK from core/schema.json — not hand-written. See ARCHITECTURE.md for the full generation pipeline.

HookEvent

interface HookEvent {
  event:     "tool:before" | "tool:after" | "session:start" | "session:stop" | "turn:stop" | "agent:stop" | "prompt:submit" | "notification";
  tool?:     string;                        // normalized tool name, e.g. "bash", "write_file"
  input?:    Record<string, unknown>;       // tool input arguments
  prompt?:   string;                        // user prompt (prompt:submit only)
  reply?:    string;                        // agent's final reply text (turn:stop only)
  transcriptPath?: string;                  // session transcript file, when the caller sends one
  output?:   Record<string, unknown>;       // tool output (tool:after only)
  sessionId: string;
  agentId?:  string;
  caller:    "claude-code" | "cursor" | "windsurf" | "cline" | "amp" | "gemini-cli" | "hermes" | "pi" | "codex" | "unknown";
}

Normalized tool names: docs/tool-names.md

HookResponse

type HookResponse =
  | { action: "approve" }
  | { action: "block";  message: string }
  | { action: "modify"; input: Record<string, unknown> }
  | { action: "context"; context: string }   // prompt:submit: add text to the model's context

Language SDKs

All SDKs expose the same two functions. The WASM module does all the work.

TypeScript / JavaScript

npm install @polyhook/sdk

Examples: packages/sdk-ts/examples/

Rust

The only SDK that links core natively — no WASM overhead.

cargo add polyhook

Examples: packages/sdk-rust/examples/

Go

go get github.com/tupe12334/polyhook/packages/sdk-go

Examples: packages/sdk-go/examples/

C# / .NET

dotnet add package Polyhook.Sdk

Examples: packages/sdk-dotnet/examples/

Python

pip install polyhook

Examples: packages/sdk-python/examples/

Any language with a WASM runtime can bind polyhook. See BINDINGS.md for the raw WASM API.


Supported Tools

Tool Status
Claude Code ✅ Supported
Cursor ✅ Supported
Windsurf ✅ Supported
Cline ✅ Supported
Amp ✅ Supported
Gemini CLI ✅ Supported
Hermes Agent ✅ Supported
Pi ✅ Supported
Codex ✅ Supported
Continue 🚧 In progress
Aider 🚧 In progress
Copilot 📋 Planned

prompt:submit context injection ({ action: "context" }) works on Claude Code, Codex, Gemini CLI (BeforeAgent), Hermes (pre_llm_call) and Cline (UserPromptSubmit). Cursor, Windsurf and Amp have no prompt hook that can add context, so there a context response is sent as approve.

Pi uses the Claude Code-compatible hook payload format; set POLYHOOK_CALLER=pi if you want it labeled explicitly.

Source of truth: tools.toml — hook documentation links: docs/tool-names.md


Design Goals

  • One implementation. All detection and serialization logic lives in core. Language SDKs cannot diverge.
  • Runtime only. No code generation. polyhook runs inside your binary at hook invocation time.
  • Transparent fallback. Unknown caller → caller: "unknown", best-effort parse. Your logic keeps running.
  • Bring your own runtime. Each SDK ships polyhook.wasm and a thin host binding. Swap the WASM runtime if your platform requires it.

Contributing

See CONTRIBUTING.md. To add tool support, add detection heuristics and mappings to core — all language SDKs pick it up automatically on the next WASM build.


License

MIT

Yorumlar (0)

Sonuc bulunamadi