Commit 4f1a2e87190b
Changed files (1)
dots
pi
agent
extensions
dots/pi/agent/extensions/claude-hooks.ts
@@ -1,141 +0,0 @@
-/**
- * Pi Extension: Claude Code Hooks Wrapper
- *
- * Wraps the Go-based Claude Code hook binaries (claude-hooks-*) to provide
- * consistent hook behavior across AI coding agents.
- *
- * Events mapped:
- * - session_start -> claude-hooks-initialize-session
- * - session_shutdown -> claude-hooks-save-session
- * - tool_result -> claude-hooks-capture-tool-output
- *
- * Migrated to native TypeScript extensions:
- * - validate-git-push.ts (was: claude-hooks-validate-git-push)
- * - terminal-status.ts (was: claude-hooks-update-terminal-title)
- *
- * Requirements:
- * - claude-hooks-* binaries must be in PATH (installed via home-manager)
- */
-
-import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
-import { execSync, spawn } from "node:child_process";
-
-// Track if session has been initialized
-let sessionInitialized = false;
-
-// Cache binary existence checks
-const binaryCache = new Map<string, boolean>();
-
-// Convert Pi event format to Claude Code JSON format
-function toClaudePreToolUse(event: { toolName: string; input: any }): string {
- return JSON.stringify({
- tool_name: capitalize(event.toolName),
- tool_input: event.input || {},
- conversation_id: "pi-session",
- });
-}
-
-function toClaudePostToolUse(event: {
- toolName: string;
- input: any;
- content?: any[];
- isError?: boolean;
-}): string {
- return JSON.stringify({
- tool_name: capitalize(event.toolName),
- tool_input: event.input || {},
- tool_response: {
- content: event.content || [],
- is_error: event.isError || false,
- },
- conversation_id: "pi-session",
- });
-}
-
-function capitalize(s: string): string {
- return s.charAt(0).toUpperCase() + s.slice(1);
-}
-
-// Run a Claude hook binary with JSON input
-async function runHook(
- binary: string,
- jsonInput?: string
-): Promise<{ exitCode: number; stdout: string; stderr: string }> {
- return new Promise((resolve) => {
- const proc = spawn(binary, [], {
- shell: true,
- stdio: ["pipe", "pipe", "pipe"],
- });
-
- let stdout = "";
- let stderr = "";
-
- proc.stdout.on("data", (data) => (stdout += data.toString()));
- proc.stderr.on("data", (data) => (stderr += data.toString()));
-
- if (jsonInput) {
- proc.stdin.write(jsonInput);
- proc.stdin.end();
- } else {
- proc.stdin.end();
- }
-
- proc.on("close", (code) => {
- resolve({ exitCode: code ?? 0, stdout, stderr });
- });
-
- proc.on("error", () => {
- resolve({ exitCode: 1, stdout: "", stderr: `Failed to spawn ${binary}` });
- });
- });
-}
-
-// Check if a binary exists in PATH (cached)
-function binaryExists(name: string): boolean {
- if (binaryCache.has(name)) {
- return binaryCache.get(name)!;
- }
- try {
- execSync(`which ${name}`, { stdio: "ignore" });
- binaryCache.set(name, true);
- return true;
- } catch {
- binaryCache.set(name, false);
- return false;
- }
-}
-
-export default function (pi: ExtensionAPI) {
- // Session initialization
- pi.on("session_start", async (_event, ctx) => {
- if (sessionInitialized) return;
- sessionInitialized = true;
-
- if (binaryExists("claude-hooks-initialize-session")) {
- const result = await runHook("claude-hooks-initialize-session");
- if (result.stderr && result.exitCode === 0) {
- ctx.ui.notify(result.stderr.trim(), "info");
- }
- }
- });
-
- // Session shutdown
- pi.on("session_shutdown", async (_event, _ctx) => {
- if (binaryExists("claude-hooks-save-session")) {
- await runHook("claude-hooks-save-session");
- }
- sessionInitialized = false;
- });
-
- // Pre-tool-use validation is now handled by validate-git-push.ts extension
- // No need to call Go binary here anymore
-
- // Post-tool-use capture
- // NOTE: terminal title is now handled by terminal-status.ts extension
- pi.on("tool_result", async (event, _ctx) => {
- if (binaryExists("claude-hooks-capture-tool-output")) {
- const jsonInput = toClaudePostToolUse(event);
- await runHook("claude-hooks-capture-tool-output", jsonInput);
- }
- });
-}