# 機能設計書 10-Write ツール

## 概要

本ドキュメントは、OpenCodeシステムにおけるWrite ツールの機能設計を記述する。Write ツールは、ファイル全体を新しいコンテンツで書き込む機能を提供し、LSP診断との連携により編集結果の妥当性を検証する。

### 本機能の処理概要

Write ツールは、指定されたファイルパスに対して、ファイル全体を新しいコンテンツで上書きまたは新規作成する。Editツールが部分的な文字列置換に特化しているのに対し、WriteツールはファイルB全体の書き込みを行う。

**業務上の目的・背景**：新規ファイルの作成や、ファイル全体の書き換えが必要な場合に使用される。Editツールでは対応が難しい大規模な変更や、新規ファイル作成時に適している。ただし、既存ファイルの部分的な編集にはEditツールの使用が推奨される。

**機能の利用シーン**：新規ファイルの作成、設定ファイルの全面書き換え、テンプレートからのファイル生成など。「新しいコンポーネントファイルを作成して」「この設定ファイルを更新して」などの指示に対応。

**主要な処理内容**：
1. ファイルパスの検証と正規化
2. 外部ディレクトリ権限チェック
3. 既存ファイルの場合、読み取り時刻の検証
4. 差分生成と権限チェック
5. ファイル書き込み
6. LSP診断情報の取得（対象ファイルとプロジェクト全体）

**関連システム・外部連携**：LSP（Language Server Protocol）との連携により、書き込み後のファイルに対する診断情報を取得。対象ファイルだけでなく、プロジェクト全体への影響も検出する。

**権限による制御**：edit権限でファイルパスパターンをチェック。外部ディレクトリへのアクセスはexternal_directory権限で制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | セッション | 補助機能 | Writeコンポーネントによるファイル書き込み結果表示 |

## 機能種別

ファイル書き込み / ファイル作成

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| content | string | Yes | 書き込む内容 | 文字列型 |
| filePath | string | Yes | 書き込み先ファイルの絶対パス | 有効なファイルパス |

### 入力データソース

- エージェントからのツール呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| filepath | string | 書き込み先ファイルパス |
| exists | boolean | 既存ファイルだったかどうか |
| diagnostics | object | LSP診断情報（対象ファイル+他ファイル） |

### 出力先

- ツール結果としてエージェントに返却
- ファイルシステムへの書き込み

## 処理フロー

### 処理シーケンス

```
1. パラメータ検証
   └─ filePath、contentの検証
2. パス正規化
   └─ 絶対パスへの変換
3. 外部ディレクトリ権限チェック
   └─ assertExternalDirectory()
4. 既存ファイルチェック
   └─ ファイル存在確認と時刻検証
5. 差分生成
   └─ createTwoFilesPatch()で差分作成
6. 権限チェック
   └─ edit権限でパターンチェック
7. ファイル書き込み
   └─ Filesystem.write()
8. イベント発行
   └─ File.Event.Edited、FileWatcher.Event.Updated
9. LSP診断取得
   └─ 対象ファイルと他ファイルの診断情報
10. 結果返却
    └─ 診断情報を含む結果を返却
```

### フローチャート

```mermaid
flowchart TD
    A[パラメータ受信] --> B[パス正規化]
    B --> C[外部ディレクトリチェック]
    C --> D{ファイル存在?}
    D -->|Yes| E[既存ファイル読み込み]
    D -->|No| F[空文字列をoldContentに設定]
    E --> G[FileTime検証]
    F --> H[差分生成]
    G --> H
    H --> I[edit権限チェック]
    I -->|許可| J[ファイル書き込み]
    I -->|拒否| K[エラー返却]
    J --> L[イベント発行]
    L --> M[LSP診断取得]
    M --> N{対象ファイルにエラー?}
    N -->|Yes| O[エラー情報追加]
    N -->|No| P{他ファイルにエラー?}
    O --> P
    P -->|Yes| Q[他ファイルエラー情報追加]
    P -->|No| R[結果返却]
    Q --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 上書き動作 | 既存ファイルは上書きされる | 常時 |
| BR-02 | 新規作成 | ファイルが存在しない場合は新規作成 | ファイル未存在時 |
| BR-03 | ファイル時刻検証 | 既存ファイルの場合、最後の読み取り以降に変更されていないことを確認 | 既存ファイル編集時 |
| BR-04 | 対象ファイル診断 | 書き込んだファイルのLSPエラーを優先表示 | 診断取得時 |
| BR-05 | 他ファイル診断 | プロジェクト内の他ファイルへの影響も検出 | 診断取得時 |
| BR-06 | 診断制限（対象） | 対象ファイルは最大20件のエラーを表示 | エラー多数時 |
| BR-07 | 診断制限（他） | 他ファイルは最大5ファイルまで表示 | 影響ファイル多数時 |
| BR-08 | Editツール推奨 | 既存ファイルの部分編集にはEditツール使用を推奨 | 常時 |

### 計算ロジック

- 差分計算: createTwoFilesPatch()で unified diff生成
- MAX_DIAGNOSTICS_PER_FILE = 20
- MAX_PROJECT_DIAGNOSTICS_FILES = 5

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ツール結果保存 | message_part | UPDATE | 書き込み結果のメタデータ保存 |

### テーブル別操作詳細

#### message_part

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | state.metadata.filepath | ファイルパス | 書き込み完了時 |
| UPDATE | state.metadata.exists | 既存ファイルフラグ | 書き込み完了時 |
| UPDATE | state.metadata.diagnostics | LSP診断情報 | 書き込み完了時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | DeniedError | 権限ルールによる拒否 | エラーメッセージ返却 |
| - | RejectedError | ユーザーによる拒否 | エラーメッセージ返却 |
| - | 書き込みエラー | ファイルシステムエラー | エラーメッセージ返却 |

### リトライ仕様

書き込み操作はリトライなし。失敗時はエラー状態を返却。

## トランザクション仕様

FileTime検証後の書き込みはアトミックに実行。

## パフォーマンス要件

- LSP診断の非同期取得
- 大規模ファイルの効率的な書き込み

## セキュリティ考慮事項

- 外部ディレクトリアクセスの権限チェック
- ファイルパスパターンに基づく権限制御
- ファイル時刻検証による競合状態検出

## 備考

Write ツールは、ドキュメントファイル（*.md）やREADMEファイルの自動作成を行わないよう推奨されている。既存ファイルの編集にはEditツールの使用が推奨される。

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

Writeツールのパラメータスキーマと出力構造を理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | write.ts | `packages/opencode/src/tool/write.ts` | WriteTool定義とパラメータスキーマ（19-24行目） |
| 1-2 | tool.ts | `packages/opencode/src/tool/tool.ts` | Tool.define()の基盤構造 |

**読解のコツ**: パラメータはcontent（書き込み内容）とfilePath（ファイルパス）の2つのみでシンプル。

#### Step 2: 実行フローを理解する

execute関数の処理フローを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | write.ts | `packages/opencode/src/tool/write.ts` | execute関数（25-84行目） |

**主要処理フロー**:
1. **26-27行目**: パス正規化とexternal_directoryチェック
2. **29-31行目**: 既存ファイルチェックと時刻検証
3. **33行目**: trimDiff()で差分生成
4. **34-42行目**: edit権限チェック
5. **44-51行目**: ファイル書き込みとイベント発行
6. **55-72行目**: LSP診断取得と結果整形

#### Step 3: LSP診断処理を理解する

対象ファイルと他ファイルの診断情報取得を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | write.ts | `packages/opencode/src/tool/write.ts` | 診断処理（54-72行目） |

**主要処理フロー**:
- **55行目**: LSP.touchFile()でファイル更新を通知
- **56行目**: LSP.diagnostics()で診断情報取得
- **59-67行目**: 対象ファイルのエラー処理
- **68-71行目**: 他ファイルのエラー処理（最大5ファイル）

### プログラム呼び出し階層図

```
WriteTool.execute() [write.ts]
    │
    ├─ assertExternalDirectory()
    │      └─ 外部ディレクトリ権限チェック
    │
    ├─ Filesystem.exists()
    │      └─ ファイル存在確認
    │
    ├─ FileTime.assert()
    │      └─ ファイル時刻検証（既存ファイル時）
    │
    ├─ trimDiff(createTwoFilesPatch())
    │      └─ 差分生成
    │
    ├─ ctx.ask({permission: "edit"})
    │      └─ 権限チェック
    │
    ├─ Filesystem.write()
    │      └─ ファイル書き込み
    │
    ├─ Bus.publish(File.Event.Edited)
    │      └─ 編集イベント発行
    │
    ├─ Bus.publish(FileWatcher.Event.Updated)
    │      └─ ファイル監視イベント発行
    │
    └─ LSP.diagnostics()
           │
           ├─ 対象ファイル診断（最大20件）
           │
           └─ 他ファイル診断（最大5ファイル）
```

### データフロー図

```
[入力]                    [処理]                         [出力]

content ───▶ Filesystem.write()
    │               │
    │               └──▶ ファイル書き込み
    │
filePath ───▶ パス正規化
    │               │
    │               ├──▶ 権限チェック
    │               │
    │               └──▶ 差分生成 ───▶ diff
                              │
                      LSP.touchFile()
                              │
                      LSP.diagnostics()
                              │
                      ┌───────┴───────┐
                      ▼               ▼
              対象ファイル       他ファイル
              エラー（20件）     エラー（5ファイル）
                      │               │
                      └───────┬───────┘
                              ▼
                        結果返却
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| write.ts | `packages/opencode/src/tool/write.ts` | ソース | Writeツール実装 |
| write.txt | `packages/opencode/src/tool/write.txt` | テンプレート | ツール説明文 |
| tool.ts | `packages/opencode/src/tool/tool.ts` | ソース | ツール基盤定義 |
| edit.ts | `packages/opencode/src/tool/edit.ts` | ソース | trimDiff()関数のインポート元 |
| external-directory.ts | `packages/opencode/src/tool/external-directory.ts` | ソース | 外部ディレクトリ権限チェック |
| time.ts | `packages/opencode/src/file/time.ts` | ソース | ファイル時刻管理（FileTime） |
| filesystem.ts | `packages/opencode/src/util/filesystem.ts` | ソース | ファイルシステム操作 |
| index.ts | `packages/opencode/src/lsp/index.ts` | ソース | LSP診断取得 |
| bus.ts | `packages/opencode/src/bus/index.ts` | ソース | イベントバス |
