コンテンツにスキップ

手を動かして確かめる

実機で 1 周する手順。 上から順に打てば、ccs が本当に動くかを自分の目で確認できる。

各手順には「期待される出力」と「違ったときに見るところ」を付けた。 期待される出力は、すべてこのマシンで実際に上から順に走らせて得たもの (UUID と時刻だけは毎回変わる)。信頼確認の 30 秒待ちも、/exit のあとに ペインがシェルへ戻ることも、実機で確認した上で書いている。

所要時間はおよそ 15 分。途中でやめても、最後の「元に戻す」だけやれば跡は残らない。


0. 前提を確かめる

tmux -V && jq --version && claude --version && which ccs

期待される出力(版は前後してよい):

tmux 3.7b
jq-1.8.1
2.1.226 (Claude Code)
/Users/apple/.local/bin/ccs
ccs が見つからない

symlink を張る。

cd ~/ghq/github.com/ken-ty/ccs && make install
tmux か jq が無い

brew install tmux jq
ccs は依存が無いと終了コード 4 で落ち、導入方法を出す。それも試してよい: CCS_JQ_BIN=nope ccs ls


1. 触る前の状態を控える

あとで「元に戻ったか」を確かめるため。 何も変わっていないことを最後に確認する。

tmux ls; ls ~/.cc-scratch 2>&1 | head -3; jq -r '.projects | length' ~/.claude.json

期待される出力(まだ tmux を使っていなければ):

no server running on /private/tmp/tmux-501/default
ls: /Users/apple/.cc-scratch: No such file or directory
55

数字は控えておく

最後の数字(.projects の件数)を覚えておく。手順 9 以外では増えないはず。


2. 何も起きないことを確かめる

resolve は副作用が無い。まずここで、打ち間違いが安全に分かることを見る。

ccs resolve x01

期待される出力:

x01 /Users/apple/ghq/github.com/ken-ty/x01

続けて、曖昧なときに勝手に選ばないことを見る。

ccs resolve IceCubesApp

期待される出力(終了コードは 1):

ccs: IceCubesApp に当てはまるリポジトリが複数あります:

  Dimillian/IceCubesApp
  ken-ty/IceCubesApp

<owner>/<repo> の形で指定し直してください。

ここまでで tmux セッションは 1 つも立っていない。 確かめる:

tmux ls

→ no server running ... のまま。


3. 立てる

ccs new x01

期待される出力(1 行の JSON。sessionId は毎回変わる):

{"slug":"x01","sessionId":"0ecd87dd-…","path":"/Users/apple/ghq/github.com/ken-ty/x01","tmux":"cc/x01","transcript":"/Users/apple/.claude/projects/-Users-apple-ghq-github-com-ken-ty-x01/0ecd87dd-….jsonl","created":true,"running":true}

見るところ:

  • created が true
  • running が true
  • 数秒で返る(30 秒待たされたら手順 3 の下の「うまくいかないとき」へ)
30 秒待って「登録されませんでした」と言われた

ccs はペインの中身を見せてくれる。だいたいは次のどれか:

  • 信頼確認で止まっている → ccs attach x01 して「1. Yes, I trust this folder」
  • ログインが切れている → ccs attach x01 して /login
  • それ以外 → ペインの内容をそのまま読む

ccs はこのときセッションを消さない。 何に詰まったかを見られるようにするため。 諦めるときは ccs kill x01。


4. ハブから見えることを確かめる(ここが核心)

ccs を作った理由はこの 1 点。 立てたセッションに、このハブから指示を送れること。

ここで使う ListAgents と SendMessage はシェルのコマンドではないので、 ターミナルではなくこのチャットで頼む。

ListAgents で今あるセッションを見せて

期待される結果: 一覧に x01 が interactive · idle として現れる。

Peer sessions (7):
  x01 [80bd9b]  ·  interactive  ·  idle  ·  started 17s ago
  …

続けて、実際に仕事を頼んでみる:

x01 のセッションに「このリポジトリの README の見出しだけ列挙して」と頼んで、結果を教えて

期待される結果: 数十秒で、x01 側が調べた内容が返ってくる。

ここが通れば、狙っていたものは動いている

ターミナルを 1 度も開かずに、新しいセッションを立てて仕事を頼み、結果を回収できた。


5. 乗り込んで、抜ける

ccs attach x01

期待される動き: Claude Code の画面に切り替わる。左下に x01 と出ている。

抜けるとき: Ctrl-b を押してから d(detach)。Ctrl-c や exit ではない。

detach と kill は違う

Ctrl-b d で抜けてもセッションは生き続ける。それが tmux を使っている理由。 畳みたいときだけ ccs kill を使う。

抜けたあと、生きていることを確かめる:

ccs ls

期待される出力:

SLUG  STATUS    SESSION ID                            PATH
x01   idle      0ecd87dd-cdb0-4835-add0-23e4c14b9b5f  ~/ghq/github.com/ken-ty/x01

6. 二度立てても増えないことを確かめる

ccs new x01

期待される出力: 手順 3 と同じ sessionId で、created が false。

{"slug":"x01","sessionId":"0ecd87dd-…",…,"created":false,"running":true}

打ち方を変えても同じになることも見る:

ccs new ken-ty/x01

→ これも created:false、同じ sessionId。

tmux セッションが 1 本のままであること:

tmux ls

→ cc/x01: 1 windows … の 1 行だけ。


7. 止まったセッションの見え方

claude だけを終了させて、ペインが残っている状態を作る。

ccs attach x01

→ Claude Code の中で /exit(または Ctrl-d)。ペインはシェルに戻る。 → Ctrl-b d で抜ける。

ccs ls

期待される出力:

SLUG  STATUS    SESSION ID                            PATH
x01   stopped   0ecd87dd-cdb0-4835-add0-23e4c14b9b5f  ~/ghq/github.com/ken-ty/x01

見るところ: stopped になっても SESSION ID が出ている。 この UUID が同じ会話に戻る手掛かりで、ccs restore もこれを使う。

この状態で ccs new x01 を打つと、成功扱いにせず復帰コマンドを出す:

ccs: cc/x01 のペインは残っていますが、claude は動いていません。

  乗り込んで、同じ会話を再開する:
    ccs attach x01
    claude --resume 0ecd87dd-cdb0-4835-add0-23e4c14b9b5f
  畳んで立て直す:     ccs kill x01 && ccs new x01

7.1 止まったセッションを戻す

ccs ls が stopped と言っている状態のまま、同じ会話で立て直す。

ccs restore

期待される出力(既定は見せるだけ。何も起きない):

立て直せるセッション:
  x01   2026-08-22 13:45  0ecd87dd-cdb0-4835-add0-23e4c14b9b5f  <会話の名前>

実行するには: ccs restore --yes
別の会話を選ぶ: ccs restore <slug> --list
ccs restore --yes
ccs ls

見るところ: SESSION ID が戻す前と同じで、STATUS が stopped でなくなっている。 ここが違っていたら、戻ったように見えて別の会話が立っている。

ccs attach x01 で乗り込むと、前の会話がそのまま続いている(スクロールバックに 前のやり取りがある)。アプリの一覧では元の名前のまま戻る ── restore は -n を渡さないので、会話に付いていた名前が消えない。

戻した会話は動き出すことがある

落ちる直前に続きがあった会話は、resume した瞬間に続きを走らせる。 ccs から止める手段は無いので、1 本ずつ戻したいときは名指しする (ccs restore x01 --yes)。詳細は restore.md。

tmux ごと消えた場合(再起動の再現)も同じ手順で戻る。ccs kill x01 で畳んでから ccs restore を打つと、x01 も候補に出る ── ghq 配下でも ccs が立てた印があれば 拾うため(印とは)。

ccs restore --yes         # まとめて
ccs restore x01 --yes     # 名指し

8. 掃除する

まず見せるだけ:

ccs gc

期待される出力:

止まっているセッション(畳む対象):
  x01                  claude --resume 0ecd87dd-cdb0-4835-add0-23e4c14b9b5f

実行するには: ccs gc --yes

見るところ: 何も消えていない。 tmux ls で確かめてよい。

内容を確認してから実行:

ccs gc --yes

期待される出力:

止まっているセッション(畳む対象):
  x01                  claude --resume 0ecd87dd-…

畳みました: cc/x01
ccs ls

期待される出力:

立っているセッションはありません。

  立てる: ccs new <target>

9. 使い捨ての作業枠(何本でも立つ)

ここで ~/.claude.json に行が増える

枠を信頼済みにするのは ccs 自身(#6、 2026-08-18 の決定)。手順 1 で控えた .projects の件数が、立てた枠のぶんだけ増える。

ccs new --tmp

期待される出力(stderr に 1 行、stdout に JSON):

ccs: ccs が発行した作業枠なので信頼済みにしました: /Users/apple/.cc-scratch/3f9a2c1b
{"slug":"tmp-1","sessionId":"…","path":"/Users/apple/.cc-scratch/1",…,"created":true}

信頼確認は出ない。 枠は ccs が作った空のディレクトリなので、確認が守ろうとしている 「知らないコード」がそこに無い。

続けてもう 1 本立てて、枠が増えることを確かめる:

ccs new --tmp
ccs ls

期待される出力: tmp-1 と tmp-2 が両方 idle で出る。ここが詰まると 「使い捨てを何本も立てる」という枠の存在理由そのものが成り立たない。

短い綴りの ccs new tmp も同じ結果になる。ただし ghq に tmp という名前のリポジトリが あるときだけは、どちらを指したか決められないので候補を出して止まる。

枠の割り当て方は変わる予定

~/.cc-scratch/1・~/.cc-scratch/2 という固定 8 枠は、セッションごとに一意な ディレクトリを作る形へ変わる(ADR-0001)。 この手順で確かめているのは「使い捨ての場所が何本でも立ち、信頼確認が出ないこと」なので、 そこは変わらない。変わるのはパスと slug の見た目で、そのときはこの手順書も直す。


9.1 セッションが自分で終わる(ccs kill --self)

アプリでアーカイブしても tmux のセッションは残る。 アーカイブは会話一覧の操作で、 CLI のプロセスは生き続ける ── ccs ls の idle は誤読ではなく事実。実測 (2026-08-28、動いている 16 本)でレジストリのキーを全部並べても、archived に 当たるものは無かった。

見たもの 値
レジストリのキー bridgeSessionId cwd entrypoint kind messagingSocketPath name nameSince nameSource peerFeatures peerProtocol pid pidDomain procStart sessionId startedAt status statusUpdatedAt tmux updatedAt version waitingFor
status の値 idle 8 / waiting 6 / busy 1 / null 1

だから終わる意思のあるセッションが、自分で終わる。

ccs kill --self          # そのセッションの中から打つ

<slug> は打たない ── 自分がどれかは $TMUX と $TMUX_PANE から決まる。

cc/ のペインを持たないセッションからも打てる。 アプリや VS Code から開いた セッションは ccs 管轄外なので $TMUX からは決まらないが、--self が言っているのは 「自分が終わる」であって「ccs が立てたものを畳む」ではない。そちらは祖先の pid で レジストリを引いて自分を特定し、tmux ではなく claude の pid に SIGTERM を送る。

  • CLAUDE_PID は使わない。 claude は自分の pid を子プロセスへ渡すが(実測 2026-08-30)、env は継承されるので入れ子や古い値で他人を指しうる。ここは kill の宛先なので、取り違えると畳まれるのが他人のセッションになる
  • SIGKILL に昇格しない。 SIGTERM なら claude は会話ログを書き切る(§10)。 KILL で落とすと書き切られていない会話が残り、--resume で戻す先が壊れる

何が残るかを実測する(本物の claude が要る)

この節だけは fake claude では答えにならない。 スタブは自前で SIGHUP を trap しているので、本物が SIGHUP でどう片付けるかは本物でしか見えない。

まず 1 本立てて、何ができるかを控える。

ls ~/.claude/sessions/ | sort > /tmp/before-sessions.txt
ls /tmp/cc-socks/      | sort > /tmp/before-socks.txt

ccs new <target>

ls ~/.claude/sessions/ | sort > /tmp/after-sessions.txt
comm -13 /tmp/before-sessions.txt /tmp/after-sessions.txt

実測(2026-08-28): 1 セッションにつき 3 つできる。

83350.cf793dc3815f1d2132ff7bf2789f0013dbc1bfd4e4dc720823fe378a773fbefc.key
83350.json
83350.sock

つまり ~/.claude/sessions/<pid>.json、~/.claude/sessions/<pid>.<hash>.key、 /tmp/cc-socks/<pid>.sock。どれも ccs は作っていない ── claude 自身のもの。

畳んで、消えることを確かめる。

ccs kill <slug>
ls ~/.claude/sessions/ | grep '^83350' ; ls /tmp/cc-socks/ | grep '^83350'

実測: tmux kill-session の SIGHUP で 3 つとも消えた。ただし即座ではない。

いつ プロセス <pid>.sock <pid>.json / .key
kill-session の直後 まだ生きている 消えている 残っている
数秒後 消えた 消えた 消えた

claude が終了しながら片付けるので、ccs が消して回る必要は無い。 逆に、 ccs が先回りして消すと、まだ生きているプロセスの足元を抜くことになる。

自分で終わるところまで通す

--self は「中から打つ」ので、外から ccs kill を撃つのとは別物。1 本立てて、 そのセッション自身に打たせる。

ccs new <target> -- "次のコマンドを 1 回だけ実行してください: ccs kill --self"

実測(2026-08-28、本物の claude で 1 本): セッションは自分を畳み、 ~/.claude/sessions は 34 → 36 → 34、/tmp/cc-socks は 17 → 18 → 17 と 元に戻った。tmux セッションも残っていない。

報告は読めない

ccs kill --self は畳む前に「同じ会話に戻るには…」を出すが、打った本人は それを読めない。 ペインごと消えるので、出力を受け取るはずのプロセスも 一緒に死ぬ(実測: 会話ログの末尾はツールの結果が書かれないまま終わっていた)。

読めるのは断られたときだけ ── そのときセッションは生きている。 戻り方が要るときは ccs restore を使う。

断られる経路

相手 どうなるか
hub 拒否(--force でも)。落とすとスマホから叩く経路が消える
未コミットの変更がある作業ツリー 拒否。ccs kill --self --force が要る
作業中 止めない。 この ccs を動かしているのが自分なので、自分の status は必ず作業中になる
git の管理下でないところ(使い捨ての作業枠など) 未コミットの門は掛からない

worktree は消さない(ccs kill と同じ方針)。片付けは ccs gc の担当。

10. 元に戻す

ccs kill tmp-1
ccs kill tmp-2
ccs gc --yes

期待される出力: 空になった枠のディレクトリも gc が消す。

消しました: ~/.cc-scratch/1
消しました: ~/.cc-scratch/2

親のディレクトリだけ残るので、それも消す:

rmdir ~/.cc-scratch
ccs ls; ls ~/.cc-scratch

期待される出力:

立っているセッションはありません。

  立てる: ccs new <target>
ls: /Users/apple/.cc-scratch: No such file or directory

tmux ls にセッションが残っていても異常ではない

ここで確かめたいのは「ccs の跡が残っていないこと」なので、tmux ls ではなく ccs ls で見る。ccs ls は cc/ が付いたセッションだけに絞るため、 自分で開いた作業用の tmux セッションを巻き込まない。

tmux ls を打つと、手順 8 で畳まなかったセッションや、この手順の外で立てた ものがそのまま出る。tmux サーバは 1 本でも残っていれば動き続けるので、 no server running … が出るのは全部畳んだときだけ。

$ tmux ls
cc/x01: 1 windows (created Tue Aug 18 03:08:39 2026)

これは「手順 7〜8 で畳んだ x01 を、そのあと立て直したまま残している」状態。 畳むなら ccs kill x01、残すならそのままでよい。

ccs 自体を外すなら:

rm ~/.local/bin/ccs
~/.claude.json について

手順 9 で増えた hasTrustDialogAccepted の行は残る。枠を次に使うときに確認が 出なくなるだけで、害は無いので消さなくてよい。気になるなら jq 'del(.projects["'"$HOME"'/.cc-scratch/1"])' で消せるが、このファイルは Claude Code の状態がまとめて入った 100KB 級のものなので、書き戻す前に 中身を確かめること。


11. hub を 1 周する(一部実施)

launchd の経路だけ実測した(2026-08-22 / macOS 15・Homebrew)

アプリ側の見え方(名前・offline の残り方・--resume での RC 張り直し)は まだ。fake claude では確かめられないので、ここは未チェックのまま残す。

ccs hub up            # JSON が返り、bridge が空でないこと
ccs hub status        # state healthy、終了コード 0
ccs ls                # hub が一覧に出ること

確かめたいこと:

  • [ ] スマホ / デスクトップのアプリの一覧に hub という名前で出るか (会話を数往復させても名前が変わらないか)
  • [ ] ccs hub restart のあと、アプリの一覧に新しい hub が出て、古いものが offline で残り続けないか
  • [ ] ccs hub restart --resume で同じ会話に戻り、そのとき Remote Control が 張り直されるか
  • [ ] remoteControlAtStartup: true を設定している環境で、--remote-control の 明示指定が二重登録にならないか
  • [x] launchd から起動した ccs hub up が、手元と同じ tmux サーバに入るか

11.1 launchd の経路(実測済み)

ccs hub agent --print > ~/Library/LaunchAgents/local.ccs.hub.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/local.ccs.hub.plist
tmux kill-server      # tmux サーバごと落として、電源断と同じ状態を作る

結果: 入る。 launchctl が起動した ccs hub up は /private/tmp/tmux-501/default ── 手元のシェルの tmux ls が見るのと同じ ソケット ── に cc/hub を作り、ccs ls にも 1 本だけ出た。bridge も付いた。

ただし、素の -lc では PATH が足りずに死ぬ。 最初の設置では ~/.cc-hub/agent.log に依存不足だけが積まれた。

$ tail ~/.cc-hub/agent.log
ccs: 必要なコマンドが見つかりません: tmux

launchd が渡す PATH は /usr/bin:/bin:/usr/sbin:/sbin 相当で、-lc を付けても 読まれるのは /etc/zprofile(path_helper)と ~/.zprofile まで。~/.zshrc は対話シェル専用なので読まれない。 Homebrew の既定の入れ方は ~/.zshrc に brew shellenv を書くので、/opt/homebrew/bin がそこで落ちる。

$ env -i HOME=$HOME PATH=/usr/bin:/bin:/usr/sbin:/sbin /bin/zsh -lc 'command -v tmux'
(何も出ない)

これを受けて ccs hub agent は、生成時に解決できた依存の在処を EnvironmentVariables に焼き込むようにした。焼き込んだあとで tmux kill-server → launchctl bootstrap を通したところ、上のとおり復帰した。

ユニットは生成した環境に紐づく

焼き込みなので、Homebrew の場所を変えたり claude を入れ直したりしたら ccs hub agent --print で出し直して置き換える。

hub down のまま放置しない

paused がある間は自動起動も止まる。確認が終わったら ccs hub up --force。

元に戻す:

launchctl bootout gui/$(id -u)/local.ccs.hub
rm ~/Library/LaunchAgents/local.ccs.hub.plist
ccs hub down && rm -rf ~/.cc-hub     # 中身を確かめてから

通ったかどうかの一覧

  • [ ] 0. 依存が揃っている
  • [ ] 2. resolve が副作用なしで答え、曖昧なときは勝手に選ばない
  • [ ] 3. ccs new が数秒で JSON を返す
  • [ ] 4. ハブの ListAgents に現れ、SendMessage で仕事を頼めた ← ここが本命
  • [ ] 5. Ctrl-b d で抜けてもセッションが生きている
  • [ ] 6. 二度立てても増えない(created:false、同じ sessionId)
  • [ ] 7. stopped でも SESSION ID が出る
  • [ ] 7.1 ccs restore が同じ sessionId で立て直し、会話の名前も変えない
  • [ ] 8. gc が既定では何も消さない
  • [ ] 9. 使い捨ての作業枠が、信頼確認なしで 2 本とも立った
  • [ ] 9.1 ccs kill --self で自分を畳め、~/.claude/sessions と /tmp/cc-socks が元の件数に戻った
  • [ ] 9.1 管轄外(アプリや VS Code から開いたセッション)でも ccs kill --self が通り、 cc/ のペインを持たない相手を SIGTERM で畳めた(自動テストでは「殺したプロセスが 走り切れない」ところを再現できないので、ここだけが本当の確認になる)
  • [ ] 10. 元に戻せた
  • [ ] 11. hub(未実施。fake claude では確かめられない部分)

4 が通れば、この道具が解こうとしていた問題は解けている。 他が引っかかっても、それは使い勝手の話。


うまくいかないときの見どころ

症状 見るところ
ccs new が 30 秒待って失敗する ペインの中身が出るのでそれを読む。信頼確認かログイン切れが大半
ListAgents に出てこない ccs ls で idle か確かめる。stopped なら claude が落ちている(使い分け)
attach から抜けられない Ctrl-b → d。Ctrl-c ではない
ccs new --tmp が「枠が全部埋まっています」 ccs gc で状況を見る。中身のある枠は ccs が消さないので自分で確認する
ccs new tmp が「同名のリポジトリもあります」 ghq に tmp がある。作業枠なら --tmp、リポジトリなら <owner>/tmp
ccs kill が「作業中です」で止まる 意図した動き。ccs attach で様子を見てから --force
アプリでアーカイブしたのに ccs ls に残っている 誤読ではない。 アーカイブは会話一覧の操作で、CLI のプロセスは生きている。そのセッションに ccs kill --self を打たせる(9.1)
ccs kill --self が「未コミットの変更があります」で止まる 意図した動き。git status --short を見てから --force
ccs restore に戻したいものが出てこない ghq の外に立てたものは列挙しない(名指しで戻す)。印の無い会話(アプリ / VS Code から開いたもの)も出ない。古い会話は既定で 7 日まで(--all)。印とは
ccs hub up が no-rc(10)で終わる Remote Control が付いていない。ccs config で CCS_REMOTE_CONTROL を見る。使わない環境なら off
ccs hub up が needs-attention(15)で止まる 再起動を繰り返している。ccs hub attach と ~/.cc-hub/hub.log を見る
終了コードの意味 0 成功 / 1 失敗 / 2 使い方の誤り / 4 依存が無い / 10〜15 は hub の状態(hub)

それでも分からなければ、このチャットにそのまま貼ってほしい。 ccs が出すメッセージは、次の手が分かるように書いてある。 書いていなかったとしたら、それはこちらの直すべき点。