【完全解説】Clineソースコードから学ぶAIエージェント機能の設計

# 1. はじめに

**Cline** は、VS Code の拡張機能として動作する AI エージェントです。

ユーザーが自然言語（チャット）でコード修正を指示すると、Cline は内部で大規模言語モデル（LLM）を活用し、以下の流れを **自動的** に実行します。

1. VS Code 上で対象ファイルを確認  
2. 修正案の生成（LLM への問い合わせ）  
3. 提案内容に応じたファイル更新

このような「チャットベースのコード修正ワークフロー」を実現するために、Cline は以下の特徴を備えています。

- **多様な LLM（OpenAI / Anthropic / DeepSeek / ...）を統合的に利用**  
- **VS Code 上で完結するリッチな UI（React + TypeScript）**  
- **チャット履歴やソースコードをスライド式に管理し、文脈量を調整**  
- **重要操作はユーザーの承認を挟んで安全性を確保**

本記事では、まず「実際にコード修正を依頼するときの流れ」を示したうえで、Cline のアーキテクチャ（機能別構成）や安全設計、**Tree-sitterによる構文解析** や **MCPツール連携** といった高度な仕組みまで掘り下げて解説します。

https://github.com/cline/cline

Cline のアーキテクチャを学ぶことにより、これからくるであろう AIエージェント開発に役立てたいと考えています。
難しい言葉でも詳細な解説はしていませんので、LLMを使いながら読み進めてみてください。
必ずあなたの糧になると思います。

---

# 2. Cline の「チャットによる修正依頼」フロー

Cline をインストールすると、VS Code の拡張パネル上にチャット UI が表示されます。  

そこでユーザーが **「特定ファイルの特定関数を修正してほしい」** と自然言語で依頼すると、Cline は以下のような手順で自動的にファイルを編集します。

```mermaid
sequenceDiagram
    participant U as ユーザー
    participant C as Cline(拡張機能)
    participant VS as VS Code APIs
    participant LLM as 大規模言語モデル

    U->>C: 「`/src/utils/path.ts` を修正して」
    note right of C: メンションやファイル名を検知
    C->>C: ファイル読み取り権限チェック<br>（AutoApproval設定など）
    alt ユーザー許可が必要
        C->>U: 「ファイルを読み取っていいですか？」
        U->>C: 「OK」
    end
    C->>VS: 該当ファイルの内容を取得
    VS-->>C: ソースコードを返却
    C->>LLM: 「修正したい箇所」と「ファイル内容」をプロンプトとして送信
    LLM-->>C: 「修正後コード差分」や提案を応答
    note right of C: AIから返された提案を解析
    alt ユーザーの最終承認が必要
        C->>U: 「この変更を適用しますか？」
        U->>C: 「承認」
    end
    C->>VS: WorkspaceEdit でファイルを上書き
    VS-->>C: 更新完了
    C-->>U: 「修正が完了しました」
```

1. ユーザーがチャットで修正指示を行う  
2. Cline が修正箇所を特定 → AI に問い合わせて修正案を生成  
3. AI からの修正提案を VS Code 上に適用

このように、開発者は「チャットで依頼して、修正内容を確認・承認する」だけで、AI によるリファクタリングやコーディング補助を受けられます。

---

# 3. Cline の機能

ここからは、筆者が特に気になった以下のポイントを順に解説します。

- **3.1 修正対象ファイルの特定**
- **3.2 ユーザー許可や自動承認(Auto Approval)ロジック**
- **3.3 AIでの差分生成とエディタ適用**
- **3.4 途中でユーザー承認を挟む例**
- **3.5 エラーハンドリングや通信リトライ**
- **3.6 スライディングウィンドウ（コンテキスト管理）**  
- **3.7 メモリ使用量・キャッシュ戦略**  
- **3.8 AIが提案した差分マージの仕組み**  
- **3.9 Tree-sitter を活用した構文解析**  
- **3.10 MCP (Model Context Protocol) や他ツール連携の拡張**


## 3.1 修正対象ファイルの特定

### 3.1.1 メンション検知 (`context-mentions.ts`)

Cline では、チャットメッセージ中に書かれた「`@path/to/file`」や「`@problems`」などのキーワードを自動的に検知します。

内部では、次のような正規表現が利用されています。

```ts
// src/shared/context-mentions.ts

// mentionRegex は、ユーザーのチャットメッセージ内で、@から始まるファイルパスや
// 特定キーワード（problems, terminal, git-changesなど）を検知するための正規表現です。
export const mentionRegex = /@((?:\/|\w+:\/\/)[^\s]+?|problems\b|terminal\b|git-changes\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/
// mentionRegexGlobal は、グローバルサーチを行うためのパラメータを設定しています。
export const mentionRegexGlobal = new RegExp(mentionRegex.source, "g")
```

1. `@` の直後にスラッシュや URL、特定キーワード（problems, terminal, git-changesなど）を含むパターンをマッチ  
2. 後ろに続く句読点はメンションの一部としては扱わない

このようにチャット上でユーザーが指定したファイルや特殊コンテキスト（例: `@problems` で問題リストを表示）を検知し、Cline の内部処理につなげています。

### 3.1.2 無視するファイルを除外 (`ClineIgnoreController.ts`)

`.clineignore` という設定ファイルを読み込み、指定されたパターンに合致するファイルパスはアクセスを拒否する仕組みがあります。
以下は抜粋例です。

```ts
// src/core/ignore/ClineIgnoreController.ts (抜粋)

// "ignore" パッケージを利用して、.clineignore で指定されたパターンを管理・判断
import ignore from "ignore"

export class ClineIgnoreController {
  private ig: ReturnType<typeof ignore>
  
  constructor(rootPath: string) {
    // ClineIgnoreController の初期化時に .clineignore ファイルを読み込み、ignore パターンを設定
    this.ig = ignore()
    const fileContent = fs.readFileSync(path.join(rootPath, '.clineignore'), 'utf-8')
    this.ig.add(fileContent.split(/\r?\n/))
  }

  public isIgnored(filePath: string): boolean {
    // プロジェクトのルートディレクトリからの相対パスを計算し、ignore リストにマッチするか判定
    const relative = path.relative(/* ルートディレクトリ */, filePath)
    return this.ig.ignores(relative)
  }
}
```

これにより、社外秘のファイルなどを誤って AI に送信しないようにフィルタリングでき、安全性やプライバシーを考慮した運用が可能です。

---

## 3.2 ユーザー許可や自動承認(Auto Approval)ロジック

Cline は危険な操作（ファイルの書き換え、外部ツール実行など）をする際、事前にユーザーの許可を求める仕組みを持っています。
必要に応じて「自動承認 (Auto Approval)」の設定を使うことで、開発のスピードを上げつつも安全性を両立します。

```ts
// src/shared/AutoApprovalSettings.ts

// 自動承認（Auto Approval）の設定を表すインターフェース
// enabled: 自動承認を有効にするか
// actions: 各アクション（ファイル読み取り、編集、コマンド実行など）を自動承認するか
// maxRequests: 自動承認できるリクエスト回数の上限
// enableNotifications: 自動承認時に通知を表示するかどうか
export interface AutoApprovalSettings {
	enabled: boolean
	actions: {
		readFiles: boolean
		editFiles: boolean
		executeCommands: boolean
		useBrowser: boolean
		useMcp: boolean
	}
	maxRequests: number
	enableNotifications: boolean
}
```

### 3.2.1 設定を参照し、必要に応じて確認ダイアログを出す

下記のように、コードを編集する際に自動承認設定を確認し、無効または未設定の場合はユーザーにダイアログで許可を求めます。

```ts
// src/core/Cline.ts の一部イメージ
async function tryEditFile(filePath: string, newContent: string) {
  // 設定を読み込む
  const { enabled, actions } = this.autoApprovalSettings
  
  if (!enabled || !actions.editFiles) {
    // ユーザーの明示許可が必要
    const userConfirmed = await this.askUser("ファイルを編集してもよろしいですか？", ["Yes", "No"])
    if (!userConfirmed) return
  }

  // 実際の編集処理（内部で WorkspaceEdit を利用）
  await this.editFileInternal(filePath, newContent)
}
```

このように **「設定で許容範囲を広くするか、随時ダイアログで確認するか」** を選択でき、意図しない変更を防ぐことが可能です。

---

## 3.3 AIでの差分生成とエディタ適用

AI に修正を依頼するときは、まずソースコードや要望をプロンプトとして送り、修正後のコードや差分を受け取ります。
その上で、VS Code の API を介してファイルを書き換える、というフローです。

### 3.3.1 AIにリクエストを送る (`/src/api/providers/*.ts`)

Cline では、OpenAI、Anthropicなど複数のモデルを一括管理できる仕組みを用意しています。`buildApiHandler()` でプロバイダを切り替え、`createMessage(...)` で実際にプロンプトを送信し、ストリーミング形式で結果を受け取ります。

```ts
// src/api/index.ts

// 指定されたプロバイダに応じて、対応するハンドラを生成
export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
  switch (configuration.apiProvider) {
    case "openai":
      return new OpenAiHandler(options)
    case "anthropic":
      return new AnthropicHandler(options)
    // ...etc
    default:
      return new AnthropicHandler(options)
  }
}
```

```ts
// src/api/providers/openai.ts (抜粋)

@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
  // OpenAI の API にリクエストを送り、ストリーミング形式でレスポンスを受け取る
  const stream = await this.client.chat.completions.create({
    model: this.options.openAiModelId ?? "",
    messages: [ { role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages) ],
    temperature: 0,
    stream: true,
    stream_options: { include_usage: true },
  })

  for await (const chunk of stream) {
    const delta = chunk.choices[0]?.delta
    if (delta?.content) {
      // 逐次生成された文章をイベントとして渡す
      yield { type: "text", text: delta.content }
    }
    // usageトークン情報も取り出す場合はここで処理
  }
}
```

### 3.3.2 AIが返す「差分形式」のパース

LLM からは「修正後のテキスト全体」を返させる方法と、「diff形式」を返させる方法があります。
Cline は主に修正後の全文を返させる方針ですが、内部的には差分をパースしてマージするためのユーティリティも備えています。

```ts
// src/core/assistant-message/diff.ts (抜粋)

// originalContent に対して patchText に書かれた差分を適用して
// 修正後のテキストを返す処理を実装（例: Unified Diff 形式など）
export function applyTextDiffToFile(originalContent: string, patchText: string): string {
  // AIが吐く「- 削除」「+ 追加」形式の差分をパースし、
  // line-by-line で合成する処理などを実装
}
```

このように、独自フォーマットや Unified Diff 形式を扱えるようにしておくことで、「差分」だけで修正内容を適用することも可能です。

### 3.3.3 VS Code APIを使った実ファイル更新

AI から最終的な修正後テキスト（または差分）が得られたら、VS Code の `WorkspaceEdit` を使ってファイルを書き換えます。
たとえば全文置換の例は次のとおりです。

```ts
// src/integrations/editor/*.ts イメージ
import * as vscode from 'vscode'

// ファイル全体を新しい内容に上書きする関数
async function overwriteFile(uri: vscode.Uri, newContent: string) {
  // 対象ドキュメントを開き、WorkspaceEdit を作成
  const document = await vscode.workspace.openTextDocument(uri)
  const edit = new vscode.WorkspaceEdit()

  // ドキュメント全体の範囲を取得（開始は 0行0列、終了は lineCount 行目の先頭0列）
  const fullRange = new vscode.Range( 
    0, 0,
    document.lineCount, 0
  )

  // 指定範囲に newContent を置換適用
  edit.replace(uri, fullRange, newContent)
  await vscode.workspace.applyEdit(edit)
  // 編集内容をファイルに保存
  await document.save()
}
```

細かい差分ごとに書き換えたい場合は `edit.insert` や `edit.delete` を併用するなど、ニーズに応じて実装を変えることができます。

---

## 3.4 途中でユーザー承認を挟む例

Cline は「本当にその修正でよいか？」をユーザーに最終確認できる仕組みを持っています。  

たとえば AI が「`edit_file`」といったコマンドをチャットメッセージに含めてきた場合、バックエンド側で自動承認設定を確認し、必要であればユーザーにダイアログを表示します。

```ts
// src/core/Cline.ts (単純化例)

async handleAiInstruction(instruction: AiInstruction) {
  if (instruction.type === "edit_file") {
    // 1) まず編集許可が得られるかチェック（AutoApproval設定の有無を確認）
    if (!this.canAutoApprove("editFiles")) {
      const userResponse = await vscode.window.showInformationMessage(
        `Edit file: ${instruction.filePath} ?`,
        "Yes", "No"
      )
      if (userResponse !== "Yes") return
    }
    // 2) 許可OKなら編集適用
    await overwriteFile(vscode.Uri.file(instruction.filePath), instruction.newContent)
  }
}
```

このように対話的に進めることで、勝手に危険な操作が進行してしまうリスクを下げています。

---

## 3.5 エラーハンドリングや通信リトライ

Cline には、通信エラー時に自動で再試行する仕組みが備わっています。
その中心となるのが `@withRetry()` デコレータです。

```ts
// src/api/retry.ts

// 指定回数までならエラー発生時に再試行を試みるデコレータ
export function withRetry(options: RetryOptions = {}) {
  return function (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value
    descriptor.value = async function* (...args: any[]) {
      for (let attempt = 0; attempt < options.maxRetries; attempt++) {
        try {
          // 元のメソッドを呼び出し、yield* でストリーミングの結果を返す
          yield* originalMethod.apply(this, args)
          return
        } catch (error) {
          // Rate Limit 等の一時的なエラーが発生したら一定時間待機して再試行
          // ...
        }
      }
    }
    return descriptor
  }
}
```

これを付与したメソッド（下記の例では `createMessage`）で一時的なエラーが起きた場合、指定回数までは自動的にリトライが試みられます。

```ts
@withRetry()
async *createMessage(...) {
  // ...
}
```

HTTP のレートリミットや一時的なネットワーク障害を吸収できるため、ユーザーの操作を止めにくくする工夫として有効です。

---


## 3.6 スライディングウィンドウ（コンテキスト管理）

Cline は LLM のコンテキスト制限を考慮し、**スライディングウィンドウ方式** でチャット履歴を管理します。  
新しいメッセージを優先し、古い履歴を削除・要約することで、最適なトークン制御を行います。

### 3.6.1 実装のポイント
1. **直近の会話を優先** - 最新のユーザー指示と AI 応答はそのまま保持  
2. **古い履歴を要約・削除** - 重要度に応じて圧縮  
3. **ファイル情報を最適化** - 必要な関数・クラスのみ抽出（Tree-sitter活用）  
4. **トークン超過を防ぐ** - 最大コンテキストサイズ内に収まるよう調整  

### 3.6.2 削除範囲の決定 (`getNextTruncationRange`)

Cline は **古いメッセージを動的に削除** しながら、`user → assistant` の会話ペア構造を維持します。

```ts
export function getNextTruncationRange(messages, currentDeletedRange, keep = "half") {
  const startOfRest = currentDeletedRange ? currentDeletedRange[1] + 1 : 1
  const messagesToRemove = Math.floor((messages.length - startOfRest) / (keep === "half" ? 4 : 8)) * 2
  let rangeEndIndex = startOfRest + messagesToRemove - 1

  if (messages[rangeEndIndex].role !== "user") rangeEndIndex -= 1

  return [1, rangeEndIndex]
}
```

- **新しい指示は残し、古い履歴を優先的に削除**
- **削除後も `user → assistant` の並びを維持**
- **トークン超過時のみ適用し、過度な履歴削除を防ぐ**


### 3.6.3 メッセージの最適化 (`getTruncatedMessages`)

この関数で、削除対象範囲を除外した履歴を生成します。

```ts
export function getTruncatedMessages(messages, deletedRange) {
  if (!deletedRange) return messages
  return [...messages.slice(0, deletedRange[0]), ...messages.slice(deletedRange[1] + 1)]
}
```

- `deletedRange` の範囲を除いた新しい履歴を作成
- **削除範囲がない場合は全履歴を維持**
- これにより、不要なデータを排除しながら、**最適なコンテキストを LLM に送信**

### 3.6.4 全体の流れ

1. **`getNextTruncationRange` で削除範囲を決定**
2. **`getTruncatedMessages` で履歴を最適化**
3. **トークン数を抑えつつ、重要な情報を LLM に送信**

この仕組みにより、**Cline は適切なコンテキスト管理を行い、LLM の性能を最大化** します。

---

## 3.7 メモリ使用量・キャッシュ戦略

- **メモリ使用量**  
  大きなファイルを一括で LLM に送るとトークン過多になるため、Cline 側で部分抜粋を行います。  
  ローカルメモリには、チャット履歴・一時的な差分など最低限を保持する設計で、VS Code が大きくメモリを消費しないよう配慮されています。

- **LLM キャッシュ**  
  DeepSeek など一部のプロバイダがサーバーサイドのプロンプトキャッシュを備えており、下記のようにストリーミングで `cacheReadTokens` や `cacheWriteTokens` が返ってきます。

```ts
// src/api/providers/deepseek.ts (抜粋)
for await (const chunk of stream) {
  if (chunk.usage) {
    yield {
      type: "usage",
      inputTokens: chunk.usage.prompt_tokens || 0,
      outputTokens: chunk.usage.completion_tokens || 0,
      cacheReadTokens: chunk.usage.prompt_cache_hit_tokens || 0,
      cacheWriteTokens: chunk.usage.prompt_cache_miss_tokens || 0,
    }
  }
}
```

Cline 側では、これらの使用量を `ApiStream` イベントとして取り込み、コスト算出や表示に使っています。

---

## 3.8 AIが提案した差分マージの仕組み

LLM が返す修正提案は「全文置換形式」か「diff形式」の場合があります。
Cline は差分パーサーを備え、VS Code API で安全にマージします。

```ts
// src/core/assistant-message/diff.ts (抜粋)
export function applyTextDiffToFile(originalContent: string, patchText: string): string {
  // AIが吐く「- 削除」「+ 追加」形式のパッチをパースし、
  // lineごとに合成して、マージ後のテキストを返す
  return mergedContent
}
```

1. **差分解析**: `-` 行や `+` 行を読み取り、行番号単位で再構築  
2. **実ファイルとの突き合わせ**: 最新のファイルを再取得し、ずれがないかを確認  
3. **ユーザー承認**: 同じ箇所に競合があればユーザーに確認を促す  
4. **WorkspaceEdit**: 確定したら VS Code へ適用

これにより、AI が生成した提案が **最大限安全かつ柔軟に** 適用されるようになっています。

---

## 3.9 Tree-sitter を活用した構文解析

Cline は特定言語の構文解析をするために **Tree-sitter** を利用し、クラスや関数の宣言を抽出したり、部分的なコードだけを LLM に渡す設計を行えます。

```ts
// src/services/tree-sitter/languageParser.ts (イメージ)
export async function parseFileContent(code: string, lang: string) {
  const parser = new Parser()
  // 事前に tree-sitter-{lang}.wasm を読み込み
  parser.setLanguage(await loadLanguage(lang))
  const tree = parser.parse(code)
  return tree
}
```

そして、言語別に定義したクエリ (`queries/*.ts`) を用いて、クラス名やメソッド定義を抽出可能です。  
例えば、JavaScript 向けには下記のようなクエリが定義されています。

```ts
// src/services/tree-sitter/queries/javascript.ts (抜粋)
(
  (function_declaration
    name: (identifier) @name.definition.function)
) @definition.function
```

この仕組みにより、ユーザーが「このファイルの特定メソッドを直して」と言った時、**正確に該当箇所のコードだけ LLM に渡す** ことが可能となります。


---

## 3.10 MCP (Model Context Protocol) や他ツール連携の拡張

Cline は **MCP (Model Context Protocol)** に対応し、AI が **外部コマンドの実行** や **リソースの取得・更新** を行うことを可能にします。

### 3.10.1 MCP の概要

- **McpTool**: 外部CLIやAPIの実行を定義（例: Docker操作ツール）。
- **McpResource**: ファイルURLやDBエンドポイントを抽象化し、LLM がアクセス可能にする。

Cline は **McpHub** を介して MCP のツールとリソースを管理し、LLM からのリクエストを適切に処理します。

### 3.10.2 McpHub の役割

**McpHub** は、Cline 内で MCP に関するすべての機能を統括するコンポーネントです。

#### 主な機能:
1. **ツールとリソースの管理**
   - `registerTool()` で外部ツールを登録
   - `registerResource()` でファイルやAPIエンドポイントを登録

2. **リクエストの処理**
   - LLM から `tool_use` 要求を受けると、適切なツールを実行
   - `readResource()` でリソースの取得をサポート

#### 例: McpHub にツールを登録
```ts
cline.mcpHub.registerTool({
  name: "docker_command",
  description: "Docker CLI を実行",
  inputSchema: { type: "object", properties: { command: { type: "string" } } }
})
```

#### 例: LLM からのツール実行要求を処理
```ts
async handleToolUseRequest(request) {
  const tool = this.tools.get(request.toolName)
  if (tool) { return executeTool(tool, request.input) }
}
```

### 3.10.3 カスタムツールの追加

1. **カスタムツールを作成**
   ```ts
   export const GitTool: McpTool = {
     name: "git_status",
     description: "Git のステータスを取得",
     inputSchema: {}
   }
   ```
   
2. **McpHub に登録**
   ```ts
   cline.mcpHub.registerTool(GitTool)
   ```

3. **LLM がリクエストを送ると実行**
   ```ts
   async handleToolUseRequest(request) {
     if (request.toolName === "git_status") { /* Git のステータスを取得 */ }
   }
   ```

### 3.10.4 運用のポイント

- **権限管理**: `autoApprove` で手動承認を制御。
- **リソース制限**: 読み取り専用リソースの設定。
- **ツール管理**: 拡張のロード/アンロードで柔軟な運用。

McpHub を活用することで、Cline は単なるエディタ拡張を超え、開発環境と AI の連携を強化できます。


# 4. まとめ

Cline はチャット形式のコマンドによってコード修正を自動化する VS Code 拡張です。

ファイル特定から編集提案、ユーザーへの承認フロー、ファイル上書きまでを一貫して行い、さらに多様な LLM と連携できる設計が特徴といえます。  

また、今回紹介したように **スライディングウィンドウ** や **差分マージ、Tree-sitter解析、MCP連携** など、高度な仕組みも取り入れており、大規模リポジトリや複雑なツール群との統合にも柔軟に対応できます。

- **修正対象の検出**（チャット上のメンション解析、.clineignore や Tree-sitter による構文抽出）
- **安全性の担保**（ユーザー承認や自動承認設定、エラーハンドリングとリトライ機構、MCPツール呼び出しの制御）
- **スライディングウィンドウ方式**（チャット履歴・ファイル情報を必要最小限にトリミングして LLM に渡す）
- **差分マージと競合対処**（AI が提案した変更をパーサーで解析し、VS Code の WorkspaceEdit に安全に適用）
- **MCP (Model Context Protocol)** による外部ツール連携

今後さらに機能が拡張されることで、より高度なコード修正やプロジェクト管理が実現できると期待されています。

---
### 会社紹介

**株式会社 Mosaica**  
最先端テクノロジーで社会課題を解決し、持続可能な未来を創造する IT カンパニー。
AI ソリューション、クラウド統合、DX 推進、経営コンサルティングなど包括的なサービスでビジネス変革を支援しています。  

詳しくは [公式サイト](https://mosaica.co.jp/) までお気軽にご相談ください。
公式サイト： https://mosaica.co.jp/