ModsCode日本語ブログ Claude から Mod のコード例を探す

ModsCode / 日本語ブログ / Claude Code Mods が動く場所:アプリ・端末・VS Code

Claude Code Mods が動く場所:アプリ・端末・VS Code

Claude Code の Mod が、ターミナル、デスクトップアプリ、VS Code、claude -p、Remote Control、クラウドで動くか、描画が出るかを一覧にし、どこでも動く Mod の書き方まで解説します。

Claude Code の Mod は、「フックが動く」ことと「描いたものが画面に出る」ことが別の話です。ターミナルでは両方動きますが、VS Code のチャットパネルでは、フックは動いても描いたものは出ません。この記事では、使う場所ごとに何が動くかを整理し、複数の場所で使われる Mod の書き方までまとめます。内容は Anthropic の公式ドキュメントと照合しています。

先に結論

  • ペインや帯のような画面の描画が出るのは、ターミナルとデスクトップアプリ(WSL を除く)だけです。
  • フックそのもの(ツール呼び出しの拒否やプロンプトの書き換え)は、VS Code、claude -p、Agent SDK でも動きます。
  • デスクトップアプリで WSL のセッションを使うと、プラグインが使えないため、Mod は動きません。
  • 描画する Mod は、今どのアプリかを調べて、描けない場所では文字の返事に切り替えられます。

場所ごとの一覧

公式ドキュメントの表を、日本語にしたものです。

使う場所フックは動く描いたものは出る
ターミナルの claude(エディタ内蔵のターミナル、JetBrains のプラグインを含む)動く出る
デスクトップアプリの Code タブ(WSL のセッションを除く)動く出る(ターミナル専用の要素を除く)
デスクトップアプリの WSL セッション動かない(プラグインが使えない)出ない
VS Code 拡張のチャットパネル動く出ない
claude -p と Agent SDK動く出ない
claude.ai やモバイルアプリからの Remote Control手元のマシンのセッションで動く手元のマシンのターミナルに出る
クラウドセッションプラグインがクラウドに引き継がれる場合は動く出ない

Windows でデスクトップアプリを使っていて Mod が動かないときは、まずそのセッションが WSL かどうかを確かめてください。WSL のセッションでは、プラグインそのものが使えません。

ターミナルとデスクトップアプリの違い

どちらでもペイン、プロンプトの上の帯、スピナー、トランスクリプトの各行は描けます。ただし、使える部品(要素)と描ける場所に差があります。

要素・場所ターミナルデスクトップアプリ
Box、Text、Button、Link、Code、Markdown、Input、Select、Client使える使える
Svg(SVG の図)使えない使える
Raster(色つきのマス目)、Image(画像)使える使えない
ToolProgress、TurnDuration、InfoNotice(状態の行)描ける描けない

デスクトップアプリで描けない要素を使うと、描画が出ません。「ターミナルでは動くのにデスクトップアプリでは出ない」ときは、まずこの表を見てください。公式のリファレンスにある レンダーサイトと要素の表 が正式な一覧です。

どこでも動くように書く

描画のフックは、e.surface(terminal か desktop)で今のアプリを知れます。そして $.ui.resolve(e) を使うと、今のアプリで使える要素だけが返ります。次の Mod は、プロンプトの上の帯に、今のアプリの名前を 1 行足します。コードは Claude Code 2.1.289 の claude plugin validate と claude plugin test で確認しました。

surface-demo/hooks/register.js
export function register(on) {
  // プロンプトの上の帯。ターミナルとデスクトップアプリの両方で描かれる
  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
    // 今のアプリで使える要素だけが入っている
    const { Box, Text } = $.ui.resolve(e)
    const where = e.surface === 'desktop' ? 'デスクトップアプリ' : 'ターミナル'
    // 他の Mod が帯に描いたものを残して、その下に 1 行足す
    const theirs = await next(e)
    return Box({
      flexDirection: 'column',
      children: [theirs, Text({ dimColor: true, children: ['ここは ' + where + ' です'] })],
    })
  })
}

ポイントは 3 つです。

  1. next(e) の結果を残す:帯はすべての Mod で共有しています。await next(e) の結果を自分の木の中に入れると、ほかの Mod の表示を消さずに済みます。入れずに木を返すと、後ろの Mod の描いたものを置き換えます。
  2. 要素は $.ui.resolve(e) から取る:直接決め打ちせず、今のアプリが描ける要素を受け取ります。
  3. 描けない場所の逃げ道を用意する:描画がないアプリ(VS Code、claude -p)でも情報を出したいときは、/コマンド の文字の返事({ text })や、$.ui.log でトランスクリプトに出す 1 行を併用します。

claude -p と CI での注意

claude -p(対話なしの実行)でもフックは動きます。ただし、次の違いがあります。

  • --plugin-dir を付けた実行では、Mod が読み込まれなかったときのメッセージは、トランスクリプトがないため標準エラー出力に出ます。ほかの Mod による拒否は、デバッグログにだけ残ります。
  • /plugin は動きません。インストール済みのプラグインは読み込まれるので、管理はシェルの claude plugin コマンドで行います。
  • $.ui.ask のように、ユーザーに聞く呼び出しは、答える人がいないため失敗します。try と catch で受けて、安全な側(拒否)に倒す書き方が公式の例にもあります。
  • Claude が書いた Mod の自動読み込み(ホットリロードの承認)は、承認する人がいないため、claude -p や dontAsk モードでは読み込まれません。

CI のように、フラグを渡せない場所では、環境変数 CLAUDE_CODE_PLUGIN_DIRS で、--plugin-dir と同じようにプラグインのフォルダを渡せます。Windows ではパスを ; で区切ります。

確認の仕方

  • 動いているか:セッションで /plugin を実行し、1 mod active · 名前 の行を見ます。VS Code や claude -p では見られないので、claude plugin list を使います。
  • ターミナルとデスクトップアプリの両方での描画:Mod のテストで、$.ui.mount の surface に terminal と desktop を順に渡すと、描画が各アプリで有効かを調べられます。ただし、実際にどう見えるかまでは確認できないので、新しい配置は実機でも見てください(Mod のテストの書き方)。

まとめ

Mod を配るなら、README に「どのアプリで動くか」を書くと親切です。描画がターミナル専用なのか、フックだけで足りるのかを、利用者は知りたがります。描画の作り方は 画面に状態を表示する Mod に、原因の切り分けは Mod が動かないときの確認手順 にあります。