create-kuckit-app 0.1.0 → 0.2.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 (92) hide show
  1. package/dist/bin.js +11 -5
  2. package/dist/{create-project-DTm05G7D.js → create-project-CP-h4Ygi.js} +7 -5
  3. package/dist/index.js +1 -1
  4. package/package.json +3 -2
  5. package/templates/base/.claude/CLAUDE.md +44 -0
  6. package/templates/base/.claude/agents/daidalos.md +76 -0
  7. package/templates/base/.claude/agents/episteme.md +79 -0
  8. package/templates/base/.claude/agents/librarian.md +132 -0
  9. package/templates/base/.claude/agents/oracle.md +210 -0
  10. package/templates/base/.claude/commands/create-plan.md +159 -0
  11. package/templates/base/.claude/commands/file-beads.md +98 -0
  12. package/templates/base/.claude/commands/review-beads.md +161 -0
  13. package/templates/base/.claude/settings.json +11 -0
  14. package/templates/base/.claude/skills/kuckit/SKILL.md +436 -0
  15. package/templates/base/.claude/skills/kuckit/references/ARCHITECTURE.md +388 -0
  16. package/templates/base/.claude/skills/kuckit/references/CLI-COMMANDS.md +365 -0
  17. package/templates/base/.claude/skills/kuckit/references/MODULE-DEVELOPMENT.md +581 -0
  18. package/templates/base/.claude/skills/kuckit/references/PACKAGES.md +112 -0
  19. package/templates/base/.claude/skills/kuckit/references/PUBLISHING.md +231 -0
  20. package/templates/base/.env.example +13 -0
  21. package/templates/base/.github/workflows/ci.yml +28 -0
  22. package/templates/base/.husky/pre-commit +1 -0
  23. package/templates/base/.prettierignore +5 -0
  24. package/templates/base/.prettierrc +8 -0
  25. package/templates/base/AGENTS.md +148 -0
  26. package/templates/base/apps/server/.env.example +18 -0
  27. package/templates/base/apps/server/AGENTS.md +37 -0
  28. package/templates/base/apps/server/package.json +13 -4
  29. package/templates/base/apps/server/src/app.ts +20 -0
  30. package/templates/base/apps/server/src/auth.ts +10 -0
  31. package/templates/base/apps/server/src/config/modules.ts +14 -6
  32. package/templates/base/apps/server/src/container.ts +81 -0
  33. package/templates/base/apps/server/src/health.ts +27 -0
  34. package/templates/base/apps/server/src/middleware/container.ts +41 -0
  35. package/templates/base/apps/server/src/rpc-router-registry.ts +26 -0
  36. package/templates/base/apps/server/src/rpc.ts +31 -0
  37. package/templates/base/apps/server/src/server.ts +39 -29
  38. package/templates/base/apps/web/.env.example +4 -0
  39. package/templates/base/apps/web/AGENTS.md +53 -0
  40. package/templates/base/apps/web/index.html +1 -1
  41. package/templates/base/apps/web/package.json +15 -3
  42. package/templates/base/apps/web/src/lib/kuckit-router.ts +42 -0
  43. package/templates/base/apps/web/src/main.tsx +26 -14
  44. package/templates/base/apps/web/src/providers/KuckitProvider.tsx +147 -0
  45. package/templates/base/apps/web/src/providers/ServicesProvider.tsx +47 -0
  46. package/templates/base/apps/web/src/routeTree.gen.ts +91 -0
  47. package/templates/base/apps/web/src/routes/__root.tsx +31 -0
  48. package/templates/base/apps/web/src/routes/index.tsx +46 -0
  49. package/templates/base/apps/web/src/routes/login.tsx +108 -0
  50. package/templates/base/apps/web/src/services/auth-client.ts +12 -0
  51. package/templates/base/apps/web/src/services/index.ts +3 -0
  52. package/templates/base/apps/web/src/services/rpc.ts +29 -0
  53. package/templates/base/apps/web/src/services/types.ts +14 -0
  54. package/templates/base/apps/web/vite.config.ts +2 -1
  55. package/templates/base/docker-compose.yml +23 -0
  56. package/templates/base/eslint.config.js +18 -0
  57. package/templates/base/package.json +32 -2
  58. package/templates/base/packages/api/AGENTS.md +27 -0
  59. package/templates/base/packages/api/package.json +35 -0
  60. package/templates/base/packages/api/src/context.ts +48 -0
  61. package/templates/base/packages/api/src/index.ts +22 -0
  62. package/templates/base/packages/api/tsconfig.json +8 -0
  63. package/templates/base/packages/auth/AGENTS.md +45 -0
  64. package/templates/base/packages/auth/package.json +27 -0
  65. package/templates/base/packages/auth/src/index.ts +22 -0
  66. package/templates/base/packages/auth/tsconfig.json +8 -0
  67. package/templates/base/packages/db/AGENTS.md +59 -0
  68. package/templates/base/packages/db/drizzle.config.ts +19 -0
  69. package/templates/base/packages/db/package.json +36 -0
  70. package/templates/base/packages/db/src/connection.ts +40 -0
  71. package/templates/base/packages/db/src/index.ts +4 -0
  72. package/templates/base/packages/db/src/migrations/0000_init.sql +54 -0
  73. package/templates/base/packages/db/src/migrations/meta/_journal.json +13 -0
  74. package/templates/base/packages/db/src/schema/auth.ts +51 -0
  75. package/templates/base/packages/db/tsconfig.json +8 -0
  76. package/templates/base/packages/items-module/AGENTS.md +112 -0
  77. package/templates/base/packages/items-module/package.json +32 -0
  78. package/templates/base/packages/items-module/src/adapters/item.drizzle.ts +66 -0
  79. package/templates/base/packages/items-module/src/api/items.router.ts +47 -0
  80. package/templates/base/packages/items-module/src/client-module.ts +39 -0
  81. package/templates/base/packages/items-module/src/domain/item.entity.ts +36 -0
  82. package/templates/base/packages/items-module/src/index.ts +15 -0
  83. package/templates/base/packages/items-module/src/module.ts +53 -0
  84. package/templates/base/packages/items-module/src/ports/item.repository.ts +13 -0
  85. package/templates/base/packages/items-module/src/ui/ItemsPage.tsx +162 -0
  86. package/templates/base/packages/items-module/src/usecases/create-item.ts +25 -0
  87. package/templates/base/packages/items-module/src/usecases/delete-item.ts +18 -0
  88. package/templates/base/packages/items-module/src/usecases/get-item.ts +19 -0
  89. package/templates/base/packages/items-module/src/usecases/list-items.ts +21 -0
  90. package/templates/base/packages/items-module/tsconfig.json +9 -0
  91. package/templates/base/turbo.json +13 -1
  92. package/templates/base/apps/web/src/App.tsx +0 -16
@@ -0,0 +1,31 @@
1
+ import type { QueryClient } from '@tanstack/react-query'
2
+ import { Outlet, createRootRouteWithContext } from '@tanstack/react-router'
3
+ import type { ORPCUtils } from '../services/types'
4
+
5
+ export interface RouterAppContext {
6
+ orpc: ORPCUtils
7
+ queryClient: QueryClient
8
+ }
9
+
10
+ export const Route = createRootRouteWithContext<RouterAppContext>()({
11
+ component: RootComponent,
12
+ head: () => ({
13
+ meta: [
14
+ {
15
+ title: '__APP_NAME__',
16
+ },
17
+ {
18
+ name: 'description',
19
+ content: '__APP_NAME__ - A Kuckit application',
20
+ },
21
+ ],
22
+ }),
23
+ })
24
+
25
+ function RootComponent() {
26
+ return (
27
+ <div style={{ minHeight: '100vh' }}>
28
+ <Outlet />
29
+ </div>
30
+ )
31
+ }
@@ -0,0 +1,46 @@
1
+ import { createFileRoute, Link } from '@tanstack/react-router'
2
+ import { useServices } from '../providers/ServicesProvider'
3
+
4
+ export const Route = createFileRoute('/')({
5
+ component: HomePage,
6
+ })
7
+
8
+ function HomePage() {
9
+ const { authClient } = useServices()
10
+ const { data: session, isPending } = authClient.useSession()
11
+
12
+ if (isPending) {
13
+ return (
14
+ <div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
15
+ <p>Loading...</p>
16
+ </div>
17
+ )
18
+ }
19
+
20
+ return (
21
+ <div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
22
+ <h1>Welcome to __APP_NAME__</h1>
23
+ {session?.user ? (
24
+ <div>
25
+ <p>Hello, {session.user.name || session.user.email}!</p>
26
+ <button
27
+ onClick={() => authClient.signOut()}
28
+ style={{
29
+ padding: '0.5rem 1rem',
30
+ cursor: 'pointer',
31
+ }}
32
+ >
33
+ Sign Out
34
+ </button>
35
+ </div>
36
+ ) : (
37
+ <div>
38
+ <p>Your Kuckit application is ready!</p>
39
+ <Link to="/login" style={{ color: 'blue', textDecoration: 'underline' }}>
40
+ Sign In
41
+ </Link>
42
+ </div>
43
+ )}
44
+ </div>
45
+ )
46
+ }
@@ -0,0 +1,108 @@
1
+ import { createFileRoute, useNavigate } from '@tanstack/react-router'
2
+ import { useState } from 'react'
3
+ import { useServices } from '../providers/ServicesProvider'
4
+
5
+ export const Route = createFileRoute('/login')({
6
+ component: LoginPage,
7
+ })
8
+
9
+ function LoginPage() {
10
+ const { authClient } = useServices()
11
+ const navigate = useNavigate()
12
+ const [isSignUp, setIsSignUp] = useState(false)
13
+ const [email, setEmail] = useState('')
14
+ const [password, setPassword] = useState('')
15
+ const [name, setName] = useState('')
16
+ const [error, setError] = useState('')
17
+ const [loading, setLoading] = useState(false)
18
+
19
+ const handleSubmit = async (e: React.FormEvent) => {
20
+ e.preventDefault()
21
+ setError('')
22
+ setLoading(true)
23
+
24
+ try {
25
+ if (isSignUp) {
26
+ await authClient.signUp.email({
27
+ email,
28
+ password,
29
+ name,
30
+ })
31
+ } else {
32
+ await authClient.signIn.email({
33
+ email,
34
+ password,
35
+ })
36
+ }
37
+ navigate({ to: '/' })
38
+ } catch (err) {
39
+ setError(err instanceof Error ? err.message : 'Authentication failed')
40
+ } finally {
41
+ setLoading(false)
42
+ }
43
+ }
44
+
45
+ return (
46
+ <div style={{ padding: '2rem', fontFamily: 'system-ui', maxWidth: '400px', margin: '0 auto' }}>
47
+ <h1>{isSignUp ? 'Create Account' : 'Sign In'}</h1>
48
+
49
+ <form
50
+ onSubmit={handleSubmit}
51
+ style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}
52
+ >
53
+ {isSignUp && (
54
+ <input
55
+ type="text"
56
+ placeholder="Name"
57
+ value={name}
58
+ onChange={(e) => setName(e.target.value)}
59
+ required
60
+ style={{ padding: '0.5rem', fontSize: '1rem' }}
61
+ />
62
+ )}
63
+ <input
64
+ type="email"
65
+ placeholder="Email"
66
+ value={email}
67
+ onChange={(e) => setEmail(e.target.value)}
68
+ required
69
+ style={{ padding: '0.5rem', fontSize: '1rem' }}
70
+ />
71
+ <input
72
+ type="password"
73
+ placeholder="Password"
74
+ value={password}
75
+ onChange={(e) => setPassword(e.target.value)}
76
+ required
77
+ style={{ padding: '0.5rem', fontSize: '1rem' }}
78
+ />
79
+
80
+ {error && <p style={{ color: 'red' }}>{error}</p>}
81
+
82
+ <button
83
+ type="submit"
84
+ disabled={loading}
85
+ style={{ padding: '0.5rem 1rem', fontSize: '1rem', cursor: 'pointer' }}
86
+ >
87
+ {loading ? 'Loading...' : isSignUp ? 'Create Account' : 'Sign In'}
88
+ </button>
89
+ </form>
90
+
91
+ <p style={{ marginTop: '1rem' }}>
92
+ {isSignUp ? 'Already have an account? ' : "Don't have an account? "}
93
+ <button
94
+ onClick={() => setIsSignUp(!isSignUp)}
95
+ style={{
96
+ background: 'none',
97
+ border: 'none',
98
+ color: 'blue',
99
+ cursor: 'pointer',
100
+ textDecoration: 'underline',
101
+ }}
102
+ >
103
+ {isSignUp ? 'Sign In' : 'Create Account'}
104
+ </button>
105
+ </p>
106
+ </div>
107
+ )
108
+ }
@@ -0,0 +1,12 @@
1
+ import type { auth } from '@__APP_NAME_KEBAB__/auth'
2
+ import { createAuthClient } from 'better-auth/react'
3
+ import { inferAdditionalFields } from 'better-auth/client/plugins'
4
+
5
+ export function createAuthClientService() {
6
+ return createAuthClient({
7
+ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000',
8
+ plugins: [inferAdditionalFields<typeof auth>()],
9
+ })
10
+ }
11
+
12
+ export type AuthClient = ReturnType<typeof createAuthClientService>
@@ -0,0 +1,3 @@
1
+ export * from './types'
2
+ export * from './rpc'
3
+ export * from './auth-client'
@@ -0,0 +1,29 @@
1
+ import { createORPCClient } from '@orpc/client'
2
+ import { RPCLink } from '@orpc/client/fetch'
3
+ import { createTanstackQueryUtils } from '@orpc/tanstack-query'
4
+ import { QueryClient } from '@tanstack/react-query'
5
+ import type { RPCClient, ORPCUtils } from './types'
6
+
7
+ export function createQueryClient(): QueryClient {
8
+ return new QueryClient()
9
+ }
10
+
11
+ export function createRPCLink() {
12
+ return new RPCLink({
13
+ url: `${import.meta.env.VITE_API_URL || 'http://localhost:3000'}/rpc`,
14
+ fetch(url, options) {
15
+ return fetch(url, {
16
+ ...options,
17
+ credentials: 'include',
18
+ })
19
+ },
20
+ })
21
+ }
22
+
23
+ export function createRPCClient(link: ReturnType<typeof createRPCLink>): RPCClient {
24
+ return createORPCClient(link)
25
+ }
26
+
27
+ export function createORPCUtils(client: RPCClient): ORPCUtils {
28
+ return createTanstackQueryUtils(client)
29
+ }
@@ -0,0 +1,14 @@
1
+ import type { QueryClient } from '@tanstack/react-query'
2
+ import type { createTanstackQueryUtils } from '@orpc/tanstack-query'
3
+ import type { AuthClient } from './auth-client'
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ export type RPCClient = Record<string, any>
7
+ export type ORPCUtils = ReturnType<typeof createTanstackQueryUtils<RPCClient>>
8
+
9
+ export interface Services {
10
+ queryClient: QueryClient
11
+ rpcClient: RPCClient
12
+ orpc: ORPCUtils
13
+ authClient: AuthClient
14
+ }
@@ -1,6 +1,7 @@
1
1
  import { defineConfig } from 'vite'
2
2
  import react from '@vitejs/plugin-react'
3
+ import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
3
4
 
4
5
  export default defineConfig({
5
- plugins: [react()],
6
+ plugins: [TanStackRouterVite(), react()],
6
7
  })
@@ -0,0 +1,23 @@
1
+ name: __APP_NAME_KEBAB__
2
+
3
+ services:
4
+ postgres:
5
+ image: postgres
6
+ container_name: __APP_NAME_KEBAB__-postgres
7
+ environment:
8
+ POSTGRES_DB: __APP_NAME_KEBAB__
9
+ POSTGRES_USER: postgres
10
+ POSTGRES_PASSWORD: password
11
+ ports:
12
+ - '5432:5432'
13
+ volumes:
14
+ - __APP_NAME_KEBAB___postgres_data:/var/lib/postgresql/data
15
+ healthcheck:
16
+ test: ['CMD-SHELL', 'pg_isready -U postgres']
17
+ interval: 10s
18
+ timeout: 5s
19
+ retries: 5
20
+ restart: unless-stopped
21
+
22
+ volumes:
23
+ __APP_NAME_KEBAB___postgres_data:
@@ -0,0 +1,18 @@
1
+ import js from '@eslint/js'
2
+ import tseslint from 'typescript-eslint'
3
+ import prettierConfig from 'eslint-config-prettier'
4
+
5
+ export default [
6
+ {
7
+ ignores: ['**/node_modules', '**/dist', '**/.turbo', '**/routeTree.gen.ts'],
8
+ },
9
+ js.configs.recommended,
10
+ ...tseslint.configs.recommended,
11
+ {
12
+ rules: {
13
+ '@typescript-eslint/no-explicit-any': 'warn',
14
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
15
+ },
16
+ },
17
+ prettierConfig,
18
+ ]
@@ -9,11 +9,41 @@
9
9
  "scripts": {
10
10
  "dev": "turbo dev",
11
11
  "build": "turbo build",
12
- "check-types": "turbo check-types"
12
+ "check-types": "turbo check-types",
13
+ "lint": "eslint apps packages",
14
+ "lint:fix": "eslint apps packages --fix",
15
+ "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
16
+ "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
17
+ "db:generate": "turbo -F @__APP_NAME_KEBAB__/db db:generate",
18
+ "db:migrate": "turbo -F @__APP_NAME_KEBAB__/db db:migrate",
19
+ "db:studio": "turbo -F @__APP_NAME_KEBAB__/db db:studio",
20
+ "prepare": "husky"
21
+ },
22
+ "lint-staged": {
23
+ "*.{ts,tsx,js,jsx}": [
24
+ "eslint --max-warnings=0",
25
+ "prettier --write"
26
+ ],
27
+ "*.{json,md,yml,yaml}": [
28
+ "prettier --write"
29
+ ]
30
+ },
31
+ "dependencies": {
32
+ "@kuckit/sdk": "^1.0.0",
33
+ "awilix": "^12.0.5",
34
+ "dotenv": "^17.2.2",
35
+ "zod": "^4.1.11"
13
36
  },
14
37
  "devDependencies": {
38
+ "@eslint/js": "^9.17.0",
39
+ "eslint": "^9.17.0",
40
+ "eslint-config-prettier": "^10.0.1",
41
+ "husky": "^9.1.7",
42
+ "lint-staged": "^16.0.0",
43
+ "prettier": "^3.4.2",
15
44
  "turbo": "^2.5.4",
16
- "typescript": "^5.8.2"
45
+ "typescript": "^5.8.2",
46
+ "typescript-eslint": "^8.18.2"
17
47
  },
18
48
  "packageManager": "bun@1.2.21"
19
49
  }
@@ -0,0 +1,27 @@
1
+ # AGENTS.md - API Package
2
+
3
+ > See root [AGENTS.md](../../AGENTS.md) for project overview
4
+
5
+ ## Purpose
6
+
7
+ Shared API context, types, and procedure definitions for oRPC.
8
+
9
+ ## Key Files
10
+
11
+ | File | Purpose |
12
+ | ------------ | -------------------------------- |
13
+ | `context.ts` | Request context type definitions |
14
+ | `index.ts` | Public exports |
15
+
16
+ ## Usage
17
+
18
+ ```typescript
19
+ import { type Context, protectedProcedure } from '@__APP_NAME_KEBAB__/api'
20
+ ```
21
+
22
+ ## Context Structure
23
+
24
+ The API context provides:
25
+
26
+ - `container` - Scoped DI container for the request
27
+ - `user` - Authenticated user (when using `protectedProcedure`)
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@__APP_NAME_KEBAB__/api",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "default": "./src/index.ts"
12
+ },
13
+ "./*": {
14
+ "types": "./src/*.ts",
15
+ "default": "./src/*.ts"
16
+ }
17
+ },
18
+ "devDependencies": {
19
+ "@types/express": "^5.0.1"
20
+ },
21
+ "peerDependencies": {
22
+ "typescript": "^5"
23
+ },
24
+ "dependencies": {
25
+ "@orpc/server": "^1.10.0",
26
+ "@orpc/client": "^1.10.0",
27
+ "@orpc/zod": "^1.10.0",
28
+ "better-auth": "^1.3.28",
29
+ "dotenv": "^17.2.2",
30
+ "zod": "^4.1.11",
31
+ "awilix": "^12.0.5",
32
+ "@__APP_NAME_KEBAB__/auth": "workspace:*",
33
+ "@__APP_NAME_KEBAB__/db": "workspace:*"
34
+ }
35
+ }
@@ -0,0 +1,48 @@
1
+ import type { Request } from 'express'
2
+ import { fromNodeHeaders } from 'better-auth/node'
3
+ import { auth } from '@__APP_NAME_KEBAB__/auth'
4
+ import { asValue, type AwilixContainer } from 'awilix'
5
+
6
+ declare global {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace Express {
9
+ interface Request {
10
+ scope?: AwilixContainer
11
+ }
12
+ }
13
+ }
14
+
15
+ export interface CreateContextOptions {
16
+ req: Request
17
+ }
18
+
19
+ /**
20
+ * Create per-request context with DI container
21
+ */
22
+ export async function createContext(opts: CreateContextOptions) {
23
+ let session
24
+ try {
25
+ session = await auth.api.getSession({
26
+ headers: fromNodeHeaders(opts.req.headers),
27
+ })
28
+ } catch (error) {
29
+ console.error('[Auth] Failed to get session:', error)
30
+ session = null
31
+ }
32
+
33
+ const requestId = crypto.randomUUID()
34
+
35
+ if (opts.req.scope) {
36
+ opts.req.scope.register({
37
+ session: asValue(session),
38
+ requestId: asValue(requestId),
39
+ })
40
+ }
41
+
42
+ return {
43
+ session,
44
+ di: opts.req.scope!,
45
+ }
46
+ }
47
+
48
+ export type Context = Awaited<ReturnType<typeof createContext>>
@@ -0,0 +1,22 @@
1
+ import { ORPCError, os } from '@orpc/server'
2
+ import type { Context } from './context'
3
+
4
+ export const o = os.$context<Context>()
5
+
6
+ export const publicProcedure = o
7
+
8
+ const requireAuth = o.middleware(async ({ context, next }) => {
9
+ if (!context.session?.user) {
10
+ throw new ORPCError('UNAUTHORIZED')
11
+ }
12
+ return next({
13
+ context: {
14
+ session: context.session,
15
+ },
16
+ })
17
+ })
18
+
19
+ export const protectedProcedure = publicProcedure.use(requireAuth)
20
+
21
+ export { createContext } from './context'
22
+ export type { Context } from './context'
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }
@@ -0,0 +1,45 @@
1
+ # AGENTS.md - Auth Package
2
+
3
+ > See root [AGENTS.md](../../AGENTS.md) for project overview
4
+
5
+ ## Purpose
6
+
7
+ Better-Auth configuration shared between server and web apps.
8
+
9
+ ## Key Files
10
+
11
+ | File | Purpose |
12
+ | ---------- | -------------------------------------- |
13
+ | `index.ts` | Auth configuration and client creation |
14
+
15
+ ## Server Usage
16
+
17
+ ```typescript
18
+ import { auth } from '@__APP_NAME_KEBAB__/auth'
19
+
20
+ // In Express
21
+ app.all('/api/auth/*', auth.handler)
22
+ ```
23
+
24
+ ## Client Usage
25
+
26
+ ```typescript
27
+ import { authClient } from '@__APP_NAME_KEBAB__/auth'
28
+
29
+ // Sign in
30
+ await authClient.signIn.email({ email, password })
31
+
32
+ // Sign out
33
+ await authClient.signOut()
34
+
35
+ // Get session
36
+ const session = await authClient.getSession()
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ Auth requires these environment variables:
42
+
43
+ - `BETTER_AUTH_SECRET` - Session encryption key
44
+ - `BETTER_AUTH_URL` - Callback URL for OAuth
45
+ - `DATABASE_URL` - For session storage
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@__APP_NAME_KEBAB__/auth",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./src/index.ts",
11
+ "default": "./src/index.ts"
12
+ },
13
+ "./*": {
14
+ "types": "./src/*.ts",
15
+ "default": "./src/*.ts"
16
+ }
17
+ },
18
+ "peerDependencies": {
19
+ "typescript": "^5"
20
+ },
21
+ "dependencies": {
22
+ "better-auth": "^1.3.28",
23
+ "dotenv": "^17.2.2",
24
+ "zod": "^4.1.11",
25
+ "@__APP_NAME_KEBAB__/db": "workspace:*"
26
+ }
27
+ }
@@ -0,0 +1,22 @@
1
+ import { betterAuth, type BetterAuthOptions } from 'better-auth'
2
+ import { drizzleAdapter } from 'better-auth/adapters/drizzle'
3
+ import { db } from '@__APP_NAME_KEBAB__/db'
4
+ import * as schema from '@__APP_NAME_KEBAB__/db/schema/auth'
5
+
6
+ export const auth = betterAuth<BetterAuthOptions>({
7
+ database: drizzleAdapter(db, {
8
+ provider: 'pg',
9
+ schema: schema,
10
+ }),
11
+ trustedOrigins: [process.env.CORS_ORIGIN || ''],
12
+ emailAndPassword: {
13
+ enabled: true,
14
+ },
15
+ advanced: {
16
+ defaultCookieAttributes: {
17
+ sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
18
+ secure: process.env.NODE_ENV === 'production',
19
+ httpOnly: true,
20
+ },
21
+ },
22
+ })
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }
@@ -0,0 +1,59 @@
1
+ # AGENTS.md - Database Package
2
+
3
+ > See root [AGENTS.md](../../AGENTS.md) for project overview
4
+
5
+ ## Purpose
6
+
7
+ Drizzle ORM configuration, database connection, migrations, and shared schema.
8
+
9
+ ## Key Files
10
+
11
+ | File | Purpose |
12
+ | ------------------- | ------------------------- |
13
+ | `drizzle.config.ts` | Drizzle Kit configuration |
14
+ | `src/connection.ts` | Database connection pool |
15
+ | `src/schema/` | Shared schema definitions |
16
+ | `src/migrations/` | SQL migration files |
17
+
18
+ ## Commands
19
+
20
+ ```bash
21
+ # Generate migration from schema changes
22
+ bun run db:generate
23
+
24
+ # Apply pending migrations
25
+ bun run db:migrate
26
+
27
+ # Open Drizzle Studio (database GUI)
28
+ bun run db:studio
29
+ ```
30
+
31
+ ## Adding a New Table
32
+
33
+ 1. Create schema file in `src/schema/` or in your module's `adapters/`:
34
+
35
+ ```typescript
36
+ import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
37
+
38
+ export const myTable = pgTable('my_table', {
39
+ id: uuid('id').primaryKey().defaultRandom(),
40
+ name: text('name').notNull(),
41
+ createdAt: timestamp('created_at').defaultNow(),
42
+ })
43
+ ```
44
+
45
+ 2. Export from `src/index.ts` if shared
46
+
47
+ 3. Generate and run migration:
48
+ ```bash
49
+ bun run db:generate
50
+ bun run db:migrate
51
+ ```
52
+
53
+ ## Connection
54
+
55
+ ```typescript
56
+ import { db } from '@__APP_NAME_KEBAB__/db'
57
+
58
+ const users = await db.select().from(usersTable)
59
+ ```
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'drizzle-kit'
2
+ import dotenv from 'dotenv'
3
+ import { dirname, resolve } from 'path'
4
+ import { fileURLToPath } from 'url'
5
+ import { buildDatabaseUrl } from './src/connection'
6
+
7
+ const currentFilePath = fileURLToPath(import.meta.url)
8
+ const currentDirPath = dirname(currentFilePath)
9
+
10
+ dotenv.config({
11
+ path: resolve(currentDirPath, '../../apps/server/.env'),
12
+ })
13
+
14
+ export default defineConfig({
15
+ schema: [resolve(currentDirPath, './src/schema')],
16
+ out: resolve(currentDirPath, './src/migrations'),
17
+ dialect: 'postgresql',
18
+ dbCredentials: { url: buildDatabaseUrl() },
19
+ })