Rework the README with current screenshots

The README was a bare file list. It now opens with the horizontal
lockup, a theme-aware hero and a workspace dashboard, both taken from
the running app, so GitHub's preview shows the product instead of
describing it.

AGENTS.md records that the design handoff is outdated: its screens and
briefs are history, while its brand assets (stones, coordinator, app
icon, wordmark) and their copies in src/data are still the foundation.
This commit is contained in:
2026-10-22 04:15:43 +01:00
parent 72a150c2b2
commit fe502b38fe
8 changed files with 131 additions and 29 deletions
+9 -2
View File
@@ -1,6 +1,13 @@
# Agent Instructions # Agent Instructions
This is **Cobble** (`cobble.moduletrace.org`): an open-source crew of small AI worker bots. The design handoff in `assets/handoff/` (gitignored locally: `PRODUCT.md`, `IMPLEMENTATION.md`, `tokens/`) is the starting point for the core concepts and visual language, not the end product. The app is expected to move away from it; when it and the current code or the user disagree, the code and the user win. Unit noun is **bot**, never "agent" or "expert". This is **Cobble** (`cobble.moduletrace.org`): an open-source crew of small AI worker bots. Unit noun is **bot**, never "agent" or "expert".
**The handoff is outdated.** `assets/handoff/` (gitignored locally: `PRODUCT.md`, `IMPLEMENTATION.md`, `tokens/`, `assets/`) is the *foundation*, not the design: it is what the project started from, and the app has moved well past it. Its layouts, screens (`assets/screens/*.png`), right-hand "working on" panel, and PRODUCT/IMPLEMENTATION briefs describe an early single-chat prototype and must not be used as reference for the current UI or copied into docs, screenshots or marketing. What is still authoritative in it:
- **Brand assets:** the stone icons (`public/brand/icons/*.svg`, mirrored into `src/data/stone-geometry.ts`), the coordinator (`public/brand/coordinator/`), the app icon (`public/brand/app-icon/`), and the wordmark (`public/brand/wordmark/`, mirrored into `src/data/wordmark-paths.ts`). These are the identity and do not change; the mark is a stone with exactly one flat cut, hue is a bot's identity (never status), the coordinator is hue-less.
- **Tokens:** `tokens/tokens.css` was the origin of the `:root` / `.dark` block in `src/styles/main.css`. From here on `src/styles/main.css` is the source of truth; treat the handoff CSS as historical.
For the current UI, read the code: `src/layouts/bots/` (rail, workspace rail, user popover), `src/pages/admin/bots/[bot_id]/` (conversation header, thread, composer, modals) and `src/pages/admin/workspaces/` (dashboard). When the handoff and the current code or the user disagree, the code and the user win, always.
## Stack ## Stack
@@ -238,7 +245,7 @@ Server functions use `index.server.ts` alongside `index.tsx` (e.g. `single-docum
- **UNCHANGEABLE RULE - twui components always win over raw JSX elements.** Use twui components (`H1`-`H5`, `P`, `Span`, `List`, `Img`, `Row`, `Stack`, `Container`, `Section`, `Card`, `Button`, `Tag`, `Link`, etc.) before basic JSX elements (`<h1>`-`<h6>`, `<p>`, `<span>`, `<ul>`, `<li>`, `<img>`, `<a>`, `<button>`, `<div>`-as-card, etc.). Never introduce or reintroduce a raw JSX element where a twui equivalent exists. Override twui defaults via `className` rather than reaching for the raw element. - **UNCHANGEABLE RULE - twui components always win over raw JSX elements.** Use twui components (`H1`-`H5`, `P`, `Span`, `List`, `Img`, `Row`, `Stack`, `Container`, `Section`, `Card`, `Button`, `Tag`, `Link`, etc.) before basic JSX elements (`<h1>`-`<h6>`, `<p>`, `<span>`, `<ul>`, `<li>`, `<img>`, `<a>`, `<button>`, `<div>`-as-card, etc.). Never introduce or reintroduce a raw JSX element where a twui equivalent exists. Override twui defaults via `className` rather than reaching for the raw element.
- **Never create new CSS classes, CSS files, or inline `style={{...}}` hacks.** Style exclusively by composing existing component props/classNames, existing twui classNames (`twui-*`), semantic theme-token utilities (`bg-dark`, `bg-light`, `text-dark`, `text-primary`, `bg-primary`, `text-accent`, `bg-accent`, `text-secondary`, `border-slate-200`, etc.), and utility classes already used elsewhere in this codebase. Build up from what exists; extend component code only when the existing API cannot express the need. - **Never create new CSS classes, CSS files, or inline `style={{...}}` hacks.** Style exclusively by composing existing component props/classNames, existing twui classNames (`twui-*`), semantic theme-token utilities (`bg-dark`, `bg-light`, `text-dark`, `text-primary`, `bg-primary`, `text-accent`, `bg-accent`, `text-secondary`, `border-slate-200`, etc.), and utility classes already used elsewhere in this codebase. Build up from what exists; extend component code only when the existing API cannot express the need.
- **Theming:** colors, radii, and fonts change only in `src/styles/main.css`: Cobble tokens as CSS vars on `:root` (light) and `.dark`, exposed in the `@theme inline` block as `paper`, `surface`, `sunken`, `ink`, `ink-secondary`, `ink-disabled`, `hairline`, `hairline-strong`, `on-ink`, `hue-<name>`, plus `shadow-popover` and `--m-fast/base/slow` motion vars (zeroed under reduced motion). Font is Lato (stand-in). Never hardcode hex colors, radii, or font stacks inside components. Hues are bot identity only, never state; use `src/dict/bot-hue-classes-dict.ts` for hue classes. - **Theming:** colors, radii, and fonts change only in `src/styles/main.css`: Cobble tokens as CSS vars on `:root` (light) and `.dark`, exposed in the `@theme inline` block as `paper`, `surface`, `sunken`, `ink`, `ink-secondary`, `ink-disabled`, `hairline`, `hairline-strong`, `on-ink`, `hue-<name>`, plus `shadow-popover` and `--m-fast/base/slow` motion vars (zeroed under reduced motion). Font is Lato (stand-in). Never hardcode hex colors, radii, or font stacks inside components. Hues are bot identity only, never state; use `src/dict/bot-hue-classes-dict.ts` for hue classes.
- **Branding:** `public/logos/logo.svg` is the single source of truth for the logo mark; `logo-icon.tsx` renders it inline (via `BotStone`) and `logo.tsx` renders the stone beside the vector `Wordmark`. Whenever the logo changes, run `bun run gen:favicon` to regenerate `public/favicon.ico`, `public/favicon.png`, `public/apple-touch-icon.png`, and `public/logos/logo.png` (the last is used in emails). Font + favicon links live in `__root.tsx` `Head()`. - **Branding:** `docs/readme/hero-light.png`, `hero-dark.png`, `workspace-light.png` and `workspace-dark.png` are screenshots of the current app (chat and workspace dashboard, 1440px at 2x, downscaled to 1600px wide), committed and picked per theme by GitHub's `#gh-light-mode-only` / `#gh-dark-mode-only` fragment. They are not generated: when the UI changes, retake them from a seeded dev instance (`bun run seed`, `bun run seed:bots`) and keep the same names, so the README never shows an older dashboard. Never use `assets/handoff/assets/screens/*.png` for anything: that design is outdated. `public/logos/logo.svg` is the single source of truth for the logo mark; `logo-icon.tsx` renders it inline (via `BotStone`) and `logo.tsx` renders the stone beside the vector `Wordmark`. Whenever the logo changes, run `bun run gen:favicon` to regenerate `public/favicon.ico`, `public/favicon.png`, `public/apple-touch-icon.png`, and `public/logos/logo.png` (the last is used in emails). Font + favicon links live in `__root.tsx` `Head()`.
- **Site name:** brand literals live only in `src/data/site-data.ts`. Page meta titles, emails, and UI copy must interpolate `SiteData["SiteName"]` - never hardcode a brand name in page files. - **Site name:** brand literals live only in `src/data/site-data.ts`. Page meta titles, emails, and UI copy must interpolate `SiteData["SiteName"]` - never hardcode a brand name in page files.
## Conventions ## Conventions
+111 -18
View File
@@ -1,21 +1,72 @@
# Cobble <div align="center">
<h1>
<img src="docs/readme/lockup-light.svg#gh-light-mode-only" alt="Cobble" height="56">
<img src="docs/readme/lockup-dark.svg#gh-dark-mode-only" alt="Cobble" height="56">
</h1>
A crew of small AI worker bots you host yourself.
![Cobble: a crew of small AI worker bots you host yourself](docs/readme/hero-light.png#gh-light-mode-only)
![Cobble: a crew of small AI worker bots you host yourself](docs/readme/hero-dark.png#gh-dark-mode-only)
[![CI](https://github.com/Moduletrace/cobble/actions/workflows/ci.yml/badge.svg)](https://github.com/Moduletrace/cobble/actions/workflows/ci.yml) [![CI](https://github.com/Moduletrace/cobble/actions/workflows/ci.yml/badge.svg)](https://github.com/Moduletrace/cobble/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Website](https://img.shields.io/badge/website-cobble.moduletrace.org-0e1116)](https://cobble.moduletrace.org)
[![Bun](https://img.shields.io/badge/runs%20on-Bun-14151a?logo=bun&logoColor=white)](https://bun.sh)
A self-hosted crew of small AI worker bots. [Website](https://cobble.moduletrace.org) · [Install](#install) · [Features](#features) · [Development](#development) · [Architecture](AGENTS.md)
Cobble gives you a coordinator bot and a crew of workers, each with its own </div>
job, models, memory and workspace. The crew talks to you in a chat, shares
workspaces, runs routines on a schedule, and drives its own computers: Cobble is a self-hosted crew of small AI worker bots. You get a coordinator
Docker sandboxes with a browser, terminal, code editor and desktop that a and a set of workers, each with its own job, models, memory and workspace.
bot can use and hand back to you when it needs help. They talk to you in one chat, share workspaces, run routines on a schedule,
and drive their own computers: Docker sandboxes with a browser, a terminal,
a code editor and a desktop that a bot can use and hand back to you when it
needs help.
It is an install, not a SaaS. One SQLite database, one Docker container, and It is an install, not a SaaS. One SQLite database, one Docker container, and
your data on your own machine. your data on your own machine.
- Website: [cobble.moduletrace.org](https://cobble.moduletrace.org) **Why small workers instead of one big assistant**
- Source: [GitHub](https://github.com/Moduletrace/cobble) and [Gitea](https://git.tben.me/Moduletrace/cobble)
- License: MIT - **A crew, not a chat.** Each worker has one job and only loads the context
for it. A five-bot crew does five things in parallel, and each one stays
cheap and focused.
- **Bots you can see through.** The face on each stone is the status: idle,
working, done, asleep, needs you. You read the whole crew at a glance
instead of asking each one what it is doing.
- **Real computers.** Workers run on your own sandboxed machines, with
browser, terminal, files and desktop, not just a tool loop.
- **Yours.** Self-hosted, MIT, one SQLite file to back up, no account and no
per-seat billing.
## Features
| Area | What you get |
| --- | --- |
| Crew | One coordinator and as many workers as you need, each with a name, a role, a hue and its own core instructions |
| Rail | The coordinator, your recent workers and their latest sessions, then your workspaces, each row carrying its status line |
| Chat | Markdown with @-mentions for bots and #-mentions for workspaces, replies and quotes, reactions, attachments, older messages on scroll, drafts kept locally |
| Header | The bot's model, provider and computer at a glance, one click from either, and its picture, instructions and settings on the avatar |
| Workspaces | A shared home for a group of bots: shared context, per-workspace instructions and memory, a default computer, and a dashboard |
| Memory | Each bot keeps core memory, memory about you, and workspace memory, and rewrites it as it works |
| Routines | Cron schedules per bot, written in chat, that queue a task on its own |
| Computers | Docker sandboxes with browser, terminal, code editor and desktop, opened from the chat and handed back to you mid-task |
| Files | Bots keep files on your data volume, search them by meaning, and attach the ones they make to a reply |
| Models | OpenRouter, OpenAI, Anthropic, Gemini, DeepSeek, Groq, Mistral, xAI, Ollama or any OpenAI-compatible server, per bot and per harness |
| Voice | Dictate into the composer, play any bot reply, with speech-to-text and text-to-speech models of your choice |
| Tools | Install your own MCP servers and custom HTTP, script or prompt tools, and give them to bots or workspaces |
| App | Installable PWA with Web Push, dark mode, and a rail built for a phone |
## Screenshots
A workspace dashboard: what it is, its latest activity, the bots in it with
their instructions there, and its computers.
![A workspace dashboard in Cobble](docs/readme/workspace-light.png#gh-light-mode-only)
![A workspace dashboard in Cobble, dark mode](docs/readme/workspace-dark.png#gh-dark-mode-only)
## Install ## Install
@@ -106,6 +157,35 @@ curl -fsSL https://url.moduletrace.com/cobble | sudo bash -s -- --uninstall --pu
| `--uninstall` | Stop and remove the app container | | `--uninstall` | Stop and remove the app container |
| `--purge` | With `--uninstall`, also delete the data directory | | `--purge` | With `--uninstall`, also delete the data directory |
## How it fits together
```
You ── chat ── Coordinator ── delegates ── Workers
│ │
├── Workspaces ────────────┤
│ context, memory, bots │
├── Routines ──────────────┤
│ cron schedules │
└── Computers ─────────────┘
docker sandboxes:
browser, terminal, code, desktop
```
- **Coordinator** is yours alone. It plans, delegates to workers, and keeps
the crew in order. It is the only bot that reaches every worker.
- **Workers** are the crew. Each has a job, its own instructions and memory,
and its own models if you want to tune cost per bot.
- **Workspaces** group bots around a subject. Models, instructions, memory
and the default computer resolve per workspace before per bot.
- **Routines** are cron schedules that queue a task without you asking.
- **Computers** are per-install Docker containers. Bots use them through the
sandbox's tools, and you can open the same machine in the browser and take
over.
Each conversation is split into sessions, so a long-running bot does not grow
one endless thread: the bot names a session, summarizes it, and carries the
summary into the next one.
## Data and settings ## Data and settings
Everything that must survive an update lives under the install directory: Everything that must survive an update lives under the install directory:
@@ -120,6 +200,8 @@ Everything that must survive an update lives under the install directory:
db/.backups/ db/.backups/
media/ media/
computers/<id>/ # each computer's home directory computers/<id>/ # each computer's home directory
bots/<id>/ # each bot's files
workspaces/<id>/ # files shared in a workspace
``` ```
`.env` holds the port and the encryption keys. The keys encrypt sessions and `.env` holds the port and the encryption keys. The keys encrypt sessions and
@@ -170,26 +252,37 @@ the app from a phone, run a proxy or tunnel in front of it (see
context. context.
| Command | Purpose | | Command | Purpose |
| -------------------- | ------------------------------------ | | --------------------- | ------------------------------------ |
| `bun run dev` | Development server | | `bun run dev` | Development server |
| `bun run start` | Production server on the host | | `bun run start` | Production server on the host |
| `bun run typecheck` | Type-check | | `bun run typecheck` | Type-check |
| `bun run test` | Run the test suite | | `bun run test` | Run the test suite |
| `bun run check:bundle`| Bundle budget after a build |
| `bun run seed` | Create the super admin from the CLI | | `bun run seed` | Create the super admin from the CLI |
| `bun run seed:bots` | Add a sample crew | | `bun run seed:bots` | Add a sample crew |
| `bun run db:schema` | Sync the schema and types | | `bun run db:schema` | Sync the schema and types |
| `bun run gen:favicon` | Regenerate favicons and PWA assets | | `bun run gen:favicon` | Regenerate favicons and PWA assets |
The app is Bun, [`@moduletrace/bunext`](https://github.com/Moduletrace/bunext), Plain Node does not work: the app runs on Bun. The stack is Bun,
SQLite via [`@moduletrace/bun-sqlite`](https://github.com/Moduletrace/bun-sqlite), [`@moduletrace/bunext`](https://github.com/Moduletrace/bunext) for
Tailwind CSS and the `@moduletrace/twui` component library. See filesystem routing, SQLite via
[AGENTS.md](AGENTS.md) for the full architecture, routes, queue, harness and [`@moduletrace/bun-sqlite`](https://github.com/Moduletrace/bun-sqlite),
data model. Tailwind CSS and the `@moduletrace/twui` component library. The model layer is
one adapter over OpenAI-compatible APIs, so any compatible provider works.
CI runs type-check and tests on every push and pull request. Releases are See [AGENTS.md](AGENTS.md) for the full architecture, routes, queue, harness
built by GitHub Actions and pushed to both registries. See and data model. CI runs type-check and tests on every push and pull request;
releases are built by GitHub Actions and pushed to both registries. See
[RELEASING.md](RELEASING.md) for the release and launch checklist. [RELEASING.md](RELEASING.md) for the release and launch checklist.
## Contributing
Issues and pull requests are welcome, in English, on either
[GitHub](https://github.com/Moduletrace/cobble) or
[Gitea](https://git.tben.me/Moduletrace/cobble). Run `bun run typecheck` and
`bun run test` before opening a pull request, and read
[AGENTS.md](AGENTS.md) first: it documents the conventions the code follows.
## License ## License
MIT. See [LICENSE](LICENSE). MIT. See [LICENSE](LICENSE).
Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB