diff --git a/skills/commander/SKILL.md b/skills/commander/SKILL.md new file mode 100644 index 0000000..4d91b89 --- /dev/null +++ b/skills/commander/SKILL.md @@ -0,0 +1,95 @@ +--- +name: Commanders +description: >- + Use when the user asks about a commander — what one is, how to start one, why one is not firing, how much + one costs — or when they describe work that should keep happening while they are away ("watch X and do Y", + "every time Z, run this"). Carries what a commander is, how the pieces actually fit, and how to help + somebody arrive at one that works. Open the Officer platform skill too if the answer needs an endpoint. +version: 1 +--- + +# What a commander is + +An agent that keeps working when nobody is watching. It waits for something, acts on it, and reports. + +Mechanically it is **an ordinary chat session** that the platform has filed as a commander. Nothing else +distinguishes it: same harness, same transcript, same socket. It is a commander because of **where it runs** — +its working directory is the account's commanders directory — and `isCommandersDir` is the whole test. + +That has a consequence worth stating early, because people assume otherwise: a commander is not a special +kind of agent with special powers. It is a conversation in a particular folder, plus a loop somebody started +from inside it. + +# The three pieces, and which one people get wrong + +**1. The conversation.** Where you and it agree what the job is. This is the part to spend time on. + +**2. The wait.** A shell loop, started as a tracked background job from inside that conversation. It polls, +and when its condition is true it acts. The wait happens **in the shell** — not by the model waking up to +check. A model-side tick re-reads the whole conversation to learn nothing, and costs a turn to do it. + +**3. The action.** The loop cannot do judgement — it cannot read a plan, weigh a host, or write a report. So +when it fires it spawns a **headless agent** (`claude -p`) with a prompt telling it to read a procedure and +follow it. That agent does the work and exits. + +The piece people get wrong is 3. They expect the commander itself to wake up and act. It does not: it started +a loop, and the loop starts somebody else. + +# Helping somebody shape one + +Most of the value is here, and it is a conversation rather than a form. Four questions, in this order: + +**What is the trigger, exactly?** Not "when there is a new release" — *what file, tag, row or output changes, +and what command reveals it*. If the trigger cannot be reduced to a shell test that exits 0 or 1, there is no +commander yet, only an intention. Push until it can. + +**What happens when it fires?** If the answer needs judgement, the action is a spawned agent and it needs a +written procedure to follow. Say that out loud — a procedure in a file, versioned, is what makes the same +deploy repeatable next month. A prompt typed into a dialog is not. + +**What must never happen twice?** This is the question nobody volunteers and it is the expensive one. A +trigger that stays true after a failed action fires again, and again. Ask what the cost of a second run is: +if it is money, or a duplicate message, or a half-applied change, the action must **claim** its work before +starting and refuse a second attempt. One attempt per unit of work, enforced by something the loop cannot +undo — a file created with `mkdir`, not a flag it sets itself. + +**What does "done" look like to you?** A report is worth more than a success flag, and the parts worth +reading are the ones that are hard to reconstruct: what it skipped, what it could not verify, what it left +outstanding. Ask for those explicitly or you will get a summary of what went right. + +# Things that are true and surprising + +- **Its cost line understates.** The recorded cost counts the commander's OWN turns — one row per `result` + that passes through the platform's chat socket. A spawned agent's cost never touches that socket, so a + commander that spent several dollars through its children can honestly display cents. Do not quote the + number as total spend. +- **A spawned agent cannot be found from its parent.** Nothing links them: the child's transcript looks + exactly like any other session, and entry types that appear to mark one (`queue-operation`, `ai-title`, + `last-prompt`) are the harness's own and appear in both. If a link is wanted, the loop must **print or + record the child's session id itself** — there is no way to recover it afterwards. +- **A child transcript is not permanent.** It is readable while it lives and for a while after. Two from a + previous day were gone. Anything worth keeping must be captured while it runs. +- **Poll a local address, never a public hostname.** Where the thing being watched is on the same machine or + the same tailnet, reach it directly. A public hostname through a reverse proxy shares one source IP with + everything else behind it, and rate limiting there refuses connections for long stretches — which shows up + as an access-rights error and is not one. +- **The platform may not know a watcher exists.** Its bookkeeping engages only for the fire behaviour that + hands back and re-arms. Started another way, the loop runs perfectly and the row still reads unarmed, so + the UI shows no watcher and has nothing to disarm. + +# When something is not firing + +In this order, because each rules out the one below: + +1. **Is the loop alive?** It is a tracked background job — look at the job, not at the agent. +2. **Is its condition ever true?** Run the test by hand, in the same shell form the loop uses. +3. **Can it reach what it polls?** An access-rights error on a repository or an endpoint that plainly exists + is usually a rate-limited public path, not a credential. +4. **Has it already claimed this work?** A refusal to act twice looks identical to a failure to notice. +5. **Only then** the model, the prompt or the procedure. + +# Where to look next + +For endpoints, permissions or why something is refused, open the **Officer platform** skill. For a deploy +specifically, `prompts/deploy-commander.md` in the platform repository is the worked example: a real +procedure, with the reasoning for each rule written beside it. diff --git a/skills/platform/SKILL.md b/skills/platform/SKILL.md new file mode 100644 index 0000000..249f6b7 --- /dev/null +++ b/skills/platform/SKILL.md @@ -0,0 +1,143 @@ +--- +name: Officer platform +description: >- + Use when the user asks what Officer can do, how to use a feature, what an endpoint is, why something is + refused, or how permissions and accounts work — "how do I start a commander", "can a member see my files", + "what is the API for tasks". Carries the permission registry, every API prefix and socket, the frontend + routes and the keyboard shortcuts, derived from the code rather than written by hand. +version: 1 +--- + + + +# What Officer is + +One self-hosted app for one person — the **owner** — that also admits additional accounts holding a +strict subset of it. The owner is user id 1, role `Super Admin`, and bypasses every permission check. +Everyone else gets what their ROLE is granted, and nothing else: grants are keyed on role, never on a +user, and **absence denies** — there is no row meaning "no". + +So "can this account do X" has three possible answers, and the third is the one people miss: yes, +no, and *not shareable at all*. + +# Permissions + +The authority for this is `src/servers/permissions/registry.ts`. Mounting an API router without an +entry there makes the server **refuse to boot** — so this list is complete by construction, not by +diligence. + +## `core` — every account has it and it cannot be denied — the caller acting on themselves + +- **Account** (`account`) — Sign in, your own profile, password, preferences and API keys + - API `/user`, `/dock`, `/api-keys`, `/theme-favorites` · screen `/settings/profile` +- **Themes** (`themes`) — Read the themes installed on this machine + - API `/themes` +- **Report a problem** (`bug-report`) — Send the server owner a bug report + - API `/bug-report` +- **Screen layouts and dashboards** (`dashboards`) — Where your own screen layouts and dashboards are saved + - API `/dashboards` · screen `/dashboards` +- **Command HQ** (`command-hq`) — Long-running commanders you started, organised into categories + - API `/command-hq` · screen `/command-hq` + +## `app` — grantable per role, at read or write + +- **Memos** (`memos`) — Notes + - screen `/memos` +- **Calendar and contacts** (`calendar`) — Calendars, contacts, and the app passwords that sync them to a phone + - API `/caldav`, `/dav` · screen `/calendar`, `/contacts` +- **Notifications** (`notify`) — Push notifications to your devices + - API `/notify` + +## `confined` — grantable, but only to an account that has its own Linux user; without one the grant resolves to nothing + +- **Terminal** (`terminal`) — A shell on this machine, as your own user + - API `/terminal` · socket `terminal` · screen `/terminal` +- **Chat** (`chat`) — The agent + - API `/chat`, `/agent-status`, `/claude-auth` · socket `chat`, `chat-notify` · screen `/chat` +- **Files** (`files`) — Your own home directory on this machine, and the code editor over it + - API `/file-browser`, `/upload` · screen `/files`, `/code-editor` + - a POST that is really a read, allowed at read level: `/download` + +## `execution` — runs as the server owner in the owner’s home. NEVER grantable, at any level, structurally + +- **Tasks and jobs** (`tasks`) — Running permissions, pipelines and background jobs + - API `/tasks`, `/jobs`, `/pipeline-jobs`, `/queue` · socket `task-runner`, `pipeline` · screen `/jobs` +- **Permission authoring** (`items`) — Skills, tools, agents and processes on disk + - API `/skills`, `/tools`, `/agents`, `/processes`, `/rescan` +- **Scraping** (`browser`) — Fetches and renders a page in a headless browser, as the server owner + - API `/scrape` + +## `admin` — the server owner only + +- **App store** (`app-store`) — Install, enable and remove the sidecars this server runs + - API `/app-store` · screen `/app-store` +- **Plugins** (`plugins`) — Install, enable and remove the plugins this server runs + - API `/plugins` · screen `/marketplace`, `/plugins` +- **Server settings** (`server-admin`) — Server configuration, integrations and the activity log + - API `/server-settings`, `/integrations`, `/activity`, `/system-monitor` · screen `/settings/server`, `/settings/system`, `/activity`, `/system-monitor` +- **User management** (`user-admin`) — Accounts, roles and what each role may reach + - API `/users` · screen `/settings/user-management` + - acts on the caller themselves, allowed with no grant: `PUT /` + +# Keyboard shortcuts + +Derived from `src/apps/officer-web/state/shortcuts.ts`. These have moved several times; quote this +list rather than remembering one. + +## Navigation + +- `Shift+Space` — Spotlight — go to any screen, plugin or setting by name +- `Ctrl+?` — This list + +## Themes + +The browser applies as you move, so you watch the app change rather than reading names. + +- `Ctrl+Shift+Space` — Open the theme browser +- `Ctrl+Shift+← / →` — Previous / next theme, without opening anything +- `Ctrl+Shift+F` — Star or unstar the current theme, from anywhere + +## In the theme browser + +- `j / k ↑ / ↓` — Move through the list +- `l →` — Apply the focused theme and keep browsing +- `h ←` — Put back the theme you started on +- `f` — Star or unstar the focused theme +- `Enter` — Keep what you are looking at +- `Esc` — Cancel and restore + +## In Spotlight + +- `↑ / ↓` — Move through the results +- `Enter` — Go there +- `Esc` — Close + +## Chat + +Recall only takes the arrow key from an EMPTY composer, and any other key ends a recall — so a draft is never overwritten. + +- `↑ / ↓` — Previous / next prompt you sent, shell-style +- `Enter` — Send · on an empty composer with something queued, send that now +- `Shift+Enter` — Newline +- `Esc` — Stop the turn that is generating + +# What is NOT in here + +- **Installed plugins.** They vary per machine and per install, so a generated list would be wrong on + every other server. Ask the platform: `GET /api/plugins` for what is installed here. +- **Request and response shapes.** This says which prefix a permission covers, not what a route + accepts. Read the router under `src/servers/api//` when the shape matters. +- **Anything about a commander beyond its permission.** That has its own skill — open it for what a + commander is, how one is started, and how to help somebody shape one. + +# When you are about to guess + +Do not. This file is generated from the registry, so a question it does not answer is a question the +registry does not answer either — say so, and read the code. Every documented count in this repository +that was maintained by hand has been wrong, and an agent quoting a stale number sounds exactly as +certain as one quoting a true one.