create-kuckit-app 0.1.1 → 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.
- package/dist/bin.js +1 -1
- package/dist/{create-project-DTm05G7D.js → create-project-CP-h4Ygi.js} +7 -5
- package/dist/index.js +1 -1
- package/package.json +3 -2
- package/templates/base/.claude/CLAUDE.md +44 -0
- package/templates/base/.claude/agents/daidalos.md +76 -0
- package/templates/base/.claude/agents/episteme.md +79 -0
- package/templates/base/.claude/agents/librarian.md +132 -0
- package/templates/base/.claude/agents/oracle.md +210 -0
- package/templates/base/.claude/commands/create-plan.md +159 -0
- package/templates/base/.claude/commands/file-beads.md +98 -0
- package/templates/base/.claude/commands/review-beads.md +161 -0
- package/templates/base/.claude/settings.json +11 -0
- package/templates/base/.claude/skills/kuckit/SKILL.md +436 -0
- package/templates/base/.claude/skills/kuckit/references/ARCHITECTURE.md +388 -0
- package/templates/base/.claude/skills/kuckit/references/CLI-COMMANDS.md +365 -0
- package/templates/base/.claude/skills/kuckit/references/MODULE-DEVELOPMENT.md +581 -0
- package/templates/base/.claude/skills/kuckit/references/PACKAGES.md +112 -0
- package/templates/base/.claude/skills/kuckit/references/PUBLISHING.md +231 -0
- package/templates/base/.env.example +13 -0
- package/templates/base/.github/workflows/ci.yml +28 -0
- package/templates/base/.husky/pre-commit +1 -0
- package/templates/base/.prettierignore +5 -0
- package/templates/base/.prettierrc +8 -0
- package/templates/base/AGENTS.md +148 -0
- package/templates/base/apps/server/.env.example +18 -0
- package/templates/base/apps/server/AGENTS.md +37 -0
- package/templates/base/apps/server/package.json +13 -2
- package/templates/base/apps/server/src/app.ts +20 -0
- package/templates/base/apps/server/src/auth.ts +10 -0
- package/templates/base/apps/server/src/config/modules.ts +22 -0
- package/templates/base/apps/server/src/container.ts +81 -0
- package/templates/base/apps/server/src/health.ts +27 -0
- package/templates/base/apps/server/src/middleware/container.ts +41 -0
- package/templates/base/apps/server/src/rpc-router-registry.ts +26 -0
- package/templates/base/apps/server/src/rpc.ts +31 -0
- package/templates/base/apps/server/src/server.ts +42 -14
- package/templates/base/apps/web/.env.example +4 -0
- package/templates/base/apps/web/AGENTS.md +53 -0
- package/templates/base/apps/web/index.html +1 -1
- package/templates/base/apps/web/package.json +15 -2
- package/templates/base/apps/web/src/lib/kuckit-router.ts +42 -0
- package/templates/base/apps/web/src/main.tsx +26 -14
- package/templates/base/apps/web/src/providers/KuckitProvider.tsx +147 -0
- package/templates/base/apps/web/src/providers/ServicesProvider.tsx +47 -0
- package/templates/base/apps/web/src/routeTree.gen.ts +91 -0
- package/templates/base/apps/web/src/routes/__root.tsx +31 -0
- package/templates/base/apps/web/src/routes/index.tsx +46 -0
- package/templates/base/apps/web/src/routes/login.tsx +108 -0
- package/templates/base/apps/web/src/services/auth-client.ts +12 -0
- package/templates/base/apps/web/src/services/index.ts +3 -0
- package/templates/base/apps/web/src/services/rpc.ts +29 -0
- package/templates/base/apps/web/src/services/types.ts +14 -0
- package/templates/base/apps/web/vite.config.ts +2 -1
- package/templates/base/docker-compose.yml +23 -0
- package/templates/base/eslint.config.js +18 -0
- package/templates/base/package.json +32 -2
- package/templates/base/packages/api/AGENTS.md +27 -0
- package/templates/base/packages/api/package.json +35 -0
- package/templates/base/packages/api/src/context.ts +48 -0
- package/templates/base/packages/api/src/index.ts +22 -0
- package/templates/base/packages/api/tsconfig.json +8 -0
- package/templates/base/packages/auth/AGENTS.md +45 -0
- package/templates/base/packages/auth/package.json +27 -0
- package/templates/base/packages/auth/src/index.ts +22 -0
- package/templates/base/packages/auth/tsconfig.json +8 -0
- package/templates/base/packages/db/AGENTS.md +59 -0
- package/templates/base/packages/db/drizzle.config.ts +19 -0
- package/templates/base/packages/db/package.json +36 -0
- package/templates/base/packages/db/src/connection.ts +40 -0
- package/templates/base/packages/db/src/index.ts +4 -0
- package/templates/base/packages/db/src/migrations/0000_init.sql +54 -0
- package/templates/base/packages/db/src/migrations/meta/_journal.json +13 -0
- package/templates/base/packages/db/src/schema/auth.ts +51 -0
- package/templates/base/packages/db/tsconfig.json +8 -0
- package/templates/base/packages/items-module/AGENTS.md +112 -0
- package/templates/base/packages/items-module/package.json +32 -0
- package/templates/base/packages/items-module/src/adapters/item.drizzle.ts +66 -0
- package/templates/base/packages/items-module/src/api/items.router.ts +47 -0
- package/templates/base/packages/items-module/src/client-module.ts +39 -0
- package/templates/base/packages/items-module/src/domain/item.entity.ts +36 -0
- package/templates/base/packages/items-module/src/index.ts +15 -0
- package/templates/base/packages/items-module/src/module.ts +53 -0
- package/templates/base/packages/items-module/src/ports/item.repository.ts +13 -0
- package/templates/base/packages/items-module/src/ui/ItemsPage.tsx +162 -0
- package/templates/base/packages/items-module/src/usecases/create-item.ts +25 -0
- package/templates/base/packages/items-module/src/usecases/delete-item.ts +18 -0
- package/templates/base/packages/items-module/src/usecases/get-item.ts +19 -0
- package/templates/base/packages/items-module/src/usecases/list-items.ts +21 -0
- package/templates/base/packages/items-module/tsconfig.json +9 -0
- package/templates/base/turbo.json +13 -1
- package/templates/base/apps/web/src/App.tsx +0 -16
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
interface Item {
|
|
4
|
+
id: string
|
|
5
|
+
name: string
|
|
6
|
+
description?: string
|
|
7
|
+
createdAt: Date
|
|
8
|
+
updatedAt: Date
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Items page component
|
|
13
|
+
* Demonstrates CRUD operations using the items module
|
|
14
|
+
*/
|
|
15
|
+
export function ItemsPage() {
|
|
16
|
+
const [items, setItems] = useState<Item[]>([])
|
|
17
|
+
const [newItemName, setNewItemName] = useState('')
|
|
18
|
+
const [newItemDescription, setNewItemDescription] = useState('')
|
|
19
|
+
const [loading, setLoading] = useState(true)
|
|
20
|
+
const [error, setError] = useState<string | null>(null)
|
|
21
|
+
|
|
22
|
+
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:3000'
|
|
23
|
+
|
|
24
|
+
// Fetch items on mount
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
fetchItems()
|
|
27
|
+
}, [])
|
|
28
|
+
|
|
29
|
+
const fetchItems = async () => {
|
|
30
|
+
try {
|
|
31
|
+
setLoading(true)
|
|
32
|
+
const response = await fetch(`${apiUrl}/rpc/items.list`, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
credentials: 'include',
|
|
36
|
+
body: JSON.stringify({}),
|
|
37
|
+
})
|
|
38
|
+
if (response.ok) {
|
|
39
|
+
const data = await response.json()
|
|
40
|
+
setItems(data)
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
setError('Failed to load items')
|
|
44
|
+
} finally {
|
|
45
|
+
setLoading(false)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const createItem = async (e: React.FormEvent) => {
|
|
50
|
+
e.preventDefault()
|
|
51
|
+
if (!newItemName.trim()) return
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(`${apiUrl}/rpc/items.create`, {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: { 'Content-Type': 'application/json' },
|
|
57
|
+
credentials: 'include',
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
name: newItemName,
|
|
60
|
+
description: newItemDescription || undefined,
|
|
61
|
+
}),
|
|
62
|
+
})
|
|
63
|
+
if (response.ok) {
|
|
64
|
+
setNewItemName('')
|
|
65
|
+
setNewItemDescription('')
|
|
66
|
+
fetchItems()
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
setError('Failed to create item')
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const deleteItem = async (id: string) => {
|
|
74
|
+
try {
|
|
75
|
+
const response = await fetch(`${apiUrl}/rpc/items.delete`, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
credentials: 'include',
|
|
79
|
+
body: JSON.stringify({ id }),
|
|
80
|
+
})
|
|
81
|
+
if (response.ok) {
|
|
82
|
+
fetchItems()
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
setError('Failed to delete item')
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (loading) {
|
|
90
|
+
return <div style={{ padding: '2rem' }}>Loading items...</div>
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div style={{ padding: '2rem', fontFamily: 'system-ui', maxWidth: '600px', margin: '0 auto' }}>
|
|
95
|
+
<h1>Items</h1>
|
|
96
|
+
|
|
97
|
+
{error && (
|
|
98
|
+
<div style={{ color: 'red', marginBottom: '1rem' }}>
|
|
99
|
+
{error}
|
|
100
|
+
<button onClick={() => setError(null)} style={{ marginLeft: '1rem' }}>
|
|
101
|
+
Dismiss
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
|
|
106
|
+
<form onSubmit={createItem} style={{ marginBottom: '2rem' }}>
|
|
107
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
|
108
|
+
<input
|
|
109
|
+
type="text"
|
|
110
|
+
placeholder="Item name"
|
|
111
|
+
value={newItemName}
|
|
112
|
+
onChange={(e) => setNewItemName(e.target.value)}
|
|
113
|
+
required
|
|
114
|
+
style={{ padding: '0.5rem', fontSize: '1rem' }}
|
|
115
|
+
/>
|
|
116
|
+
<input
|
|
117
|
+
type="text"
|
|
118
|
+
placeholder="Description (optional)"
|
|
119
|
+
value={newItemDescription}
|
|
120
|
+
onChange={(e) => setNewItemDescription(e.target.value)}
|
|
121
|
+
style={{ padding: '0.5rem', fontSize: '1rem' }}
|
|
122
|
+
/>
|
|
123
|
+
<button type="submit" style={{ padding: '0.5rem 1rem', cursor: 'pointer' }}>
|
|
124
|
+
Add Item
|
|
125
|
+
</button>
|
|
126
|
+
</div>
|
|
127
|
+
</form>
|
|
128
|
+
|
|
129
|
+
<ul style={{ listStyle: 'none', padding: 0 }}>
|
|
130
|
+
{items.map((item) => (
|
|
131
|
+
<li
|
|
132
|
+
key={item.id}
|
|
133
|
+
style={{
|
|
134
|
+
padding: '1rem',
|
|
135
|
+
border: '1px solid #ccc',
|
|
136
|
+
marginBottom: '0.5rem',
|
|
137
|
+
borderRadius: '4px',
|
|
138
|
+
display: 'flex',
|
|
139
|
+
justifyContent: 'space-between',
|
|
140
|
+
alignItems: 'center',
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
<div>
|
|
144
|
+
<strong>{item.name}</strong>
|
|
145
|
+
{item.description && (
|
|
146
|
+
<p style={{ margin: '0.5rem 0 0 0', color: '#666' }}>{item.description}</p>
|
|
147
|
+
)}
|
|
148
|
+
</div>
|
|
149
|
+
<button
|
|
150
|
+
onClick={() => deleteItem(item.id)}
|
|
151
|
+
style={{ cursor: 'pointer', color: 'red', background: 'none', border: 'none' }}
|
|
152
|
+
>
|
|
153
|
+
Delete
|
|
154
|
+
</button>
|
|
155
|
+
</li>
|
|
156
|
+
))}
|
|
157
|
+
</ul>
|
|
158
|
+
|
|
159
|
+
{items.length === 0 && <p>No items yet. Create one above!</p>}
|
|
160
|
+
</div>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ItemRepository } from '../ports/item.repository'
|
|
2
|
+
import type { Item, CreateItemInput } from '../domain/item.entity'
|
|
3
|
+
|
|
4
|
+
export interface CreateItemUseCaseInput extends CreateItemInput {
|
|
5
|
+
userId: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface CreateItemDeps {
|
|
9
|
+
itemRepository: ItemRepository
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create item use case
|
|
14
|
+
*/
|
|
15
|
+
export function makeCreateItem(deps: CreateItemDeps) {
|
|
16
|
+
return async (input: CreateItemUseCaseInput): Promise<Item> => {
|
|
17
|
+
const id = crypto.randomUUID()
|
|
18
|
+
return deps.itemRepository.create({
|
|
19
|
+
id,
|
|
20
|
+
name: input.name,
|
|
21
|
+
description: input.description,
|
|
22
|
+
userId: input.userId,
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ItemRepository } from '../ports/item.repository'
|
|
2
|
+
|
|
3
|
+
export interface DeleteItemInput {
|
|
4
|
+
id: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface DeleteItemDeps {
|
|
8
|
+
itemRepository: ItemRepository
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Delete item use case
|
|
13
|
+
*/
|
|
14
|
+
export function makeDeleteItem(deps: DeleteItemDeps) {
|
|
15
|
+
return async (input: DeleteItemInput): Promise<boolean> => {
|
|
16
|
+
return deps.itemRepository.delete(input.id)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ItemRepository } from '../ports/item.repository'
|
|
2
|
+
import type { Item } from '../domain/item.entity'
|
|
3
|
+
|
|
4
|
+
export interface GetItemInput {
|
|
5
|
+
id: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface GetItemDeps {
|
|
9
|
+
itemRepository: ItemRepository
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get item use case
|
|
14
|
+
*/
|
|
15
|
+
export function makeGetItem(deps: GetItemDeps) {
|
|
16
|
+
return async (input: GetItemInput): Promise<Item | null> => {
|
|
17
|
+
return deps.itemRepository.findById(input.id)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ItemRepository } from '../ports/item.repository'
|
|
2
|
+
import type { Item } from '../domain/item.entity'
|
|
3
|
+
|
|
4
|
+
export interface ListItemsInput {
|
|
5
|
+
userId: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type ListItemsOutput = Item[]
|
|
9
|
+
|
|
10
|
+
interface ListItemsDeps {
|
|
11
|
+
itemRepository: ItemRepository
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* List items use case
|
|
16
|
+
*/
|
|
17
|
+
export function makeListItems(deps: ListItemsDeps) {
|
|
18
|
+
return async (input: ListItemsInput): Promise<ListItemsOutput> => {
|
|
19
|
+
return deps.itemRepository.findByUserId(input.userId)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://turbo.build/schema.json",
|
|
3
|
+
"ui": "tui",
|
|
3
4
|
"tasks": {
|
|
4
5
|
"build": {
|
|
5
6
|
"dependsOn": ["^build"],
|
|
7
|
+
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
|
6
8
|
"outputs": ["dist/**"]
|
|
7
9
|
},
|
|
8
10
|
"dev": {
|
|
@@ -10,7 +12,17 @@
|
|
|
10
12
|
"persistent": true
|
|
11
13
|
},
|
|
12
14
|
"check-types": {
|
|
13
|
-
"dependsOn": ["^
|
|
15
|
+
"dependsOn": ["^check-types"]
|
|
16
|
+
},
|
|
17
|
+
"db:generate": {
|
|
18
|
+
"cache": false
|
|
19
|
+
},
|
|
20
|
+
"db:migrate": {
|
|
21
|
+
"cache": false
|
|
22
|
+
},
|
|
23
|
+
"db:studio": {
|
|
24
|
+
"cache": false,
|
|
25
|
+
"persistent": true
|
|
14
26
|
}
|
|
15
27
|
}
|
|
16
28
|
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export function App() {
|
|
2
|
-
return (
|
|
3
|
-
<div style={{ fontFamily: 'system-ui', padding: '2rem' }}>
|
|
4
|
-
<h1>Welcome to __APP_NAME__</h1>
|
|
5
|
-
<p>Your Kuckit application is ready!</p>
|
|
6
|
-
<ul>
|
|
7
|
-
<li>
|
|
8
|
-
Server: <a href="http://localhost:3000/health">http://localhost:3000/health</a>
|
|
9
|
-
</li>
|
|
10
|
-
<li>
|
|
11
|
-
Web: <a href="http://localhost:3001">http://localhost:3001</a>
|
|
12
|
-
</li>
|
|
13
|
-
</ul>
|
|
14
|
-
</div>
|
|
15
|
-
)
|
|
16
|
-
}
|