@vrocket/freecode
v0.1.10
Published
A universal AI execution layer built for correctness. Runs any model from any provider and validates its own outputs by default — a consistent, trustworthy interface for AI development, without vendor lock-in or manual verification overhead.
Maintainers
Readme
freecode
Total freedom. No guesswork.
Project status (2026-06-13): paused / handover. The harness works end-to-end with a capable model — verified live: Opus 4.1 drove a full Tauri build to shippable installers. The open problem is whether a local model can drive it on complex agentic tasks; that's unproven, and the most promising local setups (a coder model via
llama-server --jinja, or devstral) were never fairly tested. See HANDOVER.md for the honest state, known issues, and the experiment that would settle it.
freecode is a universal AI execution layer built for correctness. It runs any model from any provider and validates its own outputs by default — a consistent, trustworthy interface for AI development, without vendor lock-in or manual verification overhead.
REPL + headless. Multi-LLM, multi-tool. Drop-in Claude Code with the Anthropic lock-in removed. Bun runtime.
Your guide is Bubo (◉‿◉) — the freecode owl, named for the bronze owl Athena built to watch over Perseus. A built, watchful companion that checks the path ahead: he verifies before he claims, so a "done" is something he's seen, not something he's guessing. Say /about to meet him.
Quick start
bun install
bun test # 17 unit tests
bun run src/cli.tsx # launch REPL (mock provider — no API key needed)First run uses the Mock provider so the app works with zero config. Wire a real provider by setting env vars or .freecode-profile.json (see below).
Install (another machine)
One command, runs on Node (no clone, no Bun on the target). The repo is private, so the machine needs GitHub access once — an SSH key, gh auth login, or a PAT in your git credential helper.
npm install -g "github:Plicerin/freecode#feat/tier-a-parity"
freecodeThat clones, installs deps, bundles a Node-runnable CLI (via esbuild — no Bun needed), and puts freecode on your PATH. Upgrade by re-running the same command. (The #feat/tier-a-parity picks the working branch; once it merges to main, plain github:Plicerin/freecode will do.) Requires Node ≥ 18.
Enable shared cross-session memory by writing ~/.freecode/settings.json (Honcho is Tailscale-only, so the machine must be on your tailnet):
{ "memory": { "provider": "honcho", "enabled": true, "baseUrl": "http://<honcho-host>:8100", "workspace": "freecode", "peer": "user" } }Memory keys off one user peer, so pointing two machines at the same Honcho gives them the same accumulated memory; it's fail-soft, so freecode runs fine without it. Other config (vault keys, session logs) lives in ~/.freecode/ — copy vault.json + vault.key to carry provider keys over.
Alternative — auto-updating source install. If you'd rather run from source with an freecode launcher that self-updates on launch, clone and run ./install.sh --honcho <url> (macOS/Linux) or ./install.ps1 -HonchoUrl <url> (Windows); details in install.sh. A standalone binary (no runtime at all) is bun run build:exe → dist/freecode.
Providers
Set the active provider with an env flag, then supply the matching key:
| Provider | Selector | Key env var (or alt) |
|-----------------|-----------------------------------------|-------------------------------------|
| Anthropic | CLAUDE_CODE_USE_ANTHROPIC=1 | ANTHROPIC_API_KEY |
| OpenAI | CLAUDE_CODE_USE_OPENAI=1 | OPENAI_API_KEY |
| GitHub Models | CLAUDE_CODE_USE_GITHUB_MODELS=1 | GITHUB_TOKEN |
| Google Gemini | CLAUDE_CODE_USE_GEMINI=1 | GEMINI_API_KEY |
| AWS Bedrock | CLAUDE_CODE_USE_BEDROCK=1 | AWS_ACCESS_KEY_ID + secret + region |
| Google Vertex | CLAUDE_CODE_USE_VERTEX=1 | GOOGLE_APPLICATION_CREDENTIALS |
| Ollama | CLAUDE_CODE_USE_OLLAMA=1 | OLLAMA_HOST (default http://127.0.0.1:11434) |
| LM Studio | CLAUDE_CODE_USE_LMSTUDIO=1 | LMSTUDIO_HOST (default http://127.0.0.1:1234) |
| NVIDIA NIM | CLAUDE_CODE_USE_NIM=1 | NVIDIA_API_KEY (nvapi-...); base https://integrate.api.nvidia.com/v1 |
Without a flag, freecode auto-detects whichever key is set.
Note: AWS Bedrock and Google Vertex are listed for selector completeness but are not implemented yet — selecting them returns a clear "not implemented" error rather than fake output. Use
--provider mockfor an offline, no-key demo.
Per-project profile
.freecode-profile.json in your project root overrides env settings for that project only:
{
"provider": "openai",
"baseUrl": "https://api.openai.com/v1",
"apiKey": "sk-...",
"model": "gpt-4o"
}API key vault
On first run, freecode walks you through onboarding: pick the providers you have keys for, paste each key once, and they're stored encrypted — no plaintext keys in env vars or profiles, and you never enter them again.
Keys live in ~/.freecode/vault.json (AES-256-GCM). By default the vault auto-unlocks with a per-machine device key at ~/.freecode/vault.key (permission-locked) — zero friction, no prompt. For stronger protection, set FREECODE_VAULT_PASSPHRASE and the vault is keyed off that passphrase (scrypt) instead. No plaintext key ever touches disk.
Manage it anytime:
freecode auth set anthropic # paste a key (hidden) and store it
freecode auth list # which providers have a stored key
freecode auth remove anthropicKey precedence: CLI --api-key > .freecode-profile.json > vault > env var.
Settings priority
CLI flags > .freecode-profile.json > env vars > ~/.freecode/settings.json
settings.json is JSONC (comments and trailing commas allowed) and hot-reloads on save:
// ~/.freecode/settings.json
{
"model": "claude-sonnet-4-5",
"permissionMode": "manual", // manual | auto | bypass
"webSearchProvider": "duckduckgo", // duckduckgo | tavily | exa | firecrawl
"theme": "dark", // dark | light
"maxTurns": 50,
"contextThreshold": 0.8,
"enablePromptCache": true,
"enableExtendedThinking": false
}MCP servers
freecode is an MCP (Model Context Protocol) client. Declare stdio servers under mcpServers in ~/.freecode/settings.json and their tools are loaded at startup and offered to the model alongside the built-ins:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "ghp_..." },
"disabled": false
}
}
}- Tools are namespaced
<server>__<tool>to avoid collisions. - MCP tools always require approval (permission
confirm) — they're external code. - A server that fails to start is reported and skipped; the rest keep working.
/mcpin the REPL lists connected servers and their tools.
Currently supported transport: stdio (SSE/HTTP transport is planned).
CLI flags
freecode # REPL
freecode --prompt "..." # REPL with initial prompt
freecode --print --prompt "..." # headless one-shot
freecode --resume <session-id> # resume a session in REPL
freecode --serve --port 50051 # gRPC server (placeholder)
freecode --provider openai --model gpt-4o # override provider
freecode --permission-mode bypass # skip all prompts
freecode --thinking # enable extended thinking / reasoningSlash commands (REPL)
| Command | What it does |
|------------|-----------------------------------------------|
| /model | List the models your key can use, or switch |
| /new | Start a fresh session |
| /resume | Open the session picker (↑/↓ to choose, Enter) |
| /rename | Name the current session (shows in the picker) |
| /context | Show token usage + cost |
| /provider| Show or switch provider |
| /mcp | List connected MCP servers and their tools |
| /plan | Toggle read-only plan mode (propose, don't change) |
| /verify | Run the project's checks and report the real result |
| /bench | Race the performance ghost without leaving the REPL |
| /help | List commands |
| /compact | Force context compaction |
| /about | Meet Bubo, the freecode owl |
| /exit | Exit freecode (/quit, or Ctrl+C) |
Custom slash commands
Drop Markdown files in ./.freecode/commands/ (project) or ~/.freecode/commands/ (user) to define your own commands — the filename becomes the command name. Invoking /<name> [args] expands the file and sends it as a prompt.
<!-- .freecode/commands/review.md -->
---
description: Review a file for bugs
---
Review $ARGUMENTS for correctness bugs and suggest fixes.$ARGUMENTS is replaced with everything after the command; $1, $2, … with positional args. Project commands override same-named user ones, and they show up in /help and Tab-completion.
Keyboard
Ctrl+C exit · Ctrl+U clear input · Ctrl+T toggle tasks sidebar · Enter submit
Type / to open a live command menu (filters as you type); ↑/↓ to select, Tab to complete. ↑/↓ otherwise recall input history.
/resume opens an interactive picker — ↑/↓ to choose a session, Enter to resume, Esc to cancel. Name sessions with /rename <name> so they're easy to find there.
Tools
Bash · FileRead · FileWrite · FileEdit · Glob · Grep (ripgrep) · WebSearch · WebFetch
Bash has a denylist for rm -rf /, fork bombs, sudo, mkfs, dd if=, piped shell, chmod -R 777 /. Grep uses rg (ripgrep) when it's on PATH and falls back to a built-in search otherwise; both ignore .git/, node_modules/, dist/.
Permissions
manual— prompt for every non-safe toolauto— auto-approve safe tools (FileRead,Glob,Grep,WebSearch,WebFetch), prompt the restbypass— no prompts, log only
Denials are remembered per session, so the same call is not re-prompted on retry.
Hooks
Run shell commands on lifecycle events via hooks in ~/.freecode/settings.json. Each hook receives the event as JSON on stdin.
{
"hooks": {
// veto writes outside the project; non-zero exit blocks the tool
"PreToolUse": [{ "matcher": "FileWrite|FileEdit", "command": "node guard.js" }],
// auto-format after edits
"PostToolUse": [{ "matcher": "FileEdit", "command": "prettier --write ." }],
// notify when a turn finishes
"Stop": [{ "command": "echo done" }]
}
}- PreToolUse runs before a tool; a non-zero exit blocks it (stderr/stdout becomes the reason shown to the model).
- PostToolUse runs after a tool (side effects only — can't block).
- Stop runs when a turn finishes.
matcheris a regex tested against the tool name; omit it to match every tool.
Verification — earned confidence
freecode tries not to tell you something works until it has watched it work. After a turn that changes files, it runs the project's checks and self-corrects if they fail — so a bug it introduced is caught before it ever claims "done".
verifyMode(settings or--verify-mode):off·on(default) ·strict.- on — after a file change, run quick checks (auto-detected
typecheck/build/lint,cargo check,go build) with a visible, Esc-skippable status; on failure, feed the output back and retry up to 3 times. - strict — same, but uses the full checks (including tests) and is stricter about ending red.
- off — only the manual
/verify.
- on — after a file change, run quick checks (auto-detected
/verifyruns the full checks on demand and reports the real result.verify(settings, string or array) overrides the auto-detected commands.
The point: the slow part (full tests) stays manual or strict; the default gate is fast and legible, so verification never feels like a hung model.
Provenance ledger. After a turn that did something, freecode prints a short, machine-derived summary of what it actually did vs. what it's only assuming — never parsed from the model's prose:
✓ verified bun run typecheck passed
· observed wrote note.txt; ran `git status`
~ believed changed 2 file(s) without running checks — unverifiedverified = checks that passed · observed = tools that actually ran · believed = changes asserted but not confirmed. The believed line is the honest one — it's where a polished "done" can hide an unchecked guess.
Performance ledger — racing the ghost
freecode bench # run the hot-path benchmarks, race this machine's bests
freecode bench --filter=fuzzy # only matching benchmarks
freecode bench --no-save # don't update the ledger
freecode bench --json # machine-readablebench times freecode's own hot paths (command matching, config load, …), reduces each to robust stats (median + MAD, not mean), and compares against the personal best stored for this machine in ~/.freecode/perf.json. The only opponent is its own past self — no external repo, no imitation.
The honest part is the noise rejection: a run is called a new personal best or a regression only if it beats the ghost by more than the jitter band — max(2% of the best, 3 × combined MAD). Anything inside that band is reported as a tie, never a win. So freecode never claims it got faster when it only got lucky. The ledger is keyed by an environment fingerprint (CPU, cores, runtime), so a fast laptop can't beat a slow CI box's ghost.
This is the measurement floor for self-improvement: before any change can claim "faster," it has to out-run the ghost for real.
Headless / CI
CLAUDE_CODE_UNATTENDED_RETRY=1 \
bun run src/cli.tsx --print --prompt "summarize the test failures"CLAUDE_CODE_UNATTENDED_RETRY=1 switches retry to infinite (default 10) for unattended CI runs.
Sessions
JSONL append-only, one line per event:
~/.freecode/projects/<encoded-cwd>/<session-id>.jsonl/resume (no id) lists recent sessions; /resume <id> reconnects. Messages, tool calls, tool results, thinking, and system notes are all captured.
Errors
Provider errors are mapped to friendly messages:
Local provider not running — please start OllamaLocal provider not running — please start LM StudioModel not found — use /model to switchInvalid API key — check the key for <provider>Invalid NVIDIA API key — get one at build.nvidia.com (free tier available)Rate limited by <provider> — retrying with backoffContext window exceeded — auto-compacting
Retries use exponential backoff with jitter. CLAUDE_DEBUG=1 writes verbose logs to stderr.
Status
This is a working v0.1. See SPEC.md for the full design.
Working: real providers — Anthropic, OpenAI-compat (covers OpenAI / GitHub Models / LM Studio / NVIDIA NIM), Gemini, and Ollama (local, via its OpenAI-compatible API); plus an explicit mock provider for offline demos. Settings priority + JSONC + hot-reload, sessions + /new + /resume, 8 built-in tools + MCP client (stdio servers, tools loaded at startup), permission engine with interactive approval prompts (allow / allow-always / deny), denylist, REPL with banner + info box + slash commands + keybinds, dark/light theme, agent loop with streaming + tool exec + retry, auto-compaction when the context window fills, per-model cost estimation (local models free), friendly error mapping, --print headless mode, 27 unit tests.
Not implemented yet (fail honestly — no fake output): AWS Bedrock (needs SigV4 signing) and Google Vertex (needs service-account auth) return a clear "not implemented" error. Also deferred: gRPC bidirectional stream (serve is a port-binding placeholder), spinner shimmer component, prompt-cache markers in provider requests.
