@montytools/cli 0.5.4 → 0.5.6

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.
@@ -1,126 +1,113 @@
1
1
  ---
2
2
  name: monty-build
3
- description: Build, run, and save Monty workspace apps. Use whenever the task involves a Monty app, monty.config.ts, the monty CLI (create/dev/logs/save/add), the @montytools/sdk, or a prompt mentioning usemonty.dev. Covers folder discipline, the build loop, data/auth rules, and error handling.
3
+ description: Build, run, and save Monty workspace apps. Use whenever the task involves a Monty app, its schema (monty schema / src/monty.gen.ts), the monty CLI (create/connect/dev/logs/save), the @montytools/sdk, or a prompt mentioning usemonty.dev. Covers folder discipline, the build loop, data/auth rules, and error handling.
4
4
  ---
5
5
 
6
6
  # Building Monty apps
7
7
 
8
- Monty is a work OS: teams get internal apps built by coding agents. You write
9
- product logic only — data, auth, tenancy, deployment, and embedding are the
10
- platform's job. The complete contract lives in the app's own `AGENTS.md`
11
- (nearest-file-wins read it before writing code). This skill is the map, not
12
- the territory.
8
+ Monty is a work OS. Teams get internal apps built by coding agents. You write
9
+ product logic only. Data, auth, tenancy, deployment, and embedding are the
10
+ platform's job. The full contract is in the app's own `AGENTS.md`
11
+ (nearest file wins). Read it before writing code.
13
12
 
14
- There is ONE copy of every app: **Live**, the cloud copy the team uses
15
- (`monty save` updates it). While a dev **session** runs (a dev shell on
16
- the app's LIVE records, often already started for you by the Monty desktop),
17
- workspace admins see the session's version automatically no publish, no
18
- channel switch. "dev"/"prod" mean platform environments, never app states.
13
+ Every app has ONE copy: **Live**, the cloud copy the team uses. `monty save`
14
+ updates it. While a dev **session** runs (often already started for you by
15
+ the Monty desktop), workspace admins see the session's version automatically.
16
+ There is no publish step. "dev" and "prod" name platform environments, never
17
+ app states.
19
18
 
20
19
  ## Rules
21
20
 
22
- 1. **Folders are managed.** Apps live in `~/.monty/apps/<id>` (a server-issued
23
- id, minted when `monty create` registers the app so create needs
24
- `monty login` first). `monty current` tells you where you are;
25
- `cd "$(monty select <slug>)"` jumps to an app; `monty apps` lists local
26
- ones. Never mkdir app folders by hand, and never edit the `id:` line in
27
- `monty.config.ts`. The folder IS the app's source: every file in it
28
- rides the source snapshot on `monty save`, so never leave scratch files
29
- here (manifest edits, notes, one-off scripts). Work in the OS temp dir
30
- instead, or pipe `monty schema | <edit> | monty schema set -` needs
31
- no file at all and delete anything temporary before saving.
32
- 2. **The loop:** `monty create <slug> --name "Name" --icon <tabler-icon>` →
33
- (if the prompt includes a `build id`, pass it: `--build <id>` — the
34
- workspace's New app screen tracks your progress live)
35
- `monty install` shape the schema through `monty schema` /
36
- `monty schema set` (a brand-new app's very first session lands its
37
- monty.config.ts once; after that the workspace owns the schema) + edit
38
- `src/routes/`
39
- verify in the session: run `monty dev` once — if the dev shell is already
40
- running (the Monty desktop usually runs it for you) it prints the status,
41
- app URL, and recent log lines, then **exits immediately**; if nothing
42
- is running it starts the shell (start it in the background and move on).
43
- Then iterate: edit code → vite hot-reloads → `monty logs -n 50` shows
44
- whether it compiled and any browser errors. Re-running `monty dev` is
45
- always safe — it attaches, prints status, and exits. Never try to run a
46
- second dev *server* for the same app (attach handles this for you) and
47
- never kill a dev shell you didn't start; `monty dev --takeover` is the
48
- only sanctioned restart when a session is wedged. **You are not done
49
- until you've saved: once the work is verified in the session, run
50
- `monty save "<what changed>"`** — it builds, typechecks, and pushes the
51
- working copy to the cloud copy, like `git push main`. Save after every
52
- meaningful change, not just at the end; unsaved work exists only on this
21
+ 1. **Respect the folder.** `monty create` scaffolds new apps;
22
+ `monty connect <slug> [dir]` puts a copy of an existing app in any
23
+ folder you choose. Never mkdir an app folder by hand and never edit
24
+ `.monty/app.json` (the identity stamp). `monty current` says which app folder
25
+ you are in. Every file in the folder rides the source snapshot on
26
+ `monty save`, so keep scratch files out. Use the OS temp dir, or pipe
27
+ (`monty schema | <edit> | monty schema set -` needs no file), and delete
28
+ anything temporary before saving.
29
+ 2. **The loop.** `monty create <slug> --name "Name" --icon <tabler-icon>`
30
+ (pass `--build <id>` when the prompt includes one), then `monty install`.
31
+ Shape the schema with `monty schema` and `monty schema set`; edit
32
+ `src/routes/`. Run `monty dev` once. If a shell is already running it
33
+ prints status and exits; otherwise start it in the background. Then
34
+ iterate: edit, vite hot-reloads, `monty logs -n 50` shows compile and
35
+ browser errors. Re-running `monty dev` is always safe. Never start a
36
+ second dev server for the same app and never kill a shell you didn't
37
+ start; `monty dev --takeover` is the one sanctioned restart.
38
+ **You are not done until you've saved.** `monty save "<what changed>"`
39
+ builds, typechecks, and pushes to the cloud copy, like `git push main`.
40
+ Save after every meaningful change. Unsaved work exists only on this
53
41
  machine.
54
42
  3. **Everything through the CLI.** `monty install`, `monty build`,
55
- `monty typecheck`, `monty dev`, `monty save` never run vite, tsc,
56
- pnpm, or npm scripts directly. `monty dev` auto-picks a free port and
57
- prints it; `monty typecheck` builds first when needed. `monty logs`
58
- (add `-f` to follow) is how you read the dev shell's output vite build
59
- errors, browser errors, and save results all land there.
60
- 4. **One import surface:** `@montytools/sdk` (`defineApp`, zod) and
61
- `@montytools/sdk/react` (hooks: `useList`, `useInsert`, …). Never import
62
- Clerk or Convex directly; never fetch external APIs from app code — the
63
- platform CSP blocks them. Open external HTTPS pages with
64
- `openExternal(url)` from `@montytools/sdk`, called synchronously from the
65
- click or key handler; `window.open` and `target="_blank"` are blocked by
66
- the app iframe.
67
- 5. **The data schema lives in the WORKSPACE, not in code.** Read it with
68
- `monty schema` (JSON on stdout); change it by editing that JSON and
69
- running `monty schema set <file>` validated server-side, additive by
70
- default. On workspace-owned apps, `monty.config.ts` edits do NOT change
71
- the schema. Give every field a `description` and every enum/multiSelect
72
- a `valueDescriptions` map saying WHEN each option applies — that's the
73
- guidance later record-writing agents follow. Declare a page in the
74
- manifest BEFORE shipping its route a save with an undeclared route
75
- refuses with the fix. Field names `_*`, `updatedAt`, `createdBy` are
76
- reserved.
77
- 6. **Prefer a saved view for filtered tables.** If the request is
78
- one table with different filters, sorting, or hidden columns, use
79
- `monty views set <table> <name> ...`. For example, an evaluation queue is
43
+ `monty typecheck`, `monty dev`, `monty save`. Never run vite, tsc, pnpm,
44
+ or npm scripts directly. `monty logs` (add `-f` to follow) is where vite
45
+ errors, browser errors, and save results land.
46
+ 4. **Import only the SDK.** `@montytools/sdk` (`defineApp`, zod) and
47
+ `@montytools/sdk/react` (hooks: `useList`, `useInsert`, ...). Never
48
+ import Clerk or Convex; never fetch external APIs from app code, the CSP
49
+ blocks them. Open external pages with `openExternal(url)` from the SDK,
50
+ called synchronously from the click handler. `window.open` and
51
+ `target="_blank"` are blocked in the app iframe.
52
+ 5. **The schema lives in the workspace, not in code.** Read it with
53
+ `monty schema` (JSON on stdout), change it with
54
+ `monty schema set '<json>'` — the whole config as one JSON argument, or
55
+ piped: `monty schema | <edit> | monty schema set -` (validated
56
+ server-side, additive by default). `src/monty.gen.ts` is the GENERATED
57
+ mirror of that config — import `{ app }` from it for typed hooks, never
58
+ edit it (it regenerates within a heartbeat of any change while
59
+ `monty dev` runs). Give every field a
60
+ `description` and every enum a `valueDescriptions` map saying when each
61
+ option applies; record-writing agents follow that guidance later.
62
+ Declare a page before shipping its route (`monty page add <name>` does
63
+ both); a save with an undeclared route refuses. Field names `_*`,
64
+ `updatedAt`, `createdBy` are reserved.
65
+ 6. **Prefer a saved view for filtered tables.** One table with different
66
+ filters, sorting, or hidden columns is `monty views set <table> <name>`.
67
+ An evaluation queue:
80
68
  `monty views set leads "Evaluate" --filter '{"pipelineId":null}'`.
81
- Change selected properties or rename it later with
82
- `monty views update <table> <name> ...`; omitted properties stay unchanged.
83
- If that table needs actions, compose `RecordPage` from
84
- `@montytools/sdk/react` and add typed row/header/selection/record actions
85
- with the controls from `@montytools/sdk/ui`. Write the whole page yourself
86
- only when the shared components no longer fit. Custom pages remain the
87
- escape hatch.
88
- 7. **UI is stock shadcn** (preset already wired). Add curated components with
89
- `monty add <name>`; browse with `monty components` / `monty docs <name>`.
90
- How pages should LOOK Lyra surfaces, dark-only, the chart language is
91
- the `monty-design` skill; read it before styling any page.
69
+ A board: `--kanban <select-field>` lanes records by that field
70
+ (`monty views set deals "Pipeline" --kanban stage`). Update later with
71
+ `monty views update`; omitted properties stay unchanged. If the table
72
+ needs actions, compose `RecordPage` from `@montytools/sdk/react` with
73
+ controls from `@montytools/sdk/ui`. Write a whole custom page only when
74
+ the shared components no longer fit.
75
+ 7. **UI is stock shadcn** (preset already wired). Install curated components
76
+ with `monty components add <name>`; `monty components [query]` lists the
77
+ catalog, `monty components docs <name>` prints one's source. How pages
78
+ should look is the `monty-design` skill; read it before styling.
92
79
  8. **Errors are instructions.** Every failure prints
93
- `[MontyError CODE] Fix: …` do exactly what the Fix says; don't guess.
94
- Typecheck failures block the save by design.
80
+ `[MontyError CODE] Fix: ...`. Do what the Fix says. Typecheck failures
81
+ block the save by design.
95
82
  9. **Edits are real.** The dev shell reads and writes the app's LIVE
96
- records — there is one set of data, and every write journals into the
97
- app's Activity. Exercise the app for real; clean up test rows you
98
- create; never seed junk into a team's working tables.
83
+ records, and every write journals into the app's Activity. Exercise the
84
+ app for real, clean up test rows, never seed junk into a team's tables.
99
85
  10. **Report platform failures when sharing is enabled.** `monty support
100
- status` shows whether a workspace admin has allowed agent support
101
- reports. If enabled, submit one report when a Monty platform error still
102
- blocks the task after you followed its `Fix` and retried, or when the
103
- requested work needs a missing Monty platform feature. Summarize the
104
- thread. State the observed problem, expected behavior, attempted fixes,
105
- and exact error. Do not paste the full conversation. If sharing is off,
106
- ask the user before running `monty support enable --yes`; never grant
107
- consent yourself. Do not file reports for ordinary app-code bugs or send
108
- duplicates for the same incident.
86
+ status` shows whether an admin allowed agent reports. If enabled, submit
87
+ one report when a platform error still blocks you after following its
88
+ Fix, or when the task needs a missing platform feature. State the
89
+ problem, expected behavior, attempted fixes, and exact error. Don't
90
+ paste the conversation. If sharing is off, ask the user before running
91
+ `monty support enable --yes`. No reports for ordinary app-code bugs, no
92
+ duplicates.
109
93
 
110
94
  ## CLI reference
111
95
 
112
96
  | command | purpose |
113
97
  |---|---|
114
- | `monty login` | browser sign-in (loopback authorize), once per machine |
115
- | `monty create <slug>` | register the app in the workspace + stamp it into `~/.monty/apps/<id>` (needs login) |
116
- | `monty current` / `select` / `apps` | where am I / jump to app / list local |
117
- | `monty install` / `build` / `typecheck` | full lifecycle via the CLI no raw pnpm/vite/tsc |
118
- | `monty dev` | run the app's session, or attach to an already-running one (auto-port, live data, auto-auth) |
119
- | `monty logs [-n N] [-f]` | read/follow the dev shell log the debugging window after every edit |
120
- | `monty add <name…>` | install curated shadcn components |
121
- | `monty schema [slug]` | print the app's stored manifest (tables, pages, metrics) as JSON |
122
- | `monty schema set <file\|->` | write an edited manifest back (validated, CAS, additive by default) |
123
- | `monty views <list\|set\|update\|remove> <table>` | manage the shared saved views on a system record page |
124
- | `monty save ["what changed"]` | push the working copy to the cloud copy, like `git push main` (build + typecheck gate it) |
125
- | `monty support <status\|enable\|disable\|submit>` | manage consent and send a bounded agent-authored platform report |
126
- | `monty skills` | (re)install this skill for your agent |
98
+ | `monty login` | browser sign-in, once per machine |
99
+ | `monty connect <slug> [dir]` | pull an existing app into any folder, ready for `monty dev` |
100
+ | `monty create <slug>` | register a new app and scaffold it (needs login) |
101
+ | `monty current` / `select` / `apps` | where am I / jump to app / list local copies |
102
+ | `monty install` / `build` / `typecheck` | lifecycle, no raw pnpm/vite/tsc |
103
+ | `monty dev` | run the app's session, or attach to a running one |
104
+ | `monty logs [-n N] [-f]` | read/follow the session log |
105
+ | `monty save ["what changed"]` | push the working copy to the cloud copy (build + typecheck gate it) |
106
+ | `monty history [slug]` | saved-version history, one row per save |
107
+ | `monty schema [slug]` | print the app's config (stored in the workspace) as JSON |
108
+ | `monty schema set '<json>'` (or `set -` piped) | write the edited config back (validated, CAS) |
109
+ | `monty views <list\|set\|update\|remove> <table>` | manage shared saved views |
110
+ | `monty page add <name>` | declare + scaffold a custom page |
111
+ | `monty components add <name...>` | install curated shadcn components |
112
+ | `monty support <status\|enable\|disable\|submit>` | consent + platform reports |
113
+ | `monty skills` | (re)install this skill |
@@ -1,45 +1,92 @@
1
1
  ---
2
2
  name: monty-design
3
- description: Design Monty app pages — Lyra shadcn styling (borderless, sharp-cornered, stock components only), dark-only theming, and the Monty chart language (square marks on real axes). Use whenever building or restyling UI in a Monty app; pages, dashboards, charts, stat tiles, KPI rows, tables, or any prompt about how a Monty app should look.
3
+ description: Design Monty app pages — the Monty design tokens (type ladder, radius policy, elevation), Lyra shadcn styling (sharp surfaces, stock components only), dark-only theming, and the Monty chart language (square marks on real axes). Use whenever building or restyling UI in a Monty app; pages, dashboards, charts, stat tiles, KPI rows, tables, or any prompt about how a Monty app should look.
4
4
  ---
5
5
 
6
6
  # Designing Monty pages
7
7
 
8
8
  Monty is a B2B work OS. Apps render inside the platform shell, so a page is
9
- "designed" when it looks native to Monty: quiet, rectilinear, data-forward.
10
- The design system is already installed — your job is to NOT fight it.
11
-
12
- ## The one law: stock Lyra, nothing invented
13
-
14
- Every app ships shadcn preset `radix-lyra` (see `components.json`). Lyra
15
- surfaces are **borderless and sharp-cornered**: `Card` is `rounded-none`,
16
- no border, a `bg-card` fill with a hairline `ring-1 ring-foreground/10`.
17
-
18
- - Use the components as they come: `Card`/`CardHeader`/`CardTitle`/
19
- `CardDescription`/`CardContent`, `Table`, `Badge`, Never rebuild a
20
- surface as a styled `div` a hand-rolled `rounded-xl border bg-…` card is
21
- the canonical mistake.
22
- - Never invent tokens or raw colors. Semantic tokens only (`bg-background`,
23
- `text-muted-foreground`, `border-border`, `var(--chart-2)`, …).
24
- - Sanctioned overrides are content-level only: e.g. `text-2xl tabular-nums`
25
- on a stat value, a width on a label column. If an override styles a
9
+ "designed" when it is indistinguishable from the shell around it: quiet,
10
+ rectilinear, data-forward. The design system is already installed — the shell
11
+ and every app share ONE token sheet (`@montytools/sdk/tokens.css`, imported
12
+ by `src/index.css`) and one component surface. Your job is to NOT fight it.
13
+
14
+ ## The one law: tokens and stock components, nothing invented
15
+
16
+ - Use components as they come: `Card`/`CardHeader`/`CardTitle`/
17
+ `CardDescription`/`CardContent`, `Table`, `Badge`, `Button`, … Never
18
+ rebuild a surface as a styled `div` — a hand-rolled `rounded-xl border`
19
+ card is the canonical mistake.
20
+ - Never write a raw value the tokens already name: no hex/oklch colors, no
21
+ `text-[13px]`, no `rounded-[10px]`, no hand-written `shadow-[…]`.
22
+ Semantic utilities only (`bg-background`, `text-muted-foreground`,
23
+ `var(--chart-2)`, `shadow-control`, …).
24
+ - Sanctioned overrides are content-level only: `tabular-nums` on values, a
25
+ width on a label column, `capitalize` on an enum. If an override styles a
26
26
  SURFACE, you are off the system.
27
27
 
28
+ ## The type ladder — six styles, and fewer is better
29
+
30
+ One class per role; each carries its size, line-height, weight, and
31
+ tracking. **Hierarchy comes from weight and ink, never from inventing
32
+ sizes.**
33
+
34
+ | Class | Size | Use |
35
+ |---|---|---|
36
+ | `text-tick` | 10/12 | chart axis ticks ONLY |
37
+ | `text-meta` | 12/16 | column headers, chips, captions, timestamps, secondary values |
38
+ | `text-body` | 13/20 | THE DEFAULT — content, controls, table cells (already on `body`) |
39
+ | `text-title` | 14/20 · 500 | card titles, page-header titles, nav rows |
40
+ | `text-heading` | 18/24 · 600 | page-level headings (rare — `PageHeader` owns page tops) |
41
+ | `text-stat` | 24/32 · 600 | stat-tile values, always with `tabular-nums` |
42
+
43
+ A typical page uses three: `text-body` (inherited — write nothing),
44
+ `text-meta` for secondary lines, `text-stat` on tile values. Reach for the
45
+ others only when the role in the table matches. Weights: default 400,
46
+ `font-medium` for titled/interactive text; 600 arrives only via
47
+ `text-heading`/`text-stat`. Never `font-bold`, never `text-xs/sm/lg/2xl` —
48
+ those sizes exist for legacy chrome, not for pages.
49
+
50
+ ## Radius policy — sharp, everywhere
51
+
52
+ Lyra is rectilinear: surfaces, controls, and overlays are ALL square-
53
+ cornered. Cards/tables/tiles/chips are `rounded-none` with `bg-card ring-1
54
+ ring-foreground/10` hairlines, never borders; buttons, inputs, dialogs, and
55
+ popovers come sharp from the stock components. Elevation and hierarchy come
56
+ from hairlines and the three shadows, never from softened corners.
57
+ `rounded-full` (avatars, status dots) is the ONE exception — write no other
58
+ `rounded-*` class yourself; the semantic names the chrome uses
59
+ (`rounded-control`, `rounded-overlay`) resolve to 0 by policy.
60
+
61
+ ## Color and status
62
+
63
+ The palette is the platform's; you never pick colors. Ground `bg-background`,
64
+ surface `bg-card`, ink `text-foreground`/`text-muted-foreground`, quiet fill
65
+ `bg-accent`, the one accent `bg-primary`, links `text-link`. Status is a
66
+ **dot + muted text**, never a colored pill: `text-success`, `text-warning`,
67
+ `text-destructive` for the dot/figure, label in `text-muted-foreground`.
68
+ Elevation: `shadow-control` on rest-state controls, `shadow-btn-primary` on
69
+ the one primary action, `shadow-menu` on overlays — never `shadow-md/lg/xl`.
70
+
71
+ ## Spacing — the 4px grid, three names
72
+
73
+ Page bodies are `p-page` (24px); cards pad themselves; card grids are
74
+ `gap-3` (12px); control rows `gap-2`. Stay on the default Tailwind steps
75
+ (`p-1`…`p-6`); if you're typing an arbitrary value like `p-[18px]`, the
76
+ layout is wrong, not the scale.
77
+
28
78
  ## Dark-only, in the shell's palette
29
79
 
30
- The Monty shell is dark-only; a light page inside it reads as broken. The
31
- app theme's `.dark` tokens mirror the shell palette (#101112 ground,
32
- #17181A cards, #266DF0 primary) so embedded pages are seamless never
33
- retheme or hand-pick your own dark colors. Until
34
- the template ships dark by default: `class="dark"` on `<html>` in
35
- `index.html`, and any boot-splash background set to the dark `--background`
36
- value. Verify your page against the shell, not in isolation.
80
+ Monty is dark-only; the tokens ship the shell palette (#101112 ground) and
81
+ `index.html` carries `class="dark"` plus the dark boot splash. Never
82
+ retheme, never hand-pick a dark color, and verify your page against the
83
+ shell (`/apps/<slug>/…`), not the bare dev port.
37
84
 
38
85
  ## Page anatomy
39
86
 
40
87
  Every page opens with the platform chrome from `@montytools/sdk/ui` — the
41
- SAME components the shell renders system table views with, so a custom page
42
- is indistinguishable from a record page. Never hand-roll the header bar.
88
+ SAME components the shell renders system table views with. Never hand-roll
89
+ the header bar.
43
90
 
44
91
  ```tsx
45
92
  import { PageHeader, PageHeaderButton } from "@montytools/sdk/ui";
@@ -50,7 +97,7 @@ import { PageHeader, PageHeaderButton } from "@montytools/sdk/ui";
50
97
  <PageHeaderButton primary onClick={main}><Plus className="size-3.5" /> New</PageHeaderButton>
51
98
  </PageHeader>
52
99
  <div className="min-h-0 flex-1 overflow-auto">
53
- <div className="flex flex-col gap-3 p-6 pt-4">
100
+ <div className="flex flex-col gap-3 p-page pt-4">
54
101
  {/* KPI row */}
55
102
  <div className="grid grid-cols-[repeat(auto-fill,minmax(14rem,1fr))] gap-3">…</div>
56
103
  {/* section cards */}
@@ -72,36 +119,35 @@ A stat tile is a stock Card, nothing more:
72
119
  <Card size="sm">
73
120
  <CardHeader>
74
121
  <CardDescription>Leads added</CardDescription>
75
- <CardTitle className="text-2xl tabular-nums">{value}</CardTitle>
76
- <div className="text-xs text-muted-foreground">{delta or context}</div>
122
+ <CardTitle className="text-stat tabular-nums">{value}</CardTitle>
123
+ <div className="text-meta text-muted-foreground">{delta or context}</div>
77
124
  </CardHeader>
78
125
  </Card>
79
126
  ```
80
127
 
81
128
  ## Charts: sharp marks on real axes
82
129
 
83
- Chart marks are **sharp rectangles — no rounded corners, ever** (matches the
84
- Lyra rectilinear look). The three failure modes to avoid: pill "track+fill"
85
- bars (read as progress bars), rounded caps (misstate where a value ends),
86
- and floating bars with no axis (nothing anchors the eye).
130
+ Chart marks are **sharp rectangles — no rounded corners, ever**. The three
131
+ failure modes to avoid: pill "track+fill" bars (read as progress bars),
132
+ rounded caps (misstate where a value ends), and floating bars with no axis
133
+ (nothing anchors the eye).
87
134
 
88
135
  - **Horizontal bars**: grow from a left hairline baseline
89
136
  (`border-l border-foreground/25`) over quarter gridlines
90
137
  (`absolute left-1/4|1/2|3/4 border-l border-foreground/10` —
91
138
  foreground-alpha so hairlines read on any surface), bar `h-4`,
92
- fill `var(--chart-2)`. Label left in `text-muted-foreground`
93
- (fixed-width, truncate); count right in `font-medium tabular-nums`,
94
- zero values muted with no fill.
139
+ fill `var(--chart-2)`. Label left in `text-body text-muted-foreground`
140
+ (fixed-width, truncate); count right in `text-body font-medium
141
+ tabular-nums`, zero values muted with no fill.
95
142
  - **Columns**: a value axis with a baseline (`border-foreground/25`) plus
96
- hairline gridlines (`border-foreground/10`) and tiny tabular tick labels (10px, muted,
97
- right-aligned in a left gutter). Round the axis top to a "nice" integer
98
- (≤4 exact; else the next multiple of 5/10/50) so ticks stay honest.
99
- Direct value labels above non-zero columns.
100
- - **Color**: one accent ramp from the Monty palette — `var(--chart-2)` for
101
- the emphasized series (today, the selection), `var(--chart-5)` for
102
- context. The values are the platform's (Attio-blue family, matching the
103
- shell); never restate them as hex, never a hue per category, and text
104
- never wears the data color.
143
+ hairline gridlines (`border-foreground/10`) and `text-tick` tabular tick
144
+ labels (muted, right-aligned in a left gutter). Round the axis top to a
145
+ "nice" integer (≤4 exact; else the next multiple of 5/10/50) so ticks
146
+ stay honest. Direct `text-tick` value labels above non-zero columns.
147
+ - **Color**: one accent ramp — `var(--chart-2)` for the emphasized series
148
+ (today, the selection), `var(--chart-5)` for context. Never restate the
149
+ values as hex, never a hue per category, and text never wears the data
150
+ color.
105
151
  - **Numbers**: `tabular-nums` everywhere values align; money via
106
152
  `toLocaleString(undefined, { style: "currency", currency: "USD" })`
107
153
  (compact notation on tiles, full in tables).
@@ -109,15 +155,18 @@ and floating bars with no axis (nothing anchors the eye).
109
155
  muted line ("No activity logged on this day.") — structure stays, zeros
110
156
  carry meaning.
111
157
 
112
- Reference implementations: `ColumnChart` and `BarRow` in
113
- `demos/crm/src/routes/stats.tsx` (day-selector columns, status bars) and
114
- `demos/crm-v2/src/routes/stats.tsx` (money detail on bars, top-deals table).
158
+ Reference implementations: `demos/crm-v2/src/routes/stats.tsx` (KPI tiles,
159
+ day-selector columns, status bars, top-deals table) and
160
+ `demos/crm-v2/src/routes/reports.tsx` (stat tiles over live records).
115
161
 
116
162
  ## Checklist before you call a page done
117
163
 
118
- 1. No `border`/`rounded-*` on any surface you authored surfaces are stock
119
- Lyra components.
120
- 2. No rounded corners on any chart mark; every bar/column sits on an axis.
121
- 3. Dark: the page blends into the shell with no light seams.
122
- 4. Values in tabular figures; labels in text tokens; one accent hue.
123
- 5. Screenshot it inside the shell (`/apps/<slug>/…`), not just the dev port.
164
+ 1. No `border`/`rounded-*` anywhere you authored (`rounded-full` on dots
165
+ and avatars is the one exception) — surfaces and controls are stock
166
+ components, sharp by policy.
167
+ 2. No text class outside the six ladder styles; no arbitrary values
168
+ (`text-[…]`, `bg-[#…]`, `rounded-[…]`, `shadow-[…]`).
169
+ 3. No rounded corners on any chart mark; every bar/column sits on an axis.
170
+ 4. Dark: the page blends into the shell with no light seams.
171
+ 5. Values in tabular figures; status as dot + muted text, never a pill.
172
+ 6. Screenshot it inside the shell (`/apps/<slug>/…`), not just the dev port.