🪝

Claude Code Hooksで行末空白と改行を自動処理するシンプルな方法

に公開1

課題

Claude Codeによって生成されたコードを自分で編集していると、空行やファイル末尾に関するdiffがでてしまうことが多くて気になっていました。

  • 行末に不要な空白が残っていて、それがエディタの設定で消される
  • ファイル末尾に改行がなくて、それがエディタの設定で追加される
  • インデントされている部分の空行に空白が残っていて、それがエディタの設定で消される

特に、Lintやフォーマッタが整備されていないプロジェクトやフォルダで作業するときに面倒です。

解決方法

Claude CodeのHooksとLinuxの標準のコマンドだけで自動修正するようにしました。以下のHooksを登録すればOKです。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs sed -i -e '$a\\'"
          },
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs sed -i 's/[[:space:]]*$//'"
          }
        ]
      }
    ]
  }
}

何をしているか

1つ目のコマンド:ファイル末尾に改行を追加。

jq -r '.tool_input.file_path' | xargs sed -i -e '$a\\'

2つ目のコマンド:各行の行末空白を削除。

jq -r '.tool_input.file_path' | xargs sed -i 's/[[:space:]]*$//'

動作例

編集前:

def hello():    ← 行末に空白
    print("Hello!")  ← 行末に空白(ファイル末尾に改行なし)

編集後:

def hello():
    print("Hello!")
GitHubで編集を提案
GMOペパボ株式会社

Discussion

nishinanishina

自分もファイル末尾改行や空白問題に直面していたので助かりました!ありがとうございます!

自分のmacOSの環境ですと記載していただいた設定だとうまく動作しなかったため以下のように修正しました。

不要な空白の削除

"hooks": [
  {
    "type": "command",
-    "command": "jq -r '.tool_input.file_path' | xargs sed -i 's/[[:space:]]*$//'"
+    "command": "jq -r '.tool_input.file_path' | xargs -I {} sed -i '' 's/[[:space:]]*$//' '{}'"
  }
]
}

補足: 主な違い(Claude Codeによる回答)

  1. -i '' - macOSのsedでは、-iオプション使用時に空の拡張子''が必要
  2. -I {} ... '{}' - xargs-Iオプションを使用し、プレースホルダー{}でファイルパスを明示的に指定

変更前(Linux用):
xargs sed -i 's/[[:space:]]*$//'

変更後(macOS用):
xargs -I {} sed -i '' 's/[[:space:]]*$//' '{}'

macOSのBSD版sedとLinuxのGNU版sedの構文の違いによる修正です。

ファイル末尾に改行を追加

  • こちらは以下の記事で紹介されている設定を利用しました。

https://zenn.dev/notahotel/articles/570a61c13cdfa4

最終的な設定

.claude/setting.json
"hooks": {
  "PostToolUse": [
    {
      "matcher": "Write",
      "hooks": [
        {
          "type": "command",
          "command": "FILE_PATH=$(jq -r '.tool_input.file_path') && [ -n \"$(tail -c1 \"$FILE_PATH\")\" ] && echo >> \"$FILE_PATH\""
        }
      ]
    },
    {
      "matcher": "Write|Edit|MultiEdit",
      "hooks": [
        {
          "type": "command",
          "command": "jq -r '.tool_input.file_path' | xargs -I {} sed -i '' 's/[[:space:]]*$//' '{}'"
        }
      ]
    }
  ]
}