diff --git a/README.md b/README.md index 712f2c1..d420f52 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,60 @@ # cate-extensions -Extensions for [Cate](https://github.com/0-AI-UG/cate). Each folder under -`extensions/` is one extension, published to the catalog Cate ships by default: - -``` -https://github.com/0-AI-UG/cate-extensions/releases/download/catalog/index.json -``` +Extensions for [Cate](https://github.com/0-AI-UG/cate). One folder per extension +under `extensions/`, published to the catalog Cate ships by default. ## Extensions -| Id | Shape | What it is | -| --- | --- | --- | -| `cate.aisession` | frontend-only | Read-only chat viewer for AI coding-agent session files: drop a session `.jsonl` onto the panel. | -| `cate.excalidraw` | frontend-only | The Excalidraw whiteboard as a canvas panel, themed and autosaved per panel. | -| `cate.mcp` | server-backed | MCP server manager: configure and supervise servers in `.cate/mcp.json`, explore tools, one aggregated `/mcp` endpoint. | -| `cate.mermaid` | frontend-only | Split-pane Mermaid editor with live render, per-panel autosave, SVG/PNG export. | -| `cate.sqlite` | server-backed | Read-only SQLite browser for workspace databases (bundled WASM engine, nothing installed or spawned). | -| `cate.usage` | server-backed | Agent usage and cost dashboard powered by ccusage. | - -Nine more are url-mode: the manifest is a single `url` and Cate loads that web -app in the panel, signed in with the user's own session. No build, no server, no -`cate.*` bridge. - -| Id | Loads | -| --- | --- | -| `cate.confluence` | https://home.atlassian.com/ | -| `cate.discord` | https://discord.com/app | -| `cate.dynamics365` | https://www.office.com/apps | -| `cate.hubspot` | https://app.hubspot.com/ | -| `cate.jira` | https://home.atlassian.com/ | -| `cate.pipedrive` | https://app.pipedrive.com/ | -| `cate.salesforce` | https://login.salesforce.com/ | -| `cate.slack` | https://app.slack.com/client | -| `cate.zohocrm` | https://crm.zoho.com/crm/ShowHomePage.do | +- **AI** — `cate.aisession` (session file viewer), `cate.mcp` (MCP server + manager), `cate.usage` (agent usage and cost) +- **Data** — `cate.sqlite` (read-only SQLite browser) +- **Design** — `cate.excalidraw` (whiteboard), `cate.mermaid` (diagram editor) +- **Productivity** — `cate.confluence`, `cate.jira` +- **Communication** — `cate.discord`, `cate.slack` +- **Sales & CRM** — `cate.dynamics365`, `cate.hubspot`, `cate.pipedrive`, + `cate.salesforce`, `cate.zohocrm` +- **Development** — `cate.frontendkit`, `cate.kitchensink` (reference apps, + `"dev": true`, sideload only) -Two more folders are reference implementations, not products. They carry -`"dev": true` in their manifest, so `gen-catalog.mjs` leaves them out of the -published catalog and you only get them by sideloading: +The CRM, chat and docs ones are url-mode: the manifest is a single `url`, so +there is no build, no server and no `cate.*` bridge. -| Id | Shape | What it is | -| --- | --- | --- | -| `cate.frontendkit` | frontend-only | Smallest useful extension: static assets, no server, no build step. | -| `cate.kitchensink` | server-backed | Exercises the host API: most `cate.*` scopes, a Node server, WebSockets. | +## Layout -## Repo layout +- `extensions//` — one extension +- `kit/` — shared UI kit: tokens, theme bridge, host typings, ServiceConnection, + proxy api-client, server scaffolding (see `kit/README.md`) +- `scripts/sync-kit.mjs` — copies `kit/` into consumers' `src/_kit/` +- `scripts/gen-catalog.mjs` — builds `dist/catalog/index.json` +- `build.sh` — sync-kit, npm builds, tars artifacts, generates the catalog -``` -cate-extensions/ - extensions// # one folder per extension - kit/ # shared UI kit: tokens, theme bridge, host typings, - # ServiceConnection, proxy api-client, server - # HTTP scaffolding (see kit/README.md) - scripts/sync-kit.mjs # copies kit/ into consumers' src/_kit/ (+ src/_kitserver/) - scripts/gen-catalog.mjs # builds dist/catalog/index.json - build.sh # sync-kit, npm builds, tars extensions, then gen-catalog - .github/workflows/publish.yml - dist/ # build output (gitignored) -``` +## Development -## Local development - -`./build.sh` writes `dist/catalog/index.json` with `file://` artifact URLs; -add its absolute path as a catalog source in Cate. Local entries re-provision -on panel open, so edits land without version bumps. - -Faster for a single extension: sideload its folder via Settings, Extensions, -"Add local folder...". +- `./build.sh`, then add the absolute path of `dist/catalog/index.json` as a + catalog source in Cate. Local entries re-provision on panel open. +- Single extension: sideload its folder via Settings, Extensions, "Add local + folder...". ## Contributing -Authoring (manifest, scopes, `window.cate` API, server contract) is documented -in the Cate repo: -[`docs/extensions.md`](https://github.com/0-AI-UG/cate/blob/main/docs/extensions.md) -and +Authoring (manifest, scopes, `window.cate` API, server contract) lives in the +Cate repo: [`docs/extensions.md`](https://github.com/0-AI-UG/cate/blob/main/docs/extensions.md), [`skills/cate-extension/SKILL.md`](https://github.com/0-AI-UG/cate/blob/main/skills/cate-extension/SKILL.md). -In this repo: - -- `extensions//` with a `manifest.json`; the README's first line is the - catalog description fallback. -- With a `build` script in `package.json`, `build.sh` compiles it and the - artifact ships only `manifest.json` + `dist/`; otherwise the folder ships - as-is. -- Kit consumers: add the id in `scripts/sync-kit.mjs`, run it, commit the - synced `src/_kit/` (never edit it directly). `--check` reports stale copies - without writing. -- A reference or work-in-progress extension that should not reach users gets - `"dev": true`; the catalog skips it and it stays sideload-only. +Here: + +- `extensions//` with `manifest.json`; a README's first line is the catalog + description fallback. +- `category` in the manifest: `ai`, `development`, `data`, `design`, + `productivity`, `communication`, `sales`, `other`. Pick by what it is for, not + how it is built. The list stays short; use `other` rather than adding one. +- With a `build` script, the artifact ships `manifest.json` + `dist/`; + otherwise the folder as-is. +- Kit consumers: add the id in `scripts/sync-kit.mjs`, run it, commit + `src/_kit/` (never edit it directly). `--check` reports stale copies. +- Not meant for users yet: `"dev": true` keeps it out of the catalog. - `./build.sh` must pass; bump `version` for every published change. ## Publishing -PR CI validates `./build.sh`; merge to `main` rebuilds against the rolling -`catalog` release and uploads `index.json` plus the artifact tarballs as its -assets. +PR CI runs `./build.sh`; merging to `main` rebuilds and uploads `index.json` +plus the artifact tarballs to the rolling `catalog` release. diff --git a/extensions/cate.aisession/manifest.json b/extensions/cate.aisession/manifest.json index 6a09f30..eff0940 100644 --- a/extensions/cate.aisession/manifest.json +++ b/extensions/cate.aisession/manifest.json @@ -2,6 +2,7 @@ "id": "cate.aisession", "name": "AI Session Viewer", "version": "1.1.0", + "category": "ai", "frontend": "dist/index.html", "description": "Drop a Claude Code, Codex, or pi session file to read it back as a chat.", "panels": [ diff --git a/extensions/cate.confluence/manifest.json b/extensions/cate.confluence/manifest.json index 7a23208..7da306d 100644 --- a/extensions/cate.confluence/manifest.json +++ b/extensions/cate.confluence/manifest.json @@ -2,6 +2,7 @@ "id": "cate.confluence", "name": "Confluence", "version": "1.0.0", + "category": "productivity", "description": "Confluence as a canvas panel. Points a panel at Atlassian Home in the extension's own persistent session, so you stay signed in across restarts and can keep your team's docs next to your code.", "url": "https://home.atlassian.com/", "panels": [ diff --git a/extensions/cate.discord/manifest.json b/extensions/cate.discord/manifest.json index f316052..70d8043 100644 --- a/extensions/cate.discord/manifest.json +++ b/extensions/cate.discord/manifest.json @@ -2,6 +2,7 @@ "id": "cate.discord", "name": "Discord", "version": "1.0.0", + "category": "communication", "description": "Discord as a canvas panel. Points a panel at the Discord web app in the extension's own persistent session, so you stay signed in across restarts and can keep a chat next to your code.", "url": "https://discord.com/app", "panels": [ diff --git a/extensions/cate.dynamics365/manifest.json b/extensions/cate.dynamics365/manifest.json index 9775f15..b2280ba 100644 --- a/extensions/cate.dynamics365/manifest.json +++ b/extensions/cate.dynamics365/manifest.json @@ -2,6 +2,7 @@ "id": "cate.dynamics365", "name": "Microsoft Dynamics 365", "version": "1.0.0", + "category": "sales", "description": "Dynamics 365 as a canvas panel. Points a panel at the Microsoft 365 app launcher in the extension's own persistent session, so you stay signed in across restarts and can keep your CRM next to your code.", "url": "https://www.office.com/apps", "panels": [ diff --git a/extensions/cate.excalidraw/manifest.json b/extensions/cate.excalidraw/manifest.json index a6c8434..b4cd6ee 100644 --- a/extensions/cate.excalidraw/manifest.json +++ b/extensions/cate.excalidraw/manifest.json @@ -2,6 +2,7 @@ "id": "cate.excalidraw", "name": "Excalidraw", "version": "1.0.1", + "category": "design", "description": "The Excalidraw whiteboard, running as a panel on the Cate canvas. Drawings autosave per panel.", "frontend": "dist/index.html", "panels": [ diff --git a/extensions/cate.frontendkit/manifest.json b/extensions/cate.frontendkit/manifest.json index d1d613d..c63dfd7 100644 --- a/extensions/cate.frontendkit/manifest.json +++ b/extensions/cate.frontendkit/manifest.json @@ -2,6 +2,7 @@ "id": "cate.frontendkit", "name": "Frontend Kit", "version": "1.0.0", + "category": "development", "dev": true, "frontend": "index.html", "panels": [ diff --git a/extensions/cate.hubspot/manifest.json b/extensions/cate.hubspot/manifest.json index 8850883..c272101 100644 --- a/extensions/cate.hubspot/manifest.json +++ b/extensions/cate.hubspot/manifest.json @@ -2,6 +2,7 @@ "id": "cate.hubspot", "name": "HubSpot", "version": "1.0.0", + "category": "sales", "description": "HubSpot as a canvas panel. Points a panel at the HubSpot web app in the extension's own persistent session, so you stay signed in across restarts and can keep your pipeline next to your code.", "url": "https://app.hubspot.com/", "panels": [ diff --git a/extensions/cate.jira/manifest.json b/extensions/cate.jira/manifest.json index c545732..4c71acd 100644 --- a/extensions/cate.jira/manifest.json +++ b/extensions/cate.jira/manifest.json @@ -2,6 +2,7 @@ "id": "cate.jira", "name": "Jira", "version": "1.0.0", + "category": "productivity", "description": "Jira as a canvas panel. Points a panel at Atlassian Home in the extension's own persistent session, so you stay signed in across restarts and can keep your board next to your code.", "url": "https://home.atlassian.com/", "panels": [ diff --git a/extensions/cate.kitchensink/manifest.json b/extensions/cate.kitchensink/manifest.json index 3456376..ea2f319 100644 --- a/extensions/cate.kitchensink/manifest.json +++ b/extensions/cate.kitchensink/manifest.json @@ -2,6 +2,7 @@ "id": "cate.kitchensink", "name": "Kitchen Sink", "version": "1.0.0", + "category": "development", "dev": true, "panels": [{ "id": "main", "label": "Kitchen Sink" }], "server": { diff --git a/extensions/cate.mcp/manifest.json b/extensions/cate.mcp/manifest.json index b11dce2..ae88854 100644 --- a/extensions/cate.mcp/manifest.json +++ b/extensions/cate.mcp/manifest.json @@ -2,6 +2,7 @@ "id": "cate.mcp", "name": "MCP Servers", "version": "1.3.1", + "category": "ai", "description": "Native MCP server manager. Configure stdio and remote MCP servers in .cate/mcp.json, supervise them with health checks and auto-restart, browse their tools/resources/prompts, invoke tools from a playground, discover servers in the official MCP registry, and expose everything through one aggregated MCP endpoint any client can connect to.", "panels": [ { diff --git a/extensions/cate.mermaid/manifest.json b/extensions/cate.mermaid/manifest.json index 265bb69..1b23537 100644 --- a/extensions/cate.mermaid/manifest.json +++ b/extensions/cate.mermaid/manifest.json @@ -2,6 +2,7 @@ "id": "cate.mermaid", "name": "Mermaid", "version": "1.1.0", + "category": "design", "description": "A split-pane Mermaid diagram editor: text source on the left, live-rendered diagram on the right, resizable divider between. Theme picker, autosaves per panel, exports SVG and PNG.", "frontend": "dist/index.html", "panels": [ diff --git a/extensions/cate.pipedrive/manifest.json b/extensions/cate.pipedrive/manifest.json index bf9926f..e1bfb5c 100644 --- a/extensions/cate.pipedrive/manifest.json +++ b/extensions/cate.pipedrive/manifest.json @@ -2,6 +2,7 @@ "id": "cate.pipedrive", "name": "Pipedrive", "version": "1.0.0", + "category": "sales", "description": "Pipedrive as a canvas panel. Points a panel at the Pipedrive web app in the extension's own persistent session, so you stay signed in across restarts and can keep your deal pipeline next to your code.", "url": "https://app.pipedrive.com/", "panels": [ diff --git a/extensions/cate.salesforce/manifest.json b/extensions/cate.salesforce/manifest.json index 25e83b5..d26150b 100644 --- a/extensions/cate.salesforce/manifest.json +++ b/extensions/cate.salesforce/manifest.json @@ -2,6 +2,7 @@ "id": "cate.salesforce", "name": "Salesforce", "version": "1.0.0", + "category": "sales", "description": "Salesforce as a canvas panel. Points a panel at the Salesforce login gateway in the extension's own persistent session, so you stay signed in across restarts and can keep your org next to your code.", "url": "https://login.salesforce.com/", "panels": [ diff --git a/extensions/cate.slack/manifest.json b/extensions/cate.slack/manifest.json index 46b1a8d..fd16757 100644 --- a/extensions/cate.slack/manifest.json +++ b/extensions/cate.slack/manifest.json @@ -2,6 +2,7 @@ "id": "cate.slack", "name": "Slack", "version": "1.0.0", + "category": "communication", "description": "Slack as a canvas panel. Points a panel at the Slack web client in the extension's own persistent session, so you stay signed in across restarts and can keep a channel next to your code.", "url": "https://app.slack.com/client", "panels": [ diff --git a/extensions/cate.sqlite/manifest.json b/extensions/cate.sqlite/manifest.json index c929f84..57661dc 100644 --- a/extensions/cate.sqlite/manifest.json +++ b/extensions/cate.sqlite/manifest.json @@ -2,6 +2,7 @@ "id": "cate.sqlite", "name": "SQLite", "version": "1.1.0", + "category": "data", "description": "Browse and query the SQLite databases in your workspace, right on the Cate canvas. Self-contained: reads .db / .sqlite / .sqlite3 files with a bundled WASM engine, so nothing is installed or spawned. Lists tables and views, shows sortable paginated rows, and runs read-only SQL.", "panels": [ { diff --git a/extensions/cate.usage/manifest.json b/extensions/cate.usage/manifest.json index d32f7d6..d7f8192 100644 --- a/extensions/cate.usage/manifest.json +++ b/extensions/cate.usage/manifest.json @@ -2,6 +2,7 @@ "id": "cate.usage", "name": "Agent Usage", "version": "1.1.0", + "category": "ai", "description": "Agent usage and cost dashboard powered by ccusage. Reads the local Claude Code data (~/.claude) on the machine running the extension server and shows cost and tokens for today, this week, this month, and everything still on disk, a daily cost/token timeline, and a per-model cost split. No data leaves the machine.", "panels": [ { diff --git a/extensions/cate.zohocrm/manifest.json b/extensions/cate.zohocrm/manifest.json index 92e65f5..74873c9 100644 --- a/extensions/cate.zohocrm/manifest.json +++ b/extensions/cate.zohocrm/manifest.json @@ -2,6 +2,7 @@ "id": "cate.zohocrm", "name": "Zoho CRM", "version": "1.0.0", + "category": "sales", "description": "Zoho CRM as a canvas panel. Points a panel at the Zoho CRM home in the extension's own persistent session, so you stay signed in across restarts and can keep your accounts next to your code.", "url": "https://crm.zoho.com/crm/ShowHomePage.do", "panels": [ diff --git a/scripts/gen-catalog.mjs b/scripts/gen-catalog.mjs index d2ce37e..caff0ad 100755 --- a/scripts/gen-catalog.mjs +++ b/scripts/gen-catalog.mjs @@ -4,7 +4,8 @@ // // Dependency-free. Scans extensions//manifest.json, and for each extension // reads the already-built artifact at dist/artifacts/-.tgz, -// computes its sha256, and emits a catalog entry. +// computes its sha256, and emits a catalog entry. A manifest whose `category` +// isn't one Cate knows is a hard error (see CATEGORIES). // // artifactUrl: // - ${CATALOG_BASE_URL}/-.tgz when CATALOG_BASE_URL is set @@ -32,6 +33,20 @@ const CATALOG_DIR = join(DIST_DIR, "catalog"); const baseUrl = (process.env.CATALOG_BASE_URL || "").replace(/\/+$/, ""); +// Functional categories Cate's catalog UI filters by — must stay in sync with +// EXTENSION_CATEGORIES in cate/src/shared/extensions.ts. Cate silently files an +// unknown category under "Other", so catch typos here instead. +const CATEGORIES = [ + "ai", + "development", + "data", + "design", + "productivity", + "communication", + "sales", + "other", +]; + // First line of a README, or undefined. function readmeFirstLine(dir) { const p = join(dir, "README.md"); @@ -71,6 +86,14 @@ for (const entry of entries) { continue; } + if (manifest.category === undefined) { + console.warn(`${id}: no category — it will show up under "Other" in the catalog`); + } else if (!CATEGORIES.includes(manifest.category)) { + throw new Error( + `${id}: unknown category "${manifest.category}" (expected one of ${CATEGORIES.join(", ")})`, + ); + } + const artifactName = `${id}-${version}.tgz`; const artifactPath = join(ARTIFACT_DIR, artifactName); if (!existsSync(artifactPath) || !statSync(artifactPath).isFile()) {