はじめに

GitHub Copilot Chatには、Ask、Edit、Agent、Planという4つのモードがあります。その中でも「Editモード」は、複数ファイルに対する変更を細かく制御しながら行える点で、実務において非常に重要な役割を果たします。

本記事では、VSCode Copilot Editsモードに焦点を当て、以下の内容を解説します。

  • Editsモードの基本概念とWorking Setの活用方法
  • 複数ファイルにまたがるリファクタリングの実践手法
  • テストコードやドキュメントの一括生成
  • Agentモードとの使い分け判断基準
  • 効果的なプロンプト設計のテクニック

前提条件

VSCode Copilot Editsモードを使用するには、以下の環境が必要です。

項目 要件
GitHub Copilotプラン Free、Pro、Pro+、Business、Enterprise
VS Code バージョン1.95以降
GitHub Copilot拡張機能 最新版を推奨

Copilot Freeプランでは月間の利用制限がありますが、Editsモードの基本機能は使用可能です。

VSCode Copilot Editsモードとは

Editsモードは、指定したファイル群(Working Set)に対して、1回のやり取りごとに変更内容を確認・承認しながら編集を進めるモードです。Agentモードのように自律的にファイルを探索することはなく、編集対象を明示的に制御できる点が特徴です。

Editsモードの起動方法

VSCodeでEditsモードを起動するには、以下の手順を実行します。

  1. Ctrl + Alt + I(macOSではCmd + Alt + I)でCopilot Chatを開く
  2. チャット入力欄下部のエージェントピッカーから「Edit」を選択
  3. 編集対象のファイルをWorking Setに追加する

または、タイトルバーのCopilotアイコンからChatメニューを開き、「Edit」を選択することもできます。

Working Setの概念

Editsモードの核となる概念が「Working Set」(作業セット)です。Working Setとは、Copilotが編集対象として認識するファイルの集合のことです。

Working Setにファイルを追加する方法は複数あります。

  • ドラッグ&ドロップ: エクスプローラーからファイルをチャットビューにドロップ
  • #ファイル参照: #filename.tsのように入力して参照
  • Add Context: チャットビューの「Add Context」ボタンからファイルを選択
  • アクティブファイル: 現在開いているファイルは自動的にWorking Setの候補として表示される
1
2
3
4
// Working Setに追加されたファイルの例
// #src/services/userService.ts
// #src/models/user.ts
// #src/controllers/userController.ts

複数ファイルリファクタリングの実践

Editsモードが真価を発揮するのは、複数ファイルにまたがるリファクタリング作業です。ここでは実践的なユースケースを紹介します。

インターフェース変更の波及対応

APIレスポンスの型定義を変更した場合、関連するすべてのファイルに変更を波及させる必要があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 変更前: src/types/user.ts
interface User {
  id: number;
  name: string;
  email: string;
}

// 変更後: 新しいフィールドを追加
interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
  updatedAt: Date;
}

Editsモードで以下のようなプロンプトを実行します。

User型にcreatedAtとupdatedAtフィールドを追加しました。
#src/types/user.ts #src/services/userService.ts #src/controllers/userController.ts
の各ファイルで、この変更に対応するコードを更新してください。
新規ユーザー作成時はcreatedAtとupdatedAtに現在時刻を設定し、
更新時はupdatedAtのみを更新するようにしてください。

Copilotは指定されたWorking Set内のファイルのみを編集対象とし、各ファイルに必要な変更を提案します。

関数シグネチャの変更と呼び出し元の修正

関数の引数を変更する場合、すべての呼び出し元を漏れなく修正する必要があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 変更前
async function fetchUser(userId: string): Promise<User> {
  // 実装
}

// 変更後: オプションパラメータを追加
interface FetchUserOptions {
  includeProfile?: boolean;
  includeSettings?: boolean;
}

async function fetchUser(
  userId: string, 
  options?: FetchUserOptions
): Promise<User> {
  // 実装
}

Editsモードでのプロンプト例は以下のとおりです。

fetchUser関数のシグネチャを変更しました。
第2引数にFetchUserOptionsを追加しています。
#src/services/userService.ts #src/handlers/authHandler.ts #src/api/routes.ts
の呼び出し元をすべて確認し、必要に応じてoptionsパラメータを追加してください。
既存の呼び出しはデフォルト動作を維持するため、変更は最小限にしてください。

命名規則の統一

プロジェクト全体で命名規則を統一したい場合にもEditsモードが有効です。

以下のファイルで、変数名の命名規則をキャメルケースに統一してください。
スネークケース(user_id, created_at等)をキャメルケース(userId, createdAt等)に変換します。
#src/models/user.ts #src/models/order.ts #src/models/product.ts

テストコード生成の実践

Editsモードは、既存コードに対するテストコードの生成にも適しています。

単体テストの一括生成

複数のサービスクラスに対するテストを一度に生成できます。

以下のサービスファイルに対するJestの単体テストを生成してください。
各関数に対して、正常系と異常系のテストケースを含めてください。
モックはjest.mockを使用し、依存関係を適切にモック化してください。

#src/services/userService.ts
#src/services/orderService.ts

テストファイルは__tests__ディレクトリに配置し、
*.test.tsの命名規則に従ってください。

生成されるテストコードの例を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// __tests__/userService.test.ts
import { UserService } from '../src/services/userService';
import { prisma } from '../src/lib/prisma';

jest.mock('../src/lib/prisma');

describe('UserService', () => {
  const userService = new UserService();

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('findById', () => {
    it('存在するユーザーを正常に取得できること', async () => {
      const mockUser = { id: '1', name: 'Test User', email: 'test@example.com' };
      (prisma.user.findUnique as jest.Mock).mockResolvedValue(mockUser);

      const result = await userService.findById('1');

      expect(result).toEqual(mockUser);
      expect(prisma.user.findUnique).toHaveBeenCalledWith({
        where: { id: '1' }
      });
    });

    it('存在しないユーザーの場合nullを返すこと', async () => {
      (prisma.user.findUnique as jest.Mock).mockResolvedValue(null);

      const result = await userService.findById('999');

      expect(result).toBeNull();
    });
  });
});

統合テストの生成

APIエンドポイントの統合テストも同様に生成できます。

以下のAPIルートファイルに対するsupertest + Jestの統合テストを生成してください。
各エンドポイントに対して、HTTPステータスコードとレスポンスボディを検証するテストを含めてください。
認証が必要なエンドポイントは、事前にテストユーザーでログインした状態をシミュレートしてください。

#src/routes/userRoutes.ts
#src/routes/orderRoutes.ts

ドキュメント追加の自動化

Editsモードを使って、既存コードにJSDocやコメントを追加できます。

JSDocの一括追加

以下のファイル内のすべてのパブリック関数とクラスに対して、
JSDoc形式のドキュメントコメントを追加してください。
@param、@returns、@throws、@exampleを適切に含めてください。
日本語で記述してください。

#src/services/userService.ts
#src/services/orderService.ts
#src/utils/validation.ts

生成されるJSDocの例を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/**
 * ユーザーIDに基づいてユーザー情報を取得します。
 * 
 * @param userId - 取得対象のユーザーID
 * @returns ユーザー情報を含むPromise。ユーザーが存在しない場合はnull
 * @throws {DatabaseError} データベース接続エラーが発生した場合
 * 
 * @example
 * const user = await userService.findById('user-123');
 * if (user) {
 *   console.log(user.name);
 * }
 */
async findById(userId: string): Promise<User | null> {
  return this.prisma.user.findUnique({
    where: { id: userId }
  });
}

README.mdの更新

プロジェクト構成の変更に合わせてドキュメントを更新することもできます。

新しく追加したモジュールの説明をREADME.mdに追加してください。
以下のファイル構成を反映させ、各モジュールの役割を説明するセクションを追加してください。

#README.md
#src/services/paymentService.ts
#src/services/notificationService.ts

Agentモードとの使い分け

Copilot ChatにはEditsモード以外にAgentモードも存在します。それぞれの特性を理解し、適切に使い分けることが重要です。

Editsモードが適しているケース

Editsモードは以下のような状況で選択すべきです。

シナリオ 理由
編集対象が明確に特定できる Working Setで対象を限定できる
変更を細かくレビューしたい 1回のやり取りごとに承認できる
LLMリクエスト数を管理したい 自律的な追加リクエストが発生しない
機密性の高いファイルがある 意図しないファイルへのアクセスを防げる
既存コードの修正が中心 ファイル探索が不要

Agentモードが適しているケース

一方、Agentモードは以下の状況で効果を発揮します。

シナリオ 理由
編集対象が不明確 自律的にファイルを探索できる
新規機能の実装 必要なファイルを自動生成できる
ターミナルコマンドの実行が必要 npm install等を自動実行できる
MCPサーバーとの連携が必要 外部ツールを活用できる
大規模な変更 関連ファイルを網羅的に特定できる

使い分けのフローチャート

以下のMermaidダイアグラムで使い分けの判断フローを示します。

flowchart TD
    A[タスク開始] --> B{編集対象のファイルが<br>明確に特定できる?}
    B -->|Yes| C{変更内容を<br>細かくレビューしたい?}
    B -->|No| G[Agentモード]
    C -->|Yes| D[Editsモード]
    C -->|No| E{ターミナルコマンドの<br>実行が必要?}
    E -->|Yes| G
    E -->|No| F{新規ファイルの<br>自動生成が必要?}
    F -->|Yes| G
    F -->|No| D

ハイブリッドアプローチ

実務では、両モードを組み合わせて使用することも効果的です。

  1. Agentモードで初期実装: 新機能の骨格を自律的に生成
  2. Editsモードで微調整: 生成されたコードを細かく修正
  3. Agentモードでテスト生成: テストファイルを自動生成
  4. Editsモードでテスト修正: エッジケースや特殊なテストを追加

効果的なプロンプト設計

Editsモードで高品質な編集結果を得るためには、プロンプトの設計が重要です。

プロンプトの基本構造

効果的なプロンプトは以下の要素を含みます。

[1. 目的の明示]
○○を実現するために、以下の変更を行ってください。

[2. 対象ファイルの指定]
#file1.ts #file2.ts #file3.ts

[3. 具体的な変更内容]
- 変更点A: 詳細説明
- 変更点B: 詳細説明

[4. 制約条件]
ただし、以下の点に注意してください。
- 既存のAPIの互換性を維持する
- エラーハンドリングを追加する

[5. 期待する結果]
変更後、○○が正常に動作することを確認できるようにしてください。

良いプロンプトの例

リファクタリング時のプロンプト例を示します。

UserServiceクラスをリポジトリパターンに従ってリファクタリングしてください。

#src/services/userService.ts
#src/repositories/userRepository.ts(新規作成)
#src/interfaces/IUserRepository.ts(新規作成)

変更内容:
1. IUserRepositoryインターフェースを定義
2. UserRepositoryクラスを作成し、データアクセスロジックを移動
3. UserServiceはIUserRepositoryを依存性注入で受け取る
4. 既存のパブリックメソッドのシグネチャは変更しない

制約:
- Prismaへの直接依存はUserRepositoryのみに限定
- テストでモック化しやすい構造にする

避けるべきプロンプト

以下のようなプロンプトは期待した結果を得にくいです。

// 悪い例1: 曖昧すぎる
コードをきれいにしてください。

// 悪い例2: 対象が不明確
バグを修正してください。

// 悪い例3: 制約がない
パフォーマンスを改善してください。

コンテキストの追加

#メンションを活用して、追加のコンテキストを提供できます。

以下の型定義に合わせて、サービスの実装を更新してください。

#src/types/api.ts(型定義の参照)
#src/services/orderService.ts(編集対象)

APIResponseType<T>の形式でレスポンスを返すように変更し、
エラー時はApiErrorを含むレスポンスを返してください。

変更のレビューと承認

Editsモードで生成された変更は、すべてレビューしてから承認する必要があります。

変更の確認方法

Copilotが変更を提案すると、以下の形式で表示されます。

  1. チャットビューの変更ファイル一覧: 編集されたファイルがリスト表示される
  2. エディタのインラインdiff: ファイルを開くと変更箇所がハイライト表示される
  3. エクスプローラーのインジケータ: 保留中の編集があるファイルにアイコンが表示される

変更の承認・却下

エディタのオーバーレイコントロールを使用して、変更を操作できます。

操作 説明
Keep 現在のファイルの変更を承認
Undo 現在のファイルの変更を却下
Up/Down 変更箇所間を移動

個別の変更箇所にホバーすると、その変更のみを承認・却下することも可能です。

自動承認の設定

信頼できる変更については、自動承認を設定することもできます。

1
2
3
4
{
  "chat.editing.autoAccept": "afterDelay",
  "chat.editing.autoAcceptDelay": 5000
}

ただし、自動承認を使用する場合でも、コミット前には必ず変更内容をレビューすることを推奨します。

機密ファイルの保護

設定ファイルや環境変数ファイルなど、機密性の高いファイルへの編集は事前承認が必要です。

1
2
3
4
5
6
7
8
{
  "chat.tools.edits.autoApprove": {
    "**/*": true,
    "**/.vscode/*.json": false,
    "**/.env*": false,
    "**/secrets/**": false
  }
}

実践的なワークフロー例

実際の開発シナリオでEditsモードを活用するワークフローを紹介します。

シナリオ: 認証機能の追加

既存のREST APIに認証機能を追加するケースを考えます。

Step 1: 認証ミドルウェアの作成

JWT認証を行うExpressミドルウェアを作成してください。

#src/middlewares/authMiddleware.ts(新規作成)

要件:
- Authorizationヘッダーからトークンを抽出
- jsonwebtokenライブラリで検証
- デコードしたユーザー情報をreq.userに格納
- 検証失敗時は401エラーを返す

Step 2: 既存ルートへの適用

認証ミドルウェアを以下のルートに適用してください。

#src/routes/userRoutes.ts
#src/routes/orderRoutes.ts
#src/middlewares/authMiddleware.ts(参照用)

/usersと/ordersの全エンドポイントに認証を必須とし、
GET /users/publicのみ認証不要としてください。

Step 3: テストの更新

認証ミドルウェアの追加に伴い、テストを更新してください。

#__tests__/routes/userRoutes.test.ts
#__tests__/routes/orderRoutes.test.ts

変更内容:
- 各テストケースで認証ヘッダーを設定
- 認証なしでのアクセス拒否テストを追加
- テスト用のJWTトークン生成ヘルパーを作成

トラブルシューティング

Editsモード使用時によくある問題と対処法を紹介します。

変更が意図した範囲を超える

問題: Copilotが指定していないファイルや箇所を変更しようとする

対処法: Working Setを明示的に制限し、プロンプトで「指定したファイル以外は変更しないでください」と明記します。

変更が保守的すぎる

問題: 必要な変更が行われず、最小限の修正しか提案されない

対処法: 具体的な変更内容をリストで列挙し、期待する結果を明確に記述します。

コンテキスト不足によるエラー

問題: 依存するファイルを参照できず、不正なコードが生成される

対処法: 関連する型定義ファイルやインターフェースファイルをWorking Setに追加します。

まとめ

VSCode Copilot Editsモードは、複数ファイルにまたがる変更を制御しながら行うための強力なツールです。

本記事のポイントをまとめます。

  • Working Set: 編集対象のファイルを明示的に指定し、変更範囲を制御できる
  • リファクタリング: インターフェース変更、関数シグネチャ変更、命名規則統一など、波及的な変更に最適
  • テスト生成: 単体テスト、統合テストを一括で生成できる
  • ドキュメント追加: JSDocやREADMEの自動生成が可能
  • Agentモードとの使い分け: 対象が明確ならEdits、探索が必要ならAgent
  • プロンプト設計: 目的、対象、変更内容、制約、期待結果を明示する

Editsモードを活用することで、AIの支援を受けながらも変更内容を細かくレビューし、品質を担保した開発が実現できます。まずは小規模なリファクタリングから試し、徐々に活用範囲を広げていくことをおすすめします。

参考リンク