A TUI app you launch inside a Git repository. Every instruction you type starts a coding-agent session (Claude Code / Codex / Grok) on its own isolated git worktree, and they all run in parallel.
日本語のドキュメントは README.ja.md にあります。
codiva is a terminal UI that spins up a coding-agent session on a fresh git worktree + branch every time you type a plain-language instruction, so several tasks make progress at the same time. The goal is simple: keep throwing instructions at it and watch the work happen in parallel.
It is not Claude Code only. You can pick any of these three (switch with /agent in the session list — see Choosing and switching agents):
| Agent | What it launches |
|---|---|
| Claude Code | Claude Agent SDK (the claude CLI) |
| Codex | OpenAI's codex CLI |
| Grok | xAI's grok CLI |
You can choose the default for new sessions, and you can also switch a running session mid-flight (the worktree, working tree and PR all carry over). Where agents differ in what they support (permission dialogs, cost display, …) is spelled out in the capability table below.
- Parallel sessions — every instruction gets its own worktree (
.codiva/worktrees/<slug>) and branch (codiva/<slug>), so file edits never collide. - Pick your agent — Claude Code / Codex (
codexCLI) / Grok (xAI'sgrokCLI). Use/agentin the list to choose the default for new sessions (saved automatically), and/agentin the detail view to switch an existing session mid-flight. Install and login status is shown in/agent, and/loginlets you sign in from inside codiva. - Live progress — the list view shows every session's state (
Running/Step 4/7/Question/Awaiting permission/Completed/Failed) and elapsed time. - Non-blocking submission — you can type the next instruction the instant you submit one.
- Permission replies and follow-ups — approve/deny tool use and send follow-up instructions to a running session from the detail view.
- Merge or discard — review the diff stat of a finished session, then merge it into the base branch or throw the whole worktree away.
- Repo-wide instructions — anything written in
.codiva/prompt.md(e.g. "open a PR when you're done") is injected into every session. Editable inside the TUI via/prompt. - Plan / usage display (Claude only) — the list header shows your claude.ai plan tier (Pro / Max / Team / Enterprise) and usage limit windows (percentage used, time until reset). Codex / Grok don't report these, so nothing is shown while one of them is the default agent.
- Current branch — the header shows the branch the target repository has checked out (i.e. where new sessions branch from and merge back to). It catches up within seconds even if you switch branches in another terminal.
- Training-data warning (Claude only) — a notice line appears in the startup header only when claude.ai's "Help improve our AI models" setting is ON.
- Update notification — codiva checks npm for a newer version at startup and shows one line in the header if there is one.
/updateupgrades in place after a confirmation. - Keyboard-first — the mouse is optional. You can still drag-select and copy from the input, the header and the session log (dragging past the edge of the log auto-scrolls while the selection keeps growing). URLs in the log open in your browser on click.
- Japanese / English UI — switch via
~/.codiva/config.jsonorCODIVA_LANG.
- Node.js >= 20
- At least one of the
claude/codex/grokCLIs installed and logged in (see Choosing and switching agents). codiva starts even with none of them installed, and/loginlets you sign in from inside codiva. - The target directory is a Git repository with at least one commit
npm install -g codivaJust want to try it once:
npx codivaAt startup codiva queries the npm registry exactly once and, if a newer version exists, shows ↑ v0.3.0 is available · run /update in the banner (nothing is shown when you're up to date or the check failed).
Running /update re-checks for the latest version and, if there is an update, prints the command it would run and asks y / n. On y codiva runs npm install itself and then asks you to restart.
- Global install (
npm install -g codiva) → runsnpm install -g codiva@latest(you get a warning first if sessions are still running) npx codiva→ nothing to update, so nothing happens (the nextnpxpicks up the latest)- Anything else (a project-local dependency / under a tool manager like volta / Windows / an install layout codiva can't identify) → codiva does not run anything and only shows you the command to run
The set of layouts codiva refuses to handle is deliberately wide. Updating a local dependency rewrites your repository's package.json and lockfile and rebuilds node_modules (which is symlinked into every worktree by default). And running npm install -g against an unidentified layout could install to somewhere other than where codiva actually lives and break your environment. The cost of a misdetection is capped at "you have to run one command yourself".
The check is a single request to https://registry.npmjs.org/codiva/latest (~2.3 KB, 3-second timeout) and the only thing sent is the package name — no version, no telemetry. Startup is never blocked, even offline. Set "updateCheck": false in ~/.codiva/config.json to disable the request entirely.
Launch it from the root of the target repository.
cd path/to/your-repo
codiva- Type an instruction (e.g. "implement the login flow") and hit Enter. A new session is created and you can immediately type the next one.
- Watch each session's progress in the list view.
- Select a session to open the detail view, where you can read the log, send follow-ups and answer permission requests.
- When it's done, review the diff stat and merge or discard.
codiva writes a
.codiva/.gitignorecontaining a single*line, which hides everything under.codiva/from git (the ignore file matches*itself, so it's self-contained). Your repository's.gitignoreand the contents of.git/are never touched.
Long instructions wrap automatically at the terminal width, so nothing you type is truncated out of view (words aren't broken mid-word when there's whitespace to break at). The field grows to 8 rows and then scrolls internally to follow the caret.
These are identical in every input field (the list composer, the follow-up field in the detail view, the /prompt editor, and the free-text field in question dialogs). There's a single implementation, so behaviour never differs by location.
| Key | Action |
|---|---|
Enter |
Submit (runs a command when the text starts with /) |
Shift+Enter |
Newline (on terminals where that doesn't reach the app, end the line with \ and press Enter) |
Ctrl+U |
Clear the draft (empties the field regardless of caret position) |
←→ |
Move the caret |
↑↓ |
Move the caret (by visual row, i.e. after wrapping). Pressing again at the top/bottom row walks the input history (list composer only) |
Backspace |
Delete one character |
In the list view's input field, ↑ recalls instructions you already sent, like a shell (last 50; repeated identical instructions collapse into one).
↑only becomes history when the caret is on the top row. While you're writing a multi-line draft, normal caret movement wins, so a draft never turns into history behind your back.↓walks back toward the newest entry, and going past the newest restores the draft you had when you started browsing (peeking at history never costs you your draft).- Recalled text is fully editable (the caret lands at the end).
Ctrl+Uthrows it away. - In the detail view's follow-up field
↑↓scroll the log, so history is a list-composer-only feature.
macOS's
Cmd+Deletenever reaches the app (thesupermodifier only arrives on terminals with the kitty keyboard protocol enabled), which is why "clear all" is bound toCtrl+U. To useCmd+Delete, map it toCtrl+U(\x15) in your terminal. On Ghostty, add this one line to your config:keybind = super+backspace=text:\x15
In the list view, Tab cycles focus input field → question/permission dialog → session list → input field (Esc returns to the input field from anywhere). The dialog only joins the cycle when the selected session is actually waiting on a question or a permission request.
| Zone | What ↑↓ does |
Main keys |
|---|---|---|
| Input field | Caret movement / input history | Enter submit |
| Dialog | Move through the dialog's choices | Enter confirm · Space toggle (multi-select) · y/n (tool permission) |
| Session list | Select a session | Enter/→ detail · m/d/x · p |
- Selecting a row that's waiting on a question or permission hands the keys straight to the dialog (whether you got there with
↑↓or by clicking the row). Answering is what's blocking progress, so you shouldn't have to pressTabto reach it. Tabis the way back to the list (i.e. to switching sessions with↑↓). In the list zone the dialog stays visible — you can still read it — but it doesn't receive keys.- Dialog choices are clickable too (confirm with
Enter— the same relationship as clicking a list row and pressingEnterto open the detail view). Clicking a wrapped label line or a description line selects that choice as well. Even a dialog that's "display only" in the list zone will hand focus back to answering when you click a choice.- A click never confirms a permission (
y) or a choice.y/nfor tool permissions always require a key.
- A click never confirms a permission (
- Partial answers (which question you're on, which boxes are checked, a half-written free-text answer) survive
Tabround-trips and clicking back into "write your own". - Once you've answered, focus returns to the input field, so the next incoming question doesn't steal your keystrokes (one
Tabgets you back to answering).
Spacetoggles checkboxes even with a Japanese IME turned on. With macOS Japanese input,Spaceduring kana composition arrives as a full-width space (onlyShift+Spacearrives as a half-width one). codiva treats the full-width space as theSpacekey, so you don't have to disable the IME or addShiftjust to tick a box. (Letter keys such asy/nfor tool permissions get swallowed by IME conversion, so turn the IME off for those.)
While a question or permission dialog is up in the session detail view, Tab moves between the dialog and the conversation log. It exists so you're never shown a question in isolation and forced to answer without knowing what it's about.
| Zone | What ↑↓ / PgUp PgDn do |
Main keys |
|---|---|---|
| Dialog (default) | Move through the dialog's choices | Enter confirm · Space toggle · y/n (tool permission) |
| Conversation log | Scroll the log | Tab back to answering · Esc to the dialog |
- When a dialog appears focus lands on the dialog first (answering is what's blocking progress, so you don't press
Tabto get there). PressTabonce when you want to read back. - In the log zone the dialog stays on screen — only the keys go elsewhere.
Tab,Escor clicking the dialog all get you back to answering. - Partial answers (question index, checked boxes, half-written free text) survive the round-trip.
- Drag-selection and clicking URLs work as usual in the log zone (wheel scrolling works in both zones).
- Once you've answered, focus returns to the dialog side so you can answer the next question right away.
Ctrl+C(interrupt) works in both zones.- The dialog never crushes the log. When there are many choices or long descriptions, the dialog gets a height cap and the choices scroll internally (hidden entries are shown as
↑ 3 moreand follow your cursor as you move with↑↓). Previously the dialog grew one row per choice, and on a ~24-row terminal not a single log line was visible. The list view's dialog reserves room for session rows the same way.
In the session detail log, the agent's response grows downward as it arrives (rather than a single line being rewritten), so you can read it like a chat.
- It only follows the tail while you're at the bottom. While you're back in history via
↑/PgUp/wheel, the view doesn't move a single line even as the response grows (you never get swept along mid-read). "Viewing history — N lines to latest" appears at the bottom of the screen;↓/PgDnreturns to the tail and resumes following. - While text is still streaming it's rendered plain (no bold, no headings); it's replaced with the formatted body the moment the turn completes.
Pressing Ctrl+C in the session detail view interrupts the turn that session is currently running (the same gesture as Ctrl+C in Claude Code). codiva itself does not exit.
- An interrupted session is kept as "interrupted", not "failed", so
Ctrl+R(or just sending a follow-up) resumes the same conversation. The worktree, branch and work-in-progress code are untouched. - It works while a permission or question dialog is up too. The dialog's
n(deny) only refuses that one tool call and work continues, so useCtrl+Cwhen you want to stop the work itself. - It works while the input field has focus (use
Ctrl+Uif you only want to clear a draft). A hint appears at the bottom of the screen while a turn is running. - If you want to throw the session away rather than interrupt it, use
d(discard) /x(remove). In the list viewCtrl+Cdoes nothing — interrupting is a detail-view-only action, to avoid misfires.
Every input field (the list composer, the detail view's follow-up field, the /prompt repo-instruction editor, the free-text field in question dialogs), the header (wordmark / plan / model / branch / cwd) and the session detail log support drag to select, release to copy to the clipboard (via OSC 52, so it works over SSH too). Drag the header's cwd line to paste the path you're working in. Dragging in the header never moves typing focus or the selected row in the list.
- You can select beyond what fits on screen in the log. Keep dragging past the visible area (above the top / below the bottom) and the log auto-scrolls in that direction while the selection keeps growing. Scrolling continues even if you hold the mouse still, and only the selected range is copied when you release.
- What gets copied is exactly what you see (wrap points become newlines, and line prefixes like
•and continuation indents are included). - The selection is highlighted in reverse video and cleared by any keypress.
- Native terminal selection (select anywhere, but no app-side features) is available with Shift+drag, or by disabling mouse capture with
"mouse": false.
URLs in the session detail log are underlined and open in your browser on click (only a press-and-release without dragging opens them, so it doesn't conflict with drag-selection).
http(s)URLs are recognized. For Markdown links ([label](URL)), clicking the label follows the link.- Even when wrapping splits a URL across two lines, clicking either line opens the whole URL.
- This is a plain click, not Cmd+click (the terminal's own link handling). While codiva is full-screen it enables the terminal's mouse reporting, and some terminals disable their own link detection in that state (Ghostty ignores link hover and clicks entirely while the mouse is captured; SGR mouse reports have no bit for Cmd/Super either). So codiva takes the click itself and opens the URL, which behaves the same on every terminal.
- codiva also emits OSC 8 hyperlinks, so on terminals that support them (iTerm2 / GNOME Terminal / Windows Terminal, …) the terminal's own Cmd+click / Ctrl+click works too. On Ghostty / kitty / WezTerm, add Shift — Shift+Cmd+click (Shift+Ctrl+click on Linux) — because Shift is the mouse-capture bypass key. Terminals that don't support the escape simply ignore it and the display is unchanged.
The right edge of each list row shows the state of the PR for that session's branch (detected by running gh every 20 seconds; click or press p to open it in a browser).
| Display | Meaning |
|---|---|
⋯ |
Checking (the first query hasn't finished) |
| (blank) | No PR for this branch |
⋯ #12 |
PR exists, state being checked (e.g. right after a restart) |
? #12 |
PR exists, state could not be checked (rate limit / offline / gh not authenticated) |
✓ #12 |
Mergeable (checks are green too) |
● #12 |
Checks running |
✗ #12 |
Checks failed, or not mergeable due to conflicts |
⑂ #12 |
Merged |
⊘ #12 |
Closed without merging (not a recovery target — see below) |
✓ #12 +2 |
3 PRs in total (see below) |
? |
Could not be checked (GitHub API rate limit / offline / gh not authenticated) |
A dimmed number (#12) means a draft PR. On machines without gh the PR column is always empty.
When a session cuts its own branch and runs gh pr create, several PRs end up attached to one session. The list then shows #12 +2 (the primary number plus the remaining count), and the full set appears in the detail view (Enter) on the 3 PRs: ✓ #12 · #13 · #14 line.
- The primary is the PR for the session's branch (
codiva/<slug>). That's what click /popens, and the glyph (✓/✗/●) is that PR's state. If the branch has no PR, the last PR the session created becomes primary. - Once a PR is primary, codiva queries it by URL and tracks its state even when its head branch isn't checked out in the worktree (including PRs in another repository), so it gets a glyph. Non-primary PRs (folded into
+n) aren't tracked, so the detail view shows just their numbers. - Conversely, a reference GitHub answers "no such PR" for disappears from the list (a misdetected URL, or a PR that was deleted). It is not dropped when the state merely "couldn't be checked" due to rate limiting or being offline (you just get
?). - Detection comes from the command output (the PR URL) of a
gh pr createrun. Merely looking at other PRs withgh pr listorgh pr viewdoesn't count (nor do commands that mix reads in, likegh pr create … || gh pr list …, to avoid false positives). Detected numbers are persisted, so+nsurvives a restart. - Conversely, PRs created while codiva wasn't running, or before this feature existed, don't show up in
+n(the command output happens once and can't be re-derived from a branch name later). PRs on the session branch are still detected viaghas always. - Rows with multiple PRs widen the PR column slightly, so on narrow terminals (~80 columns) the branch column may be dropped.
A PR's number and its state are handled separately. The number is immutable for a branch, so it's persisted and #12 appears immediately on the next launch (the state glyph arrives with the first poll). When only the state can't be fetched, the number stays and ⋯ (checking) / ? (unknown) takes the glyph's place.
When you see ?, check gh auth status and gh api rate_limit. GitHub's API rate limit in particular (GraphQL: 5000/hour) is shared with the gh calls your sessions make themselves, so heavy parallel work can exhaust it. On detecting this, codiva pauses polling for 5 minutes to let it recover. The last known PR number and state stay on screen throughout (vanishing would be more confusing).
When a PR conflicts (✗) or CI goes red, you shouldn't have to enter each worktree by hand to fix it.
| Action | What it does |
|---|---|
/sync |
Merges the base branch into the selected session's worktree (the current session's, in the detail view) |
/fix-ci |
Asks the selected session to fix the failing CI |
Ctrl+F (= /recover) |
Recovers every stuck session at once (shows the count and asks y / n) |
Ctrl+F works regardless of focus. While there are stuck rows, a hint line appears in the list.
What /sync does (git fetch origin <base> → git merge):
| Situation | Result |
|---|---|
| Base is already included | Nothing happens |
| Merged cleanly | Merges and pushes (the session isn't woken — no tokens spent) |
| Conflicted | Does not git merge --abort; leaves the conflict in the worktree and asks the session to resolve it, with the conflicting file list attached |
| Uncommitted changes present | Doesn't attempt the merge; asks the session to commit/stash first, then take the base in (so it can't get mixed into work in progress) |
codiva never resolves conflicts on its own with things like -X ours (that would silently discard code). The session (the AI) decides, and is instructed to ask when it can't.
PRs closed without merging (⊘) are not recovered. Unlike a conflict or a red CI, they aren't stuck — they're done (a human decided to close them). They're excluded from Ctrl+F, and even with autoSync / autoFixCi enabled no base-merge or CI-fix instruction is sent (no tokens on closed PRs). To continue the work, reopen the PR on GitHub (the state glyph comes back on the next poll).
What /fix-ci does: it pulls the failing check names and URLs out of the very same gh pr view call that backs the PR status (no extra API calls) and attaches them to an instruction to "read the logs with gh run view --log-failed, fix the cause and push". Fetching the logs and making the fix is the session's own job.
Automation (off by default; the manual commands work regardless of these settings):
{ "autoSync": true, "autoFixCi": true }autoSync: merge the base in automatically once a PR is found to be conflicting.autoFixCi: ask for a fix automatically when checks go red.- Both only fire while a session is idle (completed, failed, interrupted, …). They never interrupt work in progress, and never run
git mergein a worktree that's being worked on (the manual/syncbehaves the same). - Automatic requests are limited to 2 per session per kind. The count only resets when the PR actually goes green (or gets merged) — not on "checks running" right after a push (otherwise a fix that never works would loop turns forever).
- The default is off because a request means a turn runs, which means you get billed.
Instead of querying every session every 20 seconds, codiva does this:
- Refresh intervals depend on state — 20 seconds while checks are running, 60 seconds while GitHub is computing mergeability, 3 minutes for a settled PR. Merged PRs and discarded/merged sessions are never queried again. In between, the cached value is displayed as-is.
- Batched into one call — when 3 or more sessions need checking in the same cycle, a single
gh pr listfetches everything and it's matched up locally. Ten sessions still means one API call. - Backs off when exhausted — on detecting a rate limit or missing authentication, polling stops for 5 minutes.
The list view's header (banner) shows the claude.ai plan you're logged into and your usage limit windows (Codex / Grok don't report these, so nothing appears while one of them is the default).
Codiva v0.3.1 3 sessions
Plan: Claude Max Model: claude-sonnet-5 Branch: main
/Users/you/projects/your-repo
Usage
Current session ████████░░░░░░░░░░░░ 42% resets in 2h 45m
This week ██████████████████░░ 88% resets in 3d 0h
- Every window (5-hour, weekly, extra usage) is listed as a gauge + percentage + time remaining. The plan name, organization name and the target repository's current branch appear on the same line as the model (
Plan: Claude Max Model: sonnet Branch: main). The branch is where new sessions branch from and merge back to; it catches up within seconds even if yougit switchin another terminal, and is hidden on a detached HEAD. - Updates come from two sources: running sessions get the latest values Claude sends at the start of each turn, and idle ones get an automatic fetch every 5 minutes (both are queries to Claude that run no inference, so there's no token usage or billing). Time-to-reset counts down.
- Some plans don't get a percentage (
%) back from Claude. In that case the gauge is omitted and only the time remaining is shown (so 0% isn't misread). - With an API key / Bedrock / Vertex there is no subscription limit, so this display doesn't appear.
- The header describes "the agent that will run next". Switch the default to Codex / Grok with
/agentand the agent name, plan, model and usage all switch together (neither reports a plan or usage, so the plan line and gauges disappear and the model falls back toModel: CLI default). The 5-minute claude.ai fetch also stops while that's the case, and resumes when you switch the default back to Claude. - The status bar at the bottom only carries the mode indicator (
⏵⏵ auto mode) and key hints. Plan / usage live in the header (press Esc to return to the list if you want to see them from the detail view).
Sessions can be driven by Claude Code (the claude CLI / Claude Agent SDK), Codex (OpenAI's codex CLI) or Grok (xAI's grok CLI). Claude is the default, but codiva is perfectly usable with only Codex / Grok installed.
- Install the CLI you want and log in (
claude→claude auth login/codex→codex login/grok→grok login. Grok installs viacurl -fsSL https://x.ai/cli/install.sh | bashand also works withXAI_API_KEY). codiva does not bundle these CLIs — likegitandgh, it launches the commands installed on your machine (so users who don't need a provider aren't shipped a large binary). codiva never touches their credentials either. - Type
/agentin the list and pick an agent; that's it — it becomes the default for new sessions (no hand-editing config: your choice is saved to~/.codiva/config.json. Writing"agent": "codex"/"agent": "grok"yourself works too). The dialog lists each agent's install and login status (Ready/Not signed in/Not installed). - To switch an already-running session, open the detail view (
Enterfrom the list) and pick/agentthere. The switch takes effect from the next instruction.
codiva starts even with none of claude, codex or grok installed. In that case the list says "No coding agent found" — follow the instructions to install one and log in.
You can sign in from inside codiva. Type /login in the list or detail view (or select a target in the /agent dialog and press l) and codiva launches codex login / claude auth login / grok login --device-auth in the background, showing the auth URL and one-time code in a dialog (the URL is also opened in your browser automatically). Finish signing in there and the status updates on its own (Esc aborts). The terminal is never handed over, so other sessions keep running.
Here's what does and doesn't carry over when you switch with /agent:
| Carries over | Does not carry over |
|---|---|
| The worktree, branch and working tree contents; codiva's user/assistant conversation log, title and PRs | The provider's native session itself (each CLI keeps its own transcript) |
The conversation id of each agent you've used is stored per session, so going Claude → Codex → Claude resumes the original conversation where it left off (this survives restarting codiva too).
The provider-native context can't be transferred directly, so codiva copies the retained user and assistant conversation into a one-time handoff for the incoming agent. It also includes the branch name, the first and most recent instructions, and a reminder to verify the working tree with git status / git diff. Tool-execution logs are left out (they're bulky, and the working tree tells the same story). The newest conversation is prioritized, and an omission marker is shown if the handoff reaches its safety limit. No extra turn runs at switch time; the handoff rides along with the next instruction you send.
You can always see what each session is running on.
- The header shows
Agent: Claude— the default for new sessions. - List rows get an agent-name column only when several agents are in play (if they're all the same it just duplicates the header, so that width goes to titles and branch names instead).
- The detail view's input field names the recipient, e.g.
Send a follow-up to Claude…. - The conversation log of a switched session gets a
── Codex from here ──divider, so you can tell which agent said what.
Worktree isolation, parallel execution, follow-ups, interrupting (Ctrl+C), merge / discard, PR automation (/sync / /fix-ci), desktop notifications, repo-wide instructions (.codiva/prompt.md) and /model all work the same on every agent. Only these four things differ.
| Claude Code | Codex | Grok | |
|---|---|---|---|
| Tool permission / question dialogs | ✅ | ❌ (sandbox instead) | ✅ |
| Cost display (total in the header) | ✅ | ❌ | ❌ |
| Plan / usage gauges | ✅ | ❌ | ❌ |
| Log restoration after a restart | ✅ | ❌ (resuming the conversation still works) | ❌ (same) |
Codex session limitations (differences from a Claude session):
- It doesn't ask for tool permission.
codex exec's JSON output mode auto-rejects approval requests inside the CLI, with no way to surface them to codiva. So rather than fake a plausible-looking permission dialog, codiva makes the sandbox the only safety valve (settingcodexSandbox; the defaultworkspace-writeconfines writes to the session's worktree). Codex sessions also never enter theQuestionstate. - It doesn't display cost. Codex only returns token counts at the end of a turn — no amounts, no account-wide usage. Codex sessions are excluded from the header's total cost (so a Claude-only figure isn't presented as "everything"). The plan display and usage gauges are about your Claude account, so they don't appear in the header while Codex is the default (and nothing is fetched either).
- The footer's mode indicator reads
confirm mode (unsupported). Since nothing can be asked, a plainconfirm modewould read as "wait and you'll be asked" (theshift+tabtoggle itself still works). - The log isn't restored after a restart (resuming the session itself does work). Log reconstruction reads the Claude CLI's transcript files, and Codex's transcripts use a different format.
/modellists Codex's own models (codex debug models). On machines where that list can't be fetched you only get "default" (codiva doesn't guess model names). Switching provider with/agentresets an incompatible model selection to the CLI default. Codex doesn't include the model name in its execution events, so the list shows whatever model you set explicitly with/model.
Grok session limitations (differences from a Claude session):
- Tool permissions and questions come through normally. Unlike Codex, Grok surfaces permission requests (
Awaiting permission) and questions (Question) over codiva's bidirectional channel, so you answer them in dialogs as usual. - It doesn't display cost. Grok only returns token counts at the end of a turn — no amounts, no account-wide usage. Grok sessions are excluded from the header's total cost, and the plan display and usage gauges (which are about your Claude account) don't appear while Grok is the default.
- The log isn't restored after a restart (resuming the session itself does work). Log reconstruction reads the Claude CLI's transcript files, and Grok's transcripts use a different format.
/modellists Grok's own models. On machines where that list can't be fetched you only get "default" (codiva doesn't guess model names). Switching provider with/agentresets an incompatible model selection to the CLI default. Unlike Codex, Grok tells you which model is actually running, so the session list shows a model name even when you didn't set one with/model.
The on/off settings can be toggled from the TUI (/config in the list view — see Changing settings from the UI). Below is the full set of keys for editing the file directly.
~/.codiva/config.json (optional):
{
"language": "auto",
"ignoredFiles": "symlink",
"updateCheck": true
}language:"ja"/"en"/"auto"(follow the OS locale). TheCODIVA_LANGenvironment variable (ja/en) takes precedence over everything.updateCheck: whether to check npm for the latest version at startup. Defaulttrue. Setting it tofalsestops the startup request and makes/updatereport "could not check".ignoredFiles: how.gitignored untracked files (node_modules/,.env, …) are carried into a session's worktree.git worktreeonly carries tracked files, so without this you'd have to reinstall dependencies and re-create env files. Default"symlink"."symlink"(default): just symlink to the repository root. Zero copy cost, instant startup. Because the real files are shared, codiva also tells the session about it, along with the "detach the link before writing" procedure (see below)."copy": copy the real files from the repository root. The worktree becomes fully independent and work can never collide, but a hugenode_modules/makes the copy slow."none": carry nothing over (the session re-creates dependencies and env files itself).- The deprecated boolean
copyIgnoredis still understood for backward compatibility (true→ likecopy,false→ likenone).ignoredFileswins if both are present. - Build output and caches are never carried over (
.next/.nuxt/.svelte-kit/.turbo/.vite/.cache/dist/build/out/coverage/target/__pycache__/*.tsbuildinfo, …). They're generated, so the session can rebuild them, and sharing them breaks things (see below).
ignoredFilesExclude: an array of patterns to add to — or cancel out of — the exclusion list above. It's appended after the defaults, and the last matching pattern wins. A pattern without/matches the final path segment, so it also catches nested locations likeapps/web/.next/. A leading*matches a suffix (*.log).This example means "do carry{ "ignoredFilesExclude": ["!dist", ".venv", "*.sqlite"] }dist/over (cancelling the default exclusion), and don't carry.venv/or*.sqlite".notifications: whether to show desktop notifications on questions, permission requests, completion and so on. Defaulttrue(falsedisables them).privacyWarning: whether to show the header notice when training-data usage is ON. Defaulttrue. Setting it tofalseskips the detection entirely (see below).autoSync: whether to merge the base branch in automatically when a PR conflicts. Defaultfalse(see above).autoFixCi: whether to ask the session to fix CI automatically when it fails. Defaultfalse(see above).crashLog: whether to write a crash log to~/.codiva/logs/on an unexpected exit. Defaulttrue(see below). Withfalseno file is written; codiva still prints the reason and restores the terminal.agent: which agent new sessions use by default."claude"(default) /"codex"/"grok". Choosing one via/agentin the list saves it here, so you normally don't write it by hand. Per-session switching is/agentin the detail view (see above).claudeSettingSources: an array of the settings layers a Claude session loads."user"(~/.claude/settings.json) /"project"(<repo>/.claude/settings.json) /"local"(<repo>/.claude/settings.local.json). The default is["project"], and"project"is always included whether you list it or not (the target repository's CLAUDE.md is only read through that layer).Add{ "claudeSettingSources": ["user", "project", "local"] }"user"if you want your Claude Code plugins to work in codiva sessions too. Plugin activation (enabledPlugins) for anything installed withclaude plugin installis written to~/.claude/settings.json, so with the default, none of a plugin's skills / commands / subagents / hooks / MCP servers get loaded. The side effect is that the rest of that layer (hooks, permissions, statusLine, …) also loads into your sessions. That's why the default is["project"]: sessions run unattended in a worktree rather than in front of you, so codiva errs on the side of not silently importing your local Claude Code setup.codexSandbox: the sandbox for Codex sessions."read-only"/"workspace-write"(default) /"danger-full-access". Because Codex can't ask for tool permission, this is the only safety valve for Codex sessions. The defaultworkspace-writemeans "read anything, write only inside the session's worktree".codexNetworkAccess: whether to allow network access whencodexSandboxis"workspace-write". Defaulttrue. Codex's own default is to block it, but that makesnpm installandghfail and most work never finishes, so codiva opens it (setfalseto close it).
With ignoredFiles: "symlink" (the default), .gitignored paths such as node_modules/ and .env are links pointing at the originals in the main repository. Reading through them is fine, but writing (adding or updating dependencies, building, code generation, clearing caches, …) rewrites the shared originals through the link, which propagates to your main checkout and to other sessions running in parallel.
So in this mode — and only this mode — codiva adds the following to each session's systemPrompt:
- that the ignored paths in this worktree are symlinks into the main repository and the targets are shared
- that reading is safe, but before writing you must detach that one path and give the worktree its own copy (either by copying the current contents, or by regenerating it with a fresh install / clean build)
- that removing the link with
rm -rf <path>/or<path>/*(trailing slash, globs) deletes the shared contents - that
.gitignorepatterns likenode_modules/don't match a symlink because of the trailing slash, so the links show up as untracked (git add -Awould commit the link itself, so stage by path) - that only the paths actually being written to need detaching, and tasks that don't write need nothing at all
The wording is language- and toolchain-agnostic ("check whether it's a symlink"), so it works the same in projects that have no node_modules. codiva never pre-emptively replaces the links itself (what will be written depends on the instruction, and copying everything would defeat the point of symlink mode).
Use "copy" if you want full independence from the start, or "none" if you'd rather set things up yourself.
Among .gitignored paths, build output and caches (.next/, dist/, target/, coverage/, *.tsbuildinfo, …) are never carried over, in either "symlink" or "copy" mode. Two reasons:
- Sharing them breaks things. Running dev servers or builds in the main repository and several worktrees at once means concurrent writes to the same files.
- Worktrees live inside the repository (
.codiva/worktrees/<slug>). So to a dev server that watches recursively from the project root (Next.js / Turbopack, …), the directory it keeps writing to appears once per worktree as a separate path. Change notifications echo back many times over, eating CPU, memory and file descriptors — sometimes freezing the whole OS (#81).
Generated files can be rebuilt by the session, so not carrying them is the safe default. Detection is a list of directory names, so add your project's own build output to ignoredFilesExclude (or cancel an exclusion with "!dist" if you do want to share something).
Links left over in existing session worktrees are removed automatically at startup (this also fixes worktrees created by older versions). Only symlinks are removed, so the link targets (the contents of the main repository) and any real build output inside the worktree are untouched.
If you want to reduce what's being watched in the first place, the reliable fix is adding .codiva to your dev server's watch-ignore config on the project side. (Hiding it from git is handled for you: codiva drops in .codiva/.gitignore. Your repository's .gitignore is never modified.)
codiva notifies you when a session changes to states like "question", "permission request", "completed" or "failed" (it stays quiet while a state persists).
Notifications are emitted by the terminal itself whenever possible (using the notification escape sequences of Ghostty / WezTerm / foot / iTerm2 / kitty). That means clicking a notification brings the terminal running codiva to the front. It works inside tmux too, but requires set -g allow-passthrough on. Over SSH, notifications reach your local terminal for Ghostty / kitty / foot (identifiable from TERM) and for iTerm2 (which forwards LC_TERMINAL).
On terminals not covered above (macOS's built-in Terminal.app, Windows Terminal, …) codiva falls back to an OS command (osascript on macOS, notify-send on Linux). On the macOS fallback path notifications are attributed to "Script Editor", and clicking one opens Script Editor — an inherent limitation of notifications posted via osascript. Notifications also won't appear if you've disabled them on the terminal side (e.g. Ghostty's desktop-notifications).
On accounts where claude.ai's "Help improve our AI models" setting is ON, conversations through Claude Code / codiva may be used to improve Anthropic's models. Because codiva pushes a lot of code through parallel sessions, it shows a notice line in the startup header — but only when it can determine that the setting is ON.
⚠ Training-data usage is ON (conversations may be used to improve models)
Change it: https://claude.ai/settings/data-privacy-controls
- Change the setting at that URL (or with Claude Code's
/privacy-settings). codiva never modifies your account settings (read-only). - Detection goes "cache in
~/.claude.json→ query the same API Claude Code uses", in that order, and never delays startup. When it can't tell (not signed in, API usage such asANTHROPIC_API_KEY, offline, an upstream change) nothing is shown. - After you turn the setting off, the warning is gone on the next launch (the API is re-checked even if the cache still says ON).
- The query uses Claude Code's OAuth token read-only (the
Claude Code-credentialsKeychain entry on macOS,~/.claude/.credentials.jsonelsewhere). If you'd rather it didn't, set"privacyWarning": falseand codiva touches neither the Keychain nor the network.
Whatever you write in the target repository's .codiva/prompt.md is injected into the systemPrompt of every session started in that repository. It lets a team share repository-specific workflow ("run the tests and open a PR when you're done", …) — usable alongside CLAUDE.md, independently of it. With no such file, nothing is injected and behaviour is unchanged.
Besides editing the file directly, typing /prompt in the list view's composer opens an in-TUI editor seeded with the current contents (Enter saves, Shift+Enter inserts a newline, Esc cancels, saving an empty buffer deletes the file). Saved content applies to subsequently created sessions (running sessions keep the instructions they started with).
Type / in the composer to see the available slash commands in a palette (/prompt, /config, /model, /agent (list = pick the default / detail = switch that session), /login (sign in from inside codiva), /sync, /fix-ci, /recover, /remove, /clear, /update, /help, …). On a short terminal the tail folds into "N more", so keep typing to narrow it down (/help shows everything).
Typing /config (alias /settings) in the list view's composer opens a dialog for toggling the on/off keys in ~/.codiva/config.json.
| Key | Action |
|---|---|
↑ ↓ |
Select an item (a one-line description of the selected item appears below) |
Enter / Space |
Toggle it on / off ([x] / [ ]). A Space typed with a Japanese IME on (full-width space) toggles too |
Esc |
Close |
Toggles are saved to ~/.codiva/config.json immediately (there's no cancel; items returned to their default value have their key removed, so the config file only ever holds what you changed from the defaults). These settings are read at startup and baked into sessions and terminal setup, though, so they take effect on the next launch (the dialog says so as well).
The items shown are: desktop notifications / mouse support / follow origin / auto-create PRs / auto base-merge on conflict / auto CI-fix requests / Claude Code plugin loading / the training-data warning / the startup update check / crash logs / Codex network access — 11 in total. Multiple-choice settings (language, ignoredFiles, codexSandbox, …) aren't in the dialog, so edit the config file for those (model and the default agent are changed via /model and /agent).
There are three ways to clean up sessions still in the list (all of them ask y / n first).
| Action | Target | What's removed |
|---|---|---|
d (discard) |
The selected session | The worktree and branch. The row stays in the list as "discarded" (it's gone after a restart) |
x (= /remove) |
The selected session | The worktree and branch plus the list row itself. The record goes too |
/clear |
Every finished session (completed, interrupted, failed, …; running ones are skipped) | Each session's worktree and branch plus its list row. Shows the count before running |
x also works in the detail view (Tab over to the action panel). Since a removed session can no longer be opened, you're returned to the list automatically.
Reach for x when you want to clean up sessions tied to old PRs. With d (discard) the row stays as "discarded", and if that branch's PR is conflicting or its CI is failing it keeps showing up as a candidate for Ctrl+F (bulk recovery). x removes the row entirely, so it's fully out of scope for bulk actions.
All three only delete the local worktree and branch — pushed remote branches and PRs on GitHub are untouched (close the PR on GitHub if you want that). Deletion is forced even with uncommitted changes present, so commit anything you want to keep first.
Even if you forget the slash, input that exactly matches a command name available on that screen (exit, help, …) runs as that command. When it will, the command palette shows it, so you know what Enter is about to do. Anything with trailing text (fix how exit behaves) and aliases like ? or changes are treated as ordinary instructions, so an instruction never turns into a command by accident.
/exit means different things on different screens. In the list view it quits codiva; in the session detail view it closes the detail view and returns to the list (same as Esc), so you can't accidentally kill the app by typing /exit while reading a session.
While full-screen, codiva enables the terminal's mouse reporting (click and drag notifications).
It always disables it on a normal exit, but if codiva is killed outright (an out-of-memory abort,
kill -9, or any death that leaves the process no chance to run code), the disable never happens and
the terminal stays in that mode. Scrolling then makes the terminal keep sending mouse positions, which
the shell sees as a flood of input like [<64;12;5M.
Run either of these to recover (both are safe and can be repeated):
codiva --reset-terminal # restore terminal modes (mouse capture, alternate screen, cursor)
reset # the generic full terminal resetcodiva also disables mouse reporting automatically at the next startup before drawing anything, so simply relaunching codiva fixes it too.
Because codiva draws on the alternate screen, errors from an abnormal exit vanish the moment it
leaves that screen. So the reason is written to ~/.codiva/logs/.
| File | Contents |
|---|---|
crash-<timestamp>-<pid>.log |
The report codiva writes itself (kind, error message, stack trace, version, terminal, memory usage, session status breakdown). The 20 most recent are kept |
report.<timestamp>.<pid>....json |
Node's diagnostic report. On out-of-memory (heap exhaustion) or a native crash no JavaScript can run, so the log above isn't written and only this one appears |
- On an unexpected exit the same content and the log path are also printed to the terminal (after restoring the screen, so it doesn't disappear).
- Exits via
SIGTERM/SIGHUPare recorded askind: signaltoo (to distinguish "it crashed" from "it was told to quit"). - Set
"crashLog": falsein the config to stop writing them. - Reporting problems is very welcome → Issues.
crash-*.logcontains only the technical information listed above — no instructions and no code.report.*.json(written by Node) may include environment variables. codiva excludes them automatically on Node versions that support it (23.3+), but older Node versions do include env vars such asANTHROPIC_API_KEY, so check the contents before sharing.
Up to 0.3.8 there was a bug where every render left memory that was never freed (in a little under a
day it reached Node's default ~4GB heap limit and exited abruptly with
Allocation failed - JavaScript heap out of memory. That kind of death leaves no crash-*.log, only
report.*.json). The cause was React running in its development build, which accumulates measurement
entries (performance.measure) on every render. 0.3.9 switches to the production build (which also made
rendering about 2.5× faster) and additionally discards measurement entries periodically.
If usage still grows on 0.3.9 or later, please report it at
Issues with ~/.codiva/logs/report.*.json attached.
The session detail log keeps the most recent 2,000 entries / 400,000 characters total and drops
anything older (a single extremely long entry is truncated with a trailing …). Holding an unbounded
log from a long-running session would make codiva itself run out of memory, and the complete record of
the conversation lives in each agent's own CLI (~/.claude/projects/ for Claude Code).
Note that when new lines are appended while you're scrolled up reading a log that has hit this limit, the view shifts slightly toward the newer end by however many lines were dropped (and any selection is cleared, for safety).
npm run dev # start the TUI with tsx (development)
npm test # vitest (with coverage)
npm run lint # biome check
npm run typecheck # tsc --noEmit
npm run build # tsup → dist/index.js (launcher shim) + dist/main-<hash>.js (the app)See docs/ for design documents (PRD / ARCHITECTURE / TECH_NOTES).
Coding conventions live in .claude/rules/ (layering, naming, i18n, Ink, the session domain, SDK integration, git/IO, testing), and step-by-step procedures for routine work such as adding a slash command are in .claude/skills/.
The overall index and the "what I want to do → which file to touch" map is in CLAUDE.md.
The documentation is authored in Japanese first; README.ja.md is the source of truth, and
docs/and.claude/are Japanese-only for now.
Publishing to npm uses npm Trusted Publishing (OIDC) and happens tokenlessly from GitHub Actions. Publishing a Release on GitHub is enough to run version syncing, the npm publish and the version-bump commit to main automatically. See docs/RELEASE.md for the first-time setup on GitHub and npm.