Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions .agents/skills/add-capability/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
---
name: add-capability
description: Scaffold a Cloudhal capability (manifest + MCP tools + policy scopes + optional Studio panel) under capabilities/. Use when adding an integration or a new set of agent tools.
description: Add a Cloudhal capability (MCP tools + policy scopes + optional REST routes and Studio panel) in packages/mcp-server, packages/agent-runtime and apps/studio. Use when adding an integration or a new set of agent tools.
license: Apache-2.0
metadata:
cloudhal.kind: "repo-skill"
cloudhal.area: "area:agent"
cloudhal.status: "scaffold-only until Sprint 5 lands the capability SDK"
cloudhal.status: "active"
---

# Add a capability

A capability is what an agent *can do*: a manifest, a set of MCP tools, the scopes they need, optional UI. A skill is *how* to do a job and never grants permission. Keep that boundary.
A capability is what an agent *can do*: MCP tools, the scopes they need, optional UI. A skill is *how* to do a job and never grants permission. Keep that boundary.

## Steps (current sprint: structure only; runtime arrives in Sprint 5)
There is no separate capability package or manifest today (`capabilities/` is still planned). A capability is added in the packages below; a significant one gets an RFC first (`write-rfc`).

1. Create `capabilities/official/<name>/` (or `capabilities/examples/<name>/` for samples) with:
- `manifest.json` — name (kebab-case), version, `tools[]` (name, description, `scopes[]`, `annotations` with honest `readOnlyHint`/`destructiveHint`/`idempotentHint`), `secrets[]` (names only), `ui?` (panel entry).
- `tools/<tool>.ts` — one file per tool; input/output JSON Schemas live next to the handler and must use the same generator-safe subset as `contract-change`.
- `README.md` — what it does, scopes, approval points, external services.
2. Scopes follow `area:verb` (`content:write`, `preview:create`, `site:publish`). Anything that publishes, deletes or rolls back is `destructiveHint: true` and must be approvable (the publish flow returns `input_required` for approval; see `docs/adr/0004-mcp-server.md`).
3. Secrets are declared, never read from env directly; the runtime injects tenant-scoped values.
4. UI panels (if any) are React components built only from `@cloudhal/ui` (see `studio-ui`).
5. Add tests: tool handlers are pure functions over a fake storage; include a policy test that a call outside declared scopes is rejected.
6. Open the PR with the `capability` issue template filled in.
## Steps

1. **Tools.** Add a module `packages/mcp-server/src/<area>-tools.ts` exporting `<area>Tools(): ToolDefinition[]` (see `common.ts`), or extend an existing one, and list it in `defineTools()` in `src/tools.ts`. The order there is what agents see; update `test/tool-order.test.ts`. Each tool has JSON Schema input/output (built from the contract schemas where they exist; same generator-safe subset as `contract-change`), honest `annotations` (`readOnlyHint`/`destructiveHint`/`idempotentHint`), its `scopes`, and a handler over `SiteGraphStore` or the package's store. Errors are `ToolError`s with stable codes and the next step in the text.
2. **Scopes.** Reuse an existing scope where it fits. A new one goes in `packages/agent-runtime/src/policy.ts`: `KEY_SCOPES` if a key may carry it, `PERSON_SCOPES` if only people hold it, plus its implications (`IMPLIES`), then in the roles' sets in `src/roles.ts` and, if agents should have it by default, `defaultScopes`. Scope names follow `area:verb` (`content:write`, `site:publish`), with an `:own` variant when it is limited to one's own nodes.
3. **Approval.** Anything that publishes, deletes or rolls back is `destructiveHint: true` or `requiresApproval: true`, so the policy engine returns `approval-required` and the server asks a person (`input_required`; ADR-0014, ADR-0015 for modes).
4. **REST and Studio** (if people need it too). Routes in `packages/api` (shared suite in `src/testing.ts`), the typed client in `apps/studio/src/lib/api/<area>.ts`, and a screen or settings section built only from `@cloudhal/ui` (load `studio-ui` and `kumo-design`). Add the tool's label to Studio's `tools` messages so Activity and Approvals name it.
5. **Secrets** are never committed (AGENTS.md rule 9); they come from the deployment's bindings and environment (`cloudhal deploy`), with test values in `.env.example`.
6. **Tests.** A test file in `packages/mcp-server/test/` that runs the tools through `runTool` (policy → audit → handler) on a fake or sqlite store, including a call outside the declared scopes being denied and an approval-gated call stopping for approval.
7. **Docs.** Run `node scripts/mcp-tools-doc.ts` to regenerate the tool reference in `packages/mcp-server/README.md` (`pnpm check` fails when it is stale). If agents need a playbook for the job, add a skill under `skills/official/`.

## References
- `docs/adr/0005-skills-format.md` — capability vs skill boundary
- `docs/adr/0004-mcp-server.md`
- `docs/adr/0011-agent-layer.md`, `packages/mcp-server/README.md`
- `packages/agent-runtime/README.md` — scopes and roles
2 changes: 1 addition & 1 deletion .agents/skills/studio-ui/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ metadata:
Stack (ADR-0003): Vite 8 + React 19 SPA, TanStack Router + Query, Tailwind 4, **Kumo** (`@cloudflare/kumo` 2.x on `@base-ui/react`, Phosphor icons) consumed only through `@cloudhal/ui`. Cloudhal is a premium, community-facing product: use Kumo's components and blocks, not hand-rolled markup.

## Workflow (every UI change)
1. **Look it up, don't guess:** `pnpm kumo ls` (53 components), `pnpm kumo doc <Name>` (props, sub-components, examples), `pnpm kumo blocks` / `pnpm kumo add <Block>` (copied into `packages/ui/src/blocks/kumo`, owned by us), `pnpm kumo ai` (usage guide). Registry: `packages/ui/node_modules/@cloudflare/kumo/ai/component-registry.json`.
1. **Look it up, don't guess:** `pnpm kumo ls` (every component), `pnpm kumo doc <Name>` (props, sub-components, examples), `pnpm kumo blocks` / `pnpm kumo add <Block>` (copied into `packages/ui/src/blocks/kumo`, owned by us), `pnpm kumo ai` (usage guide). Registry: `packages/ui/node_modules/@cloudflare/kumo/ai/component-registry.json`.
2. **Pick the template** (DESIGN.md §2) and register a new route in `ROUTE_TEMPLATES` (`apps/studio/scripts/design-lint.ts`): `list`, `dashboard`, `settings`, `editor`, `auth`, `shell`. Copy the reference screen: `SitesPage` (list + aside), `PagesPage` (list), `SiteOverviewPage` (dashboard), `LoginPage` (auth).
3. **Compose from blocks:**
- page → `PageLayout` (`crumbs` from `Crumbs`/`SiteCrumbs`, `title`, `description`, `actions` for the primary action, `tabs` only for sub-views, `aside` for usage/help, `width="narrow"` for forms). Never your own container, header, breadcrumbs or columns.
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
# Size ratchet (docs/engineering/code-structure.md): no new oversized files or functions.
- run: pnpm size:check
- run: node scripts/font-fallbacks.ts --check
- run: node scripts/mcp-tools-doc.ts --check
- run: pnpm turbo run typecheck
# Each vitest starts a worker per core; more than two packages at once oversubscribes the
# 4-vCPU runner and slow tests (auth, D1, workerd) hit their timeouts.
Expand Down
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Cloudhal is an agent-native site platform. Sites are typed graphs (Site → Page
## Commands
```bash
pnpm install # once
pnpm check # biome lint + typecheck + vitest (CI equivalent)
pnpm check # biome lint, size check, generated-file checks, typecheck, vitest (CI equivalent)
pnpm lint:fix # auto-format
pnpm size:check # size ratchet (docs/engineering/code-structure.md); size:report prints the tables
cargo test --workspace # Rust fixture tests (needs `pnpm --filter @cloudhal/core-contracts bundle` first; `pnpm gen` does it)
Expand All @@ -30,17 +30,17 @@ pnpm i18n:export / pnpm i18n:import [--locale xx] # translations as nested ICU
- `packages/core-ts` — `SiteGraphStore` (CAS on `rev`, snapshots, diff, publish, rollback). Behavioural suite in `src/testing.ts` runs on sqlite and Miniflare D1. `./local` opens the offline store; `seedStarterSite` seeds a demo. `./surfaces` is RFC-0020's generative UI: the catalog (primitives + block types), eval-free surface checks, bindings, repairs, JSON Patch (`patchPage`), `catalogPrompt`; `./interop` converts A2UI v0.9 and json-render to and from surfaces; `./design` is RFC-0021's DESIGN.md lint and guidance. `search.ts` is RFC-0019's FTS5 site search (index written with the publish batch; articles, pages, terms and authors, RFC-0029); `search-provider.ts` is RFC-0029's `SearchProvider` (`fts5`, `memory`, `withFallback`), set with `SiteGraphStoreOptions.search`; `search-ai.ts` is the experimental Cloudflare AI Search provider (`hybridProvider`, `searchProviderFromEnv`, `cloudhal.json` `search`, docs/guides/search.md).
- `packages/schedules` — RFC-0008: time model (only `temporal-polyfill/implementation`, `now` injected, never `Temporal.Now`), RRULE subset, release model (`rl1:`), automation/run state, taint and budgets, `runDue`; `./sql` is `SqlScheduleStore`, `./engine` the Temporal-free part core-ts and the site Worker use. Worker-safe; conformance suites in `src/testing.ts` on sqlite and Miniflare D1.
- `packages/astro-renderer` — `GraphSource` (live/snapshot), `cloudhal()` integration (virtual config + dev route-cache watcher), `.astro` section components (`Surface.astro` for RFC-0020 surfaces; `./surfaces` renders them, `./kit` has the table of contents and reading time). Routes live in starters, not here.
- `packages/cli` — `cloudhal init|dev|snapshot|build|studio|mcp|deploy|push|pull|search reindex`; TS bin run by Node 24 type stripping. `src/cli.ts` parses flags and dispatches through `COMMANDS`, built from `src/cli/commands-{local,remote,content,ops}.ts` (a new command is one handler plus one line in `src/cli/help.ts`). E2E test spawns the bin (Astro disables dev middleware under `VITEST`). Cloudflare tests run both Workers on workerd via wrangler's `createTestHarness`; test files run serially (shared starter caches).
- `packages/cli` — the `cloudhal` CLI (`init`, `dev`, `studio`, `mcp`, `deploy`, `push`, `pull`, …; every command is in `src/cli/help.ts`, which `cloudhal --help` prints and the docs site's CLI reference is generated from); TS bin run by Node 24 type stripping. `src/cli.ts` parses flags and dispatches through `COMMANDS`, built from `src/cli/commands-{local,remote,content,ops,site}.ts` (a new command is one handler plus one line in `src/cli/help.ts`). E2E test spawns the bin (Astro disables dev middleware under `VITEST`). Cloudflare tests run both Workers on workerd via wrangler's `createTestHarness`; test files run serially (shared starter caches).
- `packages/cloudflare` — the admin Worker (`createAdminWorker`); `./playground` is RFC-0036's playground Worker (`createPlaygroundWorker`, the `PlaygroundSite` Durable Object, signed session cookies). Generated entries must import `@cloudhal/cloudflare/workerd-prelude` first (ADR-0012).
- `packages/agent-runtime` / `packages/mcp-server` — policy, skills, audit; MCP tools and transports (ADR-0011). `@cloudhal/mcp-server/render` is RFC-0021's `render_page` core: a small DevTools client, the in-page checks and limits; the CLI drives local Chrome with it (`render-chrome.ts`), the admin Worker Browser Rendering (`browser-render.ts`).
- `starters/blog` — the theme boundary: `src/pages/[...path].astro`, `src/layouts`, `src/theme.ts`.
- `packages/api` — Hono app (`createApi`), Better Auth (`src/auth.ts`), error mapping; shared suite `src/testing.ts` on sqlite + D1. Build chat is `build-turn.ts`, its model eval `build-eval.ts`; `design-pass.ts` is RFC-0021 21c (Build chat looks at its render, the eval's vision-judged score).
- `packages/ui` — `export * from "@cloudflare/kumo"` + blocks; `styles.css` is the only Tailwind entry for admin UIs. Heavy kits have lazy subpaths: `./charts` (ECharts), `./editor` (the Tiptap markdown body editor, RFC-0014).
- `packages/messages` — RFC-0032 / ADR-0032: the ICU MessageFormat 1 subset of Studio's catalogs (`plural`, `select`, `selectordinal`, `number`). `./compile` (build-time only: the Vite plugin `./vite`, the type generator, tests; `bundle-budget.ts` keeps it out of every chunk) and `./format` (Worker-safe runtime, cached `Intl.PluralRules`/`NumberFormat`), which is also Studio's `Intl` layer: `formatNumber`, `formatDate`, `formatRelative`, `formatList` and cached `numberFormat`/`dateTimeFormat`/`relativeTimeFormat`/`displayNames`/`collator` — screens and blocks never build `Intl` formatters themselves (lint `i18n-format`). Catalogs compile to data, never code.
- `patches/` — `pnpm patch` files (`patchedDependencies` in `pnpm-workspace.yaml`). `@[email protected]` is **generated** by `node scripts/kumo-patch.ts`: the hand-written changes in `patches/kumo/*.patch` (`tooltip-timestamp.patch` adds `TimeseriesChart`'s `tooltipTimestampFormat`, DESIGN.md §2v "Kumo locale") plus every physical utility in Kumo's `dist` made logical (ADR-0033 A1). Never edit it; on a Kumo upgrade rename it and the `patchedDependencies` entry, re-run the script, `pnpm install`, re-check the gaps in `docs/design/kumo-upstream-notes.md`. `packages/ui/test/kumo-rtl.test.tsx` fails when the installed Kumo is not patched.
- `apps/studio` — SPA; routes in `src/router.tsx`, screens in `src/routes`, typed API client `src/lib/api.ts` (a barrel over `src/lib/api/<area>.ts`), i18n in `src/i18n` (messages are ICU MF1, compiled at build time, with parameter types generated into `src/i18n/generated/params.ts` by `pnpm gen` — never edit it; languages registered in `locales.ts`; add keys to `en.ts` and every `<locale>.ts`, or to a lazy namespace `src/i18n/lazy/<ns>.<locale>.ts` — `analytics`, `analyticsSettings`, `appearance`, `bodyEditor`, `media`, `menus`, `build`, `readers`, `comments`, `commentsSettings`; DESIGN.md §1; only the active language of the core catalogs loads, `tr.ts` through `loadLocale`, and each other language's lazy-namespace loaders come with it from the generated `src/i18n/loaders/<locale>.ts`; Arabic and Persian format with Western digits through the registry's `intl` tag).
- `apps/studio` — SPA; routes in `src/router.tsx`, screens in `src/routes`, typed API client `src/lib/api.ts` (a barrel over `src/lib/api/<area>.ts`), i18n in `src/i18n` (messages are ICU MF1, compiled at build time, with parameter types generated into `src/i18n/generated/params.ts` by `pnpm gen` — never edit it; languages registered in `locales.ts`; add keys to `en.ts` and every `<locale>.ts`, or to a lazy namespace `src/i18n/lazy/<ns>.<locale>.ts` (the list and how to add one: `src/i18n/namespaces.ts`; DESIGN.md §1); only the active language of the core catalogs loads, `tr.ts` through `loadLocale`, and each other language's lazy-namespace loaders come with it from the generated `src/i18n/loaders/<locale>.ts`; Arabic and Persian format with Western digits through the registry's `intl` tag).
- `docs/adr/` decisions (with sources) · `docs/rfcs/` proposals · `docs/design/DESIGN.md` the binding admin design system (Kumo).
- `.agents/skills/` — skills for you: `contract-change`, `add-capability`, `write-rfc`, `studio-ui`, and Cloudflare's official **`kumo-design`** (installed from `cloudflare/kumo`, tracked in `skills-lock.json`; update with `npx skills update`). Any admin-UI change loads `kumo-design` + `studio-ui` and follows `docs/design/DESIGN.md`.
- `.agents/skills/` — skills for you: `contract-change`, `add-capability`, `write-rfc`, `studio-ui`, `translate`, and Cloudflare's official **`kumo-design`** (installed from `cloudflare/kumo`, tracked in `skills-lock.json`; update with `npx skills update`). Any admin-UI change loads `kumo-design` + `studio-ui` and follows `docs/design/DESIGN.md`.

## Hard rules
1. **Never edit generated files.** Change the schema, run `pnpm gen`, commit both.
Expand All @@ -57,7 +57,7 @@ pnpm i18n:export / pnpm i18n:import [--locale xx] # translations as nested ICU
12. **Size ratchet** ([docs/engineering/code-structure.md](docs/engineering/code-structure.md)): files ≤ 800 lines (soft 400; `.astro` 500, tests 1500; i18n dictionaries are data and exempt — the Studio bundle budget guards them), functions ≤ 150 lines, depth ≤ 5, ≤ 4 positional params (use an options object beyond 3); one responsibility per module, dispatch tables instead of giant switches. `pnpm size:check` (in `pnpm check`) fails on a new violation or when a file in `scripts/size-baseline.json` grows — put new code in a new module next to it; after shrinking one, run `node scripts/size-check.ts --update`.

## Versions (pinned via pnpm catalog / Cargo workspace)
Node 24 · pnpm 12 · TypeScript 7.0 · Biome 2.5 · Vitest 5 · Turborepo 2.11 · Rust 2024 edition · json-schema-to-typescript 16.0.0 · cargo-typify 0.8.0 · ajv 8.20 · jsonschema 0.58 · better-sqlite3 13 · miniflare 4.20260730 (last stable 4.x; see ADR-0012) · wrangler 4.148 · @astrojs/cloudflare 14.3 · Astro 7.3 · Tailwind 4.3 · marked 18 · Hono 4.13 · Better Auth 1.7.7 · Kumo 2.14 · React 19.3 · TanStack Router 1.170 / Query 5.104 · Vite 8.3.
Node 24 · pnpm 12 · TypeScript 7.0 · Biome 2.5 · Vitest 5 · Turborepo 2.11 · Rust 2024 edition · json-schema-to-typescript 16.0.0 · cargo-typify 0.8.0 · ajv 8.20 · jsonschema 0.59 · better-sqlite3 13 · miniflare 4.20260730 (last stable 4.x; see ADR-0012) · wrangler 4.148 · @astrojs/cloudflare 14.3 · Astro 7.3 · Tailwind 4.3 · marked 18 · Hono 4.13 · Better Auth 1.7.7 · Kumo 2.14 · React 19.3 · TanStack Router 1.170 / Query 5.104 · Vite 8.3.

## When unsure
Read `docs/adr/README.md` and the relevant ADR; each ADR links its primary sources. If the change alters a contract or a public interface, write an RFC first (`.agents/skills/write-rfc`).
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Thanks for considering a contribution. The goal of this document is that you can

```bash
pnpm install
pnpm check # biome lint, size check, tsc, vitest
pnpm check # biome lint, size check, generated-file checks, typecheck, vitest
cargo test --workspace # Rust fixture tests
```

Expand Down
Loading
Loading