X Tutup

ぽくつなです

Claude Code で SKILL.md に展開されるセッション ID を利用するプラグインを作ろう

時限式の許可を与える allow-until / その場でセッションを分岐する side-fork について

いまいち知られていない挙動として、Claude Code の SKILL.md には実行時に値が展開されるプレースホルダが用意されている。

code.claude.com

$ARGUMENTS は slash command 時代からおなじみだけど、配布したスキルのスクリプトの参照に便利な ${CLAUDE_SKILL_DIR} などが定義されている。${CLAUDE_SESSION_ID} もあってセッションごとに一意な ID が入る。これを利用すると楽しいプラグインを作れるので紹介するぞ。

/allow-until: 一定時間だけセッション単位で全てのコマンド実行を許可するプラグイン

allow-until

/allow-until と打つと、一定時間だけ Bash コマンドの実行が自動で許可されるようになる。

/allow-until            # 10 分間有効(デフォルト)
/allow-until 30         # AI がよしなに解釈して 30 分間有効
/allow-until enable 30  # 30 分間有効
/allow-until disable    # 無効化
/allow-until status     # 状態確認

permission を整備しておくと普段のタスクはほとんど許可なしでスイスイ進む。しかし Claude がパイプを駆使する状況や、普段と違うコマンドを使うタスクでは許可を求められる場面が出てくる。複数セッションを並行して走らせていると、あちこちから細かく呼ばれてなかなか煩わしい。かといって全許可にはしたくない、というのがモチベーション。「風呂やトイレに行く程度の時間」「このタスクは任せていいだろう」という粒度でセッション単位の許可を出したい。

rm -rfgit push --force のような危険なコマンドは、プラグイン側の設定に応じて引き続きブロックできる。時間が経てば勝手に切れるし、セッション単位で状態を持つので別のセッションには影響しない。

機能自体は単純だけど仕組みと合わせて紹介する。

実行の仕組み

SKILL.md は短く、以下を実行しろと書いてある程度
CLAUDE_SESSION_ID=${CLAUDE_SESSION_ID} ${CLAUDE_PLUGIN_ROOT}/bin/allow-until.sh <subcommand>

流れ:

  1. ユーザーが /allow-until 10 を実行
  2. Skill を読んだ Claude が CLAUDE_SESSION_ID=${CLAUDE_SESSION_ID} ${CLAUDE_PLUGIN_ROOT}/bin/allow-until.sh enable 10 を呼び出す
  3. セッション ID に対し有効期限をファイルに書いておく
  4. Hook で Bash コマンド実行のたびに allow-until.sh check を呼び出す
  5. スクリプトが状態ファイルを読み、期限内かつ安全なコマンドなら自動許可を返す

allow-until の動作フロー

プラグインに Hook も含めて配布できる

プラグインに hooks/hooks.json を含めると Hook も配布でき、設定ファイルに手で書く必要がない。Hook でも ${CLAUDE_PLUGIN_ROOT} が展開されるので、ちゃんとプラグイン側のスクリプトのパスを得られる。

code.claude.com

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/bin/allow-until.sh check"
          }
        ]
      }
    ]
  }
}

PreToolUse フックは Bash Tool 実行前に呼ばれ、標準入力で session_id や実行コマンドを含む JSON が渡ってくる。Skill 経由でも Hook 経由でも実行元のセッション ID を得られるので、連携した状態管理ができる。

セッション単位の状態管理に git config を使う

Skill と Hook の間で共有する状態をどこかに書く必要があるが、ここではあえて git config -f を使っている。既存の git 設定ではなく、XDG_STATE_HOME にファイルを置いて git config フォーマットで読み書きする。中身はこういう感じ:

[session "abc123"]    # ← セッション ID
    until = 1739567890  # ← 有効期限の epoch
[session "def456"]
    until = 1739568000

状態をファイルに書いて管理できれば何でもよいのだが、少し考えてみたところ git config が実に用途に合うと思ったので採用した。

  • git は Claude Code を使う環境でパスに入っていると仮定してよいだろう
  • CLI で特定の値やフィールドを読み書きできる
  • モデルが git CLI について充分知識がある

json や yaml なんかに書いてもよいのだが、部分編集やエラー処理にひと手間あったり、jq はまだしも yq があると仮定するのはやや厳しい。ここでは git config を CLI で使える超軽量 DB として活用しているのである。ガッハッハ。

拒否パターンの設定

自動許可が有効でもなんでもかんでも許可したくはない、rm -rfgit push --force のようなコマンドは常にブロックする。パターンは環境変数 SKILLS_ALLOW_UNTIL_FORBIDDEN_PATTERNS でセミコロン区切りで上書きできる。/allow-until test-pattern "rm -rf /" でブロックされるか確認、/allow-until status で有効なパターン一覧を表示する。

というような仕組み。 セッション ID が分かると状態を持ったプラグインを作れて便利。

ここで終わっても良かったのだけど、もう 1 つセッション ID を参照して便利に使っているプラグインがあるので紹介。

/side-fork: セッションを分岐させた tmux ウィンドウを開く

side-fork

Claude Code には /fork コマンドがあって、ここでちょっと分岐して会話したい、小さいタスクを片付けたり確認してから続きをやりたいがセッションを汚したくない、という用途に便利...

いや便利だけどさあ、違えんだよな。リリースノートで見た時は最高!! と思ったものの、セッション ID コピペしたくないだろ。だいたい並列で作業してるから --continue がどれ指すのかも曖昧だし /resume で選ぶのもめんどくさいじゃん。これじゃねえと思ったので Skill で実装したもの。

これも Skill.md に展開されるセッション ID を利用して、tmux or screen で新しいウィンドウを開いて claude --resume=$SESSION_ID --fork-session するシェルスクリプトを実行するだけ。/fork のように今のセッションをフォーク先に置き換えるのではなく、新しいウィンドウ側でフォークする。

これは状態とかなくて単純な作りだけど、便利に使っています。

実際のところ SKILL.md で AI を経由する必要もなくて、UserPromptSubmit で入力をフックして独自のコマンドとして定義してスクリプトを呼ぶ、みたいなんでもいいのだが、さすがに落ち着きがないので見送っている。

それぞれのインストール

GitHub リポジトリ: pokutuna/claude-plugins

# /allow-until
claude plugin marketplace add pokutuna/claude-plugins
claude plugin install allow-until@pokutuna-plugins --scope user

# /side-fork
claude plugin marketplace add pokutuna/claude-plugins
claude plugin install side-fork@pokutuna-plugins --scope user

現状、プラグインのスコープの挙動は怪しいので、自分は全て user スコープでインストールしています。 プロジェクト単位で有効にしたいものは、~/.claude/settings.json で全体で無効にして <project>/.claude/settings.json で有効にする方法を採っています。(anthropics/claude-code#14202, anthropics/claude-code#20593)

余談: Agent Skills 標準

Specification - Agent Skills

というものがある。

Claude の各種機能や今回の Skill は、仕様にない挙動を利用していて当然他のコーディングエージェントでは動作しないんだけど、Claude Code が仕様を牽引している現状では、この仕様に則っているかはかなりどうでもいい状態だと思う。

構造を決めていても front matter 部分集合の意味しかなく、期待する挙動は定義されてないし、満たしていても相互運用性がロクにない。合議で仕様を決めていくような段階になく、今はアツい新機能を投入する戦乱の時代、守らせる影響力もない、空虚なものだと思う。

むしろ欲しいのは CLAUDE_SKILL_DIR みたいなパス解決だったり、要求する CLI コマンドとか事前条件の表明とか依存の定義とかそういうやつなんだけど、現状を追認する以上のことができる段階にはないし難しいよね。

だいたい、追加のパラメータを禁止しているわけでもなく仕様違反ではないものに対し VS Code 側がエラーをじゃんじゃか吐くようになって、鬱陶しく思っています。てめーが仕様を読めや。 github.com

以上楽しい記事でした。

X Tutup