NightCode reads your codebase with real tools, edits files, runs your tests, and answers — streaming every step as it happens. It asks before it does anything destructive, and it remembers what it learned last time.
Every turn is context → model → tools → result, looped up to ten times until there is a real answer. You watch the whole thing happen.
15 tools built in. The model sees a lean 9: read, write, edit, bash, grep, find, ls, todoWrite, spawn_subagent — a small surface keeps the context clean.
Destructive tools — write, delete, rename, risky bash — pause on a confirmation dialog. Nothing runs until you press y.
Answers type out live as the model generates them, and reasoning streams into a dimmed panel beside the work — no staring at a spinner wondering if it hung.
Durable facts, learned rules, and notable events are extracted after each turn and injected into the next session's prompt. Stored in your repo, never in a cloud.
Esc ends a run between steps, keeps the history valid, and kills the child process tree. The session stays usable for the next prompt.
spawn_subagent hands a scoped sub-task to a fresh session with restricted tools and no memory writes — so a side quest can't pollute the main thread.
NightCode runs on Bun. Bring your own key for any provider you like — the default is Groq.
A Bun workspace — one install covers the whole monorepo.
# clone git clone https://github.com/Nirvanjha2004/nightCode.git cd nightCode # install (bun workspace) bun install
Copy the template and fill in whichever provider you use. Bun auto-loads .env from the repo root — keys are never hardcoded.
cp .env.example .env # then edit .env — one key is enough to boot GROQ_API_KEY=gsk_… # optional: pick a different provider + model NIGHTCODE_PROVIDER=anthropic NIGHTCODE_MODEL=claude-sonnet-4-5
That's the whole setup. The UI boots into a fresh session in your current directory.
bun run dev:cli
Local models need none. NIGHTCODE_PROVIDER=ollama NIGHTCODE_MODEL=llama3.3 bun run dev:cli talks to http://localhost:11434. Same for llama.cpp, LM Studio and vLLM.
One turn, start to finish. Every stage emits an event, and every event is something you can see on screen.
Around 100k estimated tokens, older messages are summarized by the model itself — chainable across compressions, always keeping the last 15 messages intact.
A failing shell command returns its stdout, stderr and exit code as a result, not an exception — so the agent can read the error and recover on the next step.
The memory/ tree is off-limits to every agent tool. Read, write, edit, delete and grep are all blocked against it.
Everything is one keystroke away. There is no mouse mode and no menus you have to hunt for.
/clearFresh session — history and summary reset; files and memory untouched/commitStage everything and write a conventional commit message/reviewRead-only review of the uncommitted diff/explainTrace a file or function and explain it in plain language/fix-issueLocate a described bug, fix it minimally, verify with tests
Add your own: drop a markdown file in commands/ with optional
description / argument-hint frontmatter.
$ARGUMENTS is replaced with whatever you typed after the command.
All gitignored, created on first run, and always anchored to the repo root — never to whatever directory you launched from.
| Path | Contents |
|---|---|
memory/semantic.json | Durable facts about you, the project and the environment |
memory/procedural.md | Learned rules — "always do X before Y" |
memory/episodic/events.jsonl | Notable past events with embeddings, retrieved by similarity |
logs/combined.log | Winston JSON, all levels, 5 MB × 5 rotated |
logs/error.log | Warnings and above only |
One internal interface, per-provider adapters. Hosted or local, frontier or cheap — switch at boot with an env var, or mid-session with Ctrl M.
Every OpenAI-compatible provider shares a single transport configured with a base URL, key and catalog. Anthropic, Gemini, Azure, Vertex and Bedrock get dedicated adapters.
Streams, errors and usage are normalized before the loop sees them, so the agent never learns a provider's quirks.
Add a providers entry to nightcode.config.json and it registers at boot. Source changes are only needed for a genuinely new wire format.
Environment variables win over the config file, which wins over the built-in defaults.
.env — keys and selection# provider + model (optional) NIGHTCODE_PROVIDER=groq NIGHTCODE_MODEL=qwen/qwen3.6-27b # whichever keys you actually use GROQ_API_KEY= OPENAI_API_KEY= ANTHROPIC_API_KEY= GOOGLE_API_KEY= DEEPSEEK_API_KEY= # local endpoints need no key OLLAMA_BASE_URL=http://localhost:11434/v1
nightcode.config.json — the rest{
"provider": "deepseek",
"model": "deepseek-chat",
"reasoning": "off",
"providers": {
"my-provider": {
"api": "openai-compatible",
"baseUrl": "https://api.example.com/v1",
"apiKeyEnv": "MY_PROVIDER_KEY",
"models": [
{ "id": "my-model", "contextWindow": 131072 }
]
}
}
}
.env, not in the config file.
reasoning accepts off · low · medium · high · max, and is the one setting with no environment override. Full reference lives in docs/providers.md.
NightCode is a personal project, and the README keeps an honest scorecard against Pi. The short version:
Provider-neutral LLM layer across 25+ providers · live streaming of answers and reasoning · automatic three-part memory · destructive-action confirmation · cancellation that actually kills child processes · 14 assertion-based self-checks.
Sessions live in memory and are lost on exit · no session tree or branching · no extension system — you fork the code · interactive TUI only, so there is no print/JSON mode to benchmark against · memory is injected wholesale rather than on demand.