Metadata-Version: 2.4
Name: ctx-tui
Version: 0.7.2
Summary: Manage repo-scoped work contexts: fresh checkouts wrapped in terminal multiplexer sessions
License-Expression: MIT
License-File: LICENSE
Requires-Dist: click>=8.1
Requires-Dist: textual>=8.2.8
Requires-Python: >=3.13
Description-Content-Type: text/markdown

# ctx

The way I write code has changed. With agents, I context-switch more, and the
time it takes to spin up or tear down a context became increasingly noticeable.
I didn't enjoy the `git worktree` UX, so I ended up maintaining multiple
long-lived clones of the same repository nestled in different tmux sessions to
work in parallel. This worked well for me, but it wasn't exactly elegant. So
here we are.

`ctx` lets you manage repo-scoped work contexts: each context is a fresh full
checkout on its own local branch, wrapped in a terminal multiplexer session
with your panes already laid out.


Disclaimer: this is fully vibe-coded.

## Install

```sh
uv tool install ctx-tui
```

## Setup

`ctx` runs fine with zero config; setup is optional but will likely improve
your experience. The fastest way through it is to not do it yourself: tell your
agent "set up ctx following its README" and let it work through this section.

Instructions for the agent: ask the user each question, then apply their
answer using the concrete snippets under Configuration.

1. Which multiplexer do you use, tmux or zellij? Set `multiplexer` in
   the config file.
2. Which panes should every context session open with (agent, editor,
   lazygit, ...), and which gets focus? Write the `layout` table.
3. Which additional status columns do you want? For the `github` builtin, check
   `gh auth status` and whether the terminal uses a nerd font; for the `agent`
   builtin, install the hooks from the Agent builtin section.
4. Which key should summon the picker? Bind it in the multiplexer's config
   as shown under Instant picker.
5. Verify: register a repo (`ctx repo add`), create a context (`ctx new`),
   and check that `ctx list` shows the configured columns.

## Usage

Run `ctx` to manage contexts and repos interactively in the TUI (`?` lists the
keybindings). The TUI is meant to be summoned from inside a multiplexer
session: hit the picker key (see Instant picker) and it opens as a floating
pane over the session you're in.

You can also use `ctx` as a CLI:

```sh
ctx repo add https://github.com/Michael-JB/papaya-nvim.git   # once per repo
ctx new papaya-nvim my-cool-feature   # fresh checkout + session, jump in
# ...work, commit, push...
ctx archive my-cool-feature           # set it aside for later...
ctx rm my-cool-feature                # ...or tear it down
```

More detail:

```sh
# List contexts with their repo, branch, and status:
ctx list

# Contexts branch off the up-to-date default branch. To base one on another branch:
ctx new papaya-nvim follow-up -b other-base

# Re-attach to a context, unarchiving it and recreating its session if needed:
ctx open my-cool-feature

# Show, set, or clear the default repo for new contexts:
ctx repo default
ctx repo default papaya-nvim
ctx repo default --clear

# List registered repos, or remove some (their contexts are left alone):
ctx repo list
ctx repo rm papaya-nvim

# Set contexts aside without deleting them (kills their sessions), and bring one back.
# Archived contexts keep their names: names are unique across live and archived.
ctx archive my-cool-feature
ctx unarchive my-cool-feature

# List archived contexts, or empty the whole archive:
ctx list --archived
ctx archive --empty
```

## Configuration

Configure `ctx` via `$XDG_CONFIG_HOME/ctx/config.toml`. All fields shown with
their defaults.

```toml
contexts_dir = "~/.local/share/ctx/contexts"  # where checkouts live
repos_dir = "~/.local/share/ctx/repos"        # internal storage for registered repos
archive_dir = "~/.local/share/ctx/archive"    # where archived contexts go
branch_prefix = ""                            # work branch prefix, e.g. "jane/"
multiplexer = "tmux"                          # or "zellij" (requires zellij >= 0.44)
nerd_font = true                              # false swaps builtin glyphs for plain unicode
```

### Multiplexer layout

`ctx` supports `tmux` and `zellij` multiplexers. Customise the layout of a
`ctx` session via the `layout` table in the config:

```toml
# The pane layout: a tree of panes and "row"/"column" splits ("row" = side
# by side, "column" = stacked). A pane runs `command` (default: a shell) in
# the checkout; at most one pane may set `focus`.
[layout]
split = "row"

[[layout.panes]]
split = "column"
[[layout.panes.panes]]
command = "lazygit"
[[layout.panes.panes]]
command = "nvim"

[[layout.panes]]
command = "claude"
focus = true
```

#### Instant picker

In your multiplexer's config, bind a key that opens the TUI as a floating
overlay in whatever session you're in. zellij (`config.kdl`):

```kdl
bind "Alt c" {
    Run "ctx" "tui" "--exit" {
        floating true
        close_on_exit true
    }
}
```

tmux: `bind -n M-c display-popup -E "ctx tui --exit"`.

### Theme

The TUI renders with your terminal's ANSI palette. A `[theme]` table overrides
individual colours with hex values, e.g. to match a truecolor lazygit theme:

```toml
[theme]
foreground = "#c8d3f5"
selection = "#2d3f76"       # the focused panel's cursor row
border_active = "#ff966c"
border_inactive = "#589ed7"
```

### Status columns

The STATUS column shows the git state: `*` for uncommitted changes, `↑n` for n
unpushed commits.

You can add further columns via `[[status]]`. `ctx` comes with some builtin
status integrations. The TUI re-polls every few seconds and colours known
states.

#### GitHub builtin

```toml
[[status]]
name = "pr"
builtin = "github"         # the branch's latest PR, collapsed into one cell
```

Requires an authenticated `gh` in the checkout (without one the cells stay
blank), and a [nerd font](https://www.nerdfonts.com): the states render as
nerd-font glyphs (set `nerd_font = false` to fall back to plain Unicode).

#### Agent builtin

```toml
[[status]]
name = "agent"
builtin = "agent"          # .git/agent-status, hook-written by your agent
```

You'll also need to configure your agent to write its status, e.g., for Claude
Code, add the following to `~/.claude/settings.json`:

```json
{
  "hooks": {
    "UserPromptSubmit": [{"hooks": [{"type": "command", "command": "ctx claude-hook"}]}],
    "PreToolUse": [{"hooks": [{"type": "command", "command": "ctx claude-hook"}]}],
    "Notification": [{"matcher": "permission_prompt|elicitation_dialog|agent_needs_input", "hooks": [{"type": "command", "command": "ctx claude-hook"}]}],
    "Stop": [{"hooks": [{"type": "command", "command": "ctx claude-hook"}]}],
    "SessionEnd": [{"hooks": [{"type": "command", "command": "ctx claude-hook"}]}]
  }
}
```

#### Custom

You can also add your own status column via a command that runs in the
checkout:

```toml
[[status]]
name = "anything"
command = "my-status"      # first line of any command, run in the checkout
```

### Environment variables

Fresh checkouts don't carry untracked files like `.env`. All contexts of a
repo share the parent directory `<contexts_dir>/<repo>/`; you could, for
example, use a tool like [direnv](https://direnv.net) to export env vars in
all of a repo's contexts from a single `.envrc` there:

```sh
echo 'export MY_SECRET=some-value' > <contexts_dir>/papaya-nvim/.envrc
direnv allow <contexts_dir>/papaya-nvim
```

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md).
