Preserve holistic multiplexer titles

This commit is contained in:
2026-05-06 15:05:52 -07:00
parent 544da689ab
commit aaf2ebd569
4 changed files with 60 additions and 7 deletions

View File

@@ -3,16 +3,18 @@
## Multiplexer session titling ## 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. - 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 '<project> - <task>'` to update the title. The command detects tmux vs. zellij internally, prefers tmux when both are present, and no-ops outside a multiplexer. - Use `set_multiplexer_title '<project> - <task>'` 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. - Maintain a session/window/pane title that describes the durable purpose of the overall exchange.
- Prefer automatic titling: infer a concise <task> from the current user request and context without asking. - Prefer automatic titling: infer a concise <task> 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 <task> when the new user turn is a continuation, status check, refinement, or implementation detail within the same broader objective.
- Title format: "<project> - <task>". - Title format: "<project> - <task>".
- <project> is the basename of the current project directory. - <project> is the basename of the current project directory.
- Prefer git repo root basename if available; otherwise use basename of the current working directory. - Prefer git repo root basename if available; otherwise use basename of the current working directory.
- <task> is a short, user-friendly description of what we are doing. - <task> is a short, user-friendly description of what we are doing.
- Ask for a short descriptive <task> only when the task is ambiguous or you are not confident in an inferred title. - Ask for a short descriptive <task> only when the task is ambiguous or you are not confident in an inferred title.
- When the task changes substantially, update the <task> automatically if clear; otherwise ask for an updated <task>. - When the broader objective changes substantially, update the <task> automatically if clear; otherwise ask for an updated <task>.
- When a title is provided or updated, immediately run `set_multiplexer_title '<project> - <task>'`; do not call raw tmux or zellij rename commands unless debugging the helper itself. - When a title is provided or updated, immediately run `set_multiplexer_title '<project> - <task>'`; 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 ## Pane usage
- Do not create extra panes or windows unless the user asks. - Do not create extra panes or windows unless the user asks.

View File

@@ -16,10 +16,13 @@ sys.stdout.write(cwd)
sys.stdout.write("\0") sys.stdout.write("\0")
sys.stdout.write(prompt) sys.stdout.write(prompt)
sys.stdout.write("\0") sys.stdout.write("\0")
sys.stdout.write(str(data.get("session_id") or ""))
sys.stdout.write("\0")
PY PY
) )
cwd="${parsed[0]:-}" cwd="${parsed[0]:-}"
prompt="${parsed[1]:-}" prompt="${parsed[1]:-}"
session_id="${parsed[2]:-}"
if [[ -z "${cwd}" ]]; then if [[ -z "${cwd}" ]]; then
cwd="$PWD" cwd="$PWD"
@@ -46,6 +49,17 @@ if [[ -z "$task" ]]; then
task="work" task="work"
fi 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. # Trim to a reasonable length for multiplexer UI labels.
if [[ ${#task} -gt 60 ]]; then if [[ ${#task} -gt 60 ]]; then
task="${task:0:57}..." task="${task:0:57}..."
@@ -53,9 +67,42 @@ fi
title="$project - $task" 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 if command -v set_multiplexer_title >/dev/null 2>&1; then
set_multiplexer_title "$title" set_multiplexer_title "$title"
else else
hook_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) hook_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
"$hook_dir/../../lib/functions/set_multiplexer_title" "$title" "$hook_dir/../../lib/functions/set_multiplexer_title" "$title"
fi fi
if [[ -n "$hook_state_file" ]]; then
mkdir -p "$(dirname "$hook_state_file")"
printf '%s' "$title" > "$hook_state_file"
fi

View File

@@ -7,14 +7,16 @@
## Multiplexer session titling ## 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. - 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 '<project> - <task>'` to update the title. The command detects tmux vs. zellij internally, prefers tmux when both are present, and no-ops outside a multiplexer. - Use `set_multiplexer_title '<project> - <task>'` 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. - Maintain a session/window/pane title that describes the durable purpose of the overall exchange.
- Prefer automatic titling: infer a concise <task> from the current user request and context without asking. - Prefer automatic titling: infer a concise <task> 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 <task> when the new user turn is a continuation, status check, refinement, or implementation detail within the same broader objective.
- Title format: "<project> - <task>". - Title format: "<project> - <task>".
- <project> is the basename of the current project directory. - <project> is the basename of the current project directory.
- Prefer git repo root basename if available; otherwise use basename of the current working directory. - Prefer git repo root basename if available; otherwise use basename of the current working directory.
- <task> is a short, user-friendly description of what we are doing. - <task> is a short, user-friendly description of what we are doing.
- Ask for a short descriptive <task> only when the task is ambiguous or you are not confident in an inferred title. - Ask for a short descriptive <task> only when the task is ambiguous or you are not confident in an inferred title.
- When the task changes substantially, update the <task> automatically if clear; otherwise ask for an updated <task>. - When the broader objective changes substantially, update the <task> automatically if clear; otherwise ask for an updated <task>.
- When a title is provided or updated, immediately run `set_multiplexer_title '<project> - <task>'`; do not call raw tmux or zellij rename commands unless debugging the helper itself. - When a title is provided or updated, immediately run `set_multiplexer_title '<project> - <task>'`; do not call raw tmux or zellij rename commands unless debugging the helper itself.
## Pane usage ## Pane usage

View File

@@ -1,5 +1,7 @@
#!/usr/bin/env sh #!/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() { set_multiplexer_title() {
if [ "$#" -lt 1 ]; then if [ "$#" -lt 1 ]; then
echo "usage: set_multiplexer_title <title>" >&2 echo "usage: set_multiplexer_title <title>" >&2