#tmux #terminal #project #tmux-session

app devs-cli

Project-aware tmux session manager with Claude Code session tracking

2 unstable releases

Uses new Rust 2024

new 0.2.0 Mar 10, 2026
0.1.0 Mar 8, 2026

#207 in Development tools

MIT license

250KB
6.5K SLoC

devs

CI crates.io License: MIT

A project-aware tmux session manager that remembers your layouts, tracks Claude Code sessions, and sets iTerm2 tab colors.

What is this?

When you work across multiple projects, each with its own tmux session, editor, terminals, and Claude Code conversations, a machine restart wipes out all of that context. You lose the pane layouts, the Claude session IDs, and the mental breadcrumbs about where you left off.

devs fixes this. It treats projects as a first-class concept on top of tmux:

  • Register a project once with its path and preferred layout
  • Open it with a single command -- tmux session, pane layout, tab color, all set up
  • Save and restore exact pane arrangements after a reboot
  • Track Claude Code sessions per project so you can resume the right conversation
  • Jot down fleeting notes so you remember what you were doing

It is additive, not a cage. Direct tmux interaction is never blocked. devs adds project awareness on top of tmux; it does not replace it.

Features

  • Declarative layouts -- define pane arrangements in TOML; devs open builds them
  • Save and restore -- capture your live tmux layout and restore it exactly after a reboot
  • iTerm2 tab colors -- each project gets a color, set automatically via escape sequences
  • Claude Code session tracking -- record which Claude sessions belong to which project, with labels and active/done status
  • Fleeting notes -- timestamped scratchpad per project for context breadcrumbs
  • Simple storage -- TOML files in ~/.config/devs/, split into portable config (syncable across machines) and machine-local data

Installation

Prerequisites

  • tmux (3.2+ recommended for tab color passthrough)
  • iTerm2 (optional -- tab colors are silently ignored in other terminals)

Homebrew (macOS and Linux)

brew tap julianmateu/devs
brew install devs

Cargo (any platform with Rust)

cargo install devs-cli

Build from source

git clone https://github.com/julianmateu/devs-cli.git
cd devs-cli
cargo install --path .

The binary is named devs.

Development setup

After cloning, configure git to use the tracked hooks:

git config core.hooksPath .githooks

This enables the pre-commit hook which runs cargo fmt --check, cargo clippy, and cargo test before each commit.

tmux configuration

For tab colors to work inside tmux, you need to enable escape sequence passthrough. Add this to your ~/.tmux.conf:

set -g allow-passthrough on

Without this, tmux silently discards the escape sequences that set tab colors.

Quick start

# 1. Register a project
devs new my-api --path ~/src/my-api --color "#61afef"

# 2. Edit the config to define your layout
devs edit my-api

The config file opens in $EDITOR. Add a layout:

[project]
name = "my-api"
path = "~/src/my-api"
color = "#61afef"
created_at = "2026-03-05T10:00:00Z"

[layout.main]
cmd = "nvim"

[[layout.panes]]
split = "right"
cmd = "claude"
size = "40%"

[[layout.panes]]
split = "bottom-right"

This creates three panes: nvim on the left, Claude Code on the top-right, and a shell on the bottom-right.

# 3. Open the project (creates tmux session + sets tab color)
devs open my-api

# 4. Work, resize panes, split new ones...

# 5. Save the current layout
devs save my-api

# 6. After a reboot, reopen -- saved layout is restored automatically
devs open my-api

Commands

Most project-scoped commands accept an optional project name. When omitted, the project is inferred from your current directory by matching against registered project paths. This works from the project root or any subdirectory.

Project management

Command Description
devs new <name> [--path <path>] [--color <hex>] Register a new project
devs new <name> --from <project> Copy layout from an existing project
devs new <name> --from-session <session> Capture layout from a live tmux session
devs init [name] Export project config to a shareable .devs.toml
devs list List all registered projects
devs status Show all projects with live tmux/Claude status
devs config [name] Print merged config (portable + local) to stdout
devs edit [name] [--local] Open config in $EDITOR (--local for machine-local)
devs remove [name] --force [--kill] Remove a project (--kill to also kill tmux session)

devs new accepts --session LABEL:ID (repeatable) to pre-populate Claude sessions. --path defaults to the current directory and expands ~ to $HOME. If a .devs.toml file exists in the project directory, its color and layout are picked up automatically (explicit flags override).

devs new frontend --path ~/src/frontend --color "#e06c75"
cd ~/src/frontend && devs new frontend     # --path defaults to CWD
devs new fork --from frontend --session "main:abc123"
devs new captured --path ~/src/captured --from-session frontend
devs init frontend                         # export config to .devs.toml
devs list
devs status
devs config frontend
devs edit frontend
devs remove frontend --force --kill

Session management

Command Description
devs open [name] Create or attach to a tmux session
devs open [name] --default Always use the declarative layout
devs open [name] --saved Always use the saved state (error if none)
devs save [name] Snapshot the current tmux layout
devs save [name] --as-default Save current layout as the declarative default
devs close [name] [--save] Kill tmux session and reset tab color
devs reset [name] Discard saved state, revert to declarative layout

devs open is idempotent: if the tmux session already exists, it attaches to it. If saved state exists and no flags are given, it restores the saved layout. Use --default to force the declarative layout instead.

In layout pane commands, claude and claude:<label> are expanded automatically -- claude:brainstorm starts or resumes a Claude session with label "brainstorm".

devs open my-api
devs save my-api
devs save my-api --as-default   # capture current layout as the declarative default
devs close my-api --save        # save layout, kill session, reset tab color
devs open my-api --default   # ignore saved state, use config layout
devs reset my-api            # discard saved state entirely

Claude Code session tracking

Command Description
devs claude [name] <label> Launch a new Claude session with a label
devs claude [name] --resume <label> Resume an existing Claude session
devs claudes [name] List active Claude sessions for a project
devs claudes [name] --all Include completed sessions
devs claude-done [name] <label> Mark a Claude session as done

When you run devs open, active Claude sessions are printed as hints so you know what to resume.

devs claude my-api "implement auth middleware"
devs claudes my-api
devs claude my-api --resume "implement auth middleware"
devs claude-done my-api "implement auth middleware"

Notes

Command Description
devs note [name] <message> Add a timestamped note
devs notes [name] Show last 20 notes
devs notes [name] --all Show all notes
devs notes [name] --since 2d Filter by time
devs notes [name] --clear --force Delete all notes (force required if non-empty)

Notes are fleeting breadcrumbs, not tasks. They help you remember where you left off after a context switch.

devs note my-api "picking up from step 4 of the migration"
devs note my-api "blocked on API key, asked Sarah"
devs notes my-api

Global

Command Description
devs completions <shell> Generate shell completions
devs tmux-help Print tmux quick reference
devs generate-man <output-dir> Generate man pages
devs --version Print version
devs --help Print help for all commands
devs <command> --help Print help for a specific command

Shell completions

Dynamic completions complete subcommands, flags, and project names. Add one line to your shell config. Run devs completions --help to see these instructions at any time.

Zsh — add to ~/.zshrc:

source <(COMPLETE=zsh devs)

Bash — add to ~/.bashrc:

source <(COMPLETE=bash devs)

Fish — add to ~/.config/fish/config.fish:

source (COMPLETE=fish devs | psub)

Static fallback

If dynamic completions don't work on your system, you can generate static completions (subcommands and flags only, no project names):

Static setup instructions

Oh My Zsh:

mkdir -p ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/devs
devs completions zsh > ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/devs/_devs

Then add devs to the plugins=(...) list in your ~/.zshrc and restart your shell.

Vanilla zsh:

mkdir -p ~/.zfunc
devs completions zsh > ~/.zfunc/_devs

Add to your ~/.zshrc (before compinit):

fpath=(~/.zfunc $fpath)
autoload -Uz compinit && compinit

Bash:

mkdir -p ~/.local/share/bash-completion/completions
devs completions bash > ~/.local/share/bash-completion/completions/devs

Fish:

devs completions fish > ~/.config/fish/completions/devs.fish

Shareable config (.devs.toml)

Place a .devs.toml file in a project's root directory to define a shareable layout and color. When devs new is run from that directory (or with --path pointing to it), the settings are picked up automatically.

# .devs.toml
color = "#61afef"

[layout.main]
cmd = "nvim"

[[layout.panes]]
split = "right"
cmd = "claude"
size = "40%"

[[layout.panes]]
split = "bottom-right"

Explicit flags (--color, --from, --from-session) override .devs.toml values.

To export an existing project's config to .devs.toml:

devs init my-api

This writes the project's color and layout to .devs.toml in the project's directory, so team members can pick it up with devs new.

Configuration

Config is split into portable (syncable) and machine-local files under ~/.config/devs/.

Portable config (projects/<name>.toml)

Metadata, layout, and notes. Paths under $HOME use tilde form (~/...).

[project]
name = "my-api"
path = "~/src/my-api"
color = "#61afef"
created_at = "2026-03-05T10:00:00Z"

[layout.main]
cmd = "nvim"

[[layout.panes]]
split = "right"
cmd = "claude"
size = "40%"

[[layout.panes]]
split = "bottom-right"

[[notes]]
content = "picking up from step 4 of the migration"
created_at = "2026-03-03T10:15:00Z"

Machine-local config (local/<name>.toml)

Claude sessions and saved tmux state. Not synced.

[[claude_sessions]]
id = "session_abc123"
label = "implement auth middleware"
started_at = "2026-03-01T10:00:00Z"
status = "active"

[[claude_sessions]]
id = "session_def456"
label = "initial exploration"
started_at = "2026-02-28T09:00:00Z"
status = "done"
finished_at = "2026-02-28T17:00:00Z"

[last_state]
captured_at = "2026-03-03T16:00:00Z"
layout_string = "5aed,176x79,0,0[176x59,0,0,0,176x19,0,60{87x19,0,60,1,88x19,88,60,2}]"

[[last_state.panes]]
index = 0
path = "/Users/you/src/my-api"
command = "nvim"

[[last_state.panes]]
index = 1
path = "/Users/you/src/my-api"
command = "claude"

[[last_state.panes]]
index = 2
path = "/Users/you/src/my-api"
command = "zsh"

Multi-machine sync

The portable config directory can be synced via git. Machine-local data is auto-gitignored.

cd ~/.config/devs
git init && git add -A && git remote add origin <your-repo> && git push

See docs/data-model.md for full details.

Layout split directions

Value Meaning
right Vertical split to the right
bottom Horizontal split below
bottom-right Horizontal split below the rightmost pane

The main pane is always the first pane, created with the tmux session. Additional panes are created in order, each splitting relative to the previous active pane.

Architecture

devs follows a ports-and-adapters (hexagonal) architecture. Domain logic is pure and has no I/O dependencies. Infrastructure (TOML persistence, tmux commands, terminal escape sequences) is behind trait boundaries.

src/
  domain/       Pure business logic and types. No I/O.
  ports/        Trait definitions (ProjectRepository, TmuxAdapter, TerminalAdapter)
  adapters/     Implementations (TOML files, shell tmux commands, OSC escapes)
  cli/          Command handlers, receiving traits not concrete types
  main.rs       Composition root + dynamic completions (CompleteEnv)

Notable modules:

  • cli/resolve.rs — CWD-based project name inference (matches CWD against registered project paths)
  • main.rs — uses clap_complete::env::CompleteEnv for dynamic shell completions (project names, subcommands, flags)

See docs/design.md for design decisions and docs/data-model.md for the storage format.

License

MIT

Dependencies

~3–14MB
~104K SLoC