diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..5604345 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,67 @@ +# AGENTS.md — plugin-cua + +Standalone out-of-tree plugin repo serving the `cua` computer-use verb +(`verb:cua`) and the `cua` device entity (`kind:cua`). The plugin is a Go module +at `candy/plugin-cua/` (module path +`github.com/opencharly/plugin-cua/candy/plugin-cua`); the root `charly.yml` only +declares `discover: candy` so the repo is a project and its candy is scanned. + +Canonical files: + +- `candy/plugin-cua/charly.yml` — the `plugin-cua:` candy entity (`plugin:` + block, `plan:` checks). +- `candy/plugin-cua/plugin.go` / `provider.go` — the verb provider, `NewMeta()`, + and the `Invoke` verdict path. +- `candy/plugin-cua/catalog.go` — one typed wrapper per method; `client.go` — + the `cua-driver` CLI client. +- `candy/plugin-cua/kind.go` — the `kind: cua` entity decode + device resolution. +- `candy/plugin-cua/schema/cua.cue` — the self-contained `#CuaInput` / + `#CuaMethod` / `#CuaDeviceInput` (single source for `params/cue_types_gen.go`). +- `charly.yml` — the root project manifest (`discover: candy`) plus the + disposable `cua-verb-probe` / `cua-entity-probe` local beds. +- `.github/workflows/tag-on-merge.yml` — CalVer tag + `CHANGELOG/` on merge; + `.github/workflows/ci.yml` — the module's Go gate. +- `README.md` — user overview only; never agent guidance. + +## Load these skills first (R0) + +- `/charly-check:cua` — the `cua:` verb + `kind: cua` reference. Load before + changing the verb surface or the device entity. This candy carries no + `skill:` entity of its own; the gap is tracked in + [opencharly/opencharly#291](https://github.com/opencharly/opencharly/issues/291). +- `/charly-internals:plugin` — the plugin authoring reference: the `plugin:` + block, the `verb` + `kind` provider classes, the per-plugin CUE-schema + contract. +- `/charly-internals:git-workflow` — before any git/PR action. + +## Build / validate / test + +- `go build ./...` in `candy/plugin-cua/` — compile the plugin module. +- `go test ./...` in `candy/plugin-cua/` — the plugin's Go tests. +- `charly box validate` at the repo root — the structural check (the candy + + `plugin:` block, CUE schema). +- The merge gate is the **org-wide** `charly/pr-validator` (required check + `validate / validate`, defined in `opencharly/.github`); this repo has **no** + per-repo candy gate. The `ci.yml` Go gate is this repo's own module test run. +- The R10 consumers are the `check-cua-*` beds in `opencharly/distro-omarchy`, + which compose this plugin against a live Cua Fleet desktop. + +## Modify this repo + +- Edit the `plugin-cua:` candy entity, the Go source, and `schema/cua.cue` + **together** — the schema is the single source for the verb's `params/` struct; + regenerate `params/cue_types_gen.go` from it. +- The read-only / mutating split is an ALLOWLIST (`catalog.go`'s + `mutatingMethods`): a new method is read-only by default until deliberately + classified. Keep the `allow_control` gate on every mutating path. + +## Landing + +- PR-only. Every change lands through a pull request; the org-required + `charly/pr-validator` validates the diff and body and arms native auto-merge on + PASS. Direct pushes to `main` are blocked. +- History lives in `CHANGELOG/` (written by `tag-on-merge` at merge time); the PR + body IS the changelog. +- The authoritative rulebook is the umbrella `AGENTS.md` in + `opencharly/opencharly` and `charly/AGENTS.md` in the charly repo. Do not + restate its rules here. diff --git a/README.md b/README.md index a2296f3..cece677 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,73 @@ # plugin-cua -The OpenCharly plugin serving the **`cua`** computer-use verb and the **`kind: cua`** -entity — driving a live desktop through [Cua Driver](https://github.com/trycua/cua) from -a charly candy/box plan, out-of-process. +The `cua:` computer-use verb and the `kind: cua` entity for OpenCharly — drive a +live desktop through [Cua Driver](https://github.com/trycua/cua) from a candy/box +plan, served out-of-process (`verb:cua`, `kind:cua`). -## What it is +The plugin is a standalone Go module the charly loader host-builds and serves +over go-plugin gRPC, so the Cua wire lives here, out of charly's core check +surface. Every call is routed through the venue's executor, so the driver is +driven **where it lives** (a desktop VM or pod), never on the host running +charly. -`cua:` is a DECLARATIVE check/control verb — authored as `cua: ` inside a -candy/box plan `check:`/`run:` step. It is NOT a host `charly check` subcommand: the -verb's implementation lives in this out-of-tree module, and at check time the host -dispatches `cua:` through the provider registry to this plugin (the same path `jetkvm:` / -`wl:` / `record:` take). +## What it provides -Cua Driver is the background computer-use driver that exposes its tools over MCP-over-stdio -**and** a CLI (`cua-driver call ''`, `cua-driver status`, `cua-driver -list-tools`, …). This plugin uses the CLI: one process per call, no long-lived client -state — exactly what an out-of-process check step wants. Every call is routed through the -venue's executor, so the driver is driven **where it lives** (a desktop VM or pod), never on -the host running charly. +| Capability | Surface | +|---|---| +| `verb:cua` | the declarative `cua:` check/control step | +| `kind:cua` | the `cua:` device entity — connection + permission defaults a step inherits via `device:` | -## Authoring a `cua:` step +## The verb -The method name is the scalar value for a bare-method step (`cua: status`), or the -`method:` key of the `cua:` map when the step carries cua-exclusive fields — those live -INSIDE the `cua:` map. Only the shared matchers (`stdout:`, `stderr:`, `exit_status:`) and -`context:`/`id:`/`timeout:` stay siblings. +Cua Driver exposes its tools over MCP-over-stdio **and** a CLI +(`cua-driver call ''`, `cua-driver status`, …). This plugin uses the +CLI: one process per call, no long-lived client state. -```yaml -- check: the Cua driver is running in the desktop session - context: [runtime] - cua: status - stdout: - - contains: running +Read-only: `status`, `doctor`, `version`, `list-tools`, `list-apps`, +`list-windows`, `get-window-state`, `get-desktop-state`, `get-screen-size`, +`screenshot`. -- check: a fresh desktop frame is captured as a real PNG - context: [runtime] - cua: - method: get-desktop-state - artifact: /tmp/desktop.png - artifact_min_bytes: 10000 - artifact_not_uniform: true -``` +Mutating (need `allow_control: true`): `click`, `double-click`, `right-click`, +`drag`, `type`, `press-key`, `hotkey`, `scroll`, `move-cursor`, `launch-app`, +`kill-app`, `bring-to-front`, `set-window-frame`, `set-value`, `recording-start`, +`recording-stop`, `recording-status`, `recording-render`, `call`. + +`delivery: foreground` escalates an input action to Cua's foreground route; the +default is background, and a structured refusal (`background_unavailable`) is +never a silent success. ## READ-ONLY BY DEFAULT — the desktop-safety gate -A live desktop is not a throwaway. Every **mutating** method (input, app/window control, -recording, the raw `call` escape hatch) requires `allow_control: true`; without it the step -reports a documented skip naming the gate rather than acting. The classification is an -allowlist, so a method added later is read-only by default. +A live desktop is not a throwaway. Every **mutating** method requires +`allow_control: true`; without it the step reports a documented skip naming the +gate rather than acting. The classification is an allowlist, so a method added +later is read-only by default. Cua Driver's own permission mode (`standard` / +`bounded` / `unrestricted`), declared on the `kind: cua` entity, remains the +authority the plugin passes through. -Cua Driver's own permission mode (`standard` / `bounded` / `unrestricted`), declared on the -`kind: cua` entity, remains the authority the plugin passes through. +## How to use it -## Methods +Compose the plugin candy in a box or check bed's `candy:` list: -Read-only: `status`, `doctor`, `version`, `list-tools`, `list-apps`, `list-windows`, -`get-window-state`, `get-desktop-state`, `get-screen-size`, `screenshot`. +```yaml +- '@github.com/opencharly/plugin-cua/candy/plugin-cua:' +``` -Mutating (need `allow_control: true`): `click`, `double-click`, `right-click`, `drag`, -`type`, `press-key`, `hotkey`, `scroll`, `move-cursor`, `launch-app`, `kill-app`, -`bring-to-front`, `set-window-frame`, `set-value`, `recording-start`, `recording-stop`, -`recording-status`, `recording-render`, `call`. +Then author the verb in a plan: -`delivery: foreground` escalates an input action to Cua's foreground route; the default is -background, and a structured refusal (`background_unavailable`) is never a silent success. +```yaml +- check: the Cua driver is running in the desktop session + context: [runtime] + cua: status + stdout: + - contains: running +``` -## `kind: cua` +## The `kind: cua` entity -A `kind: cua` entity carries the connection/permission defaults a step inherits when it -names the entity with `device:` — the driver path, host, permission mode, and a default -session `env`. Authored step fields win over the entity's. +A `kind: cua` entity carries the connection/permission defaults a step inherits +when it names the entity with `device:` — the driver path, host, permission mode, +and a default session `env`. Authored step fields win over the entity's. ```yaml my-desktop: @@ -82,16 +80,26 @@ my-desktop: WAYLAND_DISPLAY: wayland-1 ``` -## Verifying - -```bash -charly check live --filter cua # against a live deployment with the driver -charly check run cua-verb-probe # the registry/verdict smoke bed -``` - -The full contract is exercised by the `check-cua-*` beds in `opencharly/distro-omarchy`. - -## License - -MIT — see `LICENSE`. The plugin itself carries no Cua code; it drives the `cua-driver` -binary installed by the `layer-cua` candies. +## Layout + +- `candy/plugin-cua/` — the plugin module: `plugin.go`, `provider.go`, + `catalog.go` (one wrapper per method), `client.go` (the `cua-driver` CLI + client), `kind.go` (the `kind: cua` entity decode + device resolution), + `schema/cua.cue` (the self-contained `#CuaInput` / `#CuaMethod` / + `#CuaDeviceInput`), `params/cue_types_gen.go`, `plugin_test.go`, + `cmd/serve/main.go`. +- `charly.yml` — the root project manifest (`discover: candy`) plus the + `cua-verb-probe` / `cua-entity-probe` disposable local beds. +- `.github/workflows/tag-on-merge.yml` — CalVer tag + `CHANGELOG/` on merge. +- `.github/workflows/ci.yml` — the Go gate (gofmt, golangci-lint, `go vet`, + `go test`) for the module's unit tests. +- `LICENSE` — MIT. + +## Related + +- Owning skill: `/charly-check:cua` — the `cua:` verb + `kind: cua` reference. + This candy carries no `skill:` entity of its own; the gap is tracked in + [opencharly/opencharly#291](https://github.com/opencharly/opencharly/issues/291). +- `/charly-check:check` — the declarative check-step surface the verb is + authored through. +- [`opencharly/charly`](https://github.com/opencharly/charly) — the charly CLI.