@archetypeai/ds-cli 0.3.7

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 (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -0
  3. package/bin.js +77 -0
  4. package/commands/add.js +42 -0
  5. package/commands/create.js +238 -0
  6. package/commands/init.js +199 -0
  7. package/files/AGENTS.md +63 -0
  8. package/files/CLAUDE.md +63 -0
  9. package/files/LICENSE +21 -0
  10. package/files/rules/accessibility.md +219 -0
  11. package/files/rules/charts.md +352 -0
  12. package/files/rules/components.md +267 -0
  13. package/files/rules/design-principles.md +56 -0
  14. package/files/rules/linting.md +31 -0
  15. package/files/rules/state.md +405 -0
  16. package/files/rules/styling.md +245 -0
  17. package/files/skills/apply-ds/SKILL.md +117 -0
  18. package/files/skills/apply-ds/scripts/setup.sh +271 -0
  19. package/files/skills/build-pattern/SKILL.md +202 -0
  20. package/files/skills/create-dashboard/SKILL.md +189 -0
  21. package/files/skills/deploy-worker/SKILL.md +231 -0
  22. package/files/skills/deploy-worker/references/wrangler-commands.md +327 -0
  23. package/files/skills/fix-accessibility/SKILL.md +184 -0
  24. package/files/skills/fix-metadata/SKILL.md +118 -0
  25. package/files/skills/fix-metadata/assets/favicon.ico +0 -0
  26. package/files/skills/setup-chart/SKILL.md +225 -0
  27. package/files/skills/setup-chart/data/embedding.csv +42 -0
  28. package/files/skills/setup-chart/data/timeseries.csv +173 -0
  29. package/files/skills/setup-chart/references/scatter-chart.md +229 -0
  30. package/files/skills/setup-chart/references/sensor-chart.md +156 -0
  31. package/lib/add-ds-config-codeagent.js +154 -0
  32. package/lib/add-ds-ui-svelte.js +93 -0
  33. package/lib/scaffold-ds-svelte-project.js +272 -0
  34. package/lib/use-package-manager.js +65 -0
  35. package/lib/use-shadcn-svelte-registry.js +26 -0
  36. package/lib/validate-url.js +31 -0
  37. package/package.json +34 -0
@@ -0,0 +1,405 @@
1
+ ---
2
+ paths:
3
+ - '**/*.svelte'
4
+ - '**/*.svelte.js'
5
+ - '**/*.svelte.ts'
6
+ ---
7
+
8
+ # Svelte 5 State Management
9
+
10
+ Svelte 5 uses runes - special `$` prefixed primitives that control reactivity.
11
+
12
+ ## $state - Reactive State
13
+
14
+ Declare reactive state that triggers UI updates when changed:
15
+
16
+ ```svelte
17
+ <script>
18
+ let count = $state(0);
19
+ let user = $state({ name: 'Alice', age: 30 });
20
+ </script>
21
+
22
+ <button onclick={() => count++}>
23
+ Clicked {count} times
24
+ </button>
25
+ ```
26
+
27
+ ### Deep Reactivity
28
+
29
+ Objects and arrays become deeply reactive proxies:
30
+
31
+ ```svelte
32
+ <script>
33
+ let todos = $state([{ done: false, text: 'Learn Svelte 5' }]);
34
+
35
+ // This triggers updates - the array is a proxy
36
+ todos.push({ done: false, text: 'Build app' });
37
+
38
+ // This also triggers updates
39
+ todos[0].done = true;
40
+ </script>
41
+ ```
42
+
43
+ ### $state.raw - Non-Proxied State
44
+
45
+ For large objects you don't plan to mutate, use `$state.raw` for better performance:
46
+
47
+ ```svelte
48
+ <script>
49
+ // Must reassign entirely, can't mutate
50
+ let data = $state.raw({ items: largeArray });
51
+
52
+ // This won't trigger updates
53
+ data.items.push(newItem);
54
+
55
+ // This will - full reassignment
56
+ data = { items: [...data.items, newItem] };
57
+ </script>
58
+ ```
59
+
60
+ ### $state.snapshot - Get Plain Object
61
+
62
+ Get a non-reactive copy of a state proxy (useful for APIs that don't expect proxies):
63
+
64
+ ```svelte
65
+ <script>
66
+ let state = $state({ count: 0 });
67
+
68
+ function save() {
69
+ // Pass plain object to external API
70
+ const snapshot = $state.snapshot(state);
71
+ localStorage.setItem('state', JSON.stringify(snapshot));
72
+ }
73
+ </script>
74
+ ```
75
+
76
+ ### Class Fields
77
+
78
+ Use $state in class fields:
79
+
80
+ ```svelte
81
+ <script>
82
+ class Counter {
83
+ count = $state(0);
84
+
85
+ increment() {
86
+ this.count++;
87
+ }
88
+ }
89
+
90
+ let counter = new Counter();
91
+ </script>
92
+ ```
93
+
94
+ ## $derived - Computed Values
95
+
96
+ Declare values that automatically update when dependencies change:
97
+
98
+ ```svelte
99
+ <script>
100
+ let count = $state(0);
101
+ let doubled = $derived(count * 2);
102
+ let quadrupled = $derived(doubled * 2);
103
+ </script>
104
+
105
+ <p>{count} × 2 = {doubled}</p><p>{count} × 4 = {quadrupled}</p>
106
+ ```
107
+
108
+ ### $derived.by - Complex Derivations
109
+
110
+ For multi-line logic, use `$derived.by`:
111
+
112
+ ```svelte
113
+ <script>
114
+ let items = $state([1, 2, 3, 4, 5]);
115
+
116
+ let stats = $derived.by(() => {
117
+ const sum = items.reduce((a, b) => a + b, 0);
118
+ const avg = sum / items.length;
119
+ const max = Math.max(...items);
120
+ return { sum, avg, max };
121
+ });
122
+ </script>
123
+
124
+ <p>Sum: {stats.sum}, Avg: {stats.avg}, Max: {stats.max}</p>
125
+ ```
126
+
127
+ ### Overriding Derived Values (Optimistic UI)
128
+
129
+ Derived values can be temporarily overridden:
130
+
131
+ ```svelte
132
+ <script>
133
+ let { post } = $props();
134
+ let likes = $derived(post.likes);
135
+
136
+ async function like() {
137
+ likes++; // Optimistic update
138
+ try {
139
+ await api.likePost(post.id);
140
+ } catch {
141
+ likes--; // Rollback on failure
142
+ }
143
+ }
144
+ </script>
145
+ ```
146
+
147
+ ## $effect - Side Effects
148
+
149
+ Run code when reactive dependencies change:
150
+
151
+ ```svelte
152
+ <script>
153
+ let count = $state(0);
154
+
155
+ $effect(() => {
156
+ console.log('Count changed to', count);
157
+ document.title = `Count: ${count}`;
158
+ });
159
+ </script>
160
+ ```
161
+
162
+ ### Teardown / Cleanup
163
+
164
+ Return a function to clean up before re-running or on destroy:
165
+
166
+ ```svelte
167
+ <script>
168
+ let interval = $state(1000);
169
+
170
+ $effect(() => {
171
+ const id = setInterval(() => {
172
+ console.log('tick');
173
+ }, interval);
174
+
175
+ // Cleanup: runs before re-run and on component destroy
176
+ return () => clearInterval(id);
177
+ });
178
+ </script>
179
+ ```
180
+
181
+ ### $effect.pre - Run Before DOM Updates
182
+
183
+ Rarely needed - runs before DOM updates:
184
+
185
+ ```svelte
186
+ <script>
187
+ let messages = $state([]);
188
+ let container;
189
+
190
+ $effect.pre(() => {
191
+ // Check scroll position before DOM updates
192
+ messages.length; // Track this dependency
193
+
194
+ if (container && shouldAutoScroll(container)) {
195
+ // Will scroll after DOM updates
196
+ tick().then(() => container.scrollTo(0, container.scrollHeight));
197
+ }
198
+ });
199
+ </script>
200
+ ```
201
+
202
+ ### When NOT to Use $effect
203
+
204
+ **Don't sync state - use $derived instead:**
205
+
206
+ ```svelte
207
+ <script>
208
+ let count = $state(0);
209
+
210
+ // BAD - don't do this
211
+ let doubled = $state(0);
212
+ $effect(() => {
213
+ doubled = count * 2;
214
+ });
215
+
216
+ // GOOD - use $derived
217
+ let doubled = $derived(count * 2);
218
+ </script>
219
+ ```
220
+
221
+ **Don't create infinite loops:**
222
+
223
+ ```svelte
224
+ <script>
225
+ let count = $state(0);
226
+
227
+ // BAD - infinite loop! reads and writes count
228
+ $effect(() => {
229
+ count = count + 1;
230
+ });
231
+ </script>
232
+ ```
233
+
234
+ **Use callbacks for linked values instead of effects:**
235
+
236
+ ```svelte
237
+ <script>
238
+ let spent = $state(0);
239
+ let left = $derived(100 - spent);
240
+
241
+ // Update spent via callback, not effect
242
+ function updateLeft(newLeft) {
243
+ spent = 100 - newLeft;
244
+ }
245
+ </script>
246
+
247
+ <input type="range" bind:value={spent} max={100} />
248
+ <input type="range" value={left} oninput={(e) => updateLeft(+e.target.value)} max={100} />
249
+ ```
250
+
251
+ ## $bindable - Two-Way Binding
252
+
253
+ Enable parent components to bind to a prop:
254
+
255
+ ```svelte
256
+ <!-- FancyInput.svelte -->
257
+ <script>
258
+ let { value = $bindable(''), ...props } = $props();
259
+ </script>
260
+
261
+ <input bind:value {value} {...props} />
262
+ ```
263
+
264
+ ```svelte
265
+ <!-- Parent.svelte -->
266
+ <script>
267
+ import FancyInput from './FancyInput.svelte';
268
+ let name = $state('');
269
+ </script>
270
+
271
+ <FancyInput bind:value={name} /><p>Hello, {name}!</p>
272
+ ```
273
+
274
+ See [components.md](./components.md) for more on `$props()` and `$bindable()`.
275
+
276
+ ## $inspect - Debugging (Dev Only)
277
+
278
+ Log when values change (removed in production builds):
279
+
280
+ ```svelte
281
+ <script>
282
+ let count = $state(0);
283
+ let user = $state({ name: 'Alice' });
284
+
285
+ // Logs whenever count or user changes
286
+ $inspect(count, user);
287
+ </script>
288
+ ```
289
+
290
+ ### Custom Inspection
291
+
292
+ ```svelte
293
+ <script>
294
+ let count = $state(0);
295
+
296
+ $inspect(count).with((type, value) => {
297
+ if (type === 'update') {
298
+ debugger; // Break on changes
299
+ }
300
+ });
301
+ </script>
302
+ ```
303
+
304
+ ### Trace Effect Dependencies
305
+
306
+ ```svelte
307
+ <script>
308
+ $effect(() => {
309
+ $inspect.trace(); // Must be first statement
310
+ doSomethingComplex();
311
+ });
312
+ </script>
313
+ ```
314
+
315
+ ## Common Gotchas
316
+
317
+ ### Destructuring Breaks Reactivity
318
+
319
+ ```svelte
320
+ <script>
321
+ let user = $state({ name: 'Alice', age: 30 });
322
+
323
+ // BAD - name and age are not reactive
324
+ let { name, age } = user;
325
+
326
+ // GOOD - access via object
327
+ // In template: {user.name}, {user.age}
328
+ </script>
329
+ ```
330
+
331
+ ### Async Code Doesn't Track Dependencies
332
+
333
+ ```svelte
334
+ <script>
335
+ let color = $state('red');
336
+ let size = $state(100);
337
+
338
+ $effect(() => {
339
+ // color is tracked (read synchronously)
340
+ console.log(color);
341
+
342
+ setTimeout(() => {
343
+ // size is NOT tracked (read async)
344
+ console.log(size);
345
+ }, 100);
346
+ });
347
+ </script>
348
+ ```
349
+
350
+ ### Exporting Reassigned State
351
+
352
+ Can't directly export state that gets reassigned:
353
+
354
+ ```javascript
355
+ // state.svelte.js
356
+
357
+ // BAD - won't work across modules
358
+ export let count = $state(0);
359
+ export function increment() {
360
+ count++;
361
+ }
362
+
363
+ // GOOD - wrap in object
364
+ export const counter = $state({ count: 0 });
365
+ export function increment() {
366
+ counter.count++;
367
+ }
368
+
369
+ // GOOD - use getters
370
+ let count = $state(0);
371
+ export function getCount() {
372
+ return count;
373
+ }
374
+ export function increment() {
375
+ count++;
376
+ }
377
+ ```
378
+
379
+ ### Effects Only Run in Browser
380
+
381
+ `$effect` doesn't run during SSR:
382
+
383
+ ```svelte
384
+ <script>
385
+ $effect(() => {
386
+ // Safe to use browser APIs here
387
+ window.addEventListener('resize', handleResize);
388
+ return () => window.removeEventListener('resize', handleResize);
389
+ });
390
+ </script>
391
+ ```
392
+
393
+ ## Quick Reference
394
+
395
+ | Rune | Purpose | Example |
396
+ | ------------------- | ----------------- | ---------------------------------------- |
397
+ | `$state(value)` | Reactive state | `let count = $state(0)` |
398
+ | `$state.raw(value)` | Non-proxied state | `let data = $state.raw(bigObject)` |
399
+ | `$derived(expr)` | Computed value | `let doubled = $derived(count * 2)` |
400
+ | `$derived.by(fn)` | Complex computed | `$derived.by(() => { ... })` |
401
+ | `$effect(fn)` | Side effect | `$effect(() => { ... })` |
402
+ | `$effect.pre(fn)` | Pre-DOM effect | `$effect.pre(() => { ... })` |
403
+ | `$props()` | Component props | `let { foo } = $props()` |
404
+ | `$bindable()` | Two-way prop | `let { value = $bindable() } = $props()` |
405
+ | `$inspect(...)` | Debug logging | `$inspect(count)` |
@@ -0,0 +1,245 @@
1
+ ---
2
+ paths:
3
+ - '**/*.css'
4
+ - '**/*.svelte'
5
+ ---
6
+
7
+ # Styling Rules
8
+
9
+ ## Semantic Token Reference
10
+
11
+ ### Background & Foreground Colors
12
+
13
+ | Token | Usage |
14
+ | ------------------------- | ---------------------------- |
15
+ | `bg-background` | Page/app background |
16
+ | `text-foreground` | Primary text color |
17
+ | `bg-card` | Card backgrounds |
18
+ | `text-card-foreground` | Text on cards |
19
+ | `bg-popover` | Popover/dropdown backgrounds |
20
+ | `text-popover-foreground` | Text in popovers |
21
+ | `bg-muted` | Muted/subtle backgrounds |
22
+ | `text-muted-foreground` | Secondary/muted text |
23
+
24
+ ### Interactive Colors
25
+
26
+ | Token | Usage |
27
+ | --------------------------- | ----------------------------- |
28
+ | `bg-primary` | Primary buttons, links |
29
+ | `text-primary-foreground` | Text on primary backgrounds |
30
+ | `bg-secondary` | Secondary buttons |
31
+ | `text-secondary-foreground` | Text on secondary backgrounds |
32
+ | `bg-accent` | Hover states, highlights |
33
+ | `text-accent-foreground` | Text on accent backgrounds |
34
+ | `bg-destructive` | Delete/error actions |
35
+
36
+ ### Border & Input Colors
37
+
38
+ | Token | Usage |
39
+ | --------------- | -------------------- |
40
+ | `border-border` | Default border color |
41
+ | `border-input` | Input field borders |
42
+ | `ring-ring` | Focus ring color |
43
+
44
+ ### Chart Colors
45
+
46
+ | Token | Usage |
47
+ | ----------- | -------------------------------- |
48
+ | `--chart-1` | First series color (purple) |
49
+ | `--chart-2` | Second series color (red-orange) |
50
+ | `--chart-3` | Third series color (green) |
51
+ | `--chart-4` | Fourth series color (yellow) |
52
+ | `--chart-5` | Fifth series color (coral) |
53
+
54
+ ### ATAI Brand Colors
55
+
56
+ | Token | Usage |
57
+ | -------------------- | --------------------- |
58
+ | `bg-atai-neutral` | Brand blue accent |
59
+ | `text-atai-good` | Success/healthy state |
60
+ | `text-atai-warning` | Warning state |
61
+ | `text-atai-critical` | Critical/error state |
62
+
63
+ ### Sidebar Colors
64
+
65
+ | Token | Usage |
66
+ | ------------------------- | -------------------- |
67
+ | `bg-sidebar` | Sidebar background |
68
+ | `text-sidebar-foreground` | Sidebar text |
69
+ | `bg-sidebar-accent` | Sidebar hover/active |
70
+ | `border-sidebar-border` | Sidebar borders |
71
+
72
+ ## When to Use Semantic vs Standard Tailwind
73
+
74
+ ### Use Semantic Tokens For:
75
+
76
+ ```svelte
77
+ <!-- Themed colors that should change with light/dark mode -->
78
+ <div class="bg-background text-foreground">
79
+ <div class="bg-card border-border">
80
+ <button class="bg-primary text-primary-foreground">
81
+ <span class="text-muted-foreground">
82
+ ```
83
+
84
+ ### Use Standard Tailwind For:
85
+
86
+ ```svelte
87
+ <!-- Spacing and sizing -->
88
+ <div class="p-4 gap-2 w-full h-screen">
89
+
90
+ <!-- Layout utilities -->
91
+ <div class="flex items-center justify-between">
92
+ <div class="grid grid-cols-2 gap-4">
93
+ <div class="absolute top-0 left-0">
94
+
95
+ <!-- One-off custom colors -->
96
+ <div class="bg-linear-to-r from-purple-500 to-pink-500">
97
+ <div class="text-amber-500"> <!-- specific non-themed color -->
98
+
99
+ <!-- Responsive breakpoints -->
100
+ <div class="sm:flex md:grid lg:hidden">
101
+
102
+ <!-- Animations and transitions -->
103
+ <div class="transition-all duration-200 ease-in-out">
104
+ ```
105
+
106
+ ## Dark Mode
107
+
108
+ Dark mode activates via `.dark` class on the root element:
109
+
110
+ ```html
111
+ <html class="dark">
112
+ ...
113
+ </html>
114
+ ```
115
+
116
+ Semantic tokens automatically switch values in dark mode. No manual dark: prefixes needed for themed colors:
117
+
118
+ ```svelte
119
+ <!-- This works in both light and dark mode automatically -->
120
+ <div class="bg-background text-foreground border-border">
121
+ ```
122
+
123
+ For custom dark mode overrides, use the `dark:` prefix:
124
+
125
+ ```svelte
126
+ <div class="bg-white dark:bg-slate-900"> <!-- custom dark override -->
127
+ ```
128
+
129
+ ## cn() Utility
130
+
131
+ Import and use for all class composition:
132
+
133
+ ```javascript
134
+ import { cn } from '$lib/utils.js';
135
+ ```
136
+
137
+ ```svelte
138
+ <div class={cn(
139
+ 'base-classes here',
140
+ conditional && 'conditional-classes',
141
+ className
142
+ )}>
143
+ ```
144
+
145
+ The utility:
146
+
147
+ - Uses `clsx` for conditional class joining
148
+ - Uses `tailwind-merge` to resolve conflicts (last wins)
149
+
150
+ Example conflict resolution:
151
+
152
+ ```javascript
153
+ cn('p-4', 'p-2'); // → 'p-2' (later wins)
154
+ cn('bg-red-500', 'bg-background'); // → 'bg-background' (later wins)
155
+ ```
156
+
157
+ ## CSS Import Order
158
+
159
+ In your global CSS file:
160
+
161
+ ```css
162
+ @import '@archetypeai/ds-lib-fonts-internal';
163
+ @import '@archetypeai/ds-lib-tokens/theme.css';
164
+ @import 'tailwindcss';
165
+ @import 'tw-animate-css'; /* optional, for animations */
166
+ ```
167
+
168
+ Order matters - tokens must come before Tailwind.
169
+
170
+ ## Tailwind v4 Specifics
171
+
172
+ ### @theme Directive
173
+
174
+ Custom theme values are defined with `@theme`:
175
+
176
+ ```css
177
+ @theme {
178
+ --font-sans: 'PP Neue Montreal', system-ui, sans-serif;
179
+ --radius-md: var(--radius);
180
+ --spacing-md: 0.75rem;
181
+ --color-primary: var(--primary);
182
+ }
183
+ ```
184
+
185
+ ### CSS Variables in Classes
186
+
187
+ You can use CSS variables directly:
188
+
189
+ ```svelte
190
+ <div style:--legend-color={seriesColor}>
191
+ <span class="bg-(--legend-color)"> </span>
192
+ </div>
193
+ ```
194
+
195
+ ### No @apply in Components
196
+
197
+ Prefer Tailwind classes directly in markup. Use @apply only in base layer CSS:
198
+
199
+ ```css
200
+ /* In theme.css - OK */
201
+ @layer base {
202
+ body {
203
+ @apply bg-background text-foreground;
204
+ }
205
+ }
206
+ ```
207
+
208
+ ```svelte
209
+ <!-- In components - use classes directly -->
210
+ <div class="bg-background text-foreground">
211
+ ```
212
+
213
+ ## Common Patterns
214
+
215
+ ### Focus States
216
+
217
+ ```svelte
218
+ <button class="focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 outline-none">
219
+ ```
220
+
221
+ ### Disabled States
222
+
223
+ ```svelte
224
+ <button class="disabled:pointer-events-none disabled:opacity-50">
225
+ ```
226
+
227
+ ### Data State Animations
228
+
229
+ ```svelte
230
+ <div class="data-[state=open]:animate-in data-[state=closed]:animate-out">
231
+ ```
232
+
233
+ ### Responsive Design
234
+
235
+ ```svelte
236
+ <div class="w-full max-w-[calc(100%-2rem)] sm:max-w-lg">
237
+ <div class="flex flex-col sm:flex-row">
238
+ ```
239
+
240
+ ### Icon Sizing
241
+
242
+ ```svelte
243
+ <!-- Default icon sizing utility -->
244
+ <button class="[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
245
+ ```