- Shell 67%
- Dockerfile 33%
| brown@83946ce391 | ||
| image | ||
| themes | ||
| .dockerignore | ||
| .gitignore | ||
| .gitmodules | ||
| AGENTS.md | ||
| james | ||
| LICENSE.md | ||
| README.md | ||
| TODO.txt | ||
James Brown
brown is an AI coding agent/harness and james orchestrates its containerization.
User ←→ james (bash) ←→ container ←→ brown (rust) ←→ OpenAI-compatible API
James Brown ain't no big deal — AI coding agents are commodity software now. This one was written in about a week. But it serves me better than anything else I've tried.
Table of contents
Features
Backend
- OpenAI-compatible API — tested with
llama-serverandopenrouter.ai - Configurable
temperature,reasoning_effort, andverbosity - Fetches context-window size from the API and displays token usage as a percentage of the window
Containment & safety
- Runs in a docker or podman container
- The current directory and everything below it is exposed to the agent
- The top-level
.gitdirectory is mounted read-only so the agent can't corrupt your repo. (You can put read-only ssh keys in the container so it can still clone, pull, and submodule.) - Container has lots of development tools for the agent.
- Manage the agent's homedir at
~/.local/share/james/homedir/or shell into the container withjames shell
Terminal UX
- Returns control to your terminal prompt after each agent loop completes
- Renders stylized markdown: colors, syntax-highlighted code blocks, tables that don't wrap, and configurable margins
- Two themes bundled:
opencode(dark, default) andone-dark - Thinking streams live so you can detect the agent going off the rails and interrupt it
Tools available to the LLM
- shell — no permission gating; containment handles safety
- read_file / write_file / edit_file — four edit variants (diff, append, insert, remove)
- search — regex search across the workspace
- symbols / ast_query — tree-sitter-powered source-code inspection (Rust, Python, TypeScript/JavaScript, JSON)
- web_search — SearXNG-backed, works with or without
format=json, handles many bot-protection schemes - web_fetch — fetch URLs and save to file
- todowrite — the agent maintains its own task list
- question — the agent can ask you questions
- skill — on-demand loading of SKILL.md runbooks
- context_compact — the agent can compact its own context
Context management
- Persistent — context is stored as a sequence of timestamped directories
under
~/.local/share/james/container-context/. You can inspect, edit, or delete files manually. - Clear — wipe the entire context and start fresh
- Strip — remove a single Q&A pair without losing the rest
- Compact — summarize older turns while keeping recent ones in full
- Re-render — replay the entire session with full tool outputs visible
- Stable early context — the developer prompt and frozen timestamp don't change during a session, so backend caches remain valid
Context priming
- Short developer prompt (the agent is told to respond in markdown)
- Frozen date and time injected once at session start (doesn't invalidate caches on replay)
- Configurable system prompt
- Supports
AGENTS.mdandSKILL.md(see below)
How to use this
What you need:
- Linux (no other platforms supported)
- Podman or docker
- An OpenAI-compatible API endpoint
Install / setup:
- Clone this repo
- Symlink the
jamesscript somewhere on your$PATH, e.g.ln -s /path/to/repo/james ~/bin/james - Run
james buildto build the container image - Run
james ask hello— this will write a default config to~/.config/james/config.toml(it's expected to fail on the first run since you haven't configured a backend yet) - Edit
~/.config/james/config.toml— at minimum setopenai_base_url,model, andopenai_api_keyif your provider requires one
Where things live:
~/.config/james/config.toml— your config (backend, model, prompts, etc.)~/.local/share/james/homedir/— the agent's home directory inside the container~/.local/share/james/container-context/— the conversation context (sequence of turns)~/.cache/james/— HTTP and other caches~/.config/james/skills/— global SKILL.md files (optional).james/skills/in your project — project-local skills (optional)- There is also a docker image to delete if you want a full clean-out.
james mounts these into the container at the paths brown expects, so you
only need to think about the host-side locations above.
Now it works like this:
- Change to the directory you want the agent to have access to. Beware, it has access to all subdirectories too.
- Run
james askand type your message. - The agent streams its response, calls tools as needed, and renders the final answer.
- Repeat to continue the conversation.
Commands
Run james with no arguments to see the full list. The important ones:
ask— send a message to the agentbuild— rebuild the container image (needed afterbrowncode changes)history— list user messages in the current contextclear— clear the context and start overstrip <n>— remove question n and its answers (useful if a bad turn is confusing the agent)
Others:
run <args...>— pass arbitrary arguments tobrownin the containercompact [focus]— summarize older context, optionally with a focus hintcontinue— resume the last turn without adding a new user messagererender— replay the full session with tool outputs shownshell— open an interactive shell inside the containerconfig— open the config file in your editortokens— print token usage and context window size
Profiles
The config file is organized into named profiles — [default], [work],
[personal], etc. Other profiles inherit from default. default
inherits from built-in values.
The [default] profile is used when no --profile flag is given.
# ~/.config/james/config.toml
[default]
openai_base_url = "https://openrouter.ai/api/v1"
model = "openrouter/fusion"
model_parameters = { temperature = 0.8, reasoning_effort = "Minimal", verbosity = "Medium" }
# ...
[work]
openai_base_url = "https://api.openai.com/v1"
model = "claude-opus-4.6"
model_parameters = { reasoning_effort = "Medium" }
[local]
openai_base_url = "http://localhost:8080/v1"
searxng_base_url = "http://localhost:8081"
model = "coder"
Switch profiles with --profile:
james --profile work ask "add rate limiting"
To avoid passing --profile every time, set the JAMES_PROFILE
environment variable:
export JAMES_PROFILE=work
james ask "add rate limiting"
--profile overrides JAMES_PROFILE when both are set.
Skills and AGENTS.md
AGENTS.md — if a file named AGENTS.md exists in your project root, its
content is injected as a system message at the start of every conversation.
Use it for project-specific instructions the agent should always know.
SKILL.md — skills are on-demand runbooks the agent can load with the
skill tool. Each skill is a SKILL.md file with YAML frontmatter:
---
name: rust-style
description: Rust coding conventions for this project
---
# Rust Style Guide
Use 4-space indentation. Prefer `impl Trait` over generics when possible.
Place skills in ~/.config/james/skills/ (global, available everywhere) or
.james/skills/ in your project root (project-local). Only the name and
description are primed into the context; the full body is loaded only when
the agent asks for it, so skills don't bloat the context window.
Compatibility
Tested with llama-server (llama.cpp) and openrouter.ai. Any
OpenAI-compatible API should work.
GitHub Copilot's API does not expose a standard chat completions endpoint. If you want to use Copilot as a backend, bridges like ericc-ch/copilot-api may help.
License
MIT — see LICENSE.md.