メインコンテンツへスキップ
このクックブックは、Droidがファイルを編集した後に自動的にコードをフォーマットし、手動での介入なしにプロジェクト全体で一貫したコードスタイルを確保する方法を示します。

動作原理

フック:
  1. ファイル編集時にトリガー: WriteまたはEditツールコール後に実行
  2. ファイル種別を検出: ファイル拡張子をチェックしてフォーマッターを決定
  3. 適切なフォーマッターを実行: prettier、black、gofmt、rustfmtなどを実行
  4. フィードバックを提供: フォーマット結果をユーザーに報告
  5. エラーを適切に処理: フォーマットが失敗しても継続

前提条件

あなたの言語スタックに対応するフォーマッターをインストールしてください:
npm install -D prettier

基本セットアップ

単一言語プロジェクト

JavaScript/TypeScriptプロジェクトの場合、.factory/settings.jsonに以下を追加してください:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -qE '\\.(ts|tsx|js|jsx)$'; then npx prettier --write \"$file_path\" 2>&1 && echo \"✓ Formatted $file_path\"; fi; }",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

多言語プロジェクト

複数の言語を使用するプロジェクトでは、異なるファイル種別を処理するスクリプトを使用します。 .factory/hooks/format.shを作成してください:
#!/bin/bash
set -e

# Read the hook input
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')

# Skip if file doesn't exist
if [ ! -f "$file_path" ]; then
  exit 0
fi

# Determine formatter based on file extension
case "$file_path" in
  *.ts|*.tsx|*.js|*.jsx|*.json|*.css|*.scss|*.md|*.mdx)
    if command -v prettier &> /dev/null; then
      prettier --write "$file_path" 2>&1
      echo "✓ Formatted with Prettier: $file_path"
    fi
    ;;
  *.py)
    if command -v black &> /dev/null; then
      black "$file_path" 2>&1
      echo "✓ Formatted with Black: $file_path"
    fi
    if command -v isort &> /dev/null; then
      isort "$file_path" 2>&1
      echo "✓ Sorted imports with isort: $file_path"
    fi
    ;;
  *.go)
    if command -v gofmt &> /dev/null; then
      gofmt -w "$file_path" 2>&1
      echo "✓ Formatted with gofmt: $file_path"
    fi
    ;;
  *.rs)
    if command -v rustfmt &> /dev/null; then
      rustfmt "$file_path" 2>&1
      echo "✓ Formatted with rustfmt: $file_path"
    fi
    ;;
  *.java)
    if command -v google-java-format &> /dev/null; then
      google-java-format -i "$file_path" 2>&1
      echo "✓ Formatted with google-java-format: $file_path"
    fi
    ;;
  *)
    # No formatter for this file type
    exit 0
    ;;
esac

exit 0
スクリプトを実行可能にします:
chmod +x .factory/hooks/format.sh
.factory/settings.jsonに追加してください:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$DROID_PROJECT_DIR\"/.factory/hooks/format.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

高度な設定

カスタム設定でフォーマット

プロジェクト固有のprettier設定を使用:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -qE '\\.(ts|tsx|js|jsx)$'; then npx prettier --config \"$DROID_PROJECT_DIR\"/.prettierrc --write \"$file_path\" 2>&1; fi; }",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

リントとフォーマット

フォーマットとリント修正を組み合わせます。 .factory/hooks/format-and-lint.shを作成してください:
#!/bin/bash
set -e

input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')

if [ ! -f "$file_path" ]; then
  exit 0
fi

case "$file_path" in
  *.ts|*.tsx|*.js|*.jsx)
    # Format with prettier
    if command -v prettier &> /dev/null; then
      prettier --write "$file_path" 2>&1
      echo "✓ Formatted: $file_path"
    fi
    
    # Fix lint issues
    if command -v eslint &> /dev/null; then
      eslint --fix "$file_path" 2>&1 || true
      echo "✓ Linted: $file_path"
    fi
    ;;
  *.py)
    # Format with black
    if command -v black &> /dev/null; then
      black "$file_path" 2>&1
      echo "✓ Formatted with Black: $file_path"
    fi
    
    # Sort imports
    if command -v isort &> /dev/null; then
      isort "$file_path" 2>&1
      echo "✓ Sorted imports: $file_path"
    fi
    
    # Run flake8 for style issues
    if command -v flake8 &> /dev/null; then
      flake8 "$file_path" 2>&1 || true
      echo "✓ Checked with flake8: $file_path"
    fi
    ;;
esac

exit 0
スクリプトを実行可能にします:
chmod +x .factory/hooks/format-and-lint.sh
.factory/settings.jsonに追加してください:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$DROID_PROJECT_DIR\"/.factory/hooks/format-and-lint.sh",
            "timeout": 45
          }
        ]
      }
    ]
  }
}

条件付きフォーマット

特定のディレクトリ内のファイルのみをフォーマットします。 .factory/hooks/format-src-only.shを作成してください:
#!/bin/bash
set -e

input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')

# Only format files in src/ or packages/ directories
if ! echo "$file_path" | grep -qE '^(src/|packages/)'; then
  exit 0
fi

case "$file_path" in
  *.ts|*.tsx|*.js|*.jsx)
    if command -v prettier &> /dev/null; then
      prettier --write "$file_path" 2>&1
      echo "✓ Formatted: $file_path"
    fi
    ;;
esac

exit 0
スクリプトを実行可能にします:
chmod +x .factory/hooks/format-src-only.sh
.factory/settings.jsonに追加してください:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$DROID_PROJECT_DIR\"/.factory/hooks/format-src-only.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

実例

例1: Reactコンポーネントのフォーマット

// Droid creates this file
import React from 'react';
import {useState} from 'react';

export const Button = ({onClick,label}:{onClick:()=>void;label:string})=>{
const [loading,setLoading]=useState(false);
return <button onClick={onClick} disabled={loading}>{label}</button>
}

例2: Pythonインポートソート

# Droid creates this file
from typing import Optional
import sys
from django.db import models
import os
from myapp.utils import helper

def process_data(data: Optional[str]) -> None:
    if data:
        helper(data)

ベストプラクティス

Formatters can sometimes introduce subtle bugs (e.g., changing string formats, line continuations). Always review changes before committing.
1

Start with read-only mode

Test your formatter configuration manually first:
# Dry run to see what would change
prettier --check src/
black --check src/
2

Use consistent config files

Ensure formatter configs are committed:
# Add to version control
git add .prettierrc .prettierignore
git add pyproject.toml  # for black config
3

Set appropriate timeouts

Large files may need more time:
{
  "timeout": 60  // Increase for large files
}
4

Handle formatter errors gracefully

Don’t block Droid if formatting fails:
# Use || true to continue on errors
prettier --write "$file_path" 2>&1 || true
5

Consider Git hooks integration

Combine with pre-commit hooks for consistency:
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v3.0.0
    hooks:
      - id: prettier

トラブルシューティング

フォーマッターが見つからない

問題: command not foundエラー 解決策: フォーマッターをグローバルにインストールするか、npx/プロジェクトバイナリを使用してください:
# Install globally
npm install -g prettier

# Or use project version
npx prettier --write "$file_path"

フォーマットによりコードが破損

問題: フォーマッターが構文エラーを引き起こす 解決策: フォーマット後にファイル検証を追加してください:
# For TypeScript
prettier --write "$file_path"
tsc --noEmit "$file_path" || echo "⚠️ Type errors after formatting"

フックの実行が遅い

問題: フォーマットに時間がかかりすぎる 解決策: 変更されたファイルのみをフォーマットし、より高速なフォーマッターを使用してください:
# Skip files that haven't changed
if git diff --quiet "$file_path"; then
  exit 0
fi

エディターのフォーマットと競合

問題: エディターとフックが異なるフォーマットを行う 解決策: 両方で同じ設定を使用してください:
// .vscode/settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "prettier.configPath": ".prettierrc"
}

関連情報