Claude Code のステータスラインをOSC 8でクリック可能なリンクにする
まとめ
- Claude Code のカスタムステータスラインは、標準出力に出した文字列がそのまま描画される。ANSIカラーだけでなく OSC 8 ハイパーリンク も通る
- OSC 8 は
\x1b]8;;URL\x07 表示テキスト \x1b]8;;\x07の形式。対応端末では Cmd+click で開けるリンクになり、非対応端末では ただのテキストに劣化する ので貼っても安全 - クリック可能になる条件は端末依存。iTerm2 / Kitty / WezTerm は対応、Terminal.app は非対応。tmux / SSH 越しはストリップされることがある
やりたかったこと
ステータスラインに出しているカレントフォルダ名を、ローカルで動かしている Web ツール(localhost のビューア)へのリンクにしたかった。フォルダ名をCmd+clickしたらブラウザで該当フォルダが開く、という導線。
ステータスラインに渡ってくるデータ
カスタムステータスラインのコマンドには、stdin 経由で JSON が渡ってくる。モデル名・カレントディレクトリ・セッションID・差分行数などに加えて、現在のブランチに紐づくPRの情報(pr.number / pr.url / pr.review_state)まで入っている。PRリンクを貼りたいだけなら、この pr.url をそのまま使えばいい。
OSC 8 でリンクにする
リンク化の本体はエスケープシーケンスを組み立てるだけ。Node.js のステータスラインスクリプトならこう書ける。
// text を OSC 8 ハイパーリンクで包む。url が空なら素のテキストを返す。
function hyperlink(text, url) {
if (!url) return text;
return `\x1b]8;;${url}\x07${text}\x1b]8;;\x07`;
}
// 例: カレントフォルダ名をローカル Web ツールへのリンクにする
const dir = hyperlink(path.basename(workDir), buildUrl(workDir));\x07(BEL)はリンクURLと表示テキストの区切り。終端の \x1b]8;;\x07 で「リンク終わり」を宣言する。この宣言を忘れると、以降のテキスト全部がリンク扱いになるので注意。
ポイントは、 OSC 8 に対応していない端末では表示テキストだけが残る こと。つまり対応・非対応をスクリプト側で分岐しなくても、そのまま出して問題ない。
端末側の対応が本番
出力自体が正しくても、実際にクリックできるかは端末の対応次第。cat -v に流すとエスケープシーケンスが可視化できるので、まずスクリプトの出力が正しいことを確認し、そのうえで実機でCmd+clickを試すのが切り分けの順番になる。
$ echo "$json" | node statusline.js | cat -v
[Model] ^[]8;;http://localhost:15174/?...^Gフォルダ名^[]8;;^Gtmux の中にいる場合は、外側のターミナルがハイパーリンク対応でも tmux がシーケンスを落とすことがある。自分もまさにtmux越しで、出力は完全に正しいのにリンクにならず少しハマった。出力が合っているのにクリックできないときは、スクリプトではなく端末とtmuxのレイヤーを疑うのが早い。