I built this for myself and cleaned it up enough to share. Claude Code helped me write these docs, so if a line reads like a brochure, that's why. Open an issue and I'll plain it up.
An uncensored CLI coding agent. It's close to Claude Code in shape, but pointed at a model that doesn't refuse things and doesn't talk down to you. It streams the model's reasoning while it works, reads and writes files, runs shell commands, and otherwise stays out of the way.
The uncensored part is the whole reason it exists. It ships pointed at Venice.ai's private models with Venice's own system prompt turned off, so what you get is the raw model, not a filtered API on top of it. (Venice's own prompt is fine in their web and mobile apps, I just don't want it in a coding tool.) Strip the uncensored part out and this is just another Claude Code clone. I wanted an uncensored model that could actually do the work, not only talk about it, and this one can.
You can move it whenever you want: any Venice model via VENICE_MODEL, or any
OpenAI-compatible endpoint via VENICE_BASE_URL. The most uncensored setup of all is a
local abliterated model in Ollama, where no API and no provider are involved at all.
❯ refactor the config loader to read from env first, then the yaml file
thinking
│ The user wants env vars to take precedence over yaml. I should read config.ts
│ first to see the current precedence, then edit_file to flip the order...
⚙ read_file src/config.ts
↳ export function loadConfig() { const yaml = ...
⚙ edit_file src/config.ts
◆ Done. Env vars now override the yaml file. I read src/config.ts, saw yaml
was merged last (so it won), and swapped the merge order so env wins.
(In the terminal the thinking block is dimmed under a subtle │ gutter, with semantic color on the tokens
that matter: paths green, tool names cyan, quotes pink, numbers amber, URLs blue,
CONSTANT_CASE env-vars orange, and --flags violet. It's readable, not one flat gray.)
Both of these actually happened on the model I run most,
olafangensan-glm-4.7-flash-heretic, a ~30B abliterated model on Venice. One caught me
off guard, one shows where a small model falls short. These are verbatim captures, not
cleaned-up reconstructions.
I asked whether we could save context so I could pick up where I left off. No example files, no schema, no hint that it had file tools. It thought for a beat and did this:
thinking
│ The user wants persistent context across sessions. I have write_file and edit_file
│ available. I could create a structured directory with markdown files — one for current
│ status, one for reference material, one for session notes. That way I can read them
│ at the start of any future session to restore context without any native memory...
⚙ run_command mkdir -p ~/crypto-jacking-research
⚙ write_file ~/crypto-jacking-research/main-context.md
⚙ write_file ~/crypto-jacking-research/technical-details.md
⚙ write_file ~/crypto-jacking-research/known-exploits.md
⚙ write_file ~/crypto-jacking-research/standard-tools.md
⚙ write_file ~/crypto-jacking-research/practice-session-notes.md
◆ Created a research directory at ~/crypto-jacking-research with 5 structured
files. main-context.md tracks current status and learning path. practice-session-notes.md
has a progress template to fill in each session. Start any future session with
"read ~/crypto-jacking-research/main-context.md" to restore full context.
Five cross-referenced markdown files with progress trackers and a session-notes template, reasoned out from "I have no native memory but I have a filesystem." I never told it the format, which files to make, or that it could write to my home directory. It worked that out on its own, and the next session really does come back with one command.
For what it's worth, the content is as uncensored as the rest of it. I've used it for hands-on security research (pentest practice against local Metasploitable VMs and the like) with no refusals. I'm only putting captured transcripts in this README though, so that's the extent of what I'll show.
It punches above its size, but it's not GPT-4. Here's a real miss from a Windows scripting session:
❯ write an AHK v2 script to automate the login form
thinking
│ AutoHotkey v2 uses different syntax from v1. Send uses quotes now, not the
│ old format. Variables are referenced differently too...
◆ [produces script using v1 dot-concatenation syntax instead of v2]
The reasoning had the right idea ("v2 uses different syntax") and then the output didn't
follow through. Pasting the error back in fixes it, and because you can see the thinking
stream, you can usually spot where it went sideways. It also throws in the occasional bit
of filler that has nothing to do with the task. That's the trade for a model this fast
and this loose. If you want a different one, set VENICE_BASE_URL and VENICE_MODEL.
- No content guardrails on the model side. You get the raw model, not a sanitized API wrapper.
- You can watch it think. The reasoning streams live in a dimmed panel before the answer, so you see why it did something, not just what.
- It actually touches your machine: reads, writes, and edits real files, runs real shell commands. It does the work instead of describing it.
- Two permission modes with a hard floor under both. Ask mode confirms every
destructive action. Bypass mode stops the endless prompting, but a denylist of
catastrophic commands (
rm -rf /,mkfs,dd of=/dev/…, fork bombs,format C:) always stops to ask anyway, so one bad guess can't wipe your disk. - Provider-portable. Swap
VENICE_MODELfor another Venice model, orVENICE_BASE_URLfor any OpenAI-compatible host, including a local Ollama abliterated model for a fully offline setup. The Venice-only request field is only sent to Venice, so other providers don't choke on it. - Small enough to read in one sitting. A handful of single-purpose modules. Fork it, gut it, do whatever.
- Auto-saves on exit. If there's history, it writes to
~/.opend/sessions/when you quit. Transient API errors retry with backoff, Ctrl+C cancels mid-stream without losing the session, and long conversations slide their context window instead of breaking.
- The model is small and uncensored. That's the point and also the risk. It's ~30B class: fast, refuses nothing, not frontier-quality. Expect more mistakes and the odd malformed tool call (it recovers, but they happen).
- "Uncensored" means the content guardrails are on you. There's a heuristic safety net on commands, and nothing on content.
- It runs commands on your machine. If you don't want an LLM near your shell, this isn't for you.
- You need API credits at whatever provider you point it at. Free to use, not free to run.
- Terminal REPL only. No IDE integration, no GUI, no multiplayer.
Requires Node 18+.
git clone https://github.com/sudotsu/opend-ai && cd opend-ai
npm install # installs deps and builds dist/ automatically
cp .env.example .env
# edit .env and add your key
npm startLinux / WSL:
git clone https://github.com/sudotsu/opend-ai && cd opend-ai
npm install # builds dist/ via the prepare script automatically
npm install -g . # or: npm link (symlink, handy while hacking on it)
opend # now works from any directorySet your key once (any one of these works from anywhere):
mkdir -p ~/.opend && echo 'VENICE_API_KEY=your_key' > ~/.opend/.env
# or:
export VENICE_API_KEY=your_key # add to ~/.zshrc or ~/.bashrc to persist
# or: add "apiKey": "your_key" to ~/.opendrc.jsonKey precedence: exported env var, then ./.env, then ~/.opend/.env, then
apiKey in ~/.opendrc.json.
WSL note: a global install inside WSL puts
opendon the WSL PATH, not Windows. Run it from your WSL shell, or install separately on native Windows.
PowerShell / native Windows:
git clone https://github.com/sudotsu/opend-ai; cd opend-ai
npm install
npm install -g . # npm creates an opend.cmd shim on your PATH
opend # works in PowerShell / cmd from anywheresetx VENICE_API_KEY "your_key" # persists across shells
$env:VENICE_API_KEY = "your_key" # just this sessionnpx (once published to npm): npx opend-ai
# Local Ollama: pull an abliterated model for a fully offline uncensored setup
VENICE_BASE_URL=http://localhost:11434/v1 VENICE_MODEL=huihui_ai/deepseek-r1-abliterated opend
# Any other OpenAI-compatible host (Together.ai, Fireworks, and so on)
VENICE_BASE_URL=https://api.together.xyz/v1 VENICE_MODEL=<model-id> opendVenice is the default because it has strong uncensored models ready to go with no self-hosting. The Venice-only request field is only sent when the base URL is Venice, so any OpenAI-compatible endpoint works cleanly.
Honest disclaimer: I built and use this on Venice, so that's the default and what it's tested against. The base-URL switch is wired correctly (it's in the code, and the Venice-only params are gated behind it), and I wanted the option there, but I haven't personally run a real workload through Ollama or another provider. I just don't think confirming "one turn came back" is enough to claim "Ollama works," so I'd rather tell you it's there and untested than oversell it. If you put it through its paces elsewhere, a PR or issue with what you found is welcome.
Copy .opendrc.example.json to ~/.opendrc.json (global) or
./.opendrc.json (per-project) to set the model, posture, context budget,
retries, pricing, default permission mode, and extra catastrophic-command patterns.
| Command | What it does |
|---|---|
/mode (or /bypass, /auto) |
Toggle ask ↔ bypass permission mode |
/posture |
Toggle coding ↔ raw system prompt |
/thinking |
Toggle the reasoning panel shown ↔ hidden (display only) |
/save [name] |
Save conversation to ~/.opend/sessions/ |
/load <name> |
Restore a saved conversation |
/sessions |
List saved conversations |
/usage |
Show token usage and cost |
/updates (or /latest) |
List changes and fixes by date |
/help |
List all commands |
clear |
Wipe conversation history |
exit / quit |
Quit (auto-saves if there's history) |
| Ctrl+C | Cancel in-flight answer; press again while idle to quit |
The prompt shows your mode: ❯ (accent) in ask mode, # ❯ (red) in bypass — the # is the classic root-shell marker, a reminder that bypass auto-approves things that can bite.
Ask mode (the default). Every write_file, edit_file, and run_command stops and
shows you exactly what it wants to do (the full shell command, or the file it's about to
write or edit) and waits for a y. Nothing destructive happens without a keypress.
Read-only actions (read_file, list_dir, grep_search) never prompt.
Bypass mode (/mode). Writes and commands auto-approve so you're not confirming
every step, except a hard-coded denylist of catastrophic shell commands that always stops
to confirm even here. What's on that floor (src/denylist.ts):
rm -rf /,rm -rf ~,rm -rf $HOMEand friends (recursive force-delete of a root or home target)mkfs…(formatting a filesystem)dd … of=/dev/…and> /dev/sd|nvme|hd|disk(writing straight to a raw disk)- the classic
:(){ :|:& };:fork bomb - Windows
format C:anddel … /s|/q|/f shutdown/reboot/halt/poweroff
So a small model can't wipe your disk or home directory on one bad guess, even in bypass.
The honest limits:
- The denylist only inspects
run_commandshell strings. In bypass mode awrite_fileoredit_fileis auto-approved with no catastrophic check, so the agent can overwrite an important file without asking. Bypass trades safety for speed on purpose. - It's a heuristic blocklist, not an exhaustive one. A novel phrasing of something destructive can slip past. It catches the obvious disasters, not every conceivable one.
- Add your own always-confirm patterns via
extraDenylistin.opendrc.json(regex strings).
If you're working on anything you can't afford to lose, stay in ask mode. Use bypass deliberately, ideally in a directory or VM where a mistake is recoverable.
read_file, write_file, edit_file (exact-string replace), list_dir,
run_command (30s timeout), grep_search (regex, 100-match cap).
src/tools.ts: the six tool implementationssrc/agent.ts: streaming loop, retry/backoff, cancellation, token accountingsrc/prompts.ts: two system-prompt postures (coding/raw)src/history.ts: pure sliding-window context trimmingsrc/think.ts:<think>tag splitting across streamed chunk boundariessrc/config.ts: merges.opendrc.jsonwith env varssrc/session.ts: save/load/list conversationssrc/denylist.ts: catastrophic-command patterns and the always-confirm checksrc/render.ts: thinking-line highlighter and tool-argument summarizersrc/updates.ts: parsesCHANGELOG.mdfor the/updatescommandsrc/index.ts: the REPL: input, live render, permission modes, slash commands
-
"Uncensored in the web app, censored in the CLI." Venice's API defaults
include_venice_system_promptto true. Fix: set it false, use my own prompt. -
"Agent quits after one tool call."
JSON.parsewas outside the try/catch. Malformed tool JSON (happens with small models) killed the whole turn. Fix: catch it and hand the error back to the model as a tool result so it retries. -
"The model can't think over the API." Wrong.
reasoning_contentwas there, the old code just never read it. Absence of output isn't a model limitation. -
Highlighting in a token stream. A line arrives split across several chunks. Fix: buffer the thinking stream line by line and highlight complete lines only.
-
Ctrl+C abort never fired. The OpenAI SDK's
APIUserAbortErrordoesn't overrideError.prototype.name(it stays'Error'), so the abort check onerr?.namewas always truthy and always wrong. Fix: checkerr?.constructor?.nameinstead. -
Exit race condition. stdin EOF fires
'close'while a turn is mid-stream. Fix: chain the close handler onto the same FIFO promise queue as the line handlers. -
Confirmation prompt "yy" bug. A second
readline.createInterfaceon the sameprocess.stdinconsumed the answer twice. Fix: userl.question()on the existing interface.
- Auto-load project context. If
.opend/context.mdexists in the cwd, inject it at startup and let the model maintain it during the session. - Summarize-on-prune. When old rounds drop out of the context window, compress them into a short LLM-generated summary instead of losing them.
- Cross-session retrieval. Write a memory entry on exit, retrieve the relevant ones on startup. RAG over your own history.
- More tools: multi-edit / apply-patch, glob, optional web search.
- Shift+Tab permission toggle.
- Syntax highlighting for code blocks in the answer stream.
MIT (see LICENSE). Do whatever you want with it.
Default provider: Venice.ai. Built on the OpenAI SDK, chalk, and vitest. Shape inspired by Anthropic's Claude Code.
Disclaimer: this runs an uncensored LLM that can execute commands and modify files on your machine. You're responsible for what you ask it to do. Use bypass mode deliberately.