create-theokit 1.0.13 → 1.0.15
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/cli.js +2 -12
- package/dist/cli.js.map +1 -1
- package/package.json +4 -1
- package/templates/default/README.md.tmpl +37 -50
- package/templates/default/app/layout.tsx +79 -3
- package/templates/default/app/page.tsx +281 -61
- package/templates/default/dot-claude/skills/theokit-agents/SKILL.md +15 -0
- package/templates/default/eslint.config.mjs +1 -5
- package/templates/default/package.json.tmpl +10 -9
- package/templates/default/server/routes/chat.ts +69 -0
- package/templates/default/theo.config.ts +1 -3
- package/templates/default/tsconfig.json +7 -14
- package/templates/default/types/jobs.d.ts +25 -0
- package/templates/services/agent-node/Dockerfile.tmpl +20 -0
- package/templates/services/agent-node/README.md +38 -0
- package/templates/services/agent-node/package.json.tmpl +18 -0
- package/templates/services/agent-node/src/index.ts +58 -0
- package/templates/services/agent-node/tsconfig.json +13 -0
- package/templates/default/AGENTS.md +0 -91
- package/templates/default/app/globals.css +0 -377
- package/templates/default/app.ts +0 -15
- package/templates/default/drizzle.config.ts +0 -10
- package/templates/default/server/db/index.ts +0 -10
- package/templates/default/server/db/schema.ts +0 -13
- package/templates/default/server/index.ts +0 -2
- package/templates/default/tests/tasks.test.ts +0 -18
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TheoKit polyglot agent service — Hono sidecar.
|
|
3
|
+
*
|
|
4
|
+
* Conforms to the Like-Vercel runtime contract (ADR-0015):
|
|
5
|
+
* - Fetch-handler entry (native fetch handler from Hono)
|
|
6
|
+
* - GET /health convention (200 / 503)
|
|
7
|
+
* - JSON-line stdout logs
|
|
8
|
+
* - W3C traceparent propagation
|
|
9
|
+
* - Env vars at runtime
|
|
10
|
+
*
|
|
11
|
+
* Generated by `create-theokit --backend node`.
|
|
12
|
+
*/
|
|
13
|
+
import { Hono } from 'hono'
|
|
14
|
+
import { serve } from '@hono/node-server'
|
|
15
|
+
|
|
16
|
+
const SERVICE_NAME = process.env.THEOKIT_SERVICE_NAME ?? 'agent-node'
|
|
17
|
+
const SERVICE_PORT = Number.parseInt(process.env.THEOKIT_SERVICE_PORT ?? '8002', 10)
|
|
18
|
+
|
|
19
|
+
function log(
|
|
20
|
+
level: 'info' | 'warn' | 'error',
|
|
21
|
+
message: string,
|
|
22
|
+
extra: Record<string, unknown> = {},
|
|
23
|
+
) {
|
|
24
|
+
// eslint-disable-next-line no-console -- structured stdout per ADR-0015 invariant #5
|
|
25
|
+
console.log(
|
|
26
|
+
JSON.stringify({
|
|
27
|
+
timestamp: new Date().toISOString(),
|
|
28
|
+
level,
|
|
29
|
+
message,
|
|
30
|
+
service: SERVICE_NAME,
|
|
31
|
+
...extra,
|
|
32
|
+
}),
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const app = new Hono()
|
|
37
|
+
|
|
38
|
+
// ADR-0015 invariant #6 — W3C traceparent propagation
|
|
39
|
+
app.use(async (c, next) => {
|
|
40
|
+
const tp = c.req.header('traceparent')
|
|
41
|
+
if (tp) {
|
|
42
|
+
log('info', 'request', { traceparent: tp, path: c.req.path })
|
|
43
|
+
}
|
|
44
|
+
await next()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// ADR-0015 invariant #4 — healthcheck convention
|
|
48
|
+
app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
49
|
+
|
|
50
|
+
// Example endpoint — TheoKit proxies /api/worker/* to this service.
|
|
51
|
+
app.post('/echo', async (c) => {
|
|
52
|
+
const body = (await c.req.json()) as { message: string }
|
|
53
|
+
return c.json({ echo: body.message })
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
serve({ fetch: app.fetch, port: SERVICE_PORT }, () => {
|
|
57
|
+
log('info', `agent-node listening on :${String(SERVICE_PORT)}`)
|
|
58
|
+
})
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# AGENTS.md — TheoKit App
|
|
2
|
-
|
|
3
|
-
Guide for coding agents (Claude, Copilot, Cursor) working on this TheoKit project.
|
|
4
|
-
|
|
5
|
-
## Architecture
|
|
6
|
-
|
|
7
|
-
This is a **full-stack TypeScript app** built with TheoKit — a framework for AI agent apps.
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
server/
|
|
11
|
-
routes/ → HTTP API routes (defineRoute + Zod validation)
|
|
12
|
-
health.ts → GET /api/health
|
|
13
|
-
db/
|
|
14
|
-
schema.ts → Drizzle ORM schema (SQLite) — empty, add your tables
|
|
15
|
-
index.ts → DB connection (better-sqlite3, WAL mode)
|
|
16
|
-
app/
|
|
17
|
-
page.tsx → React frontend
|
|
18
|
-
layout.tsx → Root layout
|
|
19
|
-
tests/
|
|
20
|
-
tasks.test.ts → Example unit test
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Key Patterns
|
|
24
|
-
|
|
25
|
-
### Routes (defineRoute)
|
|
26
|
-
```typescript
|
|
27
|
-
import { defineRoute } from 'theokit/server/define'
|
|
28
|
-
import { z } from 'zod'
|
|
29
|
-
|
|
30
|
-
export const GET = defineRoute({
|
|
31
|
-
handler: () => db.select().from(posts).all(),
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
export const POST = defineRoute({
|
|
35
|
-
body: z.object({ title: z.string().min(3) }),
|
|
36
|
-
status: 201,
|
|
37
|
-
handler: ({ body }) => db.insert(posts).values(body).returning().get(),
|
|
38
|
-
})
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Database (Drizzle + SQLite)
|
|
42
|
-
```typescript
|
|
43
|
-
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
|
|
44
|
-
|
|
45
|
-
export const posts = sqliteTable('posts', {
|
|
46
|
-
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
47
|
-
title: text('title').notNull(),
|
|
48
|
-
published: integer('published', { mode: 'boolean' }).notNull().default(false),
|
|
49
|
-
})
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Scaffold a Resource
|
|
53
|
-
```bash
|
|
54
|
-
npx theokit generate resource posts title:string published:boolean
|
|
55
|
-
# Creates: schema table + routes (CRUD) + test
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Validation
|
|
59
|
-
- **Zod is the single source of truth** — define schema once, get types + validation + OpenAPI
|
|
60
|
-
- `body: z.object(...)` in defineRoute validates automatically, returns 422 on failure
|
|
61
|
-
- Use `z.infer<typeof schema>` for TypeScript types
|
|
62
|
-
|
|
63
|
-
### Dynamic Routes
|
|
64
|
-
- `server/routes/posts/[id].ts` → `/api/posts/:id`
|
|
65
|
-
- Params validated with `params: z.object({ id: z.coerce.number() })`
|
|
66
|
-
|
|
67
|
-
### Path Aliases
|
|
68
|
-
- `@/*` → project root (configured in tsconfig.json)
|
|
69
|
-
|
|
70
|
-
## Commands
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
npm run dev # Start dev server
|
|
74
|
-
npm run build # Build for production
|
|
75
|
-
npm run start # Run production build
|
|
76
|
-
npm run test # Run tests (vitest)
|
|
77
|
-
npm run lint # ESLint check
|
|
78
|
-
npm run format # Prettier format
|
|
79
|
-
npm run typecheck # TypeScript type check
|
|
80
|
-
npx theokit generate resource <name> <fields...> # Scaffold CRUD resource
|
|
81
|
-
npx drizzle-kit push # Apply schema changes (dev)
|
|
82
|
-
npx drizzle-kit generate # Generate migration files (prod)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Don't
|
|
86
|
-
|
|
87
|
-
- Don't use `any` — use Zod schemas + `z.infer<>`
|
|
88
|
-
- Don't write raw `res.status().json()` — use defineRoute with status option
|
|
89
|
-
- Don't parse request body manually — use `body: z.object(...)` in defineRoute
|
|
90
|
-
- Don't import from `theokit/dist/...` or `theokit/src/...` — use public exports only
|
|
91
|
-
- Don't call LLM APIs directly — use @Agent + @Tool decorators
|
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
/* TheoKit — design system */
|
|
2
|
-
|
|
3
|
-
:root {
|
|
4
|
-
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
|
5
|
-
--font-mono: 'SF Mono', SFMono-Regular, ui-monospace, 'DejaVu Sans Mono', Menlo, Consolas, monospace;
|
|
6
|
-
|
|
7
|
-
--bg: #fafafa;
|
|
8
|
-
--bg-content: #ffffff;
|
|
9
|
-
--card: #ffffff;
|
|
10
|
-
--border: #e5e5e5;
|
|
11
|
-
--text: #171717;
|
|
12
|
-
--text-secondary: #666666;
|
|
13
|
-
--text-muted: #999999;
|
|
14
|
-
--accent: #6366f1;
|
|
15
|
-
--accent-hover: #4f46e5;
|
|
16
|
-
--btn-primary-bg: #171717;
|
|
17
|
-
--btn-primary-hover: #383838;
|
|
18
|
-
--btn-secondary-hover: #f2f2f2;
|
|
19
|
-
--btn-secondary-border: #ebebeb;
|
|
20
|
-
--green: #22c55e;
|
|
21
|
-
--red: #ef4444;
|
|
22
|
-
--yellow: #eab308;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
@media (prefers-color-scheme: dark) {
|
|
26
|
-
:root {
|
|
27
|
-
--bg: #0a0a0a;
|
|
28
|
-
--bg-content: #0a0a0a;
|
|
29
|
-
--card: #141414;
|
|
30
|
-
--border: #2a2a2a;
|
|
31
|
-
--text: #ededed;
|
|
32
|
-
--text-secondary: #999999;
|
|
33
|
-
--text-muted: #666666;
|
|
34
|
-
--accent: #818cf8;
|
|
35
|
-
--accent-hover: #6366f1;
|
|
36
|
-
--btn-primary-bg: #ededed;
|
|
37
|
-
--btn-primary-hover: #cccccc;
|
|
38
|
-
--btn-secondary-hover: #1a1a1a;
|
|
39
|
-
--btn-secondary-border: #2a2a2a;
|
|
40
|
-
--green: #22c55e;
|
|
41
|
-
--red: #ef4444;
|
|
42
|
-
--yellow: #eab308;
|
|
43
|
-
}
|
|
44
|
-
html { color-scheme: dark; }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
48
|
-
|
|
49
|
-
html, body {
|
|
50
|
-
max-width: 100vw;
|
|
51
|
-
overflow-x: hidden;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
body {
|
|
55
|
-
font-family: var(--font-sans);
|
|
56
|
-
background: var(--bg);
|
|
57
|
-
color: var(--text);
|
|
58
|
-
min-height: 100vh;
|
|
59
|
-
display: flex;
|
|
60
|
-
flex-direction: column;
|
|
61
|
-
align-items: center;
|
|
62
|
-
-webkit-font-smoothing: antialiased;
|
|
63
|
-
-moz-osx-font-smoothing: grayscale;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
a { color: inherit; text-decoration: none; }
|
|
67
|
-
code, pre { font-family: var(--font-mono); }
|
|
68
|
-
|
|
69
|
-
/* ─── Page container ────────────────────────────────── */
|
|
70
|
-
|
|
71
|
-
.page {
|
|
72
|
-
display: flex;
|
|
73
|
-
flex: 1;
|
|
74
|
-
flex-direction: column;
|
|
75
|
-
align-items: center;
|
|
76
|
-
width: 100%;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.main {
|
|
80
|
-
display: flex;
|
|
81
|
-
flex: 1;
|
|
82
|
-
flex-direction: column;
|
|
83
|
-
width: 100%;
|
|
84
|
-
max-width: 900px;
|
|
85
|
-
background: var(--bg-content);
|
|
86
|
-
padding: 80px 48px 48px;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@media (max-width: 768px) {
|
|
90
|
-
.main { padding: 48px 20px 32px; }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/* ─── Hero ──────────────────────────────────────────── */
|
|
94
|
-
|
|
95
|
-
.hero {
|
|
96
|
-
display: flex;
|
|
97
|
-
flex-direction: column;
|
|
98
|
-
align-items: center;
|
|
99
|
-
text-align: center;
|
|
100
|
-
gap: 16px;
|
|
101
|
-
margin-bottom: 48px;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.hero-logo {
|
|
105
|
-
border-radius: 16px;
|
|
106
|
-
margin-bottom: 8px;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.hero h1 {
|
|
110
|
-
font-size: 40px;
|
|
111
|
-
font-weight: 700;
|
|
112
|
-
letter-spacing: -2.4px;
|
|
113
|
-
line-height: 48px;
|
|
114
|
-
text-wrap: balance;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.hero .tagline {
|
|
118
|
-
font-size: 18px;
|
|
119
|
-
line-height: 28px;
|
|
120
|
-
color: var(--text-secondary);
|
|
121
|
-
max-width: 440px;
|
|
122
|
-
text-wrap: balance;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.hero .hint {
|
|
126
|
-
font-size: 14px;
|
|
127
|
-
color: var(--text-muted);
|
|
128
|
-
margin-top: 8px;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.hero .hint code {
|
|
132
|
-
background: var(--card);
|
|
133
|
-
border: 1px solid var(--border);
|
|
134
|
-
padding: 2px 8px;
|
|
135
|
-
border-radius: 6px;
|
|
136
|
-
font-size: 13px;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
@media (max-width: 768px) {
|
|
140
|
-
.hero h1 { font-size: 32px; line-height: 40px; letter-spacing: -1.92px; }
|
|
141
|
-
.hero .tagline { font-size: 16px; }
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/* ─── CTA Buttons ───────────────────────────────────── */
|
|
145
|
-
|
|
146
|
-
.ctas {
|
|
147
|
-
display: flex;
|
|
148
|
-
gap: 12px;
|
|
149
|
-
margin-top: 8px;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.btn {
|
|
153
|
-
display: inline-flex;
|
|
154
|
-
justify-content: center;
|
|
155
|
-
align-items: center;
|
|
156
|
-
height: 40px;
|
|
157
|
-
padding: 0 20px;
|
|
158
|
-
border-radius: 128px;
|
|
159
|
-
border: 1px solid transparent;
|
|
160
|
-
font-size: 14px;
|
|
161
|
-
font-weight: 500;
|
|
162
|
-
cursor: pointer;
|
|
163
|
-
transition: background 0.2s, border-color 0.2s;
|
|
164
|
-
text-decoration: none;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.btn.primary {
|
|
168
|
-
background: var(--btn-primary-bg);
|
|
169
|
-
color: var(--bg);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
.btn.secondary {
|
|
173
|
-
border-color: var(--btn-secondary-border);
|
|
174
|
-
color: var(--text);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
@media (hover: hover) and (pointer: fine) {
|
|
178
|
-
.btn.primary:hover { background: var(--btn-primary-hover); }
|
|
179
|
-
.btn.secondary:hover { background: var(--btn-secondary-hover); border-color: transparent; }
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/* ─── Role selector ─────────────────────────────────── */
|
|
183
|
-
|
|
184
|
-
.role-bar {
|
|
185
|
-
display: flex;
|
|
186
|
-
justify-content: center;
|
|
187
|
-
align-items: center;
|
|
188
|
-
gap: 8px;
|
|
189
|
-
margin-bottom: 32px;
|
|
190
|
-
font-size: 14px;
|
|
191
|
-
color: var(--text-muted);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
.role-bar select {
|
|
195
|
-
padding: 6px 12px;
|
|
196
|
-
background: var(--card);
|
|
197
|
-
border: 1px solid var(--border);
|
|
198
|
-
border-radius: 6px;
|
|
199
|
-
color: var(--text);
|
|
200
|
-
font-size: 13px;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/* ─── Grid ──────────────────────────────────────────── */
|
|
204
|
-
|
|
205
|
-
.grid {
|
|
206
|
-
display: grid;
|
|
207
|
-
grid-template-columns: 1fr 1fr;
|
|
208
|
-
gap: 24px;
|
|
209
|
-
width: 100%;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
@media (max-width: 768px) {
|
|
213
|
-
.grid { grid-template-columns: 1fr; }
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/* ─── Cards ─────────────────────────────────────────── */
|
|
217
|
-
|
|
218
|
-
.card {
|
|
219
|
-
background: var(--card);
|
|
220
|
-
border: 1px solid var(--border);
|
|
221
|
-
border-radius: 12px;
|
|
222
|
-
padding: 24px;
|
|
223
|
-
width: 100%;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
.card h2 {
|
|
227
|
-
font-size: 15px;
|
|
228
|
-
font-weight: 600;
|
|
229
|
-
margin-bottom: 16px;
|
|
230
|
-
display: flex;
|
|
231
|
-
align-items: center;
|
|
232
|
-
gap: 8px;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/* ─── Badges ────────────────────────────────────────── */
|
|
236
|
-
|
|
237
|
-
.badge {
|
|
238
|
-
font-size: 11px;
|
|
239
|
-
padding: 2px 10px;
|
|
240
|
-
border-radius: 99px;
|
|
241
|
-
font-weight: 500;
|
|
242
|
-
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
|
243
|
-
color: var(--accent);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
.badge-ai {
|
|
247
|
-
background: color-mix(in srgb, var(--yellow) 12%, transparent);
|
|
248
|
-
color: var(--yellow);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/* ─── Table ─────────────────────────────────────────── */
|
|
252
|
-
|
|
253
|
-
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
254
|
-
th {
|
|
255
|
-
text-align: left; padding: 8px 6px;
|
|
256
|
-
color: var(--text-muted); border-bottom: 1px solid var(--border);
|
|
257
|
-
font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
|
|
258
|
-
}
|
|
259
|
-
td { padding: 8px 6px; border-bottom: 1px solid var(--border); }
|
|
260
|
-
tr.done td { opacity: 0.4; text-decoration: line-through; }
|
|
261
|
-
|
|
262
|
-
.prio { font-size: 11px; padding: 2px 8px; border-radius: 99px; font-weight: 500; }
|
|
263
|
-
.prio-high { background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--red); }
|
|
264
|
-
.prio-medium { background: color-mix(in srgb, var(--yellow) 12%, transparent); color: var(--yellow); }
|
|
265
|
-
.prio-low { background: color-mix(in srgb, var(--green) 12%, transparent); color: var(--green); }
|
|
266
|
-
|
|
267
|
-
/* ─── Forms ─────────────────────────────────────────── */
|
|
268
|
-
|
|
269
|
-
.create-bar { display: flex; gap: 8px; margin-top: 16px; }
|
|
270
|
-
.create-bar input {
|
|
271
|
-
flex: 1; padding: 8px 12px;
|
|
272
|
-
background: var(--bg); border: 1px solid var(--border);
|
|
273
|
-
border-radius: 8px; color: var(--text); font-size: 13px;
|
|
274
|
-
outline: none; transition: border-color 0.2s;
|
|
275
|
-
}
|
|
276
|
-
.create-bar input:focus { border-color: var(--accent); }
|
|
277
|
-
.create-bar select {
|
|
278
|
-
padding: 8px 10px; background: var(--bg);
|
|
279
|
-
border: 1px solid var(--border); border-radius: 8px;
|
|
280
|
-
color: var(--text); font-size: 13px;
|
|
281
|
-
}
|
|
282
|
-
.create-bar button, .chat-bar button {
|
|
283
|
-
padding: 8px 16px; background: var(--accent); color: white;
|
|
284
|
-
border: none; border-radius: 8px; cursor: pointer;
|
|
285
|
-
font-weight: 600; font-size: 13px; transition: background 0.2s;
|
|
286
|
-
}
|
|
287
|
-
@media (hover: hover) and (pointer: fine) {
|
|
288
|
-
.create-bar button:hover, .chat-bar button:hover { background: var(--accent-hover); }
|
|
289
|
-
}
|
|
290
|
-
.create-bar button:disabled, .chat-bar button:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
291
|
-
.error { color: var(--red); font-size: 13px; margin-top: 6px; }
|
|
292
|
-
|
|
293
|
-
/* ─── Chat ──────────────────────────────────────────── */
|
|
294
|
-
|
|
295
|
-
.chat-box {
|
|
296
|
-
height: 320px; overflow-y: auto; padding: 14px;
|
|
297
|
-
background: var(--bg); border: 1px solid var(--border);
|
|
298
|
-
border-radius: 10px; margin-bottom: 12px;
|
|
299
|
-
font-size: 13px; line-height: 1.6;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
.msg { margin-bottom: 8px; padding: 8px 12px; border-radius: 8px; }
|
|
303
|
-
.msg.user { background: color-mix(in srgb, var(--accent) 10%, transparent); color: var(--accent); }
|
|
304
|
-
.msg.agent { background: var(--card); border: 1px solid var(--border); }
|
|
305
|
-
.msg.tool { background: color-mix(in srgb, var(--yellow) 8%, transparent); color: var(--yellow); font-size: 12px; font-family: var(--font-mono); }
|
|
306
|
-
.msg.system { color: var(--text-muted); font-size: 12px; font-style: italic; }
|
|
307
|
-
.msg.error { color: var(--red); font-size: 13px; }
|
|
308
|
-
|
|
309
|
-
.chat-bar { display: flex; gap: 8px; }
|
|
310
|
-
.chat-bar input {
|
|
311
|
-
flex: 1; padding: 10px 14px;
|
|
312
|
-
background: var(--bg); border: 1px solid var(--border);
|
|
313
|
-
border-radius: 10px; color: var(--text); font-size: 14px;
|
|
314
|
-
outline: none; transition: border-color 0.2s;
|
|
315
|
-
}
|
|
316
|
-
.chat-bar input:focus { border-color: var(--accent); }
|
|
317
|
-
|
|
318
|
-
/* ─── Features grid ─────────────────────────────────── */
|
|
319
|
-
|
|
320
|
-
.features {
|
|
321
|
-
margin-top: 32px;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
.feature {
|
|
325
|
-
padding: 20px;
|
|
326
|
-
border: 1px solid var(--border);
|
|
327
|
-
border-radius: 12px;
|
|
328
|
-
background: var(--card);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
.feature h3 {
|
|
332
|
-
font-size: 14px;
|
|
333
|
-
font-weight: 600;
|
|
334
|
-
margin-bottom: 6px;
|
|
335
|
-
font-family: var(--font-mono);
|
|
336
|
-
color: var(--accent);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.feature p {
|
|
340
|
-
font-size: 13px;
|
|
341
|
-
line-height: 1.5;
|
|
342
|
-
color: var(--text-secondary);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
.feature code {
|
|
346
|
-
background: var(--bg);
|
|
347
|
-
border: 1px solid var(--border);
|
|
348
|
-
padding: 1px 5px;
|
|
349
|
-
border-radius: 4px;
|
|
350
|
-
font-size: 12px;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/* ─── Footer ────────────────────────────────────────── */
|
|
354
|
-
|
|
355
|
-
.footer {
|
|
356
|
-
text-align: center;
|
|
357
|
-
padding: 40px 24px 32px;
|
|
358
|
-
font-size: 13px;
|
|
359
|
-
color: var(--text-muted);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.footer a { color: var(--text-secondary); transition: color 0.2s; }
|
|
363
|
-
.footer a:hover { color: var(--accent); }
|
|
364
|
-
|
|
365
|
-
/* ─── Loading ───────────────────────────────────────── */
|
|
366
|
-
|
|
367
|
-
.loading-spinner {
|
|
368
|
-
width: 24px; height: 24px;
|
|
369
|
-
border: 2px solid var(--border);
|
|
370
|
-
border-top-color: var(--accent);
|
|
371
|
-
border-radius: 50%;
|
|
372
|
-
animation: spin 0.8s linear infinite;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
@keyframes spin {
|
|
376
|
-
to { transform: rotate(360deg); }
|
|
377
|
-
}
|
package/templates/default/app.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TheoKit App — manual bootstrap (optional).
|
|
3
|
-
*
|
|
4
|
-
* `theokit dev` auto-discovers server/routes/ — you don't need this file
|
|
5
|
-
* for basic usage. Use it when you need explicit controller/agent registration.
|
|
6
|
-
*
|
|
7
|
-
* Example with controllers + agents:
|
|
8
|
-
*
|
|
9
|
-
* import 'reflect-metadata'
|
|
10
|
-
* import { TheoApp } from '@theokit/http/app'
|
|
11
|
-
* import { MyController } from './server/controllers/my.controller.js'
|
|
12
|
-
*
|
|
13
|
-
* const app = await TheoApp.create({ controllers: [MyController] })
|
|
14
|
-
* await app.listen(3000)
|
|
15
|
-
*/
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { drizzle } from 'drizzle-orm/better-sqlite3'
|
|
2
|
-
import Database from 'better-sqlite3'
|
|
3
|
-
import { mkdirSync } from 'node:fs'
|
|
4
|
-
import * as schema from './schema.js'
|
|
5
|
-
|
|
6
|
-
mkdirSync('data', { recursive: true })
|
|
7
|
-
const sqlite = new Database('data/dev.db')
|
|
8
|
-
sqlite.pragma('journal_mode = WAL')
|
|
9
|
-
|
|
10
|
-
export const db = drizzle(sqlite, { schema })
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// Define your Drizzle tables here. Example:
|
|
2
|
-
//
|
|
3
|
-
// import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
|
|
4
|
-
//
|
|
5
|
-
// export const posts = sqliteTable('posts', {
|
|
6
|
-
// id: integer('id').primaryKey({ autoIncrement: true }),
|
|
7
|
-
// title: text('title').notNull(),
|
|
8
|
-
// published: integer('published', { mode: 'boolean' }).notNull().default(false),
|
|
9
|
-
// createdAt: text('created_at').notNull().$defaultFn(() => new Date().toISOString()),
|
|
10
|
-
// })
|
|
11
|
-
//
|
|
12
|
-
// Or scaffold a full resource (schema + routes + test):
|
|
13
|
-
// npx theokit generate resource posts title:string published:boolean
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
|
|
3
|
-
describe('Health route response shape', () => {
|
|
4
|
-
it('returns expected fields', () => {
|
|
5
|
-
// Mirrors the shape returned by server/routes/health.ts GET handler.
|
|
6
|
-
// Unit test — no server dependency.
|
|
7
|
-
const response = {
|
|
8
|
-
status: 'ok',
|
|
9
|
-
timestamp: Date.now(),
|
|
10
|
-
framework: 'TheoKit',
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
expect(response.status).toBe('ok')
|
|
14
|
-
expect(response.framework).toBe('TheoKit')
|
|
15
|
-
expect(typeof response.timestamp).toBe('number')
|
|
16
|
-
expect(response.timestamp).toBeGreaterThan(0)
|
|
17
|
-
})
|
|
18
|
-
})
|