はじめに
GitHub Copilot CLI、便利ですよね!毎日のようにリリースされていますし、密かに盛り上がりを感じています。
でも最初のうちは Copilot CLI などの CLI 型コーディングエージェントを動かすのが不安な人もいると思います。知らないうちにローカルのファイルが消されたらどうしよう...勝手にリポジトリにコミットされたり push されるの怖い...などなど
今日はそんな方に向けて Copilot CLI に実行してほしくない操作をブロックする方法をご紹介します。
私の環境
OS:Windows 11 + WSL2 + Ubuntu 24.04.1 LTS
GitHub Copilot CLI:1.0.18
Python:3.12
--deny-tool フラグ
Copilot CLI 起動時、--allow-all をフラグとして起動した場合も --deny-tool を指定すればそのセッションの間は特定の操作を禁止できます。
copilot --allow-all \
--deny-tool='shell(git push)' \
--deny-tool='shell(rm -rf:*)'
上記コマンドで Copilot を起動した後、git push を指示すると
意図通りブロックされることが確認できます。
しかしこのフラグは設定ファイルに書くことが出来ません。~/.copilot/config.json には denied_urls は存在するのですが denied_tools は現時点では存在しません。毎回起動のたびに --deny-tool を指定するのは少し面倒ですよね。
hooks
こんなときは hooks の出番です。hooks を使えば、ファイルベースで禁止ルールを定義することが出来ます。リポジトリにコミットしてチーム全員に適用することも出来ます。
hooks とは
特定のイベントが発生したタイミングで、自動的に呼び出される処理を設定できる機能です。hooks で設定できるイベントには preToolUse / postToolUse を始めとしていくつかの種類があり、それぞれのタイミングで任意のコマンドを呼び出すことができます。今回はツール実行前に呼び出される preToolUse フックを使います。
実装例
リポジトリのルート以下に .github/hooks/safety-guard.json を作成します。ためしに git への push を禁止するルールを定義してみましょう。
{
"version": 1,
"hooks": {
"preToolUse": [
{
"type": "command",
"bash": "python3 .github/hooks/safety_guard.py",
"timeoutSec": 5
}
]
}
}
ロジックを直接 json 内に記述しようとするとコマンドが長くなり見通しも悪くなってしまうので、実際のロジックは .github/hooks/safety_guard.py に切り出してそれを呼び出す形にしています。
import json
import re
import sys
d = json.load(sys.stdin)
cmd = d.get("toolArgs", {}).get("command", "")
# git --no-pager push のように間にオプションが入っても検出できる正規表現
if re.search(r"git\b.*\bpush\b", cmd, re.I):
print(
json.dumps(
{
"permissionDecision": "deny",
"permissionDecisionReason": "git push is not allowed.",
}
)
)
これら2つのファイルを .github/hooks/ に配置した状態で Copilot CLI を起動するだけで完了です。
今度は --deny-tool を指定せずに Copilot を起動し、git push を指示してみます。
git push is not allowed. という理由とともに deny されることが確認できました!
preToolUse フックの仕組み
少しだけ踏み込んで preToolUse フックの仕組みを見てみます。名前の通り preToolUse フックはツール実行前に呼び出されます。フックに指定したコマンドは stdin から JSON を受け取り、stdout に JSON を返します。
stdin(Copilot → コマンド)
{
"timestamp": 1775260800000,
"cwd": "/path/to/repo",
"toolName": "bash",
"toolArgs": "{\"command\": \"git push\"}"
}
stdout(コマンド → Copilot)
{ "permissionDecision": "deny", "permissionDecisionReason": "理由" }
stdout で permissionDecision: "deny" を返すと、そのツール呼び出しは実行されません。--allow-all ですべてのツールの使用を許可していても、フックで deny を返せばブロックできます。
permissionDecisionReason の文字列はそのまま LLM に渡るため、LLM に拒否理由を説明することも可能です。フックのコマンドは任意の言語・ロジックで書けるため、基本的にはどんな複雑なルールも実装できます。
ユーザーレベルでの hooks
プロジェクト横断で適用したいルールがある場合は、~/.copilot/config.json の hooks キーに直接定義できます。こちらに定義するとすべてのプロジェクトで有効になります。
こちらについては以前別の記事を書いているのでもしよかったらご覧ください。
deny ルールは allow-all より常に優先される
Copilot CLI のヘルプテキスト(copilot help permissions)には次のように書かれています。
Denial rules always take precedence over allow rules, even --allow-all-tools.
(日本語訳)
拒否ルールは許可ルールよりも常に優先されます。--allow-all-tools を指定していても同様です。
つまり、他のあらゆる手段で許可を与えたとしても、拒否ルールがあればそのアクションは実行されません。hooks で拒否するアクションを定義すれば、そのアクションは autopilot を Enable all permissions で起動したとしても実行されないということになります。安心ですね。
さいごに
Copilot CLI の hooks 機能を使えば、ユーザー個人やプロジェクトのニーズに合わせて柔軟にツールの使用を制御できます。特に preToolUse フックは Copilot に実行してほしくない操作をブロックする強力な手段となりますので、ぜひ皆さんも活用してみてください。

