create-kuckit-app 0.3.5 → 0.4.1

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 (63) hide show
  1. package/dist/bin.js +1 -1
  2. package/dist/{create-project-CP-h4Ygi.js → create-project-CAsuZMK5.js} +7 -1
  3. package/dist/index.js +1 -1
  4. package/package.json +1 -1
  5. package/templates/base/.claude/CLAUDE.md +83 -0
  6. package/templates/base/.claude/skills/kuckit/SKILL.md +22 -2
  7. package/templates/base/.claude/skills/kuckit/references/MODULE-DEVELOPMENT.md +39 -28
  8. package/templates/base/.claude/skills/kuckit/references/PACKAGES.md +94 -74
  9. package/templates/base/AGENTS.md +130 -18
  10. package/templates/base/apps/server/AGENTS.md +44 -62
  11. package/templates/base/apps/server/package.json +5 -17
  12. package/templates/base/apps/server/src/config.ts +12 -0
  13. package/templates/base/apps/server/src/modules.ts +66 -0
  14. package/templates/base/apps/server/src/server.ts +4 -44
  15. package/templates/base/apps/web/AGENTS.md +63 -85
  16. package/templates/base/apps/web/package.json +7 -11
  17. package/templates/base/apps/web/src/components/KuckitModuleRoute.tsx +29 -7
  18. package/templates/base/apps/web/src/components/dashboard/dashboard-overview.tsx +2 -2
  19. package/templates/base/apps/web/src/components/dashboard/nav-user.tsx +2 -2
  20. package/templates/base/apps/web/src/main.tsx +12 -22
  21. package/templates/base/apps/web/src/modules.client.ts +43 -9
  22. package/templates/base/apps/web/src/routes/__root.tsx +1 -1
  23. package/templates/base/apps/web/src/routes/dashboard.tsx +1 -1
  24. package/templates/base/apps/web/src/routes/index.tsx +2 -2
  25. package/templates/base/apps/web/src/routes/login.tsx +2 -2
  26. package/templates/base/{packages/db/src/migrations → drizzle}/0000_init.sql +31 -36
  27. package/templates/base/{packages/db/src/migrations → drizzle}/meta/_journal.json +1 -1
  28. package/templates/base/drizzle.config.ts +34 -0
  29. package/templates/base/kuckit.config.ts +30 -0
  30. package/templates/base/package.json +14 -9
  31. package/templates/base/packages/items-module/AGENTS.md +83 -0
  32. package/templates/base/packages/items-module/package.json +7 -7
  33. package/templates/base/packages/items-module/src/api/items.router.ts +1 -1
  34. package/templates/base/apps/server/src/app.ts +0 -20
  35. package/templates/base/apps/server/src/auth.ts +0 -10
  36. package/templates/base/apps/server/src/config/modules.ts +0 -21
  37. package/templates/base/apps/server/src/container.ts +0 -81
  38. package/templates/base/apps/server/src/health.ts +0 -27
  39. package/templates/base/apps/server/src/middleware/container.ts +0 -41
  40. package/templates/base/apps/server/src/rpc-router-registry.ts +0 -26
  41. package/templates/base/apps/server/src/rpc.ts +0 -31
  42. package/templates/base/apps/web/src/providers/KuckitProvider.tsx +0 -123
  43. package/templates/base/apps/web/src/providers/ServicesProvider.tsx +0 -47
  44. package/templates/base/apps/web/src/services/auth-client.ts +0 -12
  45. package/templates/base/apps/web/src/services/index.ts +0 -3
  46. package/templates/base/apps/web/src/services/rpc.ts +0 -29
  47. package/templates/base/apps/web/src/services/types.ts +0 -14
  48. package/templates/base/packages/api/AGENTS.md +0 -66
  49. package/templates/base/packages/api/package.json +0 -35
  50. package/templates/base/packages/api/src/context.ts +0 -48
  51. package/templates/base/packages/api/src/index.ts +0 -22
  52. package/templates/base/packages/api/tsconfig.json +0 -8
  53. package/templates/base/packages/auth/AGENTS.md +0 -61
  54. package/templates/base/packages/auth/package.json +0 -27
  55. package/templates/base/packages/auth/src/index.ts +0 -22
  56. package/templates/base/packages/auth/tsconfig.json +0 -8
  57. package/templates/base/packages/db/AGENTS.md +0 -99
  58. package/templates/base/packages/db/drizzle.config.ts +0 -23
  59. package/templates/base/packages/db/package.json +0 -36
  60. package/templates/base/packages/db/src/connection.ts +0 -40
  61. package/templates/base/packages/db/src/index.ts +0 -4
  62. package/templates/base/packages/db/src/schema/auth.ts +0 -51
  63. package/templates/base/packages/db/tsconfig.json +0 -8
@@ -8,86 +8,68 @@ Express backend hosting oRPC API, Better-Auth authentication, and the Kuckit mod
8
8
 
9
9
  ## Key Files
10
10
 
11
- | File | Purpose |
12
- | ------------------------ | -------------------------- |
13
- | `server.ts` | Entry point, bootstrap |
14
- | `container.ts` | DI container setup |
15
- | `config/modules.ts` | Module registration |
16
- | `rpc.ts` | oRPC handler setup |
17
- | `rpc-router-registry.ts` | Mutable router for modules |
18
- | `auth.ts` | Better-Auth configuration |
11
+ | File | Purpose |
12
+ | ------------ | ------------------------------------------------- |
13
+ | `server.ts` | Entry point (9 lines) |
14
+ | `config.ts` | Server configuration |
15
+ | `modules.ts` | Module registration (reads from kuckit.config.ts) |
19
16
 
20
- ## Module Loading Sequence
17
+ ## How It Works
21
18
 
22
- The server bootstraps in this order:
23
-
24
- ```
25
- 1. createKuckitContainer() - Core services (db, logger, cache, etc.)
26
- 2. loadKuckitModules() with onApiRegistrations callback:
27
- ├─► register() hooks run (DI bindings)
28
- ├─► registerApi() hooks run (routers collected)
29
- ├─► onApiRegistrations() - Wire routers to rootRpcRouter
30
- └─► onBootstrap() hooks run (startup logic)
31
- 3. setupRPC() - Create RPCHandler AFTER modules loaded
32
- 4. Express listens
33
- ```
34
-
35
- ## Router Registry Pattern
36
-
37
- oRPC's `RPCHandler` captures the router at construction time. Modules wire their routers into a **mutable object** before the handler is created:
38
-
39
- ```typescript
40
- // rpc-router-registry.ts
41
- export const rootRpcRouter = { ...appRouter }
42
-
43
- export const wireModuleRpcRouters = (registrations: ApiRegistration[]) => {
44
- for (const reg of registrations) {
45
- if (reg.type === 'rpc-router') {
46
- rootRpcRouter[reg.name] = reg.router
47
- }
48
- }
49
- }
50
- ```
19
+ The server uses `@kuckit/app-server` which handles all the complexity internally:
51
20
 
52
21
  ```typescript
53
22
  // server.ts
54
- await loadKuckitModules({
55
- container,
56
- modules: getModuleSpecs(),
57
- onApiRegistrations: (registrations) => {
58
- wireModuleRpcRouters(registrations) // Wire BEFORE setupRPC()
59
- },
60
- })
61
-
62
- setupRPC(app) // Now create handler with fully-wired router
63
- ```
64
-
65
- ## Per-Request Scoping
23
+ import 'dotenv/config'
24
+ import { runKuckitServer } from '@kuckit/app-server'
25
+ import { loadConfig } from './config'
26
+ import { getModuleSpecs } from './modules'
66
27
 
67
- Each HTTP request gets a scoped container with:
28
+ runKuckitServer({ loadConfig, getModuleSpecs }).catch(console.error)
29
+ ```
68
30
 
69
- - `requestId` - Unique request identifier
70
- - `requestLogger` - Logger with request context
71
- - `session` - Current user session (if authenticated)
31
+ The `@kuckit/app-server` package handles:
72
32
 
73
- Access scoped services in routers via `context.di.cradle`.
33
+ - DI container creation and setup
34
+ - Module loading with proper lifecycle hooks
35
+ - RPC router wiring (oRPC)
36
+ - REST router mounting
37
+ - Authentication (Better-Auth)
38
+ - Health endpoints
39
+ - Graceful shutdown
74
40
 
75
41
  ## Adding New Modules
76
42
 
77
- 1. Install the module package
78
- 2. Add to `config/modules.ts`:
43
+ **`kuckit.config.ts` is the single source of truth for modules.**
44
+
45
+ 1. Add to `kuckit.config.ts` (at project root):
79
46
 
80
47
  ```typescript
81
- import { kuckitModule as myModule } from '@__APP_NAME_KEBAB__/my-module'
48
+ export default defineConfig({
49
+ modules: [
50
+ { package: '@__APP_NAME_KEBAB__/items-module' },
51
+ { package: '@acme/billing-module' }, // Add new module here
52
+ ],
53
+ })
54
+ ```
55
+
56
+ 2. Add import and mapping to `modules.ts`:
82
57
 
83
- export const getModuleSpecs = () => [
84
- { module: itemsModule },
85
- { module: myModule }, // Add here
86
- ]
58
+ ```typescript
59
+ import { kuckitModule as billingModule } from '@acme/billing-module'
60
+
61
+ const KNOWN_MODULES = {
62
+ // ...existing
63
+ '@acme/billing-module': {
64
+ module: billingModule,
65
+ },
66
+ }
87
67
  ```
88
68
 
89
69
  3. Restart the server
90
70
 
71
+ The CLI command `bunx kuckit add @acme/billing-module` automates both steps.
72
+
91
73
  ## Environment Variables
92
74
 
93
75
  See `.env.example` in this directory.
@@ -5,27 +5,15 @@
5
5
  "scripts": {
6
6
  "build": "tsdown",
7
7
  "check-types": "tsc -b",
8
- "dev": "bun run --hot src/server.ts"
8
+ "dev": "kuckit dev"
9
9
  },
10
10
  "dependencies": {
11
- "express": "^5.1.0",
12
- "cors": "^2.8.5",
13
- "dotenv": "^17.2.2",
14
- "awilix": "^12.0.5",
15
- "pg": "^8.14.1",
16
- "@orpc/server": "^1.10.0",
17
- "@orpc/zod": "^1.10.0",
18
- "better-auth": "^1.3.28",
19
- "zod": "^4.1.11",
20
- "@kuckit/sdk": "^1.0.0",
21
- "@__APP_NAME_KEBAB__/api": "workspace:*",
22
- "@__APP_NAME_KEBAB__/auth": "workspace:*",
23
- "@__APP_NAME_KEBAB__/db": "workspace:*"
11
+ "dotenv": "^17.0.0",
12
+ "@kuckit/app-server": "^2.0.0",
13
+ "@kuckit/sdk": "^2.0.0",
14
+ "@kuckit/db": "^2.0.0"
24
15
  },
25
16
  "devDependencies": {
26
- "@types/express": "^5.0.1",
27
- "@types/cors": "^2.8.17",
28
- "@types/pg": "^8.11.11",
29
17
  "typescript": "^5.8.2",
30
18
  "tsdown": "^0.15.5"
31
19
  }
@@ -0,0 +1,12 @@
1
+ import { ensureDatabaseUrl } from '@kuckit/db/connection'
2
+ import type { KuckitServerConfig } from '@kuckit/app-server'
3
+
4
+ export const loadConfig = (): KuckitServerConfig => ({
5
+ databaseUrl: ensureDatabaseUrl(),
6
+ enableFileLogging: process.env.ENABLE_FILE_LOGGING === 'true',
7
+ logDir: process.env.LOG_DIR || './logs',
8
+ logLevel: (process.env.LOG_LEVEL || 'INFO') as 'DEBUG' | 'INFO' | 'WARN' | 'ERROR',
9
+ env: process.env.NODE_ENV || 'development',
10
+ port: parseInt(process.env.PORT || '3000', 10),
11
+ corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:3001',
12
+ })
@@ -0,0 +1,66 @@
1
+ import type { ModuleSpec, KuckitConfig } from '@kuckit/sdk'
2
+ import { tryLoadKuckitConfig } from '@kuckit/sdk'
3
+ import { kuckitModule as itemsModule } from '@__APP_NAME_KEBAB__/items-module'
4
+
5
+ /**
6
+ * Known modules mapping: package name → direct module import
7
+ *
8
+ * This enables config-driven module loading while keeping direct imports
9
+ * for workspace packages (required for monorepo compatibility).
10
+ *
11
+ * When adding a new module:
12
+ * 1. Add to kuckit.config.ts (source of truth)
13
+ * 2. Add import and mapping here
14
+ */
15
+ const KNOWN_MODULES: Record<string, { module: unknown; defaultConfig?: unknown }> = {
16
+ // KUCKIT_KNOWN_MODULES_START
17
+ '@__APP_NAME_KEBAB__/items-module': {
18
+ module: itemsModule,
19
+ },
20
+ // KUCKIT_KNOWN_MODULES_END
21
+ }
22
+
23
+ /**
24
+ * Convert unified config to ModuleSpec array.
25
+ * For known modules, uses direct imports. For npm packages, uses package-based loading.
26
+ */
27
+ function configToModuleSpecs(config: KuckitConfig): ModuleSpec[] {
28
+ return config.modules
29
+ .filter((m) => m.enabled !== false)
30
+ .map((m) => {
31
+ const known = KNOWN_MODULES[m.package]
32
+ if (known) {
33
+ return {
34
+ module: known.module,
35
+ config: m.config ?? known.defaultConfig,
36
+ } as ModuleSpec
37
+ }
38
+ // For npm packages, use package-based loading
39
+ return {
40
+ package: m.package,
41
+ config: m.config,
42
+ } as ModuleSpec
43
+ })
44
+ }
45
+
46
+ /**
47
+ * Fallback module specs (used when kuckit.config.ts is not found)
48
+ */
49
+ const getFallbackModuleSpecs = (): ModuleSpec[] => [{ module: itemsModule }]
50
+
51
+ /**
52
+ * Get module specs from kuckit.config.ts (single source of truth).
53
+ * Falls back to direct module specs if config is not found.
54
+ */
55
+ export const getModuleSpecs = async (): Promise<ModuleSpec[]> => {
56
+ const config = await tryLoadKuckitConfig()
57
+
58
+ if (config) {
59
+ console.log(`[kuckit] Loading modules from: ${config._configPath}`)
60
+ return configToModuleSpecs(config)
61
+ }
62
+
63
+ // Fallback for legacy projects without kuckit.config.ts
64
+ console.log('[kuckit] No config found, using fallback module specs')
65
+ return getFallbackModuleSpecs()
66
+ }
@@ -1,49 +1,9 @@
1
1
  import 'dotenv/config'
2
- import { createApp } from './app'
3
- import { setupContainerMiddleware, getRootContainer } from './middleware/container'
4
- import { setupAuth } from './auth'
5
- import { setupRPC } from './rpc'
6
- import { setupHealth } from './health'
7
- import { disposeContainer } from './container'
2
+ import { runKuckitServer } from '@kuckit/app-server'
3
+ import { loadConfig } from './config'
4
+ import { getModuleSpecs } from './modules'
8
5
 
9
- /**
10
- * Bootstrap and start server
11
- */
12
- const bootstrap = async () => {
13
- const app = createApp()
14
-
15
- await setupContainerMiddleware(app)
16
- const rootContainer = getRootContainer()
17
-
18
- setupAuth(app)
19
- setupRPC(app)
20
- setupHealth(app, rootContainer)
21
-
22
- const port = process.env.PORT || 3000
23
- const server = app.listen(port, () => {
24
- console.log(`Server is running on port ${port}`)
25
- })
26
-
27
- const shutdown = async () => {
28
- console.log('Shutting down gracefully...')
29
-
30
- server.close(async () => {
31
- await disposeContainer(rootContainer)
32
- console.log('Server closed')
33
- process.exit(0)
34
- })
35
-
36
- setTimeout(() => {
37
- console.error('Forced shutdown')
38
- process.exit(1)
39
- }, 10000)
40
- }
41
-
42
- process.on('SIGTERM', shutdown)
43
- process.on('SIGINT', shutdown)
44
- }
45
-
46
- bootstrap().catch((error) => {
6
+ runKuckitServer({ loadConfig, getModuleSpecs }).catch((error) => {
47
7
  console.error('Failed to start server:', error)
48
8
  process.exit(1)
49
9
  })
@@ -10,118 +10,96 @@ React frontend using TanStack Router, TanStack Query, and Kuckit client modules.
10
10
 
11
11
  | File | Purpose |
12
12
  | ---------------------------------- | --------------------------------- |
13
- | `main.tsx` | App entry point |
13
+ | `main.tsx` | App entry point (17 lines) |
14
14
  | `modules.client.ts` | Client module registration |
15
- | `providers/KuckitProvider.tsx` | Kuckit context setup |
16
- | `providers/ServicesProvider.tsx` | Services context (RPC, auth) |
17
15
  | `routes/` | TanStack Router file-based routes |
18
16
  | `routes/$.tsx` | Catch-all for module routes |
19
17
  | `components/KuckitModuleRoute.tsx` | Renders module-registered routes |
20
18
 
21
- ## Adding Routes
22
-
23
- Create files in `src/routes/`:
24
-
25
- - `src/routes/about.tsx` → `/about`
26
- - `src/routes/users/$id.tsx` → `/users/:id`
27
- - `src/routes/settings/index.tsx` → `/settings`
28
-
29
- **File-based routes take precedence** over module-registered routes.
19
+ ## How It Works
30
20
 
31
- ## Module Route Integration
32
-
33
- Modules register routes via `defineKuckitClientModule`. These are rendered via a catch-all pattern:
34
-
35
- 1. Module registers route in `routes` array
36
- 2. `RouteRegistry` collects routes during `loadKuckitClientModules()`
37
- 3. Catch-all route (`$.tsx`) matches any unhandled path
38
- 4. `KuckitModuleRoute` looks up path in registry and renders the component
21
+ The web app uses `@kuckit/app-web` which provides a provider factory:
39
22
 
40
23
  ```tsx
41
- // routes/$.tsx - Catches module routes
42
- import { KuckitModuleRoute } from '@/components/KuckitModuleRoute'
43
-
44
- export function Route() {
45
- return <KuckitModuleRoute />
46
- }
24
+ // main.tsx
25
+ import ReactDOM from 'react-dom/client'
26
+ import { createKuckitWebProvider } from '@kuckit/app-web'
27
+ import { routeTree } from './routeTree.gen'
28
+ import { Route as rootRoute } from './routes/__root'
29
+ import { getClientModuleSpecs } from './modules.client'
30
+ import './index.css'
31
+
32
+ const KuckitApp = createKuckitWebProvider({
33
+ serverUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000',
34
+ modules: getClientModuleSpecs(),
35
+ env: import.meta.env.MODE,
36
+ routeTree,
37
+ rootRoute,
38
+ })
39
+
40
+ const rootEl = document.getElementById('app')!
41
+ ReactDOM.createRoot(rootEl).render(<KuckitApp />)
47
42
  ```
48
43
 
49
- ## Context Providers
50
-
51
- The app wraps content in Kuckit providers:
44
+ The `@kuckit/app-web` package handles:
52
45
 
53
- ```tsx
54
- <ServicesProvider>
55
- {' '}
56
- {/* RPC client, auth */}
57
- <KuckitProvider>
58
- {' '}
59
- {/* Nav, slots, routes registries */}
60
- <KuckitRpcProvider>
61
- {' '}
62
- {/* RPC context for modules */}
63
- {children}
64
- </KuckitRpcProvider>
65
- </KuckitProvider>
66
- </ServicesProvider>
67
- ```
46
+ - RPC client and oRPC utils creation
47
+ - React Query client setup
48
+ - Better-Auth client integration
49
+ - Module loading and route registration
50
+ - Navigation and slot registries
51
+ - Provider composition
68
52
 
69
- **Available hooks:**
53
+ ## Available Hooks
70
54
 
71
- - `useRpc<T>()` - Get typed RPC client for API calls
72
- - `useNavItems()` - Flat navigation items list
73
- - `useNavTree()` - Hierarchical navigation tree
74
- - `useSlot(name)` - Get components for a slot
75
- - `useHasSlot(name)` - Check if slot has content
55
+ Import from `@kuckit/app-web`:
76
56
 
77
- ## useRpc Hook
57
+ - `useAuth()` - Auth client for sign in/out, session
58
+ - `useRpc()` - Typed RPC client for API calls
59
+ - `useOrpc()` - oRPC TanStack Query utils
60
+ - `useQueryClient()` - React Query client
61
+ - `useKuckitWeb()` - Full context with registries
78
62
 
79
- Module components must use `useRpc()` to access the API:
80
-
81
- ```tsx
82
- import { useRpc } from '@kuckit/sdk-react'
83
- import { useQuery } from '@tanstack/react-query'
84
-
85
- interface MyRpc {
86
- items: { list: (input: {}) => Promise<Item[]> }
87
- }
63
+ ## Adding Routes
88
64
 
89
- function MyComponent() {
90
- const rpc = useRpc<MyRpc>()
65
+ Create files in `src/routes/`:
91
66
 
92
- const { data } = useQuery({
93
- queryKey: ['items'],
94
- queryFn: () => rpc.items.list({}),
95
- })
96
- }
97
- ```
67
+ - `src/routes/about.tsx` `/about`
68
+ - `src/routes/users/$id.tsx` → `/users/:id`
69
+ - `src/routes/settings/index.tsx` `/settings`
98
70
 
99
- **Important**: Never use `import.meta.env.VITE_SERVER_URL` directly in module components - it's unavailable in external packages. The `useRpc` hook provides the correctly-configured client.
71
+ **File-based routes take precedence** over module-registered routes.
100
72
 
101
- ## Using Module UI Components
73
+ ## Adding Client Modules
102
74
 
103
- ```tsx
104
- import { ItemsPage } from '@__APP_NAME_KEBAB__/items-module/ui'
75
+ **`kuckit.config.ts` is the single source of truth for modules.**
105
76
 
106
- // In your route component
107
- export function Route() {
108
- return <ItemsPage />
109
- }
110
- ```
77
+ 1. Add to `kuckit.config.ts` (at project root):
111
78
 
112
- ## Adding Client Modules
79
+ ```typescript
80
+ export default defineConfig({
81
+ modules: [
82
+ { package: '@__APP_NAME_KEBAB__/items-module' },
83
+ { package: '@acme/billing-module' }, // Add new module here
84
+ ],
85
+ })
86
+ ```
113
87
 
114
- 1. Import in `modules.client.ts`:
88
+ 2. Add import and mapping to `modules.client.ts`:
115
89
 
116
90
  ```typescript
117
- import { kuckitClientModule as myClientModule } from '@__APP_NAME_KEBAB__/my-module/client'
118
-
119
- export const clientModules = [
120
- itemsClientModule,
121
- myClientModule, // Add here
122
- ]
91
+ import { kuckitClientModule as billingClientModule } from '@acme/billing-module/client'
92
+
93
+ const KNOWN_CLIENT_MODULES = {
94
+ // ...existing
95
+ '@acme/billing-module': {
96
+ module: billingClientModule,
97
+ },
98
+ }
123
99
  ```
124
100
 
101
+ The CLI command `bunx kuckit add @acme/billing-module` automates both steps.
102
+
125
103
  ## Environment Variables
126
104
 
127
105
  See `.env.example` in this directory.
@@ -9,9 +9,8 @@
9
9
  "check-types": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@kuckit/sdk-react": "^1.0.0",
13
- "@orpc/client": "^1.10.0",
14
- "@orpc/tanstack-query": "^1.10.0",
12
+ "@kuckit/app-web": "^2.0.0",
13
+ "@kuckit/sdk-react": "^2.0.0",
15
14
  "@radix-ui/react-avatar": "^1.1.10",
16
15
  "@radix-ui/react-collapsible": "^1.1.11",
17
16
  "@radix-ui/react-dialog": "^1.1.14",
@@ -19,17 +18,14 @@
19
18
  "@radix-ui/react-separator": "^1.1.7",
20
19
  "@radix-ui/react-slot": "^1.2.3",
21
20
  "@radix-ui/react-tooltip": "^1.2.7",
22
- "@tanstack/react-query": "^5.85.5",
23
- "@tanstack/react-router": "^1.114.25",
24
- "better-auth": "^1.3.28",
21
+ "@tanstack/react-query": "^5.0.0",
22
+ "@tanstack/react-router": "^1.114.0",
25
23
  "class-variance-authority": "^0.7.1",
26
24
  "clsx": "^2.1.1",
27
25
  "lucide-react": "^0.511.0",
28
- "react": "^19.1.0",
29
- "react-dom": "^19.1.0",
30
- "tailwind-merge": "^3.3.0",
31
- "zod": "^4.1.11",
32
- "@__APP_NAME_KEBAB__/auth": "workspace:*"
26
+ "react": "^19.0.0",
27
+ "react-dom": "^19.0.0",
28
+ "tailwind-merge": "^3.0.0"
33
29
  },
34
30
  "devDependencies": {
35
31
  "@tailwindcss/vite": "^4.0.15",
@@ -1,6 +1,5 @@
1
1
  import { useRouterState, Link, useNavigate } from '@tanstack/react-router'
2
- import { useKuckit } from '@/providers/KuckitProvider'
3
- import { useServices } from '@/providers/ServicesProvider'
2
+ import { useKuckitWeb, useAuth } from '@kuckit/app-web'
4
3
  import { useEffect, useState } from 'react'
5
4
 
6
5
  /**
@@ -24,8 +23,8 @@ function isRootLevelRoute(path: string): boolean {
24
23
  * For root routes with meta.requiresAuth: true, redirects to /login if not authenticated.
25
24
  */
26
25
  export function KuckitModuleRoute() {
27
- const { routeRegistry } = useKuckit()
28
- const { authClient } = useServices()
26
+ const { routeRegistry } = useKuckitWeb()
27
+ const authClient = useAuth()
29
28
  const { location } = useRouterState()
30
29
  const navigate = useNavigate()
31
30
  const pathname = location.pathname
@@ -47,11 +46,34 @@ export function KuckitModuleRoute() {
47
46
  // For dashboard context, also try matching with /dashboard prefix stripped
48
47
  const modulePathname = isDashboardContext ? pathname.replace('/dashboard', '') || '/' : pathname
49
48
 
50
- // Find matching route
51
- let routeDef = contextRoutes.find((r) => r.path === pathname)
49
+ // Match route path against pathname, supporting catch-all patterns like /docs/$
50
+ function matchRoute(routePath: string, pathname: string): boolean {
51
+ // Exact match
52
+ if (routePath === pathname) return true
53
+
54
+ // Catch-all pattern: /docs/$ matches /docs/anything/here
55
+ if (routePath.endsWith('/$')) {
56
+ const base = routePath.slice(0, -1) // Remove trailing $
57
+ return pathname.startsWith(base)
58
+ }
59
+
60
+ return false
61
+ }
62
+
63
+ // Find matching route - prefer exact matches over catch-all
64
+ const exactMatch = contextRoutes.find((r) => r.path === pathname)
65
+ const catchAllMatch = contextRoutes.find(
66
+ (r) => r.path.endsWith('/$') && matchRoute(r.path, pathname)
67
+ )
68
+ let routeDef = exactMatch || catchAllMatch
69
+
52
70
  if (!routeDef && isDashboardContext) {
53
71
  // For dashboard routes, also try matching stripped path
54
- routeDef = contextRoutes.find((r) => r.path === modulePathname)
72
+ const exactDash = contextRoutes.find((r) => r.path === modulePathname)
73
+ const catchAllDash = contextRoutes.find(
74
+ (r) => r.path.endsWith('/$') && matchRoute(r.path, modulePathname)
75
+ )
76
+ routeDef = exactDash || catchAllDash
55
77
  }
56
78
 
57
79
  // Check auth for root routes with requiresAuth
@@ -1,7 +1,7 @@
1
- import { useServices } from '@/providers/ServicesProvider'
1
+ import { useAuth } from '@kuckit/app-web'
2
2
 
3
3
  export function DashboardOverview() {
4
- const { authClient } = useServices()
4
+ const authClient = useAuth()
5
5
  const { data: session } = authClient.useSession()
6
6
 
7
7
  return (
@@ -1,6 +1,6 @@
1
1
  import { ChevronsUpDown, LogOut, User } from 'lucide-react'
2
2
  import { useNavigate } from '@tanstack/react-router'
3
- import { useServices } from '@/providers/ServicesProvider'
3
+ import { useAuth } from '@kuckit/app-web'
4
4
  import {
5
5
  DropdownMenu,
6
6
  DropdownMenuContent,
@@ -19,7 +19,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
19
19
  export function NavUser() {
20
20
  const { isMobile } = useSidebar()
21
21
  const navigate = useNavigate()
22
- const { authClient } = useServices()
22
+ const authClient = useAuth()
23
23
  const { data: session } = authClient.useSession()
24
24
 
25
25
  const user = session?.user
@@ -1,27 +1,17 @@
1
- import { createRouter } from '@tanstack/react-router'
2
1
  import ReactDOM from 'react-dom/client'
2
+ import { createKuckitWebProvider } from '@kuckit/app-web'
3
3
  import { routeTree } from './routeTree.gen'
4
- import { ServicesProvider } from './providers/ServicesProvider'
5
- import { KuckitProvider } from './providers/KuckitProvider'
4
+ import { Route as rootRoute } from './routes/__root'
5
+ import { getClientModuleSpecs } from './modules.client'
6
6
  import './index.css'
7
7
 
8
- declare module '@tanstack/react-router' {
9
- interface Register {
10
- router: ReturnType<typeof createRouter<typeof routeTree>>
11
- }
12
- }
8
+ const KuckitApp = createKuckitWebProvider({
9
+ serverUrl: import.meta.env.VITE_API_URL || 'http://localhost:3000',
10
+ modules: getClientModuleSpecs(),
11
+ env: import.meta.env.MODE,
12
+ routeTree,
13
+ rootRoute,
14
+ })
13
15
 
14
- const rootElement = document.getElementById('app')
15
-
16
- if (!rootElement) {
17
- throw new Error('Root element not found')
18
- }
19
-
20
- if (!rootElement.innerHTML) {
21
- const root = ReactDOM.createRoot(rootElement)
22
- root.render(
23
- <ServicesProvider>
24
- <KuckitProvider />
25
- </ServicesProvider>
26
- )
27
- }
16
+ const rootEl = document.getElementById('app')!
17
+ ReactDOM.createRoot(rootEl).render(<KuckitApp />)