nandi/oripublic Fork 0
3d1f70c3b1e0d8c9a0b27adcc1bd1a9f053c050e
Commits
Clone
git clone https://git.rickub.com/nandi/ori.git
git clone ssh://git@rickub.com/nandi/ori.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

forked from bots-garden/ori

summary.md · 76 lines · 16.4 KBmarkdown Blame HistoryRaw
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday1# Ori — project summary
2
3## What it is
4
📝 Update project memory after the adapter switch (summary, history, handoff) 4166f8f k33g yesterday5Ori is a web client for ACP (Agent Client Protocol, https://agentclientprotocol.com) code agents: a Go backend serves an embedded React SPA and connects over ACP (JSON-RPC on stdio) to an agent subprocess — Claude Code via the `@agentclientprotocol/claude-agent-acp` adapter by default (since 2026-09-18). The browser gets a Zed-style agent panel: streamed markdown answers, collapsible thoughts, tool call cards (with statuses, locations and diffs), the agent's plan, and permission prompts answered from the UI. Implements ticket `.tickets/issues/0004-specifications.yaml`.
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday6
7## Architecture
8
9```
10browser ⇆ WebSocket /ws ⇆ Go backend ⇆ stdio (ACP) ⇆ agent subprocess
11```
12
13- Go module: `rickub.com/bots-garden/ori` (NOT github — the repo is not hosted on GitHub; the user corrected this explicitly).
14- `cmd/ori` — server binary; flags `--addr` (default `:8888`), `--cwd`, `--agent-cmd` (whitespace-split).
15- `cmd/ori-mock-agent` — deterministic demo ACP agent on stdio (thought → plan → read tool call → permission → edit tool call with diff → answer); used by `make run-mock` and the e2e test.
16- `internal/config` — flag parsing/validation.
17- `internal/httpserver` — embedded SPA (via `ui/embed.go`, `go:embed all:dist`), `/healthz`, SPA fallback, extra-route injection point for `/ws`.
18- `internal/agent` — spawns the agent, drives the ACP lifecycle through `github.com/coder/acp-go-sdk` v0.13.5 (community SDK listed by the official ACP docs; the zed-industries repo itself has NO Go library); generic `Handler` interface (session updates + blocking permission requests); real fs read/write; terminals refused (capability not announced).
19- `internal/files` — workspace file API (`GET /api/files`, `GET /api/file`, `PUT /api/file`, plus `GET /api/files/search?q=&limit=` — recursive, case-insensitive, skips `.git`/`node_modules`, default 50 / max 500 results, walk capped at 50 000 entries — and `GET /api/raw?path=` streaming bytes with pinned image Content-Types, Range support, no size cap): list/read/write, relative paths against `--cwd`, absolute allowed; access deliberately unrestricted (user's choice: ori targets already-isolated sandboxes); binary → 415, >5MiB → 413.
20- `internal/terminal` — one shell per WebSocket on `GET /ws/terminal` in a PTY (`creack/pty` v1.1.24); binary frames out, JSON `input`/`resize` frames in; shell from `$SHELL`→bash→sh.
21- `internal/skills``GET /api/skills`: discovers `<cwd>/.claude/skills/*/SKILL.md` and `~/.claude/skills/*/SKILL.md` (frontmatter `name`/`description`, directory-name fallback, project shadows user, sorted).
22- `internal/bridge` — hub between agent and browsers: JSON protocol (see `docs/*/reference/websocket-protocol.md`), broadcast with per-client buffers (slow client = dropped), append-only history (cap 4096) replayed on every connection, `user_message` echo so the server is the single source of truth, permission routing by requestId (pending requests replayed to late joiners; agent-side ctx cancellation handled). The `prompt` message carries optional `attachments: [{path,name}]`, turned server-side into ACP `resource_link` blocks (`file://<abs>`) next to the text block — `Prompter.Prompt(ctx, []acp.ContentBlock)` replaced `PromptText`; relative paths are joined to the root given by `Bridge.SetWorkspaceRoot` (wired in `cmd/ori/main.go`); `user_message` echoes the attachments. Uses `github.com/coder/websocket` v1.8.15; origins restricted to localhost.
23- `ui/` — Vite + React 19 + TS strict; zustand store around a pure reducer (`ui/src/reducer.ts`, handler-table style); WS client with backoff reconnect; thread resets on `hello` so replays are idempotent; unknown ACP update kinds ignored by design. Workspace panel (separate zustand store `ui/src/workspace.ts`): file tree (click=preview, double-click=edit; column resizable via `ResizeHandle` — ARIA separator, pointer drag / arrows / Home-End / double-click reset — width `fileTreeWidth` in the store, bounded 120–800 px, default 224, applied through the `--filetree-width` CSS custom property, persisted in localStorage `ori.fileTreeWidth`). Colour theme (`ui/src/theme.ts`, own zustand store): `light` (default) | `dark`, stamped as `data-theme` on `<html>` at module load and on change, remembered in localStorage `ori.theme`, toggled by the header ☾/☀ button; `app.css` keys the dark palette on `:root[data-theme="dark"]` (the former `prefers-color-scheme` media query is gone — decision: explicit choice, light default), `MonacoViewer` reads the store (`vs-dark`/`light`), the terminal stays dark. Preview tab (Monaco read-only for code — bundled workers, no CDN, lazy chunk ~1MB gzip; rendered Markdown; rendered AsciiDoc via lazily-imported `@asciidoctor/core`, Rendered/Source toggle), Editor tab (Monaco, dirty ●, Save/Ctrl+S via PUT /api/file), Terminal tab (xterm.js on /ws/terminal, starts on first activation); inactive panes are hidden not unmounted so shell/edit state survives tab switches; `execute` tool-call output renders in a monospace `<pre>`, never markdown. Composer completions (`ui/src/mentions.ts` pure helpers, `ui/src/useCompletion.ts`, `CompletionPopup`): `@` opens a workspace file search (→ attachments), `/` opens a merged list of local skills (`/api/skills`) and ACP `available_commands_update` commands (stored as `commands` in the reducer, cleared on `hello`; deduped by name, skill wins) and inserts `/<name> ` as plain text — the text is the invocation mechanism, there is no ACP "run command" method. Preview kinds `image` (`ImageView`, `<img>` over checkerboard via `/api/raw`, natural size shown) and `drawio` (`DrawioView`: iframe on `embed.diagrams.net` fed by postMessage, Rendered/Source toggle, 8 s timeout → Source fallback; `.drawio.svg/.png` are images). The mock agent emits `available_commands_update` (`review`, `compact`) on session start and echoes `[attached: name]`.
24- `ori-desktop/` — separate Go module `rickub.com/bots-garden/ori-desktop` (Wails v2.16.0, Go ≥ 1.25, "plain" template, `//go:embed all:frontend/src`, no npm; `frontend/src/main.js` is loaded as an ES module — `type="module"` — since the 2026-09-17 lint cleanup): connection screen (URL remembered in `<UserConfigDir>/ori-desktop/settings.json`, key `serverUrl`; auto-connect at startup), `/healthz` probe via bound Go methods (GetConfig, SaveConfig, CheckHealth, Connect, OpenInBrowser, SettingsPath), then the ori SPA loaded in an `<iframe>` (Wails v2 cannot navigate the main webview to an external URL; `runtime.BrowserOpenURL` is the fallback). Ticket 0005. `make desktop` / `make desktop-test`; root `go test ./...` unaffected (nested module).
25
26## Key decisions in force
27
📝 Update project memory after the adapter switch (summary, history, handoff) 4166f8f k33g yesterday28- Claude Code is reached through `@agentclientprotocol/claude-agent-acp` (default `npx -y @agentclientprotocol/claude-agent-acp`, since 2026-09-18; `internal/config.DefaultAgentCommand`, pinned by `TestDefaultAgentCommandIsClaudeAgentACP`). It bundles its own Claude Code CLI via `@anthropic-ai/claude-agent-sdk` 0.3.274 → CLI 2.1.274 delivered as per-platform optional deps (`…-sdk-linux-arm64`, `-linux-x64`, …; no `cli.js` any more). Replaced Zed's `@zed-industries/claude-code-acp` 0.16.2, whose embedded CLI 2.1.44 the API rejects for current models ("version 2.1.251 or newer is required"). The `claude` CLI itself has no native ACP mode; verified.
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday29- draw.io diagrams render online via `embed.diagrams.net` (no bundlable offline renderer exists: `viewer.min.js` is not on npm, `mxgraph` is archived and lacks draw.io's shapes, `drawio2svg` is GPL); future offline path = vendoring `viewer-static.min.js` (~3 MB, Apache-2.0). Documented in the architecture explanations.
30- ACP payloads cross the WebSocket verbatim (raw `SessionUpdate` / `RequestPermissionRequest`), keeping the front forward-compatible.
31- One agent session shared by all browsers in v1; multi-session is a planned evolution seam (`bridge.Prompter` interface, per-connection subscriptions).
32- The server owns the thread (history + user_message echo); the SPA rebuilds from replay on every (re)connection.
33- Quality config decisions (all user-approved): `.claude/**` excluded from qlty (vendored kit sources, the measurement instrument itself); `radarlint-python` plugin removed (its ~300MB JDK cannot fit the dedicated 488MB `~/.qlty` volume — diagnosis in `.qlty/qlty.toml` comments); `golangci-lint` + `biome` added manually (init missed both languages); `biome.json` at root parses `.vscode/**` as JSONC.
34
35## Build / test / run
36
37- `make deps` — npm install in `ui/`.
38- `make build` — SPA build then Go binaries (`bin/ori`, `bin/ori-mock-agent`). Order matters: the Go build embeds `ui/dist`.
📝 Update project memory after the adapter switch (summary, history, handoff) 4166f8f k33g yesterday39- `make test``go test ./...` + `vitest run` (the ui `test` script sets `NODE_OPTIONS=--no-experimental-webstorage`: Node 25's built-in `localStorage` lacks `clear()` and shadows jsdom's — 15 store tests fail without it; verified 2026-09-18 on Node 25.9). Also useful: `go test -race ./...`, `go test -short ./...` (skips e2e).
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday40- `make run` — with Claude Code adapter; `make run-mock` — with the demo agent (no Claude/network needed).
📝 Update project memory after the adapter switch (summary, history, handoff) 4166f8f k33g yesterday41- `make template` — builds the `k33g/ori` sandbox template image (`template/Dockerfile`, FROM `docker/sandbox-templates:claude-code`); launch with `sbx run -d claude <project> --template k33g/ori:0.0.2 --kit docker.io/k33g/ori-kit:latest --name ori -p 8888:8888/tcp` (detached — see the auto-stop trap below) (see `kits/ori/README.md` and docs how-to `run-in-a-sandbox`).
42- `scripts/launch-ori.applescript` (2026-09-18) — macOS launcher: `sbx ls -q` → restart by name or create (`k33g/ori:0.0.2`, `./kits/ori`, `-p 5555:8888/tcp`), poll `http://localhost:5555/healthz` up to 90 s, then `open ori-desktop/build/bin/ori-desktop.app`. Run with `osascript`, or `osacompile` into `scripts/Launch Ori.app`; repo root derived from the script's own location. Compile-checked and handlers unit-tested; full end-to-end run not yet done.
✨ Switch the ACP adapter to @agentclientprotocol/claude-agent-acp (template 0.0.2) 5d476ff k33g yesterday43- Kit publishing (sbx v0.43.0, verified 2026-09-18): `sbx kit validate kits/ori`, `sbx kit pack kits/ori -o ori-kit.zip`, `sbx kit push kits/ori docker.io/k33g/ori-kit:<tag>` — directory argument, not `spec.yaml`; there is no `sbx kit package`. Auth: `sbx login` session, then `sbx secret set --registry`, then the Docker credential store.
44- **Kit packaging trap (verified 2026-09-18)**: `pack`/`push` decode `spec.yaml` raw (no `${{ kit.args }}` expansion) — an arg placeholder in an integer field (`ports[].container`) breaks them even though `validate`/`run` accept it. `ports[0].container` is therefore a literal 8888; the `port` arg only drives the startup command and agentInstructions.
📝 Update project memory after the adapter switch (summary, history, handoff) 4166f8f k33g yesterday45- Published artefacts (2026-09-18): template `k33g/ori:0.0.2` on Docker Hub (multi-arch amd64+arm64, pushed by the user with `./template/build.sh`, ships only `claude-agent-acp`); kit `docker.io/k33g/ori-kit:latest` re-pushed with `--agent-cmd claude-agent-acp` + template 0.0.2. **They must move together**: the first `hello-world` sandbox paired image 0.0.2 with the morning's kit (`claude-code-acp`) and ori died with "executable file not found".
46- **Port publishing traps**: `-p H:8888` without protocol binds `tcp4` only — browsers resolving `localhost` to `::1` fail; always `-p H:8888/tcp` (dual-stack). Chrome refuses ports 6665–6669 (`ERR_UNSAFE_PORT`) whatever the server does. `sbx ports NAME` lists; `sbx ports ls` looks for a sandbox called `ls`.
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday47- **sbx auto-stop trap (verified in the sbx source + daemon log, 2026-09-17)**: sandboxd stops a sandbox 30 s (hard-coded `WithAutoStopDelay(30*time.Second)`, `sandboxd/pkg/server/backend_dockernext.go`) after its last CLI "sentinel" session closes; `sbx create`/`sbx exec`/`sbx run` all hold one for their own duration, browser traffic on a published port counts for nothing. Only a sandbox created with the spec field `detached: true` (CLI: `sbx run -d …`, absent from `sbx create`) is exempt; the field is create-time only (no PATCH), so an existing sandbox must be `sbx rm`'d and recreated with `sbx run -d`. No settings/env knob for the delay.
📝 Update project memory after the adapter switch (summary, history, handoff) 4166f8f k33g yesterday48- Quality: `python3 .claude/skills/quality/scripts/quality_report.py --workspace .` (the script lives in the repo, not `~/.claude`). Gate PASS, run #18, 2026-09-18: 0 errors, 0 warnings, 0 smells, complex 532 (covers the adapter switch, the MIME pin, the copy-paste feature). **Mac trap**: `.qlty/{logs,out,plugin_cachedir,results}` are symlinks qlty creates into its cache; ones created inside the sandbox point at `/home/agent/…` and dangle on the Mac, making every plugin invocation "FATAL" with no detail — delete the dangling links, qlty recreates them. `.qlty/qlty.toml` pins golangci-lint 2.13.2 (built with Go 1.27): qlty's default 1.61.0 and its latest-known 2.6.2 refuse this repo's `go 1.26.5`.
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday49- `docs/source-code-analysis.adoc` — AsciiDoc snapshot analysis of the codebase (2026-09-17), outside the bilingual Diátaxis set.
50
51## Environment facts (sandbox)
52
53- `~/.qlty` is a dedicated 488MB volume, too small for qlty's tool cache; the cache was moved to `~/.qlty-cache` on the main disk with a symlink `~/.qlty/cache -> /home/agent/.qlty-cache`. If qlty reports "No space left on device", check this symlink survived. (It did NOT survive a sandbox recreation on 2026-09-17: the volume came back empty and the first run failed exactly that way; recreating the symlink fixed it.)
54- `~/.npm` is the same kind of 488MB dedicated volume and filled up when installing Monaco; the npm cache now lives at `~/.npm-big` via `NPM_CONFIG_CACHE` exported in `/etc/sandbox-persistent.sh`.
📝 Update project memory after the adapter switch (summary, history, handoff) 4166f8f k33g yesterday55- Running ori inside this Claude-Code-driven sandbox requires `env -u CLAUDECODE ./bin/ori` (Claude refuses nested sessions). With the current template/kit (`claude-agent-acp`, bundled CLI 2.1.274) no `CLAUDE_CODE_EXECUTABLE` is needed. It remains the lever if something spawns the old Zed adapter (points it at another `claude` binary) — but the template's own CLI is 2.1.246 (base image 2026-08-26), below the 2.1.251 floor the API enforces for `claude-fable-5-1[1m]` (host `~/.claude/settings.json`), so `claude update` inside the sandbox would come first. Handoffs `2026-09-18-claude-version-too-old.md`, `2026-09-18-claude-agent-acp-adapter.md`.
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday56- npm blocks install scripts by default here; esbuild's postinstall was approved via `npm approve-scripts` (recorded in `ui/package.json` `allowScripts`).
57
58## Known limitations
59
60- `--agent-cmd` is split on whitespace; arguments containing spaces need a wrapper script.
61- One prompt turn at a time; a second `prompt` while a turn runs is rejected with an error event.
62- Terminal ACP methods are not supported (declared absent; answered with JSON-RPC method-not-found).
63- `user_message` / thought / message chunks only render text content blocks; images/audio/resources are not rendered yet; `user_message.attachments` are not rendered as chips (only the `@path` text mention).
64- Skills list is fetched once per page load; the file search walks whatever sits under `--cwd` (an untracked `sandboxes/` clone at the repo root gets walked).
65
📝 Update project memory after the merge into main 3017563 k33g yesterday66- Git: remote `origin` = `https://git.rickub.com/bots-garden/ori.git` (Gitea-style self-hosted, not GitHub). `main` fast-forwarded to `feat/acp-webapp` at commit `76d62ac` on 2026-09-17; **not pushed** yet. `hello.md` (a story generated during an ori test) and `.claude/settings.local.json` are deliberately left untracked.
67
✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme 76d62ac k33g yesterday68## Not yet established
69
70- ori-desktop: the window was launched by the user on their Mac (build OK, Check OK); after the CSS specificity fix (`#connect-screen` outranked `.screen[hidden]`, so the connect screen never hid and the viewer rendered below the fold) it has not yet been confirmed that the iframe actually shows ori inside WKWebView (`wails://` origin loading `http://localhost`; `NSAllowsLocalNetworking` set in `build/darwin/Info.plist`).
71
📝 Update project memory after the adapter switch (summary, history, handoff) 4166f8f k33g yesterday72- Real Claude Code sessions: exercised by the user on 2026-09-18 (sandbox `hello-world`, template 0.0.2 + published kit, real prompt answered). Still missing: an automated end-to-end test that drives a real prompt.
📝 Update project memory after the merge into main 3017563 k33g yesterday73
🛟 Updated. f5c963a k33g yesterday74## Tickets (`.tickets/`)
75
76- Managed by the **IssueSpec** VSCodium extension (`k33g.issuespec` 0.1.28, author = the user; source clone at `~/CodeBerg/issuespec`, format reference `docs/en/reference/ticket-files.md`). Hand edits are allowed but must keep the schema: a task is `id` (int ≥ 1, unique in the issue), `title`, `state: open|closed`, `priority: none|low|medium|high|urgent` (always written), `author {name,email}`, `createdAt`, optional `updatedAt`, and `closedAt` only while closed. The `text/done` checklist shape is **invalid** (fixed in ticket 0005 on 2026-09-18; validation recipe in handoff `2026-09-18-ticket-0005-format.md`).