diff --git a/dotfiles/agents/AGENTS.md b/dotfiles/agents/AGENTS.md index d4ab0f58..8e6d54c7 100644 --- a/dotfiles/agents/AGENTS.md +++ b/dotfiles/agents/AGENTS.md @@ -3,16 +3,18 @@ ## Multiplexer session titling - If the `TMUX` or `ZELLIJ` environment variable is set, treat this chat as the controller for the current tmux or zellij session. - Use `set_multiplexer_title ' - '` to update the title. The command detects tmux vs. zellij internally, prefers tmux when both are present, and no-ops outside a multiplexer. -- Maintain a session/window/pane title that updates when the task focus changes substantially. -- Prefer automatic titling: infer a concise from the current user request and context without asking. +- Maintain a session/window/pane title that describes the durable purpose of the overall exchange. +- Prefer automatic titling: infer a concise from the current user request and the existing chat context without asking. +- Choose holistic titles over granular turn summaries. The title should answer "what has this chat been for?" rather than describe the latest command, substep, clarification, or follow-up message. +- Preserve the existing when the new user turn is a continuation, status check, refinement, or implementation detail within the same broader objective. - Title format: " - ". - is the basename of the current project directory. - Prefer git repo root basename if available; otherwise use basename of the current working directory. - is a short, user-friendly description of what we are doing. - Ask for a short descriptive only when the task is ambiguous or you are not confident in an inferred title. -- When the task changes substantially, update the automatically if clear; otherwise ask for an updated . +- When the broader objective changes substantially, update the automatically if clear; otherwise ask for an updated . - When a title is provided or updated, immediately run `set_multiplexer_title ' - '`; do not call raw tmux or zellij rename commands unless debugging the helper itself. -- For Claude Code sessions, a UserPromptSubmit hook will also update titles automatically based on the latest prompt. +- For Claude Code sessions, a UserPromptSubmit hook may initialize titles automatically from the first substantive prompt, but it should not keep overwriting an established same-project title with the latest prompt. ## Pane usage - Do not create extra panes or windows unless the user asks. diff --git a/dotfiles/agents/hooks/tmux-title.sh b/dotfiles/agents/hooks/tmux-title.sh index b45bce92..c76f9f2e 100755 --- a/dotfiles/agents/hooks/tmux-title.sh +++ b/dotfiles/agents/hooks/tmux-title.sh @@ -16,10 +16,13 @@ sys.stdout.write(cwd) sys.stdout.write("\0") sys.stdout.write(prompt) sys.stdout.write("\0") +sys.stdout.write(str(data.get("session_id") or "")) +sys.stdout.write("\0") PY ) cwd="${parsed[0]:-}" prompt="${parsed[1]:-}" +session_id="${parsed[2]:-}" if [[ -z "${cwd}" ]]; then cwd="$PWD" @@ -46,6 +49,17 @@ if [[ -z "$task" ]]; then task="work" fi +explicit_retitle=false +case "$lower" in + "new task:"*|"new topic:"*|"switch topic:"*|"switch context:"*|"rename title:"*|"title:"*) + explicit_retitle=true + task=$(printf '%s' "$prompt_first_line" | sed -E 's/^[^:]+:[[:space:]]*//') + if [[ -z "$task" ]]; then + task="work" + fi + ;; +esac + # Trim to a reasonable length for multiplexer UI labels. if [[ ${#task} -gt 60 ]]; then task="${task:0:57}..." @@ -53,9 +67,42 @@ fi title="$project - $task" +# The hook only sees the newest prompt, not the full conversation. Avoid +# degrading a useful same-project title into a granular follow-up summary. +if [[ -n "${TMUX:-}" ]]; then + multiplexer="tmux" +elif [[ -n "${ZELLIJ:-}" ]]; then + multiplexer="zellij" +else + multiplexer="" +fi + +hook_state_file="" +if [[ -n "$multiplexer" ]]; then + state_dir="${HOME}/.agents/state" + if [[ -n "$session_id" ]]; then + safe_session_id=$(printf '%s' "$session_id" | tr -c '[:alnum:]_.-' '_') + hook_state_file="${state_dir}/${multiplexer}-title-hook-${safe_session_id}" + else + hook_state_file="${state_dir}/${multiplexer}-title" + fi + + if [[ -f "$hook_state_file" ]]; then + established_title=$(cat "$hook_state_file" 2>/dev/null || true) + if [[ "$established_title" == "$project - "* && "$established_title" != "$title" && "$explicit_retitle" != true ]]; then + exit 0 + fi + fi +fi + if command -v set_multiplexer_title >/dev/null 2>&1; then set_multiplexer_title "$title" else hook_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) "$hook_dir/../../lib/functions/set_multiplexer_title" "$title" fi + +if [[ -n "$hook_state_file" ]]; then + mkdir -p "$(dirname "$hook_state_file")" + printf '%s' "$title" > "$hook_state_file" +fi diff --git a/dotfiles/config/taffybar/AGENTS.md b/dotfiles/config/taffybar/AGENTS.md index c765218d..2975d37c 100644 --- a/dotfiles/config/taffybar/AGENTS.md +++ b/dotfiles/config/taffybar/AGENTS.md @@ -7,14 +7,16 @@ ## Multiplexer session titling - If the `TMUX` or `ZELLIJ` environment variable is set, treat this chat as the controller for the current tmux or zellij session. - Use `set_multiplexer_title ' - '` to update the title. The command detects tmux vs. zellij internally, prefers tmux when both are present, and no-ops outside a multiplexer. -- Maintain a session/window/pane title that updates when the task focus changes substantially. -- Prefer automatic titling: infer a concise from the current user request and context without asking. +- Maintain a session/window/pane title that describes the durable purpose of the overall exchange. +- Prefer automatic titling: infer a concise from the current user request and the existing chat context without asking. +- Choose holistic titles over granular turn summaries. The title should answer "what has this chat been for?" rather than describe the latest command, substep, clarification, or follow-up message. +- Preserve the existing when the new user turn is a continuation, status check, refinement, or implementation detail within the same broader objective. - Title format: " - ". - is the basename of the current project directory. - Prefer git repo root basename if available; otherwise use basename of the current working directory. - is a short, user-friendly description of what we are doing. - Ask for a short descriptive only when the task is ambiguous or you are not confident in an inferred title. -- When the task changes substantially, update the automatically if clear; otherwise ask for an updated . +- When the broader objective changes substantially, update the automatically if clear; otherwise ask for an updated . - When a title is provided or updated, immediately run `set_multiplexer_title ' - '`; do not call raw tmux or zellij rename commands unless debugging the helper itself. ## Pane usage diff --git a/dotfiles/lib/functions/set_multiplexer_title b/dotfiles/lib/functions/set_multiplexer_title index b35f6b23..5e1f7008 100755 --- a/dotfiles/lib/functions/set_multiplexer_title +++ b/dotfiles/lib/functions/set_multiplexer_title @@ -1,5 +1,7 @@ #!/usr/bin/env sh +# Low-level setter only. Callers should pass a holistic, conversation-level +# title; this helper intentionally does not infer titles from prompts. set_multiplexer_title() { if [ "$#" -lt 1 ]; then echo "usage: set_multiplexer_title " >&2