terminal-agent
A minimal terminal coding agent built with LangGraph (agent loop), LangChain (tool + model abstraction), LangSmith (tracing), and a Hugging Face model as the LLM. Same shape as OpenCode/Terminus 2: read task -> decide -> run tool -> observe result -> repeat.
Setup
pip install -r requirements.txt
cp .env.example .env
Edit .env:
HUGGINGFACEHUB_API_TOKEN— from https://huggingface.co/settings/tokensHF_MODEL_ID— a model that supports tool/function calling (see note below)LANGCHAIN_API_KEY— optional, from https://smith.langchain.com, enables tracing
Run
python main.py
If OPENROUTER_API_KEY isn't set (env or .env), the agent prompts you to
paste one at startup — input is hidden — and offers to save it to .env
for next time. You can rotate it later with the /key command.
Switching models
/models list all OpenRouter models (● = current, free first)
/models qwen filter the list by name
/models --refresh force a fresh fetch (list is cached for 24h)
/model 12 switch by list number
/model qwen/qwen-2.5-72b-instruct or any OpenRouter model id directly
The list comes live from OpenRouter's API and is cached for 24 hours in
~/.cache/closecode/; if you're offline it falls back to a curated
shortlist. The model choice is saved per session, so /resume restores
the model you were using. Free-tier :free models cost nothing; check
https://openrouter.ai/models for paid-model pricing before switching.
The agent operates inside ./sandbox (configurable via AGENT_WORKDIR) and
will ask for permission before running shell commands or writing files,
unless AGENT_AUTO_APPROVE=true.
Persistent sessions (SQLite)
Conversations are stored in ./sessions/sessions.db as per-message rows plus
session metadata (name, model, mode, created/updated timestamps, message
count). Existing session_*.json files are auto-migrated into the DB on the
first run.
--continue— resume the most recently used session (its mode/model are restored from metadata)./sessions— list all saved sessions with metadata./resume <id>— switch to a saved conversation./delete <id>— delete a saved session.- Each turn is saved automatically; a session's name is derived from its first user message.
Guardrails
The agent is scoped to coding only, and guardrails.py enforces that with
four layers:
- Input scope — off-topic chatter (greetings, opinions, trivia, creative writing, news takes) is redirected to a coding task, and clearly malicious requests (keyloggers, account hacking, phishing kits) are refused before they reach the model.
- Command blocking — destructive shells commands (
rm -rf /,mkfs, disk wipes, fork bombs,curl | sh, reverse shells) are blocked before execution, even when auto-approve is on. - Write scanning — file writes/edits containing malware indicators (ransomware, keyloggers, miners, persistence, injection) are refused.
- Output redaction — the model's final answer is scanned and flagged content is scrubbed from conversation history.
These are conservative heuristics on top of the sandbox + per-action
permission prompts, not a hard guarantee. Set AGENT_DISABLE_GUARDRAILS=true
in .env to disable them entirely (only for trusted, isolated testing).
A note on model choice
Tool-calling reliability varies significantly across open Hugging Face models — this is the single biggest factor in whether this agent actually works well. Frontier closed models (what Claude Code / OpenCode use by default) are heavily trained specifically for reliable tool use; open models are improving but inconsistent.
Models worth trying, roughly in order of tool-calling reliability:
Qwen/Qwen2.5-72B-Instructmeta-llama/Meta-Llama-3.1-70B-Instructmeta-llama/Meta-Llama-3.1-8B-Instruct(fastest/cheapest, least reliable)
If a smaller model frequently fails to call tools correctly, or hallucinates
tool arguments, that's expected — it's a real, documented gap between open
and closed models on agentic tasks, not a bug in this code. Swapping
HF_MODEL_ID is the first thing to try before changing anything else.
Where to go next
- Watch a trace in LangSmith (smith.langchain.com) once you have a run — seeing the exact messages/tool calls at each step is the fastest way to debug why the agent did something unexpected.
- Add more tools —
list_dir,edit_file(targeted find/replace instead of full overwrite),run_tests. - Split client/server — move
build_graph()behind a small FastAPI/ WebSocket server, and makemain.pya thin client that streams from it. This is the step that makes it architecturally closer to OpenCode. - Swap the sandbox for Docker — the current harness restricts file paths
but shell commands still run on your actual machine. For anything beyond
personal experimentation, run
bashcalls inside a container instead.
Metadata
Release files for closecode-ai 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| closecode_ai-0.1.0.tar.gz | 33.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| closecode_ai-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 70.4 kB
Release files / closecode_ai-0.1.0.tar.gz
| Download URL | closecode_ai-0.1.0.tar.gz |
|---|---|
| Size | 33.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f468917adde640851b0e6c4d76e2de70e8870d8082d189509a981b3ad2a66c1f
|
|
BLAKE2b-256 checksum How to use checksums |
6bcaf8bb68f5fd273eb18d07b69fe863109c411430b548d3d3e908592a62e7d0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|
Release files / closecode_ai-0.1.0-py3-none-any.whl
| Download URL | closecode_ai-0.1.0-py3-none-any.whl |
|---|---|
| Size | 37.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4da2c04f998c401790f208e515a372c6da51ce8b65c22fea69948d9b5e9b7f3b
|
|
BLAKE2b-256 checksum How to use checksums |
831098551a9f86ca229bf86fd08e4a2fd6cae322dcdca80ed6d48ecdc308c2f0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.7
|