Droidフックは、Droidのライフサイクルの様々な段階で実行されるユーザー定義のシェルコマンドです。フックは、Droidの動作を決定論的に制御し、Droidが選択的に実行するのではなく、特定のアクションが常に実行されることを保証します。
フックの使用例には以下があります:
- 通知: Droidがユーザーの入力や実行許可を待機している際の通知方法をカスタマイズします。
- 自動フォーマット: ファイル編集後に.tsファイルに対して
prettierを、.goファイルに対してgofmtを実行します。
- ログ記録: コンプライアンスやデバッグのために、実行されたすべてのコマンドを追跡・カウントします。
- フィードバック: Droidがコードベースの規約に従わないコードを生成した際に、自動フィードバックを提供します。
- カスタム権限: 本番ファイルや機密ディレクトリへの変更をブロックします。
これらのルールをプロンプト指示ではなくフックとしてエンコードすることで、提案を実行される度に毎回動作するアプリレベルのコードに変換できます。
You must consider the security implication of hooks as you add them, because hooks run automatically during Droid’s execution with your current environment’s credentials.
For example, malicious hooks code can exfiltrate your data. Always review your hooks implementation before registering them.For full security best practices, see Security Considerations in the hooks reference documentation.
Important: Always use absolute paths when referencing scripts in your hook commands, not relative paths.
Hooks execute from Droid’s current working directory, which may not be your project root.
Use $FACTORY_PROJECT_DIR for project-relative scripts (e.g., "$FACTORY_PROJECT_DIR"/.factory/hooks/script.sh)
or full paths for global scripts (e.g., /usr/local/bin/my-hook.sh or ~/.factory/hooks/script.sh).
フックイベント概要
Droidは、ワークフローの異なる段階で実行される複数のフックイベントを提供します:
- PreToolUse: ツール呼び出し前に実行(ブロック可能)
- PostToolUse: ツール呼び出し完了後に実行
- UserPromptSubmit: ユーザーがプロンプトを送信し、Droidが処理する前に実行
- Notification: Droid が通知を送信する際に実行
- Stop: Droidが応答を終了する際に実行
- SubagentStop: サブDroidタスクが完了する際に実行
- PreCompact: Droidがコンパクト操作を実行しようとする前に実行
- SessionStart: Droidが新しいセッションを開始するか、既存のセッションを再開する際に実行
- SessionEnd: Droidセッションが終了する際に実行
各イベントは異なるデータを受信し、Droidの動作を異なる方法で制御できます。
クイックスタート
このクイックスタートでは、Droidが実行するシェルコマンドをログ記録するフックを追加します。
前提条件
コマンドラインでのJSON処理のためにjqをインストールしてください。
ステップ1: フック設定を開く
/hooks slash commandを実行し、PreToolUseフックイベントを選択します。
PreToolUseフックはツール呼び出し前に実行され、異なる対応についてDroidにフィードバックを提供しながらツール呼び出しをブロックできます。
ステップ2: マッチャーを追加
+ Add new matcher…を選択して、Executeツール呼び出しのみでフックを実行します。
マッチャーにExecuteと入力します。
You can use * to match all tools.
ステップ3: フックを追加
+ Add new hook…を選択し、以下のコマンドを入力します:
jq -r '.tool_input.command' >> ~/.factory/bash-command-log.txt
ステップ4: 設定を保存
ストレージの場所については、ホームディレクトリにログ記録しているためUser settingsを選択します。これにより、フックは現在のプロジェクトだけでなく、すべてのプロジェクトに適用されます。
その後、REPLに戻るまでEscを押します。フックが登録されました!
ステップ5: フックを確認
再度/hooksを実行するか、~/.factory/settings.jsonをチェックして設定を確認します:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Execute",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.factory/bash-command-log.txt"
}
]
}
]
}
}
ステップ6: フックをテスト
Droidにlsのような簡単なコマンドを実行するよう依頼し、ログファイルを確認します:
cat ~/.factory/bash-command-log.txt
以下のようなエントリが表示されるはずです:
その他の例
コードフォーマットフック
編集後にTypeScriptファイルを自動フォーマット:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Create",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.ts'; then npx prettier --write \"$file_path\"; fi; }"
}
]
}
]
}
}
Markdownフォーマットフック
markdownファイルの言語タグの欠落やフォーマットの問題を自動修正:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Create",
"hooks": [
{
"type": "command",
"command": "\"$FACTORY_PROJECT_DIR\"/.factory/hooks/markdown_formatter.py"
}
]
}
]
}
}
以下の内容で.factory/hooks/markdown_formatter.pyを作成:
#!/usr/bin/env python3
"""
Markdown formatter for Droid output.
Fixes missing language tags and spacing issues while preserving code content.
"""
import json
import sys
import re
import os
def detect_language(code):
"""Best-effort language detection from code content."""
s = code.strip()
# JSON detection
if re.search(r'^\s*[{\[]', s):
try:
json.loads(s)
return 'json'
except:
pass
# Python detection
if re.search(r'^\s*def\s+\w+\s*\(', s, re.M) or \
re.search(r'^\s*(import|from)\s+\w+', s, re.M):
return 'python'
# JavaScript detection
if re.search(r'\b(function\s+\w+\s*\(|const\s+\w+\s*=)', s) or \
re.search(r'=>|console\.(log|error)', s):
return 'javascript'
# Bash detection
if re.search(r'^#!.*\b(bash|sh)\b', s, re.M) or \
re.search(r'\b(if|then|fi|for|in|do|done)\b', s):
return 'bash'
# SQL detection
if re.search(r'\b(SELECT|INSERT|UPDATE|DELETE|CREATE)\s+', s, re.I):
return 'sql'
return 'text'
def format_markdown(content):
"""Format markdown content with language detection."""
# Fix unlabeled code fences
def add_lang_to_fence(match):
indent, info, body, closing = match.groups()
if not info.strip():
lang = detect_language(body)
return f"{indent}```{lang}\n{body}{closing}\n"
return match.group(0)
fence_pattern = r'(?ms)^([ \t]{0,3})```([^\n]*)\n(.*?)(\n\1```)\s*$'
content = re.sub(fence_pattern, add_lang_to_fence, content)
# Fix excessive blank lines (only outside code fences)
content = re.sub(r'\n{3,}', '\n\n', content)
return content.rstrip() + '\n'
# Main execution
try:
input_data = json.load(sys.stdin)
file_path = input_data.get('tool_input', {}).get('file_path', '')
if not file_path.endswith(('.md', '.mdx')):
sys.exit(0) # Not a markdown file
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
formatted = format_markdown(content)
if formatted != content:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(formatted)
print(f"✓ Fixed markdown formatting in {file_path}")
except Exception as e:
print(f"Error formatting markdown: {e}", file=sys.stderr)
sys.exit(1)
Make the script executable:
chmod +x .factory/hooks/markdown_formatter.py
This hook automatically:
- Detects programming languages in unlabeled code blocks
- Adds appropriate language tags for syntax highlighting
- Fixes excessive blank lines while preserving code content
- Only processes markdown files (
.md, .mdx)
Custom Notification Hook
Get desktop notifications when Droid needs input:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Droid' 'Awaiting your input'"
}
]
}
]
}
}
File Protection Hook
Block edits to sensitive files:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Create",
"hooks": [
{
"type": "command",
"command": "python3 -c \"import json, sys; data=json.load(sys.stdin); path=data.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(p in path for p in ['.env', 'package-lock.json', '.git/']) else 0)\""
}
]
}
]
}
}
詳細情報