neo
neo is a lightweight, autonomous terminal coding agent. Describe a task in plain language — neo plans the work, executes it with sandboxed tools, verifies the result, and streams everything live in your terminal.
you: neo -p "add rate limiting to the API"
neo: planning… wrote .neo/plans/rate-limit.md
✓ read src/api.py (212 lines)
✓ edited src/api.py (+38 −4) · ruff clean · pyright: 0 new errors
✓ bash: pytest -x -q → 41 passed
done in 34s · 12 steps · $0.021
Lightweight means: one install command, no daemon, no browser, no IDE extension, no account. Plain terminal, plain JSON config.
Capabilities
| Area | What you get |
|---|---|
| Agent loop | plan → act → verify; parallel tool calls, retries with backoff, doom-loop guard, transcript repair, auto-compaction |
| Tools (18) | read write edit apply_patch glob grep list_dir bash (+pty) webfetch websearch todo_write todo_read task question skill plan_enter plan_exit undo |
| Sandbox | every bash call can run in bubblewrap: user/IPC/PID/net namespaces, read-only root, hidden secrets, domain-filtered network |
| Providers | 228 in the catalog (OpenAI / Anthropic / Gemini protocols); any OpenAI-compatible endpoint works with just a base_url |
| Plan mode | changes are drafted to .neo/plans/, edits locked to the plan until you approve via plan_exit |
| Safety net | permission rules (allow/ask/deny), git snapshots before risky batches, undo, session forks, worktrees |
| Code intelligence | LSP diagnostics (pyright, tsserver, gopls, rust-analyzer, eslint) + formatters run after every edit |
| Extensibility | MCP servers, Python plugins with hooks, .neo/commands/*.md slash commands, .neo/tools/*.py custom tools, skills |
Install
Requires Python 3.10+.
Recommended — pipx (isolated, neo lands on your PATH):
sudo apt install pipx # debian / ubuntu
pipx ensurepath
pipx install neo-agnt
Log out and back in once (or source ~/.bashrc), then verify:
neo --version
Alternative — virtualenv:
python3 -m venv ~/.neo-venv
~/.neo-venv/bin/pip install neo-agnt
ln -s ~/.neo-venv/bin/neo ~/.local/bin/neo
For the sandboxed bash tool on Linux, also install:
sudo apt install bubblewrap socat
Then set an API key — environment variable, auth.json, or neo.json
(first match wins):
export ANTHROPIC_API_KEY="sk-..."
# ~/.config/neo/auth.json (created with 0600 permissions)
{ "anthropic": "sk-..." }
Troubleshooting
error: externally-managed-environment on pip install neo-agnt
Ubuntu 24.04+ blocks system-wide pip install (PEP 668). Do not fight it —
use one of the methods above:
# option 1: pipx (recommended for CLI apps)
sudo apt install pipx && pipx ensurepath && pipx install neo-agnt
# option 2: virtualenv
python3 -m venv ~/.neo-venv && ~/.neo-venv/bin/pip install neo-agnt
# option 3 (not recommended): override the guard
pip install --break-system-packages neo-agnt
neo: command not found after pipx install
pipx ensurepath adds ~/.local/bin to PATH — it takes effect on next
login. Either re-login or run source ~/.bashrc, then check
echo $PATH contains ~/.local/bin.
bubblewrap: command not found when running bash
The sandbox needs bubblewrap on PATH. Install it (sudo apt install bubblewrap), or set "sandbox": {"mode": "off"} in neo.json to run
bash directly (you lose isolation).
No provider configured / auth errors
neo resolves keys in this order: environment variable →
~/.config/neo/auth.json → neo.json. Run neo config to see the
resolved configuration and which provider it will use.
Quickstart
neo # interactive TUI
neo -p "fix the failing test in test_loop" # headless, print reply and exit
neo -p "..." --allow-all # headless, skip permission prompts
neo --resume # continue the last session
neo init # scaffold .neo/ (AGENTS.md, skills, commands, agents)
neo models # list providers and models
neo config # show resolved configuration
neo snapshot | neo restore | neo fork # git snapshots and session forks
Inside the TUI, / opens the command palette, @ completes file paths,
Ctrl+C cancels the running turn, and typing while the agent works queues
your message for the next turn. /login stores a provider API key,
/logout removes it, and /model switches models within the active
provider.
Configuration
neo.json — global at ~/.config/neo/neo.json, per-project at ./neo.json
or ./.neo/neo.json (project overrides global):
{
"model": "anthropic/claude-sonnet-4-6",
"small_model": "anthropic/claude-haiku-4-5",
"max_steps": 40,
"theme": "neo-dark",
"verify_commands": ["ruff check .", "pytest -x -q"],
"permissions": {
"bash": { "git *": "allow", "rm -rf *": "deny", "*": "ask" },
"edit": { "*": "ask" },
"webfetch": { "*": "allow" }
},
"providers": {
"my-proxy": { "base_url": "https://proxy.internal/v1", "api_key_env": "PROXY_KEY" }
},
"sandbox": { "mode": "auto", "network": "filtered" },
"mcp": {
"servers": {
"fs": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/data"] }
}
}
}
Permission rules are {tool: {pattern: allow|ask|deny}}, last match wins.
neo config prints the fully resolved configuration.
Sandbox
On Linux, bash runs inside bubblewrap — the same primitive OpenAI Codex
and Anthropic's sandbox runtime build on. The model sees a normal shell;
underneath it gets namespaces, a read-only root, and filtered egress.
{
"sandbox": {
"mode": "auto",
"network": "filtered",
"allowedDomains": ["github.com", "*.github.com", "pypi.org", "*.pypi.org"],
"deniedDomains": [],
"allowWrite": ["."],
"denyRead": ["~/.ssh", "~/.aws", "~/.gnupg"],
"denyWrite": [".env"],
"passEnv": ["PATH", "HOME", "LANG"],
"allowSecrets": [],
"privateTmp": true
}
}
mode:auto(warn once, run unsandboxed if bubblewrap is missing),strict(fail loudly instead of running unsandboxed),off.network:none(fully offline),filtered(isolated net namespace + domain-allowlist proxy),full.- Environment is cleared; only
passEnvsurvives, and secret-looking variables are stripped unless listed inallowSecrets.
Every bash result is tagged — [sandbox] active (network=filtered) —
so you always know what isolation was in effect.
Plan mode
For non-trivial work, neo drafts a plan first and locks edits to it:
you: /plan add oauth login
neo: wrote .neo/plans/oauth-login.md — 5 steps, 3 files
[plan_enter] edits restricted to the plan. review, then approve.
you: looks good, proceed
neo: [plan_exit] approved — executing as build turn…
✓ edited src/auth.py … ✓ bash: pytest → 58 passed
Plans live in .neo/plans/ as Markdown. Nothing outside the plan is
touched until you approve.
Safety net
neo snapshot # snapshot the worktree (git tree-hash handle)
neo snapshot --list # list snapshots
neo restore <id> --dry-run # preview what a restore would change
neo restore <id> # restore files from a snapshot
neo fork # fork the session (copy-on-write)
Snapshots are also taken automatically before risky tool batches, and the
undo tool (approval-gated) rolls back the last mutation.
MCP
Add any MCP server in neo.json; its tools appear as server_tool,
its prompts become slash commands:
{ "mcp": { "servers": {
"github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"] },
"remote": { "url": "https://mcp.internal/v1", "headers": { "Authorization": "Bearer ${MCP_TOKEN}" } }
} } }
Each server is isolated — one failing server never takes down the rest.
Plugins
Python plugins live in ~/.neo/plugins/ or <project>/.neo/plugins/.
A plugin is a module exposing hooks:
# ~/.neo/plugins/notify.py
def on_tool_after(event):
if event.tool == "bash" and event.result.is_error:
desktop_notify("neo", f"bash failed: {event.result.output[:120]}")
hooks = {"tool.execute.after": on_tool_after}
Available hooks: tool.execute.before/after, permission.ask,
chat.params, command.execute.before, session.end. Plugins can also
register their own tools.
Custom commands and tools
<!-- .neo/commands/review.md -->
---
description: review the current diff
agent: reviewer
---
Review `!git diff --stat` and leave findings on @src/.
$1/$ARGUMENTS interpolate arguments, !cmd runs shell upfront,
@path injects file contents. Python tools go in .neo/tools/*.py
and are loaded automatically.
Project layout
src/neo/
agent/ autonomous loop, permissions, sessions, plans, compaction
tools/ 18 built-in tools (files, shell+sandbox, web, todos, subagents…)
providers/ streaming clients + 228-entry provider catalog (JSON)
sandbox/ bubblewrap argv builder, capability detection, filtering proxy
mcp/ MCP clients (stdio, StreamableHTTP), tool/prompt discovery
lsp/ format/ language servers + formatters wired into every edit
vcs/ git snapshots, selective restore, forks, worktrees
plugins/ plugin loader and hook dispatch
commands/ .neo/commands/*.md slash-command engine
custom_tools/ .neo/tools/*.py loader
agents/ plan/ agent roster, per-agent toolsets, plan-mode enforcement
tui/ Textual interface: palette, dialogs, themes, session list
auth.py ~/.config/neo/auth.json key store (0600)
Tests
python -m pytest # 339 passed, 4 skipped
License
MIT. See LICENSE.
Release files for neo-agnt 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 | |
|---|---|---|---|
| neo_agnt-0.1.1.tar.gz | 217.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| neo_agnt-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 427.9 kB
Release files / neo_agnt-0.1.1.tar.gz
| Download URL | neo_agnt-0.1.1.tar.gz |
|---|---|
| Size | 217.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d01a14ea85594f2ffae9d413f02c19888b24a060cbda00fb5eb03fb50ce4e657
|
|
BLAKE2b-256 checksum How to use checksums |
95443c2775035990ce2206ac8ab0ff39f7f0ebb0fdad2bccfefb98417deeba96
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / neo_agnt-0.1.1-py3-none-any.whl
| Download URL | neo_agnt-0.1.1-py3-none-any.whl |
|---|---|
| Size | 210.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3944715e31bef7566250d4a8841d44a378c776e3b25200803d7291dfc4ee9ac7
|
|
BLAKE2b-256 checksum How to use checksums |
5ef473fe48462a0fc0887e1b2a5520b7f9e3d9e70b996c6cbc6372270decc750
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|