New script that launches Claude Code (via claude-profile) inside a detached tmux session with --remote-control enabled, in a project's directory, named after the project (Title-cased, auto-incrementing #N on collision). Lets a session be started for later attach over VPN or control from the Claude app. Spec-first per repo conventions: specs/claude-tmux.spec.md, script, and tests/test-claude-tmux.sh (18 dryrun assertions, all passing). Claude-Session: https://claude.ai/code/session_014oa7T7z1h5n34vu8vX9zGT
4.6 KiB
4.6 KiB
claude-tmux
Purpose
Spawn a detached tmux session running Claude Code (via claude-profile) with
Remote Control enabled, in a project's directory, with the session named after
the project so it is easy to tmux attach to and easy to identify in the Claude
app.
Usage
claude-tmux [PROJECT] [OPTIONS]
Arguments
| Argument | Default | Description |
|---|---|---|
PROJECT |
(prompt if TTY) | Project name (folder under ~/dev/claude or ~/dev/claude/projects), or an explicit path to a directory |
Flags
| Flag | Default | Description |
|---|---|---|
--mode <name> |
profile's last-mode, else quick |
Engagement mode passed to claude-profile |
--profile <name> |
oreillyit-anthropic |
Claude Code profile (resolves to ~/.claude-<name>, or ~/.claude for default) |
--name <display> |
Title-cased project name | Override the session / Remote-Control name |
--attach |
off | Attach (or switch-client if already inside tmux) after spawning |
--dryrun, -n |
off | Preview all actions without creating anything |
--help, -h |
Show usage information |
Behaviour
- Resolve the profile directory (
~/.claude-<profile>, or~/.claudefordefault). Error if it does not exist. - Resolve the project. If
PROJECTis empty and stdin is a TTY, prompt for it; if empty and not a TTY, error. Resolve the folder by trying, in order: an existing path,~/dev/claude/<PROJECT>,~/dev/claude/projects/<PROJECT>. Error if none exist. - Resolve the engagement mode. If
--modeis not given, read the profile'slast-modefile, falling back toquick. The mode is always resolved explicitly and passed toclaude-profile— the launcher must never leaveclaude-profileto show its interactive mode picker, because Remote Control only registers in the app onceclaudeis actually running. - Derive the base session name: Title-Case the project folder basename
(
agent-runtimes→Agent Runtimes), unless--nameoverrides it. - Auto-increment: if a tmux session with the base name already exists, append
#2,#3, … until a free name is found. The first session gets no suffix. - Create a detached tmux session named after the resolved name, with its working directory set to the project folder.
- Send the launch command into the session's pane (sent literally, followed by
Enter):
claude-profile <profile> --mode <mode> --project <basename> -- --remote-control "<name>" - Print the attach hint and the Remote-Control name. If
--attachis set, attach (orswitch-clientwhen already inside tmux).
claude-profile is located via PATH, falling back to a sibling of this script.
Dryrun Behaviour
When --dryrun / -n is passed, no tmux session is created and nothing is sent.
Output (values substituted):
[dryrun] Project: agent-runtimes (/home/paul/dev/claude/projects/agent-runtimes)
[dryrun] Profile: oreillyit-anthropic (/home/paul/.claude-oreillyit-anthropic)
[dryrun] Mode: deep
[dryrun] Session name: Agent Runtimes
[dryrun] Would run: tmux new-session -d -s "Agent Runtimes" -c "/home/paul/dev/claude/projects/agent-runtimes"
[dryrun] Would send: claude-profile oreillyit-anthropic --mode deep --project agent-runtimes -- --remote-control Agent\ Runtimes
[dryrun] Attach with: tmux attach -t "Agent Runtimes"
Edge Cases
| Case | Handling |
|---|---|
| No PROJECT and stdin is a TTY | Prompt Project name: |
| No PROJECT and not a TTY (e.g. run by an agent) | Error: "No project given…", exit 1 |
| Project folder not found | Error naming the searched locations, exit 1 |
| Profile directory not found | Error, exit 1 |
| Session name already taken | Auto-increment with #N suffix |
| Project name has underscores | Treated like hyphens for Title-Casing |
claude-profile not found |
Error, exit 1 |
| Two concurrent sessions on the same profile | Both work: claude-profile rewrites the shared active-mode.env, but each session's cwd takes precedence for project auto-selection, so the correct project is still picked |
Examples
# Spawn a Remote-Control session for agent-runtimes (Title-cased, mode from last-mode)
$ claude-tmux agent-runtimes
Spawned tmux session: Agent Runtimes
attach: tmux attach -t "Agent Runtimes"
# A second one auto-increments
$ claude-tmux agent-runtimes
Spawned tmux session: Agent Runtimes #2
# Override the mode, and attach immediately
$ claude-tmux cluster-bootstrap --mode deep --attach
# Preview only
$ claude-tmux dns-manager --dryrun