A powerful and concise coding agent for the command line, written in Python.
Codewright : a streaming LLM loop, a polymorphic tool runtime, multi-agent orchestration, session persistence, MCP tool servers, and an interactive TUI.
- Interactive TUI with streaming output, a status bar, and modal approvals.
- Non-interactive mode for one-shot prompts and scripting.
- Semantic file tools (
read_file,list_dir,find_files,search_text) plus a statefulshelltool family (bash-anchored, with background jobs and output paging) and transactionalapply_patchedits. - Task planning: the agent maintains a live, step-by-step plan
(
update_plan) that streams to the TUI so you can watch progress. - Permission profiles that gate destructive shell commands and out-of-workspace access.
- Multi-agent: spawn child agents that each run in their own isolated
session under a built-in role —
explorer(read-only investigation),worker(scoped execution), ordefault— and report back to the parent. The primary agent's role is set by[agent] default_role/CODEWRIGHT_DEFAULT_ROLE. - Project instructions: an
AGENTS.md(or.agents.md) found in the workspace — searched from the working directory up to the workspace root — is injected into every turn as guidance for the agent. - Skills: project-scoped instructions and workflows under
<workspace>/skills/(agentskills.io format). Author them by hand, and/or let the agent auto-distill reusable, test-verified knowledge into new skills; it pulls the relevant ones back in on later tasks via theskilltool. - MCP tool servers over stdio and streamable-http.
- Automatic context compaction: as history approaches the model's context window it is summarized down in place, so long sessions keep running.
- Session persistence + resume via append-only JSONL rollouts.
- Pluggable providers: OpenAI Chat Completions or Responses API, and any OpenAI-compatible endpoint (DeepSeek, Qwen/DashScope, Ollama, local gateways).
- Python 3.12 (
>=3.12,<3.13) uv- An API key for an LLM provider (see Configuration)
Install Codewright as a standalone tool so codewright is available in any directory:
uv tool install --editable . # run once from the repo root
uv tool update-shell # if the uv tools bin dir is not on your PATH (then reopen the terminal)--editable installs from source, so later code changes take effect without
reinstalling. Omit it for a pinned snapshot (re-run with --reinstall to update).
pipx install --editable . works too.
uv sync # install deps incl. the dev group
uv run codewright --help # run without a global install (inside the repo)# Interactive TUI in the current directory (bare command == `codewright tui`):
codewright
# Interactive TUI in a specific workspace:
codewright --workspace path/to/project
# One non-interactive turn, prints the final message:
codewright run "explain what this project does" --workspace .
# List and resume saved sessions:
codewright list-sessions
codewright resume <session_id> # reopen in the TUI
codewright resume <session_id> --message "now add tests" # one turn, then exitSettings are read from ~/.codewright/config.toml. Effective-value precedence
(highest wins): CLI flags > environment variables > config file > built-in defaults.
A model API key is the only thing you must supply — everything else has a default. Provide a key via any one of:
| Source | How |
|---|---|
OPENAI_API_KEY |
export it in your shell (simplest) |
CODEWRIGHT_API_KEY |
export it in your shell |
[llm] api_key_env |
name of an env var holding the key (keeps the secret out of the file) |
[llm] api_key |
the key inline (discouraged; plaintext) |
A minimal ~/.codewright/config.toml:
[llm]
model = "gpt-4o"
api_key_env = "OPENAI_API_KEY"
# base_url = "https://api.deepseek.com/v1" # for OpenAI-compatible providersSee examples/config.example.toml for every
option (model/provider, context window, permission profile, shell path, skills
test runners, and MCP servers) with its default and explanation.
Useful environment overrides: CODEWRIGHT_MODEL, CODEWRIGHT_API_KEY,
CODEWRIGHT_API_KEY_ENV, CODEWRIGHT_BASE_URL (or OPENAI_BASE_URL),
CODEWRIGHT_PERMISSION_PROFILE, CODEWRIGHT_DEFAULT_ROLE, CODEWRIGHT_SHELL_PATH,
CODEWRIGHT_MAX_CONTEXT_TOKENS, CODEWRIGHT_COMPACT_THRESHOLD.
| Command | Description |
|---|---|
codewright |
Launch the interactive TUI in the current directory (alias for tui). |
codewright tui |
Launch the interactive TUI. |
codewright run <prompt> |
Run a single non-interactive turn and print the final message. |
codewright resume <session_id> |
Resume a saved session (TUI, or --message for one turn). |
codewright list-sessions |
List saved sessions in the workspace. |
Common flags: --workspace <dir> (default: current dir), --model,
--provider-base-url, --api-style {chat_completions,responses},
--permission-profile {read_only,workspace_write,dangerous}. run also accepts
--max-context-tokens, --print-session-id, and --no-persist. Run
codewright <command> --help for the full list.
| Key | Action |
|---|---|
Ctrl-C |
Interrupt the current turn |
Ctrl-D |
Quit |
PageUp / PageDown |
Scroll history |
Ctrl-Home / Ctrl-End |
Jump to top / bottom |
F1 or ? |
Toggle status details |
y / s / n / a |
On an approval prompt: approve / approve for session / deny / abort |
The active permission profile decides which shell commands run without asking:
read_only— no shell exec at all; only the read/search file tools.workspace_write(default) — exec is auto-allowed inside the workspace; destructive commands (rm -rf,git reset --hard, network access, …) and any cwd outside the workspace still prompt for approval.dangerous— nothing is auto-allowed; every exec prompts.
The workspace root is a hard boundary: reads and writes outside it require approval.
Each session is recorded as an append-only JSONL rollout under
<workspace>/.codewright/sessions/<session_id>.jsonl. run persists by default
(disable with --no-persist); the TUI always persists. Use list-sessions and
resume to pick up where you left off.
The engine sits behind two asyncio.Queues: front ends submit Op instances
and consume Event instances, and never touch Session internals. A single
submission_loop drives run_turn, which streams the model, dispatches tool
calls through the executor, compacts history, and emits events.
uv run pytest # full test suite
uv run pytest tests/test_run_turn.py # a single file
uv run ruff check src tests # lint
uv run ruff check --fix src tests # lint + autofix
uv run lint-imports # enforce the layering contracts (.importlinter)