10 KiB
AGENTS.md
Project-level context for AI agents working in this repository.
⚠️ IMPORTANT: All work must be done in THIS source directory (
~/src/ollie/). Never edit files under~/.config/ollie/— that is an install target. Changes made there are overwritten on the nextjust install-data. Edit source files here, then runjustto build and install.
Project Overview
Ollie is an AI agent runtime inspired by Plan 9: agent state and behaviors are exposed as files in a 9P namespace. Orchestration, scheduling, and UIs are external — shell scripts, editors, web apps. The core is minimal; capabilities come from composing scripts.
Repository Layout
Monorepo with Git submodules. Each submodule has its own Go module (except el which is Elisp and kde which is C++/Qt).
ollie/ ← you are here
├── virtfs/ (Go module) Virtual filesystem EDSL (FsNodeDecl, BuildTree, Tree)
├── toolsrv/ (Go) Tool server client library (Conn, Dial)
├── cmd/
│ ├── olliesrv/ (Go) 9P server binary
│ │ └── internal/
│ │ ├── fs/ 9P namespace (everything in spec.go)
│ │ │ ├── spec.go Single source of truth — entire namespace + handlers
│ │ │ └── support.go Shared utilities (streamChat, stripMarkers, dispatch)
│ │ ├── agent/ Agent loop, history, hooks
│ │ ├── session/ Session lifecycle, persistence
│ │ ├── backend/ LLM backends (ollama, openai, anthropic, etc.)
│ │ └── bypass/ Sandbox bypass broker
│ ├── toolsrv/ (Go) Tool server binary (sandboxed execution)
│ │ └── internal/
│ │ ├── fs/ toolsrv 9P namespace (spec.go)
│ │ └── sandbox/ Landlock enforcement
│ ├── ollie-9p/ (Go) CLI client for the 9P namespace
│ └── ollie-remote/ (Go) Remote execution agent
├── tools/ (Go) Tool implementations:
│ └── lsp/ LSP bridge + cmd/ binaries
├── kde/ (C++/Qt6) KDE integration: GUI, Kate plugin, KRunner, tray
├── contrib/elisp (Elisp) Emacs frontend (ellie.el)
├── data/agents/ Agent config JSONs (loaded at runtime)
├── data/prompts/ System prompt templates (markdown)
├── data/tools/ Tool executables + .meta sidecar files
├── data/skills/ Domain knowledge modules (markdown)
├── sandbox/ Landlock sandbox config YAML
├── doc/ Architecture docs, usage guide
├── contrib/ Community scripts
└── experiments/ Trial notes
Canonical source for prompts/tools/skills
⛔ DO NOT edit
~/.config/ollie/directly — it is an install target. All changes go in this repo.
Prompt and tool files live in data/. The just install-data target copies them to ~/.config/ollie/.
| Location | Purpose | Deployed by |
|---|---|---|
data/prompts/, data/tools/ |
Canonical source for all prompts, tools, and skills | just install-data |
data/skills/ |
Domain knowledge modules (markdown) | just install-data |
kde/ |
KDE-specific tool scripts (gui_*) |
just install-kde |
Build System
# Build everything:
just
# Individual targets:
just ninep # olliesrv + ollie-9p
just acme # acme frontend
just kde # KDE integration (cmake with ~/.local prefix)
just ollie-remote # remote execution binary
# Install targets (run automatically by the top-level paths):
just install-data # agents, prompts, tools, skills → ~/.config/ollie/
just install-scripts # CLI scripts → ~/.config/ollie/scripts/
just install-contrib # contrib scripts → ~/bin/
just install-kde # KDE plugins, desktop file, env → ~/.local/
just install-el # ellie.el → ~/.config/emacs/ellie/
# Test:
just test # run all tests
just test-core # core tests only
just test-9p # 9p tests only
# Lifecycle:
just uninstall # remove all installed files
just clean # remove build artifacts
Requires just: cargo install just
Testing
# All Go tests (excludes cmd/ollie-remote which needs just to build):
go test $(go list ./... | grep -v cmd/ollie-remote)
# KDE has integration test scripts:
cd kde && ./test-e2e.sh
# ollie-remote requires the just build pipeline to resolve embedded deps:
just ollie-remote
just test
# or individually:
just test-remote
Note on cmd/ollie-remote: This package uses //go:embed sandbox/default.yaml and //go:embed all:tools. The embedded files are not in the source tree — just ollie-remote copies them in before building then cleans up. Running go test ./... will fail on this package. Use just test-remote or go test $(go list ./... | grep -v cmd/ollie-remote) instead.
Language & Conventions
- Go (root module): Go 1.25+, standard library preferred, minimal dependencies.
- C++20/Qt6/KF6 (kde): CMake build, dual Qt5/Qt6 support where noted.
- Elisp (el): single file
ellie.el. - Tool scripts: Python 3, Bash, or compiled binaries. Must be executable. Metadata lives in a
.metasidecar JSON file (seedata/tools/*.meta).
Code style
- Go:
gofmt, short variable names, error returns (no panics), table-driven tests. - Tool scripts: emit structured output (
STATUS=ok,STATUS=error). Image/LSP tools return JSON content blocks. - Prompts: markdown, concise, example-driven. Follow the pattern in existing
tools-*.mdfiles.
Architecture (key concepts)
- One integration surface: 9P filesystem. Sessions at
session/{sname}/agent/{aname}/. All control viactl(rdwr: write command, read response). Tools, skills, memory on physical filesystem via env vars. - Agent loop (
cmd/olliesrv/internal/agent/loop.go): Streaming LLM call → parse tool calls → dispatch → loop until no more tool calls or max steps. - Tool dispatch (
cmd/toolsrv/): All tools execute remotely through a per-session tool server process. Tools are external scripts resolved from$XDG_CONFIG_HOME/ollie/tools. Load viactl tool_load <name>. - Sandbox (
cmd/toolsrv/internal/sandbox/): Landlock-based. Config insandbox/*.yamldefines filesystem access per profile. Escape via bypass broker (cmd/olliesrv/internal/bypass/). - Backends (
cmd/olliesrv/internal/backend/): Ollama, OpenAI-compatible, Anthropic, Copilot, Kiro, Gemini. Selectable per-session. - Prompts assembled at runtime: Agent JSON
promptarray specifies which prompt files to concatenate. Static prompt files can be included directly; the base system prompt is embedded in the binary and always prepended. - 9P namespace declared via virtfs EDSL: The entire filesystem is a single recursive
FsNodeDecltree incmd/olliesrv/internal/fs/spec.go, built byvirtfs.BuildTree(). Every handler is an inline closure — no indirection.
Key Files
| What | Where |
|---|---|
| 9P namespace (olliesrv) | cmd/olliesrv/internal/fs/spec.go |
| 9P namespace (toolsrv) | cmd/toolsrv/internal/fs/spec.go |
| virtfs EDSL | virtfs/decl.go, virtfs/builder.go |
| Agent loop | cmd/olliesrv/internal/agent/loop.go |
| Tool server binary | cmd/toolsrv/ |
| Tool server client | toolsrv/client9p.go |
| Remote execution | cmd/ollie-remote/ |
| Sandbox enforcement | cmd/toolsrv/internal/sandbox/ |
| Session management | cmd/olliesrv/internal/session/ |
| System prompt template | Embedded in binary (cmd/olliesrv/internal/prompts/) |
| Agent configs | data/agents/*.json |
| KDE GUI | kde/gui/ |
| Kate plugin | kde/kate/ |
Environment
Config lives in ~/.config/ollie/env. Key variables:
OLLIE_BACKEND— default backend (ollama, openai, anthropic, copilot, kiro)OLLIE_MODEL— default model- Tools live at
$XDG_CONFIG_HOME/ollie/tools(default:~/.config/ollie/tools) - Memory lives at
$XDG_CONFIG_HOME/ollie/memory(default:~/.config/ollie/memory)
Adding a new tool
Script-based tool (Python/Bash)
- Create an executable script in
data/tools/<name> - Create
data/tools/<name>.metawith JSON metadata:{"description":"...","prompt":"...","args":{...},"tier":"hot","readOnly":false} - Run
just install-datato install
Compiled tool (Go)
- Create a package under
tools/<family>/cmd/<name>/main.go- Read JSON args from stdin, write result to stdout, exit 0/1
- Share library code in
tools/<family>/(e.g.tools/lsp/)
- Create
<name>.metaalongsidemain.goin the samecmd/<name>/directory (same format as above) - Add a build target in the justfile that compiles to
{{cfg}}/tools/<name>and installs the.metafile alongside it - Run
justto build and install
The .meta file lives with the code that produces the tool, not in data/tools/. See the lsp-tools just target for the canonical pattern.
Both paths produce the same result: an executable + .meta in $XDG_CONFIG_HOME/ollie/tools.
The registry doesn't distinguish between scripts and binaries.
Adding a new prompt
- Write the markdown file in
data/prompts/ - If it should be loaded by default, reference it in
agents/default.json - Run
just install-datato install
Submodule workflow
The only remaining submodule is kde/. For KDE:
# Update KDE submodule:
git submodule update --remote --merge kde
# Work in the KDE submodule:
cd kde
# ... make changes, commit ...
git push
cd ..
git add kde
git commit -m "update kde submodule"
Each submodule has its own remote at ssh://lkn@lneely.de:44220/lkn/ollie-{name}.git.
When cloning, use --recurse-submodules or run git submodule update --init --recursive.
Environment
The only remaining submodule is kde/. For KDE:
# Update KDE submodule:
git submodule update --remote --merge kde
# Work in the KDE submodule:
cd kde
# ... make changes, commit ...
git push
cd ..
git add kde
git commit -m "update kde submodule"
Each submodule has its own remote at ssh://lkn@lneely.de:44220/lkn/ollie-{name}.git.
When cloning, use --recurse-submodules or run git submodule update --init --recursive.