# 機能設計書 91-プラグイン管理

## 概要

本ドキュメントはNushellのプラグイン管理機能について記述する。プラグインのライフサイクル全体を管理するコマンド群（plugin add, plugin rm, plugin list, plugin use, plugin stop）の設計を定義する。

### 本機能の処理概要

プラグイン管理機能は、Nushellの拡張機能であるプラグインの登録、削除、一覧表示、読み込み、停止を行う一連のコマンドを提供する。

**業務上の目的・背景**：Nushellはモジュラー設計を採用しており、コア機能を軽量に保ちながら、プラグインによって機能を拡張できる。プラグイン管理機能は、ユーザーが必要な機能のみを選択的にインストール・有効化できるようにし、システムリソースの効率的な利用とカスタマイズ性を両立させる。

**機能の利用シーン**：
- 新しいプラグインをインストールする際（plugin add）
- 不要になったプラグインを削除する際（plugin rm）
- 現在のプラグイン状態を確認する際（plugin list）
- スクリプト内でプラグインコマンドを使用する際（plugin use）
- 実行中のプラグインプロセスを停止する際（plugin stop）

**主要な処理内容**：
1. プラグイン実行ファイルの検出とシグネチャ取得（plugin add）
2. プラグインレジストリファイルへの登録・削除操作
3. プラグインの動的ロードとスコープへの追加（plugin use）
4. 実行中プラグインプロセスの管理と停止（plugin stop）
5. エンジンとレジストリの状態を統合したステータス表示（plugin list）

**関連システム・外部連携**：プラグインは独立したプロセスとして実行され、MessagePackまたはJSON形式でNushellエンジンと通信する。LocalSocketまたはStdioによる通信モードをサポートする。

**権限による制御**：プラグインの実行ファイルへのアクセス権限が必要。プラグインレジストリファイルの読み書き権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はCLIコマンドとして提供され、特定の画面とは関連しない |

## 機能種別

CRUD操作（プラグインレジストリの登録・削除・参照）/ プロセス管理（プラグインプロセスの起動・停止）

## 入力仕様

### 入力パラメータ

#### plugin add

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| filename | String | Yes | プラグイン実行ファイルのパス | nu_plugin_で始まるファイル名であること |
| --plugin-config | Filepath | No | カスタムプラグインレジストリファイルのパス | - |
| --shell | Filepath | No | プラグイン実行に使用するシェルプログラム | - |

#### plugin rm

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | Yes | 削除するプラグインの名前またはファイル名 | - |
| --plugin-config | Filepath | No | カスタムプラグインレジストリファイルのパス | - |
| --force | Switch | No | プラグインが見つからない場合もエラーにしない | - |

#### plugin list

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| --plugin-config | Filepath | No | カスタムプラグインレジストリファイルのパス | - |
| --engine | Switch | No | エンジンにロードされているプラグインのみ表示 | - |
| --registry | Switch | No | レジストリファイルのプラグインのみ表示 | - |

#### plugin use

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | Yes | ロードするプラグインの名前またはファイル名 | - |
| --plugin-config | Filepath | No | カスタムプラグインレジストリファイルのパス | - |

#### plugin stop

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | Yes | 停止するプラグインの名前またはファイル名 | - |

### 入力データソース

- プラグイン実行ファイル: ファイルシステム上のnu_plugin_*実行ファイル
- プラグインレジストリファイル: $nu.plugin-path（デフォルト）またはカスタムパス
- EngineState: 現在ロードされているプラグイン情報

## 出力仕様

### 出力データ

#### plugin add / plugin rm / plugin stop

| 項目名 | 型 | 説明 |
|--------|-----|------|
| - | Nothing | 成功時は何も出力しない |

#### plugin list

| 項目名 | 型 | 説明 |
|--------|-----|------|
| name | String | プラグイン名 |
| version | String | プラグインバージョン |
| status | String | ステータス（added/loaded/running/modified/removed/invalid） |
| pid | Int | プロセスID（実行中の場合） |
| filename | String | 実行ファイルのパス |
| shell | String | 使用するシェル（ある場合） |
| commands | List[String] | 提供するコマンドのリスト |

### 出力先

- 標準出力: plugin listの結果
- プラグインレジストリファイル: plugin add / plugin rmによる変更

## 処理フロー

### 処理シーケンス

```
plugin add:
1. プラグインファイルの検索
   └─ カレントディレクトリ → NU_PLUGIN_DIRS の順で検索
2. PluginIdentityの生成
   └─ ファイル名がnu_plugin_で始まることを検証
3. PersistentPluginの作成とプラグイン起動
   └─ PluginGcConfigでGCを即時停止に設定
4. メタデータとシグネチャの取得
   └─ プラグインプロセスからget_metadata / get_signatureを呼び出し
5. プラグインレジストリファイルの更新
   └─ PluginRegistryItemを作成してupsert

plugin list:
1. エンジンからプラグイン情報を取得
   └─ engine_state.plugins()とplugin_decls()を参照
2. レジストリファイルからプラグイン情報を取得
   └─ プラグインレジストリファイルをパース
3. 両方の情報をマージ
   └─ 名前でソートしてステータスを決定

plugin stop:
1. プラグイン名またはファイル名でプラグインを検索
2. RegisteredPlugin::stop()を呼び出し
   └─ 内部でRunningPluginをdropしてプロセスを終了
```

### フローチャート

```mermaid
flowchart TD
    A[plugin add 開始] --> B[ファイルパス解決]
    B --> C{ファイル存在?}
    C -->|No| D[エラー: ファイルが見つからない]
    C -->|Yes| E[PluginIdentity作成]
    E --> F{名前がnu_plugin_で開始?}
    F -->|No| G[エラー: 無効なファイル名]
    F -->|Yes| H[PersistentPlugin作成]
    H --> I[プラグインプロセス起動]
    I --> J[メタデータ・シグネチャ取得]
    J --> K[レジストリファイル更新]
    K --> L[成功]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-91-01 | プラグイン命名規則 | プラグイン実行ファイルはnu_plugin_で始まる必要がある | plugin add時 |
| BR-91-02 | ステータス優先順位 | running > invalid > modified > loaded/added | plugin list表示時 |
| BR-91-03 | GC自動停止 | 設定時間無操作でプラグインプロセスを自動停止 | プラグイン実行後 |

### 計算ロジック

プラグインステータスの決定ロジック:
- `running`: プラグインプロセスが実行中（pid が存在）
- `invalid`: レジストリファイルのデータがデシリアライズできない
- `modified`: エンジンとレジストリで情報が異なる
- `loaded`: エンジンとレジストリ両方に存在するが実行中でない
- `added`: レジストリのみに存在
- `removed`: エンジンのみに存在

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

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

本機能はデータベースを使用しない。プラグインレジストリファイル（MessagePack形式）を使用する。

| 操作 | 対象ファイル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| plugin add | プラグインレジストリ | UPDATE/INSERT | プラグイン情報を追加または更新 |
| plugin rm | プラグインレジストリ | DELETE | プラグイン情報を削除 |
| plugin list | プラグインレジストリ | SELECT | プラグイン情報を読み取り |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| PluginFailedToLoad | ShellError | プラグインプロセスの起動失敗 | パスを確認、実行権限を確認 |
| GenericError | ShellError | 無効なプラグインファイル名 | nu_plugin_で始まるファイル名を使用 |
| GenericError | ShellError | プラグインがレジストリに存在しない | plugin addで登録してからplugin useを実行 |
| IoError | ShellError | ファイルが見つからない | パスを確認 |

### リトライ仕様

プラグインプロセス起動時にLocalSocketモードが失敗した場合、自動的にStdioモードにフォールバックする。

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

プラグインレジストリファイルの更新は単一ファイル操作のため、トランザクション管理は不要。ファイル全体を読み込み、変更後に上書き保存する。

## パフォーマンス要件

- プラグイン起動: プロセス起動のため数百ミリ秒程度
- plugin list: 即座に応答（ファイル読み込みのみ）
- ガベージコレクション: 設定されたstop_after時間後にプラグインを自動停止

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

- プラグインは独立したプロセスとして実行され、Nushellエンジンとは分離される
- プラグイン実行ファイルへの実行権限が必要
- プラグインレジストリファイルへの読み書き権限が必要
- シグナルハンドラによりCtrl-Cをプラグインに伝播可能

## 備考

- プラグインはPython等のスクリプト言語で実装することも可能（--shellオプション使用）
- feature: local-socket が有効な場合、より効率的な通信が可能

---

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

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

### 推奨読解順序

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

プラグイン管理の中核となるデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | registered.rs | `crates/nu-protocol/src/plugin/registered.rs` | RegisteredPluginトレイトの定義。プラグインの基本インターフェースを理解 |
| 1-2 | identity.rs | `crates/nu-protocol/src/plugin/identity.rs` | PluginIdentityの定義。プラグインの識別情報（ファイル名、シェル、名前） |
| 1-3 | registry_file/mod.rs | `crates/nu-protocol/src/plugin/registry_file/mod.rs` | PluginRegistryItem、PluginRegistryFileの定義。レジストリファイル形式 |

**読解のコツ**: RegisteredPluginトレイトは全てのプラグイン実装が満たすべきインターフェースを定義している。identity(), is_running(), pid(), stop()などの基本操作を把握することが重要。

#### Step 2: エントリーポイントを理解する

各プラグイン管理コマンドの実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | add.rs | `crates/nu-cmd-plugin/src/commands/plugin/add.rs` | plugin addコマンドの実装。プラグイン登録の流れ |
| 2-2 | list.rs | `crates/nu-cmd-plugin/src/commands/plugin/list.rs` | plugin listコマンドの実装。ステータスマージロジック |
| 2-3 | stop.rs | `crates/nu-cmd-plugin/src/commands/plugin/stop.rs` | plugin stopコマンドの実装。プラグイン停止処理 |

**主要処理フロー（add.rs）**:
1. **76-82行目**: runメソッドのエントリーポイント、引数の解析
2. **88-97行目**: nu_path::locate_in_dirsでプラグインファイルを検索
3. **107-115行目**: PluginIdentityの生成とバリデーション
4. **121-130行目**: PersistentPlugin作成とシグネチャ取得
5. **132-137行目**: レジストリファイルへの書き込み

#### Step 3: プラグインエンジンを理解する

プラグインの実行管理を担当するエンジン層を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | persistent.rs | `crates/nu-plugin-engine/src/persistent.rs` | PersistentPluginの実装。プラグインのライフサイクル管理 |
| 3-2 | gc.rs | `crates/nu-plugin-engine/src/gc.rs` | PluginGcの実装。プラグインのガベージコレクション |

**主要処理フロー（persistent.rs）**:
- **61-74行目**: PersistentPlugin::new - プラグインインスタンスの作成
- **80-133行目**: get() - プラグインインターフェースの取得またはスポーン
- **136-227行目**: spawn() - プラグインプロセスの起動処理
- **229-252行目**: stop_internal() - プラグイン停止処理

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

```
plugin add
    │
    ├─ PluginAdd::run()
    │      │
    │      ├─ nu_path::locate_in_dirs()    # ファイル検索
    │      ├─ PluginIdentity::new()        # ID生成
    │      ├─ PersistentPlugin::new()      # プラグイン作成
    │      │      └─ PluginGc::new()       # GC作成
    │      ├─ GetPlugin::get_plugin()      # プラグイン起動
    │      │      └─ PersistentPlugin::spawn()
    │      │             ├─ create_command()
    │      │             ├─ Command::spawn()
    │      │             └─ make_plugin_interface()
    │      └─ modify_plugin_file()         # レジストリ更新
    │
plugin list
    │
    ├─ PluginList::run()
    │      ├─ get_plugins_in_engine()      # エンジン情報取得
    │      ├─ get_plugins_in_registry()    # レジストリ情報取得
    │      └─ merge_plugin_info()          # 情報マージ
    │
plugin stop
    │
    └─ PluginStop::run()
           └─ RegisteredPlugin::stop()
                  └─ PersistentPlugin::stop_internal()
```

### データフロー図

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

プラグイン                PersistentPlugin               プラグイン
実行ファイル ───────▶    ├─ spawn()          ───────▶  プロセス(PID)
                          └─ get_signature()
                                 │
                                 ▼
                          PluginRegistryItem
                                 │
                                 ▼
レジストリ               modify_plugin_file()  ───────▶  レジストリ
ファイル    ◀──────────  └─ upsert_plugin()              ファイル
(読み込み)                                               (更新)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| add.rs | `crates/nu-cmd-plugin/src/commands/plugin/add.rs` | ソース | plugin addコマンド実装 |
| rm.rs | `crates/nu-cmd-plugin/src/commands/plugin/rm.rs` | ソース | plugin rmコマンド実装 |
| list.rs | `crates/nu-cmd-plugin/src/commands/plugin/list.rs` | ソース | plugin listコマンド実装 |
| use_.rs | `crates/nu-cmd-plugin/src/commands/plugin/use_.rs` | ソース | plugin useコマンド実装 |
| stop.rs | `crates/nu-cmd-plugin/src/commands/plugin/stop.rs` | ソース | plugin stopコマンド実装 |
| util.rs | `crates/nu-cmd-plugin/src/util.rs` | ソース | ユーティリティ関数（レジストリファイル操作等） |
| persistent.rs | `crates/nu-plugin-engine/src/persistent.rs` | ソース | PersistentPlugin実装 |
| gc.rs | `crates/nu-plugin-engine/src/gc.rs` | ソース | PluginGc実装 |
| registered.rs | `crates/nu-protocol/src/plugin/registered.rs` | ソース | RegisteredPluginトレイト定義 |
| identity.rs | `crates/nu-protocol/src/plugin/identity.rs` | ソース | PluginIdentity定義 |
| registry_file/mod.rs | `crates/nu-protocol/src/plugin/registry_file/mod.rs` | ソース | レジストリファイル形式定義 |
