- ホーム
- Documentation
- 拡張機能
- プラグインマネージャーとインストーラーの内部構造
プラグインマネージャーとインストーラーの内部構造
このドキュメントでは、xcsh plugin 操作がディスク上のプラグイン状態をどのように変更するか、およびインストールされたプラグインがランタイム機能(現在はツール、フック/コマンドのパス解決も利用可能)としてどのように機能するかについて説明します。
スコープとアーキテクチャ
Section titled “スコープとアーキテクチャ”コードベースには2つのプラグイン管理実装があります。
- CLI コマンドで使用されるアクティブパス:
PluginManager(src/extensibility/plugins/manager.ts) - レガシーヘルパーモジュール: インストーラー関数(
src/extensibility/plugins/installer.ts)
xcsh plugin ... コマンドの実行は PluginManager を経由します。
installer.ts には重要なセキュリティチェックとファイルシステムの動作が記述されていますが、src/commands/plugin.ts + src/cli/plugin-cli.ts が使用するパスではありません。
ライフサイクル: CLI 呼び出しからランタイム利用可能までの流れ
Section titled “ライフサイクル: CLI 呼び出しからランタイム利用可能までの流れ”xcsh plugin <action> ... -> src/commands/plugin.ts -> runPluginCommand(...) in src/cli/plugin-cli.ts -> PluginManager method (install/list/uninstall/link/...) -> mutate ~/.xcsh/plugins/{package.json,node_modules,xcsh-plugins.lock.json} -> runtime discovery: discoverAndLoadCustomTools(...) -> getAllPluginToolPaths(cwd) -> custom tool loader imports tool modulesコマンドエントリーポイント
Section titled “コマンドエントリーポイント”src/commands/plugin.tsはコマンド/フラグを定義し、runPluginCommandに転送します。src/cli/plugin-cli.tsはサブコマンドをPluginManagerのメソッドにマッピングします:install、uninstall、list、link、doctor、features、config、enable、disable
- 明示的な
updateアクションは存在しません。更新は新しいパッケージ/バージョン指定でinstallを再実行することで行います。
ディスク上のモデル
Section titled “ディスク上のモデル”グローバルなプラグイン状態は ~/.xcsh/plugins 以下に保存されます:
package.json—bun install/bun uninstallで使用する依存関係マニフェストnode_modules/— インストール済みプラグインパッケージまたはシンボリックリンクxcsh-plugins.lock.json— ランタイム状態:- プラグインごとの有効/無効状態
- プラグインごとの選択済みフィーチャーセット
- 永続化されたプラグイン設定
プロジェクトローカルのオーバーライドは以下に保存されます:
<cwd>/.xcsh/plugin-overrides.json
オーバーライドはマネージャー/ローダーの観点から読み取り専用(書き込みパスなし)であり、このプロジェクトに対してプラグインを無効化したり、フィーチャー/設定をオーバーライドしたりできます。
プラグイン仕様の解析とメタデータの解釈
Section titled “プラグイン仕様の解析とメタデータの解釈”インストール仕様の文法
Section titled “インストール仕様の文法”parsePluginSpec(parser.ts)がサポートする記法:
pkg->features: null(デフォルト動作)pkg[*]-> マニフェストの全フィーチャーを有効化pkg[]-> オプションフィーチャーを有効化しないpkg[a,b]-> 指定したフィーチャーを有効化@scope/pkg@1.2.3[feat]-> スコープ付き+バージョン指定パッケージに明示的なフィーチャー選択
extractPackageName はインストール後のディスク上パス検索のためにバージョンサフィックスを除去します。
マニフェストのソースと必須フィールド
Section titled “マニフェストのソースと必須フィールド”マニフェストは以下の順序で解決されます:
package.json.xcsh- フォールバック
package.json.pi - フォールバック
{ version: package.version }
影響:
- マネージャー/ローダーには厳密なスキーマ検証が存在しません。
xcsh/piがないパッケージでもインストールおよびリスト表示は可能です。- ランタイムプラグインローディング(
getEnabledPlugins)ではxcsh/piマニフェストがないパッケージはスキップされます。 manifest.versionは常にパッケージのversionで上書きされます。
package.json の JSON が不正な場合は読み込み時にハードエラーとなります。マニフェストの構造が不正な場合は、特定のフィールドが参照されたときにのみ失敗する場合があります。
インストール/更新フロー(PluginManager.install)
Section titled “インストール/更新フロー(PluginManager.install)”- インストール仕様からフィーチャーブラケット構文を解析します。
- パッケージ名を正規表現+シェルメタキャラクター拒否リストに対して検証します。
- プラグインの
package.jsonが存在することを確認します(xcsh-plugins、プライベート依存関係マップ)。 ~/.xcsh/pluginsでbun install <packageSpec>を実行します。- インストール済みパッケージの
node_modules/<name>/package.jsonを読み込みます。 - マニフェストを解決し
enabledFeaturesを計算します:[*]: 宣言済みの全フィーチャー(フィーチャーマップがない場合はnull)[a,b]: マニフェストフィーチャーマップに各フィーチャーが存在することを検証[]: 空のフィーチャーリスト- ベア仕様:
null(後でローダーのデフォルトポリシーを使用)
- ロックファイルのランタイム状態をアップサート:
{ version, enabledFeatures, enabled: true }
更新のセマンティクス
Section titled “更新のセマンティクス”更新はインストールによって行われるため:
xcsh plugin install pkg@newVersionは依存関係とロックファイルのバージョンを更新します。- 既存の設定は保持されます。バージョン/フィーチャー/有効状態のエントリーは上書きされます。
- 個別の「更新チェック」やトランザクション型マイグレーションロジックは存在しません。
削除フロー(PluginManager.uninstall)
Section titled “削除フロー(PluginManager.uninstall)”- パッケージ名を検証します。
- プラグインディレクトリで
bun uninstall <name>を実行します。 - ロックファイルからプラグインのランタイム状態を削除します:
config.plugins[name]config.settings[name]
アンインストールコマンドが失敗した場合、ランタイム状態は変更されません。
リストフロー(PluginManager.list)
Section titled “リストフロー(PluginManager.list)”~/.xcsh/plugins/package.jsonからプラグイン依存関係マップを読み込みます。- ロックファイルのランタイム設定を読み込みます(ファイルが存在しない場合は空のデフォルト値)。
- プロジェクトオーバーライドを読み込みます(
<cwd>/.xcsh/plugin-overrides.json、解析/読み取りエラーの場合は警告を出して空オブジェクト)。 package.jsonが解決可能な各依存関係に対して:InstalledPluginレコードを構築- フィーチャー/有効状態をマージ:
- ベースはロックファイルから(またはデフォルト値)
- プロジェクトオーバーライドでフィーチャー選択を置き換え可能
- プロジェクトの
disabledリストによりプラグインが無効としてマスクされる
これが CLI のステータス出力および設定/フィーチャー操作で使用される実効状態です。
リンクフロー(PluginManager.link)
Section titled “リンクフロー(PluginManager.link)”link はローカルパッケージを ~/.xcsh/plugins/node_modules/<pkg.name> にシンボリックリンクすることで、ローカルプラグイン開発をサポートします。
動作:
- マネージャーの cwd に対して
localPathを解決します。 - ローカルの
package.jsonとnameフィールドの存在を要求します。 - プラグインディレクトリが存在することを確認します。
- スコープ付き名の場合、スコープディレクトリを作成します。
- リンクターゲットの既存パスを削除します。
- シンボリックリンクを作成します。
- デフォルトフィーチャー(
null)で有効なロックファイルエントリーを追加します。
注意点: 現在の PluginManager.link は、レガシーの installer.ts に存在する cwd パス境界チェック(normalizedPath.startsWith(normalizedCwd))を強制しないため、信頼は呼び出し元の責任となります。
ランタイムローディング: インストール済みプラグインから呼び出し可能な機能まで
Section titled “ランタイムローディング: インストール済みプラグインから呼び出し可能な機能まで”ディスカバリーゲート
Section titled “ディスカバリーゲート”getEnabledPlugins(cwd)(plugins/loader.ts)が読み込む内容:
- プラグイン依存関係マニフェスト(
package.json) - ロックファイルのランタイム状態
getConfigDirPaths("plugin-overrides.json", { user: false, cwd })経由のプロジェクトオーバーライド
フィルタリング:
- プラグインの
package.jsonがない場合はスキップ - マニフェスト(
xcsh/pi)がない場合はスキップ - ロックファイルでグローバルに無効化されている場合はスキップ
- プロジェクトで無効化されている場合はスキップ
機能パスの解決
Section titled “機能パスの解決”有効な各プラグインに対して:
resolvePluginToolPaths(plugin)resolvePluginHookPaths(plugin)resolvePluginCommandPaths(plugin)
各リゾルバーはベースエントリーとフィーチャーエントリーを含みます:
- 明示的なフィーチャーリスト -> 選択したフィーチャーのみ
enabledFeatures === null->default: trueとマークされたフィーチャーを有効化
存在しないファイルはサイレントにスキップされます(existsSync ガード)。
現在のランタイム配線の違い
Section titled “現在のランタイム配線の違い”- ツールは現在ランタイムに配線されています:
discoverAndLoadCustomTools(custom-tools/loader.ts)経由で、getAllPluginToolPaths(cwd)を呼び出します。 - パスはカスタムツールディスカバリー内で解決済み絶対パスによって重複排除されます(
seenセット、最初のパスが優先)。 - フック/コマンドリゾルバーは存在してエクスポートされていますが、このコードパスは現在ツールと同じ方法でランタイムレジストリに配線されていません。
ロック/状態管理の詳細
Section titled “ロック/状態管理の詳細”PluginManager はランタイム設定をインスタンスごとにメモリ内(#runtimeConfig)にキャッシュし、初回アクセス時に遅延読み込みします。
読み込み動作:
- ロックファイルが存在しない場合 ->
{ plugins: {}, settings: {} } - ロックファイルの読み込み/解析に失敗した場合 -> 警告+同じ空のデフォルト値
保存動作:
- ミューテーションのたびにロックファイル全体を JSON プリティプリント形式で書き込みます
クロスプロセスのロックやマージ戦略は存在しません。並行した書き込みは互いに上書きする可能性があります。
セキュリティチェックと信頼境界
Section titled “セキュリティチェックと信頼境界”入力/パッケージの検証
Section titled “入力/パッケージの検証”アクティブなマネージャーパスではパッケージ名の検証を強制します:
- スコープ付き/スコープなしパッケージ仕様(オプションのバージョン付き)の正規表現
- 明示的なシェルメタキャラクター拒否リスト(
[;&|$(){}[]<>\]`)
これにより、bun install/uninstall を呼び出す際のコマンドインジェクションリスクが制限されます。
ファイルシステムの信頼境界
Section titled “ファイルシステムの信頼境界”- プラグインコードはカスタムツールモジュールのインポート時にインプロセスで実行されます。サンドボックス化はありません。
- マニフェストの相対パスはプラグインパッケージディレクトリに対して結合され、存在チェックのみが行われます。
- プラグインパッケージ自体はインストール後は信頼されたコードとして扱われます。
レガシーインストーラー専用のチェック
Section titled “レガシーインストーラー専用のチェック”installer.ts には PluginManager.link に反映されていない追加のリンク時チェックが含まれます:
- ローカルパスはプロジェクトの cwd 内に解決される必要があります
- シンボリックリンクターゲットの命名に関する追加のパッケージ名/パストラバーサルガード
CLI は PluginManager を使用しているため、これらの厳格なリンクガードは現在メインパスには存在しません。
失敗、部分的成功、ロールバックの動作
Section titled “失敗、部分的成功、ロールバックの動作”プラグインマネージャーはトランザクション処理を行いません。
| 操作ステージ | 失敗時の動作 | ロールバック |
|---|---|---|
bun install が失敗 | stderr でインストール中断 | 該当なし(状態の書き込みはまだ行われていない) |
| インストール成功後、マニフェスト/フィーチャー検証が失敗 | コマンド失敗 | アンインストールのロールバックなし。依存関係が node_modules/package.json に残る場合あり |
| インストール成功後、ロックファイルの書き込みが失敗 | コマンド失敗 | インストール済みパッケージのロールバックなし |
bun uninstall 成功後、ロックファイルの書き込みが失敗 | コマンド失敗 | パッケージは削除済み、古いランタイム状態が残る場合あり |
link が古いターゲットを削除後、シンボリックリンクの作成に失敗 | コマンド失敗 | 以前のリンク/ディレクトリの復元なし |
運用上、doctor --fix は一部のドリフトを修復できます(bun install、孤立した設定のクリーンアップ、無効なフィーチャーのクリーンアップ)が、ベストエフォートです。
不正/欠損マニフェストの動作まとめ
Section titled “不正/欠損マニフェストの動作まとめ”xcsh/piフィールドが欠損している場合:- インストール/リスト: 許容(最小限のマニフェスト)
- ランタイムの有効プラグインディスカバリー: 非プラグインとしてスキップ
- インストール仕様または
features --set/--enableで参照されているフィーチャーが存在しない場合: 利用可能なフィーチャーリストとともにハードエラー plugin-overrides.jsonが不正な場合: マネージャーとローダーの両パスで{}にフォールバックして無視(警告あり)- マニフェストで参照されているツール/フック/コマンドのファイルパスが存在しない場合: リゾルバーの展開時にサイレントに無視。
doctorによってのみエラーとして報告
モードの違いと優先順位
Section titled “モードの違いと優先順位”--dry-run(インストール): 合成されたインストール結果を返し、ファイルシステム/ネットワーク/状態への書き込みは行いません。--json: 出力フォーマットのみ、動作の変更なし。- プロジェクトオーバーライドは常にグローバルロックファイルよりフィーチャー/設定の表示で優先されます。
- 実効的な有効状態は
runtimeEnabled && !projectDisabledです。
実装ファイル
Section titled “実装ファイル”src/commands/plugin.ts— CLI コマンド宣言とフラグマッピングsrc/cli/plugin-cli.ts— アクションディスパッチ、ユーザー向けコマンドハンドラーsrc/extensibility/plugins/manager.ts— アクティブなインストール/削除/リスト/リンク/状態/doctor 実装src/extensibility/plugins/installer.ts— レガシーインストーラーヘルパーと追加のリンクセキュリティチェックsrc/extensibility/plugins/loader.ts— 有効プラグインのディスカバリーとツール/フック/コマンドのパス解決src/extensibility/plugins/parser.ts— インストール仕様とパッケージ名の解析ヘルパーsrc/extensibility/plugins/types.ts— マニフェスト/ランタイム/オーバーライドの型定義src/extensibility/custom-tools/loader.ts— プラグイン提供のツールモジュールのランタイム配線