coldstart, command by command.
coldstart is one binary with two front doors — a CLI (the fast path for any shell-capable agent) and an MCP server (for no-shell clients), with byte-identical output. This page covers the stable core surface; for exhaustive flags, versioned changes, and internals, see the README and docs on GitHub — the source of truth.
find (which files?) and gs (what is this file, who uses it?) — and the kb family is the notebook: durable, agent-written notes kept honest by the index. That's the whole surface.Install & setup
Requires Node.js 18+. Install globally, then run init once per project.
init asks two things — the experience (cli, recommended, or mcp) and the client (claude / cursor / codex / other) — then writes the coldstart guidance into the client's own rules file — imported via coldstart.md for Claude Code, inlined directly into .cursor/rules/coldstart.mdc (Cursor) and AGENTS.md (Codex) — and wires the client. It also creates the notebook and, for Claude Code / Codex / Cursor, registers the navigation and notebook hooks. Pass --experience / --client to skip the prompts; the client is never auto-detected. Finally it indexes the repo and waits for that to finish before exiting, so your very first find is instant rather than racing a half-built index — a few seconds once, at setup.
.raw logs so the team can share it. Private by default — see Sharing.Upgrading: just npm install -g @cstart/coldstart@latest — that's the whole step. Each project's background keeper stamps its version in its lockfile, so the next find/gs in that repo sees the mismatch, shuts the old process down, and spawns a fresh one from the new binary; nothing to re-run per project. You only need coldstart init again if a node/nvm or npm-prefix change moved the global install and left the wired hook paths dangling — and coldstart status tells you exactly that (it reports the installed version, any keeper still on an older one, and any hook path that no longer resolves).
Removing: coldstart unwire reverses init — it strips coldstart's per-repo wiring (hook entries, the coldstart.md import, the AGENTS.md block, the MCP entry, owned files) without touching your own content in shared files. The notebook is kept by default; --purge also deletes it. Run it per project, then npm uninstall -g @cstart/coldstart. Any keepers still running self-reap within about half a minute of the uninstall — they notice their own binary is gone and shut down — so you don't have to hunt down background processes by hand.
Codex hook trust: Codex gates hooks behind a trust review, so the notebook/navigation hooks init writes will not fire until you approve them (untrusted hooks are skipped silently) — init cannot pre-trust them (no supported non-interactive path; see openai/codex#21615). When Codex shows "Hooks need review", choose "Trust all and continue". If your Codex app doesn't surface that prompt, launch codex from a terminal in the repo — it appears at startup there; trust persists (keyed by each hook's hash) across surfaces. Re-approve after any coldstart update changes a hook. For automated codex exec runs, pass --dangerously-bypass-hook-trust instead. Project trust (trust_level = "trusted" in config.toml) is a separate gate and does not trust hooks.
The intended flow
find → gs → Read. Orient with two cheap calls before spending a token reading. Notebook summaries ride along on find, so the orientation step often answers itself.
- find a concept → ranked candidate files, each with the evidence for why it ranked.
- gs the best file → its symbols, importers, and per-symbol callers, in one call.
- Read only the method body you actually need — not the whole file.
Pass every salient identifier from your task — the symbol, the domain noun, the rare token you half-remember — not one distilled keyword. find ranks by coverage and shows where the terms cluster, so often that's enough to answer without opening anything. Its reference pass runs on ripgrep, so it competes with raw grep on speed.
! to exclude.a|b = OR, /regex/ = regex.symbols / imports / importers / callers. Default returns all.this.method(), api.method()) don't. Chase further hops by running gs on the caller file.The notebook, in depth
A repo-local knowledge base, written and read by agents, living in .coldstart/notebook/. It's the one place semantics belong: authored by the agent that just did the work, kept honest by the index, and recalled when a later task matches. No human writes or grooms it.
What a note is
What a file is for — a single summary, or per-symbol facets for hub files.
A cross-file story: ordered steps and the invariants that hold across them.
A trap, rule, bug-cause, rationale, or a confirmed absence.
Every note carries anchors — the concrete file paths and symbols its claims rest on. Anchors are what make a note checkable rather than a floating assertion.
Where notes reach the agent
- Summary lines on
findresults — a past agent's verified overview, right where the file ranks. fresh means the file is byte-identical to when the summary was verified, so the agent can rely on it without re-reading. - Recall at prompt time — the key one. A hook wired into Claude Code, Cursor, and Codex fires on every prompt submit: notes whose titles, aliases, or anchors match are injected straight into the agent's context as a compact, hard-capped block before it acts — the agent doesn't choose to search, the note is simply already there. Nothing matches → nothing injected, no noise.
kb search/kb lookup— a search engine over the notebook, and an exact-address lookup before editing a file.
Why it can be trusted
- Freshness is mechanical. Every anchor is stamped with a content hash at write time; the index re-checks it as the code changes. A drifted note renders evidence changed and the guidance says re-verify — stale knowledge degrades into a labeled hypothesis, not a confident lie.
- The log is the truth. Notes live in an append-only
.rawevent log; the Markdown notes are derived and regenerated mechanically. A note is a pure fold of its log. - Writes go through a gate. A new note's concept is searched against existing notes first — the agent must merge into a match or declare it new. Duplicates are caught at write time.
- Concurrent sessions are safe. Per-note append-only logs, exclusive creation of new ids, lossless union-merge of shared notes, atomic renders — concurrent writers never silently merge or lose a note.
- Corrections happen in-session. An agent that finds a note wrong while the evidence is in context is told to fix or retract it right then.
Language-agnostic. The freshness machinery is content-hash based, so the notebook works on any codebase — including languages the navigation index doesn't parse. Where the index does parse, notes additionally get symbol-level freshness.
When capture happens
Capture is trigger-timed, not every-turn. The hook keeps per-file evidence of what the session actually read or edited (search hits and passing path mentions never count), scores the uncaptured backlog, and asks for notes at natural boundaries — when a burst of work settles, or the moment a commit lands (manual commits included). Most turns end silently. The ask itself is non-blocking: it arrives with your next prompt as a short worklist of the files the session genuinely worked with, each annotated with its existing note's state — never a wall of instructions after every answer.
Files whose notes are already fresh don't re-trigger capture, so a warmed-up repo asks less over time. The flip side: a brand-new notebook starts empty and warms with use — the first few real tasks seed the file notes, and "how X works" flow notes appear as sessions actually need those answers. Don't judge recall on day one.
/capture-notes — capture on demand
Sometimes you know a moment is worth recording before the score crosses — a hard-won debugging insight, a confirmed absence, a decision that didn't touch many files. /capture-notes fires the same capture flow immediately: same worklist, same checklist, only the trigger threshold is skipped. It never forces a write — the agent still decides what (if anything) is worth a note, and writes nothing if the answer is nothing.
coldstart init wires it for your client: Claude Code and Cursor get a /capture-notes slash command; Codex gets a capture-notes skill (invoke with $capture-notes) — Codex's custom prompts are global and deprecated, so a repo-scoped skill is the supported surface there. All three run the same one-liner, so it also works by hand:
It reads the evidence the capture hook has already accumulated and never writes back to it, so asking for a manual capture can't disturb the automatic one. If the session hasn't done any real work yet, or everything is already captured, it says so and stops.
.coldstartignore — files that never get notes
coldstart init scaffolds .coldstart/.coldstartignore (gitignore syntax). It's a personal file — kept out of git even when the notebook is shared, because the built-in defaults ship in the tool itself: *.json, lockfiles, dist//build//coverage/, minified/map/snapshot files, binaries and images, and .env* (notes are committed; secrets must never enter them). Every collaborator gets that baseline with or without the file; lines you add extend it on your machine. Re-include a default with !:
Ignored files are filtered at the root — they never appear in a capture worklist and never trigger it. Logic-bearing configs (vite.config.ts, CI workflow YAML, routes.rb) are deliberately not ignored by default: they carry exactly the gotchas notes exist for. kb lint reports any note that anchors an ignored file (possible only via direct kb write).
- First call exits 3 with candidate matches. Resolve it with
--into <id>(merge into an existing note) or--new(declare it genuinely new). - Reads a spec file, or
-to stream the spec from stdin. - Freshly-coined ids are created exclusively — a same-moment duplicate becomes two visible notes, never a silent merge.
.raw logs and skeleton — never your feature code, and notes never ride along in a feature commit.kb commit is CLI/human-only and is not exposed as an MCP tool. Publishing notes to git is a human decision.index.html but don't launch the browser.kb search / lookup; this is the view for a person who wants to browse what the agents have been keeping. The generated file self-registers in the notebook's .gitignore. Leave the tab open — the keeper keeps the file current in the background, so a reload is all you ever need after the first generate..raw logs.coldstart init, which already does this.How the index stays fresh
coldstart is one keeper, thin readers. A single background keeper watches the repo and keeps an on-disk cache current; the readers (find, gs, the MCP server) are stateless and never build. There is no cache TTL — the index is never discarded for being old, it's kept correct.
Patched as you type
Edits debounce (400 ms), then patch incrementally (~2–5 ms/file) or trigger a background rebuild. Re-saved in atomic generations — a reader never loads a half-written mix.
Reconciled, not rebuilt
Stat-checks every file against its fingerprint plus a git diff, and patches exactly what changed. A branch switch that used to force a full rebuild is now typically a few seconds.
Linted & audited
Every patch is checked against index invariants (a violation auto-rebuilds), and a rotating fingerprint audit after each save catches watcher-missed drift.
Lifecycle commands
Installed: version, plus a warning if any keeper is still running an older version than the installed binary (run coldstart restart) or if a repo's wired hook paths no longer exist because a node/nvm or npm-prefix change moved the global install (run coldstart init there).--root DIR targets another repo; --all kills every keeper. coldstart stop is a synonym — same kill, and the keeper still respawns the next time a reader (a find/gs or a connected MCP client) needs it, so killing it from ps alone won't keep it down while an editor is querying.-v works too). coldstart --help / -h prints the command list.coldstart restart — a fresh keeper reconciles on start, so it comes back correct, not just alive. If an update doesn't seem to have taken effect, run coldstart status first: it tells you whether the keeper is on the old version (restart), a hook path is dangling (re-init), or the install itself didn't update. If something looks structurally wrong, TROUBLESHOOTING.md on GitHub has recovery steps.MCP tools
For no-shell clients (like Claude Desktop), the same engine is exposed as MCP tools with byte-identical output to the CLI.
kb_write.kb commit and kb view stay CLI/human-only — publishing to git and opening a browser are never agent actions.Bring your own semantics
coldstart has no embeddings, no generated summaries, no semantic layer computed at index time — on purpose. The semantic layer is the agent. Every consumer is already a frontier model; pre-computing meaning at index time only duplicates that, worse and stale. So the index keeps what's cheap to keep exact — paths, symbols, exports, the import/call graph — and returns why each file ranked. The notebook applies the same rule to memory: it stores and freshness-checks the meaning agents author, and computes none of its own.
Supported languages
Tree-sitter for the parsed set; the notebook's content-hash freshness works on all of them — notes on a Swift repo are as trustworthy as notes on a TypeScript one (they just lack symbol-level freshness).
Not indexed: Swift, Dart — no extension mapping; these files aren't walked or parsed. The notebook still works on them.