機能
Claude Codeの起動
このページは英語から機械翻訳されています。修正を歓迎します。 GitHubでこのページを編集
このページの内容
ccam workは、アカウントworkとしてClaude Codeを起動し、名前より後のものはすべてそのままclaudeに渡します。ターミナル、スクリプト、パイプで入力を渡す場合など、claudeと入力するあらゆる場所で使えます。
使い方
ccam work # Claude Code as the account work
ccam 2 # the account with number 2 (see ccam list)
ccam work --resume # claude --resume, as work
ccam work -p "fix the tests" --output-format json
cat log.txt | ccam work -p "find the cause"
ccam -p "summarise" # no name: the default account
ccam use work # make work the default account
ccam run work --continue # same as ccam work --continue
ccam # the picker on a terminal, the list otherwise
ccam --dry-run work -p hi # print the command instead of running itアカウントの名前か、ccam listに表示される番号を指定します。ccamは名前より後の引数を読まないので、ccam work --helpはClaude Codeのヘルプを表示します。名前の後の--は省略できます。標準入力、終了コード、シグナルはclaudeと同じように動作します。
最初の引数が-で始まり、ccam自身のフラグでない場合、ccamはすべての引数を付けてデフォルトのアカウントを起動します。デフォルトはccam useで設定したアカウントで、設定していない場合は番号が最も小さいアカウントです。
ピッカー
引数なしのccamは、標準入力と標準出力がターミナルであればピッカーを開き、そうでなければccam listを出力します。ピッカーには、各アカウントの番号、メールアドレス、5時間の使用量、最終使用日時、共有項目が表示されます。使用量は、各アカウントの応答があり次第表示されます。
| キー | 動作 |
|---|---|
Enter |
選択したアカウントとしてClaude Codeを起動します |
1~9 |
その番号のアカウントを起動します |
Up/Down、k/j |
選択を移動します |
a |
ピッカーを閉じてccam addのウィザードを実行します |
s |
選択したアカウントの共有項目を編集します(Spaceかxで切り替え、Enterで適用、Escで戻ります) |
d |
選択したアカウントをデフォルトにします |
r |
短期キャッシュを使わずに使用量を取得し直します |
q、Esc、Ctrl+C |
終了します |
ピッカーは操作を実行する前に閉じるので、Claude Codeが起動する前にターミナルは元の状態に戻ります。dや共有の変更の後は、ピッカーがもう一度開きます。共有を変更するときは、ccam shareと同じように、先に計画を表示して確認を求めます。
オプション
| オプション | 効果 | デフォルト |
|---|---|---|
--dry-run |
ccamが実行する環境変数とコマンドを表示して終了します | オフ |
-v、--version |
ccam <version>を表示します(最初の引数の場合のみ) |
|
-h、--help |
ccam --helpやccam run --helpとして、ccamのヘルプを表示します |
引数の規則:
- ccamが自身のフラグを読むのは、アカウント名より前だけです。名前より後の引数は、
--dry-runや--helpも含めてすべてClaude Codeのものです。 --dry-runは、ccam --dry-run work、ccam run --dry-run work、ccam --dry-run(デフォルトのアカウント)の形で使えます。拒否されることはなく、そのとおりに動作します。ccam run <name|number> [claude args]は、ccam <name>の長い形式です。名前なしのccam runはデフォルトのアカウントを使います。- アカウント名には
add、list、run、usageなどのccamのコマンド名を使えないので、ccam <word>が曖昧になることはありません。
ドライランは、そのままシェルに貼り付けられる1行を出力します。
$ ccam --dry-run work -p "fix the tests"
CLAUDE_CONFIG_DIR=/home/me/.local/share/ccam/profiles/work /home/me/.local/bin/claude --mcp-config=/home/me/.local/share/ccam/groups/default/mcp.json -p 'fix the tests'しくみ
起動のたびに、コマンドラインからexecまで同じ手順をたどります。
各ボックスはその手順の実行中に強調表示され、その下の行は、Claude Codeが起動するときの環境変数と引数です。
cmd/ccam/main.goがapp.Mainを呼び出します。ccamが別の名前で起動された場合、それはショートカットなので、直接起動処理に進みます。app.parseは、cobraが関わる前にos.Argsを調べます。cli.Executeに進むのは、ccam単体(ピッカー)、ccamのコマンド名(config.Reserved)、-h/--help、-v/--versionだけです。それ以外はすべて起動です。launcher.Launchが~/.config/ccam/config.tomlを読み込みます。解析するファイルはこれだけです。launcher.Prepareが名前か番号でアカウントを見つけ、先頭の--を1つ取り除き、ResolveClaudeでclaudeの実行ファイルを特定します。PATHの検索結果は、ファイルの更新日時とともに~/.local/state/ccam/claude-pathにキャッシュされるので、ccamが起動のたびにPATHを検索することはありません。ファイルが変更されたか、なくなった場合は、検索し直します。config.tomlのclaude_binのパスは、そのまま使われます。ループになるため、ccam自身であるclaudeは、inodeで比較して拒否します。checkLinksが、アカウントのフォルダー~/.local/share/ccam/profiles/<name>/を共有設定と比較します。使うshare.Checkはlstatとreadlinkだけを呼び出すので、システムコールは項目ごとに1回か2回です。足りないリンクはその場で作成します。データを失う可能性があるものがあれば、起動を停止します。applyMCPが共有MCPサーバーを追加します(後述)。session.Registerがセッションを記録します。ccamは、自身のPIDを名前とし、プロセスの開始時刻を内容とするマーカーを$XDG_RUNTIME_DIR/ccam/profile-<name>/<pid>(XDG_RUNTIME_DIRがない場合は~/.local/state/ccam/locks/)に書き込みます。会話履歴を共有するアカウントの場合は、まずhistoryCheckがhistory-<group>-<hash>(ハッシュは現在のフォルダーから求めます)で実行中のセッションを探し、そこにも登録します。execはPIDを変えないので、マーカーはそのままClaude Codeのプロセス自体を指すことになります。保持し続けるロックではなくマーカーを使うのは、Claude Codeが起動したプロセスがロックを引き継ぎ、セッション終了後もロックを持ち続けてしまうためです。Plan.Execがターミナルのタイトルをclaude · <name>に設定し(標準エラー出力がターミナルの場合)、起動時刻をfsyncなしで~/.local/state/ccam/last-used/<name>に書き込み、syscall.Execを呼び出します。
Claude Codeは、CLAUDE_CONFIG_DIRにアカウントのフォルダー、CCAM_PROFILEにアカウント名が設定された状態で実行されます。ccamは、これらの変数について引き継いだ値をすべて取り除きます。macOSでccam importで登録した~/.claudeフォルダーの場合は、CLAUDE_SECURESTORAGE_CONFIG_DIRにも空の値を設定します。これにより、そのアカウントと通常のclaudeが同じキーチェーンのログインを使います。
execを使う理由
syscall.Execはccamのプロセスをclaudeに置き換えます。そのため、ccamがメモリーに残ることはなく、キー入力、シグナル、終了コードはClaude Code自身のものになり、引数はシェルの文字列ではなく配列のまま保たれます。claude mcp addで追加したサーバーの検出など、セッション後に必要な処理は次の起動時に行います。ccamは.claude.jsonの更新日時を~/.local/state/ccam/mcp/<name>/seenと比較し、変更があった場合だけファイルを解析します。
コマンドラインの振り分け
3つのコマンドラインが、internal/appの中でそれぞれ別の経路をたどります。
ショートカットはargv[0]で、ccam <name>は最初の引数で判定され、どちらもsyscall.Execで終わります。cobraに到達するのは、ccam usageのようなccamのコマンドだけです。
共有MCPサーバー
デフォルトのflag戦略では、ccamはグループのMCPサーバーを引数としてClaude Codeに渡し、自分で指定した引数より前に置きます。
mcp.FlagArgsは、グループのmcp.jsonがある場合にだけ引数を返し、Plan.InsertArgsがそれをclaudeの直後に置きます。自分で指定した--mcp-configや--strict-mcp-configも、その上に追加で適用されます。Claude Codeの--mcp-configは複数の値を取るので、--mcp-config=<path>という1つの引数にしています。2つの引数に分けると、続くプロンプトも設定ファイルとして読まれてしまいます。
config.tomlでmcp_strategy = "sync"を設定すると、ccamは代わりにアカウントの.claude.jsonにサーバーを書き込みます。アカウントがすでに実行中の場合は、これをスキップします。
Remote Control
ccam work rc(またはccam work remote-control)は、workとしてClaude CodeのRemote Controlを起動します。claude remote-controlは動詞の前の--mcp-configを拒否し、動詞の後でも受け付けません。また、そのセッションはアカウントの.claude.jsonからしかMCPサーバーを読みません。そのため、ここではフラグ戦略の動作が異なります。
remoteControlは、アカウントより後の引数のうち-で始まらない最初のものをClaude Codeの動詞とみなします。それがremote-controlかrcの場合、mcp.FlagArgsは何も追加しません。mcp.Syncedは、グループのmcp.jsonの更新日時をアカウントのsync-stampと比較します。.claude.jsonにグループの現在のサーバーがすでに入っている場合や、グループにサーバーがない場合は、確認せずにRemote Controlを起動します。それ以外の場合、ccamはセッションで使えなくなるサーバーの名前を示して確認します。
ccam: Remote Control sessions only get MCP servers stored in account work's .claude.json, not the shared MCP servers (ctx, fs). Copy them into .claude.json (a backup is kept)? [y/N]yと答えると、mcp.Syncがsync戦略と同じ方法でサーバーをコピーします。Claude Codeのロックを取得し、バックアップを取ったうえで、アカウント自身のサーバーは残します。Nと答えた場合やターミナルがない場合は、それらのサーバーなしでRemote Controlを起動し、起動のたびにサーバーをコピーするmcp_strategy = "sync"を案内します。
ccamは、サーバーがコピーされるまでRemote Controlの起動のたびに確認し、コピーした後はグループのmcp.jsonが再び変わったときに一度だけ確認します。その後の通常の起動では.claude.jsonのコピーが--mcp-configより優先されるため、ccam mcp add、remove、importはほかのアカウントが持つコピーも更新します(共有MCPサーバー)。
10 msの予算
ccamがClaude Codeの起動前に追加する時間は、最大10 ms(p95)です。起動経路では、cobraのコマンドツリーの構築、Bubble Teaの起動、ネットワーク接続を一切行いません。また、パッケージのinit()関数の合計が5 msを超えるとTestPackageInitIsCheapが失敗します。scripts/perf-check.shはhyperfineでccam --dry-run work、ccam work、ショートカットを計測し、10 msを超えると失敗します。internal/launcherのBenchmarkLaunchPathは、これらの手順をループで実行します。手順ごとの時間を確認するには、CCAM_TRACE=1を設定します。
$ CCAM_TRACE=1 ccam --dry-run work >/dev/null
ccam trace: start 2.830ms (total 2.830ms)
ccam trace: load config 0.397ms (total 3.227ms)
ccam trace: prepare 0.112ms (total 3.339ms)
ccam trace: check links 0.238ms (total 3.578ms)
ccam trace: prelaunch 0.083ms (total 3.661ms)安全性と特殊なケース
- 不明な名前や番号を指定すると
no account "<name>"で停止し、ccam listを案内します。アカウントが1つもない場合は、ccam add <name>で追加するよう求めます。 - 共有パスが通常のファイルになったり、間違った場所を指していたりする場合、ccamは
stopped because of share link problems:で停止し、問題を一覧表示して、ccam doctor --fixを実行するよう求めます。起動経路でデータを移動することはありません。 - このアカウントと会話履歴を共有する(同じグループの)別のClaude Codeが同じフォルダーで実行中の場合、ccamは履歴が混ざる可能性があることを警告し、
Run anyway? [y/N]と尋ねます。ターミナルがない場合は、警告を表示して続行します。 --dry-runは、MCPの同期、新しいサーバーの通知、履歴の確認、セッションのマーカー、タイトル、最終使用日時をスキップし、execも行いません。共有リンクの確認は行い、通常の起動と同じように足りないリンクを作成します。workがすでに実行中のときにccam work rcを実行すると、.claude.jsonには触れずにその旨を表示し、グループのサーバーなしでRemote Controlを起動します。サーバーをコピーするには、セッションが終了した後にもう一度実行してください。--dry-runではメッセージを表示するだけで、確認もコピーも行いません。mcp_strategy = "sync"の場合、同期に失敗するとccam: MCP sync failed (launching anyway): …と表示され、Claude Codeはそれまでのサーバーで起動します。