「欲しければ作れ」からテトリスまで、12日間
Claude Code Modsは、Claude Codeのエンジン内部で動き、そのイベントを関数としてフックするTypeScriptモジュールだ。Claude Codeを率いるBoris Chernyは、わずか5語でこれを発表した。「Claude modsが今、届く」。この投稿は2,400のいいねを集め、その時点ですでに誰かがターミナルの中にテトリスを作り上げていた。テトリス、Doom、Claudeがテストを走らせている間に育つペット——どれもプロンプトの上にトークン消費ゼロで描画される。
発表にはドキュメントページへのリンクはない。リンクされているのは、その12日前にAnthropicのエンジニアが立てたGitHub issueで、そこには一つの条件が書き込まれていた。Alice Poteatは、この機能が出荷されるかどうかはコミュニティの反応次第になるだろうと書いた。177件のコメントを経て、人々はフラグ付きのバイナリを叩き、タイムアウトを計測し、その上にゲームを作り上げていた。
| Measure | Value |
|---|---|
| Likes on the announcement | 2,400 |
| Comments on the GitHub issue | 177 |
| Typed surface in this week's build | 10,700 lines |
| Events on nouns (issue-thread build) | 84 on 19 |
| Mods on GitHub inside 48 hours | 31 |
この記事執筆時点で、ドキュメントページは404を返す。本稿では、Claude Codeを拡張する4つの方法、意図的に作って壊した一つの実用的なMod、そして出荷する前にまだ不安定な部分を扱う。
Modとは、途中に挟まる関数のこと
ソースツリーはそれを一文で定義している。Modとは、その振る舞いがhooksモジュールに宿るClaude Codeプラグインのことだ。一つのregisterエントリが、エンジンのイベントを関数としてフックする。ディスク上では、プラグインフォルダ、モジュールを一つだけ指定するhooksマニフェスト、そしてそのモジュール自体からなる。
すべてのhookは3つのものの関数だ——あらゆる副作用が通る扉である$、イベント、そしてその下に続くチェーンである継続。hookはミドルウェアのように入れ子になる。最初に登録されたものがそのイベントを所有し、それより下流のどれもそれを止められない。この順序は設定によるもので、インストールの順ではない。Alice Poteatの言葉を借りれば、順序はインストールした時点ではなく設定で決まる。
アンビエントなアクセス権は存在しない。Modが行うことはすべて$を通るので、管理者はどのイベントでも監査し、許可し、拒否し、記録できる。あるコメントはこう要約した——プラグインが何をしたかは、それが行った呼び出しそのものだ。*に対する一つのhookがすべてのイベントを見るので、監査ログは一つの関数で済む。Modは描画もできる。インターフェースがReactだからだ。BunのプロセスにおいてP99で50マイクロ秒で動く。一つの環境変数でオンになる。$に実際に何が乗るかは、いまなおアーリーアクセスのパートナーとともに設計中だ。
Claude Codeを拡張する4つの方法、それぞれの使いどころ
シェルhookは、エンジンが決まったタイミングで呼び出すスクリプトだ。標準入力でJSONを受け取り、終了コードで答える。答えには上限がある——コンテキストとして戻せるのは8,000文字、一部のhookでは2,000文字だ。Prathamの言葉では、Windowsではしばしば奇妙な形で壊れるという。
プラグインは箱だ。一つのマニフェストが、skill、agent、hook、MCPサーバーをまとめる。マーケットプレイスからインストールするか、ディスク上のフォルダから直接読み込む。何かが実行される前に、一つのコマンドがその箱を検証する。
skillは、モデルが必要なときに読むプローズだ。本文は、それに一致するプロンプトが来たときにだけロードされる。今なお、振る舞いを変える最も安上がりな方法であり続けている。
Modは同じ箱に、もう1ファイルが加わったものだ——モジュールを一つ指定するhooksマニフェスト、そしてそのモジュールはTypeScriptで、型付きで、インプロセスで、あらゆるエンジンイベント上で動く。違いはそれがすべてだ。
| Mechanism | What it is | Runs | Ceiling |
|---|---|---|---|
| Shell hook | Script called at a fixed moment | Subprocess, exit code | 8,000 chars back (2,000 for some hooks) |
| Plugin | Bundle of skills, agents, hooks, MCP servers | Installed or loaded from disk | Validated before it runs |
| Skill | Prose loaded on a matching prompt | In the model's context | Cheapest change |
| Mod | Plugin plus a TypeScript hooks module | In process, on every event | Typed, early access |
これらの型は一つのスラッシュコマンドから生まれ、$が提供するものの全一覧をあなたのプロジェクトにそのまま書き出す。古いシェルhookは廃止されたのではなく、ラップされているだけだ。あるアーリービルドでは、Spencer Morleyはそのラッパーが読み込みに失敗し、それでも宣言されたままになるのを目にした。経験則はこうだ——Claudeが知っていることを変えるならskillを書く、ある瞬間にスクリプトを走らせるならシェルhook、バンドルを出荷するならプラグイン、エンジンの内側に座るならMod。フラグがオフの環境では、シェルhookは今も変わらず動く。
Anthropic自身の3つのMod、ソースを読む
3つのModがバイナリの中に同梱されており、そのソースはGitHub上で読める——セキュリティデフォルト、diffペイン、そしてテレメトリだ。
セキュリティデフォルトは最も外側に座る。managed settingsが設定されたマシン上、あるいはTeamまたはEnterprise組織では、個人がインストールしたものは何もそれより上に来ない。12のイベントをフックし、どのhookも3つの動きのうちどれか一つを行う——user tierの先へ進める、user tierの呼び出し元を名指しで拒否する、あるいは通過させる。fail closedであり、doc commentにもそう二語で書かれている。それがスレッドが気にかけていた部分だ——管理者が$から機能を取り除けば、それより下に登録されたどれもそれを呼び出せなくなる。あるコメントの言葉では、それはプラグインに「やらないでくれ」と頼むのとはカテゴリーが違う。
Diffは、トランスクリプトの横に並ぶペインで、そのセッションの未コミットの変更をファイルごとに表示し、Claudeが編集するたびに更新される。session startで登録され、27のヘルパーファイルにまたがる、おもちゃではない代物だ。
Telemetryは、エンジン生成の内側から$に一つのnounを加える。下流にあるものをawaitし、それとそれ自身を返す。内部ビルドでのみ動く。
READMEは、ソースから一つを実行し、ソースから一つをテストするよう指示している。今日のビルドのヘルプにはvalidate、eval、detailsが並ぶ。testはそのリストにないが、それでもtestヘルプ自体は答える。組み込みのtierは、あなたのコピーも拒む——それらの名前のいずれかでプラグインを出荷しても、バイナリは自分自身のものをロードする。
42行——モデルからシークレットを隠すMod
型を書き出すあのスラッシュコマンドは、このビルドでは11,700行を出力する——23のnounにまたがる84のイベントだ。tool callのhookは、結果かdenyのどちらかで答える。テキストそのものでは決して答えない。その部分はcoreによって固定されている。
このModは3ファイルからなる——プラグインマニフェスト、1行だけのhooksマニフェスト、そしてモジュールだ。モジュールは42行。下流にあるものをawaitし、結果をスクラブし、それを返す。4つのパターンが、2種類のベンダーキー形式、GitHubトークン、そしてkey、secret、tokenという名前の変数に代入されたものすべてをカバーする。
Validateは、実行される前にソースを読む。そのモジュールがフックするイベントと、$に対して呼ぶただ一つのものを名指しする。唯一の警告はauthorの欠落だ。フラグをオンにしてディスクからロードすると、テストファイルには構造上偽の2つのキーが含まれ、モデルは「redacted」と読む。モデル自身の言葉では、値は「redacted」として返ってきたので、その中身を見ることはできない。
| Measure | Value |
|---|---|
| Module length | 42 lines |
| Hop latency, worker included | 28 ms |
| Tokens added to the session | 0 |
エンジンはこのhopを「hooksモジュールによって解決された」とログに記す。インベントリにはskillもagentも常時オンのものも何も表示されない。一つだけ限界が残る——モデルから隠すことは、画面から隠すことではない。トランスクリプトには、そのツールが実際に出力した内容がそのまま表示される。それは別のModの仕事だ、とShip NotesのMaxは一言でそう指摘した。
壊してみる——遅いガードは素通りされるガード
同じModに1行だけ加える——下流にある呼び出しの前に、15秒のsleepを挟む。10秒後、エンジンはそれを諦め、そのhookが予算を超えたためskippedになったと報告し、その下にあるものがその代わりに実行される。コマンドはそれでも実行され、「hi」と出力する。
sleepをthrowに置き換えると——574ミリ秒、判定は同じくskipped、そしてコマンドは実行される。
| Case | Time | Verdict |
|---|---|---|
| Healthy hook | 28 ms | Resolved |
| Throw | 574 ms | Skipped, command ran |
| Hang | over 10 s | Skipped, command ran |
どちらの失敗も同じ一語に行き着く——skipped。このスレッドは、その非対称性を1週間前にすでに計測していた。ロード時に欠けている機能はfail closedになる。予算を超えたhookはfail openになる——うるさく鳴るが、素通りされる、とSpencer Morleyは言う。理由のないblockは、モデルを別のツールへ向かわせるだけだ。Prathamは、モデルが別のツールを選び、それでもファイルを書き込むのを目にした。
テーブルの上にある答えはcatchだ。Alice Poteatは、時間を掛けすぎるか、throwした場合に走る、hookのreturnに対するcatchを提案する。書き直されたwriteには、それでもモデルへの一言のメモが要る——キャッシュの都合上、モデルは自分が書こうとした内容をそのまま見るので、コンテキスト行を一つ添える必要がある。予算は同時に隔離でもある。hooks workerは独立して動いており、クラッシュするとエンジンはそれを再起動し、そのセッションのhookをオフにする。修正すべきは、より長い予算ではなく、宣言されたcatchだ。
Modsの48時間、たった一文で何が作れるか
2日が経った時点で、プロンプトの上にテトリスと、他に7つのゲームが並び、Claudeが作業している間にトークン消費ゼロで遊べるようになっていた。Doom、1993年のオリジナル版はそれ自身のプロセスで動き、Modはローカルのhttp経由でそこに到達し、1秒あたり10回描画し直す。スピナーには呼吸するようなペーサーがあり、hooks workerの内部で動く26万パラメータのストーリーモデルが、APIトークン消費ゼロで走っている。
レジストリはvalidateで31のModsをスキャンした。14がホストプロセスを実行できる。13があらゆるtool callを見る。
コミュニティスレッドのDemo 8は、モデルがシークレットを読む前にそれを隠すプラグインを一文で書けると約束していた。私たちも一つ求めてみた。
| Measure | Value |
|---|---|
| Time | 4 min |
| Turns | 34 |
| Cost | $1.23 |
| Lines (plus a test file) | 190, against our 42 |
| Tests | 4 pass in a third of a second |
それは隠したものを種類ごとにラベル付けし、validateはクリーンに通り、APIキーもモデル呼び出しも要らない。ある返信はこう言い表した——製品はallowlistであり、テトリスはただのデモだ。
結論——今は自分用に作り、配布は後で
レジストリ自身の数字が、その警告そのものだ。31のModsのうち14がホストプロセスを実行でき、その検証手段は静的な足跡だけだ。evalのヘルプ自体がそう述べている——パスするテストスイートは、セキュリティの検証ではない。
| Signal | Value |
|---|---|
| Events on the 8th | 20 |
| Events on the 15th | 84 |
| CLI versions in a fortnight | 14 |
| Hacker News points | 2 |
| Replies to the compatibility question | 0 |
10秒の予算はruntimeの中に生きており、型の中にはない、とMaratは指摘する。changelogの一行もドキュメントページもローンチ投稿もまだない。あるライバルは、Anthropicがそれをコピーしたと言う——Ahmad AwaisはCommand Codeのmodsを指し示し、その上で彼らのexample modを自ら書いており、その点は割り引いて受け止めるべきだ。「early access, APIs may change」という投稿は357のいいねを集めた。
すでにhookを書いているなら、監査、redaction、あるいはペインが欲しいなら、今すぐ一つ作るといい。契約がまだ書かれていないうちにチームへ配布するのは待った方がいい。fail openになる予算、MCP呼び出しの型の不一致、ラッパーの失敗が数日のうちにユーザーによって見つかったのも、この同じ開放性のおかげだ。
AIDive