create-theokit 1.0.14 → 1.0.16

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 (30) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cli.js +4 -13
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +10 -8
  5. package/templates/default/CLAUDE.md +2 -2
  6. package/templates/default/README.md.tmpl +38 -50
  7. package/templates/default/agents/chat.ts +22 -0
  8. package/templates/default/app/layout.tsx +71 -3
  9. package/templates/default/app/page.tsx +296 -61
  10. package/templates/default/dot-claude/skills/theokit-agents/SKILL.md +78 -44
  11. package/templates/default/dot-claude/skills/theokit-frontend/SKILL.md +24 -26
  12. package/templates/default/dot-claude/skills/theokit-ui/SKILL.md +32 -28
  13. package/templates/default/eslint.config.mjs +1 -5
  14. package/templates/default/package.json.tmpl +16 -9
  15. package/templates/default/theo.config.ts +1 -3
  16. package/templates/default/tsconfig.json +7 -14
  17. package/templates/default/types/jobs.d.ts +25 -0
  18. package/templates/services/agent-node/Dockerfile.tmpl +20 -0
  19. package/templates/services/agent-node/README.md +38 -0
  20. package/templates/services/agent-node/package.json.tmpl +18 -0
  21. package/templates/services/agent-node/src/index.ts +58 -0
  22. package/templates/services/agent-node/tsconfig.json +13 -0
  23. package/templates/default/AGENTS.md +0 -106
  24. package/templates/default/app/globals.css +0 -377
  25. package/templates/default/app.ts +0 -15
  26. package/templates/default/drizzle.config.ts +0 -10
  27. package/templates/default/server/db/index.ts +0 -10
  28. package/templates/default/server/db/schema.ts +0 -13
  29. package/templates/default/server/index.ts +0 -2
  30. package/templates/default/tests/tasks.test.ts +0 -18
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: theokit-agents
3
- description: TheoKit agent/LLM integration — two streaming surfaces (decorator vs manual), @Tool, @Toolbox, memory
3
+ description: TheoKit agent/LLM integration — agents/*.ts convention (defineAgent), @Agent decorator (advanced/DI), defineAgentTool, useAgent client hook
4
4
  user-invocable: false
5
5
  paths:
6
6
  - "**/*agent*"
@@ -13,37 +13,38 @@ paths:
13
13
 
14
14
  # TheoKit Agents & Tools
15
15
 
16
- ## Two Streaming Surfaces — pick one per endpoint
16
+ ## Server Surface — agents/*.ts (zero-config convention)
17
17
 
18
- TheoKit ships two ways to create agent endpoints. Use ONE per endpoint, not both.
19
-
20
- | Surface | Server | Events | Client | When to use |
21
- |---------|--------|--------|--------|-------------|
22
- | **Manual** (recommended for most apps) | `defineAgentEndpoint({ handler: async function* })` | `AgentEvent` (Message, ToolCall, Result, Error) | `useAgentStream()` / `consumeAgentStream()` | Full control over the LLM loop — you write the generator |
23
- | **Decorator** (`@theokit/agents`) | `@Agent` class → auto-generated route | `AgentStreamEvent` (TextDelta, ToolCall, Done...) | (same client hooks work) | Declarative — framework manages LLM loop via `@MainLoop` |
24
-
25
- ### Surface 1: Manual (defineAgentEndpoint)
18
+ Create an `agents/<name>.ts` file at the project root. It is automatically served at
19
+ `POST /api/agents/<name>` (dev + build) with no manual route wiring.
26
20
 
27
21
  ```typescript
28
- // server/routes/agents/assistant.ts
29
- import { defineAgentEndpoint } from 'theokit/server/define'
30
- import type { AgentEvent } from 'theokit'
31
-
32
- export const POST = defineAgentEndpoint({
33
- handler: async function* ({ body }): AsyncGenerator<AgentEvent> {
34
- // You control the LLM loop
35
- yield { type: 'message', content: 'Thinking...' }
36
- const result = await callLLM(body.message)
37
- yield { type: 'message', content: result }
38
- },
22
+ // agents/chat.ts
23
+ import { defineAgent } from '@theokit/agents'
24
+ import { z } from 'zod'
25
+
26
+ export default defineAgent({
27
+ input: z.object({ message: z.string() }),
28
+ model: 'openai/gpt-4o-mini',
29
+ system: 'You are a helpful assistant.',
39
30
  })
40
31
  ```
41
32
 
42
- ### Surface 2: Decorator (@Agent)
33
+ The endpoint streams the ai-sdk `UIMessageStream` that `useAgent` (client hook) consumes.
34
+ `@theokit/sdk` runs the agent; conversation turns auto-persist per session — the SDK owns storage.
35
+
36
+ **Provider resolution:** `OPENROUTER_API_KEY` (preferred — routes to many models) OR
37
+ `ANTHROPIC_API_KEY` / `OPENAI_API_KEY`. Set one in `.env`.
38
+
39
+ ## Advanced Surface — @Agent Decorator (DI / class-based)
40
+
41
+ When you need dependency injection or class-based composition, use the `@Agent` class
42
+ decorator from `@theokit/agents`. The class name determines the route:
43
+ `AssistantAgent` → `POST /api/agents/assistant`.
43
44
 
44
45
  ```typescript
45
46
  // server/agents/assistant.agent.ts
46
- import { Agent, MainLoop, Tool, Toolbox } from '@theokit/agents'
47
+ import { Agent, MainLoop } from '@theokit/agents'
47
48
 
48
49
  @Agent({
49
50
  model: 'openai/gpt-4o-mini',
@@ -57,9 +58,33 @@ export class AssistantAgent {
57
58
  }
58
59
  ```
59
60
 
60
- Convention: `AssistantAgent` → `POST /api/agents/assistant`
61
+ ## Tools — defineAgentTool
61
62
 
62
- ## @Tool Decorator
63
+ Declare typed tools with `defineAgentTool` (from `theokit/server`) and pass them to
64
+ `defineAgent`'s `tools` array.
65
+
66
+ ```typescript
67
+ // agents/chat.ts
68
+ import { defineAgent } from '@theokit/agents'
69
+ import { defineAgentTool } from 'theokit/server'
70
+ import { z } from 'zod'
71
+
72
+ const currentTimeTool = defineAgentTool({
73
+ name: 'current_time',
74
+ description: 'Return the current ISO timestamp',
75
+ input: z.object({}),
76
+ execute: async () => ({ time: new Date().toISOString() }),
77
+ })
78
+
79
+ export default defineAgent({
80
+ input: z.object({ message: z.string() }),
81
+ model: 'openai/gpt-4o-mini',
82
+ system: 'You are a helpful assistant.',
83
+ tools: [currentTimeTool],
84
+ })
85
+ ```
86
+
87
+ ### @Tool Decorator (advanced / class-based)
63
88
 
64
89
  ```typescript
65
90
  import { Toolbox, Tool } from '@theokit/agents'
@@ -78,23 +103,26 @@ export class TaskTools {
78
103
  }
79
104
  ```
80
105
 
81
- ## Client — useAgentStream (React hook)
82
-
83
- Works with BOTH surfaces. Transport: `fetch` POST + `ReadableStream` (SSE).
106
+ ## Client — useAgent (React hook)
84
107
 
85
108
  ```typescript
86
- import { useAgentStream } from 'theokit/client'
109
+ import { useAgent } from 'theokit/client'
87
110
 
88
111
  function ChatUI() {
89
- const { status, events, send, reset } = useAgentStream('/api/agents/assistant')
112
+ const { messages, status, send, reset } = useAgent<{ message: string }>('/api/agents/chat')
90
113
 
91
114
  return (
92
115
  <div>
93
116
  {status === 'streaming' && <p>Thinking...</p>}
94
- {events.map(e => (
95
- <div key={e.id}>
96
- {e.type === 'message' && <p>{e.content}</p>}
97
- {e.type === 'tool_call' && <p>Using tool: {e.name}</p>}
117
+ {messages.map(message => (
118
+ <div key={message.id}>
119
+ {message.parts.map((part, i) => (
120
+ part.type === 'text'
121
+ ? <p key={i}>{part.text}</p>
122
+ : part.type === 'dynamic-tool'
123
+ ? <p key={i}>Using tool: {part.toolName}</p>
124
+ : null
125
+ ))}
98
126
  </div>
99
127
  ))}
100
128
  <button onClick={() => send({ message: 'Hello' })}>Send</button>
@@ -103,15 +131,23 @@ function ChatUI() {
103
131
  }
104
132
  ```
105
133
 
106
- ### Non-React: consumeAgentStream
134
+ `messages` is `UIMessage[]` (ai-sdk). Render `message.parts` — text parts
135
+ (`part.type === 'text'`, `part.text`) and tool parts (`part.type === 'dynamic-tool'`,
136
+ `part.toolName`, `part.state`, `part.output`). Do NOT switch on an `events`/`event.type`
137
+ pattern — the wire is `UIMessageStream`, not SSE events.
138
+
139
+ ### Non-React: consumeUIMessageStream
107
140
 
108
141
  ```typescript
109
- import { consumeAgentStream } from 'theokit/client'
142
+ import { consumeUIMessageStream } from 'theokit/client'
110
143
 
111
- const stream = consumeAgentStream('/api/agents/assistant', { body: { message: 'Hi' } })
112
- for await (const event of stream) {
113
- console.log(event.type, event.content)
114
- }
144
+ const response = await fetch('/api/agents/chat', {
145
+ method: 'POST',
146
+ body: JSON.stringify({ message: 'Hello' }),
147
+ })
148
+ consumeUIMessageStream(response, (message) => {
149
+ console.log(message.parts)
150
+ })
115
151
  ```
116
152
 
117
153
  ## SDK Ecosystem — "you are here" map
@@ -134,14 +170,12 @@ Before writing custom tools, check if they already exist:
134
170
  - `@UseGuards()` works on agents (shared with HTTP pipeline)
135
171
  - `@UseInterceptors()` and `@UseFilters()` on agents are metadata-only (emit warnings)
136
172
  - Agent runtime is `@theokit/sdk` — NEVER call LLM APIs directly via fetch
137
- - Pick ONE surface per endpoint — don't mix defineAgentEndpoint with @Agent for the same route
138
173
  - Check `@theokit/sdk-tools` BEFORE writing custom tools — it may already exist
139
174
 
140
175
  ## Anti-patterns
141
176
 
142
- - NEVER call OpenAI/Anthropic/OpenRouter APIs directly — use @Agent or defineAgentEndpoint
177
+ - NEVER call OpenAI/Anthropic/OpenRouter APIs directly — use `defineAgent` or `@Agent`
143
178
  - NEVER reimplement tool calling loop — the SDK handles it
144
179
  - NEVER reimplement file/search/shell tools — use `@theokit/sdk-tools` (readFile, writeFile, search, etc.)
145
- - NEVER store conversations manually — use @Memory (decorator) or SDK persistence
180
+ - NEVER store conversations manually — SDK persistence is automatic (the SDK owns storage)
146
181
  - NEVER infer tool capability from method name — always provide explicit `name` + `description`
147
- - NEVER mix both surfaces for the same endpoint — pick manual OR decorator
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: theokit-frontend
3
- description: TheoKit frontend — file-based routing, layouts, theoFetch typed client, useAgentStream, React patterns
3
+ description: TheoKit frontend — file-based routing, layouts, theoFetch typed client, useAgent, React patterns
4
4
  user-invocable: false
5
5
  paths:
6
6
  - "app/**"
@@ -50,23 +50,24 @@ const created = await client.tasks.POST({ body: { title: 'New' } })
50
50
 
51
51
  ## Agent Streaming
52
52
 
53
- Three client APIs, all from `theokit/client`:
53
+ Two client APIs from `theokit/client`:
54
54
 
55
- ### useAgentStream (React hook — most common)
55
+ ### useAgent (React hook — most common)
56
56
 
57
57
  ```typescript
58
- import { useAgentStream } from 'theokit/client'
58
+ import { useAgent } from 'theokit/client'
59
59
 
60
60
  function ChatUI() {
61
- const { status, events, send, reset } = useAgentStream('/api/agents/assistant')
61
+ const { messages, status, send, reset } = useAgent<{ message: string }>('/api/agents/chat')
62
62
 
63
63
  return (
64
64
  <div>
65
65
  {status === 'streaming' && <p>Thinking...</p>}
66
- {events.map(event => (
67
- <div key={event.id}>
68
- {event.type === 'message' && <p>{event.content}</p>}
69
- {event.type === 'tool_call' && <p>Using tool: {event.name}</p>}
66
+ {messages.map(message => (
67
+ <div key={message.id}>
68
+ {message.parts.map((part, i) =>
69
+ part.type === 'text' ? <p key={i}>{part.text}</p> : null
70
+ )}
70
71
  </div>
71
72
  ))}
72
73
  <button onClick={() => send({ message: 'Hello' })}>Send</button>
@@ -75,26 +76,23 @@ function ChatUI() {
75
76
  }
76
77
  ```
77
78
 
78
- ### consumeAgentStream (non-React, async iterable)
79
+ `messages` is `UIMessage[]` (ai-sdk). Render `message.parts`: text parts
80
+ (`part.type === 'text'`, `part.text`) and tool parts (`part.type === 'dynamic-tool'`,
81
+ `part.toolName`, `part.state`, `part.output`). Do NOT switch on an `events`/`event.type`
82
+ pattern — the wire is `UIMessageStream`, not SSE events.
79
83
 
80
- ```typescript
81
- import { consumeAgentStream } from 'theokit/client'
82
-
83
- const stream = consumeAgentStream('/api/agents/assistant', {
84
- body: { message: 'Hello' },
85
- })
86
- for await (const event of stream) {
87
- console.log(event.type, event.content)
88
- }
89
- ```
90
-
91
- ### parseSSEChunk (low-level SSE parser)
84
+ ### consumeUIMessageStream (non-React)
92
85
 
93
86
  ```typescript
94
- import { parseSSEChunk } from 'theokit/client'
87
+ import { consumeUIMessageStream } from 'theokit/client'
95
88
 
96
- // Parse a single SSE line into an AgentEvent (or null)
97
- const event = parseSSEChunk('data: {"type":"message","content":"Hello"}')
89
+ const response = await fetch('/api/agents/chat', {
90
+ method: 'POST',
91
+ body: JSON.stringify({ message: 'Hello' }),
92
+ })
93
+ consumeUIMessageStream(response, (message) => {
94
+ console.log(message.parts)
95
+ })
98
96
  ```
99
97
 
100
98
  ## Path Aliases
@@ -111,4 +109,4 @@ Configured in `tsconfig.json` — works in both server and app code.
111
109
  - NEVER use raw `fetch('/api/...')` — use `theoFetch` for type safety
112
110
  - NEVER create pages outside `app/` — they won't be discovered by the router
113
111
  - NEVER import server code directly in `app/` — use theoFetch or server actions
114
- - NEVER use `useEffect` + `fetch` for data loading — use theoFetch or useAgentStream
112
+ - NEVER use `useEffect` + `fetch` for data loading — use theoFetch or `useAgent`
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: theokit-ui
3
- description: "@theokit/ui component library — chat UI (ChatThread, ChatMessage, ChatComposer, CodeBlock), theming, providers, sidebar"
3
+ description: "@theokit/ui AI-native component library — AI-agent surfaces (ChatThread, ChatMessage, ChatComposer, ToolCallCard, AgentStream), theming, providers; generic primitives (CodeBlock, Sidebar, Button) come from @usetheo/ui"
4
4
  user-invocable: false
5
5
  paths:
6
6
  - "app/**"
@@ -12,13 +12,13 @@ paths:
12
12
  - "**/*Theme*"
13
13
  ---
14
14
 
15
- # @theokit/ui — AI Chat Component Library
15
+ # @theokit/ui — AI-native Component Library (AI-agent surfaces)
16
16
 
17
- `@theokit/ui` is an optional peer dependency. If installed, it provides ready-made components for chat UIs, theming, and layout. **Never build custom equivalents** of components `@theokit/ui` provides.
17
+ `@theokit/ui` is an optional peer dependency. If installed, it provides ready-made AI components for chat + coding-agent surfaces (chat thread, agent events, tool calls, diff viewer, build logs), plus theming. **Never build custom equivalents** of components it provides. Generic primitives (Button, Input, Card, CodeBlock, PageShell, Sidebar, Avatar, Alert, etc.) live in `@usetheo/ui`, which `@theokit/ui` depends on — import those from `@usetheo/ui`.
18
18
 
19
19
  ## Package Identity
20
20
 
21
- The published package is `@theokit/ui` (NOT `@usetheo/ui` — that was the old name).
21
+ `@theokit/ui` (AI-native, currently `1.0.0`) provides the AI-agent-surface components. Its generic foundation was split into `@usetheo/ui` in the 2026-07-03 AI-exclusive pivot; `@theokit/ui` depends on `@usetheo/ui`, so installing `@theokit/ui` pulls the foundation transitively.
22
22
 
23
23
  ```bash
24
24
  # Install from npm (preferred)
@@ -54,19 +54,19 @@ export default function Layout({ children }) {
54
54
  ### Full Chat Page (typical assembly)
55
55
 
56
56
  ```typescript
57
+ // AI-agent-surface components live in @theokit/ui
57
58
  import {
58
- PageShell,
59
- Sidebar,
60
- SessionListItem,
61
59
  ChatThread,
62
60
  ChatMessage,
63
61
  ChatMessageContent,
64
62
  ChatComposer,
65
63
  } from '@theokit/ui'
66
- import { useAgentStream } from 'theokit/client'
64
+ // Generic layout primitives moved to @usetheo/ui (2026-07-03 pivot)
65
+ import { PageShell, Sidebar, SessionListItem } from '@usetheo/ui'
66
+ import { useAgent } from 'theokit/client'
67
67
 
68
68
  function ChatPage() {
69
- const { status, events, send } = useAgentStream('/api/agents/assistant')
69
+ const { messages, status, send } = useAgent('/api/agents/assistant')
70
70
 
71
71
  return (
72
72
  <PageShell sidebar={
@@ -77,9 +77,13 @@ function ChatPage() {
77
77
  </Sidebar>
78
78
  }>
79
79
  <ChatThread>
80
- {messages.map(m => (
81
- <ChatMessage key={m.id} role={m.role}>
82
- <ChatMessageContent markdown={m.content} />
80
+ {messages.map(message => (
81
+ <ChatMessage key={message.id} role={message.role}>
82
+ {message.parts.map((part, i) =>
83
+ part.type === 'text'
84
+ ? <ChatMessageContent key={i} markdown={part.text} />
85
+ : null
86
+ )}
83
87
  </ChatMessage>
84
88
  ))}
85
89
  </ChatThread>
@@ -93,29 +97,29 @@ function ChatPage() {
93
97
  }
94
98
  ```
95
99
 
96
- ### Individual Components
100
+ ### AI-agent-surface components (from `@theokit/ui`)
97
101
 
98
102
  | Component | Purpose | Key Props |
99
103
  |-----------|---------|-----------|
100
104
  | `ChatThread` | Scrollable message container | `children` (ChatMessage elements) |
101
105
  | `ChatMessage` | Single message bubble | `role: 'user' \| 'assistant'`, `children` |
102
106
  | `ChatMessageContent` | Markdown + code rendering | `markdown: string` (handles streaming partial) |
103
- | `CodeBlock` | Syntax-highlighted code | `code: string`, `language?: string` (uses shiki, lazy-loaded) |
104
107
  | `ChatComposer` | Message input + submit | `onSubmit: (text) => void`, `disabled?: boolean` |
105
- | `PageShell` | App layout with sidebar slot | `sidebar?: ReactNode`, `children` |
106
- | `Sidebar` | Collapsible side panel | `children` |
107
- | `SessionListItem` | Session entry in sidebar | `title: string`, `onClick`, `active?: boolean` |
108
+ | `ToolCallCard` | Display agent tool invocations | — |
109
+ | `AgentStream` | Lower-level stream renderer | — |
108
110
 
109
- ### Other Useful Components
111
+ ### Generic primitives (from `@usetheo/ui`)
110
112
 
111
- | Component | Purpose |
112
- |-----------|---------|
113
- | `Button`, `Input`, `Textarea` | Form primitives (themed) |
114
- | `ToolCallCard` | Display agent tool invocations |
115
- | `AgentStream` | Lower-level stream renderer |
116
- | `ThemeSwitcher` | Light/dark mode toggle |
117
- | `Avatar` | User/agent avatar |
118
- | `Alert` | Status messages |
113
+ Moved out of `@theokit/ui` in the 2026-07-03 AI-exclusive pivot. Import these from `@usetheo/ui`.
114
+
115
+ | Component | Purpose | Key Props |
116
+ |-----------|---------|-----------|
117
+ | `CodeBlock` | Syntax-highlighted code | `code: string`, `language?: string` (uses shiki, lazy-loaded) |
118
+ | `PageShell` | App layout with sidebar slot | `sidebar?: ReactNode`, `children` |
119
+ | `Sidebar` | Collapsible side panel | `children` |
120
+ | `Button`, `Input`, `Textarea` | Form primitives (themed) | — |
121
+ | `Avatar` | User/agent avatar | — |
122
+ | `Alert` | Status messages | — |
119
123
 
120
124
  ## Peer Dependencies (install only what you use)
121
125
 
@@ -154,8 +158,8 @@ const myTheme = defineTheme({
154
158
 
155
159
  - NEVER build a custom chat message component — use `ChatMessage` + `ChatMessageContent`
156
160
  - NEVER build a custom markdown renderer — `ChatMessageContent` handles it (including streaming partial fences)
157
- - NEVER build a custom code highlighter — `CodeBlock` uses shiki (lazy-loaded)
158
- - NEVER import from `@usetheo/ui` — that's the deprecated package name; use `@theokit/ui`
161
+ - NEVER build a custom code highlighter — `CodeBlock` (from `@usetheo/ui`) uses shiki (lazy-loaded)
162
+ - Import AI-agent-surface components (ChatThread, ChatMessage, ToolCallCard, etc.) from `@theokit/ui`; import generic primitives (Button, Input, CodeBlock, PageShell, Sidebar, Avatar, Alert) from `@usetheo/ui` — both are live packages since the 2026-07-03 pivot (`@theokit/ui` depends on `@usetheo/ui`)
159
163
  - NEVER use `npm link` or `file:../theo-ui` to install — causes dual-React (use tarball or npm registry)
160
164
  - NEVER install ALL peer deps — only install the peers for components you actually use
161
165
  - NEVER use components without wrapping in `TheoUIProvider` + `ThemeProvider` first
@@ -1,18 +1,14 @@
1
1
  import tseslint from 'typescript-eslint'
2
2
  import prettierConfig from 'eslint-config-prettier'
3
- import drizzle from 'eslint-plugin-drizzle'
4
3
 
5
4
  export default tseslint.config(
6
- { ignores: ['dist/', 'node_modules/', 'drizzle/'] },
5
+ { ignores: ['dist/', 'node_modules/'] },
7
6
  ...tseslint.configs.recommended,
8
7
  prettierConfig,
9
8
  {
10
- plugins: { drizzle },
11
9
  rules: {
12
10
  '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
13
11
  '@typescript-eslint/no-explicit-any': 'warn',
14
- 'drizzle/enforce-delete-with-where': 'error',
15
- 'drizzle/enforce-update-with-where': 'error',
16
12
  },
17
13
  },
18
14
  )
@@ -8,8 +8,6 @@
8
8
  "build": "theokit build",
9
9
  "start": "theokit start",
10
10
  "test": "vitest run",
11
- "db:migrate": "theokit db migrate",
12
- "db:generate": "theokit db generate",
13
11
  "lint": "eslint .",
14
12
  "lint:fix": "eslint . --fix",
15
13
  "format": "prettier --write .",
@@ -17,25 +15,34 @@
17
15
  "typecheck": "tsc --noEmit"
18
16
  },
19
17
  "dependencies": {
20
- "theokit": "^0.5.4",
18
+ "theokit": "^0.15.1",
19
+ "@theokit/agents": "^0.30.1",
20
+ "@theokit/sdk": "^2.13.0",
21
+ "@theokit/ui": "^1.0.0",
22
+ "@usetheo/ui": "^0.14.0",
23
+ "lucide-react": "^0.469.0",
21
24
  "react": "^19.0.0",
22
25
  "react-dom": "^19.0.0",
23
26
  "react-router": "^7.0.0",
24
- "zod": "^4.0.0",
25
- "drizzle-orm": "^0.44.0",
26
- "better-sqlite3": "^12.0.0"
27
+ "zod": "^4.0.0"
27
28
  },
28
29
  "devDependencies": {
29
- "@types/better-sqlite3": "^7.6.0",
30
30
  "@types/react": "^19.0.0",
31
31
  "@types/react-dom": "^19.0.0",
32
- "drizzle-kit": "^0.30.0",
32
+ "tailwindcss": "^4.0.0",
33
+ "@tailwindcss/vite": "^4.0.0",
33
34
  "eslint": "^9.0.0",
34
35
  "eslint-config-prettier": "^10.0.0",
35
- "eslint-plugin-drizzle": "^0.2.0",
36
36
  "prettier": "^3.0.0",
37
37
  "typescript": "^5.5.0",
38
38
  "typescript-eslint": "^8.0.0",
39
39
  "vitest": "^3.0.0"
40
+ },
41
+ "pnpm": {
42
+ "onlyBuiltDependencies": [
43
+ "esbuild",
44
+ "better-sqlite3",
45
+ "workerd"
46
+ ]
40
47
  }
41
48
  }
@@ -1,5 +1,3 @@
1
1
  import { defineConfig } from 'theokit'
2
2
 
3
- export default defineConfig({
4
- port: 3000,
5
- })
3
+ export default defineConfig({})
@@ -1,22 +1,15 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "experimentalDecorators": true,
7
- "emitDecoratorMetadata": true,
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
8
6
  "strict": true,
9
- "jsx": "react-jsx",
7
+ "noEmit": true,
10
8
  "esModuleInterop": true,
11
9
  "skipLibCheck": true,
12
- "outDir": "dist",
13
- "baseUrl": ".",
14
- "paths": {
15
- "@/*": ["./*"],
16
- "@/server/*": ["./server/*"],
17
- "@/app/*": ["./app/*"]
18
- }
10
+ "jsx": "react-jsx",
11
+ "isolatedModules": true,
12
+ "resolveJsonModule": true
19
13
  },
20
- "include": ["**/*.ts", "**/*.tsx"],
21
- "exclude": ["node_modules", "dist"]
14
+ "include": ["app/**/*.ts", "app/**/*.tsx", "server/**/*.ts", "agents/**/*.ts"]
22
15
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * JobRegistry augmentation — REQUIRED for typed `ctx.queue.enqueue` calls.
3
+ *
4
+ * Without this augmentation, `ctx.queue.enqueue('foo', ...)` errors with:
5
+ * "Type 'foo' is not assignable to type 'never'"
6
+ *
7
+ * This is the canonical TheoKit jobs onboarding bug (EC-110). To add a
8
+ * job:
9
+ *
10
+ * 1. Create `server/jobs/<name>.ts` exporting `defineJob('<name>', ...)`
11
+ * 2. Add `'<name>': { ...inputShape }` below
12
+ * 3. Use `ctx.queue.enqueue('<name>', { ...input })` from any route handler
13
+ *
14
+ * See: docs/concepts/jobs.md
15
+ */
16
+ declare module 'theokit/server' {
17
+ interface JobRegistry {
18
+ // Add your jobs here. Examples (uncomment and customize):
19
+ //
20
+ // 'process-document': { documentId: string }
21
+ // 'send-email': { to: string; subject: string; body: string }
22
+ }
23
+ }
24
+
25
+ export {}
@@ -0,0 +1,20 @@
1
+ FROM node:22-alpine
2
+
3
+ WORKDIR /app
4
+
5
+ # Install pnpm (or use npm if pnpm not available in image)
6
+ RUN corepack enable && corepack prepare pnpm@latest --activate || true
7
+
8
+ # Copy manifest and install
9
+ COPY package.json ./
10
+ RUN pnpm install --frozen-lockfile || npm install
11
+
12
+ # Copy source
13
+ COPY . .
14
+
15
+ EXPOSE 8002
16
+
17
+ HEALTHCHECK --interval=10s --timeout=5s --retries=3 \
18
+ CMD wget --spider -q http://localhost:8002/health || exit 1
19
+
20
+ CMD ["pnpm", "start"]
@@ -0,0 +1,38 @@
1
+ # Agent service (Node / Hono)
2
+
3
+ This is a TheoKit polyglot sidecar generated by `create-theokit --backend node`.
4
+
5
+ ## Requirements
6
+
7
+ - **Node.js 22+** (matches TheoKit's floor)
8
+ - **pnpm** or **npm** (TheoKit's CLI prefers pnpm)
9
+
10
+ ## Run it
11
+
12
+ The TheoKit app boots this service automatically via `pnpm dev` (per `theo.config.ts > services`).
13
+
14
+ To run standalone:
15
+
16
+ ```bash
17
+ cd services/agent-node
18
+ pnpm install
19
+ pnpm dev
20
+ ```
21
+
22
+ ## Endpoints
23
+
24
+ - `GET /health` — healthcheck (returns `{"status":"ok"}`)
25
+ - `POST /echo` — example: `{ "message": "..." }` → `{ "echo": "..." }`
26
+
27
+ ## What is wired
28
+
29
+ - Native fetch handler via Hono (works on TheoCloud and local)
30
+ - JSON-line stdout logs
31
+ - W3C `traceparent` propagation from incoming headers
32
+ - TheoKit injects `THEOKIT_SERVICE_NAME` + `THEOKIT_SERVICE_PORT` env vars
33
+
34
+ ## Add a tool the TheoKit agent can call
35
+
36
+ Add another `app.post('/tool/<name>', ...)` handler. From the TheoKit TS side,
37
+ `services.worker.<name>({...})` is auto-typed when an OpenAPI URL is reachable
38
+ (Hey API integration — Phase 5).
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "{{name}}-agent-node",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "tsx watch src/index.ts",
7
+ "start": "tsx src/index.ts",
8
+ "build": "echo 'no build step; tsx runs TS directly'"
9
+ },
10
+ "dependencies": {
11
+ "hono": "^4.6.0",
12
+ "@hono/node-server": "^1.13.0"
13
+ },
14
+ "devDependencies": {
15
+ "tsx": "^4.19.0",
16
+ "typescript": "^5.7.0"
17
+ }
18
+ }