@cat-factory/app 0.303.0 → 0.303.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,7 @@ The SPA source lives under `app/` (the Nuxt srcDir).
15
15
  - [What it is](#what-it-is)
16
16
  - [Tech stack](#tech-stack)
17
17
  - [Layout](#layout)
18
+ - [Nuxt UI: agent tooling and overrides](#nuxt-ui-agent-tooling-and-where-the-spa-overrides-the-skill)
18
19
  - [Task swimlanes](#task-swimlanes)
19
20
  - [Roles (engineer / product manager / designer)](#roles-engineer--product-manager--designer)
20
21
  - [Interface modes (basic / advanced)](#interface-modes-basic--advanced)
@@ -41,7 +42,8 @@ over the WebSocket. How that sync works is written up in
41
42
 
42
43
  ## Tech stack
43
44
 
44
- - **Nuxt 4 / Vue 3** SPA: single route (`pages/index.vue`).
45
+ - **Nuxt 4 / Vue 3** SPA: the board is `pages/index.vue`, beside three standalone routes
46
+ (`mcp-authorize.vue`, `mcp-oauth-callback.vue`, `reset-password.vue`).
45
47
  - **Pinia** (+ `pinia-plugin-persistedstate`): feature stores.
46
48
  - **Vue Flow** (`core`, `background`, `controls`, `node-resizer`): the canvas.
47
49
  - **Nuxt UI** + Tailwind: components and styling.
@@ -50,15 +52,15 @@ over the WebSocket. How that sync works is written up in
50
52
 
51
53
  ## Layout
52
54
 
53
- | Path | Contents |
54
- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
55
- | `app.vue` | Root; wraps the page in `AuthGate`. |
56
- | `pages/index.vue` | The only route: mounts the sidebar, canvas, toolbar, inspector, focus view, and all modals. |
57
- | `components/` | UI grouped by area (see [Key UI surfaces](#key-ui-surfaces)). |
58
- | `composables/` | `useApi` (typed client), `useWorkspaceStream` (WebSocket sync), `useBlockDrag`, `useBlockQueries`, `useBoardFlow`, `useSemanticZoom`, `useDepLabels`. |
59
- | `stores/` | Pinia stores, one per feature domain. |
60
- | `types/` | TypeScript domain unions (`domain.ts`) and wire types mirroring the contracts. |
61
- | `utils/` | Small pure helpers. |
55
+ | Path | Contents |
56
+ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
57
+ | `app.vue` | Root; wraps the page in `AuthGate`. |
58
+ | `pages/` | The board (`index.vue`: sidebar, canvas, toolbar, inspector, focus view, all modals), plus the standalone `mcp-authorize`, `mcp-oauth-callback` and `reset-password` routes. |
59
+ | `components/` | UI grouped by area (see [Key UI surfaces](#key-ui-surfaces)). |
60
+ | `composables/` | `useApi` (typed client), `useWorkspaceStream` (WebSocket sync), `useBlockDrag`, `useBlockQueries`, `useBoardFlow`, `useSemanticZoom`, `useDepLabels`. |
61
+ | `stores/` | Pinia stores, one per feature domain. |
62
+ | `types/` | TypeScript domain unions (`domain.ts`) and wire types mirroring the contracts. |
63
+ | `utils/` | Small pure helpers. |
62
64
 
63
65
  ### The board's top overlay region has ONE owner
64
66
 
@@ -304,6 +306,64 @@ A **theme** is a Nuxt UI theme editor document (https://ui.nuxt.com/theme): the
304
306
 
305
307
  **Colour mode** (system / light / dark) is `@nuxtjs/color-mode`'s, registered by Nuxt UI, with its own persistence; `nuxt.config.ts` follows the system preference and falls back to dark. `AppearanceSwitcher` at the sidebar bottom is the one control for both axes. The pre-JS loading shell (`spa-loading-template.html`) cannot resolve a token (no stylesheet is loaded yet), so the plugin caches the computed colours of the four tokens the shell paints with, per colour mode, in `localStorage` (`utils/theme/bootPalette.ts`), and a two-line inline script in the shell applies the entry for the mode the `<head>` colour-mode script has already put on `<html>`. The hardcoded defaults are the default theme's, used only until a theme has been seen in that mode.
306
308
 
309
+ ## Nuxt UI: agent tooling and where the SPA overrides the skill
310
+
311
+ The SPA runs on Nuxt UI 4. Two first-party sources carry the rules this repo does not restate:
312
+
313
+ - **The vendored [`nuxt-ui` skill](https://github.com/kibertoad/cat-factory/tree/main/.claude/skills/nuxt-ui)** teaches WHEN to reach for which
314
+ component (Modal vs Slideover vs Drawer, Tabs vs NavigationMenu, Toast vs Alert) and HOW to build
315
+ well (the variant-weight table, one solid primary per view, semantic utility roles). It is
316
+ vendored under `.claude/skills/nuxt-ui/`, beside the project's own skills, so the rules version
317
+ with the code and every contributor on Claude Code reads the same text. This is the Claude Code
318
+ surface: `.claude/skills/` auto-loads for a local Claude Code session, and the repo `.mcp.json` is
319
+ offered once for approval on the first session in the repo (re-offered when the file changes); a
320
+ decline is remembered, and `claude mcp reset-project-choices` un-sticks it. Another tool that reads
321
+ `AGENTS.md` (Codex, for one) gets the pointers below but not an auto-loaded skill or an auto-wired
322
+ server. Making the platform's OWN coder agents (Claude Code, Codex, Pi in
323
+ the runner image) apply the skill is a separate, backend change: they stage skills and tool
324
+ servers per run through the agent-capabilities system (ADR 0024 /
325
+ [`custom-agents.md`](https://github.com/kibertoad/cat-factory/blob/main/backend/docs/custom-agents.md)), not from these repo files. The `@nuxt/ui` npm
326
+ package does not ship the skill, so it cannot move with `pnpm update`: it is vendored from the
327
+ `v4` branch of `nuxt/ui` (which tracks the 4.x line) and pinned by convention to the Nuxt UI
328
+ major in [`package.json`](./package.json) (`@nuxt/ui@^4.11`). Refresh it by re-vendoring
329
+ `skills/nuxt-ui/` from that branch (or `npx skills add nuxt/ui`) when the major moves. The
330
+ vendored files are kept byte-for-byte as upstream: `.oxfmtrc.json` exempts them from the
331
+ formatter and `scripts/check-doc-links.mjs` skips them, so a refresh stays a clean diff even when
332
+ upstream ships a link this repo's own doc rules would reject.
333
+ - **The [`nuxt-ui` MCP server](https://github.com/kibertoad/cat-factory/blob/main/.mcp.json)** (`https://ui.nuxt.com/mcp`, declared repo-level)
334
+ answers WHAT a component accepts: props, slots, events, theme files and examples per component.
335
+ The skill defers to it for every API question.
336
+
337
+ The SPA's own rules win where they and the skill disagree. Each entry below links the rule it
338
+ protects, so the two never contradict silently:
339
+
340
+ - **Visible copy is an i18n key, never a literal string.** The skill's examples hard-code labels and
341
+ placeholders (`label="Email"`, `placeholder="you@example.com"`); here every user-facing string
342
+ goes through `@nuxtjs/i18n` and lands in the catalog. [Rule](#internationalization-i18n-authoring).
343
+ - **Schema validation goes through valibot, never zod.** The skill's `forms.md` recipe opens with
344
+ `import * as z from 'zod'` and validates through `z.object({...})`; this package depends on
345
+ `valibot` (and `@toad-contracts/valibot`) and has no `zod`, so a `zod` import fails typecheck and
346
+ "fixing" it pulls in a dependency the repo deliberately lacks (which then has to clear the 24h
347
+ `minimumReleaseAge` gate). Validate with valibot, mirroring the wire contracts
348
+ ([`@cat-factory/contracts`](https://github.com/kibertoad/cat-factory/tree/main/backend/packages/contracts)).
349
+ - **A backend-declared form renders through `DescriptorFields.vue`, not a hand-built `UForm`.** The
350
+ skill teaches the hand-rolled `UForm` + `UFormField` shape for every form; when the backend
351
+ declares the fields and the SPA only collects them, that shape is forbidden here.
352
+ [Rule](#a-backend-declared-form-renders-through-descriptorfieldsvue).
353
+ - **Failure toasts go through the funnel, not a bare `toast.add`.** The skill uses `useToast()`
354
+ freely; here a failed backend call is reported with `usePipelineErrorToast().present(error, key)`.
355
+ [Rule](#every-failure-toast-goes-through-one-funnel).
356
+ - **Icon-only buttons carry an accessible name.** Use `common/IconButton.vue`
357
+ ([source](./app/components/common/IconButton.vue)), which applies `label` as both `title` and
358
+ `aria-label`, rather than a bare `<UButton icon>`.
359
+ - **Colour goes through theme tokens, never a fixed palette shade.** The skill bans raw Tailwind
360
+ hues; this repo also bans fixed numbered aliases, enforced by
361
+ [`scripts/check-frontend-palette.mjs`](https://github.com/kibertoad/cat-factory/blob/main/scripts/check-frontend-palette.mjs).
362
+ [Rule](#colour-through-theme-tokens-never-a-fixed-palette-shade).
363
+ - **Confirmation dialogs use the shared `useConfirm`.** The skill shows `useOverlay()` per call
364
+ site; here callers `await useConfirm().confirm({...})`
365
+ ([source](./app/composables/useConfirm.ts)) against one app-mounted `<ConfirmDialog />`.
366
+
307
367
  ## Task swimlanes
308
368
 
309
369
  A service frame lays its tasks out in **status lanes**, not at coordinates. Three lanes a reader
@@ -1357,12 +1417,21 @@ wrong place is invisible until a user cannot find it:
1357
1417
 
1358
1418
  ## Develop & test
1359
1419
 
1420
+ This package has no `dev` or `lint` script of its own: it is a layer, consumed through `extends`,
1421
+ and linting is whole-tree from the repo root (CLAUDE.md). The `AGENTS.md` verify checklist carries
1422
+ the same list, plus the install-free frontend guards.
1423
+
1360
1424
  ```bash
1425
+ # From the repo root:
1361
1426
  pnpm install
1362
- pnpm dev # Nuxt dev server (expects the Worker running; set NUXT_PUBLIC_API_BASE)
1363
- pnpm test # vitest
1364
- pnpm typecheck # nuxt typecheck
1365
- pnpm lint # oxlint + oxfmt --check
1427
+ pnpm dev:frontend # Nuxt dev server via deploy/frontend; set NUXT_PUBLIC_API_BASE to a running Worker
1428
+ pnpm exec turbo run typecheck --filter=@cat-factory/app # typecheck goes through Turbo
1429
+ pnpm lint # oxlint + oxfmt over the whole tree
1430
+
1431
+ # From frontend/app:
1432
+ pnpm exec vitest run <file> # run the spec your change touched
1433
+ pnpm i18n:check # catalog is well-formed
1434
+ pnpm i18n:parity # locales are in parity
1366
1435
  ```
1367
1436
 
1368
1437
  > Building/deploying the static site is covered in the deployment docs: see the
@@ -35,8 +35,10 @@ const back = useIntegrationBack(open)
35
35
  // that trade on somebody's behalf.
36
36
  const RECOMMENDED_SLUGS = [
37
37
  'anthropic/claude-fable-5.1',
38
+ 'anthropic/claude-opus-5.5',
38
39
  'anthropic/claude-opus-5',
39
40
  'openai/gpt-6-astra',
41
+ 'openai/gpt-6-sol',
40
42
  'openai/gpt-5.6-sol',
41
43
  'openai/gpt-5.6-terra',
42
44
  'google/gemini-3.1-pro-preview',
@@ -44,6 +46,7 @@ const RECOMMENDED_SLUGS = [
44
46
  'meta/muse-spark-1.3',
45
47
  'deepseek/deepseek-v4.1-flash',
46
48
  'moonshotai/kimi-k2.7-code',
49
+ 'x-ai/grok-4.7',
47
50
  'z-ai/glm-5.3',
48
51
  'z-ai/glm-5.2',
49
52
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.303.0",
3
+ "version": "0.303.1",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,7 +26,7 @@
26
26
  "@modular-vue/runtime": "^1.4.1",
27
27
  "@modular-vue/vue": "^1.4.1",
28
28
  "@nuxt/fonts": "^0.14.0",
29
- "@nuxt/ui": "^4.11.1",
29
+ "@nuxt/ui": "^4.11.2",
30
30
  "@nuxtjs/i18n": "^10.6.0",
31
31
  "@pinia/nuxt": "^1.0.2",
32
32
  "@toad-contracts/core": "1.0.0",