riterm — Ring Group Terminal
A terminal app launched with riterm inside Terminal, Ghostty, or another terminal emulator. Riterm uses an isolated tmux server for real interactive shells and AI coding agents, with a Python curses sidebar.
RITERM │ 1 · codex │ 2 · codex
│ │
WORKSPACES │ │
▾ My Project │ │
🤖 ⠋ AI coding │ │
Other work ├──────────────────────┼─────────────────────
▸ Another Project │ 3 · codex │ 4 · codex
│ │
──────────────────── │ │
REDMINE [r] refresh │ │
#142 Fix login timeout │ │
In progress │ │
#138 Add export support │ │
Open │ │
The sidebar has exactly two navigation levels: collapsible workspaces and their sessions. Terminal panes appear only in the main area. Each session stores its name, pane count, and startup command; ordinary session panes inherit the workspace directory; new Redmine sessions use dedicated Git worktrees. Four panes form an even 2×2 grid. Counts from 1 to 16 are supported; incomplete rows fill the available width.
Sessions show a 🤖 animated spinner while any pane displays a Codex interrupt hint or Claude working spinner (such as ✢ Accomplishing…). Collapsed workspaces show the indicator when one of their sessions is working. The current session stays highlighted without a dot marker. Activity is checked once per second and clears when the working status disappears, including when the agent returns to its prompt. Detection is best-effort from the visible terminal footer; unsupported agents or changed CLI status text may not be detected.
Install and launch
Requires macOS or Linux, Python 3.11+, and tmux 3.3+. Install tmux with brew install tmux on macOS, or your Linux package manager.
git clone https://github.com/jovinel/riterm.git
cd riterm
./install.sh
riterm
You can also download the source archive or wheel from GitHub Releases. See release installation instructions.
The installer creates a project virtual environment and links its executable into ~/.local/bin. That directory must be in your PATH. Moving the checkout requires rerunning the installer. Alternatively, run .venv/bin/riterm directly.
When no workspaces are saved, Riterm shows an empty workspace and opens a shell in the filesystem root (/). Use cd in that terminal to enter a project directory; Riterm automatically creates a workspace and saves the same running shell as its first session. The launch directory is never used as a default workspace. Press Ctrl+B, then n, to create your agent session: enter its name and 4 panes, then select Codex, Claude, or Shell with Up/Down and Enter. Claude starts with claude --dangerously-skip-permissions; Esc cancels the selection. To add another workspace, select + Add workspace in the sidebar and press Enter, or press w while the sidebar is focused. Enter a name and an existing project directory; Esc cancels either prompt. The new workspace is selected automatically; press n to add its first session. Session settings are reusable: selecting a stopped session starts it, while selecting a running session returns to the same processes.
You can also create sessions from the shell:
riterm workspace "My Project" ~/projects/my-project
riterm session "My Project" "AI coding" --panes 4 --command codex
riterm session "My Project" "Other work" --panes 1 --command ''
riterm list
riterm doctor
Use --no-attach with riterm session to start it in the background. Startup commands run through your login shell. When a command exits, the pane opens a shell and displays its exit code. Exiting that shell removes the pane, and neighboring panes expand to fill its space.
Controls
| Location | Key | Action |
|---|---|---|
| Anywhere | Ctrl+R | Focus the first Redmine ticket (query setting if empty); Up/Down stay in Redmine |
| Anywhere | Ctrl+Q | Focus workspace navigation; Up/Down skip sessions; Down continues into Redmine at the end |
| Anywhere | Ctrl+B (or F2) | Open the sidebar menu |
| Terminal pane | Ctrl+/ | Split vertically: open a shell to the right (Apple Terminal requires Riterm Keys) |
| Terminal pane | Ctrl+- | Split horizontally: open a shell below (Apple Terminal requires Riterm Keys) |
| Anywhere | Ctrl-a, then arrow | Move between panes |
| Anywhere | Ctrl-a, then z | Zoom/unzoom current pane |
| Anywhere | Ctrl-a, then d | Detach; processes keep running |
| Anywhere | Ctrl-a, then [ | Scrollback/copy mode; q exits |
| Sidebar | Up/Down or k/j | Select workspace, session, or ticket |
| Sidebar | Enter | Collapse workspace, open session, or show ticket details |
| Sidebar | Space | Collapse workspace/ticket group or open session |
| Sidebar | Left on session | Focus the first visible Redmine ticket; Up/Down stay in Redmine |
| Sidebar | Right on ticket | Choose provider, model, then effort; Enter or click confirms, Right also confirms effort, and Esc cancels |
| Sidebar | Tab | Focus terminal |
| Sidebar | w | Add workspace |
| Sidebar | n | Add session under selected workspace |
| Sidebar | i | Edit the Bash initialization script for new ticket worktrees |
| Sidebar | d | Delete the focused session and stop its terminal panes |
| Sidebar | c | Set Redmine saved-query URL |
| Sidebar | a | Set or clear Redmine API key (masked input) |
| Sidebar | r | Rename the focused workspace; refresh Redmine on other rows |
| Sidebar | g | Cycle Redmine grouping: status, priority, off |
| Sidebar | q | Detach |
In workspace navigation, Enter expands/collapses the selected workspace. Left on a workspace title or session jumps to that workspace's first Redmine ticket. Right enters its session list; pressing Right on a session opens it and focuses its terminal; Esc or Ctrl+B returns to normal sidebar navigation.
Pressing d on a session removes its saved definition and closes its running panes. Deleting the last running session keeps Riterm open with an unsaved shell at /. The shortcut does nothing on workspace or ticket rows.
Click to focus panes or select sidebar rows. Redmine ticket links open only with Enter. Drag horizontally from inside the sidebar (including the Redmine panel) to change its width; this width is kept when resizing the terminal. You can also drag tmux pane borders to resize; resizing the overall terminal restores the even agent grid unless you have created a split, in which case it preserves your split arrangement. New splits focus a shell in the current pane’s directory and last for the running session; they do not change the saved startup pane count. Ctrl+B opens the menu immediately. F2 remains an alternative (some keyboards require Fn-F2). Ctrl+R opens Redmine navigation; use Ctrl+A then Ctrl+R for shell reverse-history search. The tmux prefix is Ctrl+A; Ctrl+A then b sends a literal Ctrl+B to the active terminal, and Ctrl+A then Ctrl+A sends Ctrl+A. Ctrl-a followed by a key means two separate keystrokes; ordinary Ctrl-c, Tab, and typing stay with the active terminal program.
Redmine
Each workspace has its own Redmine query URL, API key environment variable, and ticket cache. Select a workspace or one of its sessions to show its tickets under REDMINE. In the sidebar, select Set query URL (or Edit query URL) under REDMINE and press Enter. You can also press Ctrl+B, then c from any terminal pane. Paste your custom query URL and press Enter to save and refresh tickets. Esc cancels; Ctrl-u clears the URL to disable Redmine. Changing the URL preserves your configured API key environment variable.
Select API key under REDMINE, or press a in the sidebar, to save your REST API key. Input is masked. Enter saves, Esc cancels, and Ctrl+U then Enter clears the saved key. The saved key is shared only across workspaces using the same HTTPS host, port, and Redmine installation path. It takes precedence over that workspace’s configured environment variable; clearing it restores environment-variable fallback. Configure the query URL before saving a key. Changing to a different server does not reuse the previous server’s saved key. Legacy profile-wide keys must be re-entered for each server; they are never assigned automatically. Saving refreshes tickets immediately, and ticket details use the updated key on their next fetch.
Existing global query settings carry over to the first workspace; other workspaces start unconfigured. The CLI accepts --workspace with a name or ID. Without it, Riterm uses the workspace matching the current directory, or the only workspace if there is just one; ambiguous selections require --workspace.
Alternatively, configure it from the command line, then provide a REST API key through an environment variable before launching Riterm:
riterm redmine --workspace 'My Project' 'https://redmine.example.com/projects/my-project/issues?query_id=119'
export REDMINE_API_KEY='your-api-key'
riterm
Use your local secret manager or shell configuration to supply the key; do not commit it. A different variable name can be configured using riterm redmine URL --workspace "My Project" --key-env VARIABLE. The in-app query editor preserves this setting; the default is REDMINE_API_KEY. Authenticated users need access to the saved query and the server must enable the REST API.
Riterm reads /issues.json with the saved query_id and project filter, and retrieves all pages. It displays issue number, subject, and status. Pressing Right on a ticket shows a Codex / Claude picker, followed by a model picker and an effort picker. The session opens after confirming effort with Enter, Right, or a click on an effort option; Esc cancels at any step. Codex lists visible models from its local model catalog when available; Claude offers Fable, Opus, and Sonnet aliases. Both pickers offer Provider default and Custom model… (enter a model ID). Use Up/Down and Enter to create or reopen its dedicated one-pane terminal session in the active workspace, or Esc to cancel without opening a session. Riterm loads a structured brief (ticket metadata, description, workspace, and suggested workflow) and starts an interactive Codex or Claude session with that brief as its initial prompt. The agent summarizes the requirements and missing information, then waits for your next instruction. The chosen agent, model, and effort are saved with the session and used again by riterm ticket-start. Each provider/model/effort combination has a separate session for each ticket. Explicit models are passed to the CLI using --model. Effort uses Claude’s --effort or Codex’s model_reasoning_effort setting. Codex effort options follow the selected model’s cached capabilities when available, with Low through Xhigh as the fallback; Claude offers Low through Max. Provider default leaves effort unchanged; Provider default leaves the CLI’s configured model in effect. Set the picker’s initial selection with riterm redmine --agent claude or riterm redmine --agent codex; auto initially highlights Codex. Legacy sessions without a saved choice still use the configured preference, with auto falling back from Codex to Claude. The brief is saved outside the repository under ~/.config/riterm/briefs/; RITERM_TICKET_FILE, RITERM_TICKET_ID, and RITERM_TICKET_URL are available in that session. The ticket text is passed as a literal prompt argument, never evaluated as a shell command. After exiting the agent, use cat "$RITERM_TICKET_FILE" to read it again. Full-detail fetch failures show available cached metadata and descriptions with a specific network/API warning. Missing fields are marked unavailable when the fetch fails. Run riterm ticket-refresh in the ticket pane to fetch and print the brief again; it also updates the saved brief. Successful detail fetches are saved for later offline use. Choosing the same agent, model, and effort for the same ticket in the same workspace returns to its running session without resubmitting anything. For an existing ticket pane already sitting at a shell, run riterm ticket-start to launch the agent with its ticket context. If the chosen agent is unavailable, Riterm keeps the brief and opens a shell with setup guidance. Enter on a ticket opens a centered popup over the full terminal window (85% of its width and height) with its cached Redmine information and description. Close is focused by default; press Tab or Left/Right to select Open in browser, then Enter to open its Redmine page. Up/Down and Page Up/Page Down scroll the details; Esc closes the modal. Edit (or e) opens a draft editor for status, priority, assignee, category, start date, due date, done %, and estimated hours. Choose fields with the arrow keys and Enter, then select Save changes to update Redmine. Esc or Cancel discards the draft. Dates use YYYY-MM-DD; blank dates, assignee, category, or estimated hours clear those values. Status choices use allowed workflow transitions when supported by Redmine; server permissions and validation still apply. Saving refreshes the popup and saved-query sidebar.
Ticket worktrees and initialization
Each new ticket session opened with Right gets a separate Git worktree and branch from the workspace’s current HEAD. Each provider/model/effort combination gets its own worktree. Uncommitted changes and untracked files in the original checkout are not copied. The workspace must be a Git repository with at least one commit. Riterm stores new worktrees under ~/.config/riterm/worktrees/<workspace>/<ticket>/<agent-model-effort-session-id> (or your RITERM_HOME) with branches named riterm/ticket-<ticket-id>-<session-id>. The workspace folder uses a filesystem-safe workspace name, adds an ID if the name collides, and stays stable after workspace renames. Ticket folders use the ticket number; omitted model and effort choices are omitted from the session folder name. Existing sessions retain their saved worktree paths.
To initialize dependencies or copy local configuration, select the workspace, press Ctrl+B then i, and type your Bash script directly in Riterm:
npm install
cp "$RITERM_WORKSPACE_DIR/.env" .
The editor loads your saved script. Enter adds a line, arrow keys move the cursor, Ctrl+G saves, and Esc cancels. Ctrl+U clears the script; save it empty to disable initialization. Long commands and scripts scroll as you edit.
You can also import a script file from the command line:
riterm redmine --workspace "My Project" --init-script ~/riterm-init.sh
Riterm saves the script’s contents per workspace. Imported files are snapshots; import again to pick up external edits, or edit the saved contents inside Riterm. Pass --init-script - to clear it from the command line. Configuration changes apply to worktrees that have not completed initialization.
The script runs with bash -e in the new worktree before the agent launches, with live output in its terminal. RITERM_WORKSPACE_DIR points to the original workspace and RITERM_WORKTREE_DIR points to the new worktree; RITERM_TICKET_ID and RITERM_TICKET_URL are also available. Relative paths such as cp ../.env . resolve from the new worktree; use "$RITERM_WORKSPACE_DIR/.env" to copy from the original workspace reliably.
Successful initialization runs once. Reopening a session or running riterm ticket-start reuses its worktree without rerunning setup. If initialization fails or is interrupted, the agent does not start; Riterm opens a shell for troubleshooting. Fix the script or files, then run riterm ticket-start to retry. Retried scripts should tolerate partially completed setup.
Deleting a session or workspace preserves its worktree files and Git branches, including uncommitted work. Remove them manually with git worktree remove PATH and git branch -d BRANCH when finished. A missing previously created worktree produces an error instead of silently replacing your checkout.
Select Group by and press Enter or Space, click the row, or press g to cycle through status, priority, and off. Select a heading and press Enter or Space, or click it, to collapse or expand its tickets. Priority groups appear in this order: Immediate (red), Urgent (orange), High (yellow), Normal (green), Low (pale green); unknown or missing priorities follow these groups. Ticket numbers use their priority color in every grouping mode, including when selected. Colors use the terminal palette, with basic-color fallbacks on terminals without 256-color support. Grouping and collapsed groups are saved per workspace; grouping defaults to off. Within each status group, tickets are ordered by priority (Immediate through Low), then ticket number (highest first); unknown or missing priorities follow known priorities. Within priority groups, tickets are oldest first. Bilingual status and priority labels share groups. Turning grouping off restores the flat list with a status beneath each ticket.
Tickets refresh when a sidebar starts, when selecting a different workspace (at most once per minute), and on r when a workspace name is not focused. Requests run in a background thread, serialized per workspace. On failures, the last successful list remains visible with an error indicator. DNS errors mean the Redmine hostname could not be resolved; check your network or VPN, then retry. The cache retains descriptions and metadata supplied by the issue list as well as titles and statuses. Redirects are rejected to avoid forwarding the API key to a login page. HTTPS is required. Saved query definitions determine the filters; additional ad hoc filters in the pasted browser URL are not imported.
Keys saved through the sidebar take effect without restarting Riterm. If you use only an environment variable and change it while Riterm is running, refresh from a shell with the new environment:
riterm redmine --workspace "My Project" --refresh
The running sidebar reads that shared cache automatically. For ongoing in-app refreshes with the new key, restart the Riterm server after finishing your sessions.
Persistence and scope
Existing Qute profiles in ~/.config/qute are moved to ~/.config/riterm when the new directory does not exist. A compatibility symlink at the old path preserves existing Git worktrees and running sessions; new worktrees use the Riterm directory. Explicit RITERM_HOME or legacy QUTE_HOME paths are honored without migration, with RITERM_HOME taking precedence. Legacy QUTE_* ticket and initialization variables remain available for saved scripts.
Settings and ticket cache live in ~/.config/riterm, with private files. RITERM_HOME selects a separate profile and tmux server. No user tmux configuration is changed. Keys entered in the sidebar are stored as plaintext in a separate credentials.json file with owner-only permissions (0600), never in settings or ticket caches. Environment-only keys are not persisted.
Detaching and reconnecting preserves live processes. Workspaces and session definitions survive a machine restart; terminal process state does not survive a reboot. Saved sessions start on selection after the server stops. New Redmine sessions keep their own Git worktree and branch across restarts. Ordinary sessions and ticket sessions created before this feature keep their original workspace directory.
Press d on a focused session to delete it, or on a workspace to remove it. Empty workspaces are removed immediately. Workspaces with sessions ask for confirmation: y deletes the workspace and its sessions and stops their running processes; n, Escape, or Enter cancels. The project folder and its files stay on disk. If deletion closes all running sessions, Riterm opens an unsaved shell at / so you can continue using it.
Press r on a focused workspace name to rename it. Enter saves and Esc cancels. Each saved session is its own launch preset; there is no separate named preset library. Keep the terminal at least 70×12 cells and use a larger window for many panes. Riterm should be launched outside an existing tmux session.
Development
No Python runtime dependencies beyond the standard library.
python3 -m unittest discover -s tests -v
Tests cover saved settings, URL parsing, API authentication/pagination, cached failures, and real tmux panes: startup commands, shell input, grid geometry, resizing, and session switching. Integration tests run on isolated temporary profiles, with no AI provider calls.
References: tmux manual, Redmine saved queries, Redmine REST authentication.
Command shortcuts in Apple Terminal (optional)
Apple Terminal handles Command shortcuts outside the TUI. The optional Riterm Keys menu bar helper maps Command+Shift+W to Riterm's Ctrl+Q workspace navigation and Command+Shift+R to Ctrl+R to focus the first Redmine ticket only when the frontmost app is Apple Terminal and its focused window has Riterm's title marker. It also maps Ctrl+/ and Ctrl+- to distinct F6/F7 events for splits, since terminal control characters cannot always distinguish these combinations. These function keys also work as split fallbacks. Other windows keep their usual shortcut behavior. Ctrl+Q and Ctrl+R remain available directly.
./macos/install-keys.sh
open "$HOME/Applications/Riterm Keys.app"
Enable Riterm Keys under System Settings → Privacy & Security → Accessibility. The helper retries automatically after access is enabled. It displays R⌘ in the menu bar, where it can be quit. It is not added to login items automatically; open it when needed. No keystrokes are recorded or stored. If Terminal's Secure Keyboard Entry prevents the helper from receiving events, Ctrl+Q and Ctrl+R remain the fallbacks.
Riterm sets the terminal title to [Riterm:riterm-…] — workspace / session to scope the shortcut. The helper leaves the shortcut untouched if it cannot verify that title. Reconnect with riterm after installation to update an older session's title. To uninstall, quit Riterm Keys and remove ~/Applications/Riterm Keys.app.
License
MIT. Copyright © 2026 Jovi Nel Parilla.
Release files for riterm-terminal 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| riterm_terminal-0.1.1.tar.gz | 78.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| riterm_terminal-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 123.3 kB
Release files / riterm_terminal-0.1.1.tar.gz
| Download URL | riterm_terminal-0.1.1.tar.gz |
|---|---|
| Size | 78.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e27fe9f7783ec1408708c4df66fe3d3966b28a1a8ab8be4415c8dbb6838de9e5
|
|
BLAKE2b-256 checksum How to use checksums |
b73278b81cb533bc0d3e4b5fef5fb266de05699128d262f553af7ce22a50d64e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency logRelease files / riterm_terminal-0.1.1-py3-none-any.whl
| Download URL | riterm_terminal-0.1.1-py3-none-any.whl |
|---|---|
| Size | 44.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fd0ae9c287ecb150bfd35242c3cc501f6ec4e1322c2341c1d48e9f5cb525cf75
|
|
BLAKE2b-256 checksum How to use checksums |
1b9f98c68ece1c27f3d3f495cdd4b049c59bd402a77cb3a4035b38408e900216
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.
Transparency log