Skip to content

Weasel Quickstart

chimera weasel is the fourth Chimera coding-agent CLI. Where chimera mink mirrors a TUI-first agent, chimera otter mirrors a server-first multi-client agent, and chimera ferret mirrors an IDE-first sandbox-first agent, weasel mirrors the minimal harness: powerful defaults plus four operating modes — interactive, print, rpc, sdk — and an auto-discovered .weasel/extensions/ directory. Adapt-to-your-workflow rather than ship-every-feature.

Headline trade is simplicity. Weasel ships no sub-agents, no plan mode, no built-in approval presets, no opinionated session chrome. What it ships is a clean four-mode entry surface, auto-discovered extensions, and an embeddable Agent class. If you want more, you build it (or install an extension); weasel will not get in the way.

Deeper dives:

  • Python 3.11+
  • uv
  • For the JS/TS extension path: Node 20+
  • One of: an Anthropic key, an OpenAI key, an OpenRouter key, an Ollama daemon, or an Ollama Cloud account
Terminal window
uv --version # >= 0.4
uv sync --extra dev --extra anthropic # core + Anthropic SDK

Weasel resolves the provider in this order (first match wins):

  1. --model <id> on the CLI.
  2. $WEASEL_MODEL environment variable.
  3. $ANTHROPIC_API_KEYclaude-sonnet-4-6.
  4. $OPENAI_API_KEYgpt-4o.
  5. $OPENROUTER_API_KEYanthropic/claude-sonnet-4.
  6. $OLLAMA_API_KEYgpt-oss:120b-cloud.
  7. Local Ollama daemon on :11434 → first installed tag.
  8. Friendly error pointing at the env vars above.
Terminal window
export ANTHROPIC_API_KEY=sk-ant-...
# OR — free path
export OLLAMA_HOST=https://ollama.com
export OLLAMA_API_KEY=<your-key>
ModeCommandI/O envelopeWhen to use
Interactivechimera weaselstreaming TTYDay-to-day, conversational, mid-turn steering.
Print (one-shot)chimera weasel -p "..."stdout text or JSONScripts, CI, xargs, shell pipelines.
RPC (stdio)chimera weasel --mode rpcJSON-RPC 2.0 on stdin/stdoutAnother tool drives weasel as a subprocess.
SDK (embedded)from chimera.weasel.sdk import AgentPython objectDrop into your Python app; no subprocess.

The modes share one loop, one tool registry, one extension surface, and one session store. Switching modes does not change semantics — only the I/O envelope.

Terminal window
chimera weasel
weasel · claude-sonnet-4-6 · /Users/me/proj
> list the top-level files and read the README
I'll list the repo first, then read the README.
▶ list_files(path=".")
CHANGELOG.md CLAUDE.md README.md chimera/ docs/ examples/ tests/
▶ Read(path="README.md")
# Chimera
A composable coding agent framework
...
> ▌

Slash commands are intentionally sparse: /help, /exit, /model, /cost, /clear, /sessions, /extensions. Anything else you want, you add via an extension.

-p runs a single turn and exits. Plain text on stdout by default, JSON with --json:

Terminal window
chimera weasel -p "summarize TODO comments in src/" --json
chimera weasel -p "ship it" --max-steps 5
chimera weasel --model gpt-oss:120b-cloud -p "draft a release note"
chimera weasel -p "audit" --allowed-tools Read,Bash

Pass -p repeatedly to chain turns in a single non-interactive invocation. Each -p reuses the same loop / context.

Terminal window
chimera weasel -p "read CHANGELOG.md" \
-p "summarize the last release in 3 bullets" \
-p "draft a tweet for it"

Any token of the form @<path> in a -p argument is expanded to the contents of that file (UTF-8, max 1 MB) inline:

Terminal window
chimera weasel -p "review this diff: @./pending.diff"
chimera weasel -p "rewrite @./prompt.txt to be terser"

Surface the model’s thinking trace alongside text deltas:

Terminal window
chimera weasel --thinking medium --stream-json \
-p "explain why the test is flaking" \
| jq 'select(.event=="thinking_delta" or .event=="text_delta")'

Sample stream:

{"event":"thinking_delta","text":"The test asserts ..."}
{"event":"text_delta","text":"Looking at the test, "}
{"event":"thinking_delta","text":"... but the fixture seeds RNG."}
{"event":"text_delta","text":"the race comes from the seeded RNG ..."}

Stdout is one JSON line per LoopEvent. Stderr carries the run-id banner so pipelines stay clean.

--mode rpc turns weasel into a JSON-RPC 2.0 server on stdin/stdout:

Terminal window
chimera weasel --mode rpc < requests.jsonl

Request:

{"jsonrpc":"2.0","id":1,"method":"prompt","params":{"text":"list files"}}

Response stream:

{"jsonrpc":"2.0","method":"event","params":{"type":"text_delta","text":"I'll "}}
{"jsonrpc":"2.0","method":"event","params":{"type":"tool_call","name":"list_files"}}
{"jsonrpc":"2.0","id":1,"result":{"text":"...","cost":0.0042}}

Methods: prompt, steer, cancel, get_state, compact, list_sessions, resume. Schema in modes.md.

For when you want weasel inside your Python process — no subprocess, no JSON-RPC, just a class. Sync and async forms ship:

from chimera.weasel.sdk import Agent
agent = Agent(model="claude-sonnet-4-6")
result = agent.run("list the top-level files and read the README")
print(result.text)
print(f"cost: ${result.cost:.4f}")

Async form:

import asyncio
from chimera.weasel.sdk import Agent
async def main() -> None:
agent = Agent(model="claude-sonnet-4-6")
async for event in agent.stream("explain this repo"):
if event.type == "text_delta":
print(event.text, end="", flush=True)
asyncio.run(main())

Full recipe in sdk.md.

Weasel auto-discovers .weasel/extensions/*.{py,js,ts} in cwd and ~/.weasel/extensions/ globally.

A minimal Python extension:

from chimera.weasel.sdk import extension, tool
@extension(name="hello", version="0.1")
def register(api):
@tool
def hello(name: str) -> str:
"""Say hi."""
return f"hello, {name}!"
api.register_tool(hello)

A minimal TypeScript extension (run via the bundled Node executor):

.weasel/extensions/word_count.ts
export const manifest = {
name: "word_count",
version: "0.1",
};
export function register(api: any) {
api.register_tool({
name: "word_count",
description: "Return the word count of a file.",
parameters: { path: "string" },
async run({ path }: { path: string }) {
const fs = await import("node:fs/promises");
const text = await fs.readFile(path, "utf-8");
return { words: text.split(/\s+/).filter(Boolean).length };
},
});
}

Drop the .ts file under .weasel/extensions/word_count.ts and the next chimera weasel invocation will pick it up, transparently shelling out to the bundled Node executor. JS works the same way; the manifest.name matches the directory or filename.

Terminal window
chimera weasel sessions list
chimera weasel sessions show weasel-20260514T101455-1f3c2a8b
chimera weasel --resume weasel-20260514T101455-1f3c2a8b # explicit
chimera weasel -c # newest in cwd

Recommended models for the minimal-harness posture:

BackendTagWhy for weasel
Anthropicclaude-sonnet-4-6Default; strongest tool calling.
Ollama Cloudgpt-oss:120b-cloudFree w/ Ollama account; native tools.
OpenAIgpt-4oStrong baseline; $OPENAI_API_KEY.
OpenRouteranthropic/claude-sonnet-4Same Anthropic model via OpenRouter.

See the Ollama Cloud recipe.

VariableDefaultMeaning
WEASEL_MODEL(unset)Default model id.
ANTHROPIC_API_KEY(unset)Anthropic chain.
OPENAI_API_KEY(unset)OpenAI chain.
OPENROUTER_API_KEY(unset)OpenRouter chain.
OLLAMA_API_KEY(unset)Ollama Cloud.
OLLAMA_HOSThttp://localhost:11434Daemon URL.
WEASEL_EXTENSIONS_DIR.weasel/extensions/Extensions search root.
NO_COLOR(unset)Plain output handler.
PathWhat
~/.chimera/eventlog/weasel-<id>/Per-run event stream + summary.
.weasel/settings.jsonProject-local settings.
.weasel/extensions/Project-local extensions.
~/.weasel/extensions/User-global extensions.
~/.chimera/credentials.jsonOAuth tokens (mode 0o600).

Everything is local. Purge with rm -rf ~/.chimera/eventlog/weasel-*.


Two commands from this quickstart, against Ollama Cloud:

$ OLLAMA_HOST=https://ollama.com OLLAMA_API_KEY=*** \
chimera weasel -p "Hello, please reply with one word: hello" \
--model gpt-oss:120b-cloud --max-steps 2
hello
$ chimera weasel --version
chimera weasel 0.7.0