# 通知設計書 4-command_finished（コマンド完了通知）

## 概要

本ドキュメントは、Ghosttyターミナルエミュレータにおけるシェルコマンド完了時のデスクトップ通知機能の設計について記述する。

### 本通知の処理概要

command_finished通知は、シェル統合機能を使用してターミナル内でコマンドが完了した際に発生する通知である。実行時間とリターンコードを含むメッセージが表示される。通知の発火条件、通知方法、最小実行時間などを設定で制御可能。

**業務上の目的・背景**：長時間実行されるビルド、コンパイル、テスト、データ処理などのコマンドが完了した際に、ユーザーに自動的に通知を送信する。ユーザーがターミナルから離れて他の作業をしている場合でも、コマンドの完了を見逃さないようにすることが目的である。シェル統合機能（OSC 133）と連携して動作する。

**通知の送信タイミング**：シェルがOSC 133シーケンスでコマンド終了を報告した時点で、設定された条件（unfocused/always）を満たす場合に通知が発生する。最小実行時間（notify-on-command-finish-after）を下回る短時間コマンドは通知対象外。

**通知の受信者**：Ghosttyターミナルを使用しているローカルユーザー。通知はデスクトップ通知システムまたはベル音として送信される。

**通知内容の概要**：コマンドの終了コード（exit code）と実行時間（duration）を含む情報。通知方法はbell（ベル音）またはnotify（デスクトップ通知）から選択可能。

**期待されるアクション**：ユーザーはコマンド完了通知を受けて、ターミナルウィンドウに戻り、コマンドの実行結果を確認する。終了コードが0以外の場合はエラー処理を行う。

## 通知種別

複合通知（ベル音 / デスクトップ通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（コマンド終了検出時に即時処理） |
| 優先度 | 中 |
| リトライ | 無し |

### 送信先決定ロジック

通知は、コマンドが実行されたターミナルサーフェスに対して送信される。`notify-on-command-finish`設定に基づき、フォーカス状態に応じて通知が抑制される場合がある。

## 通知テンプレート

### ベル通知の場合

| 項目 | 内容 |
|-----|------|
| 効果 | システムベル音 |

### デスクトップ通知の場合

| 項目 | 内容 |
|-----|------|
| タイトル | コマンド完了 |
| 本文 | 実行時間と終了コードを含む情報 |
| アイコン | Ghosttyアプリケーションアイコン |

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| exit_code | コマンドの終了コード | OSC 133シーケンスのパラメータ | No（nullの場合あり） |
| duration | コマンドの実行時間（ナノ秒） | start_command時刻からの経過時間 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| シェル統合 | OSC 133によるコマンド終了報告 | `notify-on-command-finish`がnever以外 かつ 実行時間が`notify-on-command-finish-after`以上 | シェルがコマンド終了を報告した時点で発火 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `notify-on-command-finish = never` | 通知が完全に無効化 |
| `notify-on-command-finish = unfocused` かつ サーフェスがフォーカス中 | フォーカス時は通知しない |
| 実行時間が`notify-on-command-finish-after`未満 | 短時間コマンドは通知対象外（デフォルト5秒） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[OSC 133 コマンド終了シーケンス受信] --> B[StreamHandler.semanticPrompt呼び出し]
    B --> C{action == end_command?}
    C -->|Yes| D[surfaceMessageWriter stop_command送信]
    C -->|No| E[他のセマンティックプロンプト処理]
    D --> F[Surface.handleMessage stop_command]
    F --> G[実行時間計算]
    G --> H[rt_app.performAction command_finished]
    H --> I[apprt実装による処理]
    I --> J{notify-on-command-finish設定}
    J -->|never| K[処理終了]
    J -->|unfocused| L{サーフェスがフォーカス中?}
    J -->|always| M[通知条件チェック]
    L -->|Yes| K
    L -->|No| M
    M --> N{実行時間 >= notify-on-command-finish-after?}
    N -->|No| K
    N -->|Yes| O{notify-on-command-finish-action}
    O -->|bell| P[ベル通知]
    O -->|notify| Q[デスクトップ通知]
    P --> R[終了]
    Q --> R
    K --> R
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| タイマー未初期化 | start_commandが呼ばれずにstop_commandが呼ばれた | 処理をスキップ |
| performAction失敗 | apprrtが通知をサポートしていない | 警告ログを出力し、処理を継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（24時間いつでも送信可能）

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

- コマンドの終了コードと実行時間のみが通知され、コマンド内容自体は含まれない
- シェル統合機能を使用するため、信頼できるシェルでのみ動作する

## 備考

- シェル統合（shell-integration）またはOSC 133サポートが必須
- GTKでは右クリックメニューから単一コマンドの通知を有効化可能
- デフォルトの最小実行時間は5秒
- デフォルトではbell通知のみ有効（notifyは無効）

---

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

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

### 推奨読解順序

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

コマンド完了通知に関連する設定とデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Config.zig | `src/config/Config.zig` | notify-on-command-finish設定（行1199-1267） |
| 1-2 | action.zig | `src/apprt/action.zig` | CommandFinished構造体（行938-954） |
| 1-3 | surface.zig | `src/apprt/surface.zig` | start_command/stop_commandメッセージ（行95-100） |

**読解のコツ**: NotifyOnCommandFinishはenum（never/unfocused/always）、NotifyOnCommandFinishActionはbell/notifyのフラグ構造体。

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

コマンド終了検出の処理を追跡する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | stream_handler.zig | `src/termio/stream_handler.zig` | semanticPrompt関数（行1069-1103） |

**主要処理フロー**:
1. **行1078-1087**: end_command時にexit_codeを抽出してstop_commandメッセージを送信
2. **行1074-1076**: end_input_start_outputでstart_commandメッセージを送信

#### Step 3: サーフェス処理を理解する

サーフェスがコマンド完了メッセージを受け取った後の処理を追跡する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Surface.zig | `src/Surface.zig` | handleMessage関数のstart_command/stop_command分岐（行1109-1131） |

**主要処理フロー**:
- **行1109-1111**: start_commandでcommand_timerを現在時刻に設定
- **行1113-1131**: stop_commandで実行時間を計算し、command_finishedアクションを実行

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

```
terminal.Parser (OSC 133シーケンス検出)
    │
    └─ stream_handler.semanticPrompt()
           │
           ├─ end_input_start_output
           │      └─ surfaceMessageWriter(.start_command)
           │
           └─ end_command
                  └─ surfaceMessageWriter(.stop_command)
                         │
                         └─ Surface.handleMessage()
                                │
                                ├─ .start_command → command_timer設定
                                │
                                └─ .stop_command
                                       │
                                       ├─ 実行時間計算
                                       │
                                       └─ rt_app.performAction(.command_finished)
                                              │
                                              └─ apprt固有の処理
                                                     │
                                                     ├─ 設定チェック
                                                     ├─ フォーカス状態チェック
                                                     ├─ 実行時間チェック
                                                     │
                                                     └─ bell/notify実行
```

### データフロー図

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

OSC 133;D  ───▶  semanticPrompt(.end_input_start_output)  ───▶  start_command
(コマンド開始)           │
                        ▼
                  Surface.handleMessage()
                        │
                        ▼
                  command_timer = now()

OSC 133;D;exit  ───▶  semanticPrompt(.end_command)        ───▶  stop_command
(コマンド終了)          │
                        ▼
                  Surface.handleMessage()
                        │
                        ▼
                  duration = now - command_timer
                        │
                        ▼
                  rt_app.performAction(.command_finished)
                        │
                        ▼
                  通知判定・実行                              ───▶  bell/デスクトップ通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Config.zig | `src/config/Config.zig` | ソース | 通知関連設定定義 |
| stream_handler.zig | `src/termio/stream_handler.zig` | ソース | semanticPrompt処理 |
| surface.zig | `src/apprt/surface.zig` | ソース | start_command/stop_commandメッセージ定義 |
| Surface.zig | `src/Surface.zig` | ソース | コマンド完了処理、タイマー管理 |
| action.zig | `src/apprt/action.zig` | ソース | command_finishedアクション定義 |
| application.zig | `src/apprt/gtk/class/application.zig` | ソース | GTKでの通知実装 |
