closecode-ai 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- closecode_ai-0.5.0/PKG-INFO +284 -0
- closecode_ai-0.5.0/README.md +259 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/agent.py +10 -1
- closecode_ai-0.5.0/closecode_ai.egg-info/PKG-INFO +284 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/closecode_ai.egg-info/SOURCES.txt +15 -1
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/closecode_ai.egg-info/requires.txt +3 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/closecode_ai.egg-info/top_level.txt +5 -0
- closecode_ai-0.5.0/harness.py +529 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/main.py +9 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/modes.py +3 -1
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/pyproject.toml +19 -1
- closecode_ai-0.5.0/tests/test_config.py +175 -0
- closecode_ai-0.5.0/tests/test_guardrails.py +357 -0
- closecode_ai-0.5.0/tests/test_harness.py +678 -0
- closecode_ai-0.5.0/tests/test_llm.py +332 -0
- closecode_ai-0.5.0/tests/test_modes.py +262 -0
- closecode_ai-0.5.0/tests/test_search.py +274 -0
- closecode_ai-0.5.0/tests/test_session.py +395 -0
- closecode_ai-0.5.0/tests/test_todos.py +271 -0
- closecode_ai-0.5.0/tests/test_token_tracker.py +241 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/tools.py +33 -1
- closecode_ai-0.5.0/tui.py +372 -0
- closecode_ai-0.5.0/tui_messages.py +167 -0
- closecode_ai-0.5.0/tui_theme.py +170 -0
- closecode_ai-0.5.0/tui_widgets.py +421 -0
- closecode_ai-0.5.0/ui.py +272 -0
- closecode_ai-0.5.0/ui_prompts.py +180 -0
- closecode_ai-0.5.0/ui_theme.py +79 -0
- closecode_ai-0.3.0/PKG-INFO +0 -252
- closecode_ai-0.3.0/README.md +0 -229
- closecode_ai-0.3.0/closecode_ai.egg-info/PKG-INFO +0 -252
- closecode_ai-0.3.0/harness.py +0 -200
- closecode_ai-0.3.0/tui.py +0 -1041
- closecode_ai-0.3.0/ui.py +0 -422
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/closecode_ai.egg-info/dependency_links.txt +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/closecode_ai.egg-info/entry_points.txt +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/config.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/debug_response.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/guardrails.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/llm.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/mcp_tools.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/render.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/search.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/session.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/setup.cfg +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/todos.py +0 -0
- {closecode_ai-0.3.0 → closecode_ai-0.5.0}/token_tracker.py +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: closecode-ai
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: CloseCode — an agentic terminal coding assistant (LangGraph + OpenRouter + MCP)
|
|
5
|
+
Author: Om Gite
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: langchain
|
|
10
|
+
Requires-Dist: langgraph
|
|
11
|
+
Requires-Dist: langchain-huggingface
|
|
12
|
+
Requires-Dist: langsmith
|
|
13
|
+
Requires-Dist: huggingface_hub
|
|
14
|
+
Requires-Dist: python-dotenv
|
|
15
|
+
Requires-Dist: requests
|
|
16
|
+
Requires-Dist: langchain-openai
|
|
17
|
+
Requires-Dist: langchain-mcp-adapters
|
|
18
|
+
Requires-Dist: mcp-server-git
|
|
19
|
+
Requires-Dist: langchain-openrouter
|
|
20
|
+
Requires-Dist: rich
|
|
21
|
+
Requires-Dist: pyfiglet
|
|
22
|
+
Requires-Dist: textual
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
25
|
+
|
|
26
|
+
<div align="center">
|
|
27
|
+
|
|
28
|
+
# CloseCode
|
|
29
|
+
|
|
30
|
+
**An agentic terminal coding assistant — LangGraph loop, sandboxed execution, and a full-screen TUI.**
|
|
31
|
+
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
[](https://www.python.org/downloads/)
|
|
34
|
+
<!-- Once CI exists, add: [](https://github.com/omgite333/CLOSECODE/actions) -->
|
|
35
|
+
|
|
36
|
+
<!-- SCREENSHOT: full-screen TUI on startup — banner, model name, workdir, tool list.
|
|
37
|
+
Capture: run `closecode`, wait for the banner, screenshot the terminal.
|
|
38
|
+
Save as: docs/banner.png -->
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
CloseCode reads a task, decides what to do, runs a tool, looks at the result, and repeats —
|
|
46
|
+
the same loop as OpenCode or Claude Code, built from scratch on LangGraph (agent loop),
|
|
47
|
+
LangChain (tool + model abstraction), and OpenRouter (model access). It runs entirely in
|
|
48
|
+
your terminal, in a full-screen TUI or a classic line-based mode, and every action it takes
|
|
49
|
+
on your files or shell goes through a sandboxed harness with permission prompts.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Why
|
|
53
|
+
|
|
54
|
+
Most people can't `pip install openai` and get an agent — the hard part isn't calling a
|
|
55
|
+
model, it's the loop around it: binding the right tools per mode, confirming risky actions,
|
|
56
|
+
persisting sessions, and stopping the model from doing something destructive. CloseCode is
|
|
57
|
+
that scaffolding, built openly, with open models via OpenRouter instead of a closed API.
|
|
58
|
+
|
|
59
|
+
## Features
|
|
60
|
+
|
|
61
|
+
- **Full-screen TUI** (Textual) or a classic line-based REPL — same agent loop underneath,
|
|
62
|
+
switchable with `--no-tui`
|
|
63
|
+
- **Sandboxed execution** — file and shell operations are confined to a working directory;
|
|
64
|
+
path traversal (`../../etc/passwd`, absolute paths, Windows drive prefixes) is blocked
|
|
65
|
+
at the harness level, not just by convention
|
|
66
|
+
- **Plan / Build modes** — Plan mode literally never binds write/edit/bash tools to the
|
|
67
|
+
model, so it can explore and propose a plan with no possibility of a side effect
|
|
68
|
+
- **Guardrails** — four layers: input-scope filtering, destructive-command blocking
|
|
69
|
+
(`rm -rf /`, fork bombs, `curl | sh`), malicious-write scanning, and output redaction
|
|
70
|
+
- **Persistent sessions** — SQLite-backed conversation history; `/resume`, `/sessions`,
|
|
71
|
+
`--continue`
|
|
72
|
+
- **Todo tracking** — the agent maintains a visible task list for multi-step work
|
|
73
|
+
(`todo_write` / `todo_read`)
|
|
74
|
+
- **Code-aware search** — dedicated `grep`/`glob` tools (not raw shell), sandboxed and
|
|
75
|
+
available even in Plan mode since they're read-only
|
|
76
|
+
- **Git tools via MCP** — status, diff, log, commit, branches, through `mcp-server-git`
|
|
77
|
+
- **Any OpenRouter model** — free-tier models by default; switch with `/model`, browse
|
|
78
|
+
with `/models`
|
|
79
|
+
- **Per-user config** — your API key and default model persist in `~/.closecode/config.json`
|
|
80
|
+
(0600 permissions), independent of which directory you launch from
|
|
81
|
+
|
|
82
|
+
## Install
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pipx install closecode-ai
|
|
86
|
+
closecode
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
<!-- Adjust this section once published — this is the target state, not necessarily
|
|
90
|
+
live yet. Until it's on PyPI, use the git-based install below instead. -->
|
|
91
|
+
|
|
92
|
+
**From source, right now:**
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git clone https://github.com/omgite333/CLOSECODE.git
|
|
96
|
+
cd CLOSECODE
|
|
97
|
+
pip install -e .
|
|
98
|
+
closecode
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`pipx` is recommended over `pip` for the packaged version since it installs CLI tools into
|
|
102
|
+
an isolated environment and puts them straight on your `PATH`.
|
|
103
|
+
|
|
104
|
+
## Tests
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pip install -e ".[dev]"
|
|
108
|
+
pytest
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
429 tests covering guardrails, sandbox path resolution, undo/checkpoints, background
|
|
112
|
+
processes, todo-store invariants, plan-mode tool filtering, session persistence, config
|
|
113
|
+
permissions, and the search tools. The suite is fully isolated: `config.py` and
|
|
114
|
+
`session.py` are redirected to temp dirs, no test touches the network, and nothing is
|
|
115
|
+
written to your real `~/.closecode`.
|
|
116
|
+
|
|
117
|
+
Some tests are marked `xfail` for **known bugs** rather than fixed behaviour — they
|
|
118
|
+
document a gap and will flip to passing when it's fixed. Run `pytest -rx` to list them.
|
|
119
|
+
|
|
120
|
+
## Quick start
|
|
121
|
+
|
|
122
|
+
1. Get a free API key at [openrouter.ai/settings/keys](https://openrouter.ai/settings/keys)
|
|
123
|
+
2. Run `closecode` — on first launch it prompts for the key (input hidden) and offers to
|
|
124
|
+
save it to `~/.closecode/config.json` so you're not asked again
|
|
125
|
+
3. Type a task:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
> find every place we call the old auth API and list the files
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
<!-- SCREENSHOT: a permission prompt in action — "Allow agent to run: `grep -r ...`?"
|
|
132
|
+
This is worth showing on its own since it's the project's core safety story.
|
|
133
|
+
Save as: docs/permission-prompt.png -->
|
|
134
|
+

|
|
135
|
+
|
|
136
|
+
## How it works
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
user input
|
|
140
|
+
│
|
|
141
|
+
▼
|
|
142
|
+
┌──────────┐ binds tools for current mode ┌───────────────┐
|
|
143
|
+
│ agent │ ───────────────────────────────▶ │ LangGraph loop│
|
|
144
|
+
│ (llm.py) │ │ (agent.py) │
|
|
145
|
+
└──────────┘ └──────┬────────┘
|
|
146
|
+
│ tool call
|
|
147
|
+
▼
|
|
148
|
+
┌───────────────────────┐
|
|
149
|
+
│ Harness │
|
|
150
|
+
│ sandboxed fs + shell │──▶ guardrails.py
|
|
151
|
+
└───────────────────────┘ (blocks/scans)
|
|
152
|
+
│ result
|
|
153
|
+
▼
|
|
154
|
+
┌──────────────────────┐
|
|
155
|
+
│ Renderer interface │
|
|
156
|
+
│ (render.py) │
|
|
157
|
+
└──────┬──────────┬────┘
|
|
158
|
+
▼ ▼
|
|
159
|
+
tui.py + tui_*.py ui.py + ui_*.py
|
|
160
|
+
(Textual) (classic)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
A single `Renderer` interface (`render.py`) decouples the agent loop from presentation, so
|
|
164
|
+
the Textual TUI and the classic REPL are two implementations of the same contract rather
|
|
165
|
+
than two copies of the agent logic.
|
|
166
|
+
|
|
167
|
+
Each frontend is a small set of single-purpose modules behind a facade, so the entry module
|
|
168
|
+
stays readable and the parts can be reused or tested on their own:
|
|
169
|
+
|
|
170
|
+
| Frontend | Facade | Split into |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| Textual | `tui.py` — app, message handlers, input, suggestions | `tui_theme.py` (banner, palette, CSS) · `tui_messages.py` (Message classes, `TuiRenderer`) · `tui_widgets.py` (conversation blocks, input box, modals, `ConfirmBridge`) |
|
|
173
|
+
| Classic | `ui.py` — the `print_*` / `stream_*` renderers | `ui_theme.py` (shared `Console`, palette, banner) · `ui_prompts.py` (permission dialog, input, `EscListener`) |
|
|
174
|
+
|
|
175
|
+
Each facade re-exports the names its split modules define, so `from ui import ...` and
|
|
176
|
+
`from tui import ...` keep working exactly as before.
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## Modes
|
|
180
|
+
|
|
181
|
+
| Mode | Command | Tools available | Use for |
|
|
182
|
+
|---|---|---|---|
|
|
183
|
+
| **Plan** | `/plan` | Read-only: `read_file`, `list_dir`, `grep`, `glob`, `tavily_search` | Exploring a codebase, proposing an approach with zero risk of a side effect |
|
|
184
|
+
| **Build** | `/build` | Everything, including `write_file`, `edit_file`, `bash`, `run_tests`, git tools | Actually making changes |
|
|
185
|
+
|
|
186
|
+
Plan mode isn't a prompt instruction the model can ignore — the write/edit/bash tools are
|
|
187
|
+
never bound to the model in the first place.
|
|
188
|
+
|
|
189
|
+
## Tools
|
|
190
|
+
|
|
191
|
+
| Tool | Description |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `read_file` / `write_file` / `edit_file` | Read, overwrite, or targeted find-and-replace on a file |
|
|
194
|
+
| `list_dir` | List a directory's contents |
|
|
195
|
+
| `glob` / `grep` | Find files by pattern / search file contents by regex — sandboxed, available in Plan mode |
|
|
196
|
+
| `bash` | Run a shell command in the sandbox (capped timeout, guardrail-checked) |
|
|
197
|
+
| `run_tests` | Run the project's test command and report pass/fail |
|
|
198
|
+
| `todo_write` / `todo_read` | Maintain a visible multi-step task list |
|
|
199
|
+
| `tavily_search` | Web search for current docs/APIs (requires `TAVILY_API_KEY`) |
|
|
200
|
+
| git tools (`status`, `diff`, `log`, `commit`, branches) | Via `mcp-server-git`, enabled with `AGENT_ENABLE_GIT=true` |
|
|
201
|
+
|
|
202
|
+
## Commands
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
/plan, /build switch modes
|
|
206
|
+
/models [filter] list OpenRouter models (free-tier first)
|
|
207
|
+
/model <id|number> switch model
|
|
208
|
+
/key update your OpenRouter API key
|
|
209
|
+
/sessions list saved conversations
|
|
210
|
+
/resume <id> switch to a saved session
|
|
211
|
+
/delete <id> delete a saved session
|
|
212
|
+
/usage token usage for this session
|
|
213
|
+
/clear start a new session
|
|
214
|
+
/help show all commands
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Configuration
|
|
218
|
+
|
|
219
|
+
All settings are environment variables (`.env`, or exported in your shell) — see
|
|
220
|
+
`.env.example` for the full list. Key ones:
|
|
221
|
+
|
|
222
|
+
| Variable | Default | Purpose |
|
|
223
|
+
|---|---|---|
|
|
224
|
+
| `OPENROUTER_API_KEY` | — | Required (or saved via `/key` into `~/.closecode/config.json`) |
|
|
225
|
+
| `OPENROUTER_MODEL` | `nvidia/nemotron-3.5-lightning:free` | Default model |
|
|
226
|
+
| `AGENT_WORKDIR` | `./sandbox` | Directory the agent is confined to |
|
|
227
|
+
| `AGENT_AUTO_APPROVE` | `false` | Skip permission prompts (guardrails still apply) |
|
|
228
|
+
| `AGENT_ENABLE_GIT` | `false` | Load git tools via MCP |
|
|
229
|
+
| `AGENT_DISABLE_GUARDRAILS` | `false` | Disable guardrails — trusted/isolated testing only |
|
|
230
|
+
| `TAVILY_API_KEY` | — | Enables `tavily_search` |
|
|
231
|
+
| `LANGCHAIN_API_KEY` | — | Enables LangSmith tracing |
|
|
232
|
+
|
|
233
|
+
## Safety model
|
|
234
|
+
|
|
235
|
+
This is layered defense, not a single mechanism:
|
|
236
|
+
|
|
237
|
+
1. **Input scope** — off-topic requests are redirected; clearly malicious requests
|
|
238
|
+
(keyloggers, phishing kits, account-hacking) are refused before reaching the model
|
|
239
|
+
2. **Command blocking** — destructive shell commands are blocked before execution, even
|
|
240
|
+
with auto-approve on
|
|
241
|
+
3. **Write scanning** — file writes/edits are scanned for malware indicators before
|
|
242
|
+
they're applied
|
|
243
|
+
4. **Output redaction** — flagged content is scrubbed from conversation history
|
|
244
|
+
5. **Sandboxed paths** — every file operation resolves through the harness, which refuses
|
|
245
|
+
to write outside the configured working directory regardless of how the path is phrased
|
|
246
|
+
|
|
247
|
+
These are conservative heuristics layered on top of the sandbox and per-action permission
|
|
248
|
+
prompts — not a formal guarantee. Shell commands currently run on the host inside a
|
|
249
|
+
path-restricted directory, not inside a container; see [Roadmap](#roadmap).
|
|
250
|
+
|
|
251
|
+
## A note on model choice
|
|
252
|
+
|
|
253
|
+
Tool-calling reliability varies a lot across open models — this is the single biggest
|
|
254
|
+
factor in how well the agent performs. Frontier closed models are heavily trained for
|
|
255
|
+
reliable tool use; open models are improving but inconsistent. Roughly in order of
|
|
256
|
+
reliability, worth trying via `/model`:
|
|
257
|
+
|
|
258
|
+
- `qwen/qwen-2.5-coder-32b-instruct` — code-specialized, solid tool use, free tier available
|
|
259
|
+
- `qwen/qwen-2.5-72b-instruct` — strong, reliable, paid
|
|
260
|
+
- `meta-llama/llama-3.1-70b-instruct` — strong, reliable, paid
|
|
261
|
+
- `meta-llama/llama-3.1-8b-instruct` — fastest/cheapest, least reliable
|
|
262
|
+
|
|
263
|
+
If a smaller model frequently fails to call tools or hallucinates arguments, that's a
|
|
264
|
+
known gap between open and closed models on agentic tasks, not a bug here. Switching
|
|
265
|
+
models is the first thing to try before changing anything else.
|
|
266
|
+
|
|
267
|
+
## Roadmap
|
|
268
|
+
|
|
269
|
+
- [x] Test suite (`pytest tests/`) — guardrails, sandbox path resolution, todo-store invariants
|
|
270
|
+
- [ ] CI (run the suite on push)
|
|
271
|
+
- [ ] Docker-based sandbox for shell execution, not just path restriction
|
|
272
|
+
- [ ] Client/server split — `build_graph()` behind FastAPI/WebSocket, thin streaming client
|
|
273
|
+
- [ ] PyPI release + prebuilt binaries (PyInstaller) for no-Python-required installs
|
|
274
|
+
- [ ] Homebrew tap
|
|
275
|
+
|
|
276
|
+
## Contributing
|
|
277
|
+
|
|
278
|
+
Issues and PRs welcome. If you're adding a tool, follow the pattern in `tools.py` /
|
|
279
|
+
`search.py`: bind state via a module-level `bind_*()` function, keep it sandboxed to the
|
|
280
|
+
harness root, and add it to the Plan-mode allowlist only if it's genuinely read-only.
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# CloseCode
|
|
4
|
+
|
|
5
|
+
**An agentic terminal coding assistant — LangGraph loop, sandboxed execution, and a full-screen TUI.**
|
|
6
|
+
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://www.python.org/downloads/)
|
|
9
|
+
<!-- Once CI exists, add: [](https://github.com/omgite333/CLOSECODE/actions) -->
|
|
10
|
+
|
|
11
|
+
<!-- SCREENSHOT: full-screen TUI on startup — banner, model name, workdir, tool list.
|
|
12
|
+
Capture: run `closecode`, wait for the banner, screenshot the terminal.
|
|
13
|
+
Save as: docs/banner.png -->
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
CloseCode reads a task, decides what to do, runs a tool, looks at the result, and repeats —
|
|
21
|
+
the same loop as OpenCode or Claude Code, built from scratch on LangGraph (agent loop),
|
|
22
|
+
LangChain (tool + model abstraction), and OpenRouter (model access). It runs entirely in
|
|
23
|
+
your terminal, in a full-screen TUI or a classic line-based mode, and every action it takes
|
|
24
|
+
on your files or shell goes through a sandboxed harness with permission prompts.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Why
|
|
28
|
+
|
|
29
|
+
Most people can't `pip install openai` and get an agent — the hard part isn't calling a
|
|
30
|
+
model, it's the loop around it: binding the right tools per mode, confirming risky actions,
|
|
31
|
+
persisting sessions, and stopping the model from doing something destructive. CloseCode is
|
|
32
|
+
that scaffolding, built openly, with open models via OpenRouter instead of a closed API.
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- **Full-screen TUI** (Textual) or a classic line-based REPL — same agent loop underneath,
|
|
37
|
+
switchable with `--no-tui`
|
|
38
|
+
- **Sandboxed execution** — file and shell operations are confined to a working directory;
|
|
39
|
+
path traversal (`../../etc/passwd`, absolute paths, Windows drive prefixes) is blocked
|
|
40
|
+
at the harness level, not just by convention
|
|
41
|
+
- **Plan / Build modes** — Plan mode literally never binds write/edit/bash tools to the
|
|
42
|
+
model, so it can explore and propose a plan with no possibility of a side effect
|
|
43
|
+
- **Guardrails** — four layers: input-scope filtering, destructive-command blocking
|
|
44
|
+
(`rm -rf /`, fork bombs, `curl | sh`), malicious-write scanning, and output redaction
|
|
45
|
+
- **Persistent sessions** — SQLite-backed conversation history; `/resume`, `/sessions`,
|
|
46
|
+
`--continue`
|
|
47
|
+
- **Todo tracking** — the agent maintains a visible task list for multi-step work
|
|
48
|
+
(`todo_write` / `todo_read`)
|
|
49
|
+
- **Code-aware search** — dedicated `grep`/`glob` tools (not raw shell), sandboxed and
|
|
50
|
+
available even in Plan mode since they're read-only
|
|
51
|
+
- **Git tools via MCP** — status, diff, log, commit, branches, through `mcp-server-git`
|
|
52
|
+
- **Any OpenRouter model** — free-tier models by default; switch with `/model`, browse
|
|
53
|
+
with `/models`
|
|
54
|
+
- **Per-user config** — your API key and default model persist in `~/.closecode/config.json`
|
|
55
|
+
(0600 permissions), independent of which directory you launch from
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pipx install closecode-ai
|
|
61
|
+
closecode
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
<!-- Adjust this section once published — this is the target state, not necessarily
|
|
65
|
+
live yet. Until it's on PyPI, use the git-based install below instead. -->
|
|
66
|
+
|
|
67
|
+
**From source, right now:**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/omgite333/CLOSECODE.git
|
|
71
|
+
cd CLOSECODE
|
|
72
|
+
pip install -e .
|
|
73
|
+
closecode
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`pipx` is recommended over `pip` for the packaged version since it installs CLI tools into
|
|
77
|
+
an isolated environment and puts them straight on your `PATH`.
|
|
78
|
+
|
|
79
|
+
## Tests
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install -e ".[dev]"
|
|
83
|
+
pytest
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
429 tests covering guardrails, sandbox path resolution, undo/checkpoints, background
|
|
87
|
+
processes, todo-store invariants, plan-mode tool filtering, session persistence, config
|
|
88
|
+
permissions, and the search tools. The suite is fully isolated: `config.py` and
|
|
89
|
+
`session.py` are redirected to temp dirs, no test touches the network, and nothing is
|
|
90
|
+
written to your real `~/.closecode`.
|
|
91
|
+
|
|
92
|
+
Some tests are marked `xfail` for **known bugs** rather than fixed behaviour — they
|
|
93
|
+
document a gap and will flip to passing when it's fixed. Run `pytest -rx` to list them.
|
|
94
|
+
|
|
95
|
+
## Quick start
|
|
96
|
+
|
|
97
|
+
1. Get a free API key at [openrouter.ai/settings/keys](https://openrouter.ai/settings/keys)
|
|
98
|
+
2. Run `closecode` — on first launch it prompts for the key (input hidden) and offers to
|
|
99
|
+
save it to `~/.closecode/config.json` so you're not asked again
|
|
100
|
+
3. Type a task:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
> find every place we call the old auth API and list the files
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
<!-- SCREENSHOT: a permission prompt in action — "Allow agent to run: `grep -r ...`?"
|
|
107
|
+
This is worth showing on its own since it's the project's core safety story.
|
|
108
|
+
Save as: docs/permission-prompt.png -->
|
|
109
|
+

|
|
110
|
+
|
|
111
|
+
## How it works
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
user input
|
|
115
|
+
│
|
|
116
|
+
▼
|
|
117
|
+
┌──────────┐ binds tools for current mode ┌───────────────┐
|
|
118
|
+
│ agent │ ───────────────────────────────▶ │ LangGraph loop│
|
|
119
|
+
│ (llm.py) │ │ (agent.py) │
|
|
120
|
+
└──────────┘ └──────┬────────┘
|
|
121
|
+
│ tool call
|
|
122
|
+
▼
|
|
123
|
+
┌───────────────────────┐
|
|
124
|
+
│ Harness │
|
|
125
|
+
│ sandboxed fs + shell │──▶ guardrails.py
|
|
126
|
+
└───────────────────────┘ (blocks/scans)
|
|
127
|
+
│ result
|
|
128
|
+
▼
|
|
129
|
+
┌──────────────────────┐
|
|
130
|
+
│ Renderer interface │
|
|
131
|
+
│ (render.py) │
|
|
132
|
+
└──────┬──────────┬────┘
|
|
133
|
+
▼ ▼
|
|
134
|
+
tui.py + tui_*.py ui.py + ui_*.py
|
|
135
|
+
(Textual) (classic)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
A single `Renderer` interface (`render.py`) decouples the agent loop from presentation, so
|
|
139
|
+
the Textual TUI and the classic REPL are two implementations of the same contract rather
|
|
140
|
+
than two copies of the agent logic.
|
|
141
|
+
|
|
142
|
+
Each frontend is a small set of single-purpose modules behind a facade, so the entry module
|
|
143
|
+
stays readable and the parts can be reused or tested on their own:
|
|
144
|
+
|
|
145
|
+
| Frontend | Facade | Split into |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| Textual | `tui.py` — app, message handlers, input, suggestions | `tui_theme.py` (banner, palette, CSS) · `tui_messages.py` (Message classes, `TuiRenderer`) · `tui_widgets.py` (conversation blocks, input box, modals, `ConfirmBridge`) |
|
|
148
|
+
| Classic | `ui.py` — the `print_*` / `stream_*` renderers | `ui_theme.py` (shared `Console`, palette, banner) · `ui_prompts.py` (permission dialog, input, `EscListener`) |
|
|
149
|
+
|
|
150
|
+
Each facade re-exports the names its split modules define, so `from ui import ...` and
|
|
151
|
+
`from tui import ...` keep working exactly as before.
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
## Modes
|
|
155
|
+
|
|
156
|
+
| Mode | Command | Tools available | Use for |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| **Plan** | `/plan` | Read-only: `read_file`, `list_dir`, `grep`, `glob`, `tavily_search` | Exploring a codebase, proposing an approach with zero risk of a side effect |
|
|
159
|
+
| **Build** | `/build` | Everything, including `write_file`, `edit_file`, `bash`, `run_tests`, git tools | Actually making changes |
|
|
160
|
+
|
|
161
|
+
Plan mode isn't a prompt instruction the model can ignore — the write/edit/bash tools are
|
|
162
|
+
never bound to the model in the first place.
|
|
163
|
+
|
|
164
|
+
## Tools
|
|
165
|
+
|
|
166
|
+
| Tool | Description |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `read_file` / `write_file` / `edit_file` | Read, overwrite, or targeted find-and-replace on a file |
|
|
169
|
+
| `list_dir` | List a directory's contents |
|
|
170
|
+
| `glob` / `grep` | Find files by pattern / search file contents by regex — sandboxed, available in Plan mode |
|
|
171
|
+
| `bash` | Run a shell command in the sandbox (capped timeout, guardrail-checked) |
|
|
172
|
+
| `run_tests` | Run the project's test command and report pass/fail |
|
|
173
|
+
| `todo_write` / `todo_read` | Maintain a visible multi-step task list |
|
|
174
|
+
| `tavily_search` | Web search for current docs/APIs (requires `TAVILY_API_KEY`) |
|
|
175
|
+
| git tools (`status`, `diff`, `log`, `commit`, branches) | Via `mcp-server-git`, enabled with `AGENT_ENABLE_GIT=true` |
|
|
176
|
+
|
|
177
|
+
## Commands
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
/plan, /build switch modes
|
|
181
|
+
/models [filter] list OpenRouter models (free-tier first)
|
|
182
|
+
/model <id|number> switch model
|
|
183
|
+
/key update your OpenRouter API key
|
|
184
|
+
/sessions list saved conversations
|
|
185
|
+
/resume <id> switch to a saved session
|
|
186
|
+
/delete <id> delete a saved session
|
|
187
|
+
/usage token usage for this session
|
|
188
|
+
/clear start a new session
|
|
189
|
+
/help show all commands
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Configuration
|
|
193
|
+
|
|
194
|
+
All settings are environment variables (`.env`, or exported in your shell) — see
|
|
195
|
+
`.env.example` for the full list. Key ones:
|
|
196
|
+
|
|
197
|
+
| Variable | Default | Purpose |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `OPENROUTER_API_KEY` | — | Required (or saved via `/key` into `~/.closecode/config.json`) |
|
|
200
|
+
| `OPENROUTER_MODEL` | `nvidia/nemotron-3.5-lightning:free` | Default model |
|
|
201
|
+
| `AGENT_WORKDIR` | `./sandbox` | Directory the agent is confined to |
|
|
202
|
+
| `AGENT_AUTO_APPROVE` | `false` | Skip permission prompts (guardrails still apply) |
|
|
203
|
+
| `AGENT_ENABLE_GIT` | `false` | Load git tools via MCP |
|
|
204
|
+
| `AGENT_DISABLE_GUARDRAILS` | `false` | Disable guardrails — trusted/isolated testing only |
|
|
205
|
+
| `TAVILY_API_KEY` | — | Enables `tavily_search` |
|
|
206
|
+
| `LANGCHAIN_API_KEY` | — | Enables LangSmith tracing |
|
|
207
|
+
|
|
208
|
+
## Safety model
|
|
209
|
+
|
|
210
|
+
This is layered defense, not a single mechanism:
|
|
211
|
+
|
|
212
|
+
1. **Input scope** — off-topic requests are redirected; clearly malicious requests
|
|
213
|
+
(keyloggers, phishing kits, account-hacking) are refused before reaching the model
|
|
214
|
+
2. **Command blocking** — destructive shell commands are blocked before execution, even
|
|
215
|
+
with auto-approve on
|
|
216
|
+
3. **Write scanning** — file writes/edits are scanned for malware indicators before
|
|
217
|
+
they're applied
|
|
218
|
+
4. **Output redaction** — flagged content is scrubbed from conversation history
|
|
219
|
+
5. **Sandboxed paths** — every file operation resolves through the harness, which refuses
|
|
220
|
+
to write outside the configured working directory regardless of how the path is phrased
|
|
221
|
+
|
|
222
|
+
These are conservative heuristics layered on top of the sandbox and per-action permission
|
|
223
|
+
prompts — not a formal guarantee. Shell commands currently run on the host inside a
|
|
224
|
+
path-restricted directory, not inside a container; see [Roadmap](#roadmap).
|
|
225
|
+
|
|
226
|
+
## A note on model choice
|
|
227
|
+
|
|
228
|
+
Tool-calling reliability varies a lot across open models — this is the single biggest
|
|
229
|
+
factor in how well the agent performs. Frontier closed models are heavily trained for
|
|
230
|
+
reliable tool use; open models are improving but inconsistent. Roughly in order of
|
|
231
|
+
reliability, worth trying via `/model`:
|
|
232
|
+
|
|
233
|
+
- `qwen/qwen-2.5-coder-32b-instruct` — code-specialized, solid tool use, free tier available
|
|
234
|
+
- `qwen/qwen-2.5-72b-instruct` — strong, reliable, paid
|
|
235
|
+
- `meta-llama/llama-3.1-70b-instruct` — strong, reliable, paid
|
|
236
|
+
- `meta-llama/llama-3.1-8b-instruct` — fastest/cheapest, least reliable
|
|
237
|
+
|
|
238
|
+
If a smaller model frequently fails to call tools or hallucinates arguments, that's a
|
|
239
|
+
known gap between open and closed models on agentic tasks, not a bug here. Switching
|
|
240
|
+
models is the first thing to try before changing anything else.
|
|
241
|
+
|
|
242
|
+
## Roadmap
|
|
243
|
+
|
|
244
|
+
- [x] Test suite (`pytest tests/`) — guardrails, sandbox path resolution, todo-store invariants
|
|
245
|
+
- [ ] CI (run the suite on push)
|
|
246
|
+
- [ ] Docker-based sandbox for shell execution, not just path restriction
|
|
247
|
+
- [ ] Client/server split — `build_graph()` behind FastAPI/WebSocket, thin streaming client
|
|
248
|
+
- [ ] PyPI release + prebuilt binaries (PyInstaller) for no-Python-required installs
|
|
249
|
+
- [ ] Homebrew tap
|
|
250
|
+
|
|
251
|
+
## Contributing
|
|
252
|
+
|
|
253
|
+
Issues and PRs welcome. If you're adding a tool, follow the pattern in `tools.py` /
|
|
254
|
+
`search.py`: bind state via a module-level `bind_*()` function, keep it sandboxed to the
|
|
255
|
+
harness root, and add it to the Plan-mode allowlist only if it's genuinely read-only.
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -9,7 +9,8 @@ from llm import get_llm
|
|
|
9
9
|
|
|
10
10
|
SYSTEM_PROMPT = """You are a terminal coding agent running in a sandboxed working directory.
|
|
11
11
|
You have tools for reading, writing, and editing files, running shell commands and tests,
|
|
12
|
-
listing directories,
|
|
12
|
+
listing directories, managing background processes, undoing your own file changes,
|
|
13
|
+
and interacting with git (status, diff, log, commit, branches).
|
|
13
14
|
|
|
14
15
|
Note: your available tools change depending on the current mode. In "plan" mode only
|
|
15
16
|
read-only tools are bound to you (you literally cannot call write/edit/bash/commit tools
|
|
@@ -25,6 +26,14 @@ Rules:
|
|
|
25
26
|
- Verify your work: after making a change, run a command, run tests, or read the
|
|
26
27
|
file back to confirm it did what you intended. Never report a task complete
|
|
27
28
|
without verifying — bugs are unacceptable, so test before you say "done".
|
|
29
|
+
- Long-running processes (dev servers, watchers, tunnels) go through
|
|
30
|
+
start_background, never a foreground bash call that would hang until timeout.
|
|
31
|
+
Use tail_logs to watch the process's output, fix what crashes, and restart
|
|
32
|
+
with kill_background + start_background. Always kill servers you started
|
|
33
|
+
when the task no longer needs them.
|
|
34
|
+
- Every write_file/edit_file is snapshotted before the change, and the user can
|
|
35
|
+
revert with /undo — treat that as a safety net, not a workflow: verify with
|
|
36
|
+
tests instead of writing sloppily and undoing.
|
|
28
37
|
- Use git tools deliberately: check status/diff before committing, and never force-push
|
|
29
38
|
or hard-reset unless the user explicitly asked for that specific action.
|
|
30
39
|
- When the task is complete, reply with plain text summarizing what you did.
|