Skip to main content

Development

How BOR is built, the conventions to follow, and where things live. If you’re extending BOR, start here.

Setup

git clone https://github.com/SixHq/bed-of-roses
cd bed-of-roses
npm install
npm run dev     # runtime on :7777 with --watch
# or
npm run v2      # the Electron host
There is no build step, no test suite, and no lint config. Node ≥ 20. The runtime is stdlib-only except for electron, @modelcontextprotocol/sdk, and @playwright/mcp.

The three trees

  • server/ — the Node runtime (the brain). HTTP server, LLM harness, providers, the tool protocol, handlers, memory, cron.
  • shell/ — the classic browser display (onboarding + dev), vanilla HTML/CSS/JS.
  • v2/host/ — the Electron Mac host (the product): presence window, native app windows, wallpaper.
See Architecture.

Key files

FileWhat it is
server/index.jsHTTP server + routes. Keep the path-validation guards.
server/ai.jsThe LLM harness — system prompt, chat loop, streaming.
server/protocol/registry.jsThe single source of truth for the tool surface.
server/protocol/parser.jsThe streaming XML/CDATA parser.
server/protocol/executor.jsRuns handlers, formats tool results.
server/protocol/handlers/*One module per tool family.
server/providers/*Provider adapters.
server/validator.jsSafety checks for AI HTML + shell commands.
v2/host/main.cjsElectron main process.
v2/host/presence.{html,js,css}The presence UI.

Conventions

These are enforced across the codebase:
  • Strict B&W default aesthetic. No emojis, gradients, or color in core UI. Apple-grade. Named themes are the deliberate exceptions. Run the design process before any UI redesign.
  • LLM model ids go stale. Before editing server/providers/*.js, fetch the provider’s live model docs — hardcoded ids rot in months.
  • Avatar = DiceBear avataaars only. Extend shell/memoji.js; never re-add hand-rolled canvas drawing.
  • Provider parity. All providers are first-class; never “optimize for one, adapt the rest.”
  • The default file root for AI-created files (no path given) is .bor/, not the project tree.
  • Surfaces are sandboxed iframes. All I/O goes through parent.postMessage → the bridge, validated server-side.
  • Audit everything that mutates state. Call ctx.audit({ kind, … }) from handlers.

The two LLM harnesses

During a migration, two harnesses coexist. The legacy XML-tool harness (server/ai.js + server/protocol/) is the default; the native-tool-calling harness (server/llm/) is gated behind BOR_NEW_LLM=1. Both read the same registry and handlers. When you add a tool, both pick it up automatically (the new path shims the legacy handler).

Extending BOR

Code style

Match the surrounding code — its comment density, naming, and idioms. Reference code as file_path:line. The runtime is dependency-light by design; prefer the standard library.