@vertz/create-vertz-app 0.2.15 → 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,WAAW,EAAE,MAAM;CAIhC;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzF"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,WAAW,EAAE,MAAM;CAIhC;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DzF"}
package/dist/scaffold.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { appComponentTemplate, bunfigTemplate, bunPluginShimTemplate, clientTemplate, dbTemplate, entryClientTemplate, envExampleTemplate, envModuleTemplate, envTemplate, gitignoreTemplate, homePageTemplate, packageJsonTemplate, schemaTemplate, serverTemplate, tasksEntityTemplate, themeTemplate, tsconfigTemplate, vertzConfigTemplate, } from './templates/index.js';
3
+ import { apiDevelopmentRuleTemplate, appComponentTemplate, bunfigTemplate, bunPluginShimTemplate, claudeMdTemplate, clientTemplate, dbTemplate, entryClientTemplate, envExampleTemplate, envModuleTemplate, envTemplate, gitignoreTemplate, homePageTemplate, packageJsonTemplate, schemaTemplate, serverTemplate, tasksEntityTemplate, themeTemplate, tsconfigTemplate, uiDevelopmentRuleTemplate, vertzConfigTemplate, } from './templates/index.js';
4
4
  /**
5
5
  * Error thrown when the project directory already exists
6
6
  */
@@ -35,10 +35,12 @@ export async function scaffold(parentDir, options) {
35
35
  const entitiesDir = path.join(apiDir, 'entities');
36
36
  const pagesDir = path.join(srcDir, 'pages');
37
37
  const stylesDir = path.join(srcDir, 'styles');
38
+ const claudeRulesDir = path.join(projectDir, '.claude', 'rules');
38
39
  await Promise.all([
39
40
  fs.mkdir(entitiesDir, { recursive: true }),
40
41
  fs.mkdir(pagesDir, { recursive: true }),
41
42
  fs.mkdir(stylesDir, { recursive: true }),
43
+ fs.mkdir(claudeRulesDir, { recursive: true }),
42
44
  ]);
43
45
  // Write all files in parallel
44
46
  await Promise.all([
@@ -63,6 +65,10 @@ export async function scaffold(parentDir, options) {
63
65
  writeFile(srcDir, 'entry-client.ts', entryClientTemplate()),
64
66
  writeFile(pagesDir, 'home.tsx', homePageTemplate()),
65
67
  writeFile(stylesDir, 'theme.ts', themeTemplate()),
68
+ // LLM rules
69
+ writeFile(projectDir, 'CLAUDE.md', claudeMdTemplate(projectName)),
70
+ writeFile(claudeRulesDir, 'api-development.md', apiDevelopmentRuleTemplate()),
71
+ writeFile(claudeRulesDir, 'ui-development.md', uiDevelopmentRuleTemplate()),
66
72
  ]);
67
73
  }
68
74
  /**
@@ -1,3 +1,15 @@
1
+ /**
2
+ * CLAUDE.md — project-level instructions for LLMs
3
+ */
4
+ export declare function claudeMdTemplate(projectName: string): string;
5
+ /**
6
+ * .claude/rules/api-development.md — API conventions for LLMs
7
+ */
8
+ export declare function apiDevelopmentRuleTemplate(): string;
9
+ /**
10
+ * .claude/rules/ui-development.md — UI conventions for LLMs
11
+ */
12
+ export declare function uiDevelopmentRuleTemplate(): string;
1
13
  /**
2
14
  * Package.json template — full-stack deps + #generated imports map
3
15
  */
@@ -63,7 +75,7 @@ export declare function appComponentTemplate(): string;
63
75
  */
64
76
  export declare function entryClientTemplate(): string;
65
77
  /**
66
- * src/styles/theme.ts — configureTheme from @vertz/theme-shadcn
78
+ * src/styles/theme.ts — configureThemeBase from @vertz/theme-shadcn/base
67
79
  */
68
80
  export declare function themeTemplate(): string;
69
81
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA6B/D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAoBzC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAa5C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAIpC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAIvC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAc9C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CA6B1C;AAID;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAW1C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAoBvC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAavC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CASnC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAe5C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAOvC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAgD7C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAWtC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAkIzC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA6B5D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAyJnD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CA0NlD;AAID;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA6B/D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAoBzC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAa5C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAIpC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAI3C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAIvC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAc9C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CA6B1C;AAID;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAW1C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAoBvC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAavC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CASnC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAe5C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAOvC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAgD7C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAW5C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAWtC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAkIzC"}
@@ -1,3 +1,416 @@
1
+ // ── LLM rules templates ───────────────────────────────────
2
+ /**
3
+ * CLAUDE.md — project-level instructions for LLMs
4
+ */
5
+ export function claudeMdTemplate(projectName) {
6
+ return `# ${projectName}
7
+
8
+ A full-stack TypeScript application built with [Vertz](https://vertz.dev).
9
+
10
+ ## Stack
11
+
12
+ - Runtime: Bun
13
+ - Framework: Vertz (full-stack TypeScript)
14
+ - Language: TypeScript (strict mode)
15
+ - Docs: https://docs.vertz.dev
16
+
17
+ ## Development
18
+
19
+ \`\`\`bash
20
+ bun install # Install dependencies
21
+ bun run dev # Start dev server with HMR
22
+ bun run build # Production build
23
+ \`\`\`
24
+
25
+ The dev server automatically runs codegen and migrations when files change.
26
+
27
+ ## Conventions
28
+
29
+ - See \`.claude/rules/\` for API and UI development conventions
30
+ - Refer to https://docs.vertz.dev for full framework documentation
31
+ - Entity files use the \`.entity.ts\` suffix
32
+ - The Vertz compiler handles all reactivity — never use \`.value\`, \`signal()\`, or \`computed()\` manually
33
+ `;
34
+ }
35
+ /**
36
+ * .claude/rules/api-development.md — API conventions for LLMs
37
+ */
38
+ export function apiDevelopmentRuleTemplate() {
39
+ return `# API Development
40
+
41
+ ## Imports
42
+
43
+ All Vertz packages are available through the \`vertz\` meta-package:
44
+
45
+ \`\`\`ts
46
+ import { createServer, entity, createEnv } from 'vertz/server';
47
+ import { s } from 'vertz/schema';
48
+ import { d } from 'vertz/db';
49
+ \`\`\`
50
+
51
+ ## Defining Tables and Models
52
+
53
+ Use \`d.table()\` to define database tables and \`d.model()\` to create a typed model:
54
+
55
+ \`\`\`ts
56
+ import { d } from 'vertz/db';
57
+
58
+ export const postsTable = d.table('posts', {
59
+ id: d.uuid().primary(),
60
+ title: d.text(),
61
+ body: d.text(),
62
+ published: d.boolean().default(false),
63
+ authorId: d.uuid(),
64
+ createdAt: d.timestamp().default('now').readOnly(),
65
+ updatedAt: d.timestamp().autoUpdate().readOnly(),
66
+ });
67
+
68
+ export const postsModel = d.model(postsTable);
69
+ \`\`\`
70
+
71
+ ### Field Types
72
+
73
+ - \`d.uuid()\` — UUID field
74
+ - \`d.text()\` — Text/string field
75
+ - \`d.boolean()\` — Boolean field
76
+ - \`d.integer()\` — Integer field
77
+ - \`d.timestamp()\` — Timestamp field
78
+
79
+ ### Field Modifiers
80
+
81
+ - \`.primary()\` — Primary key
82
+ - \`.default(value)\` — Default value (\`'now'\` for timestamps)
83
+ - \`.readOnly()\` — Not writable via API
84
+ - \`.autoUpdate()\` — Auto-set on update (timestamps)
85
+ - \`.unique()\` — Unique constraint
86
+
87
+ ## Defining Entities
88
+
89
+ Entities define your API resources. Each entity gets automatic CRUD endpoints.
90
+ Entity files go in \`src/api/entities/\` with the \`.entity.ts\` suffix.
91
+
92
+ \`\`\`ts
93
+ import { entity } from 'vertz/server';
94
+ import { postsModel } from '../schema';
95
+
96
+ export const posts = entity('posts', {
97
+ model: postsModel,
98
+ access: {
99
+ list: () => true,
100
+ get: () => true,
101
+ create: () => true,
102
+ update: () => true,
103
+ delete: () => true,
104
+ },
105
+ });
106
+ \`\`\`
107
+
108
+ ### Entity Operations
109
+
110
+ Each entity automatically provides these operations:
111
+
112
+ | Operation | HTTP | Description |
113
+ | --------- | ----------------------- | ------------------------------ |
114
+ | \`list\` | GET /api/<entity> | List with filtering/pagination |
115
+ | \`get\` | GET /api/<entity>/:id | Get by ID |
116
+ | \`create\` | POST /api/<entity> | Create new record |
117
+ | \`update\` | PATCH /api/<entity>/:id | Update existing record |
118
+ | \`delete\` | DELETE /api/<entity>/:id| Delete record |
119
+
120
+ ## Server Configuration
121
+
122
+ Register all entities with \`createServer\`:
123
+
124
+ \`\`\`ts
125
+ import { createServer } from 'vertz/server';
126
+ import { db } from './db';
127
+ import { env } from './env';
128
+ import { posts } from './entities/posts.entity';
129
+
130
+ const app = createServer({
131
+ basePath: '/api',
132
+ entities: [posts],
133
+ db,
134
+ });
135
+
136
+ export default app;
137
+
138
+ if (import.meta.main) {
139
+ app.listen(env.PORT).then((handle) => {
140
+ console.log(\\\`Server running at http://localhost:\\\${handle.port}/api\\\`);
141
+ });
142
+ }
143
+ \`\`\`
144
+
145
+ ## Environment Variables
146
+
147
+ Use \`createEnv\` for validated, typed environment variables:
148
+
149
+ \`\`\`ts
150
+ import { createEnv } from 'vertz/server';
151
+ import { s } from 'vertz/schema';
152
+
153
+ export const env = createEnv({
154
+ schema: s.object({
155
+ PORT: s.coerce.number().default(3000),
156
+ DATABASE_URL: s.string().default('local.db'),
157
+ }),
158
+ });
159
+ \`\`\`
160
+
161
+ ## Schemas for Custom Actions
162
+
163
+ Standard CRUD operations (list, get, create, update, delete) derive their schemas automatically
164
+ from the model — you don't need to define schemas for them.
165
+
166
+ Use \`s.*\` builders only when defining **custom actions** on entities:
167
+
168
+ \`\`\`ts
169
+ import { s } from 'vertz/schema';
170
+
171
+ const CompleteTaskInput = s.object({
172
+ note: s.string().optional(),
173
+ completedAt: s.date(),
174
+ });
175
+
176
+ type CompleteTaskInput = s.infer<typeof CompleteTaskInput>;
177
+ \`\`\`
178
+
179
+ ### Common Schema Types
180
+
181
+ - \`s.string()\` — String with \`.min()\`, \`.max()\`, \`.email()\`, \`.uuid()\`, \`.url()\`
182
+ - \`s.number()\` — Number with \`.int()\`, \`.min()\`, \`.max()\`, \`.positive()\`
183
+ - \`s.boolean()\` — Boolean
184
+ - \`s.date()\` — Date
185
+ - \`s.enum([...])\` — Enum from literal values
186
+ - \`s.array(schema)\` — Array of schema type
187
+ - \`s.object({...})\` — Object with typed fields
188
+ - \`.optional()\` — Makes any field optional
189
+ - \`.default(value)\` — Provides a default value
190
+ `;
191
+ }
192
+ /**
193
+ * .claude/rules/ui-development.md — UI conventions for LLMs
194
+ */
195
+ export function uiDevelopmentRuleTemplate() {
196
+ return `# UI Development
197
+
198
+ Vertz uses a custom JSX runtime with a compiler that transforms reactive code.
199
+ You write plain-looking code and the compiler makes it reactive automatically.
200
+
201
+ ## Imports
202
+
203
+ \`\`\`ts
204
+ import { css, query, queryMatch, globalCss, ThemeProvider, variants } from 'vertz/ui';
205
+ import { api } from '../client';
206
+ \`\`\`
207
+
208
+ ## Components
209
+
210
+ ### Destructure props in parameters
211
+
212
+ Don't annotate return types — the JSX factory handles typing:
213
+
214
+ \`\`\`tsx
215
+ // RIGHT
216
+ export function TaskCard({ task, onClick }: TaskCardProps) {
217
+ return <div onClick={onClick}>{task.title}</div>;
218
+ }
219
+
220
+ // WRONG — don't annotate return type
221
+ export function TaskCard({ task }: TaskCardProps): HTMLElement { ... }
222
+ \`\`\`
223
+
224
+ ### Props Naming
225
+
226
+ - Interface: \`ComponentNameProps\`
227
+ - Callbacks: \`on\` prefix (\`onClick\`, \`onSubmit\`, \`onSuccess\`)
228
+
229
+ ## Reactivity
230
+
231
+ The Vertz compiler transforms your code to be reactive. You don't call signal/computed APIs manually.
232
+
233
+ ### \`let\` for local state (compiled to signals)
234
+
235
+ \`\`\`tsx
236
+ export function Counter() {
237
+ let count = 0;
238
+
239
+ return (
240
+ <button onClick={() => { count++; }}>
241
+ Count: {count}
242
+ </button>
243
+ );
244
+ }
245
+ \`\`\`
246
+
247
+ ### \`const\` for derived values (compiled to computed)
248
+
249
+ \`\`\`tsx
250
+ export function TaskList() {
251
+ let filter = 'all';
252
+ const tasks = query(api.tasks.list());
253
+ const filtered = filter === 'all'
254
+ ? tasks.data.items
255
+ : tasks.data.items.filter((t) => t.status === filter);
256
+
257
+ return <div>{filtered.map((t) => <TaskItem task={t} />)}</div>;
258
+ }
259
+ \`\`\`
260
+
261
+ ## JSX
262
+
263
+ ### Fully declarative — no imperative DOM manipulation
264
+
265
+ Never use \`appendChild\`, \`innerHTML\`, \`textContent\`, \`document.createElement\`.
266
+
267
+ \`\`\`tsx
268
+ // RIGHT
269
+ return <div class={styles.panel}>{title}</div>;
270
+
271
+ // WRONG — no imperative DOM
272
+ const el = document.createElement('div');
273
+ el.textContent = title;
274
+ \`\`\`
275
+
276
+ ### Use JSX for custom components, not function calls
277
+
278
+ \`\`\`tsx
279
+ // RIGHT
280
+ <TaskCard task={task} onClick={handleClick} />
281
+
282
+ // WRONG
283
+ TaskCard({ task, onClick: handleClick });
284
+ \`\`\`
285
+
286
+ ### Conditionals and Lists
287
+
288
+ \`\`\`tsx
289
+ {isLoading && <div>Loading...</div>}
290
+
291
+ {error ? <div class={styles.error}>{error.message}</div> : <div>{content}</div>}
292
+
293
+ {tasks.map((task) => (
294
+ <TaskItem key={task.id} task={task} />
295
+ ))}
296
+ \`\`\`
297
+
298
+ ## Data Fetching
299
+
300
+ ### \`query()\` — Reactive data fetching
301
+
302
+ \`\`\`tsx
303
+ const tasks = query(api.tasks.list());
304
+ \`\`\`
305
+
306
+ The query result has reactive properties (\`.data\`, \`.error\`, \`.loading\`) that the compiler
307
+ auto-unwraps everywhere — just access them directly, the compiler handles the rest.
308
+
309
+ ### \`queryMatch()\` — Pattern matching for query states
310
+
311
+ \`\`\`tsx
312
+ {queryMatch(tasksQuery, {
313
+ loading: () => <div>Loading...</div>,
314
+ error: (err) => <div>Error: {err.message}</div>,
315
+ data: (response) => (
316
+ <div>
317
+ {response.items.map((item) => (
318
+ <div key={item.id}>{item.title}</div>
319
+ ))}
320
+ </div>
321
+ ),
322
+ })}
323
+ \`\`\`
324
+
325
+ ### Automatic Cache Invalidation
326
+
327
+ After mutations (\`create\`, \`update\`, \`delete\`), related queries are automatically
328
+ refetched in the background. No manual \`refetch()\` calls needed — the framework
329
+ handles cache invalidation via optimistic updates.
330
+
331
+ ## Styling
332
+
333
+ ### \`css()\` for scoped styles
334
+
335
+ \`\`\`tsx
336
+ const styles = css({
337
+ container: ['flex', 'flex-col', 'gap:4', 'p:6'],
338
+ title: ['font:xl', 'font:bold', 'text:foreground'],
339
+ card: ['rounded:md', 'border:1', 'border:border', 'bg:card', 'p:4'],
340
+ });
341
+
342
+ return <div class={styles.container}>...</div>;
343
+ \`\`\`
344
+
345
+ ### \`variants()\` for parameterized styles
346
+
347
+ \`\`\`tsx
348
+ const button = variants({
349
+ base: ['inline-flex', 'items:center', 'rounded:md', 'font:medium'],
350
+ variants: {
351
+ intent: {
352
+ primary: ['bg:primary.600', 'text:white'],
353
+ secondary: ['bg:secondary', 'text:secondary-foreground'],
354
+ danger: ['bg:destructive', 'text:white'],
355
+ },
356
+ size: {
357
+ sm: ['text:xs', 'px:3', 'py:1'],
358
+ md: ['text:sm', 'px:4', 'py:2'],
359
+ },
360
+ },
361
+ defaultVariants: { intent: 'primary', size: 'md' },
362
+ });
363
+
364
+ <button class={button({ intent: 'danger', size: 'sm' })}>Delete</button>
365
+ \`\`\`
366
+
367
+ ### Style Tokens
368
+
369
+ Styles use a token system (similar to Tailwind but with Vertz syntax):
370
+
371
+ - **Layout:** \`flex\`, \`grid\`, \`block\`, \`inline-flex\`
372
+ - **Spacing:** \`p:4\`, \`px:6\`, \`py:2\`, \`m:4\`, \`mx:auto\`, \`gap:2\`
373
+ - **Typography:** \`font:lg\`, \`font:bold\`, \`font:medium\`, \`text:foreground\`, \`text:sm\`
374
+ - **Colors:** \`bg:background\`, \`bg:primary.600\`, \`text:white\`, \`border:border\`
375
+ - **Sizing:** \`w:full\`, \`h:screen\`, \`max-w:2xl\`, \`min-h:screen\`
376
+ - **Borders:** \`rounded:md\`, \`rounded:lg\`, \`border:1\`, \`border:border\`
377
+ - **Flexbox:** \`items:center\`, \`justify:between\`, \`flex-1\`, \`flex-col\`
378
+
379
+ ## Context
380
+
381
+ \`\`\`tsx
382
+ import { createContext, useContext } from 'vertz/ui';
383
+
384
+ export const SettingsContext = createContext<SettingsValue>();
385
+
386
+ export function useSettings() {
387
+ const ctx = useContext(SettingsContext);
388
+ if (!ctx) throw new Error('useSettings must be called within SettingsContext.Provider');
389
+ return ctx;
390
+ }
391
+ \`\`\`
392
+
393
+ ## Router
394
+
395
+ ### Navigation
396
+
397
+ Pages access the router via hooks — no prop threading:
398
+
399
+ \`\`\`tsx
400
+ import { useRouter, useParams } from 'vertz/ui';
401
+
402
+ export function TaskListPage() {
403
+ const { navigate } = useRouter();
404
+ return <button onClick={() => navigate({ to: '/tasks/new' })}>New Task</button>;
405
+ }
406
+
407
+ export function TaskDetailPage() {
408
+ const { id } = useParams<'/tasks/:id'>();
409
+ // id is typed as string
410
+ }
411
+ \`\`\`
412
+ `;
413
+ }
1
414
  // ── Config file templates ──────────────────────────────────
2
415
  /**
3
416
  * Package.json template — full-stack deps + #generated imports map
@@ -314,12 +727,12 @@ mount(App, {
314
727
  `;
315
728
  }
316
729
  /**
317
- * src/styles/theme.ts — configureTheme from @vertz/theme-shadcn
730
+ * src/styles/theme.ts — configureThemeBase from @vertz/theme-shadcn/base
318
731
  */
319
732
  export function themeTemplate() {
320
- return `import { configureTheme } from '@vertz/theme-shadcn';
733
+ return `import { configureThemeBase } from '@vertz/theme-shadcn/base';
321
734
 
322
- const { theme, globals } = configureTheme({
735
+ const { theme, globals } = configureThemeBase({
323
736
  palette: 'zinc',
324
737
  radius: 'md',
325
738
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/create-vertz-app",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Create a new Vertz application",
@@ -29,7 +29,9 @@
29
29
  "bin"
30
30
  ],
31
31
  "scripts": {
32
- "build": "tsc"
32
+ "build": "tsc",
33
+ "typecheck": "tsc --noEmit",
34
+ "test": "bun test"
33
35
  },
34
36
  "dependencies": {
35
37
  "commander": "^14.0.0"