Skip to content

Commit d83a99e

Browse files
committed
Enhance session management in team.sh and update documentation
- Updated `bin/init.sh` to append `.agents/.claude-session-id.*` to `.gitignore`, ensuring local session IDs are not committed. - Revised `docs/TEAM.md` to clarify the behavior of session resumption, emphasizing the use of pinned UUIDs for each tmux session. - Modified `templates/scripts/team.sh.tmpl` to implement UUID generation for session IDs, improving the management of Claude conversations across tmux sessions. These changes aim to enhance user experience by ensuring that each tmux session maintains its own conversation context, preventing ambiguity in session management.
1 parent 43ab52e commit d83a99e

3 files changed

Lines changed: 73 additions & 22 deletions

File tree

‎bin/init.sh‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,12 @@ if [ "$FRESH_SCAFFOLD" -eq 1 ] && [ ! -f "$STAMP" ]; then
392392
printf 'init.sh: wrote %s\n' "$STAMP"
393393
fi
394394

395-
# .agents/.oc-port is local machine state (which port scripts/team.sh last
396-
# bound), never something to commit. Append-if-missing when the target is a
397-
# git repo — additive only, in keeping with this script's never-overwrite
398-
# stance; a project that ignores it differently is left alone.
395+
# .agents/.oc-port and .agents/.claude-session-id.* are local machine state
396+
# (which port scripts/team.sh last bound; which Claude conversation each
397+
# tmux session name is pinned to), never something to commit.
398+
# Append-if-missing when the target is a git repo — additive only, in
399+
# keeping with this script's never-overwrite stance; a project that ignores
400+
# these differently is left alone.
399401
if [ -d "$TARGET/.git" ]; then
400402
GITIGNORE="$TARGET/.gitignore"
401403
if ! grep -qxF '.agents/.oc-port' "$GITIGNORE" 2>/dev/null; then
@@ -405,6 +407,13 @@ if [ -d "$TARGET/.git" ]; then
405407
} >> "$GITIGNORE"
406408
printf 'init.sh: added .agents/.oc-port to %s\n' "$GITIGNORE"
407409
fi
410+
if ! grep -qxF '.agents/.claude-session-id.*' "$GITIGNORE" 2>/dev/null; then
411+
{
412+
printf '\n# local Claude session ids pinned per tmux session name by scripts/team.sh\n'
413+
printf '.agents/.claude-session-id.*\n'
414+
} >> "$GITIGNORE"
415+
printf 'init.sh: added .agents/.claude-session-id.* to %s\n' "$GITIGNORE"
416+
fi
408417
fi
409418

410419
cat <<MSG

‎docs/TEAM.md‎

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,26 @@ Flags can combine with a session name in any order:
1818

1919
## Resuming is the default
2020

21-
Pane 0 runs `claude --continue`, which picks up the most recent
22-
conversation in this project's directory — not a fresh one. This exists
23-
because killing the tmux session (the usual way to free up a port, or just
24-
closing the terminal) used to also throw away the lead's context, with no
25-
way back into the same conversation. Now: kill it, come back later, run
26-
`scripts/team.sh` again, the lead is where you left it.
21+
Pane 0 resumes the lead's own conversation — not a fresh one — but it does
22+
so by pinning to a stored UUID (`.agents/.claude-session-id.<session-name>`,
23+
written the first time that session name runs) rather than
24+
`claude --continue`. `--continue` only means "the most recent conversation
25+
in this directory," with no notion of *which* tmux session started it, so
26+
running a second `scripts/team.sh` session name against the same repo (or
27+
just running a one-off `claude` there for something unrelated) could make
28+
the next `--continue` resume the wrong thread entirely. The pinned id
29+
removes that ambiguity: each session name always resumes its own
30+
conversation via `claude --resume <uuid>`, however many other Claude
31+
conversations have happened in the same directory since.
32+
33+
This exists because killing the tmux session (the usual way to free up a
34+
port, or just closing the terminal) used to also throw away the lead's
35+
context, with no way back into the same conversation. Now: kill it, come
36+
back later, run `scripts/team.sh` again, the lead is where you left it.
2737

2838
Pass `--fresh` on the rare run where you actually want a clean slate
29-
instead.
39+
instead — this mints a new pinned id, so later resumes follow the new
40+
conversation, not the old one.
3041

3142
## Running two projects at once
3243

‎templates/scripts/team.sh.tmpl‎

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@
1515
# scripts/team.sh --port <N> opencode server port (default 4096)
1616
# scripts/team.sh --kill [session-name]
1717
#
18-
# Resuming is the default, not an opt-in: pane 0 runs `claude --continue`,
19-
# which picks up the most recent conversation in this project's directory.
20-
# That matters because killing this tmux session (the usual way to free up
21-
# a port so a second project can use scripts/team.sh at the same time) used
22-
# to also throw away the lead's context — there was no way back into the
23-
# same conversation short of scrolling terminal history. `--continue` fixes
24-
# that: kill the session, come back later, run scripts/team.sh again, the
25-
# lead picks up where it left off. Pass --fresh on the rare run where you
26-
# actually want a clean slate.
18+
# Resuming is the default, not an opt-in: pane 0 resumes the lead's own
19+
# Claude conversation, pinned to this session name via a stored UUID (see
20+
# .agents/.claude-session-id.<session-name>) rather than `claude --continue`
21+
# (which just means "the most recent conversation in this directory" — with
22+
# no notion of *which* tmux session started it, so a second team.sh session
23+
# name in the same repo, or even a one-off `claude` run there, could steal
24+
# the next --continue). That matters because killing this tmux session (the
25+
# usual way to free up a port so a second project can use scripts/team.sh at
26+
# the same time) used to also throw away the lead's context — there was no
27+
# way back into the same conversation short of scrolling terminal history.
28+
# The pinned session id fixes that: kill the session, come back later, run
29+
# scripts/team.sh again, the lead picks up its own thread — never someone
30+
# else's. Pass --fresh on the rare run where you actually want a clean
31+
# slate (this also mints a new pinned id, so future resumes follow it).
2732
#
2833
# Running two projects at once: opencode's server binds one port per
2934
# process, so two projects both defaulting to --port 4096 collide — the
@@ -66,6 +71,19 @@ fi
6671

6772
command -v tmux >/dev/null 2>&1 || { echo "team.sh: tmux not found" >&2; exit 1; }
6873

74+
gen_uuid() {
75+
if command -v uuidgen >/dev/null 2>&1; then
76+
uuidgen | tr '[:upper:]' '[:lower:]'
77+
elif [ -r /proc/sys/kernel/random/uuid ]; then
78+
cat /proc/sys/kernel/random/uuid
79+
elif command -v python3 >/dev/null 2>&1; then
80+
python3 -c 'import uuid; print(uuid.uuid4())'
81+
else
82+
echo "team.sh: need uuidgen or python3 to mint a Claude session id" >&2
83+
exit 1
84+
fi
85+
}
86+
6987
if tmux has-session -t "$SESSION" 2>/dev/null; then
7088
echo "team.sh: session '$SESSION' already exists — attaching"
7189
exec tmux attach -t "$SESSION"
@@ -74,8 +92,21 @@ fi
7492
mkdir -p "$REPO_ROOT/.agents"
7593
printf '%s' "$PORT" > "$REPO_ROOT/.agents/.oc-port"
7694

77-
CLAUDE_CMD="claude --continue"
78-
[ "$FRESH" -eq 1 ] && CLAUDE_CMD="claude"
95+
# Pin pane 0 to one Claude conversation per tmux session name (see usage
96+
# comment above for why --continue alone isn't enough).
97+
SESSION_ID_FILE="$REPO_ROOT/.agents/.claude-session-id.$SESSION"
98+
[ "$FRESH" -eq 1 ] && rm -f "$SESSION_ID_FILE"
99+
100+
if [ -f "$SESSION_ID_FILE" ]; then
101+
CLAUDE_UUID="$(cat "$SESSION_ID_FILE")"
102+
# Fall back to re-minting under the same id if the stored conversation is
103+
# gone (history pruned, project path changed) so the pane never just dies.
104+
CLAUDE_CMD="claude --resume $CLAUDE_UUID || claude --session-id $CLAUDE_UUID"
105+
else
106+
CLAUDE_UUID="$(gen_uuid)"
107+
printf '%s' "$CLAUDE_UUID" > "$SESSION_ID_FILE"
108+
CLAUDE_CMD="claude --session-id $CLAUDE_UUID"
109+
fi
79110

80111
tmux new-session -d -s "$SESSION" -c "$REPO_ROOT" -n team
81112

0 commit comments

Comments
 (0)