|
|
||
|---|---|---|
| cfg | ||
| claude | ||
| cmd | ||
| docs | ||
| elisp | ||
| internal | ||
| kiro-cli | ||
| mcptools | ||
| ollama | ||
| roles | ||
| scripts | ||
| services | ||
| skills | ||
| team-templates | ||
| workflows | ||
| .gitignore | ||
| BEADS.md | ||
| EVENTS.md | ||
| LICENSE.md | ||
| OUTPUT_PROTOCOL.md | ||
| README.md | ||
| SECURITY.md | ||
| SKILLS_PROMPT.md | ||
| WORKFLOW_DESIGN.md | ||
| bookmarks.html | ||
| go.mod | ||
| go.sum | ||
| mkfile | ||
| test-code-execution.sh | ||
| test_beads.sh | ||
README.md
AnviLLM
LLM orchestrator using 9P — scriptable, multi-backend, crash-resilient
Architecture
Core: anvilsrv (9P daemon), anvilmcp (MCP server), anvilwebgw (API proxy) Clients: Assist (Acme), anvillm.el (Emacs), anvillm (TUI), anvilweb (web) Benefits: Shared sessions, crash recovery, scriptable via 9P, cross-backend agent communication
Why 9P?
9P turns orchestration into file operations (read, write, ls, stat). Control agents with standard tools: cat, echo, shell scripts, or any language with file I/O. Compose workflows with Unix pipes, grep, awk, jq. Only requirement: a 9P client (plan9port's 9p or compatible). Built with 9fans.net/go.
Requirements
Go 1.21+, plan9port, tmux, landrun (kernel 5.13+), backend (Claude Code, Kiro, or Ollama + ollie)
Installation
git clone https://github.com/lneely/anvillm && cd anvillm && mk
Service integration (optional, see services/*/README.md):
# systemd user
cp services/systemd/anvilsrv-user.service ~/.config/systemd/user/
systemctl --user enable --now anvilsrv
# systemd system
sudo cp services/systemd/anvilsrv.service /etc/systemd/system/
sudo systemctl enable --now anvilsrv
# runit
sudo cp -r services/runit /etc/sv/anvilsrv
sudo ln -s /etc/sv/anvilsrv /var/service/
Usage
anvilsrv start # background
anvilsrv fgstart # foreground
anvilsrv status
anvilsrv stop
Assist auto-starts if needed.
Namespaces: Run multiple instances via $NAMESPACE (default: /tmp/ns.$USER.:0)
NAMESPACE=/tmp/ns.$USER.:1 anvilsrv start
NAMESPACE=/tmp/ns.$USER.:1 Assist
Clients
TUI: anvillm — Keys: s start | p prompt | t stop | R restart | K kill | a alias | r refresh | d daemon | ? help | q quit
Emacs: (require 'anvillm) then M-x anvillm — Keys: s start | p prompt | P prompt (minibuffer) | t stop | R restart | K kill | a alias | r/g refresh | d daemon | ? help | q quit
Web: anvilweb (port :8080) — ⚠️ NO auth, localhost only — Remote: ssh -L 8080:localhost:8080 user@remote
Acme: Type Assist and middle-click
Tag commands: Get Attach Stop Restart Kill Alias Context Daemon Inbox Archive
Interaction: Right-click ID for prompt window, 2-1 chord for fire-and-forget
| Command | Action |
|---|---|
Get |
Refresh session list |
Kiro <dir> / Claude <dir> / Ollama <dir> |
Start session |
Attach <id> |
Open tmux |
Stop <id> / Restart <id> / Kill <id> |
Control session |
Alias <id> <name> |
Name session |
Context <id> |
Edit context |
Daemon |
Manage daemon |
Inbox [id] / Archive [id] |
View messages |
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
NAMESPACE |
/tmp/ns.$USER.:0 |
9P namespace for server/client communication |
ANVILLM_BEADS_PATH |
~/.beads |
Beads database location (shared across namespaces) |
ANVILLM_TERMINAL |
foot |
Terminal command for tmux attach (Assist) |
ANTHROPIC_API_KEY |
— | Claude API key (optional if using claude /login) |
CLAUDE_AGENT_NAME |
anvillm-agent |
Claude agent configuration name |
KIRO_API_KEY |
— | Kiro API key (optional if using kiro-cli login) |
ANVILLM_OLLAMA_MODEL |
qwen3:8b |
Ollama model to use for ollama backend |
ANVILLM_SKILLS_DIR |
$CLAUDE_CONFIG_DIR/skills:~/.kiro/skills:~/.config/anvillm/skills |
Colon-separated skill directories (searched in order) |
Skills System
Skills are loaded from multiple directories via the anvillm/skills 9pfs. By default, searches:
$CLAUDE_CONFIG_DIR/skills(ifCLAUDE_CONFIG_DIRset)~/.kiro/skills~/.config/anvillm/skills
Override with ANVILLM_SKILLS_DIR (colon-separated paths). Skills are organized by intent (virtual directories) and discovered via SKILL.md front-matter.
Usage:
9p ls anvillm/skills # list intents
9p read anvillm/skills/help # search index
9p read anvillm/skills/tasks/beads/SKILL.md
Sandbox Config Templates
Available in sandbox YAML files (~/.config/anvillm/):
| Template | Expands To | Description |
|---|---|---|
{CWD} |
Current working directory | Session's working directory |
{HOME} |
User home directory | $HOME |
{TMPDIR} |
Temp directory | $TMPDIR or /tmp |
{XDG_CONFIG_HOME} |
XDG config | $XDG_CONFIG_HOME or ~/.config |
{XDG_DATA_HOME} |
XDG data | $XDG_DATA_HOME or ~/.local/share |
{XDG_CACHE_HOME} |
XDG cache | $XDG_CACHE_HOME or ~/.cache |
{XDG_STATE_HOME} |
XDG state | $XDG_STATE_HOME or ~/.local/state |
{ANY_ENV_VAR} |
Environment variable | Any $ENV_VAR from the environment |
Templates use {VARNAME} syntax. Any environment variable can be referenced.
Backends & Sandboxing
Backends: Claude (npm install -g @anthropic-ai/claude-code), Kiro (kiro.dev), Ollama (local models via ollie)
Sandbox: landrun (always enabled) — Defaults: CWD//tmp/config (rw), /usr//lib//bin (ro+exec), no network
Config (~/.config/anvillm/): Layered YAML files, most permissive wins:
- Default role:
roles/default.yaml
network: {enabled: true, unrestricted: true}
filesystem: {rw: ["{CWD}", "{HOME}/.npm"]}
Templates: {CWD}, {HOME}, {TMPDIR}, {XDG_*} (see Configuration)
Kernel requirements: 5.13+ (Landlock v1), 6.7+ (v4), 6.10+ (v5 network)
Set best_effort: true for unsandboxed fallback (⚠️ if no Landlock support)
Session lifecycle:
State transitions: idle ↔ running cycle via CLI hooks (userPromptSubmit when user sends prompt, stop when agent finishes). Crash → error → auto-restart → starting. Note: any state can transition to stopped or killed (not shown); stopped can restart → starting.
Self-healing: Auto-restarts crashes every 5s (preserves context/alias/cwd), skips intentional stops
Restored sessions automatically resume the latest conversation (kiro: -r, claude: -c).
Add backend: Implement CommandHandler/StateInspector in internal/backends/yourbackend.go, register in main.go
Ollama Backend
Run local LLMs via Ollama.
Requirements:
-
Ollama: Install from ollama.com
curl -fsSL https://ollama.com/install.sh | sh ollama serve ollama pull qwen2.5-coder:7b -
Ollie CLI: Install from github.com/lneely/ollie
git clone https://github.com/lneely/ollie && cd ollie && go install
Usage:
echo 'new ollama /path/to/project' | 9p write anvillm/ctl
Configuration: Set model via ANVILLM_OLLAMA_MODEL (default: qwen3:8b)
ANVILLM_OLLAMA_MODEL=llama3.2 echo 'new ollama /path' | 9p write anvillm/ctl
9P Filesystem
$NAMESPACE/agent:
anvillm/
├── ctl # "new <backend> <cwd>" creates session
├── list # id, alias, state, pid, cwd
├── events # Event stream (state changes, messages)
├── beads/ # Task tracking (persistent across namespaces)
│ ├── ctl # Commands: init, new, claim, complete, fail, dep
│ ├── list # All beads as JSON
│ ├── ready # Ready beads (no blockers) as JSON
│ └── <bead-id>/
│ ├── status
│ ├── title
│ ├── description
│ ├── assignee
│ └── json
└── <id>/
├── ctl # "stop", "restart", "kill"
├── state # starting, idle, running, stopped, error, exited
├── context # Prepended to prompts (r/w)
├── alias # Session name (r/w)
├── pid # Process ID
├── cwd # Working directory
├── backend # Backend name
├── role # Role name
├── tasks # Task names
├── tmux # Tmux session name
├── inbox # Incoming messages (JSON)
├── outbox # Outgoing messages (JSON)
├── completed # Archived messages (JSON, "Archive" in Assist)
└── mail # Write messages (convenience)
Client Interactions:
Different clients interact with different parts of the filesystem: frontends read state, control files manage sessions, scripts consume events.
Beads
Persistent task tracking via beads — Dependency-aware graph
echo 'init' | 9p write anvillm/beads/ctl
echo 'new "Implement login" "Add JWT auth"' | 9p write anvillm/beads/ctl
echo 'claim bd-a1b2' | 9p write anvillm/beads/ctl
echo 'complete bd-a1b2' | 9p write anvillm/beads/ctl
echo 'dep bd-child bd-parent' | 9p write anvillm/beads/ctl # child blocks parent
9p read anvillm/beads/ready | jq -r '.[] | "\(.id): \(.title)"'
Config: ~/.beads/ (override: ANVILLM_BEADS_PATH) — Shared across namespaces — See internal/beads/README.md
Events & Mailbox
Mailbox Flow:
Cross-backend communication: messages route between any participants (user, Claude agents, Kiro agents, Ollama agents) via the mailbox system.
Examples:
# Events
9p read anvillm/events # {"type":"state_change","session_id":"...","state":"running",...}
# Mailbox
echo '{"to":"a3f2b9d1","type":"REVIEW_REQUEST","subject":"...","body":"..."}' | 9p write anvillm/b4e3c8f2/mail
9p read anvillm/a3f2b9d1/inbox
9p read anvillm/a3f2b9d1/completed
Basic Session Example
# Create session
echo 'new claude /home/user/project' | 9p write anvillm/ctl
# List sessions (newest first)
9p read anvillm/list
# Get most recent session ID
ID=$(9p read anvillm/list | head -1 | awk '{print $1}')
# Send prompt via mailbox
echo '{"to":"'$ID'","type":"PROMPT_REQUEST","subject":"User prompt","body":"Hello"}' | 9p write user/mail
# Check state
9p read anvillm/$ID/state
# Read response from inbox
9p read user/inbox
See SECURITY.md
Bot Templates
Bot templates provide pre-configured agents for common tasks. Templates are shell scripts that create sessions with specialized context and configuration.
Available Templates:
Taskmaster— Converts project plans into dependency-aware task graphs (beads)Conductor— Orchestrates parallel execution by spawning and coordinating specialized agents
Usage:
./bot-templates/Taskmaster kiro /path/to/project
./bot-templates/Conductor kiro /path/to/project
Templates set up context, roles, and initial configuration. Interact via any client (Assist, TUI, Emacs, Web) or directly via 9P mailbox.
Creating Templates: Shell scripts that write to anvillm/ctl and configure context, role, tasks. See existing templates for examples.
Autonomous Workflows
Automate workflows with Taskmaster and Conductor bots. Input project plan → Taskmaster creates tasks with dependencies → Conductor orchestrates parallel execution.
Conductor receives the top-level bead ID, analyzes dependencies, and spawns agents to work in parallel. Agents notify Conductor when blocked; Conductor signals them to resume when dependencies resolve.
Usage
Examples use raw 9P calls. These actions can also be performed from any front-end (Assist, TUI, Emacs, Web).
1. Taskmaster: Create tasks from project plan
./bot-templates/Taskmaster kiro /path/to/project
TASKMASTER_ID=$(9p read anvillm/list | head -1 | awk '{print $1}')
# Send project plan
echo '{"to":"'$TASKMASTER_ID'","type":"PROMPT_REQUEST","subject":"Create tasks","body":"<your project plan>"}' | 9p write user/mail
# Get top-level bead ID
BEAD_ID=$(9p read anvillm/beads/list | jq -r '.[0].id')
2. Conductor: Execute work
./bot-templates/Conductor kiro /path/to/project
CONDUCTOR_ID=$(9p read anvillm/list | head -1 | awk '{print $1}')
# Send work request
echo '{"to":"'$CONDUCTOR_ID'","type":"WORK_REQUEST","subject":"Execute","body":"Complete bead '$BEAD_ID'"}' | 9p write user/mail
Conductor analyzes dependencies, spawns specialized bots, and delegates work in parallel.
Monitoring: Use any front-end (Assist, TUI, Emacs, Web). For custom integrations, see EVENTS.md.
MCP Integration
anvilmcp exposes AnviLLM via Model Context Protocol for Claude Desktop, Cline, etc.
Install:
./kiro-cli/install-mcp.sh # Adds to ~/.kiro/settings/cli.json
Tool: execute_code - Execute bash scripts in isolated subprocess
Enables code execution pattern for 80-99% token reduction:
- Progressive tool discovery via 9P filesystem
- Data processing in subprocess (not in context)
- Loops, conditionals, and complex workflows
See Code Execution User Guide for details.
Legacy tools removed: Direct tool calls (check_inbox, send_message, etc.) replaced by bash scripts accessible via 9p read anvillm/tools/anvilmcp/*.sh
Troubleshooting
| Problem | Solution |
|---|---|
| Can't connect | anvilsrv status; try anvilsrv start |
| Session won't start | Check stderr; verify backend installed |
| Landlock ABI error | Set best_effort: true or upgrade kernel |
| Permission denied | Add paths to layered config |
| Orphaned tmux | tmux kill-session -t anvillm-0 |
| 9P not working | 9p ls agent |
| Stale PID | anvilsrv stop auto-cleans |
| Daemon won't stop | anvilsrv fgstart for logs |
| anvilweb issues | Check anvilsrv running, namespace matches |
See Configuration section for environment variables.