# many-ai-cli 設計書 v0.3.0 > 最終更新: 2026-07-02 — v0.4.0 で Workbench / Hub 内蔵チャットプロキシを撤去した旨の注記を追加 > 本書は v0.2.0 設計書(`docs/v0.2.x-any-ai-cli-design.md`)をベースに、v0.3.0 で追加・変更された機能を現行ソース・README・CHANGELOG・`docs/local/` の v0.3.0 plan 群と突き合わせて反映した詳細メモ。公開仕様は README、実装仕様はソースコードを正本とする。 > **v0.4.0(Unreleased)での撤去に関する注記**: 本書中で「Workbench タブ」「SQLite セッション履歴(Workbench 用途)」「`internal/proxy/`(Hub 内蔵チャットプロキシ)」「`/api/workbench/*`」「`chat_proxy`」に言及している章(特に §11 全体・§0 サマリ・§4 タブ表の `workbench` 行・§14 の Workbench API 節・§17 のパス表の Workbench 項・§20 のパッケージ一覧の `workbench_handlers.go`)は、v0.4.0 でコード側から撤去済みである。設計書としての履歴性のため章自体は残置するが、現行実装では該当機能は存在しない。副次効果として、`ANTHROPIC_BASE_URL` / `OPENAI_BASE_URL` の差し替えが Anthropic/OpenAI 経路で行われなくなり、Sonnet 5 以降のデフォルト 1M コンテキストが Hub 経由でも回復する。Ollama / LM Studio ルーティングの BASE_URL 差し替えは温存。 ## 0. v0.2.0 からの主な差分(サマリ) v0.3.0 は v0.2.0 系(v0.2.0 / v0.2.2)からの minor release。テーマは「単発の承認ビュー+作業面」から「**セッション履歴を残し、複数 Hub を遠隔運用できる常駐基盤**」への拡張。常駐の手段はランチャーの tunnel 再接続が `serve` 常駐にも Docker 常駐にも等しく繋がる形で複数あり、手軽さ・隔離・運用負荷で選ぶ。 主な追加: - **Workbench タブ + SQLite セッション履歴**: PTY セッション・タイムライン・チャット・承認・添付を `~/.many-ai-cli/logs/many-ai-cli.db`(modernc.org/sqlite, pure-Go)へ蓄積し、横断検索・要約・エクスポート・タスクボード・プロンプトテンプレート・診断などを 1 タブに集約。 - **承認(Approval)専用タブ**: 全セッションの承認待ちをカード一覧で一括処理(モバイル動線強化)。 - **PWA + opt-in Web Push**: manifest / service worker / アイコンを同梱し、承認待ち・タスク完了を Web Push 通知(VAPID 鍵はサーバ自動生成。通知に Hub token を含めない)。 - **統合ランチャー(クロスプラットフォーム)**: 旧 `many-ai-cli-wsl.exe` を廃止し、`many-ai-cli-launcher` が WSL / SSH serve / SSH tunnel を 1 プロファイルファイルで一元管理。常駐リモート Hub への再接続に対応。SSH プロファイルは Windows / Linux / macOS で動作し(`wsl` プロファイルは Windows 専用)、ランチャーバイナリは本体と同じく全 OS 向けに配布する。 - **リモートサーバー / Docker デプロイ資産**: GHCR への自動 publish、loopback-only な per-user compose サンプル、リソース制限、health check、冪等な `aac-update.sh`。 - **Files / Git 操作の拡張**: フォルダ新規作成・空ファイル作成・base-mtime 競合検出付き保存・空フォルダ削除(Files)、`git fetch` / `git pull --ff-only`(Git)。 - **音声(Whisper)/ outbound 通知(ntfy・webhook)/ トークンコストバー** の設定追加。 CHANGELOG 上のリリース日は v0.3.0 = 2026-06-13。 ## 1. v0.3.0 の位置づけ v0.2.0 が「Hub UI を承認ビューから複数セッション運用の作業面へ拡張」したのに対し、v0.3.0 は次の 2 軸で常駐運用を強化する。 1. **永続化軸**: セッションを SQLite に残し、再起動・横断検索・エクスポート・要約・利用量集計を可能にする(Workbench)。 2. **遠隔運用軸**: WSL / リモートサーバー(SSH serve / tunnel)/ Docker(GHCR + compose)を統合ランチャーと PWA/Web Push で結び、ローカル端末から常駐 Hub を安全に操作する。 セキュリティモデル(`127.0.0.1` bind 固定・token 必須・外部公開禁止)は変更しない。遠隔運用はすべて SSH local forward または loopback publish の上に成り立つ。 ## 2. 公開スコープ 公開 README 上の wrap 対象は Claude Code / Codex CLI / GitHub Copilot CLI / Cursor Agent CLI / Grok Build CLI。Gemini CLI は対象外。OpenCode は UI / config / usage-links に痕跡が残るが、`/api/spawn` のバリデーションは `claude` / `codex` / `copilot` / `cursor-agent` / `grok` の 5 provider のみを許可し、OpenCode は v0.3.0 でも非公開・spawn 非対応。 Ollama は provider ではなく route / model backend。Claude Code / Codex CLI の子プロセスに環境変数を注入し、既存 provider CLI を Ollama 経由で使う。 検証状況: - 実機検証済み: Windows、WSL(統合ランチャー `wsl` プロファイル経由)、リモートサーバー(Docker / SSH tunnel 経由の常駐 Hub) - native Linux / macOS: build 対象だが十分な実機検証は未完了 - ビルド要件: Go 1.25.11 以上(`go 1.25` 指定ではなく patch まで確定。Docker / GoReleaser とも) v0.3.0 スコープ確定(統合 plan): - voice / Whisper 全工程(managed ワンクリックインストール含む)を v0.3.0 に含める。当初は Whisper 実機検証通過を着手ゲートとしていたが撤廃し、品質未検証のままインストール導線を出すリスクはユーザー了承済み。 - mobile-ux ロードマップの後続フェーズ、未着手のリモートサーバー案件は v0.3.0 スコープ外。 ### 2-2. リリース品質ゲート release tag 前に以下をすべて PASS させる。 - `go test ./...` / `go vet ./...` / `go mod verify` / `go test -race ./...`(CGO + C compiler 必須)/ `govulncheck ./...` - `npm test`(web)/ `scripts/local/check-third-party.ps1` / `goreleaser check` - Hub UI manual smoke(必須導線): spawn(**Ollama Cloud route を含む**・cloud モデルで確認)/ reconnect / Files preview / path open / Git tab / approval action bar / settings save `go test -race` と `goreleaser check` は CGO / 外部要件のため Windows ローカルでは未完了になりやすく、CI 等の外部環境で PASS を記録してから tag する。 ## 2-1. Runtime / Environment Identity Hub UI は `GET /api/info` を single source として実行環境を表示する。`runtime_mode` / `runtime_label` / `ssh` / `host_ip` は後方互換のため残し、ブラウザタブ・favicon・ヘッダー badge の瞬間識別には `env_*` を使う(v0.2.0 から変更なし)。 `env_kind` の標準値: | env_kind | 表示 | 用途 | favicon | |---|---|---|---| | `local` | Local | ローカル OS 上で直接起動した Hub | 緑 + `L` | | `wsl` | WSL | WSL 内、または Windows launcher 経由の WSL Hub | 青 + `W` | | `remote` | リモートサーバー | SSH 経由でその場で起動するリモートサーバー上の Hub(常駐させることも可) | オレンジ + `R` | | `remote-tunnel` | リモートサーバー(トンネル) | 既に常駐しているリモート Hub(`serve` 常駐 / Docker いずれも)へ SSH local forward で再接続 | 赤 + `T` | 判定優先順は `MANY_AI_CLI_ENV_KIND` → `config.yaml:hub.env_kind` → `/api/net-hint` → runtime / SSH 環境変数 → `local`。不正な `env_kind` は `local` へフォールバックする。`MANY_AI_CLI_HOST_LABEL` と `/api/net-hint.host_label` は `env_host_label` として `/api/info` に返し、リモートサーバー / tunnel の識別補助に使う。 `GET /api/info` の環境識別フィールドは v0.2.0 と同一(`env_kind` / `env_label` / `env_short` / `env_color` / `env_title` / `env_host_label`)。 ## 3. Hub UI のタブ構成 v0.3.0 のメイン領域は単一の unified tab bar(`#unified-tab-bar`)に統合された 8 タブ。 | 順序 | data-tab | タブ | 役割 | 実装 | |---|---|---|---|---| | 1 | `terminal` | Terminal | xterm.js のライブ PTY 表示(デフォルト) | `web/src/app/terminal.ts` | | 2 | `multi` | Multi | 複数セッションのグリッド表示(レイアウトバッジ付き) | `web/src/app/multi-pane.ts` | | 3 | `chat` | Chat | PTY stream から抽出した会話履歴(未読バッジ付き) | `web/src/app/chat-history.ts` | | 4 | `split` | Split | Terminal と Chat の横並び | `web/src/app.ts` | | 5 | `files` | Files | project file browser / preview(遅延ロード) | `web/src/app/files-view.ts` | | 6 | `git` | Git | commit log / diff / commit all / fetch / pull(遅延ロード) | `web/src/app/git-view.ts` | | 7 | `workbench` | Workbench | セッション履歴・タスク・テンプレート等の作業ハブ(**v0.3.0 追加**) | `web/src/app/workbench.ts` | | 8 | `approval` | 承認 | 全セッション承認待ちカード一覧(未処理件数バッジ付き、**v0.3.0 追加**) | `web/src/app/approval-queue-tab.ts` | Files / Git は `lazy` クラスで初回クリック時に backend API を呼ぶ。Workbench は `workbench-opened` カスタムイベントで初期化する。`#main-tab-bar` は Files タブ内の複数タブ管理(FilesTabManager)用の隠し DOM として残置。 ### 3-1. モバイル対応設計方針 - ブレークポイントは 720px(スマホ / 2 カラム境界)と 1000px(Split・Multi タブ非表示境界)の 2 段。機種固有 px は使わない。レイアウト切替は `max-width: 720px`、タッチ最適化(ターゲット拡大・リサイザー無効化)は `pointer: coarse` の 2 軸メディアクエリで独立制御する。 - モバイル幅ではセッション一覧を左ドロワー(オーバーレイ)に切り替え、未選択時は全幅・選択時は自動クローズ。 - 承認 action-bar はモバイル幅 + coarse で画面下部固定・縦積み 48px ボタンになり、`env(safe-area-inset-bottom)` でホームインジケータ余白を確保する。承認の横断処理は「承認」タブ(§8)に集約する。 - coarse 時のみキーボード補助バー(Esc / Ctrl / Tab / 矢印 / ^O / ^C)を ⌨ ボタンでトグル表示し、タップで対応エスケープシーケンスを PTY へ送る。 - Split・Multi タブは幅 1000px 未満で非表示(縮小時は単一ペインへフォールバック)。`100vh` は全 CSS で `100dvh` に統一し、Android Chrome 対策に viewport meta へ `interactive-widget=resizes-content` を付与する。 ### 3-2. 入力欄・操作アフォーダンス アクティブセッションが `running` のとき、入力欄プレースホルダを「Esc で停止(実行中)」に差し替え、`#send-btn` を ▶ から ■(停止)へ切替えてクリックで `\x1b`(Esc)を送る。アイドル時は通常文言・通常送信に戻す。両者は単一の `isActiveSessionRunning()` + `updateInputAffordance()` から一致制御し、言語切替時も running 状態を再評価する。 ## 4. Chat / Split v0.2.0 から仕様は基本変更なし。Chat はセッションごとに `Message[]` を in-memory で保持し、データ源は PTY stream と UI 操作イベント(user input / AI output / approval / attach)。ターン境界は Hub から挿入される OSC marker `\x1b]47777;user-turn\x07`。 v0.3.0 では、ブラウザ再接続時の履歴 seed に加え、`GET /api/session-chat`(後述)で SQLite に保存された過去チャットを復元できる。Chat UI(吹き出し / provider icon・user avatar / copy / tool call 折りたたみ / raw PTY 確認 / filter / search / minimap)は v0.2.0 同等。 Split は terminal と chat を同時表示するモードで、bottom state を見て自動追従する。 チャット履歴の一次ソースは WS の `pty_data` ストリーム(メモリ上のライブ配信)で、ログ設定(`log.session_enabled`)とは独立に動作する。ログ OFF でも開いているセッションの会話はチャットタブにライブ表示され、件数バッジはライブ件数に追従する(履歴ゼロは空状態プレースホルダ)。ディスク記録(ログ ON)は永続化・再接続復元・過去セッション閲覧のためのオプトイン上位機能で、`/api/session-chat`(SQLite 復元)は過去にログ ON で蓄積した履歴がある場合のみ機能する(無ければ空のライブビューから開始)。当初検討した「ログ OFF 時にチャットタブを非表示」案はライブ表示が成立するため撤回した。 ## 5. Multi タブ 複数セッションを同時に見るためのグリッドビュー(v0.2.0 同等)。最大 18 ペイン想定、grid picker でレイアウト選択、active slot を sidebar / input / approval action-bar と同期、ResizeObserver で各ペインを fit / resize。入力欄・添付・approval action-bar は共有 UI のまま、フォーカス中ペインの session id に送信する。 ## 6. Files タブ Files は project file browser / preview。project group の Files 導線、セッションカード右クリック、`Ctrl+Shift+F` から開く。 Backend API: | API | Method | 内容 | |---|---|---| | `/api/files-roots` | GET | session / project に基づく root candidates(git root + docs 候補) | | `/api/files-list` | GET | root 以下の file tree flat list(最大 2000 件 / 深さ 8) | | `/api/files-content` | GET | preview 用テキスト取得(1 MiB 上限) | | `/api/files-asset` | GET | image / media preview asset | | `/api/files-download` | GET | file download attachment | | `/api/files-move` | POST | single / multi file move(preflight + rollback) | | `/api/files-rename` | POST | 同一ディレクトリ内 basename rename | | `/api/files-mkdir` | POST | **v0.3.0 追加。** 親 dir 直下に 1 段だけフォルダ作成(`MkdirAll` 不使用、既存は 409) | | `/api/files-create` | POST | **v0.3.0 追加。** テキスト系 basename の空ファイル作成(`O_EXCL`、上書き・中間 dir 作成なし) | | `/api/files-save` | POST | テキストファイルの上書き保存(base-mtime 競合検出付き、新規作成不可) | | `/api/files-delete-dir` | POST | ディレクトリ削除(空ディレクトリ削除を想定。allowed root 自身・ファイル・symlink は拒否) | ### POST /api/files-save リクエスト body(JSON): | フィールド | 型 | 必須 | 説明 | |---|---|---|---| | `path` | string | 必須 | 書き込み対象ファイルの絶対パス | | `content` | string | 必須 | 保存するテキスト内容 | | `baseMtime` | string (RFC3339) | 省略可 | 競合検出用の基準 mtime。非ゼロ指定時はサーバ側 mtime と照合 | レスポンス body(JSON): `ok` / `path` / `size` / `mtime`(次回 `baseMtime`)/ `error` / `detail`。 エラーコード一覧: | HTTP | `error` | 意味 | |---|---|---| | 400 | `bad_request` | path 未指定 / 相対パス / ディレクトリ指定 / JSON 不正 | | 401 | — | token 不一致 | | 403 | `forbidden` | allowed roots 外 / 非テキスト拡張子 | | 404 | `not_found` | ファイルが存在しない(新規作成不可) | | 409 | `conflict` | `baseMtime` と現在 mtime が不一致。レスポンスに現在 `mtime` を含める | | 413 | `too_large` | content が 1 MiB 超 / request body が 2 MiB 超 | | 500 | `internal_error` / `write_failed` | stat / write 失敗 | 制約: - すべてのエンドポイントは `s.guard()`(token 検証 + メソッドチェック + POST 系の Host/Origin チェック)を通る - traversal 防止のため allowed roots は cwd / git root に限定(`isPathUnderAllowedRoots` は `EvalSymlinks` 込みで判定) - walk は depth / item count 上限あり、symlink は追跡しない - preview text は許可拡張子とサイズ上限を持つ - multi move は preflight 後 all-or-nothing。実行中失敗時は成功済み rename を逆順 rollback - `/api/files-download` は preview の 1 MiB / テキスト拡張子制限を適用せず、`Content-Disposition: attachment` + 推定 `Content-Type` + `X-Content-Type-Options: nosniff` でストリーミング配信する(メモリ全読みしない、ディレクトリは 400)。ダウンロード導線は Files ツリーとターミナルのパスリンクメニュー(共通の `showPathPopup`)の両方に出る。ディレクトリ一括 ZIP は v0.3.0 スコープ外 Frontend: tree search / Markdown・code preview / image・media preview / context menu(copy path, open, reveal, move, rename)/ multi select + D&D / フォルダ・ファイル新規作成 / restored orphan tab の修復。 ## 7. Git タブ Git タブは repository inspection + 軽量操作 UI。branch badge click、session card context menu、`Ctrl+Shift+G` で開く。 Backend API: | API | Method | 内容 | |---|---|---| | `/api/git-log` | GET | commit list。`ref`(`HEAD`/``/`--all`), `limit`(最大 1000), `skip` 対応 | | `/api/git-show` | GET | commit detail / file diff(diff は 256 KiB で truncation) | | `/api/git-refs` | GET | local / remote / tag refs と HEAD(github.com は HTTPS URL も) | | `/api/git-status` | GET | working tree status / branch / ahead-behind / upstream | | `/api/git-commit-message` | POST | diff/stat ベースのヒューリスティック commit message 生成(LLM 不使用) | | `/api/git-commit-all` | POST | `git add -A` + `git commit`(subject 200 / body 8192 文字上限) | | `/api/git-fetch` | POST | **v0.3.0 追加。** `git fetch`(timeout 30 秒) | | `/api/git-pull` | POST | **v0.3.0 追加。** `git pull --ff-only`(timeout 30 秒) | `/api/git-pull` のエラー: 分岐あり → 409 `not_fast_forward`、未コミット変更あり → 409 `local_changes`、upstream 未設定 → 400 `no_upstream`。 `Commit all` は local commit のみで push は実行しない。modal は subject / body 編集、review gate、identity missing error を扱う。git command は timeout 付き(fetch / pull は 30 秒、その他は共通 5 秒)で実行し、セッション cwd を `resolveGitRoot(sid)` で検証する。 ## 8. Approval ### Multi-question approval `[MANY-AI-CLI]` 承認ブロック内に複数の番号付き質問がある場合、Hub は stacked choices として表示し、選択結果をまとめて PTY へ送る(v0.2.0 同等)。question ごとの選択状態保持 / progress・clear・keyboard navigation / Submit all で space-separated digit string 送信 / global numbering の補正 / false positive 時の banner dismiss。 ### 承認キュータブ(v0.3.0 追加) unified tab bar の「承認」タブ(`approval-queue-tab.ts`)は、全セッションの承認待ちをカード一覧で表示し、1 画面でまとめて処理できる。スマートフォンからの一括承認動線を強化したもので、未処理件数を tab バッジに表示する(`approval-queue.ts` がバッジ制御)。 ### Go-side native approval detection v0.2.0 で導入した Go 側 VT buffer による native approval detection を v0.3.0 で各 provider 向けに確定。`internal/hub/approval_detector.go` / `vt_buffer.go` が terminal output を正規化し、`approvalSourceGoVT = "go_vt"` として検出する。VT バッファは末尾 120 行を取得し、末尾 90 行を有効候補として検査する。 | provider | 検出方式 | kind | |---|---|---| | `claude` | 番号付きカーソル選択肢(Enter/ESC ヒント付きセレクタ含む) | `native` | | `codex` | ショートカット `(y)/(p)/(n)/(esc)` 形式 | `native_codex_shortcut` | | `copilot` | ショートカット `(y)/(n)/(!)/(#)/(?)/(esc)` 形式 | `native_copilot_shortcut` | | `cursor-agent` | `(y)/(tab)/(shift+tab)/(esc or n)` 等の多様キー表記 | `native_cursor_agent_shortcut` | | `grok` | Claude Code 互換 harness の番号付きカーソル選択肢(Enter/ESC ヒント付きセレクタ含む) | `native` | 検出時は WebSocket event `approval_detected` / `approval_cleared` を UI に送り、action-bar と状態表示へ反映する。 ### Approval marker rule injection 承認ボタン機能が有効な場合、wrapped session の起動時に `[MANY-AI-CLI]` マーカー出力ルールを instruction file へ冪等注入する(v0.2.0 同等)。 | provider | 注入先 | 方式 | |---|---|---| | Claude | `~/.claude/CLAUDE.md` | `@~/.many-ai-cli/approval-rules.md` import 行 | | Codex | `$CODEX_HOME/AGENTS.md`、未設定なら `~/.codex/AGENTS.md` | `` 共有ブロック | | GitHub Copilot | `/AGENTS.md` | 共有ブロック | | Cursor Agent | `/AGENTS.md` | 共有ブロック | | Grok | `/AGENTS.md` | 共有ブロック | `` は git root を優先し、取れない場合は session cwd。共有ブロックは path 単位で冪等注入し、そのファイルを読む active wrap が 0 になった時点で many-ai-cli のブロックだけ削除する。 ### Approval pattern profiles 承認 trigger phrase は `resources/approval-patterns/{claude,codex,copilot,cursor-agent,grok,common}.md` を source とし、Hub 起動時に fetch / cache する。`.official.json`(読み取り専用)/ `.custom.json`(ユーザー編集)/ `.json`(active mirror)の 3 ファイル構成。 API: `GET /api/approval-patterns` / `GET|PUT|DELETE /api/approval-patterns/`。承認機能のオン/オフは `/api/approval/status` `/api/approval/enable` `/api/approval/disable` `/api/approval/dismiss`。remote sync で差分があれば `approval_patterns_updated` を broadcast し、UI が toast / reload する。`/approval-patterns/`(静的)は token 必須の専用ハンドラで配信する。 ## 9. Model Picker / Ollama Route Model picker は `web/src/app/spawn-panel.ts` と `/api/models` で実装される(v0.2.0 同等)。 | Group | provider | route | source | |---|---|---|---| | Anthropic | `claude` | `anthropic` | `resources/models/defaults.json` remote fetch + fallback | | OpenAI | `codex` | `openai` | 同上 | | GitHub Copilot | `copilot` | (空) | 同上 | | Cursor Agent | `cursor-agent` | (空) | 同上 | | Grok | `grok` | (空) | 同上 | | Ollama Cloud | both | `ollama` | local daemon `/api/tags` の `remote_host` 付き aliases | | Ollama Local | both | `ollama` | local daemon `/api/tags` + `config.yaml:local_models` | `/api/models` は `config.yaml:ollama.base_url`(空なら `http://localhost:11434`)の `/api/tags` 結果を 60 秒 cache し、default model list は remote fetch 失敗時に hardcoded fallback を使う。デフォルト取得元 URL は `config.yaml:models_source` で上書きできる。 Spawn 時の route 判定: ①request の `route` 明示 → ②model が known Ollama IDs → ③model に `:cloud` を含む → ④Claude なら `anthropic` → ⑤Codex なら `openai` → ⑥Copilot は `""` → ⑦Cursor Agent は `""` → ⑧Grok は `""`。 Ollama route の env preset: | provider | env | |---|---| | Claude | `ANTHROPIC_AUTH_TOKEN=ollama`, `ANTHROPIC_API_KEY=`, `ANTHROPIC_BASE_URL=` | | Codex | `OPENAI_API_KEY=ollama`, `OPENAI_BASE_URL=/v1` | Codex × Ollama では `--codex-oss` を wrapper へ渡す。Ollama route の model は `last_model` に保存しない。 spawn パネルの provider 選択はネイティブ `