#!/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"
}