create-theokit 1.0.10 → 1.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-theokit",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "type": "module",
5
5
  "description": "Scaffold a new TheoKit project",
6
6
  "license": "Apache-2.0",
@@ -12,6 +12,7 @@ This project includes TheoKit-aware skills that activate automatically when you
12
12
  | theokit-agents | `**/*agent*`, `**/*tool*`, `**/*Agent*`, `**/*Tool*` | @Agent, @Tool, @Toolbox decorators, LLM integration |
13
13
  | theokit-database | `**/*schema*`, `**/*db*`, `**/drizzle*`, `**/*migration*`, `**/*seed*` | Drizzle ORM, SQLite, schema patterns, migrations |
14
14
  | theokit-frontend | `app/**` | File-based routing, layouts, theoFetch, useAgentStream |
15
+ | theokit-ui | `app/**`, `**/*Chat*`, `**/*Sidebar*`, `**/*Theme*` | @theokit/ui components: ChatThread, ChatMessage, CodeBlock, Sidebar, theming |
15
16
  | theokit-config | `theo.config*`, `**/*config*` | defineConfig options, plugins, security, storage |
16
17
 
17
18
  ### Settings
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: theokit-agents
3
- description: TheoKit agent/LLM integration — @Agent, @Tool, @Toolbox decorators, streaming, memory
3
+ description: TheoKit agent/LLM integration — two streaming surfaces (decorator vs manual), @Tool, @Toolbox, memory
4
4
  user-invocable: false
5
5
  paths:
6
6
  - "**/*agent*"
@@ -13,31 +13,51 @@ paths:
13
13
 
14
14
  # TheoKit Agents & Tools
15
15
 
16
- ## @Agent Decorator
16
+ ## Two Streaming Surfaces — pick one per endpoint
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)
17
26
 
18
27
  ```typescript
19
- import { Agent, MainLoop, Hook, Memory, Budget } from '@theokit/agents'
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
+ },
39
+ })
40
+ ```
41
+
42
+ ### Surface 2: Decorator (@Agent)
43
+
44
+ ```typescript
45
+ // server/agents/assistant.agent.ts
46
+ import { Agent, MainLoop, Tool, Toolbox } from '@theokit/agents'
20
47
 
21
48
  @Agent({
22
- model: 'openai/gpt-4o-mini', // Required: LLM model
23
- systemPrompt: 'You are a helpful task assistant.',
49
+ model: 'openai/gpt-4o-mini',
50
+ systemPrompt: 'You are a helpful assistant.',
24
51
  })
25
- @Memory({ provider: 'built-in', scope: 'per-user' }) // Optional
26
- @Budget({ maxCostUsd: 1.00, window: 'daily' }) // Optional
27
52
  export class AssistantAgent {
28
53
  @MainLoop({ strategy: 'react', maxIterations: 5 })
29
54
  async run() {
30
- // Framework handles the LLM loop
31
- }
32
-
33
- @Hook('before:llm-call')
34
- async onBeforeLLM(ctx) {
35
- // Intercept before each LLM call
55
+ // Framework handles the LLM loop via @theokit/sdk
36
56
  }
37
57
  }
38
58
  ```
39
59
 
40
- Convention: `AssistantAgent` class name maps to `GET/POST /api/agents/assistant`.
60
+ Convention: `AssistantAgent` → `POST /api/agents/assistant`
41
61
 
42
62
  ## @Tool Decorator
43
63
 
@@ -50,56 +70,63 @@ export class TaskTools {
50
70
  @Tool({
51
71
  name: 'list_tasks',
52
72
  description: 'List all tasks, optionally filtered by status',
53
- input: z.object({
54
- done: z.boolean().optional(),
55
- }),
73
+ input: z.object({ done: z.boolean().optional() }),
56
74
  })
57
75
  async listTasks({ done }: { done?: boolean }) {
58
- const all = db.select().from(tasks).all()
59
- return done !== undefined ? all.filter(t => t.done === done) : all
60
- }
61
-
62
- @Tool({
63
- name: 'create_task',
64
- description: 'Create a new task with a title',
65
- input: z.object({
66
- title: z.string().min(1),
67
- }),
68
- })
69
- async createTask({ title }: { title: string }) {
70
- return db.insert(tasks).values({ title }).returning().get()
76
+ return db.select().from(tasks).all()
71
77
  }
72
78
  }
73
79
  ```
74
80
 
75
- ## Frontend — useAgentStream
81
+ ## Client — useAgentStream (React hook)
82
+
83
+ Works with BOTH surfaces. Transport: `fetch` POST + `ReadableStream` (SSE).
76
84
 
77
85
  ```typescript
78
86
  import { useAgentStream } from 'theokit/client'
79
87
 
80
88
  function ChatUI() {
81
- const { status, events, send } = useAgentStream('/api/agents/assistant')
89
+ const { status, events, send, reset } = useAgentStream('/api/agents/assistant')
82
90
 
83
91
  return (
84
92
  <div>
85
- {events.map(e => <p key={e.id}>{e.content}</p>)}
93
+ {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>}
98
+ </div>
99
+ ))}
86
100
  <button onClick={() => send({ message: 'Hello' })}>Send</button>
87
101
  </div>
88
102
  )
89
103
  }
90
104
  ```
91
105
 
106
+ ### Non-React: consumeAgentStream
107
+
108
+ ```typescript
109
+ import { consumeAgentStream } from 'theokit/client'
110
+
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
+ }
115
+ ```
116
+
92
117
  ## Rules
93
118
 
94
119
  - Tool `name` and `description` are ALWAYS explicit — never inferred from method names (G4)
95
120
  - Tool `input` uses Zod schema — same pattern as defineRoute
96
121
  - `@UseGuards()` works on agents (shared with HTTP pipeline)
97
- - `@UseInterceptors()` and `@UseFilters()` on agents are metadata-only (emit warnings, not enforced at runtime)
122
+ - `@UseInterceptors()` and `@UseFilters()` on agents are metadata-only (emit warnings)
98
123
  - Agent runtime is `@theokit/sdk` — NEVER call LLM APIs directly via fetch
124
+ - Pick ONE surface per endpoint — don't mix defineAgentEndpoint with @Agent for the same route
99
125
 
100
126
  ## Anti-patterns
101
127
 
102
- - NEVER call OpenAI/Anthropic/OpenRouter APIs directly — use @Agent + @Tool
128
+ - NEVER call OpenAI/Anthropic/OpenRouter APIs directly — use @Agent or defineAgentEndpoint
103
129
  - NEVER reimplement tool calling loop — the SDK handles it
104
- - NEVER store conversations manually — use @Memory
130
+ - NEVER store conversations manually — use @Memory (decorator) or SDK persistence
105
131
  - NEVER infer tool capability from method name — always provide explicit `name` + `description`
132
+ - NEVER mix both surfaces for the same endpoint — pick manual OR decorator
@@ -43,18 +43,21 @@ import { createAppClient } from 'theokit/client'
43
43
 
44
44
  const client = createAppClient()
45
45
 
46
- // Proxy pattern — method names match route structure
47
46
  const tasks = await client.tasks.GET()
48
47
  const task = await client.tasks[':id'].GET({ params: { id: 1 } })
49
48
  const created = await client.tasks.POST({ body: { title: 'New' } })
50
49
  ```
51
50
 
52
- ## Agent Streaming (useAgentStream)
51
+ ## Agent Streaming
52
+
53
+ Three client APIs, all from `theokit/client`:
54
+
55
+ ### useAgentStream (React hook — most common)
53
56
 
54
57
  ```typescript
55
58
  import { useAgentStream } from 'theokit/client'
56
59
 
57
- function ChatComponent() {
60
+ function ChatUI() {
58
61
  const { status, events, send, reset } = useAgentStream('/api/agents/assistant')
59
62
 
60
63
  return (
@@ -66,12 +69,34 @@ function ChatComponent() {
66
69
  {event.type === 'tool_call' && <p>Using tool: {event.name}</p>}
67
70
  </div>
68
71
  ))}
69
- <input onSubmit={e => send({ message: e.target.value })} />
72
+ <button onClick={() => send({ message: 'Hello' })}>Send</button>
70
73
  </div>
71
74
  )
72
75
  }
73
76
  ```
74
77
 
78
+ ### consumeAgentStream (non-React, async iterable)
79
+
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)
92
+
93
+ ```typescript
94
+ import { parseSSEChunk } from 'theokit/client'
95
+
96
+ // Parse a single SSE line into an AgentEvent (or null)
97
+ const event = parseSSEChunk('data: {"type":"message","content":"Hello"}')
98
+ ```
99
+
75
100
  ## Path Aliases
76
101
 
77
102
  ```typescript
@@ -83,7 +108,7 @@ Configured in `tsconfig.json` — works in both server and app code.
83
108
 
84
109
  ## Anti-patterns
85
110
 
86
- - NEVER use `fetch('/api/...')` directly — use `theoFetch` for type safety
111
+ - NEVER use raw `fetch('/api/...')` — use `theoFetch` for type safety
87
112
  - NEVER create pages outside `app/` — they won't be discovered by the router
88
113
  - NEVER import server code directly in `app/` — use theoFetch or server actions
89
114
  - NEVER use `useEffect` + `fetch` for data loading — use theoFetch or useAgentStream
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: theokit-ui
3
+ description: "@theokit/ui component library — chat UI (ChatThread, ChatMessage, ChatComposer, CodeBlock), theming, providers, sidebar"
4
+ user-invocable: false
5
+ paths:
6
+ - "app/**"
7
+ - "**/*Chat*"
8
+ - "**/*chat*"
9
+ - "**/*Sidebar*"
10
+ - "**/*sidebar*"
11
+ - "**/*theme*"
12
+ - "**/*Theme*"
13
+ ---
14
+
15
+ # @theokit/ui — AI Chat Component Library
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.
18
+
19
+ ## Package Identity
20
+
21
+ The published package is `@theokit/ui` (NOT `@usetheo/ui` — that was the old name).
22
+
23
+ ```bash
24
+ # Install from npm
25
+ npm install @theokit/ui
26
+
27
+ # Or link from source (development)
28
+ npm link ../theo-ui # if you have the source repo
29
+ ```
30
+
31
+ ## Provider Setup (required before using any component)
32
+
33
+ ```typescript
34
+ // app/layout.tsx
35
+ import '@theokit/ui/styles.css'
36
+ import { TheoUIProvider, ThemeProvider } from '@theokit/ui'
37
+
38
+ export default function Layout({ children }) {
39
+ return (
40
+ <TheoUIProvider>
41
+ <ThemeProvider>
42
+ {children}
43
+ </ThemeProvider>
44
+ </TheoUIProvider>
45
+ )
46
+ }
47
+ ```
48
+
49
+ ## Chat Components
50
+
51
+ ### Full Chat Page (typical assembly)
52
+
53
+ ```typescript
54
+ import {
55
+ PageShell,
56
+ Sidebar,
57
+ SessionListItem,
58
+ ChatThread,
59
+ ChatMessage,
60
+ ChatMessageContent,
61
+ ChatComposer,
62
+ } from '@theokit/ui'
63
+ import { useAgentStream } from 'theokit/client'
64
+
65
+ function ChatPage() {
66
+ const { status, events, send } = useAgentStream('/api/agents/assistant')
67
+
68
+ return (
69
+ <PageShell sidebar={
70
+ <Sidebar>
71
+ {sessions.map(s => (
72
+ <SessionListItem key={s.id} title={s.title} onClick={() => select(s)} />
73
+ ))}
74
+ </Sidebar>
75
+ }>
76
+ <ChatThread>
77
+ {messages.map(m => (
78
+ <ChatMessage key={m.id} role={m.role}>
79
+ <ChatMessageContent markdown={m.content} />
80
+ </ChatMessage>
81
+ ))}
82
+ </ChatThread>
83
+
84
+ <ChatComposer
85
+ disabled={status === 'streaming'}
86
+ onSubmit={text => send({ message: text })}
87
+ />
88
+ </PageShell>
89
+ )
90
+ }
91
+ ```
92
+
93
+ ### Individual Components
94
+
95
+ | Component | Purpose | Key Props |
96
+ |-----------|---------|-----------|
97
+ | `ChatThread` | Scrollable message container | `children` (ChatMessage elements) |
98
+ | `ChatMessage` | Single message bubble | `role: 'user' \| 'assistant'`, `children` |
99
+ | `ChatMessageContent` | Markdown + code rendering | `markdown: string` (handles streaming partial) |
100
+ | `CodeBlock` | Syntax-highlighted code | `code: string`, `language?: string` (uses shiki, lazy-loaded) |
101
+ | `ChatComposer` | Message input + submit | `onSubmit: (text) => void`, `disabled?: boolean` |
102
+ | `PageShell` | App layout with sidebar slot | `sidebar?: ReactNode`, `children` |
103
+ | `Sidebar` | Collapsible side panel | `children` |
104
+ | `SessionListItem` | Session entry in sidebar | `title: string`, `onClick`, `active?: boolean` |
105
+
106
+ ### Other Useful Components
107
+
108
+ | Component | Purpose |
109
+ |-----------|---------|
110
+ | `Button`, `Input`, `Textarea` | Form primitives (themed) |
111
+ | `ToolCallCard` | Display agent tool invocations |
112
+ | `AgentStream` | Lower-level stream renderer |
113
+ | `ThemeSwitcher` | Light/dark mode toggle |
114
+ | `Avatar` | User/agent avatar |
115
+ | `Alert` | Status messages |
116
+
117
+ ## Peer Dependencies (install only what you use)
118
+
119
+ **Chat/markdown path** (most apps need these):
120
+ ```bash
121
+ npm install mdast-util-from-markdown mdast-util-to-hast mdast-util-gfm \
122
+ hast-util-to-jsx-runtime hast-util-sanitize hast-util-from-html \
123
+ micromark-extension-gfm unist-util-visit unist-util-visit-parents shiki
124
+ ```
125
+
126
+ **DO NOT install** unless you use the specific components:
127
+ - `mermaid` — only for diagram rendering components
128
+ - `katex` — only for math/LaTeX rendering
129
+ - `roughjs` / `perfect-freehand` — only for whiteboard/drawing components
130
+
131
+ ## Theming
132
+
133
+ ```typescript
134
+ import { defineTheme, ThemeProvider } from '@theokit/ui'
135
+
136
+ // Built-in themes
137
+ import { dracula, oneDark, githubDark, anthropicStyle } from '@theokit/ui'
138
+
139
+ // Custom theme
140
+ const myTheme = defineTheme({
141
+ name: 'my-theme',
142
+ colors: { primary: '#3b82f6', background: '#0a0a0a' },
143
+ })
144
+
145
+ <ThemeProvider theme={myTheme}>
146
+ {children}
147
+ </ThemeProvider>
148
+ ```
149
+
150
+ ## Anti-patterns
151
+
152
+ - NEVER build a custom chat message component — use `ChatMessage` + `ChatMessageContent`
153
+ - NEVER build a custom markdown renderer — `ChatMessageContent` handles it (including streaming partial fences)
154
+ - NEVER build a custom code highlighter — `CodeBlock` uses shiki (lazy-loaded)
155
+ - NEVER import from `@usetheo/ui` — that's the deprecated package name; use `@theokit/ui`
156
+ - NEVER install ALL peer deps — only install the peers for components you actually use
157
+ - NEVER use components without wrapping in `TheoUIProvider` + `ThemeProvider` first