-
Notifications
You must be signed in to change notification settings - Fork 2
Description
v2.1 概要 & 変更点
v2.0 → v2.1 の主な改善点
スマホアクセスとセキュリティ(localhost / 0.0.0.0 / 認証)の前提を明文化
Stop フック+tmux capture-pane の「どこからどこまでが今回の応答か」を切り出す方式を具体化
タイムアウト・エラー時の UX を明文化
Worktree / ChatMessage のデータモデルを拡張(updatedAt, lastMessageSummary, summary など)
tmux / Claude CLI の死活監視と自動再起動方針を明文化
複数クライアント(PC+スマホ)同時利用時の WebSocket 配信方針を明確化
ログファイル命名規則・保持方針の骨子を追加
要求
1.1 ユーザー体験 (UX)
開発者は スマホブラウザ または PC ブラウザからアプリ(ローカルホスト上の Web アプリ)を開く。
[画面A: ホーム]
git worktree として管理されているブランチ(ディレクトリ)の一覧が、最終更新日時の新しい順に表示される。
特定のブランチ(例: feature/foo)をタップすると、
[画面B: チャット] に遷移し、その worktree 専用の過去の対話履歴が読み込まれる。
チャット入力欄から Claude への指示を送信する。
UI はすぐに「送信済み状態(送信中バブル表示)」になり、ブロックされない(非同期)。
バックグラウンドで Claude CLI が処理を実行し、完了すると Stop フックが発火する。
バックエンドがフックを受信し、tmux capture-pane で結果を取得し、
.claude_logs/ に Markdown ログを書き出し
db.sqlite に ChatMessage を保存し
WebSocket 経由でチャット画面に応答をプッシュする。
開発者は必要に応じて [画面C: ログ] に切り替え、
Markdown 形式で保存された 詳細な実行ログを確認できる。
同じ worktree を PC・スマホ複数端末から開いている場合、
全クライアントがリアルタイムに同じ更新を受け取る。
1.2 機能要求
カテゴリ 要求事項 優先度 備考
UI/UX (スマホ) スマホブラウザで快適に操作できるレスポンシブデザイン 必須 PCブラウザでも利用可能
画面A (ホーム): worktree 一覧をリスト表示(最終更新順) 必須 トーク一覧風 UI
画面B (チャット): 選択した worktree 専用のチャット画面に遷移 必須
画面C (ログ): チャット画面から Markdown ログ一覧・ビューアに遷移可能 必須
セッション管理 1 worktree ごとに 1 tmux セッション + 1 Claude CLI セッション 必須 1:1 マッピング
セッションは遅延起動(初回アクセス or 初回送信時に起動) 必須
Claude連携 指示送信後、UIは応答を待たずに「送信中」として表示(非同期) 必須
Claude 完了時、Stop フックがバックエンド API をコール 必須
フック API が tmux から結果を取得し、WS 経由で UI へプッシュ 必須
ログ管理 指示+応答の Markdown ログを .{root}//.claude_logs/ に保存 必須 各 worktree 配下
履歴管理 チャット履歴を db.sqlite に保存し、Worktree 一覧の要約・更新日時に反映 必須
1.3 非機能要求
項目 要求事項 備考
セキュリティ デフォルトは localhost バインド。オプションで 0.0.0.0 バインドを許可し、その場合は簡易認証(トークン or Basic 認証)を必須とする 必須
root ディレクトリ配下の worktree のみアクセス対象とする 必須
応答性 (UI) 画面A→B の遷移(チャット履歴読み込み)は 1 秒以内を目標 目標
応答性 (フック) Claude 完了から UI へのプッシュまでのオーバーヘッドを最小化(100〜300ms 程度を目安) 必須
耐障害性 tmux セッション / Claude プロセスが落ちた場合、自動再起動(UC-2) 推奨
拡張性 将来的な messageId などのメッセージ単位識別への拡張を許容する API デザイン 推奨
保守性 ルートディレクトリ変更・worktree 追加/削除に対する再スキャン機構を備える 推奨
要件詳細
2.1 UI 画面フロー (スマホ中心)
画面A: Worktree 一覧(ホーム /)
表示内容
ルートディレクトリ配下で検出された worktree をリスト表示
各項目に表示する情報:
name: ブランチ名(例: main, feature/foo)
lastMessageSummary: 最後のチャット内容の要約(最大 ~80 文字程度)
updatedAt: 最終更新日時(相対時間表示「5分前」など)
操作
タップすると 画面B /worktrees/:id に遷移
データ取得
/api/worktrees (GET) から一覧を取得
サーバ側で updatedAt 降順ソート
画面B: チャット画面(/worktrees/:id)
表示内容
選択した worktree 専用の ChatMessage 一覧を表示(最新から過去にスクロール)
ヘッダー
worktree 名(ブランチ名)
[← 戻る] ボタン(画面Aへ)
[ログ] ボタン(画面C /worktrees/:id/logs へ)
フッター
メッセージ入力欄
[送信] ボタン
チャット履歴のロード
初回表示時に最新 N 件(例: 50 件)をロード
スクロールで過去をさかのぼる場合は /api/worktrees/:id/messages?before= などで追加ロード
送信フロー
[送信] 押下 → POST /api/worktrees/:id/send
body: { message: string }
UI は即座に自分の ChatMessage をローカル表示(role="user")し、 さらに role="system" 相当の「送信中…」バブルを表示
その後の Claude 応答は WebSocket で受信して表示
エラー / タイムアウト UX
送信 API がエラーの場合:
「送信に失敗しました」とエラーバブル表示
Stop フックが一定時間(例: 120 秒)以内に来ない場合:
「応答の取得に時間がかかっています。ログ画面から状況を確認できます。」などの警告バブル
バックエンド側でもタイムアウトログを出力
画面C: ログビューア(/worktrees/:id/logs)
表示内容
.{worktreePath}/.claude_logs/ 配下の Markdown ファイル一覧
各行:ファイル名+作成日時+簡易概要(あれば)
操作
ファイル名タップ → Markdown レンダリングビューへ遷移
[チャットへ戻る] ボタン
データ取得
/api/worktrees/:id/logs で一覧取得
/api/worktrees/:id/logs/:logFileName で Markdown 本文取得
データモデル
SQLite (db.sqlite) に保存される主要スキーマと、API レスポンスに利用する型。
3.1 Worktree
interface Worktree {
id: string; // "main", "feature-foo" など (URL セーフな ID)
name: string; // "main", "feature/foo" など、表示名
path: string; // "/path/to/root/feature/foo" (絶対パス)
lastMessageSummary?: string; // 最後のメッセージ要約(画面A用)
updatedAt?: Date; // 最終メッセージの timestamp
}
3.2 ChatMessage
type ChatRole = "user" | "claude";
interface ChatMessage {
id: string; // UUID
worktreeId: string; // Worktree.id
role: ChatRole;
content: string; // 表示用の全文テキスト
summary?: string; // 任意の短い要約(画面A用に流用可能)
timestamp: Date;
logFileName?: string; // 対応する Markdown ログファイル名(相対パス)
requestId?: string; // 将来の拡張用: 1送信=1 UUID
}
3.3 セッション状態 (tmux capture 用)
interface WorktreeSessionState {
worktreeId: string;
lastCapturedLine: number; // tmux capture-pane の scrollback 上の前回行数 or オフセット
// 将来的に必要であれば、最後に処理した requestId なども持てる
}
アーキテクチャ
4.1 システム構成(イベント駆動)
Claude CLI の Stop フックをトリガーとする イベント駆動アーキテクチャ を採用する。
graph TD
subgraph "スマホ/PCブラウザ (UI)"
UI_A[画面A: Worktree一覧]
UI_B[画面B: チャット画面]
UI_C[画面C: ログビューア]
UI_WS[WebSocketクライアント]
end
subgraph "Next.js / Node.js バックエンド"
API_Send[/POST /api/worktrees/:id/send/]
API_Hook[/POST /api/hooks/claude-done/]
API_WT[/GET /api/worktrees, .../]
WS_Server[WebSocketサーバー<br/>(worktreeごとのroom)]
DB[(SQLite db.sqlite)]
end
subgraph "tmux / Claude CLI"
T[(tmux server)]
S1[tmux session: cw_main]
S2[tmux session: cw_feature-foo]
CL1[Claude CLI]
end
subgraph "ローカルFS"
Logs[.claude_logs/]
end
UI_B -- 1. 指示送信 --> API_Send
API_Send -- 2. tmux send-keys --> S1
S1 --> CL1
CL1 -- 3. 処理完了 --> Hook(Stopフック発火)
Hook -- 4. curl POST --> API_Hook
API_Hook -- 5. capture-pane (結果取得) --> T
API_Hook -- 6. ログ保存 --> Logs
API_Hook -- 7. メッセージ保存 --> DB
API_Hook -- 8. 応答プッシュ --> WS_Server
WS_Server -- 9. リアルタイム通知 --> UI_WS
UI_WS -- 10. チャット表示更新 --> UI_B
UI_A -- Worktree/最終メッセージ取得 --> DB
UI_B -- チャット履歴取得 --> DB
UI_C -- ログ一覧/詳細取得 --> Logs
4.2 実行シーケンス (標準フロー)
4.2.1 指示送信〜応答表示
指示送信 (UI → API)
UI(画面B)が POST /api/worktrees/:id/send に { message } を送信。
レスポンスには requestId (UUID) を含めてもよい(v2.1では必須ではないが将来拡張を想定)。
コマンド実行 (API → tmux)
バックエンドは worktreeId に対応する tmux セッション(例: cw_feature-foo)の存在を確認。
無ければ UC-2(遅延起動)で新規作成。
tmux send-keys により、Claude CLI の入力に以下のようなテキストを送る(例):
REQUEST START
<ユーザーの指示メッセージ>
REQUEST END
将来的には requestId を埋め込んだマーカーも付与可能:
REQUEST {requestId} START
...
REQUEST {requestId} END
Claude 処理完了 (Claude → Stop フック)
Claude CLI がタスクを完了すると、CLAUDE_HOOKS_STOP に設定されたフックが呼び出される。
完了通知 (フック → API)
環境変数 CLAUDE_HOOKS_STOP に設定されたコマンド:
HOOK_COMMAND="curl -X POST http://localhost:3000/api/hooks/claude-done
-H 'Content-Type: application/json'
-d '{"worktreeId":"{worktreeId}"}'"
export CLAUDE_HOOKS_STOP="${HOOK_COMMAND}"
これにより、POST /api/hooks/claude-done に JSON が送信される。
結果取得・差分抽出 (API_Hook)
API_Hook は次の処理を行う:
WorktreeSessionState.lastCapturedLine を読み込む(デフォルト 0)。
tmux capture-pane を実行し、最新の scrollback 全体を取得する。
前回行数との差分部分のみを「今回の新規出力」として抽出。
(将来拡張)マーカーや requestId が埋め込まれている場合はその範囲のみ切り出し。
ログ保存 & ChatMessage 保存
差分テキストを Markdown 形式に整形し、
.claude_logs/{YYYYMMDD-HHmmss}-{worktreeId}-{uuid}.md のような命名で保存。
ChatMessage を 2 件以上に分割して保存する案:
ユーザー入力分(必要であれば既に送信時に保存)
Claude 応答分(role="claude")
Claude 応答分の ChatMessage には logFileName を紐づける。
Worktree の lastMessageSummary / updatedAt を更新。
UI へのプッシュ (API_Hook → WebSocket → UI)
API_Hook は WebSocket サーバに対して「worktreeId チャンネル」に新しい ChatMessage を publish。
クライアントは接続時に worktreeId を subscribe しておき、
受信した ChatMessage を画面Bに反映。「送信中…」バブルを置き換え。
Stop フック実装とセッション管理
5.1 UC-2: 遅延セッション起動時のフック設定
バックエンドが tmux セッションを新規作成する際の手順:
1. tmuxセッションを起動 (-c {path} でカレントディレクトリ指定)
{sessionName} = cw_{worktreeId} (例: cw_feature-foo)
{worktreePath} = /path/to/root/feature/foo
tmux new-session -d -s "{sessionName}" -c "{worktreePath}"
2. Stopフックを設定して Claude を起動
HOOK_COMMAND="curl -X POST http://localhost:3000/api/hooks/claude-done
-H 'Content-Type: application/json'
-d '{"worktreeId":"{worktreeId}"}'"
tmux send-keys -t "{sessionName}" "export CLAUDE_HOOKS_STOP='${HOOK_COMMAND}'" C-m
3. Claude CLIを起動
tmux send-keys -t "{sessionName}" "claude" C-m
API_Send 実行時に、cw_{worktreeId} の存在を確認し、無ければ上記を実行。
Claude CLI が終了している(プロンプトに戻っている)場合も同様に再起動。
5.2 死活監視の粒度
tmux セッション存在チェック
tmux has-session -t cw_{worktreeId} で存在確認。
Claude プロセス稼働チェック (任意)
tmux list-panes -t cw_{worktreeId} -F "#{pane_pid}" → ps で claude プロセスがいるか確認。
いない場合は CLAUDE_HOOKS_STOP 再設定+claude 再起動。
WebSocket & マルチクライアント
6.1 接続モデル
クライアントは WebSocket でサーバに接続し、
worktreeId ごとの「room / channel」を subscribe する。
メッセージ:
{
"type": "chat_message_created",
"worktreeId": "feature-foo",
"message": { ...ChatMessage... }
}
6.2 ブロードキャストポリシー
同じ worktreeId を閲覧中のクライアント全てに対してブロードキャスト。
PC とスマホを両方開いている場合、双方向で同じ状態がリアルタイムに同期される。
セキュリティとネットワーク
7.1 バインドアドレス
デフォルト:
127.0.0.1:3000 にバインド(ローカルマシンのみ)。
オプション(スマホから LAN 経由でアクセスする場合):
0.0.0.0:3000 にバインドし、PC の IP アドレス経由でアクセス。
この場合、認証必須:
簡易な API トークン(HTTP ヘッダ) or Basic 認証 など
トークンはローカル設定ファイルに保存し、URL には埋め込まない
7.2 ファイルアクセス制限
ルートディレクトリを明示的に設定し、その配下の worktree のみ対象とする。
API から任意パスを指定する操作は提供しない(ID を介してのみアクセス)。
ログポリシー
8.1 ログファイル命名規則
格納場所:{worktreePath}/.claude_logs/
命名例:
20251116-123045-feature-foo-bd2f8c3d.md
YYYYMMDD-HHmmss-{worktreeId}-{uuid}.md
8.2 ログ内容
差分テキストをそのまま Markdown 化:
ユーザー指示・Claude 応答の両方を含む
必要に応じて区切り見出し:
User
<ユーザーのプロンプト>
Claude
<Claudeの応答>
8.3 保存とクリーンアップ
v2.1 時点では自動削除は行わず、「クリーンアップスクリプト提供」を検討。
将来的には:
N 日より古いログを削除/圧縮する CLI サブコマンドを用意する方針。
将来拡張のための設計配慮
requestId の導入余地
API_Send のレスポンスで requestId を返し、将来的に Stop フックやログ切り出しに活用できるよう、ChatMessage/ログ命名に requestId フィールドを用意。
メッセージ単位のより厳密な切り出し
現時点では「前回 capture からの差分」を 1 要素として扱うが、
将来、差分テキスト内に複数のリクエスト・応答が含まれるケースに対応できるよう、マーカー(### REQUEST {id} START/END)などを用いたパーシングを許容する設計とする。
評価指標の追加
将来的に「応答時間」「エラー率」などを計測し、画面上で可視化するために、
ChatMessage などにメタ情報(latencyMs など)を追加できる余地を残す。
以上が、改善点を反映した Claude Code CLI コンパニオン 設計書 v2.1 です。
このまま実装仕様として使える粒度になっているつもりですが、
実装言語・フレームワークの前提(例: Next.js App Router / Node.js v20)
WebSocket 実装方式(ws / Next.js の Route Handlers / Socket.IO など)
も固まっているようであれば、「10. 実装前提(Tech Stack)」セクションを追加しても良さそうです。必要ならそこも一緒に書きます!