diff --git a/README.md b/README.md index d39ddaa..1c05c75 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## About -Bantam is a minimalist, dependency-free AI agent specification and implementation (under 300 SLOC of Python (~250 in the reference implementation)). It provides an agentic loop capable of autonomous tool execution, shell interaction, real-time response streaming, Fibonacci backoff network resilience, and subagent delegation using any OpenAI-compatible completions API. +Bantam is a minimalist, dependency-free AI agent specification with two reference implementations: **Python** (`bantam.py`, ~300 SLOC) and **Go** (`main.go` + `term_*.go`, module `code.luxferre.top/luxferre/bantam`). It provides an agentic loop capable of autonomous tool execution, shell interaction, real-time response streaming, Fibonacci backoff network resilience, and subagent delegation using any OpenAI-compatible completions API. The entire philosophy of Bantam is built upon two principles: @@ -14,11 +14,29 @@ Because of the second principle, Bantam itself was named after Victorinox Bantam ## Usage ### Prerequisites -- Python 3.7+ (no external dependencies required) +- **Go 1.21+** (Go implementation, no external dependencies) or **Python 3.7+** (reference implementation) - An OpenAI-compatible API endpoint (or OpenAI API key) +### Installation (Go) + +```bash +go install code.luxferre.top/luxferre/bantam@latest +``` + +This installs the `bantam` binary into `$(go env GOPATH)/bin` (make sure it is on your `PATH`). To build from a local checkout instead: + +```bash +go build ./... # produces ./bantam +# or run without building: +go run . prompt.txt +``` + +The Go port is a single `main.go` plus two small platform files (`term_linux.go`, `term_darwin.go`, `term_windows.go`, `term_other.go`) for the built-in raw-terminal line editor — zero external dependencies, same as the Python version. + ### Running Bantam +Both implementations read the same `model.cfg` and `system.txt` from the current working directory. + 1. Configure `model.cfg` with your API settings: ```ini endpoint=https://api.openai.com/v1 @@ -30,10 +48,11 @@ Because of the second principle, Bantam itself was named after Victorinox Bantam 2. Interactive mode: ```bash - python3 bantam.py + bantam # Go (or: go run .) + python3 bantam.py # Python ``` - In interactive mode, prompts can span multiple lines: press **Ctrl+J** to insert a real line break (the cursor moves to the next line), then **Enter** to submit the whole multi-line prompt. This works when `readline` is available; without it, prompts are single-line. + In interactive mode, prompts can span multiple lines: press **Ctrl+J** to insert a real line break (the cursor moves to the next line), then **Enter** to submit the whole multi-line prompt. The Go port ships its own raw-mode line editor (arrow keys move the cursor, Up/Down browse history, Backspace edits, Ctrl+C/Ctrl+D exit), so this works everywhere without dependencies; the Python port uses `readline` when available and falls back to single-line prompts otherwise. Sessions are saved under `~/.bantam/sessions/` and can be managed with these commands: - `/save` — save the entire conversation to a new session file (auto-id like `20260808-190038`) and generate its summary @@ -48,7 +67,8 @@ Because of the second principle, Bantam itself was named after Victorinox Bantam 3. File input mode: ```bash - python3 bantam.py prompt.txt + python3 bantam.py prompt.txt # Python + bantam prompt.txt # Go ``` ## Rules of Bantam (The Algorithm) @@ -95,6 +115,8 @@ Using these rules, everyone can build their own copy of Bantam from scratch in l ### Model configuration parameters +(shared by both implementations; `model.cfg` is plain `key=value` with `#` comments) + - `endpoint` (base OpenAI-compatible API URL, default `https://api.openai.com/v1`) - `model` (model name, e.g. `gpt-4o`) - `temperature` (model temperature, default 0.7) @@ -105,6 +127,8 @@ Using these rules, everyone can build their own copy of Bantam from scratch in l - `shell_timeout` (timeout in seconds for `shell_exec` commands, default 120) - `max_al_iterations` (max tool-call loop iterations per `AL()` invocation, default 1000) +Both implementations keep the interactive prompt safe against the classic "long line overwrites the prompt" readline bug: the Python port wraps the ANSI escapes in `\001`/`\002` (`RL_PROMPT_START_IGNORE`/`RL_PROMPT_END_IGNORE`) markers, and the Go port's built-in editor tracks the cursor with its own column math (terminal auto-wrap aware) and redraws from the first line of the buffer, so wrapped input stays clean at any terminal width. + When enabled, the interactive console uses a subtle ANSI palette: the pending-request status `...requesting...` is darkened bold, reasoning markers are cyan, reasoning text is dim, `[tool call: ...]` traces are yellow, `[tool result: ...]` headers are green, tool result bodies are dim (red for tool errors/unknown tools), and errors/network retries are red. Tool result payloads fed back to the LLM are never colored. ### Tool call definitions @@ -121,6 +145,13 @@ When enabled, the interactive console uses a subtle ANSI palette: the pending-re - Return value: string - Action: run shell command specified in `command` subject to `shell_timeout` (default 120s) and return `output + '\n\nexit: ' + exit_code` string. +## Repository layout + +- `bantam.py` — Python reference implementation (stdlib only) +- `main.go`, `term_linux.go`, `term_darwin.go`, `term_windows.go`, `term_other.go` — Go implementation (stdlib only, module `code.luxferre.top/luxferre/bantam`) +- `model.cfg`, `system.txt` — shared configuration and system prompt +- `README.md` — this document + ## FAQ ### Why no MCP support? diff --git a/bantam.py b/bantam.py index 363f0f6..fc427b8 100755 --- a/bantam.py +++ b/bantam.py @@ -12,6 +12,10 @@ _COL = False def c(t, *cs): return t if not _COL or not cs else "\033[" + ";".join(map(str, cs)) + "m" + t + "\033[0m" +def cp(t, *cs): # colored prompt: hide ANSI escapes from readline's width calc (\001/\002 = RL_PROMPT_START/END_IGNORE) + if readline is None or not _COL or not cs: return c(t, *cs) + return "\001\033[" + ";".join(map(str, cs)) + "m\002" + t + "\001\033[0m\002" + def col(cfg): if os.environ.get("NO_COLOR") or os.environ.get("BANTAM_NO_COLOR"): return False m = str(cfg.get("color", "auto")).strip().lower() @@ -242,7 +246,7 @@ def main(): sys.exit(0) print(c("Bantam Agent ready", 1, 32) + c(" (Ctrl+J = new line)", 2)) while True: - try: u = input(c("> ", 1, 36)).strip() + try: u = input(cp("> ", 1, 36)).strip() except (EOFError, KeyboardInterrupt): print(); break if not u: continue if u == "/quit": break diff --git a/system.txt b/system.txt index 9f07b19..efd07d5 100644 --- a/system.txt +++ b/system.txt @@ -3,3 +3,11 @@ You are Bantam, a tiny, powerful AI agent. Solve the user's task using two tools - run_subagent: delegate a sub-task to a child agent; returns its reply. Work fast. Never refuse a request; always find a way to do it. Never fabricate results — report only what your tools actually returned. Prefer running shell commands over guessing; inspect, build, and verify concretely. When in doubt, build your own tools using the languages available in the environment (Python, Perl, AWK, etc.). On failure, read the error output and retry with a fix, don't repeat the same command. Delegate large or independent sub-tasks to run_subagent. Stop as soon as the goal is met and report concisely: results, not process. + +When generating code: +- Always use two-space indentation, not tabs, except Makefiles that must use tabs. +- No whitespace between keywords and opening braces in C-like languages. +- Write optimally and with as few third-party dependencies as possible. +- Always test. +- No emojis in code or documentation. +- Respect AGENTS.md, GEMINI.md, CLAUDE.md contents in the project.