以前の記事「Obsidian CLIでAIエージェントがObsidianをよりパワフルに操作できるようになった」では、Obsidian CLIの登場によりClaude Codeからデイリーノートの作成やTemplater展開ができるようになったことを紹介しました。
あれから2ヶ月ほど運用し、Obsidian CLIをClaude Codeのhooksと組み合わせることで、より実践的な使い方が見えてきました。この記事では、Obsidian Vault上でClaude Codeを安全かつ賢く動かすために設定している3つのPreToolUseフックを紹介します。
前提: Obsidian Vaultでのエージェント操作で起きる問題
Obsidianのノートはプレーンなマークダウンファイルなので、Claude Codeはファイルの読み書きだけで操作できます。しかし、Obsidianには「プレーンなマークダウン」では見えないルールがあります。
[[wikilink]]によるノート間のリンク構造がVaultの中核にある- ファイルの移動・リネームはObsidianアプリを経由しないとリンクが壊れる
- ノートを理解するにはバックリンク(どこからリンクされているか)の文脈が重要
そのため、Claude Codeが普段通りにReadツールやGrepツールで情報を探索するだけでは適切にコンテキストを収集できない場合も多く、また普段通りにEditツールを使ってしまうとリンクが崩れてしまいます。CLAUDE.mdでObsidianの使い方を指示するのと同時に、hooksで行動を矯正できるとよさそうです。その際にObsidian CLIは活躍してくれています。
1. ノート読み取り時にバックリンクを自動注入する
あるノートを編集するとき、そのノートの中身だけでなく「どのノートから参照されているか」を知っていると、より適切な判断ができます。Obsidianアプリでは被リンクがサイドパネルに表示されますが、Claude Codeがファイルを読んだだけではその情報は得られません。
そこで、Readツールの実行前にObsidian CLIのobsidian backlinksコマンドを呼び出し、結果をadditionalContextとして注入するフックを設定しています。
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "F=$(jq -r '.tool_input.file_path // empty') && [[ \"$F\" == \"$CLAUDE_PROJECT_DIR\"/*.md ]] && B=$(obsidian backlinks path=\"${F#$CLAUDE_PROJECT_DIR/}\" 2>/dev/null & P=$!; (sleep 1; kill $P 2>/dev/null) &; wait $P 2>/dev/null) && B=$(printf '%s' \"$B\" | grep -vE 'Loading|Error:') && [ -n \"$B\" ] && jq -n --arg b \"$B\" '{hookSpecificOutput:{hookEventName:\"PreToolUse\",additionalContext:(\"Backlinks: \" + $b)}}' || true",
"timeout": 1000
}
]
}処理を分解すると:
tool_input.file_pathから読み取り対象のパスを取得- プロジェクトディレクトリ内の
.mdファイルかチェック(別ディレクトリの場合や、設定ファイル等を除外) obsidian backlinks path=...でバックリンク一覧を取得(1秒でタイムアウト)- 結果を
additionalContextとしてClaudeに注入
ポイントはpermissionDecisionではなくadditionalContextで返している点です。Readツールの実行自体はブロックせず、Claudeの判断材料に情報を足しています。
これにより、Claude CodeがVaultを探索する際に重要な情報に気づきやすくなります。
2. obsidian createを禁止してWriteツールに統一する
Obsidian CLIにはノートを作成するobsidian createコマンドがありますが、Claude Codeからこのコマンドを使うと、ファイルの中身がBashコマンドの引数として渡されます。これだと改行文字などが展開されないので、作成されるコンテンツの事前レビューが見づらく困難です。
Claude Codeにobsidian CLIを使い方を教えると、積極的にCLIを使おうとします(これ自体は良い振る舞いです)。ただ、ノートの作成に関しては通常のWriteツールの方が使いやすいです。
Writeツールでファイルを作成すれば、書き込む内容がdiffとして表示され、ユーザーが承認前に確認できます。
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command // empty' | grep -qE '^\\s*obsidian\\s+create(\\s|$)' && echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"ノート作成はWriteツールを使用してください。obsidian createはコンテンツのレビューが困難です\"}}' || true"
}
]
}permissionDecisionReasonにブロック理由を記載しておくと、Claudeは「なぜブロックされたか」を理解して次の試行でWriteツールに切り替えます。単にブロックするだけでなく、代替手段をフック自身が教えるのが大事です。
ちなみにObsidian CLIの読み取り系コマンド(obsidian backlinks、obsidian search、obsidian dailyなど)はブロックしていません。CLI活用とレビュー担保のバランスを取っています。
3. mvコマンドを禁止してobsidian move/renameに統一する
これが最も重要なフックです。mvコマンドで.mdファイルを移動すると、ファイルシステム上のパスは変わりますが、そのファイルを参照している[[wikilink]]は古いパスのままになります。リンク切れの発生です。
Obsidian CLIのobsidian moveとobsidian renameはObsidian APIを経由するため、バックリンクを自動的に更新してくれます。
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command // empty' | grep -qE '\\bmv\\b.*\\.md\\b' && echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\"permissionDecisionReason\":\"mdファイルのリネーム・移動にはobsidian rename / obsidian moveを使用してください。バックリンクが自動更新されます\"}}' || true"
}
]
}CLAUDE.mdにも同様の指示を書いていますが、フックで技術的にブロックすることで確実性を担保しています。
# CLAUDE.md(抜粋)
## Obsidian CLI
ノートのリネーム・移動には `obsidian rename` / `obsidian move` を使う。
Obsidian APIを経由するため、バックリンクが自動更新される。CLAUDE.mdは「なぜそうすべきか」を伝え、フックは「実行レベルで強制する」。この二重防御の組み合わせでうまく機能しています。
まとめ
| フック | Obsidian CLIコマンド | 目的 |
|---|---|---|
| バックリンク注入 | obsidian backlinks | リンク構造の文脈をClaudeに渡す |
obsidian createブロック | — (Writeツールに誘導) | コンテンツレビューを確実にする |
mv *.mdブロック | obsidian move / obsidian renameに誘導 | バックリンクの破壊を防ぐ |
前回の記事では「Obsidian CLIの登場でエージェントにできることが広がった」と書きましたが、実際に運用してみると「できることが増えた分、やってはいけないことも明確にする必要がある」と感じました。Obsidian CLIのコマンドをhooksに組み込むことで、Claudeが自律的にVaultを操作しつつリンク構造を壊さない仕組みを作れています。