nekolas note
2.5 KB Raw
#!/usr/bin/env sh
# cc-tmux: /rename 即時追従ウォッチャ(FR-041 強化, T044)。
# 参照: contracts/hooks.md, lib/name.sh
#
# Claude Code は /rename・セッション命名時に transcript(JSONL) へ
# {"type":"custom-title","customTitle":"..."} を追記する。本スクリプトは
# transcript を tail -F で監視し、custom-title 行が追記された瞬間に
# タブ名を追従させる。追記イベントでのみ動くイベント駆動であり、
# 定期ポーリング(sleep ループでの再読込)はしない。
#
# 引数: <session_id> <window_id> <transcript_path>
# 起動: フック(SessionStart/busy)から setsid/nohup でデタッチ起動(多重防止 pidfile)。
# 終了: SessionEnd の stop_namewatch(kill)、または状態ディレクトリ消滅の検知。
set -u

CC_DIR=$(cd "$(dirname "$0")/.." && pwd)
. "$CC_DIR/lib/state.sh"
. "$CC_DIR/lib/tmux.sh"
. "$CC_DIR/lib/name.sh"

sid=${1:-}
win=${2:-}
tr_path=${3:-}
[ -n "$sid" ] && [ -n "$win" ] && [ -n "$tr_path" ] || exit 0
[ -f "$tr_path" ] || exit 0
cc_in_tmux || exit 0
command -v jq >/dev/null 2>&1 || exit 0

d=$(cc_state_dir "$sid")
[ -d "$d" ] || exit 0
pidf="$d/namewatch.pid"

# 多重防止(スピナーと同じ pidfile 方式)。
if [ -f "$pidf" ]; then
  p=$(cat "$pidf" 2>/dev/null)
  if [ -n "$p" ] && kill -0 "$p" 2>/dev/null; then
    exit 0
  fi
fi
printf '%s' "$$" >"$pidf"

fifo="$d/namewatch.fifo"
rm -f "$fifo"
mkfifo "$fifo" 2>/dev/null || exit 0

# tail は明示 pid で管理する(macOS には setsid が無くプロセスグループ kill が
# 使えないため、グループではなく個別に殺す)。
tail -n 0 -F "$tr_path" >"$fifo" 2>/dev/null &
tailpid=$!

cleanup() {
  trap - EXIT TERM INT
  kill "$tailpid" 2>/dev/null
  rm -f "$pidf" "$fifo" 2>/dev/null
}
trap cleanup EXIT
trap 'cleanup; exit 0' TERM INT

# 最新の明示名を反映(変化時のみ)。ロジックはフックの follow_name と同一。
apply() {
  n=$(name_follow "$sid" "$tr_path" 2>/dev/null) || return 0
  [ -n "$n" ] || return 0
  prev=$(state_meta "$sid" name 2>/dev/null) || prev=""
  if [ "$n" != "$prev" ]; then
    state_meta_set "$sid" name "$n"
    cc_tmux_rename_force "$win" "$n"
    cc_tmux_refresh
  fi
}

# 追記行のうち custom-title を含むものにだけ反応する。
while IFS= read -r line; do
  [ -d "$d" ] || break # SessionEnd 後(状態破棄済み)は自主終了
  case $line in
  *'"custom-title"'*) apply ;;
  esac
done <"$fifo"

exit 0