diff --git a/.design-sync/conventions.md b/.design-sync/conventions.md index 2a9c0b65..b8e4a1d7 100644 --- a/.design-sync/conventions.md +++ b/.design-sync/conventions.md @@ -1,6 +1,6 @@ # Building with Botloft -Botloft is a desktop app where a crew of Claude Code bots works together. Each bot is a small flame +Botloft is a desktop app where a crew of AI agents works together. Each agent is a small flame mascot in its own color. The look is quiet and warm: neutral surfaces, one orange accent, IBM Plex. ## Setup @@ -9,9 +9,9 @@ mascot in its own color. The look is quiet and warm: neutral surfaces, one orang (Button, fields, Dialog, Callout, BotAvatar, BotStateBadge...) need nothing around them. - **App screens need `BotloftProvider`.** `Sidebar`, `CrewBots`, `BotRun`, `InboundRow` and `ApprovalCard` are the app's real screens and read the app's data: wrap them in one - `` per design (not one per piece). Build the data - with `makeCrew`, `makeBot`, `makeMessage` and `chat.*` (below); never write a bot object by hand. - A new `bots` array updates every screen, which is how a design animates states. + `` per design (not one per piece). Build the data + with `makeCrew`, `makeBot`, `makeMessage` and `chat.*` (below); never write an agent object by hand. + A new `agents` array updates every screen, which is how a design animates states. - **Theme:** light by default. Put `data-theme="dark"` on a wrapper (or ``) and every token switches. Give the page `bg-canvas text-ink` so the background follows the theme. - **Language:** the components' own words (state labels, Close, Cancel) follow the browser's @@ -40,7 +40,7 @@ use `style={{ height: 360 }}` for one-off sizes. The tokens behind them are CSS variables: `--canvas`, `--panel`, `--sunken`, `--line`, `--line-strong`, `--ink`, `--ink-soft`, `--muted`, `--accent`, `--ok`, `--work`, `--warn`, `--danger`. Use `var(--accent)` in inline styles; never hard-code hex for UI colors. -Bot colors are the exception: each bot has its own from `Botloft.AVATAR_PALETTE` (`#FF7A59` +Agent colors are the exception: each agent has its own from `Botloft.AVATAR_PALETTE` (`#FF7A59` Botloft orange, `#5EC8FF`, `#A48BFF`, `#9BE564`, `#FFC857`, `#FF7EB6`, `#3DD9C1`, `#E8D5B0`); `makeBot` hands them out in that order. @@ -56,55 +56,55 @@ const scout = makeBot({ crew, name: "Scout", state: "busy", role: "Finds sources - `state`: `idle`, `busy` (working), `needs_approval`, `launching`, `rate_limited`, `backoff`, `auth_error`, `offline` (with `paused: true` it reads Paused). -- A bot's turn is a list for `BotRun`: `chat.reply(bot, markdown)`, `chat.tool(bot, { name: - "WebSearch" | "WebFetch" | "Read" | "Write" | "Bash", summary })`, `chat.askSite(bot, url)`, - `chat.askCommand(bot, command, { explanation })`, `chat.suggestBot(chief, { name, role, model, - instructions, reason })`, `chat.askPlan(bot, markdown)`, `chat.turn(bot, seconds)`. Each takes +- An agent's turn is a list for `BotRun`: `chat.reply(agent, markdown)`, `chat.tool(agent, { name: + "WebSearch" | "WebFetch" | "Read" | "Write" | "Bash", summary })`, `chat.askSite(agent, url)`, + `chat.askCommand(agent, command, { explanation })`, `chat.suggestBot(chief, { name, role, model, + instructions, reason })`, `chat.askPlan(agent, markdown)`, `chat.turn(agent, seconds)`. Each takes `{ status: "allowed" | "denied" }` to show it answered. `ApprovalCard` takes one of them's `.body`. - `chat.askRoutine(bot, { name, prompt, schedule, bot: "writer" })` is a bot asking to set up a - routine (for itself, or for another bot by handle). -- Routines, for `CrewRoutines` (pass them as `BotloftProvider` `routines`): `makeRoutine({ bot, + `chat.askRoutine(agent, { name, prompt, schedule, agent: "writer" })` is an agent asking to set up a + routine (for itself, or for another agent by handle). +- Routines, for `CrewRoutines` (pass them as `BotloftProvider` `routines`): `makeRoutine({ agent, name, prompt, schedule: schedule.weekly([5], "09:00") | schedule.weekdays("08:30") | schedule.every(120), lastRun: { status: "done" | "failed" | "queued" | "skipped" } })`. Days are 1 = Monday ... 7 = Sunday. The next run follows the schedule. `status: "queued"` reads "Running now": a new `routines` array with it shows a routine starting. -- `makeMessage({ from: bot | "owner" | "system", to: bot, body, kind: "note" | "task" })` for - `InboundRow`: yours on the right, a bot's or Botloft's on the left. +- `makeMessage({ from: agent | "owner" | "system", to: agent, body, kind: "note" | "task" })` for + `InboundRow`: yours on the right, an agent's or Botloft's on the left. ## Components and their roles - `BotAvatar` is the mascot: `color`, `size`, `mood` (idle, working, waiting, tired, sleeping; it animates), `framed` for Botloft itself on its black square. -- `BotStateBadge` shows a bot's state with icon and word; `ChiefBadge` marks a crew's lead bot. +- `BotStateBadge` shows an agent's state with icon and word; `ChiefBadge` marks a crew's lead agent. - `Button` variants: `primary` (one per view), `secondary`, `ghost` (toolbars), `danger`. - `Dialog`, `Confirm` are modal (fixed, covering the window). `Callout` for notices in a page, `Toaster` + `notifyError(what, error)` for failures away from a form. - Fields: `TextField`, `TextArea`, `SelectField` (labeled), `Select` (custom dropdown), `Choices` (2-6 options side by side), `Switch`, `Tabs`, `Menu`, `Details`, `SidePanel`. -- App screens: `Sidebar` (crews and bots, 288px wide), `CrewBots` (a crew's bot cards), `BotRun` - (one turn of a bot in its chat, inside a `