@dynokostya/just-works 1.0.0

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 (46) hide show
  1. package/.claude/agents/csharp-code-writer.md +32 -0
  2. package/.claude/agents/diagrammer.md +49 -0
  3. package/.claude/agents/frontend-code-writer.md +36 -0
  4. package/.claude/agents/prompt-writer.md +38 -0
  5. package/.claude/agents/python-code-writer.md +32 -0
  6. package/.claude/agents/swift-code-writer.md +32 -0
  7. package/.claude/agents/typescript-code-writer.md +32 -0
  8. package/.claude/commands/git-sync.md +96 -0
  9. package/.claude/commands/project-docs.md +287 -0
  10. package/.claude/settings.json +112 -0
  11. package/.claude/settings.json.default +15 -0
  12. package/.claude/skills/csharp-coding/SKILL.md +368 -0
  13. package/.claude/skills/ddd-architecture-python/SKILL.md +288 -0
  14. package/.claude/skills/feature-driven-architecture-python/SKILL.md +302 -0
  15. package/.claude/skills/gemini-3-prompting/SKILL.md +483 -0
  16. package/.claude/skills/gpt-5-2-prompting/SKILL.md +295 -0
  17. package/.claude/skills/opus-4-6-prompting/SKILL.md +315 -0
  18. package/.claude/skills/plantuml-diagramming/SKILL.md +758 -0
  19. package/.claude/skills/python-coding/SKILL.md +293 -0
  20. package/.claude/skills/react-coding/SKILL.md +264 -0
  21. package/.claude/skills/rest-api/SKILL.md +421 -0
  22. package/.claude/skills/shadcn-ui-coding/SKILL.md +454 -0
  23. package/.claude/skills/swift-coding/SKILL.md +401 -0
  24. package/.claude/skills/tailwind-css-coding/SKILL.md +268 -0
  25. package/.claude/skills/typescript-coding/SKILL.md +464 -0
  26. package/.claude/statusline-command.sh +34 -0
  27. package/.codex/prompts/plan-reviewer.md +162 -0
  28. package/.codex/prompts/project-docs.md +287 -0
  29. package/.codex/skills/ddd-architecture-python/SKILL.md +288 -0
  30. package/.codex/skills/feature-driven-architecture-python/SKILL.md +302 -0
  31. package/.codex/skills/gemini-3-prompting/SKILL.md +483 -0
  32. package/.codex/skills/gpt-5-2-prompting/SKILL.md +295 -0
  33. package/.codex/skills/opus-4-6-prompting/SKILL.md +315 -0
  34. package/.codex/skills/plantuml-diagramming/SKILL.md +758 -0
  35. package/.codex/skills/python-coding/SKILL.md +293 -0
  36. package/.codex/skills/react-coding/SKILL.md +264 -0
  37. package/.codex/skills/rest-api/SKILL.md +421 -0
  38. package/.codex/skills/shadcn-ui-coding/SKILL.md +454 -0
  39. package/.codex/skills/tailwind-css-coding/SKILL.md +268 -0
  40. package/.codex/skills/typescript-coding/SKILL.md +464 -0
  41. package/AGENTS.md +57 -0
  42. package/CLAUDE.md +98 -0
  43. package/LICENSE +201 -0
  44. package/README.md +114 -0
  45. package/bin/cli.mjs +291 -0
  46. package/package.json +39 -0
@@ -0,0 +1,454 @@
1
+ ---
2
+ name: shadcn-ui-coding
3
+ description: Apply when generating shadcn/ui code. Covers the copy-paste component model, Radix UI compound components, theming with CSS variables, Form/DataTable integrations, and CLI conventions. Does NOT cover general React or Tailwind CSS patterns.
4
+ ---
5
+
6
+ # shadcn/ui coding
7
+
8
+ Match project conventions. Read `components.json` for style, Tailwind version, aliases, and RSC settings. Check `components/ui/` for local component source and `package.json` for React version (18 vs 19) and Radix package format (unified `radix-ui` vs individual `@radix-ui/react-*`). These defaults apply only when the project has no established convention.
9
+
10
+ ## Never rules
11
+
12
+ These are unconditional. They prevent runtime errors, accessibility breakage, and infinite re-renders regardless of project style.
13
+
14
+ - **Never import shadcn components from npm** -- shadcn is not a package. Import from the local alias.
15
+
16
+ ```tsx
17
+ // Wrong: shadcn is not an npm package
18
+ import { Button } from "shadcn/ui";
19
+ import { Button } from "@shadcn/ui";
20
+
21
+ // Correct: import from the local alias defined in components.json
22
+ import { Button } from "@/components/ui/button";
23
+ ```
24
+
25
+ - **Never flatten Radix compound components** -- Dialog, DropdownMenu, Select, AlertDialog, Menubar, NavigationMenu, ContextMenu, and Tabs require their nested structure. Removing wrapper layers breaks ARIA roles and keyboard navigation.
26
+
27
+ ```tsx
28
+ // Wrong: missing compound structure, broken accessibility
29
+ <Dialog>
30
+ <DialogTrigger>Open</DialogTrigger>
31
+ <DialogTitle>Title</DialogTitle>
32
+ <DialogDescription>Desc</DialogDescription>
33
+ </Dialog>
34
+
35
+ // Correct: full compound structure preserved
36
+ <Dialog>
37
+ <DialogTrigger>Open</DialogTrigger>
38
+ <DialogContent>
39
+ <DialogHeader>
40
+ <DialogTitle>Title</DialogTitle>
41
+ <DialogDescription>Desc</DialogDescription>
42
+ </DialogHeader>
43
+ </DialogContent>
44
+ </Dialog>
45
+ ```
46
+
47
+ - **Never omit `asChild` when wrapping custom elements in Trigger components** -- without `asChild`, the Trigger renders its own `<button>`, creating button-in-button nesting (invalid HTML, broken accessibility).
48
+
49
+ ```tsx
50
+ // Wrong: renders <button><button>...</button></button>
51
+ <DialogTrigger>
52
+ <Button variant="outline">Open</Button>
53
+ </DialogTrigger>
54
+
55
+ // Correct: Slot merges props onto the child element
56
+ <DialogTrigger asChild>
57
+ <Button variant="outline">Open</Button>
58
+ </DialogTrigger>
59
+ ```
60
+
61
+ - **Never use hardcoded colors** -- use CSS variable tokens. Hardcoded values bypass dark mode and break theme consistency.
62
+
63
+ ```tsx
64
+ // Wrong: hardcoded color, breaks in dark mode
65
+ <div className="bg-blue-500 text-white">
66
+
67
+ // Correct: CSS variable tokens, theme-aware
68
+ <div className="bg-primary text-primary-foreground">
69
+ ```
70
+
71
+ - **Never compose classNames with template literals** -- use `cn()` (clsx + tailwind-merge). Template literals can't resolve Tailwind class conflicts.
72
+
73
+ ```tsx
74
+ // Wrong: conflicting classes not resolved
75
+ <div className={`px-2 ${className}`}>
76
+
77
+ // Correct: tailwind-merge resolves conflicts, last wins
78
+ import { cn } from "@/lib/utils";
79
+ <div className={cn("px-2", className)}>
80
+ ```
81
+
82
+ - **Never spread `{...field}` on Select, Checkbox, or Switch** -- these use `onValueChange`/`onCheckedChange`, not `onChange`. Spreading `field` binds `onChange`, which is silently ignored.
83
+
84
+ ```tsx
85
+ // Wrong: onChange is ignored by Radix Select
86
+ <Select {...field}>
87
+
88
+ // Correct: wire value and handler explicitly
89
+ <Select onValueChange={field.onChange} value={field.value}>
90
+ ```
91
+
92
+ - **Never define DataTable columns inside the component** -- new object references each render cause infinite re-renders with TanStack Table's referential equality checks.
93
+
94
+ ```tsx
95
+ // Wrong: new columns array every render, infinite loop
96
+ function UsersTable({ data }: { data: User[] }) {
97
+ const columns: ColumnDef<User>[] = [
98
+ { accessorKey: "name", header: "Name" },
99
+ ];
100
+ return <DataTable columns={columns} data={data} />;
101
+ }
102
+
103
+ // Correct: stable reference, defined outside component
104
+ const columns: ColumnDef<User>[] = [
105
+ { accessorKey: "name", header: "Name" },
106
+ ];
107
+ function UsersTable({ data }: { data: User[] }) {
108
+ return <DataTable columns={columns} data={data} />;
109
+ }
110
+ ```
111
+
112
+ - **Never put more or fewer than one child inside FormControl** -- uses Radix Slot internally, merges props onto exactly one child. Zero or multiple children is a runtime error.
113
+ - **Never build custom modals, dropdowns, or tooltips** -- use shadcn components (Dialog, DropdownMenu, Tooltip, Popover, Sheet). Hand-built versions lack focus trapping, keyboard nav, and portals.
114
+ - **Never nest TooltipProvider** -- mount once at app root. Nesting creates separate delay contexts.
115
+ - **Never hallucinate props** -- shadcn components only have props defined in their local source. Read `components/ui/` if unsure.
116
+ - **Never use `npx shadcn-ui@latest`** -- correct CLI is `npx shadcn@latest`.
117
+ - **Never use Toast** -- deprecated. Use Sonner (`npx shadcn@latest add sonner`).
118
+
119
+ ## Component architecture
120
+
121
+ ### Directory structure
122
+
123
+ ```
124
+ components/
125
+ ui/ # CLI-managed shadcn source -- avoid editing directly
126
+ button.tsx
127
+ dialog.tsx
128
+ ...
129
+ app/ # App-specific wrappers and compositions
130
+ confirm-dialog.tsx # wraps Dialog with app-specific logic
131
+ user-avatar.tsx # wraps Avatar with business defaults
132
+ ```
133
+
134
+ Create wrappers in `components/app/` for app-specific behavior. Edit `components/ui/` directly only for global style changes.
135
+
136
+ ### cn() utility
137
+
138
+ `cn()` from `@/lib/utils` combines `clsx` (conditional classes) and `tailwind-merge` (conflict resolution):
139
+
140
+ ```tsx
141
+ import { cn } from "@/lib/utils";
142
+
143
+ function Card({ className, isActive, ...props }: CardProps) {
144
+ return (
145
+ <div
146
+ className={cn(
147
+ "rounded-lg border bg-card p-6",
148
+ isActive && "border-primary",
149
+ className
150
+ )}
151
+ {...props}
152
+ />
153
+ );
154
+ }
155
+ ```
156
+
157
+ ### CVA (class-variance-authority)
158
+
159
+ shadcn components use CVA for variants. Export `*Variants` separately to style non-native elements (e.g., `<Link className={cn(buttonVariants({ variant: "outline" }))}>`). Use `VariantProps<typeof xVariants>` for type-safe variant props.
160
+
161
+ ## Theming
162
+
163
+ ### CSS variables
164
+
165
+ Format depends on Tailwind version:
166
+
167
+ **Tailwind v4 (current):** OKLCH color format, `@theme inline` directive:
168
+
169
+ ```css
170
+ @theme inline {
171
+ --color-background: oklch(1 0 0);
172
+ --color-foreground: oklch(0.145 0 0);
173
+ --color-primary: oklch(0.205 0 0);
174
+ --color-primary-foreground: oklch(0.985 0 0);
175
+ }
176
+ ```
177
+
178
+ **Tailwind v3 (legacy):** HSL format (e.g., `--primary: 222.2 47.4% 11.2%`) in `@layer base`, referenced as `hsl(var(--primary))` in `tailwind.config.ts`.
179
+
180
+ ### Dark mode
181
+
182
+ **Tailwind v4:** Use `@custom-variant` with `:where()` (not `:is()`) and include both `.dark` and `.dark *`:
183
+
184
+ ```css
185
+ @custom-variant dark (&:where(.dark, .dark *));
186
+ ```
187
+
188
+ **Tailwind v3:** `darkMode: "class"` in `tailwind.config.ts`.
189
+
190
+ Use `next-themes` for the theme provider. Never implement manual dark mode toggling.
191
+
192
+ ### Adding custom colors
193
+
194
+ Define the variable in the theme, then reference with the semantic token:
195
+
196
+ ```css
197
+ @theme inline {
198
+ --color-brand: oklch(0.6 0.2 250);
199
+ --color-brand-foreground: oklch(0.98 0 0);
200
+ }
201
+ ```
202
+
203
+ ```tsx
204
+ <div className="bg-brand text-brand-foreground">
205
+ ```
206
+
207
+ ## Composition patterns
208
+
209
+ ### Compound components and Slot
210
+
211
+ Radix compound components use React context. Each piece must be nested correctly. `asChild` swaps the rendered element for its child via Slot (prop merging):
212
+
213
+ ```tsx
214
+ // Slot merges Trigger's event handlers and ARIA props onto the Link
215
+ <NavigationMenuLink asChild>
216
+ <Link href="/about">About</Link>
217
+ </NavigationMenuLink>
218
+ ```
219
+
220
+ ### DropdownMenu + Dialog combo
221
+
222
+ Wrap both in a shared parent. Use `e.preventDefault()` in the menu item's `onSelect` to prevent the menu from stealing focus from the dialog. Manage dialog state with `useState`, not `DialogTrigger` — the trigger lives inside the menu, not the dialog.
223
+
224
+ ### Sidebar
225
+
226
+ The Sidebar component is complex (30+ sub-components). Minimum viable setup:
227
+
228
+ ```tsx
229
+ import { SidebarProvider, SidebarTrigger, SidebarInset } from "@/components/ui/sidebar";
230
+ import { AppSidebar } from "@/components/app-sidebar";
231
+
232
+ export default function Layout({ children }: { children: React.ReactNode }) {
233
+ return (
234
+ <SidebarProvider>
235
+ <AppSidebar />
236
+ <SidebarInset>
237
+ <header>
238
+ <SidebarTrigger />
239
+ </header>
240
+ <main>{children}</main>
241
+ </SidebarInset>
242
+ </SidebarProvider>
243
+ );
244
+ }
245
+ ```
246
+
247
+ ## Forms
248
+
249
+ shadcn Form wraps react-hook-form with automatic ARIA wiring. Don't bypass with raw `register()`.
250
+
251
+ ### Standard pattern
252
+
253
+ ```tsx
254
+ const schema = z.object({
255
+ name: z.string().min(1, "Required"),
256
+ role: z.string().min(1, "Select a role"),
257
+ });
258
+
259
+ function CreateUserForm() {
260
+ const form = useForm<z.infer<typeof schema>>({
261
+ resolver: zodResolver(schema),
262
+ defaultValues: { name: "", role: "" }, // required: avoids controlled/uncontrolled warnings
263
+ });
264
+
265
+ return (
266
+ <Form {...form}>
267
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
268
+ {/* Text input: spread field directly */}
269
+ <FormField control={form.control} name="name" render={({ field }) => (
270
+ <FormItem>
271
+ <FormLabel>Name</FormLabel>
272
+ <FormControl><Input {...field} /></FormControl>
273
+ <FormMessage />
274
+ </FormItem>
275
+ )} />
276
+
277
+ {/* Select: wire onValueChange explicitly, never spread field */}
278
+ <FormField control={form.control} name="role" render={({ field }) => (
279
+ <FormItem>
280
+ <FormLabel>Role</FormLabel>
281
+ <Select onValueChange={field.onChange} value={field.value}>
282
+ <FormControl>
283
+ <SelectTrigger><SelectValue placeholder="Select a role" /></SelectTrigger>
284
+ </FormControl>
285
+ <SelectContent>
286
+ <SelectItem value="admin">Admin</SelectItem>
287
+ <SelectItem value="member">Member</SelectItem>
288
+ </SelectContent>
289
+ </Select>
290
+ <FormMessage />
291
+ </FormItem>
292
+ )} />
293
+
294
+ <Button type="submit">Create</Button>
295
+ </form>
296
+ </Form>
297
+ );
298
+ }
299
+ ```
300
+
301
+ Checkbox/Switch: use `checked={field.value} onCheckedChange={field.onChange}` (never spread `{...field}`).
302
+
303
+ ### FormField nesting order
304
+
305
+ Always: `FormField > FormItem > FormLabel + FormControl > [input] + FormDescription + FormMessage`. FormControl must wrap exactly one child element (Slot constraint).
306
+
307
+ ## Data tables
308
+
309
+ ### File structure
310
+
311
+ ```
312
+ components/
313
+ users/
314
+ columns.tsx # column definitions (stable reference, outside component)
315
+ data-table.tsx # reusable DataTable shell (pagination, sorting, filtering)
316
+ page.tsx # fetches data, renders <DataTable columns={columns} data={data} />
317
+ ```
318
+
319
+ ### Column rules
320
+
321
+ Pair column features with table feature configuration:
322
+
323
+ | Column Feature | Required Table Config |
324
+ |---|---|
325
+ | `enableSorting` on column | `getSortedRowModel()` in useReactTable |
326
+ | `enableColumnFilter` | `getFilteredRowModel()` |
327
+ | `cell` with row actions | Column with `id: "actions"`, no `accessorKey` |
328
+
329
+ ### Row selection
330
+
331
+ Use `onCheckedChange` (not `onChange`) for the checkbox column:
332
+
333
+ ```tsx
334
+ {
335
+ id: "select",
336
+ header: ({ table }) => (
337
+ <Checkbox
338
+ checked={table.getIsAllPageRowsSelected()}
339
+ onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
340
+ />
341
+ ),
342
+ cell: ({ row }) => (
343
+ <Checkbox
344
+ checked={row.getIsSelected()}
345
+ onCheckedChange={(value) => row.toggleSelected(!!value)}
346
+ />
347
+ ),
348
+ }
349
+ ```
350
+
351
+ ## Accessibility
352
+
353
+ Radix primitives provide: focus trapping in modals, arrow-key navigation in menus, `aria-expanded`/`aria-controls` on triggers, `role` attributes, Escape to close, screen reader announcements.
354
+
355
+ You handle: `FormLabel` association with inputs (via FormField), visible focus rings (`focus-visible:ring-2`), color contrast (WCAG AA minimum), skip-to-content links, meaningful `alt` text on images, `DialogTitle` and `DialogDescription` in every Dialog (required by Radix, warns if missing).
356
+
357
+ ## CLI and configuration
358
+
359
+ ### Commands
360
+
361
+ ```bash
362
+ npx shadcn@latest init # initialize project, creates components.json
363
+ npx shadcn@latest add button # add a component
364
+ npx shadcn@latest add sonner # add sonner (toast replacement)
365
+ npx shadcn@latest diff button # show upstream changes to a component
366
+ npx shadcn@latest create # scaffold a new registry block (Dec 2025+)
367
+ ```
368
+
369
+ ### components.json
370
+
371
+ Key settings:
372
+
373
+ | Field | Effect |
374
+ |---|---|
375
+ | `style` | `"new-york"` only. `"default"` is deprecated. |
376
+ | `rsc` | `true` adds `"use client"` to components automatically |
377
+ | `aliases.components` | Import path prefix (e.g., `@/components`) |
378
+ | `aliases.utils` | Path to `cn()` utility (e.g., `@/lib/utils`) |
379
+
380
+ ### Deprecations and migrations
381
+
382
+ | Deprecated | Replacement |
383
+ |---|---|
384
+ | `shadcn-ui` CLI package | `shadcn` (use `npx shadcn@latest`) |
385
+ | `"default"` style | `"new-york"` style |
386
+ | Toast component | Sonner |
387
+ | `tailwindcss-animate` | `tw-animate-css` |
388
+ | Individual `@radix-ui/react-*` | Unified `radix-ui` package |
389
+
390
+ ## Decision tables
391
+
392
+ ### Overlay selection
393
+
394
+ | Need | Component | Key Trait |
395
+ |---|---|---|
396
+ | Confirm destructive action | AlertDialog | Blocks interaction, requires explicit response |
397
+ | Form or complex content | Dialog | Focus-trapped modal, closes on overlay click |
398
+ | Side panel (filters, nav, detail) | Sheet | Slides from edge, good for secondary content |
399
+ | Mobile-friendly bottom panel | Drawer | Touch-friendly, swipe to dismiss (uses vaul) |
400
+ | Anchored to trigger, lightweight | Popover | Positioned relative to trigger, no overlay |
401
+ | Brief hint on hover | Tooltip | Hover/focus only, no interactive content |
402
+ | Rich preview on hover | HoverCard | Hover card with delay, supports interactive content |
403
+ | Action list from trigger | DropdownMenu | Click to open, keyboard-navigable menu |
404
+ | Action list from right-click | ContextMenu | Right-click triggered, same API as DropdownMenu |
405
+
406
+ ### Selection component
407
+
408
+ | Need | Component |
409
+ |---|---|
410
+ | Fixed list, <10 items | Select |
411
+ | Searchable list | Combobox (popover + command) |
412
+ | Searchable with groups/actions | Command (standalone) |
413
+ | Multi-select from small set | ToggleGroup |
414
+
415
+ ### Customization approach
416
+
417
+ | Situation | Approach |
418
+ |---|---|
419
+ | Global style change (border radius, colors) | Edit `components/ui/` directly |
420
+ | App-specific defaults (icon + label combos) | Wrapper in `components/app/` |
421
+ | One-off layout composition | Inline composition in the page/feature |
422
+
423
+ ## Testing
424
+
425
+ ### Portal rendering
426
+
427
+ Dialog, Popover, Sheet, Select, and DropdownMenu content render into a portal at `document.body`. Query by role, not by DOM hierarchy:
428
+
429
+ ```tsx
430
+ // Wrong: content is not inside the trigger's DOM subtree
431
+ const content = within(triggerParent).getByText("Option A");
432
+
433
+ // Correct: query from screen (portaled to body)
434
+ const content = screen.getByRole("option", { name: "Option A" });
435
+ ```
436
+
437
+ ### User events
438
+
439
+ Radix components listen on `pointerdown`, not `click`. Use `userEvent.setup()` (not `fireEvent`):
440
+
441
+ ```tsx
442
+ import userEvent from "@testing-library/user-event";
443
+
444
+ it("opens the dialog", async () => {
445
+ const user = userEvent.setup();
446
+ render(<MyDialog />);
447
+ await user.click(screen.getByRole("button", { name: "Open" }));
448
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
449
+ });
450
+ ```
451
+
452
+ ### JSDOM limitations
453
+
454
+ JSDOM does not implement `pointerdown` or `resize` observers fully. For components that rely on these (Sheet, Drawer, Resizable), test in a real browser environment (Playwright) or mock the specific Radix primitive.