Guidelines for agents working on Berd.
Berd is a standalone Tauri 2 + React 19 desktop. ACP is the main interface we use for the actual agent loop - creating and running sessions, finding available models, and setting configuration. When available, we work over ACP methods, but the UI can handle operations that are not yet in ACP or are client specific.
src/— React UI/features/shared codesrc-tauri/— Tauri shell that starts or resolvesgoose servesdk/— vendored@aaif/goose-sdkpackage and generated ACP typesdistro/— bundled app defaults and packaged distribution assetsgoose-backend.lock.json— pinned upstream Goose backend used by dev and bundlesscripts/ensure-local-goose.sh— managed local Goose checkout for devscripts/prepare-goose-sidecar.sh— stages the pinned or explicit Goose binary for Tauri bundlingscripts/update-goose-backend-lock.sh— resolves and records a new Goose backend pinsrc/features/berdctl/— berdctl command registrysrc-tauri/plugins/berdctl/— berdctl brokersrc-tauri/crates/berdctl/— bundled berdctl CLIdistro/skills/berd-help/references/berdctl.md— berdctl guidance agents read from the bundledberd-helpskill
LAWS/ defines required product and user experience behavior. Before planning,
implementing, or reviewing behavior changes, read LAWS/README.md and every
law file relevant to the affected behavior. Laws take correctness precedence
over the current code and tests; when they disagree, change the implementation
and tests or explicitly propose a product-approved law change.
Startup artifact media is resolved by the Tauri backend and returned as local
cache paths. Renderer code should use getArtifacts() or
selectProjectPreviewArtifacts() from src/shared/api/artifacts.ts, then pass
paths through convertFileSrc(..., "asset") before rendering media. Do not
vendor startup media, fetch catalogs in the renderer, or construct CDN
URLs in UI code.
Experiments are opt-in, user-local switches for in-progress UI or workflow
behavior. Use .agents/skills/experimental-features/SKILL.md before adding,
reviewing, graduating, or removing an experiment.
Do not create one-off localStorage keys, distro flags, Tauri commands, or capabilities for per-user experiments unless the skill says the use case requires it.
just setup— install pnpm deps, build SDK, build managed local Goosejust dev— run the Tauri app in dev modejust fmt— format frontend and Tauri/Rust filesjust fmt-check— check frontend and Tauri/Rust formattingjust lint— Biome lint checksjust typecheck— TypeScript type checksjust check— frontend formatting/lint/i18n/type checksjust test— Vitest suitejust tauri-check— Rust check with external sidecars disabledjust clippy— Rust clippy with warnings deniedjust ci— local validation gate: frontend checks, Tauri/Rust checks, clippy, tests, buildjust bundle— stage the pinned Goose backend and runpnpm tauri build
- Frontend changes:
just check - Vitest-covered behavior:
just test src-tauri/, Tauri config, sidecars, or Rust:just tauri-check- berdctl commands:
pnpm generate:berdctl-contract,pnpm vitest run src/features/berdctl, andcargo test -p berdctl(fromsrc-tauri/) - Broad/release/packaging changes:
just ci
berdctl lets agents control the app: CLI → broker → renderer registry.
Design and reasoning: docs/berdctl-architecture.md. To add or change a
command, use .agents/skills/berdctl-new-command/SKILL.md
(just new-command <noun> <verb>).
Invariants (1, 3, 4 are gated by test failures; 2, 5, 6 are review rules — the doc has the whys and the enforcement map):
- No command-specific knowledge below the renderer registry — the broker
stays transport-only (single reviewed exception: the create-cap's
action == "create"peek). - Single dispatch point in the renderer.
- Bounds live in zod; clap only mirrors them.
- Help is hand-authored in the command module (summary, description,
helpFooter,
.describe()per field);cargo test -p berdctlfails on empty/TODO prose. - UI-visible verbs only; prefer reversible mutations, but one-way visible product actions like creating a session or sending a prompt are allowed. Delete, bulk, silent, or invisible work reopens the auth decision as a design review, not a PR.
- Reviewers identify breaking wire reshapes and bump
protocolVersionin both discovery.rs copies and the contract.ts mirror; tests pin only that the constants are equal.
The CLI is built from the contract at startup: command modules (zod schemas
- help prose) →
pnpm generate:berdctl-contract→api-surface.json(the client-neutral wire surface, with JSON Schema per action) +cli-surface.json(the CLI projection) → embedded by the berdctl crate, whosetree.rsbuilds the clap tree at runtime (validate.rsgates consistency via the crate's tests). Never hand-edit the contract JSONs.
Release builds should use the Goose backend pinned in goose-backend.lock.json:
just setup
just bundleThe Tauri config uses "externalBin": ["binaries/goosed"]; the staging script
copies to src-tauri/binaries/goosed-$(rustc -vV | sed -n 's|host: ||p'), which
is the filename Tauri expects. Use GOOSE_BIN=/path/to/goose only as an explicit
local override.
- Use
@/imports for frontend code. - Use
cn()from@/shared/lib/cnfor Tailwind class merging. - All
<button>elements needtype="button"unless intentionally submitting. - Do not hand-edit generated SDK files unless this repo intentionally freezes a
generated snapshot; prefer updating
sdk/schema/*and regenerating.