@owlmeans/agent-skills 0.1.8

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.
Files changed (54) hide show
  1. package/README.md +92 -0
  2. package/agent-meta/instructions/shadcn-versions.instructions.md +46 -0
  3. package/agent-meta/instructions/shadcn-web.instructions.md +94 -0
  4. package/agent-meta/manifest.json +37 -0
  5. package/agent-meta/skills/shadcn-versions/SKILL.md +126 -0
  6. package/agent-meta/skills/shadcn-web/SKILL.md +222 -0
  7. package/build/apply.d.ts +13 -0
  8. package/build/apply.d.ts.map +1 -0
  9. package/build/apply.js +28 -0
  10. package/build/apply.js.map +1 -0
  11. package/build/args.d.ts +14 -0
  12. package/build/args.d.ts.map +1 -0
  13. package/build/args.js +93 -0
  14. package/build/args.js.map +1 -0
  15. package/build/bin.d.ts +3 -0
  16. package/build/bin.d.ts.map +1 -0
  17. package/build/bin.js +21 -0
  18. package/build/bin.js.map +1 -0
  19. package/build/discover.d.ts +41 -0
  20. package/build/discover.d.ts.map +1 -0
  21. package/build/discover.js +160 -0
  22. package/build/discover.js.map +1 -0
  23. package/build/index.d.ts +11 -0
  24. package/build/index.d.ts.map +1 -0
  25. package/build/index.js +6 -0
  26. package/build/index.js.map +1 -0
  27. package/build/linked.d.ts +15 -0
  28. package/build/linked.d.ts.map +1 -0
  29. package/build/linked.js +44 -0
  30. package/build/linked.js.map +1 -0
  31. package/build/plan.d.ts +15 -0
  32. package/build/plan.d.ts.map +1 -0
  33. package/build/plan.js +40 -0
  34. package/build/plan.js.map +1 -0
  35. package/build/prompt.d.ts +4 -0
  36. package/build/prompt.d.ts.map +1 -0
  37. package/build/prompt.js +21 -0
  38. package/build/prompt.js.map +1 -0
  39. package/build/run.d.ts +18 -0
  40. package/build/run.d.ts.map +1 -0
  41. package/build/run.js +124 -0
  42. package/build/run.js.map +1 -0
  43. package/package.json +37 -0
  44. package/src/apply.ts +38 -0
  45. package/src/args.ts +108 -0
  46. package/src/bin.ts +20 -0
  47. package/src/discover.ts +202 -0
  48. package/src/index.ts +14 -0
  49. package/src/linked.ts +51 -0
  50. package/src/plan.ts +69 -0
  51. package/src/prompt.ts +25 -0
  52. package/src/run.ts +167 -0
  53. package/tests/installer.test.ts +297 -0
  54. package/tsconfig.json +11 -0
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @owlmeans/agent-skills
2
+
3
+ CLI installer that lifts embedded Claude Code skills and GitHub Copilot instructions
4
+ out of installed `@owlmeans/*` packages and places them into your project's native
5
+ agent-guidance locations.
6
+
7
+ ## Usage
8
+
9
+ After installing `@owlmeans/*` packages, run once:
10
+
11
+ ```sh
12
+ npx @owlmeans/agent-skills
13
+ ```
14
+
15
+ The installer scans `node_modules/@owlmeans/*/agent-meta/`, shows you what guidance
16
+ is available, and (with your confirmation) copies it into:
17
+
18
+ - **Claude Code**: `.claude/skills/<name>/SKILL.md`
19
+ - **GitHub Copilot**: `.github/instructions/<name>.instructions.md`
20
+
21
+ Re-run after updating `@owlmeans/*` packages to pick up revised guidance.
22
+
23
+ ## Flags
24
+
25
+ ```
26
+ --dir <path> target project directory (default: current working directory)
27
+ --yes, -y skip interactive confirmation (non-destructive; conflicts still skip)
28
+ --only <pkg,...> install guidance from these packages only (comma-separated @owlmeans/* names)
29
+ --claude-only install only Claude Code skills (.claude/skills/)
30
+ --copilot-only install only Copilot instructions (.github/instructions/)
31
+ --extras include extras bundled in the installer itself (default: on)
32
+ --no-extras skip extras bundled in the installer
33
+ --force overwrite locally-edited files (no AUTO-GENERATED banner)
34
+ --dry-run print plan without writing any files
35
+ --help, -h show this help
36
+ ```
37
+
38
+ ## Conflict policy
39
+
40
+ Files managed by the installer carry an `AUTO-GENERATED` banner. On re-run:
41
+
42
+ - **Managed files** (banner present) — always updated to the latest embedded version.
43
+ - **Locally-edited files** (no banner) — **skipped** and reported as conflicts by default.
44
+ Pass `--force` to overwrite, or confirm individually in interactive mode.
45
+
46
+ ## Extras (shadcn)
47
+
48
+ The installer ships `shadcn-web` and `shadcn-versions` as bundled extras — design
49
+ system guidance that spans multiple packages and has no single lead package. These
50
+ are installed by default alongside package-specific guidance from `node_modules`.
51
+
52
+ ## Linked monorepo
53
+
54
+ If any `@owlmeans/*` packages are **symlinked** (e.g. via a `libraries/` workspace
55
+ link in an OwlMeans monorepo), the installer refuses and exits with code 4. In that
56
+ setup agents already load guidance from the linked monorepo's root `.claude/skills/`
57
+ and `.github/instructions/` — running the installer would install stale copies.
58
+
59
+ Pass `--force` only if you understand this and want to proceed anyway.
60
+
61
+ ## Schema
62
+
63
+ Each `@owlmeans/*` package ships its embedded guidance under:
64
+
65
+ ```
66
+ agent-meta/
67
+ manifest.json # package, version, entries list
68
+ skills/<name>/
69
+ SKILL.md # Claude Code skill
70
+ instructions/
71
+ <name>.instructions.md # GitHub Copilot instruction
72
+ ```
73
+
74
+ Embedded files are **generated and read-only**. To contribute guidance edits, open
75
+ a PR against [owlmeans/common](https://github.com/owlmeans/common).
76
+
77
+ <!-- owlmeans:agent-guidance:start -->
78
+ ## Agent guidance
79
+
80
+ This package ships embedded Claude Code skills and GitHub Copilot instructions under
81
+ `agent-meta/`. After installing your `@owlmeans/*` packages, run the OwlMeans
82
+ agent-skills installer to place them into your project's native locations
83
+ (`.claude/skills/` and `.github/instructions/`):
84
+
85
+ ```sh
86
+ npx @owlmeans/agent-skills
87
+ ```
88
+
89
+ The embedded files are version-matched to this package release. Do not edit them
90
+ directly — they are regenerated on each publish. To contribute guidance edits,
91
+ open a PR against the source monorepo.
92
+ <!-- owlmeans:agent-guidance:end -->
@@ -0,0 +1,46 @@
1
+ ---
2
+ description: How to update Tailwind CSS, shadcn UI primitives, and related UI lib versions across shadcn-based OwlMeans packages. Distinct from the @owlmeans/* version sync (see versions.instructions.md).
3
+ applyTo: "**/components.json"
4
+ ---
5
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
6
+
7
+ # shadcn + Tailwind version management
8
+
9
+ This covers **external UI dependency versions** across shadcn-based OwlMeans packages. It is separate from the `@owlmeans/*` version sync (see `versions.instructions.md`).
10
+
11
+ ## What to update
12
+
13
+ - `tailwindcss`, `@tailwindcss/vite`, `@tailwindcss/postcss`
14
+ - `class-variance-authority`, `clsx`, `tailwind-merge`, `lucide-react`
15
+ - `@radix-ui/*` (added per component as primitives are copied in)
16
+ - Shadcn primitive `.tsx` files in `src/@/components/ui/` (copied source, not npm)
17
+
18
+ ## Bumping npm versions
19
+
20
+ ```bash
21
+ # Example: bump tailwindcss in a shadcn package (run per package)
22
+ sed -i 's/"tailwindcss": "[^"]*"/"tailwindcss": "^4.1.0"/g' <shadcn-package>/package.json
23
+ bun install
24
+ bun run build
25
+ ```
26
+
27
+ ## Re-syncing copied primitives
28
+
29
+ 1. Find the new source for the component in shadcn GitHub.
30
+ 2. Diff against the local copy to preserve any project-local changes.
31
+ 3. Update the version comment: `// shadcn <name> — sourced from shadcn@<version> <date>`.
32
+ 4. Run `bun run build` and category-D UI tests.
33
+
34
+ ## Tailwind major upgrades
35
+
36
+ Follow the official Tailwind upgrade guide. Check `@theme` token compatibility and `@source` syntax. Update `@tailwindcss/vite` to a matching major version.
37
+
38
+ ## Verification
39
+
40
+ ```bash
41
+ bun run build
42
+ # Run category-D UI tests for shadcn packages
43
+ bun test --filter <shadcn-pkg-name> ./tests
44
+ ```
45
+
46
+ See `.claude/skills/shadcn-versions/SKILL.md` for full procedure.
@@ -0,0 +1,94 @@
1
+ ---
2
+ description: How to build and maintain shadcn UI + Tailwind v4 web packages in OwlMeans Common. Load when editing components.json, tailwind.config.*, globals.css, or files under components/ui/.
3
+ applyTo: "**/components.json, **/tailwind.config.{ts,js,mjs}, **/globals.css, **/components/ui/**, **/@/lib/utils.ts"
4
+ ---
5
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
6
+
7
+ # shadcn UI + Tailwind v4 — OwlMeans web packages
8
+
9
+ Shadcn-based web packages are the next-generation Web layer replacing `web-panel` (MUI). They wrap the same headless `@owlmeans/client-panel` logic and mirror `web-panel`'s package shape — swapping MUI for shadcn UI + Tailwind v4.
10
+
11
+ ## The `@` contract — three invariants
12
+
13
+ 1. **No shadcn registries.** Primitives are **hand-copied** into `src/@/components/ui/`. Never use `registries` in `components.json`.
14
+ 2. **Same `@` import prefix as the final app.** Components import `@/components/ui/button`, `@/lib/utils`, etc. The build emits `@/…` verbatim (Bundler resolution). The app's bundler resolves `@` to its own shadcn copy. The package never ships its primitives as a public import.
15
+ 3. **Local copy is dev/test-only.** `src/@/` exists so the package can build and test in isolation. Never expose `./@/*` in `package.json` exports.
16
+
17
+ ## tsconfig — `@` alias setup
18
+
19
+ ```json
20
+ {
21
+ "compilerOptions": {
22
+ "baseUrl": ".",
23
+ "paths": { "@/*": ["./src/@/*"] }
24
+ }
25
+ }
26
+ ```
27
+
28
+ `moduleResolution: Bundler` (from `dep-config/tsconfig.base.json`) emits `@/…` verbatim — TypeScript resolves types via `paths` but does not rewrite the specifier.
29
+
30
+ ## components.json
31
+
32
+ ```json
33
+ {
34
+ "$schema": "https://ui.shadcn.com/schema.json",
35
+ "style": "new-york",
36
+ "rsc": false,
37
+ "tsx": true,
38
+ "tailwind": { "config": "", "css": "src/@/globals.css", "baseColor": "neutral", "cssVariables": true },
39
+ "iconLibrary": "lucide",
40
+ "aliases": { "components": "@/components", "ui": "@/components/ui", "utils": "@/lib/utils", "lib": "@/lib", "hooks": "@/hooks" }
41
+ }
42
+ ```
43
+
44
+ `tailwind.config: ""` is required for Tailwind v4.
45
+
46
+ ## Tailwind v4 — globals.css
47
+
48
+ ```css
49
+ @import "tailwindcss";
50
+ @source "../..";
51
+ @theme {
52
+ --color-primary: oklch(0.205 0 0);
53
+ --color-primary-foreground: oklch(0.985 0 0);
54
+ --color-destructive: oklch(0.577 0.245 27.325);
55
+ --color-border: oklch(0.922 0 0);
56
+ --radius: 0.625rem;
57
+ /* ... full token set in @owlmeans/shadcn-web reference.md ... */
58
+ }
59
+ ```
60
+
61
+ In production, the **app owns its own `globals.css`**. The package's CSS is only for dev/test.
62
+
63
+ ## Adding a primitive (no registry)
64
+
65
+ 1. Copy `.tsx` source from shadcn GitHub into `src/@/components/ui/<name>.tsx`.
66
+ 2. Repoint imports to `@/lib/utils` and `@/components/ui/…`.
67
+ 3. Add any `@radix-ui/*` packages as **peerDependencies**.
68
+ 4. Add comment: `// shadcn <name> — sourced from shadcn@<version> <date>`.
69
+
70
+ ## Wrapping `@owlmeans/client-panel`
71
+
72
+ Replace MUI rendering in `web-panel` components with shadcn + Tailwind classes. The `FormProvider`, `useFormContext`, `Controller`, AJV resolver, and `useFormI18n` are unchanged — only JSX changes.
73
+
74
+ ```tsx
75
+ // MUI: <TextField {...field} label={label} error={...} />
76
+ // shadcn:
77
+ <div className="flex flex-col gap-1.5">
78
+ <Label htmlFor={field.name}>{label}</Label>
79
+ <Input id={field.name} {...field} aria-invalid={fieldState.error != null} />
80
+ {fieldState.error && <p className="text-sm text-destructive">{fieldState.error.message}</p>}
81
+ </div>
82
+ ```
83
+
84
+ ## package.json peerDependencies
85
+
86
+ `class-variance-authority`, `clsx`, `tailwind-merge`, `lucide-react`, `react`, `react-dom`, `react-hook-form`, `tailwindcss`. Add `@radix-ui/*` per component copied.
87
+
88
+ ## package.json exports — never expose `@/*`
89
+
90
+ ```json
91
+ { "exports": { ".": { "import": "./build/index.js", "types": "./build/index.d.ts" } } }
92
+ ```
93
+
94
+ See `.claude/skills/shadcn-web/SKILL.md` for the full pattern.
@@ -0,0 +1,37 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "package": "@owlmeans/agent-skills",
4
+ "version": "0.1.8",
5
+ "generatedAt": "2026-06-11T16:30:27.174Z",
6
+ "canonicalRepo": "https://github.com/owlmeans/common",
7
+ "entries": [
8
+ {
9
+ "kind": "skill",
10
+ "name": "shadcn-web",
11
+ "category": "multi-package",
12
+ "file": "skills/shadcn-web/SKILL.md",
13
+ "canonicalPath": ".claude/skills/shadcn-web/SKILL.md"
14
+ },
15
+ {
16
+ "kind": "skill",
17
+ "name": "shadcn-versions",
18
+ "category": "multi-package",
19
+ "file": "skills/shadcn-versions/SKILL.md",
20
+ "canonicalPath": ".claude/skills/shadcn-versions/SKILL.md"
21
+ },
22
+ {
23
+ "kind": "instruction",
24
+ "name": "shadcn-versions",
25
+ "category": "multi-package",
26
+ "file": "instructions/shadcn-versions.instructions.md",
27
+ "canonicalPath": ".github/instructions/shadcn-versions.instructions.md"
28
+ },
29
+ {
30
+ "kind": "instruction",
31
+ "name": "shadcn-web",
32
+ "category": "multi-package",
33
+ "file": "instructions/shadcn-web.instructions.md",
34
+ "canonicalPath": ".github/instructions/shadcn-web.instructions.md"
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,126 @@
1
+ ---
2
+ name: shadcn-versions
3
+ description: How to update versions of shadcn UI primitives, Tailwind CSS v4, and related UI libs across all shadcn-based OwlMeans web packages. Distinct from the @owlmeans/* package version sync (see [[versions]] skill). Use when bumping tailwind, shadcn components, or utility libs.
4
+ allowed-tools: Bash(grep *), Bash(sed *), Bash(bun install)
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # shadcn + Tailwind version management — OwlMeans Common
9
+
10
+ This skill handles **external UI dependency versions** for the shadcn-based web packages. It is **distinct** from the `[[versions]]` skill, which manages the synchronized `@owlmeans/*` package version (`0.1.x` etc.).
11
+
12
+ ## What this covers
13
+
14
+ | Package | Location | Notes |
15
+ |---|---|---|
16
+ | `tailwindcss` | peerDependency + devDependency | core engine |
17
+ | `@tailwindcss/vite` | devDependency | Vite plugin for dev/test harness |
18
+ | `@tailwindcss/postcss` | devDependency | PostCSS fallback (if used) |
19
+ | `class-variance-authority` | peerDependency | `cva()` for component variants |
20
+ | `clsx` | peerDependency | conditional class names |
21
+ | `tailwind-merge` | peerDependency | merge Tailwind classes without conflicts |
22
+ | `lucide-react` | peerDependency | icon library |
23
+ | `@radix-ui/*` | peerDependency | per-component Radix UI primitives |
24
+ | shadcn primitives (copied) | source in `src/@/components/ui/` | hand-copied `.tsx` files — not npm |
25
+
26
+ Shadcn primitive `.tsx` files inside each package are **not** npm dependencies — they are copied source files. Updating them is a separate step (see below).
27
+
28
+ ## Checking current versions
29
+
30
+ To see what versions installed `@owlmeans/*` shadcn packages declare:
31
+
32
+ ```bash
33
+ # Which tailwind version do installed OwlMeans shadcn packages use?
34
+ grep -r '"tailwindcss"' node_modules/@owlmeans/*/package.json
35
+
36
+ # Which tailwind-merge / clsx version?
37
+ grep -r '"tailwind-merge"\|"clsx"\|"class-variance-authority"' node_modules/@owlmeans/*/package.json
38
+
39
+ # Which @radix-ui/* versions?
40
+ grep -r '"@radix-ui/' node_modules/@owlmeans/*/package.json
41
+ ```
42
+
43
+ ## Bumping external UI lib versions
44
+
45
+ Shadcn packages use caret ranges in peerDependencies (e.g. `"tailwindcss": "*"` or `"tailwindcss": "^4.x"`). To pin to a specific version across all shadcn packages in the `@owlmeans/common` source repo, run these in the monorepo root (OwlMeans framework contributors):
46
+
47
+ ```bash
48
+ # Example: pin tailwindcss to ^4.1.0 across all shadcn packages
49
+ OLD_TW='\*' # or whatever the current range is
50
+ NEW_TW='^4.1.0'
51
+
52
+ # Update each shadcn package's package.json (run per package)
53
+ sed -i "s/\"tailwindcss\": \"$OLD_TW\"/\"tailwindcss\": \"$NEW_TW\"/g" <shadcn-package>/package.json
54
+
55
+ # Same pattern for other UI libs
56
+ sed -i 's/"tailwind-merge": "\*"/"tailwind-merge": "^2.5.0"/g' <shadcn-package>/package.json
57
+ sed -i 's/"clsx": "\*"/"clsx": "^2.1.0"/g' <shadcn-package>/package.json
58
+
59
+ bun install
60
+ ```
61
+
62
+ Always run `bun run build` and the category-D UI tests after bumping to catch breaking changes.
63
+
64
+ ## Bumping the Vite plugin / PostCSS plugin
65
+
66
+ These are devDependencies only (used in the test harness, not production). Update them separately:
67
+
68
+ ```bash
69
+ sed -i 's/"@tailwindcss\/vite": "\*"/"@tailwindcss\/vite": "^4.1.0"/g' <shadcn-package>/package.json
70
+ bun install
71
+ ```
72
+
73
+ ## Tailwind major version upgrades (v4 → v5, etc.)
74
+
75
+ 1. Read the [Tailwind upgrade guide](https://tailwindcss.com/docs/upgrade-guide) for breaking changes.
76
+ 2. Check `@theme` token compatibility — token names may change between major versions.
77
+ 3. Check `@source` directive syntax changes.
78
+ 4. Update `@tailwindcss/vite` and `@tailwindcss/postcss` to matching major versions.
79
+ 5. Rebuild (`bun run build`) and run all category-D UI tests.
80
+ 6. Update this skill and `[[shadcn-web]]` with the new conventions.
81
+
82
+ ## Re-syncing copied shadcn primitives
83
+
84
+ When shadcn upstream releases a new version, manually update the hand-copied primitives:
85
+
86
+ 1. **Find the changed component source** — check the shadcn GitHub diff or changelog.
87
+ 2. **Get the new source** — copy from `https://ui.shadcn.com/r/<name>.json` (the raw file URL) or from the [shadcn GitHub](https://github.com/shadcn-ui/ui) (`registry/new-york/ui/<name>.tsx`).
88
+ 3. **Diff against the local copy** to identify any project-local modifications (custom classes, extra props, i18n tweaks) that must be preserved.
89
+ 4. **Apply changes**, preserving local modifications.
90
+ 5. **Update the version comment** at the top of the file: `// shadcn <name> — sourced from shadcn@<version> <date>`.
91
+ 6. **Check `components.json` schema** — if the shadcn schema version changed, update `"$schema"` URLs.
92
+ 7. Run `bun run build` and UI tests.
93
+
94
+ ## Keeping `@radix-ui/*` versions consistent
95
+
96
+ Radix UI packages are added as peerDependencies when primitives are copied in. Keep them consistent across packages:
97
+
98
+ ```bash
99
+ # Check radix versions in installed OwlMeans shadcn packages
100
+ grep -r '"@radix-ui/' node_modules/@owlmeans/*/package.json | grep -v node_modules
101
+
102
+ # Bump a specific radix package (run per shadcn package in the source repo)
103
+ sed -i 's/"@radix-ui\/react-slot": "[^"]*"/"@radix-ui\/react-slot": "^1.1.0"/g' <shadcn-package>/package.json
104
+ bun install
105
+ ```
106
+
107
+ ## Verification
108
+
109
+ After any version change:
110
+
111
+ ```bash
112
+ # Build all packages
113
+ bun run build
114
+
115
+ # Run category-D UI tests for shadcn packages
116
+ bun test --filter <shadcn-pkg-name> ./tests
117
+ ```
118
+
119
+ If category-D tests don't exist yet, at minimum load the harness URL manually to confirm components render without JS errors.
120
+
121
+ ## Cross-references
122
+
123
+ - `[[versions]]` — synchronized `@owlmeans/*` package version (separate concern)
124
+ - `[[shadcn-web]]` — how to build/maintain shadcn-based OwlMeans packages
125
+ - `[[testing-ui]]` — category-D Playwright tests that verify UI after version bumps
126
+ - `[[bun]]` — bun install, workspace filter, build commands
@@ -0,0 +1,222 @@
1
+ ---
2
+ name: shadcn-web
3
+ description: How to build and maintain shadcn UI + Tailwind v4 web packages in the OwlMeans framework. Auto-invoked when editing components.json, tailwind.config.*, globals.css, or files under components/ui/. Use when creating a new shadcn-based web package or adding shadcn components.
4
+ user-invocable: false
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # shadcn UI + Tailwind v4 web packages — OwlMeans pattern
9
+
10
+ Shadcn-based web packages are the **next-generation Web layer**, replacing MUI `web-panel` over time. They sit at the same architecture layer (Web), wrap the same headless `@owlmeans/client-panel` logic, and follow the same `web-panel` package shape — swapping MUI + Emotion for shadcn UI + Tailwind CSS v4.
11
+
12
+ See `reference.md` in this skill folder for full code examples (components.json, tsconfig, globals.css @theme tokens, peerDeps, cn(), MUI→shadcn mapping table).
13
+
14
+ ## The `@` contract — core rule
15
+
16
+ Three invariants govern every shadcn-based OwlMeans package:
17
+
18
+ 1. **No shadcn registries.** Primitives (Button, Input, Card, …) are **hand-copied** into `src/@/components/ui/` and committed. The `registries` field in `components.json` is always empty/absent.
19
+
20
+ 2. **Same `@` import prefix as the final app.** Components inside the package import shadcn primitives as `@/components/ui/button`, `@/lib/utils`, etc. The build leaves `@/…` specifiers **verbatim** (TypeScript Bundler resolution never rewrites them). The downstream app's bundler resolves `@` to **its own** shadcn copy + theme. The package never ships its primitives as a public import.
21
+
22
+ 3. **Local copy is dev/test-only.** Each package keeps its own copy under `src/@/` so it can build and test in isolation. The `package.json` `exports` map must **not** expose `./@/*` — consumers never accidentally import the package's copy.
23
+
24
+ ## Package skeleton (mirrors `web-panel`)
25
+
26
+ ```
27
+ <your-package>/
28
+ ├── src/
29
+ │ ├── @/ # dev/test-only primitives — NOT a public export
30
+ │ │ ├── components/ui/ # hand-copied shadcn primitives
31
+ │ │ │ ├── button.tsx
32
+ │ │ │ └── input.tsx
33
+ │ │ ├── lib/
34
+ │ │ │ └── utils.ts # cn() utility
35
+ │ │ └── globals.css # Tailwind v4 entry (app provides its own in prod)
36
+ │ ├── components/ # OwlMeans framework components (wrap client-panel)
37
+ │ │ ├── form/
38
+ │ │ │ ├── component.tsx
39
+ │ │ │ ├── text/component.tsx
40
+ │ │ │ ├── button/component.tsx
41
+ │ │ │ ├── types.ts
42
+ │ │ │ └── index.ts
43
+ │ │ ├── layout/component.tsx
44
+ │ │ ├── panel-app/component.tsx
45
+ │ │ └── index.ts # barrel; re-exports @owlmeans/client-panel
46
+ │ ├── context.ts # makeContext wrapping web-client's makeContext
47
+ │ ├── main.tsx # render() — no theme arg (theme is pure CSS)
48
+ │ ├── types.ts
49
+ │ ├── modules.ts
50
+ │ └── index.ts
51
+ ├── tests/
52
+ │ ├── harness/
53
+ │ │ ├── index.html # copy from @owlmeans/test-ui harness/index.html
54
+ │ │ └── mount.tsx # per-package: imports globals.css, registers components
55
+ │ ├── context.ts # boots Vite with @tailwindcss/vite + @ alias
56
+ │ └── *.spec.ts
57
+ ├── components.json
58
+ ├── package.json
59
+ └── tsconfig.json
60
+ ```
61
+
62
+ ## tsconfig setup
63
+
64
+ ```json
65
+ {
66
+ "extends": [
67
+ "@owlmeans/dep-config/tsconfig.base.json",
68
+ "@owlmeans/dep-config/tsconfig.react.json"
69
+ ],
70
+ "compilerOptions": {
71
+ "baseUrl": ".",
72
+ "paths": { "@/*": ["./src/@/*"] },
73
+ "rootDir": "./src/",
74
+ "outDir": "./build/"
75
+ },
76
+ "exclude": ["./dist/**/*", "./build/**/*", "./tests/**/*", "./*.ts"]
77
+ }
78
+ ```
79
+
80
+ `moduleResolution: Bundler` (from `tsconfig.base.json`) causes `tsc` to emit `@/…` verbatim. `paths` is used only for **type-checking** during build — it finds types for `@/components/ui/button` in `src/@/components/ui/button.tsx` without rewriting the emitted specifier.
81
+
82
+ ## components.json
83
+
84
+ ```json
85
+ {
86
+ "$schema": "https://ui.shadcn.com/schema.json",
87
+ "style": "new-york",
88
+ "rsc": false,
89
+ "tsx": true,
90
+ "tailwind": {
91
+ "config": "",
92
+ "css": "src/@/globals.css",
93
+ "baseColor": "neutral",
94
+ "cssVariables": true,
95
+ "prefix": ""
96
+ },
97
+ "iconLibrary": "lucide",
98
+ "aliases": {
99
+ "components": "@/components",
100
+ "ui": "@/components/ui",
101
+ "utils": "@/lib/utils",
102
+ "lib": "@/lib",
103
+ "hooks": "@/hooks"
104
+ }
105
+ }
106
+ ```
107
+
108
+ `tailwind.config: ""` is required for Tailwind v4 (no separate config file). Do not add a `registries` field.
109
+
110
+ ## Tailwind v4 wiring
111
+
112
+ `src/@/globals.css` — the Tailwind entry used in dev/test only:
113
+
114
+ ```css
115
+ @import "tailwindcss";
116
+
117
+ @source "../..";
118
+
119
+ @theme {
120
+ --color-background: oklch(1 0 0);
121
+ --color-foreground: oklch(0.145 0 0);
122
+ --color-primary: oklch(0.205 0 0);
123
+ --color-primary-foreground: oklch(0.985 0 0);
124
+ --color-destructive: oklch(0.577 0.245 27.325);
125
+ --color-border: oklch(0.922 0 0);
126
+ --color-input: oklch(0.922 0 0);
127
+ --color-ring: oklch(0.708 0 0);
128
+ --radius: 0.625rem;
129
+ }
130
+ ```
131
+
132
+ `@source "../.."` scans the package root. In the Vite dev/test harness, `@tailwindcss/vite` processes this CSS. The **production app owns its own `globals.css`** with its own `@theme` tokens — the package's CSS is only for tests.
133
+
134
+ ## Adding a primitive without a registry
135
+
136
+ 1. Find the component source on the [shadcn GitHub](https://github.com/shadcn-ui/ui) (e.g. `registry/new-york/ui/<name>.tsx`) or via `npx shadcn@latest add <name> --cwd <your-package-dir>` in a throwaway branch.
137
+ 2. Copy the `.tsx` source into `src/@/components/ui/<name>.tsx`.
138
+ 3. Repoint all imports to `@/lib/utils` and `@/components/ui/…` for sub-primitives.
139
+ 4. Add any `@radix-ui/*` packages the file imports as **peerDependencies** in `package.json`.
140
+ 5. Add a comment at the top: `// shadcn <name> — sourced from shadcn@<version> <date>`.
141
+ 6. Run `bun install` and `bun run build` to verify.
142
+
143
+ ## Wrapping `@owlmeans/client-panel`
144
+
145
+ Shadcn form/field components are the rendering layer over the framework-agnostic headless logic from `@owlmeans/client-panel`. Mirror the MUI pattern from `@owlmeans/web-panel` (form text component):
146
+
147
+ ```tsx
148
+ // MUI version (web-panel) — for reference
149
+ import { TextField } from '@mui/material'
150
+ // ...
151
+ <TextField {...field} label={label} error={fieldState.error != null} />
152
+
153
+ // shadcn version
154
+ import { Input } from '@/components/ui/input'
155
+ import { Label } from '@/components/ui/label'
156
+ // ...
157
+ <div className="flex flex-col gap-1.5">
158
+ <Label htmlFor={field.name}>{label}</Label>
159
+ <Input id={field.name} {...field} aria-invalid={fieldState.error != null} />
160
+ {fieldState.error && (
161
+ <p className="text-sm text-destructive">{fieldState.error.message}</p>
162
+ )}
163
+ </div>
164
+ ```
165
+
166
+ `FormProvider`, `useFormContext`, `Controller`, the AJV validation resolver, and `useFormI18n` from `@owlmeans/client-i18n` are unchanged — only the rendered JSX changes.
167
+
168
+ `panel-app/component.tsx` no longer needs MUI `ThemeProvider` or `CssBaseline`. It simply renders children. Theme is provided via the app's `globals.css` Tailwind entry.
169
+
170
+ ## package.json exports
171
+
172
+ ```json
173
+ {
174
+ "exports": {
175
+ ".": {
176
+ "import": "./build/index.js",
177
+ "types": "./build/index.d.ts"
178
+ },
179
+ "./auth": {
180
+ "import": "./build/auth/index.js",
181
+ "types": "./build/auth/index.d.ts"
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ Do **not** add `./@/*` or `./src/@/*` to exports.
188
+
189
+ ## Dev/test harness (Vite + @tailwindcss/vite)
190
+
191
+ See `[[testing-ui]]` for the full Playwright harness pattern. For shadcn packages the `tests/context.ts` Vite config adds the Tailwind plugin and the `@` alias:
192
+
193
+ ```ts
194
+ import tailwindcss from '@tailwindcss/vite'
195
+ import { resolve } from 'node:path'
196
+
197
+ const server = await createServer({
198
+ configFile: false,
199
+ root: resolve(here, './harness'),
200
+ plugins: [react(), tailwindcss()],
201
+ resolve: {
202
+ alias: { '@': resolve(here, '../src/@') },
203
+ },
204
+ server: { port: 0 },
205
+ })
206
+ ```
207
+
208
+ `tests/harness/mount.tsx` imports the package globals.css so components render with Tailwind styles:
209
+
210
+ ```tsx
211
+ import '../../src/@/globals.css'
212
+ // ... dynamic component mounting ...
213
+ ```
214
+
215
+ ## Cross-references
216
+
217
+ - `[[client-panel]]` — headless form/layout logic being wrapped
218
+ - `[[web-panel]]` — MUI counterpart; the structural and API reference
219
+ - `[[testing-ui]]` — Playwright harness for component acceptance tests
220
+ - `[[shadcn-versions]]` — bumping tailwind/shadcn external deps across packages
221
+ - `[[tsconfig]]` — OwlMeans TypeScript config conventions
222
+ - `[[bun]]` — build, install, workspace filter scripts
@@ -0,0 +1,13 @@
1
+ import type { InstallItem } from './plan.js';
2
+ export interface ApplyResult {
3
+ installed: number;
4
+ updated: number;
5
+ skipped: number;
6
+ conflicts: number;
7
+ }
8
+ /**
9
+ * Execute an install plan. Items with action 'conflict' or 'skip-uptodate'
10
+ * are not written. Only 'install' and 'update' items are written.
11
+ */
12
+ export declare const applyInstall: (items: InstallItem[]) => ApplyResult;
13
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,WAAW,EAAE,KAAG,WAsBnD,CAAA"}
package/build/apply.js ADDED
@@ -0,0 +1,28 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ /**
4
+ * Execute an install plan. Items with action 'conflict' or 'skip-uptodate'
5
+ * are not written. Only 'install' and 'update' items are written.
6
+ */
7
+ export const applyInstall = (items) => {
8
+ const result = { installed: 0, updated: 0, skipped: 0, conflicts: 0 };
9
+ for (const item of items) {
10
+ if (item.action === 'skip-uptodate') {
11
+ result.skipped++;
12
+ continue;
13
+ }
14
+ if (item.action === 'conflict') {
15
+ result.conflicts++;
16
+ continue;
17
+ }
18
+ const content = readFileSync(item.entry.sourcePath, 'utf8');
19
+ mkdirSync(dirname(item.targetPath), { recursive: true });
20
+ writeFileSync(item.targetPath, content, 'utf8');
21
+ if (item.action === 'install')
22
+ result.installed++;
23
+ else
24
+ result.updated++;
25
+ }
26
+ return result;
27
+ };
28
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAUnC;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAoB,EAAe,EAAE;IAChE,MAAM,MAAM,GAAgB,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAA;IAElF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,eAAe,EAAE,CAAC;YACpC,MAAM,CAAC,OAAO,EAAE,CAAA;YAChB,SAAQ;QACV,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/B,MAAM,CAAC,SAAS,EAAE,CAAA;YAClB,SAAQ;QACV,CAAC;QAED,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QAC3D,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACxD,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;QAE/C,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,MAAM,CAAC,SAAS,EAAE,CAAA;;YAC5C,MAAM,CAAC,OAAO,EAAE,CAAA;IACvB,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA"}