12 KiB
AnviLLM
An Acme-native interface for LLM chat backends
AnviLLM brings Claude, Kiro, and other LLM chat interfaces directly into your Acme editor. It provides:
- Native Acme integration: Chat sessions appear as Acme windows with familiar tag commands
- 9P filesystem: Sessions exposed as files (
9p ls agent) - Multi-backend support: Run sessions for Claude and Kiro (and other supported backends) simultaneously
- Tmux isolation: Each session runs in its own tmux window for direct access when needed
- Session management: Save/load transcripts (if supported by backend, otherwise take advantage of acme), alias sessions, switch between multiple conversations .
- If all else fails: Fire up a terminal and attach to the correct
tmuxsession with a single click.
Architecture
┌─────────────────────────────────────────────────┐
│ Acme Windows │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ /AnviLLM/ │ │ /chat/claude │ │
│ │ Sessions │ │ Save Load │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ Session Manager │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Backend: │ │ Backend: │ │ Backend: │ │
│ │ Claude │ │ Kiro-CLI │ │ (more) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ 9P Filesystem │ │ Tmux Sessions │
│ /mnt/anvillm/ │ │ anvillm-claude │
│ agent/ │ │ anvillm-kiro-cli │
│ <id>/ │ └──────────────────────┘
│ in │
│ out │
│ ctl │
│ backend │
└──────────────────┘
Requirements
- Go 1.21+ (for building)
- Plan 9 from User Space (plan9port) - provides Acme and 9P tools
- tmux - session isolation and management
- At least one backend CLI:
- Claude Code CLI - Anthropic's official CLI
- kiro-cli - Alternative Claude interface
Installation
git clone https://github.com/lneely/anvillm
cd anvillm
mk # installs to $HOME/bin
Usage
Starting AnviLLM
Type Assist somewhere in Acme, and middle click. This opens the main /AnviLLM/ window in Acme showing all active sessions (probably empty).
Creating Sessions
From the /AnviLLM/ window, use tag commands:
- Kiro
<directory>- Start a kiro-cli session in the specified directory - Claude
<directory>- Start a Claude Code session in the specified directory
Example: Click on a path, and 2-1 chord on Claude to start a Claude session.
Each session opens a new $PWD/+Chat.<session-id> window where you can:
- Type prompts in the window body
- Use
Getto send the text as a prompt - Use
Saveto export the transcript - Use
Loadto import a previous transcript - Use
Attachto open the underlying tmux window
Session Management Commands
Main Window (/AnviLLM/)
- Open
<session-id>- Reopen a session window (2-1 chord with session ID highlighted). Or, just right-click the session ID! - Kill
<pid>- Terminate a session (2-1 chord with pid highlighted)
Chat Windows (/chat/<backend>/<id>)
- Send - Send the window body as a prompt to the LLM
- Save
[path]- Save transcript to file (if backend supportrs it, defaults to~/.config/anvillm/transcripts/<timestamp>.txt) - Load
<path>- Load a previous transcript (if backend supports it) - Alias
<name>- Give the session a memorable name (2-1 click with anameargument) - Attach - Open the tmux window for direct terminal access
- Stop - Interrupt the current running prompt (CTRL+C)
9P Filesystem
AnviLLM exports a 9P filesystem at agent:
agent/
└── ctl # Control file (create new sessions)
└── list # list of active sessions
└── <session-id>/
├── in # Write prompts here
├── out # Read responses here
├── err # Error output
├── ctl # Session control (stop, close)
├── state # Current state (idle, running, exited)
├── pid # Process ID
├── cwd # Working directory
├── alias # Session alias
├── winid # Acme window ID
└── backend # Backend name (claude, kiro-cli)
9P Usage Examples
Create a new session:
echo 'new claude /home/user/project' > agent/ctl
Send a prompt:
echo 'What is the capital of France?' > agent/a3f2b9d1/in
9p read agent/a3f2b9d1/out
Check session state:
9p read agent/a3f2b9d1/state
# Output: idle
Backends
Supported Backends
Claude (Claude Code CLI)
- Official Anthropic CLI for Claude
- Supports chat, session save/load, slash commands
- Install:
npm install -g @anthropic-ai/claude-code
Kiro-CLI
- Alternative Claude interface provided by Amazon for AWS development
- Supports
/chat loadand/chat savefor session persistence - Install: See kiro-cli repository
Adding New Backends
Backends are defined in internal/backends/. To add a new backend:
- Create a new function in
internal/backends/yourbackend.go:
func NewYourBackend() backend.Backend {
return tmux.New(tmux.Config{
Name: "yourbackend",
Command: []string{"your-cli-command", "args"},
Environment: map[string]string{
"TERM": "xterm-256color",
"NO_COLOR": "1",
},
TmuxSize: tmux.TmuxSize{
Rows: 40,
Cols: 120,
},
StartupTime: 15 * time.Second,
Detector: &yourDetector{},
Cleaner: &yourCleaner{},
Commands: &yourCommandHandler{},
})
}
-
Implement the required interfaces:
Detector- Detect when the backend is ready and when responses completeCleaner- Strip ANSI codes and noise from outputCommandHandler- Handle backend-specific slash commands
-
Register in
main.go:
backendMap := map[string]backend.Backend{
"kiro-cli": backends.NewKiroCLI(),
"claude": backends.NewClaude(),
"yourbackend": backends.NewYourBackend(),
}
- Add tag command in
main.goevent loop
See internal/backends/kirocli.go or claude.go for complete examples.
Configuration
Terminal Command
The terminal used for Attach is configured in main.go:
const terminalCommand = "foot" // Change to: "kitty", "xterm", "alacritty", etc.
Tmux Session Names
Backends create persistent tmux sessions named anvillm-<backend>:
anvillm-claudeanvillm-kiro-cli
These sessions persist across AnviLLM restarts and are cleaned up on exit.
Sandboxing
AnviLLM uses landrun to sandbox backend processes for enhanced security. Sandboxing restricts filesystem and network access for LLM-controlled processes while allowing backends to run with full permissions (--dangerously-skip-permissions, --trust-all-tools) within the sandbox.
How It Works
landrun --rw /project --rox /usr --connect-tcp 443 -- claude --dangerously-skip-permissions
↑ ↑
Sandbox layer (restricts access) Backend runs with full permissions
(but only within sandbox)
Quick Start
Sandboxing is enabled by default with a locked-down policy:
- Filesystem: Read/write access to session working directory only
- System binaries: Read-only + execute access to
/usr,/lib,/bin,/sbin - Network: HTTPS (port 443) for API calls
- Config files: Access to
~/.claude,~/.kiro,~/.config/anvillm
Requirements
- landrun: Install with
go install github.com/landlock-lsm/landrun@latest - Linux Kernel: 5.13+ with Landlock support
- Fallback: Runs unsandboxed if landrun unavailable (best-effort mode)
Configuration
Configure sandboxing via the Sandbox tag command in the main /AnviLLM/ window:
- Click Sandbox to open the configuration window
- Click Edit to modify
~/.config/anvillm/sandbox.yaml - Click Reload to reload configuration from disk
- Create new sessions to apply changes
Configuration file: ~/.config/anvillm/sandbox.yaml
Important: Changes apply to NEW sessions only. Active sessions are NOT affected.
Path Templates
Use these templates in filesystem paths:
{CWD}- Session working directory{HOME}- User home directory (~){TMPDIR}- Temporary directory (/tmp)
Common Configurations
Development (Permissive)
network:
enabled: true
connect_tcp:
- "443"
- "80"
- "3000-9000"
filesystem:
rw:
- "{CWD}"
- "{HOME}/.npm"
- "{HOME}/.cache"
- "{HOME}/.local"
rox:
- "/usr"
- "/lib"
- "/bin"
Code Review (Restrictive)
network:
enabled: false
filesystem:
ro:
- "{CWD}" # Read-only project access
rw:
- "{TMPDIR}"
rox:
- "/usr"
- "/bin"
Disabled (Testing Only)
general:
enabled: false
Security Model
Default protections:
- ✓ Cannot read sensitive files outside CWD (e.g.,
~/.ssh/id_rsa,~/.aws/credentials) - ✓ Cannot modify system files
- ✓ Cannot make unauthorized network connections
- ✓ Cannot access files in other users' directories
User can still:
- Modify files in session CWD (by design - project workspace)
- Weaken sandboxing via config (user choice)
Troubleshooting
Landrun not available?
- Install:
go install github.com/landlock-lsm/landrun@latest - Check status: Click
Sandbox→Statusin AnviLLM
Network errors?
- Ensure
connect_tcp = ["443"]in config for API calls - Or set
unrestricted = truefor full network access
File access denied?
- Add required paths to
rworroin config - Example for SSH keys:
ro = ["{HOME}/.ssh"] - Example for NPM:
rw = ["{HOME}/.npm", "{HOME}/.cache"]
Check sandbox status:
# View current config
cat ~/.config/anvillm/sandbox.yaml
# Test landrun availability
landrun --version
For more details, see landrun documentation.
Troubleshooting
"Backend not found" error
Make sure the backend CLI is installed and in your PATH:
which claude
which kiro-cli
Tmux windows not cleaned up
If AnviLLM exits unexpectedly, you may have orphaned tmux sessions:
tmux list-sessions | grep anvillm
tmux kill-session -t anvillm-claude
tmux kill-session -t anvillm-kiro-cli
Can't connect to 9P filesystem
Make sure plan9port is installed and 9P tools are working:
9p ls agent