Claude CodeでVS Code拡張を自作する — 雛形からvsce公開まで
yo codeの雛形、package.jsonのactivationEventsとcontributes、テスト、vsceでのパッケージングと公開までを、Claude Codeに任せる範囲と手元で確認する範囲に分けて扱います。
VS Code拡張機能は、雛形生成、package.jsonの宣言、TypeScriptの実装、vsceでのパッケージングという定型の流れで作れます。Claude Codeはこの流れのうち、宣言と実装の往復とテストの実行を得意とします。拡張機能を実際のウィンドウで動かす確認だけは手元で行います。
ここではHello Worldの雛形から公開までを、Claude Codeに渡す規約とあわせて順に見ます。Claude Code公式拡張の使い方はClaude Code VS Code拡張機能の使い方にまとまっています。こちらは拡張機能を自分で作る側の話です。
前提
Node.jsとGitがインストール済みであることが前提です。動作確認のF5実行には、VS Code本体も使います。
雛形はyo codeで作る
VS Codeのチュートリアルは、Yeomanとgenerator-codeで雛形を作る手順です。グローバルインストールが不要なら次の1行で足ります。
npx --package yo --package generator-code -- yo code対話では、種類にNew Extension (TypeScript)、名前にHelloWorld、識別子にhelloworldを選び、残りは既定のままにします。バンドラーはunbundled、パッケージマネージャーはnpmがチュートリアルの選択です。
雛形ができたらsrc/extension.tsを開き、F5キーか、コマンドパレットのDebug: Start Debuggingを実行します。新しく開くExtension Development HostのウィンドウでHello Worldコマンドを実行すると、通知が出て成功です。コマンドが見えないときは、package.jsonのengines.vscodeが手元のVS Codeと合っているかを確認します。
この対話ウィザードは人間が答える前提の作りです。Claude Codeに任せるなら、yo codeだけは自分で実行して雛形を作り、そのディレクトリでClaude Codeを起動する分担が無理がありません。
拡張機能を構成する3つの概念
Hello Worldの拡張機能は3つのことをしています。この3つが、以降のすべての変更の置き場所を決めます。
| 概念 | 置き場所 | 役割 |
|---|---|---|
| Activation Events | 置き場所package.jsonのactivationEvents | 役割拡張機能が有効になる契機 |
| Contribution Points | 置き場所package.jsonのcontributes | 役割コマンド・メニュー・キーバインドなどの静的な宣言 |
| VS Code API | 置き場所src/extension.ts | 役割実行時に呼ぶJavaScript API |
package.jsonは拡張機能のマニフェストです。nameとpublisherから<publisher>.<name>という一意のIDが決まります。mainがエントリポイント、engines.vscodeが必要とするVS Code APIの最低バージョンです。
サンプルの中核は次の形です。
{
"name": "helloworld-sample",
"publisher": "vscode-samples",
"engines": { "vscode": "^1.51.0" },
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"commands": [
{ "command": "helloworld.helloWorld", "title": "Hello World" }
]
}
}エントリファイルはactivateとdeactivateの2つを公開します。activateは登録した契機が起きたときに実行され、deactivateはアンインストールや無効化の前に後始末をする場所です。
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const disposable = vscode.commands.registerCommand('helloworld.helloWorld', () => {
vscode.window.showInformationMessage('Hello World!');
});
context.subscriptions.push(disposable);
}
export function deactivate() {}package.jsonのcommandとregisterCommandの第1引数は一致させます。この対応が崩れると、コマンドは見えても実行時に失敗します。Claudeに新しいコマンドを足させるときは、両方を同時に触らせる指示にすると崩れにくくなります。
activationEventsは何を書くか
VS Code 1.74.0からは、contributesで宣言したコマンド・言語・ビュー・カスタムエディター・認証プロバイダーが呼ばれたとき、対応するonCommandなどをactivationEventsに書かなくても拡張機能が有効になります。雛形のactivationEventsが空なのはこのためです。1.74より前のバージョンを対象にするなら、onCommand:helloworld.helloWorldのように明示が必要です。
明示的に書く場面は次のとおりです。
| 契機 | 書き方の例 | 向く場面 |
|---|---|---|
| ファイルの言語 | 書き方の例onLanguage:markdown | 向く場面特定言語のファイルを開いたときに動かす |
| 起動完了後 | 書き方の例onStartupFinished | 向く場面起動直後の常駐処理 |
| 起動時 | 書き方の例* | 向く場面ほかの契機で成立しないときだけ |
*はVS Codeの起動のたびに有効化されます。ほかの契機の組み合わせで成り立たない場合にだけ使うのが前提です。onStartupFinishedは*に近い効果を持ちながら、起動を遅くしません。
*は安易に入れやすい設定です。次の節の規約に、*を使わないという一行を入れておくと、レビューで拾う手間が減ります。
CLAUDE.mdに拡張機能開発の規約を置く
CLAUDE.mdはセッションの開始時に毎回読み込まれます。目安は200行未満で、指示は検証できる具体さで書きます。「テストを実行する」ではなく「コミット前にnpm testを実行する」という書き方です。
VS Code拡張向けに書くなら、次のような形になります。以下はClaude Codeのドキュメントの書き方の原則に沿った例で、プロジェクトごとに調整する前提のひな形です。
# VS Code拡張機能
## 構成
- エントリポイントは src/extension.ts(main は ./out/extension.js)
- コマンドは package.json の contributes.commands に宣言し、
activate 内の registerCommand で同じ ID を実装する
## ルール
- activationEvents に "*" を書かない。必要なら onStartupFinished を先に検討する
- engines.vscode と @types/vscode のバージョンを揃える
- registerCommand などの戻り値は context.subscriptions に push する
- コマンド ID は helloworld.xxx の形式で、package.json と実装を同時に変更する
## 確認
- 変更後に npm run compile を実行し、エラーが 0 件であることを確認する
- コミット前に npm test を実行する構成とルールは、Claudeが推測すると外しやすい部分です。@types/vscodeのバージョンは、engines.vscodeの値に合わせて決まります。2つがずれると、対象のVS Codeにはまだ無いAPIを型チェックが通してしまいます。
保存のたびにフォーマットを走らせる
書いた規約は、Claudeが守ってくれることを期待するだけの助言です。確実に動かしたい処理は、フックで決定的に実行します。Claude Code公式の例に、ファイルを編集するたびにPrettierを走らせるPostToolUseフックがあります。.claude/settings.jsonに次のように置きます。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}Edit|Writeのマッチャーにより、ファイル編集のあとだけ動きます。雛形にPrettierは入っていないので、npm i -D prettierで追加しておきます。そのうえで、拡張機能の開発でもこの形を使えます。テストやLintをフックで回す書き分けは、hooksでテストを自動実行する方法で扱っています。
Claude Codeで開発ループを回す
雛形ができて規約を置いたら、依頼は小さな単位で出します。たとえば「現在時刻を情報メッセージで出すコマンドを追加する」です。VS Codeのチュートリアルにある練習課題でもあります。
Claudeが触るのはpackage.jsonのcontributes.commandsとsrc/extension.tsの2か所です。指示のあとに、次のことを確認させます。
npm run compileでTypeScriptのエラーが出ないことpackage.jsonのコマンドIDとregisterCommandのIDが一致していることactivationEventsに不要な追記がないこと
そのあと、動作確認は手元で行います。F5でExtension Development Hostを起動し、コマンドパレットからコマンドを実行します。メッセージを書き換えたときは、新しいウィンドウでDeveloper: Reload Windowを実行すると反映されます。ビルドし直して再起動する必要はありません。
デバッグはVS Code内蔵の機能をそのまま使えます。行の左をクリックしてブレークポイントを置くと、Extension Development Host側で実行を止められます。
テストを自動化する
Extension Development Hostを人が触る確認は手元の作業です。Claudeが自分で回せる検証にするには、テストCLIを入れます。
npm install --save-dev @vscode/test-cli @vscode/test-electronpackage.jsonのscriptsに"test": "vscode-test"を足し、設定ファイル.vscode-test.jsを置きます。最小の設定は次の1行です。
// .vscode-test.js
const { defineConfig } = require('@vscode/test-cli');
module.exports = defineConfig({ files: 'out/test/**/*.test.js' });テストはExtension Development Hostの中で走り、VS Code APIを使えます。内部ではMochaが動きます。これでnpm testが、Claudeが自分で実行して出力を読める検証コマンドになります。「テストを書き、npm testが通るまで直す」という反復を、CLAUDE.mdの確認欄の一行が支えます。
versionで使うVS Codeのバージョンを指定できます。既定はstableです。
vsceでパッケージングして公開する
公開の道具はvsceです。パッケージングと公開、検索やメタデータの取得までを担うCLIです。
npm install -g @vscode/vsce
vsce package
vsce publishvsce packageは.vsixファイルを作ります。VSIXは、Marketplaceを介さずに他の人へ配る形式としても使えます。
公開の前に、パッケージの中身を整えます。
README.mdをルートに置く。Marketplaceのページに表示される内容になるLICENSEファイルを置く。package.jsonのlicenseはSEE LICENSE IN <filename>の形で参照できるiconは128x128ピクセル以上のパスを指定する.vscodeignoreに実行時に不要なファイルを列挙する。TypeScriptなら**/*.tsが例になるscriptsにvscode:prepublishを置くと、パッケージングのたびにコンパイルが走る
README内の画像はhttpsのURLで解決できる必要があります。SVGは、信頼できるバッジ提供元のものを除いて使えません。ユーザーが用意したSVG画像を含む拡張機能は、セキュリティ上の理由からvsceが公開を拒否します。
.vscodeignoreとvscode:prepublishは、Claudeに任せてよい作業です。ただし.vscodeignoreは、除外の範囲を広げすぎると実行に必要なコンパイル済みファイルまで外れかねません。パッケージング後に.vsixの中身を確認する一手を、CLAUDE.mdに入れておくと事故が減ります。
認証は移行期にある
公開には発行元(publisher)が必要です。発行元のIDは作成後に変えられません。以前の手順は、Azure DevOpsでPersonal Access Token(PAT)を作り、vsce login <publisher id>で検証する流れでした。
2026年12月1日に、Azure DevOpsのグローバルPATが廃止されます。今後の推奨は、Microsoft Entra IDによるワークロードIDフェデレーションとマネージドIDを使う自動公開です。長期のシークレットを持たずに済む方式です。その場合の公開コマンドはvsce publish --azure-credentialで、vsceのバージョンは2.26.1以上が必要です。CIからの公開を組むなら、最初からこちらで設計するほうが後の作り直しを避けられます。
先行版の出し方
先行版を配るには、vsce package --pre-releaseかvsce publish --pre-releaseを使います。バージョンはmajor.minor.patchだけで、semverのプレリリースタグは使えません。先行版と通常版でバージョンを揃えられない点にも注意します。VS Codeは利用可能な最高バージョンへ自動更新するため、先行版を選んだ利用者も、より高い通常版が出れば通常版へ移ります。
よくあるつまずき
- コマンドが出ない:
engines.vscodeが手元のVS Codeより新しい、またはcontributes.commandsのIDと実装がずれている - 変更が反映されない: Extension Development Host側で
Developer: Reload Windowを実行していない - 起動が重くなる:
activationEventsに*を入れている。onStartupFinishedか、個別の契機に置き換える - 1.74未満で有効にならない: 古い
engines.vscodeを指定すると、onCommandの明示が必要になる - 公開できない: READMEにSVG画像が入っている、または2026年12月1日以降はPATで公開できなくなる(認証方式をEntra IDへ移す)
まとめ
拡張機能の設計は、契機・宣言・APIの3つに分けて考えると迷いません。宣言はpackage.json、実装はextension.ts、契機は既定なら書かずに済みます。Claude Codeには、CLAUDE.mdに規約を、フックに決定的な処理を、npm testに検証を置いて渡します。手元で確かめるのはExtension Development Hostでの操作と、公開前の.vsixの中身です。
同じ考え方で、ブラウザ側の拡張はChrome拡張をManifest V3で開発する手順、ランチャー側の拡張はRaycast拡張の開発ガイドにまとめています。