# 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 next `just install-data`. > Edit source files here, then run `just` to 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 ├── agent/ (Go) Agent loop, history, hooks, commands ├── toolsrv/ (Go) Tool server, registry, sandboxed execution ├── session/ (Go) Session lifecycle, config, persistence ├── fs/ (Go) 9P filesystem tree (EDSL-declared) │ ├── spec.go Single source of truth — entire namespace declared here │ ├── fsnode.go Core FsNodeDecl type + Dir/Leaf/TemplateDir constructors │ └── handlers_*.go Read/write handlers grouped by scope (session, agent, root) ├── fsedsl/ (Go) Filesystem declaration EDSL (generic, reusable) │ └── builder.go BuildTree — walks spec, validates, produces *Tree ├── cmd/ (Go) Binaries (olliesrv, ollie-9p, ollie-remote) ├── tools/ (Go) Tool implementations: │ └── lsp/ LSP bridge + cmd/ binaries (definition, hover, rename, ...) ├── kde/ (C++/Qt6) KDE integration: plasmoid, 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 (installed to $XDG_CONFIG_HOME/ollie/tools) ├── 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 ```bash # 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](https://github.com/casey/just): `cargo install just` ## Testing ```bash # 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 `.meta` sidecar JSON file (see `data/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-*.md` files. ## Architecture (key concepts) 1. **One integration surface**: 9P filesystem. Sessions at `session/{sname}/agent/{aname}/`. All control via `ctl` (rdwr: write command, read response). Tools, skills, memory on physical filesystem via env vars. 2. **Agent loop** (`agent/loop.go`): Streaming LLM call → parse tool calls → dispatch → loop until no more tool calls or max steps. 3. **Tool dispatch** (`toolsrv/`): `shell` is built-in. All others are external scripts resolved from `$XDG_CONFIG_HOME/ollie/tools`. Load via `ctl tool_load `. 4. **Sandbox** (`sandbox/`): Landlock-based. Config in `sandbox/*.yaml` defines filesystem access per profile. Escape via bypass broker (`bypass/`). 5. **Backends** (`backend/`): Ollama, OpenAI-compatible, Anthropic, Copilot, Kiro, Gemini, CodeWhisperer. Selectable per-session. 6. **Prompts assembled at runtime**: Agent JSON `prompt` array 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. 7. **9P namespace declared via EDSL**: The entire filesystem is a single recursive `FsNodeDecl` tree in `fs/spec.go`, built by `BuildTree()` in `fsedsl/builder.go`. The `fs/` package is flat — handlers in `handlers_*.go`, ctl dispatch in `ctl.go`. See `doc/edsl.md` for the full reference. ## Key Files | What | Where | |------|-------| | Agent loop | `agent/loop.go` | | Tool server | `toolsrv/server.go` | | Remote execution | `toolsrv/remote.go`, `cmd/ollie-remote/` | | Sandbox enforcement | `sandbox/` | | 9P filesystem (EDSL spec) | `fs/spec.go` | | 9P filesystem (builder) | `fsedsl/builder.go` | | Session management | `session/session.go` | | System prompt template | Embedded in binary | | Agent configs | `agents/*.json` | | Standalone 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) 1. Create an executable script in `data/tools/` 2. Create `data/tools/.meta` with JSON metadata: ```json {"description":"...","prompt":"...","args":{...},"tier":"hot","readOnly":false} ``` 3. Run `just install-data` to install ### Compiled tool (Go) 1. Create a package under `tools//cmd//main.go` - Read JSON args from stdin, write result to stdout, exit 0/1 - Share library code in `tools//` (e.g. `tools/lsp/`) 2. Create `.meta` alongside `main.go` in the same `cmd//` directory (same format as above) 3. Add a build target in the justfile that compiles to `{{cfg}}/tools/` **and** installs the `.meta` file alongside it 4. Run `just` to 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 1. Write the markdown file in `data/prompts/` 2. If it should be loaded by default, reference it in `agents/default.json` 3. Run `just install-data` to install ## Submodule workflow The only remaining submodule is `kde/`. For KDE: ```bash # 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: ```bash # 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`.