basalt-ui 2.0.0 → 2.0.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## [2.0.2](https://github.com/jkrumm/basalt-ui/compare/v2.0.1...v2.0.2) (2026-10-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * keep the brand symbol out of keyof a groupTokens result ([27f44d3](https://github.com/jkrumm/basalt-ui/commit/27f44d3da50f07f9adb5b850edc86e8e388d0550))
7
+ * make doctor and sync monorepo-aware ([eafb701](https://github.com/jkrumm/basalt-ui/commit/eafb7013d07a7935b393c70553e11d2a57f01c71))
8
+ * throw a clear error for a group not built by groupTokens ([7c0dc44](https://github.com/jkrumm/basalt-ui/commit/7c0dc447e2faff22561166f66d6e449be511981c))
9
+
10
+ ## [2.0.1](https://github.com/jkrumm/basalt-ui/compare/v2.0.0...v2.0.1) (2026-10-14)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * close argo friction rows 1-3 ([41627c3](https://github.com/jkrumm/basalt-ui/commit/41627c328ad278fc303ca9317cada2d2ab5f99a4))
16
+
1
17
  # [2.0.0](https://github.com/jkrumm/basalt-ui/compare/v1.41.0...v2.0.0) (2026-10-14)
2
18
 
3
19
 
package/MIGRATING.md CHANGED
@@ -66,18 +66,18 @@ There is one row per SURFACE, not per export, and a surface earns a row once any
66
66
  shipped lefthook preset both changed in 1.30.0 with no row that owned them — which is how a
67
67
  consumer read this index, found nothing, and diffed a KPI row that had silently gone 4-up → 2-up.
68
68
 
69
- | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | 1.32.0 | 1.32.1 | 1.33.0 | 1.34.0 | 1.34.1 | 1.35.0 | 1.36.0 | 1.36.1 | 1.37.0 | 1.37.1 | 1.38.0 | 1.38.1 | 1.39.0 | 1.40.0 | 1.40.1 | 1.40.2 | 1.40.3 | 1.41.0 | 2.0.0 | Unreleased |
70
- | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ------ | ------ | ----------------------------------------------- | ------ | ------ | ------ | ------ | ------------------------------------------------------------ | ---------- | --- | --- |
71
- | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ 1.31.0) | — | `SIZE_CLASSES`/`CONTAINER_CLASSES` actually exported (§ Fixes) | `CONTAINER_GRID_BREAKPOINTS`; `raw-breakpoint` trusts it (§ 1.33.0) | `CONTAINER_KEYS` (§ 1.34.0) | — | — | — | — | — | — | — | | `appShellInset` 20 → 16 (§ 1.38.1) | — | — | — | — | — | — | — | — |
72
- | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ 1.31.0) | — | `ResponsiveChartHeight` `sm` → key (§ Container recipes) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
73
- | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ 1.31.0) | `useBreakpoint` removal → 1.33.0; `useSizeClass` is shell-only (§ 1.32.0) | shell-less `shell/` gutter, `AppShell` breakpoints, `useSizeClass` for overlays (§§ Shell-less, `useSizeClass`) | — | `useBreakpoint` removed (§ 1.34.0) | — | — | `useCoarsePointer()` (§ `useCoarsePointer()`) | — | — | — | — | | content gutter 16, block 8 from `sm` (§ 1.38.1) | — | — | — | — | — | — | — | — |
74
- | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — | — | `NumberFilter clearable`; `createSearchStore` `resets` (§ State and controls) | — | — | — | `FormSection`/`FormRow`/`FormGroup`/`FormActions` move here (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — |
75
- | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — | `FormGroup` row no longer stretches (§ Fixes) | — | — | — | — | the layout + `useForm`/`schemaResolver` deprecated, removed 1.37.0 (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — |
76
- | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — | `renderCard` documented (§ `renderCard`) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
77
- | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
78
- | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
79
- | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | the managed block moves `CLAUDE.md` → `AGENTS.md` (§ 1.41.0) | — | — |
80
- | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | `raw-breakpoint`: the container-grid answer, `error` at 1.33.0 (§ 1.32.0) | `FormRow`/`FormGroup` homes in both lanes; `basalt-main` dropped (§ Fixes) | C1 + breakpoint pairs → `error`; `unknown-vx-token` `warn` (§ Guards) | — | — | — | `unknown-vx-token` → `error`; the token factories throw (§ Guards) | — | — | — | — | — | — | — | — | — | — | — | — | — |
69
+ | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | 1.32.0 | 1.32.1 | 1.33.0 | 1.34.0 | 1.34.1 | 1.35.0 | 1.36.0 | 1.36.1 | 1.37.0 | 1.37.1 | 1.38.0 | 1.38.1 | 1.39.0 | 1.40.0 | 1.40.1 | 1.40.2 | 1.40.3 | 1.41.0 | 2.0.0 | 2.0.1 | 2.0.2 | Unreleased |
70
+ | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ------ | ------ | ----------------------------------------------- | ------ | ------ | ------ | ------ | ------------------------------------------------------------ | ---------- | --- | --- | --- | --- |
71
+ | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ 1.31.0) | — | `SIZE_CLASSES`/`CONTAINER_CLASSES` actually exported (§ Fixes) | `CONTAINER_GRID_BREAKPOINTS`; `raw-breakpoint` trusts it (§ 1.33.0) | `CONTAINER_KEYS` (§ 1.34.0) | — | — | — | — | — | — | — | | `appShellInset` 20 → 16 (§ 1.38.1) | — | — | — | — | — | — | — | — | — | — |
72
+ | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ 1.31.0) | — | `ResponsiveChartHeight` `sm` → key (§ Container recipes) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
73
+ | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ 1.31.0) | `useBreakpoint` removal → 1.33.0; `useSizeClass` is shell-only (§ 1.32.0) | shell-less `shell/` gutter, `AppShell` breakpoints, `useSizeClass` for overlays (§§ Shell-less, `useSizeClass`) | — | `useBreakpoint` removed (§ 1.34.0) | — | — | `useCoarsePointer()` (§ `useCoarsePointer()`) | — | — | — | — | | content gutter 16, block 8 from `sm` (§ 1.38.1) | — | — | — | — | — | — | — | — | — | — |
74
+ | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — | — | `NumberFilter clearable`; `createSearchStore` `resets` (§ State and controls) | — | — | — | `FormSection`/`FormRow`/`FormGroup`/`FormActions` move here (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
75
+ | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — | `FormGroup` row no longer stretches (§ Fixes) | — | — | — | — | the layout + `useForm`/`schemaResolver` deprecated, removed 1.37.0 (§ The form layout moves) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
76
+ | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — | `renderCard` documented (§ `renderCard`) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
77
+ | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
78
+ | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
79
+ | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | the managed block moves `CLAUDE.md` → `AGENTS.md` (§ 1.41.0) | — | — | — | — |
80
+ | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | `raw-breakpoint`: the container-grid answer, `error` at 1.33.0 (§ 1.32.0) | `FormRow`/`FormGroup` homes in both lanes; `basalt-main` dropped (§ Fixes) | C1 + breakpoint pairs → `error`; `unknown-vx-token` `warn` (§ Guards) | — | — | — | `unknown-vx-token` → `error`; the token factories throw (§ Guards) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
81
81
 
82
82
  ---
83
83
 
@@ -88,8 +88,10 @@ Contract: `docs/V2-SPEC.md`. Every removal and move is one row, grouped by area.
88
88
  ### Upgrading from 1.x
89
89
 
90
90
  1. Go to `1.41.0` first and clear its deprecations: 2.0 retires the lifecycle, so nothing deprecated survives.
91
- 2. Install `basalt-ui@2` and the now-required peers (§ Subpaths, peers and gates); delete your
92
- `@mantine/*/styles.layer.css` imports, because `basalt-ui/styles.css` carries them.
91
+ 2. Install `basalt-ui@2` and the now-required peers (§ Subpaths, peers and gates); delete only the
92
+ `@mantine/{core,notifications,spotlight}/styles.layer.css` imports, because `basalt-ui/styles.css`
93
+ `@import`s exactly those three. Keep every extension package's layer CSS (`@mantine/dates`,
94
+ `@mantine/schedule`, …) imported AFTER `basalt-ui/styles.css`.
93
95
  3. Replace the hand-built root (providers, `QueryClient`, router) with `BasaltProvider` +
94
96
  `createBasaltRouter` (§ Provider and wiring).
95
97
  4. Rewrite imports by area, using the tables below. A `move` changes only the specifier.
@@ -990,6 +992,34 @@ optional, subpath-required peers.
990
992
  _Nothing yet. Author the next section under this heading as `## Unreleased — <title>`; the
991
993
  release renames it and opens a new one here._
992
994
 
995
+ ## 2.0.2
996
+
997
+ _Nothing to migrate in this release._
998
+
999
+ ## 2.0.1
1000
+
1001
+ _Nothing to migrate in this release._
1002
+
1003
+ ## Next patch — doctor reads the peers and the config
1004
+
1005
+ - `doctor` gains `exact-peers`: for each exact-pinned peer of the installed basalt-ui (the three
1006
+ `@fontsource-variable/*`, `motion`, `remend`, …) it compares your installed version and prints a
1007
+ one-line `bun add <pkg>@<version>` fix on a mismatch. `bun add` accepts the mismatch silently.
1008
+ The three font peers now pin `5.3.0`.
1009
+ - `doctor` gains `config-keys`: a key in the package.json `basalt` object that the config does not
1010
+ read fails with its fix. `basalt.chartMissingAriaLabel` is gone with its guard kind; `ariaLabel`
1011
+ is required on a chart now, so delete the key.
1012
+ - Corrected the § 2.0 "Upgrading from 1.x" step 2: delete only the core, notifications and
1013
+ spotlight layer imports; extension packages (`@mantine/dates`, `@mantine/schedule`) stay, after
1014
+ `basalt-ui/styles.css`.
1015
+ - `doctor` and `sync` are monorepo-aware. The manifest, `.claude/` and the AGENTS block stay at the
1016
+ install root; the app-scoped half (`tsconfig-base`, `app-wiring`, the `src/main.tsx`,
1017
+ `src/lib/nav.tsx` and `src/routes/__root.tsx` seeds) targets the app package: `basalt.app`
1018
+ (relative path, e.g. `"apps/dashboard"`) in the root package.json, else the one workspace package
1019
+ that depends on basalt-ui and has `src/main.tsx` or `index.html`, else the cwd. Two candidates
1020
+ fail with a message naming `basalt.app`. `doctor` run inside the app package finds the root
1021
+ manifest, and warns about a router seed an earlier `sync` left at the root — delete it.
1022
+
993
1023
  ## 2.0.0
994
1024
 
995
1025
  _Nothing to migrate in this release._
@@ -26,7 +26,7 @@ declare module '@tanstack/react-router' {
26
26
  createRoot(document.getElementById('root')!).render(<BasaltProvider router={router} />)
27
27
  ```
28
28
 
29
- 3. If you use `basalt-ui/controls-dates`, import `@mantine/dates/styles.layer.css` after `basalt-ui/styles.css` (core must precede dates in `@layer mantine`).
29
+ 3. Import extension layer CSS (`@mantine/dates` for `basalt-ui/controls-dates`, `@mantine/schedule`) after `basalt-ui/styles.css` (core must precede them in `@layer mantine`).
30
30
  4. Install the peers of each subpath you import: read its `Install: bun add …` line in `node_modules/basalt-ui/llms.txt`; a subpath with no `Install:` line needs only the required peers listed at the top of that file.
31
31
  5. In `vite.config.ts` spread `...basaltViteConfig({ port, apiTarget })` and register, in this order, `plugins: [tanstackRouter({ target: 'react', autoCodeSplitting: true }), react(), ...basaltAppPlugin({ name, description })]`. Run `bun add -d @tanstack/router-plugin` and import `tanstackRouter` from `@tanstack/router-plugin/vite`.
32
32
  6. Run `vite build` once so the plugin writes `src/routeTree.gen.ts`; commit it (`tsc -b` fails without it).
@@ -1,6 +1,16 @@
1
1
  import type { GuardKind, GuardSeverity } from '../guard/index.js';
2
2
  /** Shape of the optional `"basalt"` key in a consumer's package.json. */
3
3
  export type BasaltConfig = {
4
+ /**
5
+ * The Vite app's directory relative to the install root, for a workspace monorepo whose manifest,
6
+ * `.claude/` and AGENTS block live at the root. App-scoped checks (`tsconfig-base`, `app-wiring`)
7
+ * and the router seeds target it. Default: the single workspace package that depends on basalt-ui
8
+ * and has a Vite entry, else the root itself.
9
+ *
10
+ * @example
11
+ * { app: 'apps/dashboard' }
12
+ */
13
+ app?: string;
4
14
  /** Source roots to scan. Default: `['src']`. Set explicitly for a monorepo layout. */
5
15
  roots?: string[];
6
16
  /** Files exempt from the scan (they ARE the palette source). Default: argo's exempt set. */
@@ -165,4 +175,8 @@ export declare const DEFAULT_ROOTS: string[];
165
175
  * "matches zero files" break this derivation exists to prevent.
166
176
  */
167
177
  export declare function resolveRoots(cfg: BasaltConfig): string[];
178
+ /** Every `BasaltConfig` key, as data for `doctor`'s `config-keys` (a test pins it to the type). */
179
+ export declare const BASALT_CONFIG_KEYS: (keyof BasaltConfig)[];
180
+ /** Keys removed from `BasaltConfig`, with the reason `doctor` prints. */
181
+ export declare const RETIRED_CONFIG_KEYS: Readonly<Record<string, string>>;
168
182
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExD,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG;IACzB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IACnC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;wFACoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;IACpD,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAA;IACzD;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5E,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,EAAE,CAG5E;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGlF;AAED,eAAO,MAAM,YAAY,QAAQ,CAAA;AACjC,eAAO,MAAM,aAAa,UAAiB,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAExD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExD,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAA;IACnC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;wFACoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAA;IACpD,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAA;IACzD;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5E,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,EAAE,CAG5E;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAGlF;AAED,eAAO,MAAM,YAAY,QAAQ,CAAA;AACjC,eAAO,MAAM,aAAa,UAAiB,CAAA;AAE3C;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAExD;AAED,mGAAmG;AACnG,eAAO,MAAM,kBAAkB,EAIf,CAAC,MAAM,YAAY,CAAC,EAAE,CAAA;AAEtC,yEAAyE;AACzE,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEhE,CAAA"}
@@ -11,9 +11,15 @@ const DEFAULT_ROOTS = [DEFAULT_ROOT];
11
11
  function resolveRoots(cfg) {
12
12
  return cfg.roots?.length ? cfg.roots : DEFAULT_ROOTS;
13
13
  }
14
+ const BASALT_CONFIG_KEYS = "app roots exempt profile include spacingSteps severity forbiddenAccents accentHue rawRadius rawSurface cardWithBorder offSystemSurfaceVar mantineShadeIndex rawHtmlLayout inlineSpacing inlineDisplay rawMotionValue rawFormControl sub16InputFont seriesModulePath exemptRules aiMajorSkewReason".split(" ");
15
+ const RETIRED_CONFIG_KEYS = {
16
+ chartMissingAriaLabel: "its guard kind is gone in 2.0 (`ariaLabel` is required on a chart now)"
17
+ };
14
18
  export {
19
+ BASALT_CONFIG_KEYS,
15
20
  DEFAULT_ROOT,
16
21
  DEFAULT_ROOTS,
22
+ RETIRED_CONFIG_KEYS,
17
23
  exemptRulePaths,
18
24
  exemptRuleReason,
19
25
  resolveRoots
@@ -28,4 +28,16 @@ export declare function seedTsconfigExtends(cwd: string): string | null;
28
28
  * proves nothing.
29
29
  */
30
30
  export declare function appWiringCheck(cwd: string): Check;
31
+ /**
32
+ * `config-keys`: a key in package.json's `basalt` object that `BasaltConfig` does not declare is
33
+ * silently ignored, so a key retired in 2.0 keeps looking configured. Reads the raw file, not
34
+ * `readBasaltConfig`, which is typed and would hide the very keys this looks for.
35
+ */
36
+ export declare function unknownConfigKeysCheck(cwd: string): Check;
37
+ /**
38
+ * `exact-peers`: basalt-ui pins some peers to one exact version, and `bun add` accepts any other
39
+ * version without a word. Compares each installed one against the install's declared pin; a peer
40
+ * that is not installed is not this check's business (optional peers are legitimately absent).
41
+ */
42
+ export declare function exactPeersCheck(cwd: string, install: BasaltInstall): Check;
31
43
  //# sourceMappingURL=doctor-upgrade.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-upgrade.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-upgrade.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,MAAM,MAAM,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAgB9D,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,KAAG,MACc,CAAA;AA+CvD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAqBpD;AA+CD,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,KAAK,CAyBhF;AA4CD,mGAAmG;AACnG,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,aAAa,GACrB,KAAK,CA8BP;AAUD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkC9D;AAoDD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CA+BjD"}
1
+ {"version":3,"file":"doctor-upgrade.d.ts","sourceRoot":"","sources":["../../src/cli/doctor-upgrade.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE5C,MAAM,MAAM,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAgB9D,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,KAAG,MACc,CAAA;AA+CvD,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAqBpD;AA+CD,4FAA4F;AAC5F,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,KAAK,CAyBhF;AA4CD,mGAAmG;AACnG,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,aAAa,GACrB,KAAK,CA8BP;AAUD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkC9D;AAoDD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CA+BjD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAezD;AAaD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,KAAK,CAwB1E"}
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import { dirname, relative, resolve } from "node:path";
4
+ import { BASALT_CONFIG_KEYS, RETIRED_CONFIG_KEYS } from "./config.js";
4
5
  import { parseJsonc, readIfExists, scannableFiles } from "./index.js";
5
6
  const guarded = (id, run) => {
6
7
  try {
@@ -259,11 +260,55 @@ function appWiringCheck(cwd) {
259
260
  };
260
261
  });
261
262
  }
263
+ function unknownConfigKeysCheck(cwd) {
264
+ return guarded("config-keys", () => {
265
+ const raw = readIfExists(resolve(cwd, "package.json"));
266
+ const block = raw === null ? null : JSON.parse(raw).basalt;
267
+ if (typeof block !== "object" || block === null || Array.isArray(block)) return null;
268
+ const known = new Set(BASALT_CONFIG_KEYS);
269
+ const unknown = Object.keys(block).filter((k) => !known.has(k));
270
+ if (unknown.length === 0) return null;
271
+ const named = unknown.map(
272
+ (k) => `basalt.${k} (${RETIRED_CONFIG_KEYS[k] ?? "not a basalt key"})`
273
+ );
274
+ return {
275
+ fail: `config-keys: package.json sets keys basalt-ui does not read: ${named.join("; ")}. Fix: delete each key.`
276
+ };
277
+ });
278
+ }
279
+ const EXACT_VERSION = /^\d+\.\d+\.\d+$/;
280
+ function installedVersion(cwd, pkg) {
281
+ for (let dir = cwd; ; dir = dirname(dir)) {
282
+ const raw = readIfExists(resolve(dir, "node_modules", pkg, "package.json"));
283
+ if (raw !== null) return JSON.parse(raw).version ?? null;
284
+ if (dirname(dir) === dir) return null;
285
+ }
286
+ }
287
+ function exactPeersCheck(cwd, install) {
288
+ const id = "exact-peers";
289
+ if (install.dir === null)
290
+ return { skip: `${id}: basalt-ui does not resolve, so its peers cannot be read.` };
291
+ const dir = install.dir;
292
+ return guarded(id, () => {
293
+ const raw = readIfExists(resolve(dir, "package.json"));
294
+ const peers = (raw === null ? {} : JSON.parse(raw).peerDependencies) ?? {};
295
+ const off = Object.entries(peers).filter(([, range]) => EXACT_VERSION.test(range)).flatMap(([pkg, pin]) => {
296
+ const have = installedVersion(cwd, pkg);
297
+ return have === null || have === pin ? [] : [`${pkg}@${have} (basalt-ui pins ${pin}): \`bun add ${pkg}@${pin}\``];
298
+ });
299
+ if (off.length === 0) return null;
300
+ return {
301
+ fail: `${id}: installed peers differ from basalt-ui's exact pins. Fix: ${off.join("; ")}.`
302
+ };
303
+ });
304
+ }
262
305
  export {
263
306
  appWiringCheck,
264
307
  droppedSubpathsCheck,
308
+ exactPeersCheck,
265
309
  retiredLintRulesCheck,
266
310
  seedTsconfigExtends,
267
311
  tsconfigBaseCheck,
268
- tsconfigFile
312
+ tsconfigFile,
313
+ unknownConfigKeysCheck
269
314
  };
@@ -22,7 +22,8 @@ export type DoctorResult = {
22
22
  * 4. `oxlint-preset`: `.oxlintrc.json` extends the shipped preset AND that path resolves.
23
23
  * 7. `ai-major-parity`: the `ai` major agrees across this package.json's dependency fields
24
24
  * (exempt via `basalt.aiMajorSkewReason`; a stale reason warns).
25
- * 8-11. `tsconfig-base`, `retired-lint-rules`, `dropped-subpaths`, `app-wiring` (./doctor-upgrade);
25
+ * 8-13. `tsconfig-base`, `retired-lint-rules`, `dropped-subpaths`, `app-wiring`, `exact-peers`,
26
+ * `config-keys` (./doctor-upgrade);
26
27
  * a check that cannot run is SKIPPED and exits non-zero.
27
28
  *
28
29
  * Warning (non-fatal):
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAqDA,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AA8CD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,aAAa,GAAE,MAAsB,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,CAiS1F"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAyDA,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAA;IACpB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AA8CD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,aAAa,GAAE,MAAsB,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,CAsU1F"}
@@ -19,7 +19,9 @@ import {
19
19
  readBasaltConfig,
20
20
  readIfExists,
21
21
  readManifest,
22
+ resolveAppDir,
22
23
  resolveProjectDir,
24
+ ROUTER_SEED_DESTS,
23
25
  sameFile,
24
26
  scannableFiles,
25
27
  scanBlock,
@@ -29,8 +31,10 @@ import { resolveRoots } from "./config.js";
29
31
  import {
30
32
  appWiringCheck,
31
33
  droppedSubpathsCheck,
34
+ exactPeersCheck,
32
35
  retiredLintRulesCheck,
33
- tsconfigBaseCheck
36
+ tsconfigBaseCheck,
37
+ unknownConfigKeysCheck
34
38
  } from "./doctor-upgrade.js";
35
39
  import { findManifestAbove, parentInstallAdvice } from "./sync.js";
36
40
  function isSameFile(a, b) {
@@ -72,7 +76,12 @@ function resolveAiMajorSkewReason(cfg) {
72
76
  function doctor(invocationCwd = process.cwd(), flags = []) {
73
77
  if (conflictingProfileFlags("doctor", flags)) return 1;
74
78
  const project = resolveProjectDir(invocationCwd);
75
- const cwd = project.dir;
79
+ const aboveRoot = project.relocatedFrom === null && !existsSync(resolve(project.dir, MANIFEST_PATH)) ? findManifestAbove(project.dir) : null;
80
+ const insideApp = aboveRoot !== null && (() => {
81
+ const app2 = resolveAppDir(aboveRoot);
82
+ return !("error" in app2) && app2.dir === project.dir;
83
+ })();
84
+ const cwd = insideApp && aboveRoot !== null ? aboveRoot : project.dir;
76
85
  const cfg = readBasaltConfig(cwd);
77
86
  const result = { hardFailures: 0, warnings: 0, skipped: 0 };
78
87
  const lines = [`
@@ -97,6 +106,11 @@ basalt-ui doctor \u2014 ${cwd}
97
106
  lines.push(` \u2192 BASALT_CWD relocated from ${project.relocatedFrom} to ${cwd}.
98
107
  `);
99
108
  }
109
+ if (insideApp)
110
+ lines.push(` \u2192 ${project.dir} is the app package; checking the install root ${cwd}.
111
+ `);
112
+ const app = resolveAppDir(cwd);
113
+ const appDir = "error" in app ? cwd : app.dir;
100
114
  const profile = declaredProfile(cfg, flags);
101
115
  if (profile === "tokens-only") {
102
116
  lines.push(
@@ -118,6 +132,19 @@ basalt-ui doctor \u2014 ${cwd}
118
132
  fail(`${MANIFEST_PATH} missing \u2014 run \`basalt-ui init\` to scaffold the consumer repo`);
119
133
  }
120
134
  const install = findBasaltInstall(cwd);
135
+ if ("error" in app) {
136
+ fail(`app-dir: ${app.error}`);
137
+ } else if (app.rel !== "") {
138
+ pass(`app-dir: the app is ${app.rel} (agent layer and manifest stay at the install root)`);
139
+ const ledger = readManifest(cwd).files;
140
+ for (const seed of Object.values(ROUTER_SEED_DESTS)) {
141
+ if (ledger[seed] !== void 0 && existsSync(resolve(cwd, seed))) {
142
+ warn(
143
+ `app-dir: ${seed} was seeded at the install root by an earlier \`sync\`, but the app is ${app.rel} \u2014 it is dead code there. Delete it (\`sync\` seeds ${app.rel}/${seed} instead).`
144
+ );
145
+ }
146
+ }
147
+ }
121
148
  const manifestVersion = profile === "tokens-only" ? void 0 : readManifest(cwd).basaltVersion;
122
149
  const at = install.dir === null ? "" : `${relative(cwd, install.dir)} (${install.version})`;
123
150
  if (install.dir === null) {
@@ -253,7 +280,7 @@ basalt-ui doctor \u2014 ${cwd}
253
280
  );
254
281
  } else {
255
282
  const upgrade = [
256
- ["tsconfig-base: tsconfig.json extends the shipped preset", tsconfigBaseCheck(cwd)],
283
+ ["tsconfig-base: tsconfig.json extends the shipped preset", tsconfigBaseCheck(appDir)],
257
284
  [
258
285
  "retired-lint-rules: .oxlintrc.json sets no retired basalt/* rule",
259
286
  retiredLintRulesCheck(cwd, install)
@@ -264,7 +291,12 @@ basalt-ui doctor \u2014 ${cwd}
264
291
  ],
265
292
  [
266
293
  "app-wiring: the entry mounts BasaltProvider and the router tree is generated",
267
- appWiringCheck(cwd)
294
+ appWiringCheck(appDir)
295
+ ],
296
+ ["exact-peers: installed exact-pinned peers match basalt-ui", exactPeersCheck(cwd, install)],
297
+ [
298
+ "config-keys: the basalt config block sets only keys basalt-ui reads",
299
+ unknownConfigKeysCheck(cwd)
268
300
  ]
269
301
  ];
270
302
  for (const [ok, check] of upgrade) {
@@ -199,6 +199,22 @@ export declare const ROUTER_SEED_DESTS: {
199
199
  readonly root: "src/routes/__root.tsx";
200
200
  readonly main: "src/main.tsx";
201
201
  };
202
+ /** A router seed's dest, relative to the install root: under the app dir when it is not the root. */
203
+ export declare function routerSeedDest(appRel: string, seed: keyof typeof ROUTER_SEED_DESTS): string;
204
+ /** The app directory a command's app-scoped half targets: `rel` is '' when it is the install root. */
205
+ export type AppResolution = {
206
+ dir: string;
207
+ rel: string;
208
+ } | {
209
+ error: string;
210
+ };
211
+ /**
212
+ * Where the Vite app lives relative to the install root `root` (manifest, `.claude/`, AGENTS block).
213
+ * ONE resolution for `doctor`, `sync` and `init`, in order: `basalt.app` in the root package.json;
214
+ * else the single workspace package that depends on basalt-ui AND has a Vite entry; else `root`
215
+ * itself (a single-package app). Two such packages is an error — the choice is not inferable.
216
+ */
217
+ export declare function resolveAppDir(root: string): AppResolution;
202
218
  /**
203
219
  * Where the seed engine is running relative to the consumer's git repo — decides which
204
220
  * repo-root-shaped seeds are safe to write. `init`/`sync` compute it once per run via
@@ -214,6 +230,10 @@ export type PlacementFlags = {
214
230
  /** Absolute path of the detected repo root. Meaningful only when `isPackageRepoRoot` is false —
215
231
  * otherwise it equals the package dir and is never read. */
216
232
  repoRoot: string;
233
+ /** The app directory relative to the install root ('' = the root itself); see {@link resolveAppDir}. */
234
+ appRel: string;
235
+ /** Non-null when the app directory is ambiguous or invalid: commands refuse rather than guess. */
236
+ appError: string | null;
217
237
  };
218
238
  /** The default placement — today's single-package-at-repo-root behaviour, unconditionally. */
219
239
  export declare const ROOT_PLACEMENT: PlacementFlags;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -351,6 +351,37 @@ const ROUTER_SEED_DESTS = {
351
351
  root: "src/routes/__root.tsx",
352
352
  main: "src/main.tsx"
353
353
  };
354
+ function routerSeedDest(appRel, seed) {
355
+ return appRel === "" ? ROUTER_SEED_DESTS[seed] : `${appRel}/${ROUTER_SEED_DESTS[seed]}`;
356
+ }
357
+ const appRelative = (root, dir) => relativePosix(root, dir).replace(/^\.\/?/, "");
358
+ const hasViteEntry = (dir) => existsSync(resolve(dir, "src/main.tsx")) || existsSync(resolve(dir, "index.html"));
359
+ function resolveAppDir(root) {
360
+ const self = { dir: root, rel: "" };
361
+ const explicit = readBasaltConfig(root).app;
362
+ if (explicit !== void 0) {
363
+ const dir = resolve(root, explicit);
364
+ const rel = appRelative(root, dir);
365
+ if (rel.startsWith("..") || !existsSync(resolve(dir, "package.json"))) {
366
+ return {
367
+ error: `basalt.app "${explicit}" must be a directory with a package.json inside ${root}.`
368
+ };
369
+ }
370
+ return dir === root ? self : { dir, rel };
371
+ }
372
+ const candidates = collectWorkspacePackages(root).packages.filter(
373
+ (pkg) => pkg.dir !== root && hasDependency(pkg.dir, "basalt-ui") && hasViteEntry(pkg.dir)
374
+ );
375
+ if (candidates.length === 0) return self;
376
+ const [only] = candidates;
377
+ if (candidates.length === 1 && only !== void 0) {
378
+ return { dir: only.dir, rel: appRelative(root, only.dir) };
379
+ }
380
+ const names = candidates.map((pkg) => appRelative(root, pkg.dir)).join(", ");
381
+ return {
382
+ error: `${candidates.length} workspace packages depend on basalt-ui and have a Vite entry (${names}) \u2014 cannot tell which is the app. Set \`basalt.app\` (e.g. \`"basalt": { "app": "apps/web" }\`) in the root package.json.`
383
+ };
384
+ }
354
385
  function hasDependency(cwd, pkgName) {
355
386
  try {
356
387
  const pkg = JSON.parse(readFileSync(resolve(cwd, "package.json"), "utf8"));
@@ -363,7 +394,9 @@ function hasDependency(cwd, pkgName) {
363
394
  }
364
395
  const ROOT_PLACEMENT = {
365
396
  isPackageRepoRoot: true,
366
- repoRoot: ""
397
+ repoRoot: "",
398
+ appRel: "",
399
+ appError: null
367
400
  };
368
401
  function findRepoRoot(dir) {
369
402
  let current = dir;
@@ -384,9 +417,12 @@ function oxlintBinResolves(dir) {
384
417
  }
385
418
  function resolvePlacement(cwd) {
386
419
  const repoRoot = findRepoRoot(cwd);
420
+ const app = resolveAppDir(cwd);
387
421
  return {
388
422
  isPackageRepoRoot: repoRoot === cwd,
389
- repoRoot
423
+ repoRoot,
424
+ appRel: "error" in app ? "" : app.rel,
425
+ appError: "error" in app ? app.error : null
390
426
  };
391
427
  }
392
428
  function managedFiles(placement = ROOT_PLACEMENT) {
@@ -480,7 +516,7 @@ pre-commit:
480
516
  `
481
517
  };
482
518
  const rootRoute = {
483
- dest: ROUTER_SEED_DESTS.root,
519
+ dest: routerSeedDest(placement.appRel, "root"),
484
520
  mode: "seed",
485
521
  source: "agent/templates/__root.tpl",
486
522
  render: (ctx) => {
@@ -490,13 +526,13 @@ pre-commit:
490
526
  }
491
527
  };
492
528
  const navModule = {
493
- dest: ROUTER_SEED_DESTS.nav,
529
+ dest: routerSeedDest(placement.appRel, "nav"),
494
530
  mode: "seed",
495
531
  source: "agent/templates/nav.tpl",
496
532
  render: (ctx) => readSource(ctx.pkgRoot, "agent/templates/nav.tpl")
497
533
  };
498
534
  const mainEntry = {
499
- dest: ROUTER_SEED_DESTS.main,
535
+ dest: routerSeedDest(placement.appRel, "main"),
500
536
  mode: "seed",
501
537
  source: "agent/templates/main.tpl",
502
538
  render: (ctx) => readSource(ctx.pkgRoot, "agent/templates/main.tpl")
@@ -1002,11 +1038,13 @@ export {
1002
1038
  relativePosix,
1003
1039
  renderContext,
1004
1040
  reportPrune,
1041
+ resolveAppDir,
1005
1042
  resolveGuardConfig,
1006
1043
  resolvePlacement,
1007
1044
  resolveProjectDir,
1008
1045
  retiredManagedDests,
1009
1046
  rootsNotices,
1047
+ routerSeedDest,
1010
1048
  run,
1011
1049
  sameFile,
1012
1050
  scanBlock,
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,SAAS,CAAA;AA4D7D,yGAAyG;AACzG,wBAAgB,IAAI,CAAC,GAAG,GAAE,MAAsB,EAAE,aAAa,GAAE,aAAkB,GAAG,MAAM,CAkL3F"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,SAAS,CAAA;AA6D7D,yGAAyG;AACzG,wBAAgB,IAAI,CAAC,GAAG,GAAE,MAAsB,EAAE,aAAa,GAAE,aAAkB,GAAG,MAAM,CA0L3F"}
package/dist/cli/init.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  declaredProfile,
7
7
  readBasaltConfig,
8
8
  ROUTER_SEED_DESTS,
9
+ routerSeedDest,
9
10
  findBasaltInstall,
10
11
  managedFiles,
11
12
  mergeOxlintExtends,
@@ -47,6 +48,11 @@ function init(cwd = process.cwd(), scaffoldFlags = {}) {
47
48
  `\u26A0 basalt-ui init: basalt-ui does not resolve from ${cwd} or any directory above it \u2014 the seeded \`extends\` paths and CI steps are being written against the repo root by assumption. Install basalt-ui here (or at the repo root) and re-run \`basalt-ui init\` so they resolve.`
48
49
  );
49
50
  }
51
+ const appError = resolvePlacement(cwd).appError;
52
+ if (appError !== null) {
53
+ console.error(`\u2716 basalt-ui init: ${appError}`);
54
+ return 1;
55
+ }
50
56
  const rootsState = reconcileRoots(cwd, { write: true });
51
57
  const ctx = renderContext(cwd);
52
58
  const lintScript = `oxlint . && ${ctx.vars.BASALT_BIN} check-theme`;
@@ -74,7 +80,7 @@ function init(cwd = process.cwd(), scaffoldFlags = {}) {
74
80
  const manifest = readManifest(cwd);
75
81
  const placement = resolvePlacement(cwd);
76
82
  const tokensOnly = declaredProfile(readBasaltConfig(cwd), []) === "tokens-only";
77
- const routerSeeds = Object.values(ROUTER_SEED_DESTS);
83
+ const routerSeeds = Object.keys(ROUTER_SEED_DESTS).map((seed) => routerSeedDest(placement.appRel, seed));
78
84
  const files = managedFiles(placement).filter((f) => !(tokensOnly && routerSeeds.includes(f.dest)));
79
85
  const writtenFiles = [];
80
86
  const keptFiles = [];
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/cli/sync.ts"],"names":[],"mappings":"AAwCA,gGAAgG;AAChG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAGjE;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,mGAAmG;IACnG,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC1B,CAAA;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrF;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,IAAI,GAAE,WAAgB,EAAE,aAAa,GAAE,MAAsB,GAAG,MAAM,CA0L1F"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/cli/sync.ts"],"names":[],"mappings":"AAwCA,gGAAgG;AAChG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAGjE;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,mGAAmG;IACnG,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC1B,CAAA;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrF;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,IAAI,GAAE,WAAgB,EAAE,aAAa,GAAE,MAAsB,GAAG,MAAM,CAgM1F"}
package/dist/cli/sync.js CHANGED
@@ -63,6 +63,11 @@ function sync(opts = {}, invocationCwd = process.cwd()) {
63
63
  );
64
64
  return 0;
65
65
  }
66
+ const appError = resolvePlacement(cwd).appError;
67
+ if (appError !== null) {
68
+ console.error(`\u2716 basalt-ui sync: ${appError}`);
69
+ return 1;
70
+ }
66
71
  if (!existsSync(resolve(cwd, MANIFEST_PATH))) {
67
72
  const elsewhere = findManifestAbove(cwd);
68
73
  const wouldWrite = managedFiles(resolvePlacement(cwd)).length;
@@ -190,18 +190,19 @@ export declare function seriesTokens<const T extends SeriesMap>(map: T, prefix?:
190
190
  * not the widened SeriesMap, enabling downstream exact-key checks in groupTokens / seriesTokens).
191
191
  */
192
192
  export declare function defineSeries<const T extends SeriesMap>(map: T): T;
193
- /** Runtime key of the {@link SeriesGroup} brand. */
194
- declare const SERIES_GROUP: unique symbol;
193
+ declare const GROUP_REF: unique symbol;
194
+ /** A `var(--vx-<name>-<key>)` ref minted by `groupTokens`. The brand lives on the VALUES, so it never shows up in `keyof` the group. */
195
+ export type GroupRef = string & {
196
+ readonly [GROUP_REF]: true;
197
+ };
195
198
  /**
196
- * The brand `groupTokens` puts on its return value. `BuildPaletteOpts.groups` takes these, so the
197
- * `${name}-` key that emits the variables and the one that references them come from one call and
198
- * cannot be spelled two ways.
199
+ * What `groupTokens` returns. `BuildPaletteOpts.groups` takes these, so the `${name}-` key that
200
+ * emits the variables and the one that references them come from one call and cannot be spelled
201
+ * two ways. A bare series map (object values) and a plain string record (unbranded values) are
202
+ * both rejected; the runtime payload sits under the non-enumerable {@link SERIES_GROUP} symbol.
199
203
  */
200
204
  export type SeriesGroup = {
201
- readonly [SERIES_GROUP]: {
202
- readonly prefix: string;
203
- readonly map: SeriesMap;
204
- };
205
+ readonly [key: string]: GroupRef;
205
206
  };
206
207
  /**
207
208
  * Build a single namespaced group's token refs (`var(--vx-<name>-<key>)`).
@@ -210,8 +211,8 @@ export type SeriesGroup = {
210
211
  * error at the call site rather than a silent `Record<string, string>` widening.
211
212
  */
212
213
  export declare function groupTokens<const T extends SeriesMap>(name: string, map: T): {
213
- [K in keyof T]: string;
214
- } & SeriesGroup;
214
+ readonly [K in keyof T]: GroupRef;
215
+ };
215
216
  /**
216
217
  * The attribute + values the per-scheme blocks key off. Defaults reproduce Mantine's own toggle
217
218
  * (`data-mantine-color-scheme` = `dark` / `light`), so a consumer only names the axis it differs
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/tokens/core.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/tokens/core.ts"],"names":[],"mappings":"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"}
@@ -596,7 +596,15 @@ function buildPaletteCss(opts = {}, data = buildPaletteData()) {
596
596
  const derived = extraDerived ? `${derivedBlock}
597
597
  ${extraDerived}` : derivedBlock;
598
598
  const side = (s) => {
599
- const extra = groups.map((group) => groupSide(group[SERIES_GROUP].prefix, group[SERIES_GROUP].map, s)).join("\n");
599
+ const extra = groups.map((group) => {
600
+ const payload = group[SERIES_GROUP];
601
+ if (!payload) {
602
+ throw new Error(
603
+ "BuildPaletteOpts.groups: every group must come from groupTokens(name, map)"
604
+ );
605
+ }
606
+ return groupSide(payload.prefix, payload.map, s);
607
+ }).join("\n");
600
608
  const framework = frameworkPrimitives(s, data, legacyAliases);
601
609
  return extra ? `${framework}
602
610
  ${extra}` : framework;
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # basalt-ui v2.0.0
1
+ # basalt-ui v2.0.2
2
2
  # Opinionated framework for Mantine-based React apps — theme, app shell, TanStack Charts, toolchain presets
3
3
  #
4
4
  # Entry points: use the exact import specifier below; do not guess.
@@ -13,7 +13,7 @@
13
13
  # A subpath's `Install:` line is its required peers; `OptionalPeers:` are import()-only, install on use.
14
14
 
15
15
  Package: basalt-ui
16
- Version: 2.0.0
16
+ Version: 2.0.2
17
17
 
18
18
  ## basalt-ui
19
19
  Import: import { ... } from 'basalt-ui'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "basalt-ui",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Opinionated framework for Mantine-based React apps — theme, app shell, TanStack Charts, toolchain presets",
5
5
  "keywords": [
6
6
  "charts",
@@ -165,9 +165,9 @@
165
165
  },
166
166
  "peerDependencies": {
167
167
  "@ai-sdk/provider": "^4.0.26",
168
- "@fontsource-variable/hubot-sans": "5.2.8",
169
- "@fontsource-variable/jetbrains-mono": "5.2.8",
170
- "@fontsource-variable/nunito-sans": "5.2.7",
168
+ "@fontsource-variable/hubot-sans": "5.3.0",
169
+ "@fontsource-variable/jetbrains-mono": "5.3.0",
170
+ "@fontsource-variable/nunito-sans": "5.3.0",
171
171
  "@mantine/core": "^9.7.0",
172
172
  "@mantine/dates": "^9.7.0",
173
173
  "@mantine/form": "^9.7.0",
package/src/cli/config.ts CHANGED
@@ -4,6 +4,16 @@ import type { GuardKind, GuardSeverity } from '../guard'
4
4
 
5
5
  /** Shape of the optional `"basalt"` key in a consumer's package.json. */
6
6
  export type BasaltConfig = {
7
+ /**
8
+ * The Vite app's directory relative to the install root, for a workspace monorepo whose manifest,
9
+ * `.claude/` and AGENTS block live at the root. App-scoped checks (`tsconfig-base`, `app-wiring`)
10
+ * and the router seeds target it. Default: the single workspace package that depends on basalt-ui
11
+ * and has a Vite entry, else the root itself.
12
+ *
13
+ * @example
14
+ * { app: 'apps/dashboard' }
15
+ */
16
+ app?: string
7
17
  /** Source roots to scan. Default: `['src']`. Set explicitly for a monorepo layout. */
8
18
  roots?: string[]
9
19
  /** Files exempt from the scan (they ARE the palette source). Default: argo's exempt set. */
@@ -178,3 +188,15 @@ export const DEFAULT_ROOTS = [DEFAULT_ROOT]
178
188
  export function resolveRoots(cfg: BasaltConfig): string[] {
179
189
  return cfg.roots?.length ? cfg.roots : DEFAULT_ROOTS
180
190
  }
191
+
192
+ /** Every `BasaltConfig` key, as data for `doctor`'s `config-keys` (a test pins it to the type). */
193
+ export const BASALT_CONFIG_KEYS = (
194
+ 'app roots exempt profile include spacingSteps severity forbiddenAccents accentHue rawRadius rawSurface ' +
195
+ 'cardWithBorder offSystemSurfaceVar mantineShadeIndex rawHtmlLayout inlineSpacing inlineDisplay ' +
196
+ 'rawMotionValue rawFormControl sub16InputFont seriesModulePath exemptRules aiMajorSkewReason'
197
+ ).split(' ') as (keyof BasaltConfig)[]
198
+
199
+ /** Keys removed from `BasaltConfig`, with the reason `doctor` prints. */
200
+ export const RETIRED_CONFIG_KEYS: Readonly<Record<string, string>> = {
201
+ chartMissingAriaLabel: 'its guard kind is gone in 2.0 (`ariaLabel` is required on a chart now)',
202
+ }
@@ -6,6 +6,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
6
6
  import { createRequire } from 'node:module'
7
7
  import { dirname, relative, resolve } from 'node:path'
8
8
 
9
+ import { BASALT_CONFIG_KEYS, RETIRED_CONFIG_KEYS } from './config'
9
10
  import type { BasaltConfig } from './config'
10
11
  import { parseJsonc, readIfExists, scannableFiles } from './index'
11
12
  import type { BasaltInstall } from './index'
@@ -390,3 +391,67 @@ export function appWiringCheck(cwd: string): Check {
390
391
  }
391
392
  })
392
393
  }
394
+
395
+ /**
396
+ * `config-keys`: a key in package.json's `basalt` object that `BasaltConfig` does not declare is
397
+ * silently ignored, so a key retired in 2.0 keeps looking configured. Reads the raw file, not
398
+ * `readBasaltConfig`, which is typed and would hide the very keys this looks for.
399
+ */
400
+ export function unknownConfigKeysCheck(cwd: string): Check {
401
+ return guarded('config-keys', () => {
402
+ const raw = readIfExists(resolve(cwd, 'package.json'))
403
+ const block = raw === null ? null : (JSON.parse(raw) as { basalt?: unknown }).basalt
404
+ if (typeof block !== 'object' || block === null || Array.isArray(block)) return null
405
+ const known = new Set<string>(BASALT_CONFIG_KEYS)
406
+ const unknown = Object.keys(block).filter((k) => !known.has(k))
407
+ if (unknown.length === 0) return null
408
+ const named = unknown.map(
409
+ (k) => `basalt.${k} (${RETIRED_CONFIG_KEYS[k] ?? 'not a basalt key'})`,
410
+ )
411
+ return {
412
+ fail: `config-keys: package.json sets keys basalt-ui does not read: ${named.join('; ')}. Fix: delete each key.`,
413
+ }
414
+ })
415
+ }
416
+
417
+ const EXACT_VERSION = /^\d+\.\d+\.\d+$/
418
+
419
+ /** `node_modules/<pkg>/package.json`'s version, searching `cwd` then every ancestor. */
420
+ function installedVersion(cwd: string, pkg: string): string | null {
421
+ for (let dir = cwd; ; dir = dirname(dir)) {
422
+ const raw = readIfExists(resolve(dir, 'node_modules', pkg, 'package.json'))
423
+ if (raw !== null) return (JSON.parse(raw) as { version?: string }).version ?? null
424
+ if (dirname(dir) === dir) return null
425
+ }
426
+ }
427
+
428
+ /**
429
+ * `exact-peers`: basalt-ui pins some peers to one exact version, and `bun add` accepts any other
430
+ * version without a word. Compares each installed one against the install's declared pin; a peer
431
+ * that is not installed is not this check's business (optional peers are legitimately absent).
432
+ */
433
+ export function exactPeersCheck(cwd: string, install: BasaltInstall): Check {
434
+ const id = 'exact-peers'
435
+ if (install.dir === null)
436
+ return { skip: `${id}: basalt-ui does not resolve, so its peers cannot be read.` }
437
+ const dir = install.dir
438
+ return guarded(id, () => {
439
+ const raw = readIfExists(resolve(dir, 'package.json'))
440
+ const peers =
441
+ (raw === null
442
+ ? {}
443
+ : (JSON.parse(raw) as { peerDependencies?: Record<string, string> }).peerDependencies) ?? {}
444
+ const off = Object.entries(peers)
445
+ .filter(([, range]) => EXACT_VERSION.test(range))
446
+ .flatMap(([pkg, pin]) => {
447
+ const have = installedVersion(cwd, pkg)
448
+ return have === null || have === pin
449
+ ? []
450
+ : [`${pkg}@${have} (basalt-ui pins ${pin}): \`bun add ${pkg}@${pin}\``]
451
+ })
452
+ if (off.length === 0) return null
453
+ return {
454
+ fail: `${id}: installed peers differ from basalt-ui's exact pins. Fix: ${off.join('; ')}.`,
455
+ }
456
+ })
457
+ }
package/src/cli/doctor.ts CHANGED
@@ -26,7 +26,9 @@ import {
26
26
  readBasaltConfig,
27
27
  readIfExists,
28
28
  readManifest,
29
+ resolveAppDir,
29
30
  resolveProjectDir,
31
+ ROUTER_SEED_DESTS,
30
32
  sameFile,
31
33
  scannableFiles,
32
34
  scanBlock,
@@ -36,8 +38,10 @@ import { resolveRoots } from './config'
36
38
  import {
37
39
  appWiringCheck,
38
40
  droppedSubpathsCheck,
41
+ exactPeersCheck,
39
42
  retiredLintRulesCheck,
40
43
  tsconfigBaseCheck,
44
+ unknownConfigKeysCheck,
41
45
  } from './doctor-upgrade'
42
46
  import type { Check } from './doctor-upgrade'
43
47
  import { findManifestAbove, parentInstallAdvice } from './sync'
@@ -120,7 +124,8 @@ function resolveAiMajorSkewReason(cfg: BasaltConfig): {
120
124
  * 4. `oxlint-preset`: `.oxlintrc.json` extends the shipped preset AND that path resolves.
121
125
  * 7. `ai-major-parity`: the `ai` major agrees across this package.json's dependency fields
122
126
  * (exempt via `basalt.aiMajorSkewReason`; a stale reason warns).
123
- * 8-11. `tsconfig-base`, `retired-lint-rules`, `dropped-subpaths`, `app-wiring` (./doctor-upgrade);
127
+ * 8-13. `tsconfig-base`, `retired-lint-rules`, `dropped-subpaths`, `app-wiring`, `exact-peers`,
128
+ * `config-keys` (./doctor-upgrade);
124
129
  * a check that cannot run is SKIPPED and exits non-zero.
125
130
  *
126
131
  * Warning (non-fatal):
@@ -132,7 +137,18 @@ function resolveAiMajorSkewReason(cfg: BasaltConfig): {
132
137
  export function doctor(invocationCwd: string = process.cwd(), flags: string[] = []): number {
133
138
  if (conflictingProfileFlags('doctor', flags)) return 1
134
139
  const project = resolveProjectDir(invocationCwd)
135
- const cwd = project.dir
140
+ // Inside the app package of a monorepo the manifest is above: read the install root instead.
141
+ const aboveRoot =
142
+ project.relocatedFrom === null && !existsSync(resolve(project.dir, MANIFEST_PATH))
143
+ ? findManifestAbove(project.dir)
144
+ : null
145
+ const insideApp =
146
+ aboveRoot !== null &&
147
+ (() => {
148
+ const app = resolveAppDir(aboveRoot)
149
+ return !('error' in app) && app.dir === project.dir
150
+ })()
151
+ const cwd = insideApp && aboveRoot !== null ? aboveRoot : project.dir
136
152
  const cfg = readBasaltConfig(cwd)
137
153
  const result: DoctorResult = { hardFailures: 0, warnings: 0, skipped: 0 }
138
154
  const lines: string[] = [`\nbasalt-ui doctor — ${cwd}\n`]
@@ -157,6 +173,11 @@ export function doctor(invocationCwd: string = process.cwd(), flags: string[] =
157
173
  lines.push(` → BASALT_CWD relocated from ${project.relocatedFrom} to ${cwd}.\n`)
158
174
  }
159
175
 
176
+ if (insideApp)
177
+ lines.push(` → ${project.dir} is the app package; checking the install root ${cwd}.\n`)
178
+
179
+ const app = resolveAppDir(cwd)
180
+ const appDir = 'error' in app ? cwd : app.dir
160
181
  const profile = declaredProfile(cfg, flags)
161
182
  if (profile === 'tokens-only') {
162
183
  lines.push(
@@ -189,6 +210,22 @@ export function doctor(invocationCwd: string = process.cwd(), flags: string[] =
189
210
 
190
211
  const install = findBasaltInstall(cwd)
191
212
 
213
+ // ── App placement: the agent layer is at the root, the app is where `resolveAppDir` says ────
214
+ if ('error' in app) {
215
+ fail(`app-dir: ${app.error}`)
216
+ } else if (app.rel !== '') {
217
+ pass(`app-dir: the app is ${app.rel} (agent layer and manifest stay at the install root)`)
218
+ const ledger = readManifest(cwd).files
219
+ for (const seed of Object.values(ROUTER_SEED_DESTS)) {
220
+ if (ledger[seed] !== undefined && existsSync(resolve(cwd, seed))) {
221
+ warn(
222
+ `app-dir: ${seed} was seeded at the install root by an earlier \`sync\`, but the app is ` +
223
+ `${app.rel} — it is dead code there. Delete it (\`sync\` seeds ${app.rel}/${seed} instead).`,
224
+ )
225
+ }
226
+ }
227
+ }
228
+
192
229
  // ── Hard check 2: install matches the manifest, and doctor says WHERE (the AGENTS-block's claim)
193
230
  const manifestVersion = profile === 'tokens-only' ? undefined : readManifest(cwd).basaltVersion
194
231
  const at = install.dir === null ? '' : `${relative(cwd, install.dir)} (${install.version})`
@@ -370,14 +407,14 @@ export function doctor(invocationCwd: string = process.cwd(), flags: string[] =
370
407
  pass('ai-major-parity: this package.json declares no ai dependency — nothing to compare')
371
408
  }
372
409
 
373
- // ── Hard checks 8-11: strictness and the 1.x → 2.0 upgrade path (./doctor-upgrade) ──────────
410
+ // ── Hard checks 8-13: strictness and the 1.x → 2.0 upgrade path (./doctor-upgrade) ──────────
374
411
  if (profile === 'tokens-only') {
375
412
  pass(
376
413
  'tsconfig-base / retired-lint-rules / dropped-subpaths / app-wiring: n/a — a tokens-only consumer',
377
414
  )
378
415
  } else {
379
416
  const upgrade: [string, Check][] = [
380
- ['tsconfig-base: tsconfig.json extends the shipped preset', tsconfigBaseCheck(cwd)],
417
+ ['tsconfig-base: tsconfig.json extends the shipped preset', tsconfigBaseCheck(appDir)],
381
418
  [
382
419
  'retired-lint-rules: .oxlintrc.json sets no retired basalt/* rule',
383
420
  retiredLintRulesCheck(cwd, install),
@@ -388,7 +425,12 @@ export function doctor(invocationCwd: string = process.cwd(), flags: string[] =
388
425
  ],
389
426
  [
390
427
  'app-wiring: the entry mounts BasaltProvider and the router tree is generated',
391
- appWiringCheck(cwd),
428
+ appWiringCheck(appDir),
429
+ ],
430
+ ['exact-peers: installed exact-pinned peers match basalt-ui', exactPeersCheck(cwd, install)],
431
+ [
432
+ 'config-keys: the basalt config block sets only keys basalt-ui reads',
433
+ unknownConfigKeysCheck(cwd),
392
434
  ],
393
435
  ]
394
436
  for (const [ok, check] of upgrade) {
package/src/cli/index.ts CHANGED
@@ -654,6 +654,56 @@ export const ROUTER_SEED_DESTS = {
654
654
  main: 'src/main.tsx',
655
655
  } as const
656
656
 
657
+ /** A router seed's dest, relative to the install root: under the app dir when it is not the root. */
658
+ export function routerSeedDest(appRel: string, seed: keyof typeof ROUTER_SEED_DESTS): string {
659
+ return appRel === '' ? ROUTER_SEED_DESTS[seed] : `${appRel}/${ROUTER_SEED_DESTS[seed]}`
660
+ }
661
+
662
+ /** The app directory a command's app-scoped half targets: `rel` is '' when it is the install root. */
663
+ export type AppResolution = { dir: string; rel: string } | { error: string }
664
+
665
+ /** `dir` relative to `root` without relativePosix's leading `./` ('' for the root itself). */
666
+ const appRelative = (root: string, dir: string): string =>
667
+ relativePosix(root, dir).replace(/^\.\/?/, '')
668
+
669
+ const hasViteEntry = (dir: string): boolean =>
670
+ existsSync(resolve(dir, 'src/main.tsx')) || existsSync(resolve(dir, 'index.html'))
671
+
672
+ /**
673
+ * Where the Vite app lives relative to the install root `root` (manifest, `.claude/`, AGENTS block).
674
+ * ONE resolution for `doctor`, `sync` and `init`, in order: `basalt.app` in the root package.json;
675
+ * else the single workspace package that depends on basalt-ui AND has a Vite entry; else `root`
676
+ * itself (a single-package app). Two such packages is an error — the choice is not inferable.
677
+ */
678
+ export function resolveAppDir(root: string): AppResolution {
679
+ const self = { dir: root, rel: '' }
680
+ const explicit = readBasaltConfig(root).app
681
+ if (explicit !== undefined) {
682
+ const dir = resolve(root, explicit)
683
+ const rel = appRelative(root, dir)
684
+ if (rel.startsWith('..') || !existsSync(resolve(dir, 'package.json'))) {
685
+ return {
686
+ error: `basalt.app "${explicit}" must be a directory with a package.json inside ${root}.`,
687
+ }
688
+ }
689
+ return dir === root ? self : { dir, rel }
690
+ }
691
+ const candidates = collectWorkspacePackages(root).packages.filter(
692
+ (pkg) => pkg.dir !== root && hasDependency(pkg.dir, 'basalt-ui') && hasViteEntry(pkg.dir),
693
+ )
694
+ if (candidates.length === 0) return self
695
+ const [only] = candidates
696
+ if (candidates.length === 1 && only !== undefined) {
697
+ return { dir: only.dir, rel: appRelative(root, only.dir) }
698
+ }
699
+ const names = candidates.map((pkg) => appRelative(root, pkg.dir)).join(', ')
700
+ return {
701
+ error:
702
+ `${candidates.length} workspace packages depend on basalt-ui and have a Vite entry (${names}) — ` +
703
+ 'cannot tell which is the app. Set `basalt.app` (e.g. `"basalt": { "app": "apps/web" }`) in the root package.json.',
704
+ }
705
+ }
706
+
657
707
  /** True when `pkgName` appears in the consumer's dependencies, devDependencies, or peerDependencies. */
658
708
  function hasDependency(cwd: string, pkgName: string): boolean {
659
709
  try {
@@ -687,12 +737,18 @@ export type PlacementFlags = {
687
737
  /** Absolute path of the detected repo root. Meaningful only when `isPackageRepoRoot` is false —
688
738
  * otherwise it equals the package dir and is never read. */
689
739
  repoRoot: string
740
+ /** The app directory relative to the install root ('' = the root itself); see {@link resolveAppDir}. */
741
+ appRel: string
742
+ /** Non-null when the app directory is ambiguous or invalid: commands refuse rather than guess. */
743
+ appError: string | null
690
744
  }
691
745
 
692
746
  /** The default placement — today's single-package-at-repo-root behaviour, unconditionally. */
693
747
  export const ROOT_PLACEMENT: PlacementFlags = {
694
748
  isPackageRepoRoot: true,
695
749
  repoRoot: '',
750
+ appRel: '',
751
+ appError: null,
696
752
  }
697
753
 
698
754
  /**
@@ -725,9 +781,12 @@ export function oxlintBinResolves(dir: string): boolean {
725
781
  /** Resolve a run's placement flags once, from the consumer cwd. */
726
782
  export function resolvePlacement(cwd: string): PlacementFlags {
727
783
  const repoRoot = findRepoRoot(cwd)
784
+ const app = resolveAppDir(cwd)
728
785
  return {
729
786
  isPackageRepoRoot: repoRoot === cwd,
730
787
  repoRoot,
788
+ appRel: 'error' in app ? '' : app.rel,
789
+ appError: 'error' in app ? app.error : null,
731
790
  }
732
791
  }
733
792
 
@@ -854,7 +913,7 @@ export function managedFiles(placement: PlacementFlags = ROOT_PLACEMENT): Manage
854
913
 
855
914
  /** TanStack Router root route with QueryClient context wiring (seed — consumer-owned after init). */
856
915
  const rootRoute: ManagedFile = {
857
- dest: ROUTER_SEED_DESTS.root,
916
+ dest: routerSeedDest(placement.appRel, 'root'),
858
917
  mode: 'seed',
859
918
  source: 'agent/templates/__root.tpl',
860
919
  render: (ctx) => {
@@ -866,7 +925,7 @@ export function managedFiles(placement: PlacementFlags = ROOT_PLACEMENT): Manage
866
925
 
867
926
  /** The leaf `NAV` module `__root.tsx` imports (seed — consumer-owned after init). */
868
927
  const navModule: ManagedFile = {
869
- dest: ROUTER_SEED_DESTS.nav,
928
+ dest: routerSeedDest(placement.appRel, 'nav'),
870
929
  mode: 'seed',
871
930
  source: 'agent/templates/nav.tpl',
872
931
  render: (ctx) => readSource(ctx.pkgRoot, 'agent/templates/nav.tpl'),
@@ -874,7 +933,7 @@ export function managedFiles(placement: PlacementFlags = ROOT_PLACEMENT): Manage
874
933
 
875
934
  /** The 2.0 entry: mounts the router and declares the `Register` augmentation `defineNav` needs. */
876
935
  const mainEntry: ManagedFile = {
877
- dest: ROUTER_SEED_DESTS.main,
936
+ dest: routerSeedDest(placement.appRel, 'main'),
878
937
  mode: 'seed',
879
938
  source: 'agent/templates/main.tpl',
880
939
  render: (ctx) => readSource(ctx.pkgRoot, 'agent/templates/main.tpl'),
package/src/cli/init.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  declaredProfile,
14
14
  readBasaltConfig,
15
15
  ROUTER_SEED_DESTS,
16
+ routerSeedDest,
16
17
  findBasaltInstall,
17
18
  managedFiles,
18
19
  mergeOxlintExtends,
@@ -82,6 +83,12 @@ export function init(cwd: string = process.cwd(), scaffoldFlags: ScaffoldFlags =
82
83
  )
83
84
  }
84
85
 
86
+ const appError = resolvePlacement(cwd).appError
87
+ if (appError !== null) {
88
+ console.error(`✖ basalt-ui init: ${appError}`)
89
+ return 1
90
+ }
91
+
85
92
  const rootsState = reconcileRoots(cwd, { write: true })
86
93
 
87
94
  // Built AFTER the roots patch — every roots-derived template variable reads the key just written.
@@ -120,7 +127,9 @@ export function init(cwd: string = process.cwd(), scaffoldFlags: ScaffoldFlags =
120
127
  const placement = resolvePlacement(cwd)
121
128
  // A tokens-only consumer has no use for the Mantine/React scaffolds (doctor skips them there too).
122
129
  const tokensOnly = declaredProfile(readBasaltConfig(cwd), []) === 'tokens-only'
123
- const routerSeeds: string[] = Object.values(ROUTER_SEED_DESTS)
130
+ const routerSeeds: string[] = (
131
+ Object.keys(ROUTER_SEED_DESTS) as (keyof typeof ROUTER_SEED_DESTS)[]
132
+ ).map((seed) => routerSeedDest(placement.appRel, seed))
124
133
  const files = managedFiles(placement).filter((f) => !(tokensOnly && routerSeeds.includes(f.dest)))
125
134
 
126
135
  const writtenFiles: string[] = []
package/src/cli/sync.ts CHANGED
@@ -101,6 +101,12 @@ export function sync(opts: SyncOptions = {}, invocationCwd: string = process.cwd
101
101
  return 0
102
102
  }
103
103
 
104
+ const appError = resolvePlacement(cwd).appError
105
+ if (appError !== null) {
106
+ console.error(`✖ basalt-ui sync: ${appError}`)
107
+ return 1
108
+ }
109
+
104
110
  // Refuse BEFORE reconcileRoots, which writes `basalt.roots` into package.json.
105
111
  if (!existsSync(resolve(cwd, MANIFEST_PATH))) {
106
112
  const elsewhere = findManifestAbove(cwd)
@@ -820,15 +820,23 @@ export function defineSeries<const T extends SeriesMap>(map: T): T {
820
820
  /** Runtime key of the {@link SeriesGroup} brand. */
821
821
  const SERIES_GROUP: unique symbol = Symbol('basalt.series-group')
822
822
 
823
- /**
824
- * The brand `groupTokens` puts on its return value. `BuildPaletteOpts.groups` takes these, so the
825
- * `${name}-` key that emits the variables and the one that references them come from one call and
826
- * cannot be spelled two ways.
827
- */
828
- export type SeriesGroup = {
823
+ type GroupPayload = {
829
824
  readonly [SERIES_GROUP]: { readonly prefix: string; readonly map: SeriesMap }
830
825
  }
831
826
 
827
+ declare const GROUP_REF: unique symbol
828
+
829
+ /** A `var(--vx-<name>-<key>)` ref minted by `groupTokens`. The brand lives on the VALUES, so it never shows up in `keyof` the group. */
830
+ export type GroupRef = string & { readonly [GROUP_REF]: true }
831
+
832
+ /**
833
+ * What `groupTokens` returns. `BuildPaletteOpts.groups` takes these, so the `${name}-` key that
834
+ * emits the variables and the one that references them come from one call and cannot be spelled
835
+ * two ways. A bare series map (object values) and a plain string record (unbranded values) are
836
+ * both rejected; the runtime payload sits under the non-enumerable {@link SERIES_GROUP} symbol.
837
+ */
838
+ export type SeriesGroup = { readonly [key: string]: GroupRef }
839
+
832
840
  /**
833
841
  * Build a single namespaced group's token refs (`var(--vx-<name>-<key>)`).
834
842
  *
@@ -838,12 +846,12 @@ export type SeriesGroup = {
838
846
  export function groupTokens<const T extends SeriesMap>(
839
847
  name: string,
840
848
  map: T,
841
- ): { [K in keyof T]: string } & SeriesGroup {
849
+ ): { readonly [K in keyof T]: GroupRef } {
842
850
  const refs = seriesTokens(map, `${name}-`)
843
851
  // Non-enumerable, symbol-keyed: a series key can never collide with the brand, and
844
852
  // `Object.keys(refs)` still lists exactly the series.
845
853
  Object.defineProperty(refs, SERIES_GROUP, { value: { prefix: `${name}-`, map } })
846
- return refs as { [K in keyof T]: string } & SeriesGroup
854
+ return refs as { readonly [K in keyof T]: GroupRef }
847
855
  }
848
856
 
849
857
  /**
@@ -1024,7 +1032,15 @@ export function buildPaletteCss(
1024
1032
  const derived = extraDerived ? `${derivedBlock}\n${extraDerived}` : derivedBlock
1025
1033
  const side = (s: Side): string => {
1026
1034
  const extra = groups
1027
- .map((group) => groupSide(group[SERIES_GROUP].prefix, group[SERIES_GROUP].map, s))
1035
+ .map((group) => {
1036
+ const payload = (group as unknown as Partial<GroupPayload>)[SERIES_GROUP]
1037
+ if (!payload) {
1038
+ throw new Error(
1039
+ 'BuildPaletteOpts.groups: every group must come from groupTokens(name, map)',
1040
+ )
1041
+ }
1042
+ return groupSide(payload.prefix, payload.map, s)
1043
+ })
1028
1044
  .join('\n')
1029
1045
  const framework = frameworkPrimitives(s, data, legacyAliases)
1030
1046
  return extra ? `${framework}\n${extra}` : framework