投稿

Claude Code のステータスラインをOSC 8でクリック可能なリンクにする

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;;^G

tmux の中にいる場合は、外側のターミナルがハイパーリンク対応でも tmux がシーケンスを落とすことがある。自分もまさにtmux越しで、出力は完全に正しいのにリンクにならず少しハマった。出力が合っているのにクリックできないときは、スクリプトではなく端末とtmuxのレイヤーを疑うのが早い。

トレンドのタグ