mini-code-cli
A minimal coding agent CLI
No bloat, no dependencies, no tracking, no telemetry...
Purpose: a no-bloat, minimal coding CLI to enjoy experimenting and researching.
Install · Quick start · Modes & safety · Options · Tools · Sub-agents · In-session commands · Development · Docs · Citation
Install
pip3 install mini-code-cli
- PyPI: https://pypi.org/project/mini-code-cli/
- Python:
>= 3.10· runtime dependency:requestsonly - From source / contributing: see docs/README_DEV.md
Quick start
1 — Local model (e.g. vLLM or SGLang on port 30000; see serving a model locally):
mini-code # talks to http://0.0.0.0:30000/v1
2 — Hosted / custom API:
export MINI_CODE_API_KEY="sk-YOUR_API_KEY"
mini-code --url "https://api.deepseek.com" --model "deepseek-v4-flash"
3 — The shortcuts (plan mode, sub-agents, shell, auto-mode, no permission prompts):
mini-code --all # = --plan --enable-subagents --enable-shell --auto-mode --sudo
mini-code --all-last # ...and continue your last session
4 — A first prompt:
Write an agent.md file for me that helps an LLM write efficient triton kernels,
given a reference implementation.
Command-line options
| Option | Default | What it does |
|---|---|---|
--url URL |
http://0.0.0.0:30000 |
API host |
--model MODEL |
— | model name |
--api-key KEY |
$MINI_CODE_API_KEY |
API key for authentication |
--max-tokens N |
— | reply budget; also the output headroom used when deriving the compaction limit |
--temperature T |
1.0 |
sampling temperature |
--system-prompt TEXT |
built-in | replace the system prompt (an --agent-md file is appended after it) |
--prompt TEXT |
— | initial user prompt |
-a, --auto-mode |
off | keep querying the model and running tools until no tool calls remain |
--agent-md FILE |
— | append an agent.md file after the system prompt (e.g. ./skill.md) |
--ask-permission |
off | ask before every tool call (ALLOW-tier shell commands still skip the prompt) |
--sudo |
off | dangerous: never ask. DENY-tier commands are still refused |
--allowed-dir DIR |
./ |
sandbox for file tools (does not restrict the shell) |
-s, --enable-shell |
off | allow shell execution (classifier always applies) |
-g, --enable-subagents |
off | expose sub-agents as tools |
--skills |
off | enable the .mini skills directory (skill.mds, SKILLS.md, runnable scripts) |
-p, --plan |
off | plan mode (CURRENT_GOAL.md / CURRENT_PLAN.md, /plan) |
--llm-timeout S |
500 |
HTTP timeout for LLM requests |
--tool-timeout S |
120 |
timeout for a single tool call |
--max-context-tokens N |
derived | input-token budget before the history is compacted |
--cache-dir DIR |
~/.cache/mini/sessions/ |
where session histories are stored |
--restore-session PATH |
— | continue a specific previous session |
-l, --load-last-session |
off | continue the session recorded in the .mini pointer |
--all |
off | --plan --enable-subagents --enable-shell --auto-mode --sudo |
--all-last |
off | --all + --load-last-session |
--verbose |
off | print full detail (default: user/assistant turns, token usage, tool calls only) |
-h, --help |
— | show the built-in help |
Available tools
The agent can call a small set of tools, all defined in mini_code_cli/tools.py. Every
parameter a model does not need is defaulted, so omitting it keeps the previous behaviour.
| Tool | What it does |
|---|---|
read_file |
read a file; offset (1-based line) and limit page through large files |
write_file |
write/overwrite; append adds to the end, create_dirs creates parents |
edit |
replace an exact string (old_string → new_string); refuses a missing or ambiguous match unless replace_all |
grep |
regex search a file or directory; recursive, include, ignore_case, line_numbers, max_results (noise dirs skipped) |
glob |
list files matching a glob (one per line, ** supported), optionally against a root |
web_search |
best-effort lookup: related queries + a short Wikipedia extract. Never raises |
read_website |
fetch raw text/HTML of a URL (bounded by --tool-timeout), max_chars caps size |
run_shell_command |
run a shell command (needs --enable-shell); cwd, timeout, and stdout/stderr/exit code are all reported |
Sub-agents
--enable-subagents lets the main agent delegate a focused task to a sub-agent, exposed to
the model as an extra tool. A sub-agent is a self-contained, narrowed agent: it runs unattended
in auto-mode, in its own isolated session folder, and hands back a short text result (plus an
optional report.md). It reuses all the existing machinery — tools, the shell-safety
classifier, the --allowed-dir sandbox, compaction and --plan — and cannot spawn further
sub-agents.
mini-code --enable-subagents # the available sub-agents then show up in /help
| Sub-agent | What it is for |
|---|---|
research |
read-only web research: searches, reads a few sources, returns a sourced summary. Can only use web_search / read_website — never the filesystem or shell. Optional sources hint (e.g. "wikipedia.org only") |
planner |
drafts or refines a step-by-step plan for a goal; always runs in plan mode |
software_engineer |
writes, edits, refactors, fixes and tests code; can run shell commands (tests, builds) to verify |
generic |
general-purpose helper for anything that fits no narrower agent |
Adding your own sub-agent
A sub-agent is just a small class — subclass SubAgent and register it:
# my_subagents.py
from mini_code_cli.subagents import SubAgent, register
class SummarizeSubAgent(SubAgent):
name = "summarize"
description = "Summarize a piece of text the main agent provides."
tools = ["read_file", "write_file"] # restrict the nested agent's tools
enable_shell = False
max_steps = 5
register(SummarizeSubAgent())
Make sure the module is imported (e.g. import my_subagents) and it is discovered and exposed
to the model automatically when --enable-subagents is set. See
mini_code_cli/subagents/builtin.py for every knob and
docs/ARCHITECTURE.md (sections 2–3) for the design.
In-session commands
| Command | What it does |
|---|---|
/history |
list the conversation (turn number, what happened, where) |
/history <n> |
show the full messages recorded for turn n |
/history last |
...for the most recent turn |
/reset [<n>] |
rewind to the end of turn n (last works too); drops everything after it |
/clear |
clear the screen (keeps the conversation history) |
/plan |
show the current goal + plan (plan mode only) |
/help |
show this help |
/quit, /exit |
save the session and exit |
Development
Linting, the test suite, CI and the release process are documented in docs/README_DEV.md.
python3 scripts/lint.py # lint (standard library only)
python3 -m unittest discover -s tests -p "test_*.py" # run the test suite
Releases are cut by tagging main: pushing a v* tag triggers
.github/workflows/release.yml, which builds the sdist + wheel,
verifies them with twine check, and attaches them to a new GitHub release.
Documentation
Everything beyond this README lives in docs/:
| Document | What it covers |
|---|---|
| docs/README_DEV.md | developer guide — local setup, repository layout, linting, the test suite, CI and the release / PyPI upload process |
| docs/ARCHITECTURE.md | design — module layout, on-disk conversation state (history_{n}.json, summary_history.json, usage files), the sub-agent framework and how to extend it |
This README stays the user-facing entry point (install, CLI flags, tools, sub-agents, in-session commands, modes & safety); the two documents above go deeper on the developer and design side.
Modes & safety
Shell execution — -s / --enable-shell
Everything the agent runs from the shell first passes a safety classifier that sorts the command into one of three tiers:
| Tier | Examples | What happens |
|---|---|---|
| DENY | rm -rf /, dd of=/dev/..., mkfs.*, fork bombs |
refused outright, never run |
| ASK | rm file, sudo ..., chmod, curl ... | sh |
requires your confirmation |
| ALLOW | ls, cat, git status |
runs without prompting |
Plan mode — -p / --plan
The loop keeps two human-readable tracking files in the session directory: CURRENT_GOAL.md
(your latest instruction) and CURRENT_PLAN.md (a step-by-step plan for that goal, marked
finished when it is done). They are authored by the loop, not the agent, live outside
--allowed-dir, and can be inspected any time with /plan.
Sessions — resume, or start fresh
Every session is saved under --cache-dir (default ~/.cache/mini/sessions/).
| Flag | Behaviour |
|---|---|
--restore-session PATH |
continue that exact session (a session directory or a bare session id under --cache-dir) |
-l, --load-last-session |
continue the session recorded in the .mini/last_session.json pointer, without asking |
--load-last-session continues that session in place (same id, same folder, same
messages), so the conversation you were in is the one you get back; an explicit
--restore-session wins if both are given. The "most recent" session comes from a single
source — the tiny .mini/last_session.json pointer written when a session starts or is
restored inside --allowed-dir (deliberately no timestamp fallback: with no pointer, a fresh
session starts). That pointer lives in the same .mini directory as --skills, but as its
own dedicated file, so the two never clash.
A session folder is restorable only if it has what this version writes: a valid
summary_history.json recorder, plus the history_{n}.json segment its last record names
(usage files are optional). Anything else — a folder from an older release, a hand-assembled
transcript, a recorder that does not validate, a missing segment — is refused with a note
saying why, and a fresh session is started instead. Nothing is repaired, guessed at, or
written into the refused folder.
Context compaction — --max-context-tokens
Compaction is decided by tokens only, never by message count: a few large tool outputs can
fill a window that hundreds of short turns would not. The next request is projected from the
server-reported prompt_tokens, plus an estimate of what was appended since, plus the output
reserve (--max-tokens). Unset (default) it is derived from the model's context window minus the
output reserve and a safety margin. Lower it to compact sooner, raise it to compact later — there
is no message-count cap to configure.
Compaction replaces the older turns with a summary, but it always carries the turn in flight
across the rollover: the message you just typed (or, mid-turn, the tool call the model is
waiting on) is kept verbatim after the summary. So a compaction that fires just as you send an
instruction cannot swallow it — the model still answers what you asked, and the turn stays in
the session file, in /history and in a later restore.
Because a rollover leaves no trace in the transcript (the old turns really are gone), the
console says in plain terms what happened — all of it without --verbose:
[!] Compacting the conversation: 42 messages -> a summary (51000 tokens in, 8192 reserved for the reply > 52000 budget). The current turn (1 message) is kept verbatim.
[!] Summary preview: The user asked for a token-based compaction trigger, then fixed ...
[!] Compacted history_0.json (42 messages) into a summary; history_1.json now starts with 3 messages (summary + 1 kept message).
So you can see what was folded away (a preview of the summary itself), how much (the
message count of the segment that was closed), where it went (history_0.json closed,
history_1.json started) and that nothing you just typed was lost. If the summariser answers
with an empty summary twice, or the call fails, compaction is abandoned and the note says so
explicitly — "nothing was lost, but the next request may still be too large" — with the
transcript and the segment left untouched.
Serving a model locally
Tested with vLLM v0.30 (tool calling enabled):
vllm serve ornith-ai/Ornith-1.5-397B-FP8 \
--tensor-parallel-size 4 \
--host 0.0.0.0 \
--port 30000 \
--enable-auto-tool-choice \
--tool-call-parser qwen3_xml \
--reasoning-parser qwen3 \
--trust-remote-code
Citation
If you use mini-code-cli in your research or writing, please cite it:
@misc{mini-code-cli,
author = {Rozanov, Nikolai},
title = {mini-code-cli: A minimal, dependency-light coding agent},
year = {2026},
version = {0.1.1.0},
howpublished = {\url{https://github.com/ai-nikolai/mini-code-cli}},
note = {A no-bloat, dependency-free agent CLI for experimenting.}
}
License
AGPL-3.0 — (C) Nikolai Rozanov, 2026 – present.
Metadata
Release files for mini-code-cli 0.1.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| mini_code_cli-0.1.1.2.tar.gz | 173.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mini_code_cli-0.1.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 306.6 kB
Release files / mini_code_cli-0.1.1.2.tar.gz
| Download URL | mini_code_cli-0.1.1.2.tar.gz |
|---|---|
| Size | 173.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fbbc9d592bfd135d2b4325cc6d6e38127d40c52548dc4476963b173477c06348
|
|
BLAKE2b-256 checksum How to use checksums |
d80fda44ea5c0e19bd105e97083e9b0c14b38b4770b2649bb5672c3919435cd6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.13
|
Release files / mini_code_cli-0.1.1.2-py3-none-any.whl
| Download URL | mini_code_cli-0.1.1.2-py3-none-any.whl |
|---|---|
| Size | 133.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
de001d7ecab5801349b4730c5cd086bf6e36b1a6a33dcf212047c852d50df823
|
|
BLAKE2b-256 checksum How to use checksums |
b9e6435da9d99d7327491bc9487f3ad9cd51921d154ace9dc9d24b2c353ef43f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.13
|
Security for mini-code-cli 0.1.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.