Open source. Python. Runs in your terminal.
One terminal for the coding CLIs you already pay for.
thwip finds Codex, Claude Code, and the Antigravity CLI already signed in on your machine and lets you use them from one REPL. Switch mid-conversation without losing the thread, fail over the moment a usage limit hits, and keep one memory file per project that every CLI reads and maintains.
- No API keys needed. Each CLI keeps its own login, tools, sandbox, and plan. thwip drives it through its published protocol and relays its permission questions to you.
- Limit hits are a keypress. When one account is exhausted, pick another and your message is re-sent with context.
- Project memory travels. A
context.mdper project, filed into your Obsidian vault with cross-project links.
How it works
Four things happen between typing a message and reading an answer. Each one is a real mechanism you can inspect in the source.
-
01
It uses the sign-ins you already have
On startup thwip looks for
codex,claude, andagy. Codex is driven over its App Server JSON-RPC protocol, Claude Code and Antigravity through theirstream-jsonprint modes. Model lists come live from each CLI. No credential is read or copied; a direct API key for a provider takes precedence when you set one. -
02
Each provider keeps a warm session
The first message to a provider carries the conversation once. After that only new messages are sent: Codex resumes its thread on a long-lived process, Claude Code uses
--resume, Antigravity uses--conversation. Return to a provider after talking to another and it gets a short catch-up of what it missed. Session IDs are stored with the thwip session and validated on load. -
03
A usage limit becomes a switch, not a stop
When Codex reports "you've hit your usage limit", Claude Code rejects a rate limit, Google returns
RESOURCE_EXHAUSTED, or an API answers 429, thwip lists the ready alternatives and re-sends your unanswered message to the one you pick./limitsshows each account's 5-hour and 7-day windows, and thwip warns you once a window is 85 percent used and names the ready alternatives, so you can switch before the wall. When the conversation itself nears the model's context window, thwip offers to compact: older turns are summarised by a different ready provider, never the one running out, and recent turns stay word for word. Every path, command and error from the dropped turns is checked against the summary and added back if missing. -
04
The project remembers, whichever CLI you used
Every provider receives the project's
context.mdas instructions. After a real conversation,/memory updateasks the current model for a revision, shows you the diff, and writes only when you confirm. Nothing is written silently.
Project memory and your second brain
One file per project holds the durable state. A vault holds the map between projects.
context.md, in the project root
Snapshot, current work as Now / Blocked / Next, architecture notes, decisions with their reasons, known issues, and dated recent changes. /memory init creates it from the repository's detected stack. It is plain Markdown, so it works with any editor and any agent, including the CLIs run outside thwip.
Only the relevant sections are sent
The file is split at its headings and ranked against your message with BM25, the keyword formula behind search engines. The head always goes; the rest goes by relevance within a budget, and a warm session is only sent sections it has not seen. Measured with thwip's evaluation harness: on a file longer than the old cut-off, every provider answered a buried fact directly, with 593 characters of memory instead of 8,286.
An Obsidian vault, connected on first run
thwip detects vaults registered with the Obsidian desktop app, or creates a folder. Each project gets an index card; hub notes are generated per stack, area, and tag; a dashboard lists everything. Projects that share a stack, area, or tag link to each other, which is what makes the graph useful. Only notes carrying generated_by: thwip are ever overwritten. Your own notes stay yours.
Kept honest by review
Updates are proposed by the model you are already talking to, limited to facts that changed, and shown as a diff. You confirm. /quit offers this once after a conversation with content.
---
project: thwip
area: Developer Tools
status: active
updated: 2026-10-12
stack: [Python, rich, prompt-toolkit, httpx, Vite, JavaScript]
context_path: /Users/tanmay/Developer/Projects/thwip/context.md
generated_by: thwip
---
# thwip
## Links
- Area: [[Projects/thwip/Areas/Developer Tools|Developer Tools]]
- Stack: [[Projects/thwip/Stack/Python|Python]]
- Stack: [[Projects/thwip/Stack/Vite|Vite]]
## Related projects
- [[Projects/thwip/blend|blend]]: shares stack Python, stack Vite
- [[Projects/thwip/typetrace|typetrace]]: shares area Developer Tools, stack Vite
- [[Projects/thwip/obsidian|obsidian]]: shares area Developer Tools
- [[Projects/thwip/web-dashboard|web-dashboard]]: shares stack Python
Everything else it does
thwip is a wrapper, not a cage. Each CLI keeps its own tools, sandbox and settings. These are the parts that sit around them.
No restrictions of its own
Codex runs in its workspace sandbox and asks through thwip before anything beyond it. Claude Code keeps its own tools and your settings; anything that would prompt arrives as a y/N question in thwip. Antigravity cannot relay questions headless, so a declined tool is shown with the CLI's own explanation. /permissions prints this summary.
Your own models
/providers add registers any server that speaks the OpenAI chat protocol: llama.cpp, LM Studio, vLLM, Ollama's /v1, a company gateway, or a hosted API. Give it a name and a base URL; thwip lists the models from the server. It then appears in /switch, the fallback chain, and as a compaction worker.
Images go to the CLI's own input
@shot.png in a message sends the picture to the CLI's image input instead of reading it as text. Codex receives the local path, Claude Code receives the image inline, Antigravity receives the path and may open it with its own tools.
Compaction that keeps the exact items
Older turns are summarised by a different ready provider, never the one running out. Every path, command, error, code word, URL and version in the dropped turns is checked against the summary; anything missing is asked for again, then appended verbatim. Archived turns stay searchable with /recall.
A small handoff before a big switch
A switch sends the live conversation to the new CLI. When that transfer would fill a large share of the new window, thwip says how many tokens it is and offers to compact first. /handoff shows the same numbers without switching.
Start from your home folder
--project PATH always wins. Otherwise the folder you start in is the project. From your home folder, the last project is the default and a picker offers the rest, including New project, which creates the folder and its context.md. Sessions remember their project.
Antigravity stays open
Starting agy costs about 12 seconds of account checks before the model is asked, so thwip keeps one process per conversation and sends each new message to it. Measured: 41 s for the first turn, 22 s for the next ones. Codex keeps its app server open; Claude Code starts fast and resumes by session ID.
The commands you already type
Each CLI's own wording is accepted: /clis, /agents or /list; /resume or /continue; /init for the memory file; /status or /context; /permissions or /approvals. /switch changes the CLI, /model changes the model inside it.
Runs on Windows too
The package is pure Python. CI runs the full test suite, the offline harness and the build on a Windows runner as well as Linux, so one wheel runs on macOS, Linux and Windows. Native CLIs must themselves support the platform.
Auditable handoffs
/handoff previews what transfers, what is lost and the context fit before any switch, locally and without a model call. /export writes the conversation to Markdown with per-message model attribution, and /history shows which model answered each turn.
Architecture
A schematic of the moving parts. Commands and code run with your local user permissions; native CLIs apply their own sandboxes.
Benchmark
Thirteen evaluation tasks, each with a deterministic pass condition, run the same way against every signed-in CLI. Generated by python -m thwip.evals --provider all --html; the raw JSON is in the repository under docs/benchmark/.
| Provider | Passed | Mean latency s | Tool calls | Malformed | Errors |
|---|---|---|---|---|---|
| claude | 12/12 | 8.2 | 0 | 0 | 0 |
| 8/9 | 70.0 | 0 | 0 | 0 | |
| openai | 10/10 | 13.0 | 0 | 0 | 0 |
Every run
| Task | Provider | Result | Latency s | Input tokens | Note |
|---|---|---|---|---|---|
| tool-schemas-consistent | claude | pass | 0.0 | 0 | 8 tools consistent across both shapes; 4 invalid calls rejected |
| compaction-worker-never-active | claude | pass | 0.0 | 0 | active provider skipped; chain order honored; last resort handled |
| contract-stream-and-done | claude | pass | 2.2 | 22068 | 3 events, usage 22068/13 |
| exact-single-word | claude | pass | 2.9 | 22071 | answer was 'pong' |
| tool-read-file | claude | pass | 4.4 | 46503 | native CLI used its own tools; answer 'PLUM' |
| tool-path-containment | claude | n/a | 12.2 | 48076 | skipped: thwip adds no restrictions to native CLIs by design; the path guard covers thwip's own tools (the CLI did not read the file) |
| memory-deep-recall | claude | pass | 2.3 | 22309 | deep recall: answer 'THURSDAY' |
| handoff-recall | claude | pass | 4.2 | 22229 | handoff recall: answer 'MAGENTA' |
| compaction-recall | claude | pass | 8.2 | 22534 | compaction recall: answer 'OLIVE' |
| tool-edit-file | claude | pass | 7.0 | 74367 | edited via the CLI with its own tools (write questions approved by the harness) |
| missing-file-honesty | claude | pass | 34.6 | 74488 | reported missing: "I'll read the release notes file now.The file isn't at the p" |
| archive-recall | claude | pass | 18.9 | 22814 | archive recall: answer 'SAFFRON' |
| structured-summary | claude | pass | 10.1 | 0 | valid JSON with 3 context and 1 decision items |
| contract-stream-and-done | pass | 41.3 | 16680 | 3 events, usage 16680/106 | |
| exact-single-word | pass | 40.3 | 16681 | answer was 'pong' | |
| tool-read-file | pass | 54.0 | 33825 | native CLI used its own tools; answer 'PLUM' | |
| tool-path-containment | n/a | 85.9 | 4962 | skipped: thwip adds no restrictions to native CLIs by design; the path guard covers thwip's own tools (the CLI did not read the file) | |
| memory-deep-recall | pass | 71.1 | 16856 | deep recall: answer 'THURSDAY' | |
| handoff-recall | pass | 41.8 | 16787 | handoff recall: answer 'MAGENTA' | |
| compaction-recall | pass | 98.7 | 16888 | compaction recall: answer 'OLIVE' | |
| tool-edit-file | n/a | 47.4 | 33884 | skipped: the CLI cannot ask for write permission in headless mode and said so | |
| missing-file-honesty | fail | 47.8 | 21619 | answer did not clearly say the file is missing: '[Antigravity CLI produced no answer: it needed a tool permission that its headless mode ca' | |
| archive-recall | pass | 88.0 | 16987 | archive recall: answer 'SAFFRON' | |
| structured-summary | pass | 153.9 | 0 | valid JSON with 2 context and 1 decision items | |
| contract-stream-and-done | openai | pass | 12.7 | 18439 | 3 events, usage 18439/6 |
| exact-single-word | openai | pass | 7.3 | 17746 | answer was 'pong' |
| tool-read-file | openai | pass | 11.5 | 17907 | native CLI used its own tools; answer 'PLUM' |
| tool-path-containment | openai | n/a | 14.0 | 17974 | skipped: thwip adds no restrictions to native CLIs by design; the path guard covers thwip's own tools (the CLI read the file with its own tools) |
| memory-deep-recall | openai | pass | 9.0 | 17906 | deep recall: answer 'THURSDAY' |
| handoff-recall | openai | pass | 7.3 | 17832 | handoff recall: answer 'MAGENTA' |
| compaction-recall | openai | pass | 17.7 | 18638 | compaction recall: answer 'OLIVE' |
| tool-edit-file | openai | pass | 21.7 | 19827 | edited via the CLI with its own tools (write questions approved by the harness) |
| missing-file-honesty | openai | pass | 19.6 | 18304 | reported missing: 'I’ll inspect the release notes and summarize the newest entr' |
| archive-recall | openai | pass | 14.8 | 18733 | archive recall: answer 'SAFFRON' |
| structured-summary | openai | pass | 7.9 | 0 | valid JSON with 2 context and 1 decision items |
Method and caveats
- Tasks: streaming contract, exact instruction, read-file tool round trip, path guard, tool-schema consistency, deep recall from a long memory file, handoff recall, compaction recall, a real file edit, honesty about a missing file, recall of archived turns, a schema-valid structured summary, and the compaction worker rule.
- Native CLIs use their own tools, so most tool tasks score their final answer only; the note column says when that applied. The edit task checks the file on disk.
- Latency includes each CLI's own startup and system prompt. Antigravity's time is its own per-turn work, not the connection: 54 s on home Wi-Fi and 51 s on a phone hotspot with one process per turn. Since v1.17.0 thwip keeps one Antigravity process open per conversation, which pays its 12 s of account checks once: measured through the REPL, 41 s for the first turn and 22 s for the next ones.
- One machine, one day, one run per task; earlier full runs from 2026-10-04 are kept in the repository next to this one. Latency varies with provider load between runs; pass counts have been stable except for Antigravity, which moves by one depending on whether it reaches for a tool that its headless mode then declines.
- What the results mean. thwip adds no restriction to a native CLI by design: each keeps its own tools, sandbox and settings, and the questions it would ask you are relayed to the thwip prompt (Codex and Claude Code). So the path-guard task, which tests thwip's own tools, is marked not applicable for native CLIs. Antigravity cannot relay questions in headless mode and declines such tools itself, which is what its empty answers are; thwip prints its explanation. The file-edit task approves each CLI's own write question and all three edit the file correctly.
Providers
Installed CLI sign-ins are used directly. Direct API keys fetch the live model list from the provider. The IDs below are examples, not a fixed catalog.
| Provider | Example Models | Connection | Context Limit | Tools Supported |
|---|---|---|---|---|
| Anthropic | claude-fable-5, claude-opus-5, claude-sonnet-5, claude-haiku-4-5 |
Claude Code sign-in (claude), or ANTHROPIC_API_KEY |
Up to 1M tokens | Chat, Edit, Run, Term, Git |
| Google Gemini | gemini-3.1-pro-preview, gemini-3.7-flash, gemini-3.5-flash-lite |
Antigravity CLI sign-in (agy), or GEMINI_API_KEY |
1M+ tokens | Chat, Edit, Run, Term, Git |
| OpenAI | gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna |
Codex sign-in (codex App Server), or OPENAI_API_KEY |
1.05M tokens | Chat, Edit, Run, Term, Git |
| DeepSeek | deepseek-v4-pro, deepseek-v4-flash |
DEEPSEEK_API_KEY, ~/.thwip/config.toml |
1M tokens | Chat, Edit, Run, Term, Git |
| Groq | llama-3.3-70b-versatile, openai/gpt-oss-120b |
GROQ_API_KEY, ~/.thwip/config.toml |
128K tokens | Chat, Edit, Run, Term, Git |
| Ollama (Local) | deepseek-r1:latest, qwen2.5-coder:32b, llama3.3 |
http://localhost:11434 (Auto-detected) |
Local GPU/VRAM | Chat, Edit, Run, Term, Git |
| OpenRouter | anthropic/claude-opus-5, openai/gpt-5.6-terra, google/gemini-3.7-flash |
OPENROUTER_API_KEY, ~/.thwip/config.toml |
Provider-dependent | Chat, Edit, Run, Term, Git |
| Your own server | Whatever the server lists from /models |
/providers add, saved as [providers.<name>] |
Server-dependent | Chat, Edit, Run, Term, Git |
Commands
Full reference of slash commands and keyboard shortcuts inside the thwip REPL.
| Command / Shortcut | Arguments | Description | Example |
|---|---|---|---|
/switch |
<cli> [model] |
Switch the active CLI, or its model, mid-conversation; the conversation carries over as text | /switch google gemini-3.7-flash |
/agents or /clis |
None | Lists the discovered CLIs, direct API readiness, and your own providers | /agents |
/models |
[cli] |
Displays available models, context window limits, and pricing for a provider | /models openai |
/about or /guide |
None | Opens the in-depth system guide and navigation manual inside the terminal | /about |
/limits |
None | Token usage, each CLI account's 5-hour and 7-day windows with reset times, and spend for direct API use | /limits |
/tools |
None | Lists thwip's own tools (read, edit, write, run, bash, git), used only with direct API providers; native CLIs use their own | /tools |
/history |
None | Shows conversation turn summary and token breakdown across all models | /history |
/session save |
<name> |
Saves the conversation with its project and the CLIs' native session IDs | /session save refactor-auth |
/session load |
<name> |
Restores a saved session and resumes context from where you left off | /session load refactor-auth |
/model |
[id] |
Pick a model for the current CLI from a numbered list or by ID | /model |
/handoff |
[cli] [model] |
Preview what transfers, what is lost, and the context fit before switching | /handoff google |
/compact |
None | Summarize older turns on another ready provider with an exact-item coverage check; recent turns stay verbatim and the summary travels across providers | /compact |
/new and /resume or /continue |
[name] |
Start a fresh conversation (the current one is saved) or resume a saved one from a list | /resume |
/diff, /copy, /export |
[staged], none, [path] |
Show the project git diff, copy the last response, or export the conversation to Markdown with model attribution | /export notes.md |
!command |
Shell command | Run a shell command in the project without involving a model | !git status |
/prompt |
[set|save|reset] |
Your standing instructions for every CLI. thwip imposes no persona; it adds only a neutral note and the project memory | /prompt set Answer in Hindi |
/memory |
[init|update|sync|vault|link] |
Show, create, or model-update the project's memory file; file it into your second-brain vault | /memory update |
THWIP_*_BASE_URL |
Environment or [endpoints] |
Point any direct adapter at a proxy, gateway, or OpenAI-compatible server; keys stay provider keys | THWIP_OPENAI_BASE_URL=https://gateway/v1 |
@path |
In a message | Attach a project file's content to your message; paths outside the project are ignored | Explain @src/app.py |
/clear |
None | Clears current conversation memory and resets session tokens | /clear |
/permissions or /approvals |
None | Explain how each CLI's own permission questions reach you; thwip adds no restrictions of its own | /permissions |
/providers |
[add|remove|list] |
Register any OpenAI-compatible server as a provider, with its live model list | /providers add |
/project |
[path] |
Show or change the project folder; bare /project opens the picker with New project |
/project ~/Developer/api |
/recall |
<query> |
Search the turns archived by compaction and bring the matching ones back | /recall migration error |
/status or /context |
None | Current CLI, model, project, native session IDs and token stats | /status |
/key |
[provider] |
Enter a direct API key without placing it in prompt history | /key openai |
/detect |
None | Re-scan installed CLIs and reconnect their sign-ins | /detect |
/native |
<cli> |
Save the session and leave thwip for the installed CLI itself; a launcher, no context transfer | /native codex |
/session list, /cost |
None | List saved sessions with their project; show estimated session and cumulative cost for direct API use | /session list |
@shot.png |
In a message | Send an image to the CLI's own image input instead of reading it as text | What is in @shot.png |
Ctrl + S |
Hotkey | Interactive CLI and model switch prompt | Hotkey |
Ctrl + T |
Hotkey | Live CLI status, token counters, and active provider info | Hotkey |
Ctrl + C |
Hotkey | Interrupt active streaming response or tool execution | Hotkey |
/exit or /quit |
None | Exits thwip; offers a memory update once if the conversation had content | /exit |