howone 0.1.8 → 0.1.10

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": "howone",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "private": false,
5
5
  "description": "HowOne command line tools for creating app templates.",
6
6
  "type": "module",
@@ -5,16 +5,28 @@ description: Build application code with @howone/sdk correctly. Use when impleme
5
5
 
6
6
  # HowOne SDK
7
7
 
8
- Use this skill when writing app code that consumes `@howone/sdk`. The goal is stable generated code: read backend-synced manifests, define zod schemas, bind entities and AI actions in the app SDK entry, then call those bindings from UI code.
8
+ Use this skill when writing app code that consumes `@howone/sdk`. The goal is stable generated code: read backend-synced manifests, define runtime schemas, bind entities and AI actions in the app SDK entry, then call those bindings from UI code.
9
+
10
+ This skill is the normal source of truth for HowOne SDK usage in generated apps. Do not inspect `node_modules/@howone/sdk` just to rediscover imports, hook names, entity methods, AI action call shapes, or Vite environment wiring. Use dependency source only for narrow missing details, signature drift proven by verification, or SDK-package development.
9
11
 
10
12
  ## Workflow
11
13
 
12
- 1. Locate the app root first. If an app was created by `howone init app <name>`, all `.howone/*` metadata and `src/lib/sdk.ts` work should happen inside that created app directory.
13
- 2. Read the existing app SDK entry, usually `src/lib/sdk.ts`, before editing. Preserve its imports and local style.
14
- 3. For Entity bindings, read `.howone/database/manifest.json`.
15
- 4. For AI action bindings, read `.howone/ai/manifest.json`.
16
- 5. Generate source code from the manifests, not from memory or tool-call summaries.
17
- 6. Keep sync tools and app-code edits separate. `sync_schema_artifacts` and `sync_ai_artifacts` write metadata only; the coding agent edits `src/lib/sdk.ts`.
14
+ 1. Establish the app root. All `.howone/*` metadata and `src/lib/sdk.ts` work belongs inside that app root.
15
+ 2. Read `references/usage-patterns.md` once before writing SDK-related code. It contains the SDK mental model, essential public API, generation recipes, and common traps.
16
+ 3. Read the existing app SDK entry, usually `src/lib/sdk.ts`, before editing. Preserve its imports and local style.
17
+ 4. For Entity bindings, read `.howone/database/manifest.json`.
18
+ 5. For AI action bindings, read `.howone/ai/manifest.json`.
19
+ 6. Generate source code from the manifests, not from memory or tool-call summaries.
20
+ 7. Keep sync tools and app-code edits separate. `sync_schema_artifacts` and `sync_ai_artifacts` write metadata only; the coding agent edits `src/lib/sdk.ts`.
21
+
22
+ ## Source Order
23
+
24
+ Use this order when deciding what to read:
25
+
26
+ 1. Current app files: `src/lib/sdk.ts`, relevant UI files, and generated manifests.
27
+ 2. This skill: `SKILL.md` plus `references/usage-patterns.md`.
28
+ 3. TypeScript/compiler errors from verification.
29
+ 4. SDK source or declaration files, only as a narrow fallback for a missing or disputed API.
18
30
 
19
31
  ## Rules
20
32
 
@@ -24,9 +36,10 @@ Use this skill when writing app code that consumes `@howone/sdk`. The goal is st
24
36
  - Current action binding shape is `howone.ai.<actionName>.run(input)`, `stream(input)`, and `events(input)`. Do not generate `howone.ai.run.<actionName>(input)` unless the SDK implementation has been changed to support that API.
25
37
  - Do not name AI actions `run`, `stream`, or `events`; those are reserved.
26
38
  - Define entity create/update types explicitly. Avoid deriving create types from `Omit<EntityRecord & ...>` when it widens the payload.
39
+ - React integration does not provide entity/query/AI hooks. Use plain async SDK calls from React state/effects or an app-level data-fetching library.
27
40
  - Never make generated SDK/type files under `.howone`. `.howone` stores manifests only.
28
41
  - If an AI-first feature persists generated results, create/update AI capability schema first, sync `.howone/ai`, then derive Entity fields from the AI `outputSchema`.
29
42
 
30
43
  ## References
31
44
 
32
- Read `references/usage-patterns.md` when implementing or reviewing actual code.
45
+ Read `references/usage-patterns.md` before writing SDK-related code. It is the working reference for `@howone/sdk` and `@howone/sdk/react`: mental model, imports, method names, app SDK entry shape, entity examples, AI action examples, React usage, validation caveats, and when source fallback is justified.
@@ -1,5 +1,213 @@
1
1
  # HowOne SDK Usage Patterns
2
2
 
3
+ Working reference for `@howone/sdk` and `@howone/sdk/react`. It covers the SDK surface that generated HowOne apps normally need: client setup, entity bindings, AI action bindings, React provider usage, and the known traps that cause repeated source inspection.
4
+
5
+ ## Reading Strategy
6
+
7
+ Use this file as the working SDK manual for normal app generation. It intentionally includes the public imports, client shape, entity methods, AI action bindings, React exports, app SDK entry shape, and known validation caveats.
8
+
9
+ Do not inspect `node_modules/@howone/sdk` for the same information unless:
10
+
11
+ - This file does not cover the exact API needed.
12
+ - TypeScript or runtime verification contradicts this file.
13
+ - You are intentionally updating the SDK package itself, not an app that consumes it.
14
+
15
+ When source fallback is necessary, keep it narrow: inspect the exact exported symbol or declaration that is missing, then return to app code. Do not glob or read the whole SDK package to relearn the basic usage below.
16
+
17
+ ## SDK Mental Model
18
+
19
+ - `createClient(...)` creates the base HowOne client with auth, entity, AI, upload, and user helpers.
20
+ - `client.entity<TRecord, TCreate, TUpdate>('EntityName')` creates one typed entity client.
21
+ - `defineEntities(...)` plus `withEntities(client, entities)` makes `howone.entities.<EntityName>` typed in app code.
22
+ - `defineAiAction(...)` plus `defineAiActions(...)` plus `withAiActions(...)` makes `howone.ai.<actionName>.run|stream|events` typed in app code.
23
+ - `@howone/sdk/react` provides auth/theme/loading UI primitives only. It does not provide entity, query, or AI data hooks.
24
+ - `.howone/database/manifest.json` and `.howone/ai/manifest.json` are the generated contracts. App source should be generated from those manifests, not from memory.
25
+
26
+ ## Import Reference
27
+
28
+ ```ts
29
+ // @howone/sdk — all public exports
30
+ import {
31
+ createClient,
32
+ defineAiAction,
33
+ defineAiActions,
34
+ defineEntities,
35
+ withAiActions,
36
+ withEntities,
37
+ type CreateClientOptions,
38
+ type EntityRecord,
39
+ type EntityClient,
40
+ type EntityBindings,
41
+ type QueryOptions,
42
+ type QueryResult,
43
+ type PageInfo,
44
+ type DeleteResult,
45
+ type AiClient,
46
+ type AiActionDefinition,
47
+ type AiActionClient,
48
+ type AiActionConfig,
49
+ type AiResult, // = ExecutionResult
50
+ type AiSession, // { result: Promise<AiResult>, cancel(): void, signal: AbortSignal }
51
+ type AiEvent, // SSE event payload
52
+ type AiOptions,
53
+ type UserProfile,
54
+ AiSchemaValidationError,
55
+ } from '@howone/sdk'
56
+
57
+ // @howone/sdk/react — React components and hooks
58
+ import {
59
+ HowOneProvider,
60
+ useHowoneContext,
61
+ FloatingButton,
62
+ Loading,
63
+ LoadingSpinner,
64
+ type HowOneProviderProps,
65
+ type HowOneAuthMode,
66
+ type HowOneThemeMode,
67
+ type HowOneBrandMode,
68
+ } from '@howone/sdk/react'
69
+
70
+ import { z } from 'zod'
71
+ ```
72
+
73
+ ---
74
+
75
+ ## createClient
76
+
77
+ ```ts
78
+ const client = createClient({
79
+ projectId: import.meta.env.VITE_HOWONE_PROJECT_ID, // required in Vite apps
80
+ env: import.meta.env.VITE_HOWONE_ENV, // 'local' | 'dev' | 'prod'
81
+ // Optional advanced options (rarely needed):
82
+ // apiUrl, aiUrl, caseStyle ('camel'|'snake'), auth: { mode, getToken }
83
+ })
84
+
85
+ // client shape:
86
+ client.entity<TRecord, TCreate, TUpdate>(entityName: string): EntityClient<...>
87
+ client.entities: Record<string, EntityClient>
88
+ client.ai: AiClient // low-level; prefer withAiActions bindings
89
+ client.me(): Promise<UserProfile | null>
90
+ client.requireMe(): Promise<UserProfile>
91
+ client.auth.setToken(t: string | null): void
92
+ client.auth.getToken(): string | null
93
+ client.auth.isAuthenticated(): boolean
94
+ client.auth.login(redirect?: string): void
95
+ client.auth.logout(): void
96
+ client.upload.file(file, options?): Promise<{ url, thumbnailUrl?, id?, size?, mimeType? }>
97
+ client.upload.image(file): Promise<{ url }>
98
+ client.upload.batch({ files, concurrent?, onProgress?, onFileComplete? }): Promise<BatchUploadResponse>
99
+ ```
100
+
101
+ ---
102
+
103
+ ## EntityClient
104
+
105
+ ```ts
106
+ type EntityClient<TRecord, TCreate, TUpdate> = {
107
+ name: string
108
+ // Basic CRUD
109
+ list(options?: { page?, limit?, sort?, order?, ...filters }): Promise<TRecord[]>
110
+ get(id: string): Promise<TRecord | null>
111
+ getOrThrow(id: string): Promise<TRecord>
112
+ create(data: TCreate): Promise<TRecord>
113
+ update(id: string, data: TUpdate): Promise<TRecord>
114
+ delete(id: string): Promise<{ deleted: boolean; id: string; message? }>
115
+ // Advanced query with filters/pagination
116
+ query(options?: QueryOptions<TRecord>): Promise<QueryResult<TRecord>>
117
+ query.mine(options?: QueryOptions<TRecord>): Promise<QueryResult<TRecord>>
118
+ bulkCreate(records: TCreate[], options?: { sample?: boolean }): Promise<TRecord[]>
119
+ aggregate(pipeline: unknown[]): Promise<unknown[]>
120
+ }
121
+
122
+ // QueryOptions
123
+ type QueryOptions<TRecord> = {
124
+ where?: { [K in keyof TRecord]?: TRecord[K] | FieldOperator }
125
+ // FieldOperator: { eq, ne, gt, gte, lt, lte, contains, like, startsWith, endsWith, in, notIn }
126
+ search?: string
127
+ page?: { number?: number; size?: number }
128
+ orderBy?: Partial<Record<keyof TRecord | string, 'asc' | 'desc'>>
129
+ }
130
+
131
+ // QueryResult
132
+ type QueryResult<T> = {
133
+ items: T[]
134
+ page: { number, size, total, totalPages, hasNext, hasPrev }
135
+ }
136
+
137
+ // EntityRecord base
138
+ type EntityRecord = {
139
+ id: string
140
+ createdDate?: string
141
+ updatedDate?: string
142
+ createdById?: string
143
+ isSample?: boolean
144
+ [key: string]: unknown
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ## AI Action
151
+
152
+ ```ts
153
+ // Define a typed action
154
+ defineAiAction(id: string, config?: {
155
+ inputSchema?: z.ZodType<TInput>
156
+ outputSchema?: z.ZodType<TOutput>
157
+ mode?: 'run' | 'stream' | 'events'
158
+ }): AiActionDefinition<TInput, TOutput>
159
+
160
+ // Group actions
161
+ defineAiActions({ [name]: AiActionDefinition, ... }): actions
162
+
163
+ // Bind to client
164
+ withAiActions(client, actions): client & { ai: { [name]: AiActionClient } }
165
+
166
+ // AiActionClient methods
167
+ howone.ai.<actionName>.run(inputs?, options?): Promise<TOutput>
168
+ howone.ai.<actionName>.stream(inputs?, options?): AiSession
169
+ howone.ai.<actionName>.events(inputs?, options?): AsyncIterable<AiEvent>
170
+
171
+ // AiSession
172
+ type AiSession = {
173
+ result: Promise<AiResult>
174
+ cancel(): void
175
+ signal: AbortSignal
176
+ }
177
+
178
+ // Reserved names — do NOT use as action names: run, stream, events
179
+ ```
180
+
181
+ ---
182
+
183
+ ## React: HowOneProvider & hooks
184
+
185
+ `@howone/sdk/react` exports exactly: `HowOneProvider`, `useHowoneContext`, `FloatingButton`, `Loading`, `LoadingSpinner`.
186
+
187
+ **There are no `useEntity`, `useQuery`, `useAi`, or other data-fetching hooks.** Entity and AI calls are plain async functions — call them directly in components with `useEffect`/`useState` or any data-fetching library (SWR, React Query, etc.).
188
+
189
+ ```tsx
190
+ // Wrap app root — handles auth, theme, toasts, floating button
191
+ <HowOneProvider
192
+ projectId={...} // optional if client is already configured
193
+ auth="optional" // 'required' | 'optional' | 'none'
194
+ brand="visible" // 'visible' | 'hidden'
195
+ theme="inherit" // 'dark' | 'light' | 'system' | 'inherit'
196
+ showBrandButton={true}
197
+ >
198
+ <App />
199
+ </HowOneProvider>
200
+
201
+ // Auth context hook — the only hook in the package
202
+ const { user, token, isAuthenticated, logout } = useHowoneContext()
203
+ // user shape: { id, email, name, avatar } | null
204
+
205
+ // Loading components
206
+ <Loading size="md" tone="brand" label="..." description="..." fullScreen />
207
+ <LoadingSpinner size="sm" tone="neutral" />
208
+ // size: 'sm' | 'md' | 'lg' tone: 'brand' | 'neutral' | 'inverse'
209
+ ```
210
+
3
211
  ## App SDK Entry
4
212
 
5
213
  Preferred Vite app entry shape:
@@ -28,7 +236,6 @@ export const entities = defineEntities({
28
236
  export const ai = defineAiActions({
29
237
  // generateImage: defineAiAction('generateImage', {
30
238
  // inputSchema: generateImageInputSchema,
31
- // outputSchema: generateImageOutputSchema,
32
239
  // }),
33
240
  })
34
241
 
@@ -37,6 +244,29 @@ const howone = withAiActions(withEntities(client, entities), ai)
37
244
  export default howone
38
245
  ```
39
246
 
247
+ ## Manifest-To-Code Recipe
248
+
249
+ When backend metadata has been synced, generate app SDK code in this order:
250
+
251
+ 1. Read `src/lib/sdk.ts` to preserve the template's import style and existing bindings.
252
+ 2. Read `.howone/database/manifest.json` for entity names and fields. Generate `Record`, `Create`, and `Update` types, then bind each entity with `client.entity<Record, Create, Update>('EntityName')`.
253
+ 3. Read `.howone/ai/manifest.json` for AI capability names and JSON schemas. Generate zod input schemas and, only when safe, output schemas.
254
+ 4. Export a single default `howone` client built by composing `withEntities` and `withAiActions`.
255
+ 5. In UI code, import the default client and call `howone.entities.<Entity>.*` or `howone.ai.<action>.run(...)`.
256
+
257
+ Basic JSON schema mapping for generated zod:
258
+
259
+ ```ts
260
+ string -> z.string()
261
+ number -> z.number()
262
+ integer -> z.number().int()
263
+ boolean -> z.boolean()
264
+ array -> z.array(...)
265
+ object -> z.object({ ... })
266
+ enum -> z.enum([...])
267
+ missing from required[] -> .optional()
268
+ ```
269
+
40
270
  ## Entity Types
41
271
 
42
272
  Prefer explicit payload types:
@@ -61,6 +291,22 @@ export const entities = defineEntities({
61
291
  })
62
292
  ```
63
293
 
294
+ Generated entity app code usually needs only these calls:
295
+
296
+ ```ts
297
+ const result = await howone.entities.Story.query({
298
+ search,
299
+ page: { number: 1, size: 20 },
300
+ orderBy: { createdDate: 'desc' },
301
+ })
302
+
303
+ await howone.entities.Story.create({ title, content, prompt })
304
+ await howone.entities.Story.update(id, { title })
305
+ await howone.entities.Story.delete(id)
306
+ ```
307
+
308
+ Prefer `query()` for list UIs that need paging/search/sort metadata. Use `list()` only for simple array reads.
309
+
64
310
  Avoid this for generated create payloads:
65
311
 
66
312
  ```ts
@@ -90,7 +336,6 @@ export type GenerateStoryOutput = z.infer<typeof generateStoryOutputSchema>
90
336
  export const ai = defineAiActions({
91
337
  generateStory: defineAiAction('generateStory', {
92
338
  inputSchema: generateStoryInputSchema,
93
- outputSchema: generateStoryOutputSchema,
94
339
  mode: 'run',
95
340
  }),
96
341
  })
@@ -103,6 +348,7 @@ const result = await howone.ai.generateStory.run({
103
348
  topic,
104
349
  ageRange,
105
350
  })
351
+ const story = result.finalResult as GenerateStoryOutput
106
352
  ```
107
353
 
108
354
  For streaming:
@@ -120,19 +366,18 @@ for await (const event of howone.ai.generateStory.events(input)) {
120
366
  }
121
367
  ```
122
368
 
123
- ## AI Output Validation Caveat
369
+ ## AI Output Validation
124
370
 
125
- Check `packages/sdk/src/services/ai.ts` before relying on output validation. The desired behavior is for `defineAiAction(...).run()` to validate and return the workflow final result, not the whole SSE execution envelope.
371
+ `run()` validates input with `inputSchema`, calls the workflow, then validates the **raw `ExecutionResult`** (the full SSE envelope) with `outputSchema`. It does **not** automatically unwrap `result.finalResult`.
126
372
 
127
- Desired implementation shape:
373
+ If you want to type the final result only, omit `outputSchema` and unwrap manually:
128
374
 
129
375
  ```ts
130
- const result = await base.run(definition.id, validInputs, options)
131
- const output = result?.finalResult ?? result
132
- return parseAiValue(definition.id, 'output', output, definition.outputSchema)
376
+ const result = await howone.ai.generateStory.run(input) // returns AiResult
377
+ const finalResult = result.finalResult as GenerateStoryOutput
133
378
  ```
134
379
 
135
- If the SDK still validates the whole `ExecutionResult`, either fix the SDK first or avoid generating final-result `outputSchema` in app code that will immediately run.
380
+ Only define `outputSchema` when it matches the full `ExecutionResult` envelope or after verifying the SDK has changed to validate `finalResult` directly.
136
381
 
137
382
  ## AI Result Persistence
138
383
 
@@ -147,14 +392,46 @@ Example:
147
392
 
148
393
  ```ts
149
394
  const story = await howone.ai.generateStory.run(input)
395
+ const finalResult = story.finalResult as GenerateStoryOutput
150
396
 
151
397
  await howone.entities.Story.create({
152
- title: story.title,
153
- content: story.content,
398
+ title: finalResult.title,
399
+ content: finalResult.content,
154
400
  prompt: input.topic,
155
401
  })
156
402
  ```
157
403
 
404
+ ## Do Not Generate
405
+
406
+ Avoid these patterns unless the SDK has explicitly added them:
407
+
408
+ ```ts
409
+ // No data hooks in @howone/sdk/react
410
+ const todos = useEntity('Todo')
411
+ const query = useQuery(...)
412
+ const result = useAi(...)
413
+
414
+ // Wrong AI binding shape
415
+ await howone.ai.run.generateStory(input)
416
+
417
+ // Too-loose create payload
418
+ type StoryCreate = Omit<StoryRecord, 'id' | 'createdDate' | 'updatedDate'>
419
+
420
+ // Generated files do not belong under .howone
421
+ // .howone stores synced manifests only.
422
+ ```
423
+
424
+ ## When To Read SDK Source
425
+
426
+ Read SDK source or declaration files only for details not covered above, for example:
427
+
428
+ - A newly added public method not listed here.
429
+ - A TypeScript error proving a signature drifted.
430
+ - A runtime error that points to a specific SDK helper.
431
+ - Work that intentionally modifies `packages/sdk` itself.
432
+
433
+ For app generation, do not inspect SDK source to rediscover `createClient`, entity CRUD, AI action binding shape, React exports, or Vite env setup.
434
+
158
435
  ## Generated Code Checklist
159
436
 
160
437
  - `src/lib/sdk.ts` imports only APIs it uses.
@@ -6,6 +6,39 @@ Read this file once when entering the project.
6
6
  Follow it during implementation.
7
7
  Do not repeatedly reread it unless it changes or the current task requires checking project rules again.
8
8
 
9
+ ## Multi-turn Policy
10
+
11
+ After reading this file, maintain a concise project policy summary for the active workspace session.
12
+ Use that summary across turns instead of rereading this file or relying on old chat history.
13
+
14
+ The project policy summary should stay short and include only durable rules:
15
+
16
+ - package manager and common commands
17
+ - trusted libraries and framework assumptions
18
+ - context-loading rules
19
+ - validation rules
20
+ - project-specific constraints
21
+
22
+ Do not store transient command output, temporary reasoning, full file contents, or step-by-step logs in the project policy.
23
+
24
+ Maintain a separate short task state summary while implementing a user request:
25
+
26
+ - current goal
27
+ - current phase: explore, implement, validate, fix, or done
28
+ - files already read
29
+ - files changed
30
+ - last validation command and result
31
+ - known blockers or open questions
32
+
33
+ Use task state to avoid repeating exploration or rereading unchanged files. Reset or replace it when the user starts a new unrelated task.
34
+
35
+ Do not reread this file unless:
36
+
37
+ - this file changed
38
+ - the project policy summary is unavailable
39
+ - the task enters a nested project with its own `AGENTS.md`
40
+ - validation errors suggest a project rule was missed
41
+
9
42
  ## Library Trust Policy
10
43
 
11
44
  For common public libraries already known to modern coding models, prefer model knowledge and package metadata over reading local source files.
@@ -43,4 +76,22 @@ Only read local component or library-style source files when:
43
76
  - the project has custom wrappers, custom variants, or unusual exports
44
77
  - the user explicitly asks to follow local component conventions
45
78
 
46
- Prefer failure-driven inspection over speculative context loading.
79
+ Prefer failure-driven inspection over speculative context loading.
80
+
81
+ ## Context Budget Policy
82
+
83
+ Start with the smallest useful context:
84
+
85
+ 1. Identify the app root and relevant scripts.
86
+ 2. Read the smallest file that directly controls the requested behavior.
87
+ 3. Prefer package metadata, config files, and public API usage before local library internals.
88
+ 4. Expand to additional files only when implementation, validation, or ambiguity requires it.
89
+
90
+ Avoid broad project scans unless the user asks for a repository-wide change or the relevant files are genuinely unknown.
91
+
92
+ ## Validation Policy
93
+
94
+ After meaningful code changes, run the narrowest relevant validation command available in this project.
95
+ Prefer build or typecheck before deeper investigation into library internals.
96
+
97
+ If validation fails, inspect the smallest file or error location needed to fix the failure.
@@ -35,7 +35,9 @@
35
35
  "tailwind-merge": "^3.5.0",
36
36
  "tailwindcss": "^4.2.1",
37
37
  "tw-animate-css": "^1.4.0",
38
- "vaul": "^1.1.2"
38
+ "vaul": "^1.1.2",
39
+ "zod": "^4.3.6",
40
+ "framer-motion": "^12.38.0"
39
41
  },
40
42
  "devDependencies": {
41
43
  "@eslint/js": "^9.39.4",