# 機能設計書 3-CLIフレームワーク

## 概要

本ドキュメントは、Doom EmacsのCLI（コマンドラインインターフェース）フレームワーク（doom-cli.el）の機能設計を記述する。このフレームワークは、`bin/doom`スクリプトを通じて実行されるすべてのコマンドライン操作（sync、upgrade、doctor、clean等）の基盤を提供する。

### 本機能の処理概要

**業務上の目的・背景**：Doom Emacsは、従来のEmacsの設定管理の煩雑さを解消するために、専用のCLIツールを提供する。このCLIフレームワークにより、パッケージのインストール・更新・同期、設定の診断、環境のセットアップなどを一貫したインターフェースで実行できる。

**機能の利用シーン**：
- `doom sync`: パッケージの同期とプロファイルの再生成
- `doom upgrade`: Doom Emacsとパッケージの更新
- `doom doctor`: 環境診断
- `doom install`: 初期インストールウィザード
- `doom clean`: キャッシュ・一時ファイルのクリーンアップ

**主要な処理内容**：
1. CLIコマンドの定義と登録（defcli!マクロ）
2. 引数・オプションの解析（doom-cli-context-parse）
3. コマンドの実行（doom-cli-context-execute）
4. 出力管理（stdout/stderr/pager）
5. エラーハンドリングとデバッグ出力
6. コマンドの自動読み込み（defcli-autoload!）
7. ヘルプ・バージョン表示

**関連システム・外部連携**：doom.el（コアシステム）、doom-lib.el（標準ライブラリ）、doom-straight.el（パッケージ管理）、シェルスクリプト（bin/doom）と連携する。

**権限による制御**：特にロールベースの権限制御はないが、一部のコマンドはroot権限を必要としない設計。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIはターミナル操作のため画面なし |

## 機能種別

コマンドラインインターフェース / 設定管理 / パッケージ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| command | string/list | Yes | 実行するCLIコマンド | doom-cli-get で検証 |
| --help / -? | flag | No | ヘルプ表示 | - |
| --version | flag | No | バージョン表示 | - |
| &rest args | list | No | コマンド固有の引数 | 各CLIで検証 |

### オプション引数タイプ

| タイプ | 説明 | 検証ルール | 例 |
|--------|------|-----------|-----|
| dir | ディレクトリパス | file-directory-p | /path/to/dir |
| file | ファイルパス | file-exists-p | /path/to/file |
| path | パス（存在不問） | - | /new/path |
| int | 整数 | ^[0-9]+$ | 123 |
| num | 数値（小数含む） | ^[0-9]+(\.[0-9]+)?$ | 3.14 |
| bool | 真偽値 | yes/no/true/false | yes |
| date | 日付 | parse-time-string | 2024-12-18 |
| size | ファイルサイズ | ^[0-9]+[kmgt]?b$ | 10mb |

### 入力データソース

- コマンドライン引数
- 環境変数（DOOMPATH、DOOMPAGER、__DOOMSH、__DOOMPID等）
- 標準入力（パイプ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| exit-code | integer | 終了コード（0=成功、1-255=エラー） |
| stdout | string | 標準出力 |
| stderr | string | 標準エラー出力 |
| log-file | file | ログファイル（エラー時） |

### 出力先

- 標準出力（ターミナル）
- 標準エラー出力
- ログファイル（`$DOOMSTATE/logs/cli.*.log`）
- ページャー（長い出力の場合）

## 処理フロー

### 処理シーケンス

```
1. CLIスクリプト起動（bin/doom）
   └─ Emacsのバッチモード起動
2. doom-cli.el読み込み
   └─ CLI定義の初期化
3. コンテキスト生成（doom-cli-context-parse）
   └─ コマンドライン引数の解析
   └─ オプションの検証
4. コマンド解決（doom-cli-get）
   └─ 遅延読み込み（必要に応じて）
   └─ エイリアス解決
5. コマンド実行（doom-cli-context-execute）
   └─ :before フック実行
   └─ メイン処理実行
   └─ :after フック実行
6. 終了処理
   └─ ログ出力
   └─ 終了コード返却
```

### フローチャート

```mermaid
flowchart TD
    A[bin/doom 実行] --> B[Emacsバッチモード起動]
    B --> C[doom-cli.el読み込み]
    C --> D[doom-cli-context-parse]
    D --> E{メタコマンド?}
    E -->|--help| F[ヘルプ表示]
    E -->|--version| G[バージョン表示]
    E -->|No| H[doom-cli-get]
    H --> I{コマンド存在?}
    I -->|No| J[エラー: コマンド未発見]
    I -->|Yes| K{自動読み込み?}
    K -->|Yes| L[doom-cli-load]
    K -->|No| M[doom-cli-context-execute]
    L --> M
    M --> N[:before フック]
    N --> O[CLI関数実行]
    O --> P[:after フック]
    P --> Q{エラー?}
    Q -->|Yes| R[doom-cli-debugger]
    Q -->|No| S[正常終了]
    R --> T[ログファイル出力]
    T --> U[exit 1-255]
    S --> V[exit 0]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | コマンド階層 | コマンドは階層構造（doom sync packages等） | 常時 |
| BR-02 | 部分コマンド | :before/:afterで親コマンドの前後処理を定義 | defcli!の:type指定時 |
| BR-03 | エイリアス解決 | エイリアスは元コマンドに解決される | doom-cli-get呼び出し時 |
| BR-04 | ページャー起動 | 出力がターミナル高さを超えるとページャー起動 | exit! :pager時 |
| BR-05 | ログ保持 | 直近12個のログを保持 | doom-cli-log-retain |

### 計算ロジック

ページャー起動条件:
```elisp
;; 出力行数 > ターミナル高さ * doom-cli-pager-ratio の場合にページャー起動
(when (> output-lines (* (doom-cli-context-height context) doom-cli-pager-ratio))
  (doom-cli--exit-pager ...))
```

## データ構造

### doom-cli構造体

```elisp
(cl-defstruct doom-cli
  (command nil :read-only t)  ; コマンドリスト (例: '("doom" "sync"))
  type        ; :before / :after / nil
  docs        ; ドキュメント文字列
  autoload    ; 自動読み込みファイル
  alias       ; エイリアス先
  options     ; doom-cli-option のリスト
  arguments   ; 引数仕様
  plist       ; 追加プロパティ
  fn)         ; 実行関数
```

### doom-cli-context構造体

```elisp
(cl-defstruct doom-cli-context
  init-time           ; コンテキスト作成時刻
  pid                 ; プロセスID
  step                ; 再起動回数
  geometry            ; ターミナルサイズ (width . height)
  pipes               ; パイプ状態
  suppress-prompts-p  ; プロンプト抑制
  prefix              ; スクリプトベース名
  meta-p              ; メタリクエスト（--help等）
  error               ; エラー情報
  command             ; 現在のコマンド
  path                ; 解決済みコマンドパス
  whole               ; 未処理引数
  options             ; オプションalist
  arguments           ; 引数alist
  stdin               ; 標準入力バッファ
  stdout              ; 標準出力バッファ
  stderr              ; 標準エラーバッファ
  state)              ; 状態alist
```

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| doom-cli-definition-error | 定義エラー | defcli!の不正な定義 | 定義を修正 |
| doom-cli-autoload-error | 自動読み込みエラー | 遅延コマンドの読み込み失敗 | ファイルパス確認 |
| doom-cli-invalid-prefix-error | プレフィックスエラー | 無効なプレフィックス | プレフィックス確認 |
| doom-cli-command-not-found-error | コマンド未発見 | 存在しないコマンド | コマンド名確認 |
| doom-cli-wrong-number-of-arguments-error | 引数数エラー | 引数の過不足 | 引数を修正 |
| doom-cli-unrecognized-option-error | 不正オプション | 未定義オプション | オプション名確認 |
| doom-cli-invalid-option-error | オプション値エラー | オプション値の検証失敗 | 値を修正 |

### リトライ仕様

CLI実行自体のリトライ機構はない。ただし、`exit! :restart`で再起動可能。

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

該当なし（各コマンドは独立して実行）

## パフォーマンス要件

- 遅延読み込み（defcli-autoload!）による起動時間短縮
- ベンチマーク閾値（doom-cli-log-benchmark-threshold: 5秒）
- ログ保持数制限（doom-cli-log-retain: 12個）

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

- シェルインジェクション対策（直接プロセス呼び出し）
- ログファイル権限（#o600）
- 環境変数のサニタイズ

## 備考

- CLIフレームワークは非インタラクティブセッションでの使用を想定
- インタラクティブセッションでもAPIアクセス可能
- Windows環境ではbin/doom.ps1を使用

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | doom-cli.el | `lisp/doom-cli.el` | doom-cli構造体（251-261行目） |
| 1-2 | doom-cli.el | `lisp/doom-cli.el` | doom-cli-context構造体（647-686行目） |
| 1-3 | doom-cli.el | `lisp/doom-cli.el` | doom-cli-option構造体（547-555行目） |

**読解のコツ**: 3つの主要構造体の関係を把握する。
- `doom-cli`: コマンド定義を保持
- `doom-cli-context`: 実行時の状態を保持
- `doom-cli-option`: オプション仕様を保持

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | doom-cli.el | `lisp/doom-cli.el` | doom-cli-context-parse（760-869行目） |
| 2-2 | doom-cli.el | `lisp/doom-cli.el` | doom-cli-context-execute（688-725行目） |

**主要処理フロー**:
- **760-774行目**: 引数セパレータ "--" の検出
- **776-780行目**: 短縮オプション "-abc" の展開
- **782-827行目**: オプション解析
- **829-860行目**: コマンド解決
- **862-863行目**: 位置引数の収集

#### Step 3: コマンド定義を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | doom-cli.el | `lisp/doom-cli.el` | defcli!マクロ（ファイル後半） |
| 3-2 | doom-cli.el | `lisp/doom-cli.el` | doom-cli-argument-types（31-44行目） |

**主要な引数タイプ**:
- `&args`: 残りの引数すべて
- `&cli`: 現在のCLI構造体
- `&context`: 現在のコンテキスト
- `&flags`: フラグオプション
- `&multiple`: 複数回指定可能
- `&optional`: オプション引数
- `&rest`: 残りの引数（コマンド固有）
- `&required`: 必須引数

#### Step 4: オプション検証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | doom-cli.el | `lisp/doom-cli.el` | doom-cli-option-arg-types（66-139行目） |
| 4-2 | doom-cli.el | `lisp/doom-cli.el` | doom-cli-option-validate（557-593行目） |

**オプション検証の流れ**:
1. 型定義から`:test`関数を取得
2. 入力値を検証
3. `:read`関数で値を変換
4. 失敗時は`:error`メッセージを使用

#### Step 5: エラーハンドリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | doom-cli.el | `lisp/doom-cli.el` | エラー定義（239-245行目） |
| 5-2 | doom-cli.el | `lisp/doom-cli.el` | doom-cli-debugger（957-999行目以降） |

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

```
bin/doom (シェルスクリプト)
    │
    └─ emacs --batch -l doom-cli.el
           │
           └─ doom-cli-context-parse (引数解析)
                  │
                  ├─ doom-cli-get (コマンド取得)
                  │       │
                  │       └─ doom-cli-load (遅延読み込み)
                  │
                  └─ doom-cli-context-execute (実行)
                         │
                         ├─ doom-cli-find (関連CLI検索)
                         │       └─ :before / :after 取得
                         │
                         ├─ doom-cli--bindings (バインディング生成)
                         │
                         └─ doom-cli-execute (実行)
                                 │
                                 └─ CLI関数呼び出し
                                         │
                                         └─ exit! (終了処理)
```

### データフロー図

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

コマンドライン引数 ────▶ doom-cli-context-parse ─────▶ doom-cli-context
                          │
                          ├─ オプション解析 ───────────▶ context.options
                          │
                          ├─ コマンド解決 ─────────────▶ context.command
                          │
                          └─ 引数収集 ─────────────────▶ context.arguments

doom-cli-context ──────▶ doom-cli-context-execute ───▶ 実行結果
                          │
                          ├─ :before CLI実行
                          │
                          ├─ メインCLI実行 ────────────▶ stdout/stderr
                          │
                          └─ :after CLI実行

実行結果 ──────────────▶ exit処理 ───────────────────▶ 終了コード
                          │
                          ├─ ログファイル出力 ─────────▶ logs/cli.*.log
                          │
                          └─ ページャー ───────────────▶ less/DOOMPAGER
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| doom-cli.el | `lisp/doom-cli.el` | ソース | CLIフレームワーク本体 |
| doom | `bin/doom` | シェルスクリプト | CLI起動スクリプト（Unix） |
| doom.ps1 | `bin/doom.ps1` | PowerShell | CLI起動スクリプト（Windows） |
| doom.el | `lisp/doom.el` | ソース | コアシステム（CLI初期化含む） |
| doom-lib.el | `lisp/doom-lib.el` | ソース | 標準ライブラリ |
| cli/ | `lisp/cli/` | ディレクトリ | 各種CLIコマンド定義 |
