{"id":48789290,"url":"https://github.com/chenhaodev/pyopencode","last_synced_at":"2026-04-13T19:02:43.118Z","repository":{"id":349261480,"uuid":"1201639293","full_name":"chenhaodev/pyopencode","owner":"chenhaodev","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-05T02:57:52.000Z","size":137,"stargazers_count":1,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-05T03:24:21.847Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chenhaodev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-05T00:24:26.000Z","updated_at":"2026-04-05T02:57:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/chenhaodev/pyopencode","commit_stats":null,"previous_names":["chenhaodev/pyopencode"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/chenhaodev/pyopencode","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chenhaodev%2Fpyopencode","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chenhaodev%2Fpyopencode/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chenhaodev%2Fpyopencode/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chenhaodev%2Fpyopencode/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chenhaodev","download_url":"https://codeload.github.com/chenhaodev/pyopencode/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chenhaodev%2Fpyopencode/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31766482,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T15:25:13.801Z","status":"ssl_error","status_checked_at":"2026-04-13T15:25:09.162Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-13T19:02:41.856Z","updated_at":"2026-04-13T19:02:43.106Z","avatar_url":"https://github.com/chenhaodev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PyOpenCode\n\nTerminal **AI coding agent** for Python **3.11+**. It targets the same problem\nspace as **OpenCode** and **Claude Code**: a small, hackable ReAct core with\ndisciplined tool use (read before edit, `todo_write`, tiered permissions,\nstring-replacement `edit_file`). Backends are unified through\n[LiteLLM](https://github.com/BerriAI/litellm) (Claude, OpenAI, Gemini,\nQwen / DashScope, SiliconFlow), with an optional **Textual TUI** and LSP tools.\n\n**Ultrawork (compact)** is an optional high-push mode: extra system instructions\nfor finishing tasks, todos, parallel exploration, and verification — enabled\nwith **`--ultrawork` / `-U`**, **`[agent] ultrawork = true`**, or a message prefix\n`ulw ` / `ultrawork `. It is not the full oh-my-opencode Ultrawork stack (no\nbundled sub-agents beyond `dispatch_subagents`).\n\n---\n\n## Install\n\n**From this repository** (recommended until the package is on PyPI):\n\n```bash\ngit clone https://github.com/chenhaodev/pyopencode.git\ncd pyopencode\npip install -e .\n```\n\n### Using uv (recommended for this repo)\n\nLocked install (matches CI — includes dev tools if you add `--extra dev`):\n\n```bash\ncd pyopencode\nuv venv\nuv sync --frozen --extra dev    # or: uv sync --extra dev\n```\n\nRuntime-only (no pytest/ruff in the lock group — still pulls **click** + **litellm**):\n\n```bash\nuv sync --frozen\n```\n\nEditable install with **pip** front-end (must target the venv’s Python explicitly\nif the environment is not the project default):\n\n```bash\nuv venv\nuv pip install --python .venv/bin/python -e .\n# optional UI:  uv pip install --python .venv/bin/python -e \".[tui]\"\n```\n\n**Trap — empty venv + `UV_PROJECT_ENVIRONMENT`:** if `UV_PROJECT_ENVIRONMENT`\npoints at a separate, empty environment and you run **`uv pip install -e .`**\nwithout **`--python …/bin/python`**, uv may only link the editable package and\n**skip dependencies**, leading to `ModuleNotFoundError: click` when you run\n`pyopencode`. **Unset `UV_PROJECT_ENVIRONMENT`**, work from the repo root with\nthe default `.venv`, or always pass **`--python`** to `uv pip install` for your\ntarget interpreter. After install, **`pyopencode doctor`** checks **litellm**\n(and prints the interpreter path).\n\n| Extra | Install | Purpose |\n|-------|---------|---------|\n| *(none)* | `pip install -e .` | CLI + agent core |\n| **`[tui]`** | `pip install -e \".[tui]\"` | Textual chat UI (`--tui`, themes, grouped tool panels) |\n| **`[repomap]`** | `pip install -e \".[repomap]\"` | `get_repomap` with `prefer_tree_sitter=true` (tree-sitter) |\n| **`[lsp]`** | `pip install -e \".[lsp]\"` | Optional `pygls` for building/extending language servers |\n| **`[mcp]`** | `pip install -e \".[mcp]\"` | Placeholder extra; MCP stdio uses stdlib (see [Config snippets](#config-snippets-pyopencodeconfigtoml-or-pyopencodetoml)) |\n| **`[dev]`** | `pip install -e \".[dev]\"` | pytest, ruff, pre-commit, Textual (Pilot tests) |\n\nIf `python -m pyopencode` fails with **No module named 'click'**, your venv is\nmissing dependencies — use **`uv sync`** or **`uv pip install --python … -e .`**\nas above (not only the editable name).\n\n**From PyPI:** `pip install pyopencode` / `pip install \"pyopencode[tui]\"` after\nrelease ([RELEASING.md](RELEASING.md)). If `pip` reports *no matching\ndistribution*, use the clone flow above.\n\n---\n\n## Quick start\n\n1. **API keys** — set env vars or run `pyopencode auth login` (see [API keys](#api-keys)).\n2. **Sanity check:** `pyopencode doctor` (Python version, which keys are set,\n   whether Textual is installed).\n3. **Run:** `pyopencode \"summarize this repo\"`, `pyopencode -U \"bigger task\"` for\n   [Ultrawork](#config-snippets-pyopencodeconfigtoml-or-pyopencodetoml), or\n   `pyopencode --tui` for the UI (`--tui-theme light|dark`, `--tui-high-contrast`,\n   `--no-group-tools` optional).\n\nEntry points:\n\n- `pyopencode …`\n- `python -m pyopencode …` (same commands)\n\n---\n\n## API keys\n\nThe default provider is **Anthropic**. Use the key that matches your\n`--provider` / config.\n\n### Environment variables\n\n| Provider | Variable |\n|----------|----------|\n| Anthropic | `ANTHROPIC_API_KEY` |\n| OpenAI | `OPENAI_API_KEY` |\n| Gemini | `GEMINI_API_KEY` |\n| Qwen (DashScope) | `DASHSCOPE_API_KEY` |\n| SiliconFlow | `SILICONFLOW_API_KEY` |\n\n```bash\nexport ANTHROPIC_API_KEY=\"sk-ant-...\"\n# … add others as needed for your providers\n```\n\n### `auth login` (credentials file)\n\nStores keys in **`~/.pyopencode/credentials.json`** (file mode `0600`). Values\nare loaded into the process environment on each run (whitelist only; no extra\nkeys).\n\n```bash\npyopencode auth login\npyopencode auth login --provider openai   # skip the interactive menu\n```\n\nDo **not** commit keys. See [SECURITY.md](SECURITY.md).\n\n---\n\n## CLI\n\nGlobal options: **`pyopencode --help`**, **`pyopencode --version`**.\n\n### Command overview\n\n| Command | What it does |\n|---------|----------------|\n| **`run`** | Start the agent: interactive REPL, or one-shot if you pass a prompt. |\n| **`auth login`** | Save API keys to `~/.pyopencode/credentials.json`. |\n| **`sessions list`** | List saved chats for the **current working directory**. |\n| **`sessions list --all`** | List recent sessions across **all** project paths. |\n| **`sessions list --limit N`** | Cap rows (default 50). |\n| **`sessions path`** | Print path to the **SQLite** session database. |\n| **`config paths`** | Show global / project TOML, credentials, and DB paths (no secrets). |\n| **`config show`** | Print effective `model`, `provider`, `max_tokens` after merge. |\n| **`doctor`** | Python/OS, env key presence, `credentials.json`, Textual, `cwd`. |\n| **`version`** | Package version (same as `--version`). |\n\n**`run` flags (high level):** `--model` / `--provider`, `--resume` / `--session-id`,\n`--list-sessions`, **`--ultrawork` / `-U`**, `--tui` with `--tui-theme`,\n`--tui-high-contrast`, `--no-group-tools`. See **`pyopencode run --help`**.\n\n### `run` (agent)\n\n```bash\npyopencode run                          # interactive REPL\npyopencode run \"your task\"              # one-shot then REPL\npyopencode run --model gpt-4o --provider openai \"task\"\n\npyopencode run --list-sessions          # sessions for cwd, then exit\npyopencode run --resume                 # continue latest session for cwd\npyopencode run --session-id \u003cuuid\u003e      # continue a specific session\npyopencode run --tui                    # Textual UI (needs [tui])\npyopencode run --tui --resume\npyopencode run --tui --tui-theme light --tui-high-contrast\npyopencode run --tui --no-group-tools   # one Panel per tool instead of batch\npyopencode run -U \"refactor X\"          # Ultrawork (compact high-push mode)\n```\n\n**Shorthand:** if the first argument is **not** a known subcommand (`run`,\n`auth`, `sessions`, `config`, `doctor`) and not `--help` / `--version`, the CLI\nprepends **`run`**. So these are equivalent:\n\n```bash\npyopencode \"hello\"\npyopencode run \"hello\"\npyopencode --model gpt-4o --provider openai \"hello\"\n```\n\nUse **`pyopencode run --help`** for the full option list.\n\n### Config snippets (`~/.pyopencode/config.toml` or `.pyopencode.toml`)\n\n**Ultrawork (compact “push to finish” mode)**\n\n```toml\n[agent]\nultrawork = true\n```\n\nOr per run: `pyopencode run -U \"your task\"` / `pyopencode run --ultrawork --tui`. You can also\nstart a message with `ulw ` or `ultrawork ` (similar to OpenCode’s keyword), which enables the\nmode for that session and strips the prefix. This only appends extra system instructions — no\nseparate plugin agents. Shorthand works too: `pyopencode -U \"task\"` (CLI injects `run`).\n\n**Tool timeouts / bash cap / retries**\n\n```toml\n[tools]\nsync_timeout_sec = 120.0\nasync_timeout_sec = 180.0\nbash_max_timeout_sec = 300\nmax_retries = 1\nretry_delay_sec = 0.3\n```\n\n**MCP stdio servers** (then use agent tools `mcp_list_tools` / `mcp_call_tool`)\n\n```toml\n[mcp.servers.files]\ncommand = [\"npx\", \"-y\", \"@modelcontextprotocol/server-filesystem\", \"{root}\"]\n# optional: cwd = \"/tmp\", env = { KEY = \"value\" }\n```\n\n`{root}` expands to the absolute path of the current working directory when the\nserver is started.\n\n### Sessions (persistence)\n\n- State is stored in **`~/.pyopencode/sessions.db`** (SQLite), not a `sessions/`\n  folder. Inspect the path with **`pyopencode sessions path`**.\n- Sessions are **scoped by resolved project path** (the directory you run from).\n\n---\n\n## Textual TUI (`--tui`)\n\nRequires the **`[tui]`** extra (`pip install -e \".[tui]\"` or PyPI\n`pyopencode[tui]`).\n\n```bash\npyopencode --tui\npyopencode run --tui --resume\npyopencode run --tui -U \"large refactor\"   # Ultrawork + TUI\n```\n\n**Ultrawork in TUI:** use **`run --ultrawork`** / **`-U`**, set **`[agent] ultrawork`**\nin TOML, or prefix the first (or any) message with **`ulw `** / **`ultrawork `**.\n\n### Composer (bottom area)\n\n- **Multiline `TextArea`** — **Enter** inserts a newline.\n- **Send** — **Ctrl+Enter** or the **Send** button submits the message.\n- Placeholder text summarizes shortcuts.\n\n### Transcript (main log)\n\n- **User** and **Assistant** turns appear as **panels** with **timestamps**\n  (`You · HH:MM`, `Assistant · HH:MM`).\n- **Assistant** final text is rendered as **Markdown** when possible (code\n  blocks use a dark theme); on parse issues it falls back to plain text.\n- A **narrow strip** under the log shows the **current stream** with an\n  **“Assistant · typing…”** label while tokens arrive.\n- **Tool calls / results** are grouped in **panels** (`Tool · name`); errors use\n  a red border.\n- **Welcome** panel on first open (no `--tui` initial prompt) explains\n  send/focus shortcuts.\n- **Dim rules** separate completed turns.\n\n### Keyboard\n\n| Key | Action |\n|-----|--------|\n| **F1** | Help overlay (shortcuts \u0026 UI notes). |\n| **Ctrl+Enter** | Send message from the input area. |\n| **Ctrl+L** | Clear transcript **and** in-memory conversation (new session). |\n| **Ctrl+K** | Request context **compaction** (token saving). |\n| **Ctrl+Shift+G** | Focus the chat log (scroll with PgUp/PgDn / arrows). |\n| **Ctrl+C** | Quit. |\n\nDangerous tools open a **permission modal** (allow once / always / deny).\n\nLSP / `PYOPENCODE_PYRIGHT_JS` notes for contributors: [CONTRIBUTING.md](CONTRIBUTING.md).\n\n---\n\n## Configuration\n\nConfig is merged in order (later overrides earlier, deep merge where applicable):\n\n1. Built-in defaults (`pyopencode/config.py`)\n2. `~/.pyopencode/config.toml`\n3. `.pyopencode.toml` in the **current working directory**\n\nEnvironment-based API keys and `credentials.json` are applied as documented in\n**[API keys](#api-keys)**.\n\nInspect resolved paths and non-secret values:\n\n```bash\npyopencode config paths\npyopencode config show\n```\n\n---\n\n## Documentation\n\n| Doc | Contents |\n|-----|----------|\n| [CONTRIBUTING.md](CONTRIBUTING.md) | Dev setup, pytest, CI, pre-commit, LSP integration tests |\n| [ROADMAP.md](ROADMAP.md) | Status vs [TASK.md](TASK.md), next steps |\n| [CHANGELOG.md](CHANGELOG.md) | Release notes |\n| [RELEASING.md](RELEASING.md) | PyPI / tags |\n| [SECURITY.md](SECURITY.md) | Keys, `credentials.json`, staging checklist |\n| [TASK.md](TASK.md) | Short design reference |\n| [LICENSE](LICENSE) | MIT |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchenhaodev%2Fpyopencode","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchenhaodev%2Fpyopencode","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchenhaodev%2Fpyopencode/lists"}