Claude Code + agmsg + xfce4-terminalでのタブタイトルの変更 #claude #agmsg #xfce4

はじめに

筆者はDebian環境において、xfce4-terminalで複数タブを開き、それぞれでClaude Codeを起動し、複数のプロジェクトを開発し、agmsgを通して各プロジェクト間でメッセージをやりとりしています。

以前は、プロジェクトAにて行った作業をB,C,D,…にも行いたい場合、プロジェクトAで「今の作業指示をMarkdownファイルに書き出して」などと指示し、B,C,D,…にてそれぞれ「Markdownファイルを読み込んでその作業を行って」などと指示していました。agmsg 導入後は「全エージェント、○○を実施」と言えばプロジェクトA,B,C,D,…すべてでやってくれるので大変簡単になりました。

ただ、xfce4-terminalの複数タブでは、アクティブでないタブで動いているClaude Codeが人間の指示を求めても気がつかない、という問題が出てきました。ターミナルマルチプレクサとしてはGNU Screentmuxなどがありますが、生憎使っていないため、xfce4-terminalでの解決策を(Claude Codeと)調べてみました。

Claude Codeにタブタイトルを変更させない

Claude Codeは状況により自動的にターミナル(タブ)タイトルを変更しています。そのため、タブタイトルを別管理しようとすると、この機能で上書きされてしまうおそれがあります。環境変数 CLAUDE_CODE_DISABLE_TERMINAL_TITLE1 に設定すると、タブタイトルの自動更新を無効にできます。

~/.claude/settings.json でも設定できます。

  "env": {
    "CLAUDE_CODE_DISABLE_TERMINAL_TITLE": "1"
  },

xfce4-terminal のタブタイトルを変更する

xfce4-terminal に限らず、いわゆるANSIエスケープシーケンスをサポートしていればコマンドラインからタブタイトルを変更できます。例えば次を実行すれば、タブタイトルは ABC に変わります。

echo -ne "\033]0;ABC\007"

デフォルトのタブタイトルに戻すには次を実行します。

echo -ne "\033]2;\007"

Claude Code のイベントとフック

Claude Codeは動作中にさまざまなイベントを発火しており、それをフックという仕組みで拾って任意のコマンドを実行できます。

先に述べた通り、「人間の指示待ち」になったらタブタイトルで知らせてほしいというのが現在の要望で、この「人間の指示待ち」というイベントをフックしたらよさそうです。

  • Notification
    • matcher: permission_request
  • PermissionRequest

あたりが「人間の指示待ち」イベントに相当する、とClaude Codeが教えてくれました。

一方、「人間の指示待ち」イベントだけをフックしたら、待っていないときにも待っています、というタブタイトルになってしまうので「Claude Codeが作業中」「Claude Codeが作業完了、人間の入力待ち」といったイベントをフックして、そのときもタブタイトルを変えるとよさそうです。

  • Claude Codeが作業中
    • UserPromptSubmit
    • PermissionDenied
    • PostToolUse
  • Claude Codeが作業完了、人間の入力待ち
    • SessionStart
    • Stop

あたりのイベントをClaude Codeが教えてくれました。

  • 🔔: 人間の指示待ち
  • ⏳: Claude Codeが作業中
  • ✅: Claude Codeが作業完了、人間の入力待ち

各イベントで各絵文字をタブタイトルに出すことにしました。

xfce4-terminal のタブタイトル変更スクリプト

次を ~/.claude/tab-title.sh として、Claude Codeが作成しました。本スクリプトはDebian GNU/Linuxで動作確認しています。

#!/bin/sh
icon=$1
label=$(basename "${CLAUDE_PROJECT_DIR:-$PWD}")

tty=
pid=$$
i=0
while [ "$i" -lt 12 ]; do
    t=$(ps -o tty= -p "$pid" 2>/dev/null | tr -d ' ')
    case $t in
    pts/*)
        tty=/dev/$t
        break
        ;;
    esac
    pid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ')
    [ -n "$pid" ] && [ "$pid" -gt 1 ] || break
    i=$((i + 1))
done

[ -n "$tty" ] || exit 0

if [ -n "$icon" ]; then
    title="$icon $label"
else
    title="$label"
fi

printf '\033]0;%s\007' "$title" >"$tty" 2>/dev/null

exit 0

最初、単純にANSIエスケープシーケンスを流すだけでいいんじゃない?と思ったのですが、Claude Codeは標準出力を横取りしているので、このような複雑なことをしないとだめだそうです(Feature Request: Set terminal title from within Claude Code session)。

Claude Code のイベントフック設定

~/.claude/settings.json に次を設定します。 XXXXXXXX はご自身のユーザ名に置き換えてください。

  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/home/XXXXXXXX/.claude/tab-title.sh",
            "args": ["✅"]
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/home/XXXXXXXX/.claude/tab-title.sh",
            "args": ["⏳"]
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "permission_request",
        "hooks": [
          {
            "type": "command",
            "command": "/home/XXXXXXXX/.claude/tab-title.sh",
            "args": ["🔔"]
          }
        ]
      }
    ],
    "PermissionRequest": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/home/XXXXXXXX/.claude/tab-title.sh",
            "args": ["🔔"]
          }
        ]
      }
    ],
    "PermissionDenied": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/home/XXXXXXXX/.claude/tab-title.sh",
            "args": ["⏳"]
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/home/XXXXXXXX/.claude/tab-title.sh",
            "args": ["⏳"]
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/home/XXXXXXXX/.claude/tab-title.sh",
            "args": ["✅"]
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/home/XXXXXXXX/.claude/tab-title.sh"
          }
        ]
      }
    ]
  },

xfce4-terminalの起動スクリプト

毎回1つ1つタブを開き、それぞれで対象のプロジェクトに移動し、agmsgを手動で起動するのは面倒です。そこで、xfce4-terminalで複数のタブを開いて複数プロジェクトに対してClaude Codeを実行し、自動的に /agmsg を実行して待ち受けするスクリプトを作りました。XXXXXXXX はご自身のプロジェクトディレクトリに置き換えてください。

#!/bin/zsh

DIR=$HOME/XXXXXXXX
COMMAND='zsh -i -c "claude /agmsg"'

xfce4-terminal \
    --tab --working-directory=${DIR}/PROJECT_A -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_B -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_C -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_D -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_E -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_F -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_G -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_H -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_I -e ${COMMAND} \
    --tab --working-directory=${DIR}/PROJECT_J -e ${COMMAND} \
&

実際に動かすと、次のようになります。

タブをたくさん開いていても、状況が一目でわかるようになりました。

まとめ

本稿では、xfce4-terminalでたくさんタブを開き、それぞれでagmsgで連携したClaude Codeを動かしている場合、アクティブでないタブで人間の指示待ちが発生しても、タブタイトルを変更してアイコンを出せるようにしてみました。

ターミナルマルチプレクサを使っていれば、ここまでやらないでいいような気もしますが…使っていない環境で何とか実現できないかClaude Codeと相談しながら作ってみました。本稿でのスクリプトはタブタイトルを変更しているだけですが、音を鳴らしたりシステムトレイに通知したりと、いろいろ応用ができそうです。

もし同じような環境で同じような悩みを持っている方に参考となれば幸いです。

Author

はやりの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA