nekolas note
3.4 KB Raw
#!/usr/bin/env sh
# cc-tmux: セッション名の導出(T019/T021)。
# 参照: data-model.md(SessionName), research.md R2
#
# 導出ルール(FR-040/044):
#   1. name_locked(既に明示名あり)→ 呼び出し側で何もしない(上書き禁止)。
#   2. 名前未設定 → 「最初のユーザープロンプトの短い要約」優先、無ければ cwd ベース名。
#
# /rename 完全追従は初版では非対応(ベストエフォート, FR-041)。
# 本ファイルは bin/* から `. lib/name.sh` で読み込まれる(POSIX sh 互換)。

# 文字列を tmux タブ名向けに短く整える(前後空白除去・改行/制御除去・長さ制限)。
_name_sanitize() {
  # 改行・タブを空白へ、連続空白を1つへ、前後トリム、24 文字に切詰め。
  printf '%s' "$1" \
    | tr '\n\t' '  ' \
    | sed -E 's/  +/ /g; s/^ +//; s/ +$//' \
    | cut -c1-24
}

# transcript(JSONL)から最初のユーザープロンプトを抽出して要約。
# 取得できなければ空。jq 必須。
_name_from_transcript() {
  _nft_path=$1
  [ -n "$_nft_path" ] && [ -f "$_nft_path" ] || return 1
  command -v jq >/dev/null 2>&1 || return 1
  # 最初の user メッセージのテキストを取得(Claude Code JSONL 形式に対応)。
  # message.content が文字列 or 配列(text 要素)どちらにも対応。
  _nft_text=$(jq -r '
    select(.type == "user") |
    (.message.content) |
    if type == "string" then .
    elif type == "array" then (map(select(.type=="text") | .text) | join(" "))
    else empty end
  ' "$_nft_path" 2>/dev/null | grep -v '^[[:space:]]*$' | head -n 1)
  [ -n "$_nft_text" ] || return 1
  _name_sanitize "$_nft_text"
}

# cwd のベース名から導出。
_name_from_cwd() {
  _nfc_cwd=$1
  [ -n "$_nfc_cwd" ] || return 1
  _nfc_base=$(basename "$_nfc_cwd" 2>/dev/null)
  [ -n "$_nfc_base" ] && [ "$_nfc_base" != "/" ] || return 1
  _name_sanitize "$_nfc_base"
}

# name_derive <session_id> <cwd> <transcript_path> -> 導出名(空なら exit 1)
# 優先: 最初のプロンプト要約 → cwd ベース名。
name_derive() {
  _nd_cwd=${2:-}
  _nd_tr=${3:-}
  _nd=$(_name_from_transcript "$_nd_tr" 2>/dev/null) && [ -n "$_nd" ] && { printf '%s' "$_nd"; return 0; }
  _nd=$(_name_from_cwd "$_nd_cwd" 2>/dev/null) && [ -n "$_nd" ] && { printf '%s' "$_nd"; return 0; }
  return 1
}

# /rename・セッション名追従(FR-041/043)。
# Claude Code は /rename やセッション命名時に transcript(JSONL) へ
# {"type":"custom-title","customTitle":"<名前>"} を追記する(実機確認済み)。
# 最後の customTitle を「ユーザーの明示名」として返す。
# 旧版互換で title/summary もフォールバックとして見る。読めなければ静かに諦める。
# name_follow <session_id> <transcript_path> -> 現在名(変化検出は呼び出し側)
name_follow() {
  _nf_tr=${2:-}
  [ -n "$_nf_tr" ] && [ -f "$_nf_tr" ] || return 1
  command -v jq >/dev/null 2>&1 || return 1
  _nf_name=$(jq -r '
    select(.type? == "custom-title" and (.customTitle? != null)) | .customTitle
  ' "$_nf_tr" 2>/dev/null | grep -v '^[[:space:]]*$' | tail -n 1)
  if [ -z "$_nf_name" ]; then
    _nf_name=$(jq -r '
      select((.title? != null) or (.summary? != null)) |
      (.title // .summary)
    ' "$_nf_tr" 2>/dev/null | grep -v '^[[:space:]]*$' | tail -n 1)
  fi
  [ -n "$_nf_name" ] || return 1
  _name_sanitize "$_nf_name"
}