Claude-skill-registry cc-hooks-ts
TypeScriptで型安全なClaude Code Hooksを作成するためのガイド。cc-hooks-tsライブラリを使用してフックを実装する際に使用する。(1) Claude Code用の新しいhookを作成する時、(2) 既存のhookをTypeScriptで書き直す時、(3) PreToolUse/PostToolUse/SessionStart等のイベントフックを実装する時、(4) plugin.jsonにhooksを設定する時
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/cc-hooks-ts" ~/.claude/skills/majiayu000-claude-skill-registry-cc-hooks-ts && rm -rf "$T"
manifest:
skills/data/cc-hooks-ts/SKILL.mdsource content
cc-hooks-ts を使用した Claude Code Hooks 作成ガイド
インストール
bun add cc-hooks-ts
基本的なフック構造
#!/usr/bin/env bun import { defineHook, runHook } from "cc-hooks-ts"; const hook = defineHook({ trigger: { // トリガーするイベントを指定 PostToolUse: { Write: true, Edit: true, }, }, // オプション: 条件付き実行 shouldRun: () => { return process.platform === "darwin"; // macOSのみ }, run: (context) => { // フックのロジック // 成功レスポンス(何もしない) return context.success({}); // または、追加コンテキストを返す return context.json({ event: "PostToolUse", output: { hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "Claude へのメッセージ", }, suppressOutput: true, }, }); // または、エラーでブロック return context.blockingError("操作をブロックしました"); }, }); if (import.meta.main) { await runHook(hook); }
サポートされているイベント
| イベント | 説明 | ユースケース |
|---|---|---|
| セッション開始時 | 環境チェック、初期設定 |
| ツール使用前 | 操作のブロック、入力検証 |
| ツール使用後 | ログ記録、追加処理 |
| ツール失敗時 | エラーハンドリング |
| 通知イベント | アラート処理 |
ツール固有のフック
特定のツールに対してのみフックをトリガーする場合:
const hook = defineHook({ trigger: { PreToolUse: { Read: true, // Read ツールのみ }, }, run: (context) => { const filePath = context.input.tool_input.file_path; // .env ファイルへのアクセスをブロック if (filePath.endsWith(".env")) { return context.blockingError("環境ファイルへのアクセスは許可されていません"); } return context.success({}); }, });
主要 API
context オブジェクト
| メソッド | 説明 |
|---|---|
| 成功レスポンス(処理を続行) |
| エラーでブロック |
| 構造化レスポンス(追加コンテキスト付き) |
context.input の構造
interface HookInput { event: string; // イベント名 tool_name?: string; // ツール名(ToolUse系イベント) tool_input?: Record<string, any>; // ツールの入力パラメータ tool_output?: string; // ツールの出力(PostToolUse) }
plugin.json での設定
{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "bun run -i --silent ${CLAUDE_PLUGIN_ROOT}/path/to/hook.ts" } ] } ] } }
重要:
を使用することbun run -i --silent
でプラグインルートを参照${CLAUDE_PLUGIN_ROOT}
実装例
例1: ファイル書き込み時に知見ファイルを提案
#!/usr/bin/env bun import { existsSync } from "node:fs"; import { basename } from "node:path"; import { defineHook, runHook } from "cc-hooks-ts"; const hook = defineHook({ trigger: { PostToolUse: { Write: true, Edit: true, }, }, run: (context) => { const filePath = context.input.tool_input.file_path; const knowledgePath = `${filePath}.knowledge.md`; if (existsSync(knowledgePath)) { return context.json({ event: "PostToolUse", output: { hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: `${knowledgePath} に知見を追記してください`, }, suppressOutput: true, }, }); } return context.success({}); }, }); if (import.meta.main) { await runHook(hook); }
例2: 特定ディレクトリへの書き込みをブロック
#!/usr/bin/env bun import { defineHook, runHook } from "cc-hooks-ts"; const PROTECTED_DIRS = ["/etc", "/usr", "/System"]; const hook = defineHook({ trigger: { PreToolUse: { Write: true, }, }, run: (context) => { const filePath = context.input.tool_input.file_path; for (const dir of PROTECTED_DIRS) { if (filePath.startsWith(dir)) { return context.blockingError( `${dir} への書き込みは許可されていません` ); } } return context.success({}); }, }); if (import.meta.main) { await runHook(hook); }
例3: セッション開始時の環境チェック
#!/usr/bin/env bun import { existsSync } from "node:fs"; import { defineHook, runHook } from "cc-hooks-ts"; const hook = defineHook({ trigger: { SessionStart: true, }, run: (context) => { const requiredFiles = [".env", "package.json"]; const missing = requiredFiles.filter(f => !existsSync(f)); if (missing.length > 0) { console.error(`警告: 以下のファイルが見つかりません: ${missing.join(", ")}`); } return context.success({}); }, }); if (import.meta.main) { await runHook(hook); }
カスタムツール型の拡張
MCP で定義されたカスタムツールに型を追加する場合:
import { defineHook } from "cc-hooks-ts"; declare module "cc-hooks-ts" { interface ToolSchema { my_custom_tool: { input: { query: string }; output: { result: string }; }; } } const hook = defineHook({ trigger: { PostToolUse: { my_custom_tool: true, }, }, run: (context) => { // context.input.tool_input は { query: string } として型付けされる return context.success({}); }, });
ベストプラクティス
- エラーハンドリング: try-catch で予期しないエラーをキャッチ
- クールダウン: 頻繁なトリガーを避けるためクールダウン機構を実装
- ログ出力:
でユーザーに情報を表示(console.error()
は避ける)console.log - 冪等性: 同じ入力に対して同じ結果を返すように設計
- 軽量化: フック処理は高速に完了するよう設計