@pikku/skills 0.12.21 → 0.12.25

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 (101) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/dist/skills.gen.js +1 -1
  3. package/package.json +1 -1
  4. package/skills/pikku-addon/SKILL.md +10 -9
  5. package/skills/pikku-agent/SKILL.md +67 -316
  6. package/skills/pikku-agent/references/agents.md +299 -0
  7. package/skills/{pikku-ai-vercel/SKILL.md → pikku-agent/references/runner-vercel.md} +2 -24
  8. package/skills/{pikku-ai-voice/SKILL.md → pikku-agent/references/voice.md} +1 -22
  9. package/skills/pikku-architect/SKILL.md +264 -0
  10. package/skills/pikku-auth/SKILL.md +89 -0
  11. package/skills/{pikku-better-auth/SKILL.md → pikku-auth/references/better-auth.md} +42 -47
  12. package/skills/{pikku-jose/SKILL.md → pikku-auth/references/jose.md} +1 -23
  13. package/skills/{pikku-machine-auth/SKILL.md → pikku-auth/references/machine-auth.md} +0 -23
  14. package/skills/{pikku-permissions/SKILL.md → pikku-auth/references/permissions.md} +5 -24
  15. package/skills/{pikku-security/SKILL.md → pikku-auth/references/sessions.md} +3 -23
  16. package/skills/pikku-build/SKILL.md +87 -0
  17. package/skills/{pikku-build-app/SKILL.md → pikku-build/references/app.md} +77 -25
  18. package/skills/{pikku-feature/SKILL.md → pikku-build/references/feature.md} +1 -8
  19. package/skills/{pikku-build-app → pikku-build}/references/multi-app.md +1 -1
  20. package/skills/{pikku-build-platform/SKILL.md → pikku-build/references/platform.md} +22 -37
  21. package/skills/{pikku-template-clone/SKILL.md → pikku-build/references/post-clone.md} +0 -6
  22. package/skills/{pikku-build-quick/SKILL.md → pikku-build/references/quick.md} +6 -22
  23. package/skills/{pikku-build-app → pikku-build}/references/ship.md +7 -1
  24. package/skills/pikku-concepts/SKILL.md +75 -8
  25. package/skills/pikku-concepts/references/concept-mapping.md +8 -8
  26. package/skills/pikku-deploy/SKILL.md +158 -0
  27. package/skills/{pikku-deploy-azure/SKILL.md → pikku-deploy/references/azure.md} +18 -50
  28. package/skills/pikku-deploy/references/cloudflare.md +104 -0
  29. package/skills/pikku-deploy/references/express.md +92 -0
  30. package/skills/{pikku-deploy-fastify/SKILL.md → pikku-deploy/references/fastify.md} +8 -32
  31. package/skills/{pikku-deploy-lambda/SKILL.md → pikku-deploy/references/lambda.md} +6 -27
  32. package/skills/{pikku-deploy-nextjs/SKILL.md → pikku-deploy/references/nextjs.md} +9 -33
  33. package/skills/pikku-deploy/references/uws.md +72 -0
  34. package/skills/pikku-deploy/references/ws.md +75 -0
  35. package/skills/pikku-emails/SKILL.md +3 -2
  36. package/skills/pikku-fabric/SKILL.md +20 -10
  37. package/skills/{pikku-fabric-debug/SKILL.md → pikku-fabric/references/debugging.md} +0 -6
  38. package/skills/pikku-i18n/SKILL.md +60 -207
  39. package/skills/{pikku-paraglide/SKILL.md → pikku-i18n/references/enum-labels.md} +0 -6
  40. package/skills/pikku-i18n/references/messages.md +218 -0
  41. package/skills/{pikku-rtl/SKILL.md → pikku-i18n/references/rtl.md} +3 -9
  42. package/skills/pikku-knowledge/SKILL.md +14 -0
  43. package/skills/pikku-kysely/SKILL.md +13 -13
  44. package/skills/pikku-meta/SKILL.md +58 -130
  45. package/skills/{pikku-deps/SKILL.md → pikku-meta/references/audit.md} +1 -17
  46. package/skills/pikku-meta/references/meta.md +114 -0
  47. package/skills/{pikku-versioning/SKILL.md → pikku-meta/references/versioning.md} +0 -26
  48. package/skills/pikku-middleware/SKILL.md +8 -8
  49. package/skills/pikku-n8n-import/SKILL.md +0 -1
  50. package/skills/pikku-react/SKILL.md +50 -298
  51. package/skills/pikku-react/references/client.md +293 -0
  52. package/skills/{pikku-react-query/SKILL.md → pikku-react/references/react-query.md} +2 -22
  53. package/skills/{pikku-workflows-client/SKILL.md → pikku-react/references/workflows.md} +1 -22
  54. package/skills/pikku-scenario/SKILL.md +64 -49
  55. package/skills/pikku-scenario/references/persona-run.md +148 -0
  56. package/skills/pikku-service-backends/SKILL.md +154 -0
  57. package/skills/pikku-service-backends/references/aws.md +106 -0
  58. package/skills/pikku-service-backends/references/backblaze.md +57 -0
  59. package/skills/pikku-service-backends/references/mongodb.md +90 -0
  60. package/skills/pikku-service-backends/references/redis.md +75 -0
  61. package/skills/pikku-service-backends/references/schema.md +63 -0
  62. package/skills/pikku-services/SKILL.md +68 -291
  63. package/skills/{pikku-audit/SKILL.md → pikku-services/references/audit.md} +0 -22
  64. package/skills/{pikku-config/SKILL.md → pikku-services/references/config.md} +1 -25
  65. package/skills/{pikku-pino/SKILL.md → pikku-services/references/pino.md} +0 -20
  66. package/skills/pikku-services/references/services.md +272 -0
  67. package/skills/pikku-software-archaeology/README.md +5 -1
  68. package/skills/pikku-software-archaeology/SKILL.md +15 -2
  69. package/skills/{pikku-product-second-opinion/example/sample-report.md → pikku-software-archaeology/example/second-opinion-sample-report.md} +1 -1
  70. package/skills/pikku-software-archaeology/references/blueprint.schema.json +1 -1
  71. package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
  72. package/skills/{pikku-product-second-opinion/SKILL.md → pikku-software-archaeology/references/second-opinion.md} +4 -9
  73. package/skills/pikku-webhook/SKILL.md +199 -0
  74. package/skills/pikku-wiring/SKILL.md +180 -0
  75. package/skills/{pikku-websocket/SKILL.md → pikku-wiring/references/channel.md} +2 -35
  76. package/skills/{pikku-cli/SKILL.md → pikku-wiring/references/cli.md} +1 -33
  77. package/skills/{pikku-gateway-slack/SKILL.md → pikku-wiring/references/gateway-slack.md} +0 -23
  78. package/skills/{pikku-http/SKILL.md → pikku-wiring/references/http.md} +4 -40
  79. package/skills/{pikku-mcp/SKILL.md → pikku-wiring/references/mcp.md} +0 -33
  80. package/skills/{pikku-queue/SKILL.md → pikku-wiring/references/queue.md} +1 -33
  81. package/skills/{pikku-realtime/SKILL.md → pikku-wiring/references/realtime.md} +2 -25
  82. package/skills/{pikku-rpc/SKILL.md → pikku-wiring/references/rpc.md} +0 -32
  83. package/skills/{pikku-schedule/SKILL.md → pikku-wiring/references/scheduler.md} +1 -35
  84. package/skills/{pikku-trigger/SKILL.md → pikku-wiring/references/trigger.md} +0 -43
  85. package/skills/pikku-workflow/SKILL.md +3 -3
  86. package/skills/pikku-aws/SKILL.md +0 -161
  87. package/skills/pikku-backblaze/SKILL.md +0 -104
  88. package/skills/pikku-deploy-cloudflare/SKILL.md +0 -123
  89. package/skills/pikku-deploy-express/SKILL.md +0 -122
  90. package/skills/pikku-deploy-uws/SKILL.md +0 -144
  91. package/skills/pikku-mongodb/SKILL.md +0 -113
  92. package/skills/pikku-product-second-opinion/README.md +0 -43
  93. package/skills/pikku-redis/SKILL.md +0 -99
  94. package/skills/pikku-schema-ajv/SKILL.md +0 -83
  95. package/skills/pikku-schema-cfworker/SKILL.md +0 -82
  96. package/skills/pikku-ws/SKILL.md +0 -87
  97. /package/skills/{pikku-build-app → pikku-build}/references/theming.md +0 -0
  98. /package/skills/{pikku-product-second-opinion/references/report-template.md → pikku-software-archaeology/references/second-opinion-report-template.md} +0 -0
  99. /package/skills/{pikku-cli/references/complete-example.md → pikku-wiring/references/cli-complete-example.md} +0 -0
  100. /package/skills/{pikku-http → pikku-wiring}/references/http-options.md +0 -0
  101. /package/skills/{pikku-realtime/references/other-routes.md → pikku-wiring/references/realtime-other-routes.md} +0 -0
@@ -0,0 +1,114 @@
1
+ # Pikku Project Metadata
2
+
3
+ `pikku meta` is the machine-readable view of the project and the write path to it.
4
+ `pikku info` is the same ground as human-readable tables. Prefer `meta` when you are
5
+ going to act on the output; prefer `info` when a person is going to read it.
6
+
7
+
8
+ ## Reading
9
+
10
+ | Command | What it answers |
11
+ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
12
+ | `pikku meta context` | Everything a planner needs in one call — functions, wires, middleware, permissions, workflows, capabilities, layout. Start here. |
13
+ | `pikku meta functions get <id>` | One function's input/output schema names, source file, tags, expose/readonly |
14
+ | `pikku meta schemas get <name>` | One generated JSON schema |
15
+ | `pikku meta workflows get <id>` | One workflow's steps |
16
+ | `pikku meta permissions list` | What permissions exist and where they are defined |
17
+ | `pikku meta middleware list` | What middleware exists |
18
+ | `pikku meta wires list` | Wires by transport (http, channel, scheduler, queue, trigger) |
19
+ | `pikku meta clients` | Exposed RPCs/workflows/channels with their type names — what a frontend can call |
20
+
21
+ `list` is the default for each group, so `pikku meta functions` and `pikku meta functions list`
22
+ are the same call.
23
+
24
+ A function's input/output shape comes from here. Do not infer it by reading the
25
+ function body, and do not cast a call site to make it compile — the schema is the type.
26
+
27
+ ## Changing
28
+
29
+ `pikku meta apply` applies a batch of edits to your own source. Pass JSON as a file
30
+ or on stdin:
31
+
32
+ ```bash
33
+ pikku meta apply ops.json
34
+ ```
35
+
36
+ ```json
37
+ {
38
+ "operations": [
39
+ {
40
+ "kind": "functionConfig",
41
+ "sourceFile": "src/functions/todos.functions.ts",
42
+ "exportedName": "listTodos",
43
+ "changes": { "title": "List Todos", "tags": ["todos", "read"] }
44
+ },
45
+
46
+ {
47
+ "kind": "functionConfig",
48
+ "sourceFile": "src/functions/todos.functions.ts",
49
+ "exportedName": "listTodos",
50
+ "changes": {
51
+ "permissions": {
52
+ "functionLevel": {
53
+ "name": "isTodoOwner",
54
+ "from": "../permissions.js"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ]
60
+ }
61
+ ```
62
+
63
+ Three kinds: `functionConfig`, `agentConfig`, `functionBody`. Every operation names
64
+ a `sourceFile` and the `exportedName` declared in it.
65
+
66
+ `functionConfig` changes: `title`, `description`, `summary`, `tags`, `errors`,
67
+ `expose`, `remote`, `mcp`, `readonly`, `approvalRequired`, `permissions`.
68
+ `agentConfig` changes: `name`, `description`, `instructions`, `role`, `personality`,
69
+ `goal`, `model`, `maxSteps`, `temperature`, `toolChoice`, `tools`, `tags`.
70
+
71
+ `null` removes a property. Edits are spliced into the original text, so formatting,
72
+ comments and JSDoc survive.
73
+
74
+ `permissions` and `tools` are written as identifiers rather than literals, so each
75
+ one carries the module it comes from (`{"name": "isTodoOwner", "from": "../permissions.js"}`)
76
+ and the missing import is added for you — widening an existing import from that
77
+ module rather than adding a second one.
78
+
79
+ ### Why batch
80
+
81
+ The whole batch either lands or it does not: every operation is resolved before
82
+ anything is written, so a failure leaves every file untouched and names the
83
+ operation that caused it. Batching is also what makes one codegen pass correct —
84
+ **run `pikku all` once after the batch**, not once per property. The response tells
85
+ you whether it is needed:
86
+
87
+ ```json
88
+ {
89
+ "schemaVersion": "meta-apply.v1",
90
+ "applied": 2,
91
+ "files": ["src/functions/todos.functions.ts"],
92
+ "generatedMetaIsStale": true
93
+ }
94
+ ```
95
+
96
+ ## Human-readable tables (`pikku info`)
97
+
98
+ Four subcommands only — `functions`, `tags`, `middleware`, `permissions`. Routes,
99
+ channels, schedulers and queues are not subcommands; they are the _transport_ column
100
+ of `info functions --verbose`.
101
+
102
+ ```bash
103
+ yarn pikku info functions --verbose --silent
104
+ yarn pikku info tags --silent
105
+ yarn pikku info middleware --verbose --silent
106
+ yarn pikku info permissions --verbose --silent
107
+ ```
108
+
109
+ `--silent` suppresses the banner and inspector diagnostics. It works, but it is not
110
+ declared as an option, so every run also prints `Warning: Unknown option: --silent
111
+ (ignored)` — the warning is wrong. Ignore that one line.
112
+
113
+ `--limit N` caps rows (default 50); the footer says how many were withheld.
114
+ On `tags`, `--verbose` swaps counts for names; elsewhere it adds columns.
@@ -1,31 +1,5 @@
1
- ---
2
- name: pikku-versioning
3
- description: >-
4
- Use when versioning Pikku function contracts, detecting breaking changes, or managing API
5
- backward compatibility. Covers the version property, versions.pikku.json manifest, contract
6
- hashing, and CI integration. Also covers `pikku semver`, which derives a release's semver by
7
- diffing this build's surface against a deployed one and writes .pikku/changes.gen.json.
8
- TRIGGER when: code uses version: on a pikkuFunc, user asks about
9
- API versioning, breaking changes, contract hashes, backward compatibility, what semver a
10
- release should get, comparing against production/staging, or "pikku versions" / "pikku semver"
11
- CLI commands. DO NOT TRIGGER when: user asks about secrets/variables/OAuth2 (use pikku-config)
12
- or general function definitions (use pikku-concepts), or about updating dependency versions
13
- (use pikku-deps).
14
- ---
15
-
16
1
  # Pikku Function Versioning
17
2
 
18
- ## Agent Operating Procedure
19
-
20
- Use this skill as an execution checklist, not reference material.
21
-
22
- 1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
23
- 2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
24
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
25
- 4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
26
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
27
-
28
- Track and protect function contracts across releases. Pikku hashes each function's input/output schema into a manifest so you can detect breaking changes before they ship.
29
3
 
30
4
  ## Before You Start
31
5
 
@@ -6,8 +6,8 @@ description: >-
6
6
  understanding middleware execution order and priority. TRIGGER when: user wants middleware on
7
7
  some or all routes, machine-to-machine auth, tag-scoped cross-cutting concerns, global
8
8
  interceptors, or middleware priority/order questions. DO NOT TRIGGER when: user asks about
9
- permissions/authorization checks (use pikku-permissions), auth strategies like
10
- authBearer/authCookie (use pikku-security), or deployment.
9
+ permissions, sessions or auth strategies like authBearer/authCookie (use
10
+ pikku-auth), or deployment.
11
11
  installGroups: [core]
12
12
  ---
13
13
 
@@ -23,7 +23,7 @@ installGroups: [core]
23
23
  ## The `pikkuMiddleware` Factory
24
24
 
25
25
  ```typescript
26
- import { pikkuMiddleware } from '#pikku/function'
26
+ import { pikkuMiddleware } from '#pikku/middleware'
27
27
 
28
28
  // Simple: just a function
29
29
  const myMiddleware = pikkuMiddleware(async (services, wire, next) => {
@@ -139,7 +139,7 @@ Tags from the function definition and the wire object are merged — middleware
139
139
  ### Registering Tag Middleware
140
140
 
141
141
  ```typescript
142
- import { addTagMiddleware } from '#pikku/function'
142
+ import { addTagMiddleware } from '#pikku/middleware'
143
143
 
144
144
  addTagMiddleware('machine-agent', [machineAgentBearerAuth])
145
145
  ```
@@ -223,7 +223,7 @@ export const reportSomething = pikkuFunc({
223
223
  })
224
224
  ```
225
225
 
226
- An unresolved token leaves the session unset and the function throws `MissingSessionError` — 401, for free. Declare the scope tree once with `defineScope` (see `pikku-permissions`).
226
+ An unresolved token leaves the session unset and the function throws `MissingSessionError` — 401, for free. Declare the scope tree once with `defineScope` (see `pikku-auth`).
227
227
 
228
228
  ### It MUST be `addHTTPMiddleware`, never `addTagMiddleware`
229
229
 
@@ -255,13 +255,13 @@ Unlike tag middleware over `/rpc`, this works: `runScheduledTask` builds its wir
255
255
 
256
256
  ### The one sessionless exception: bootstrap
257
257
 
258
- An endpoint that runs BEFORE the caller has an identity — registering a new host with a shared bootstrap key, a login, a device-code request — has no session to set. That one stays `pikkuSessionlessFunc` and declares its gate in `permissions` (see `pikku-permissions`).
258
+ An endpoint that runs BEFORE the caller has an identity — registering a new host with a shared bootstrap key, a login, a device-code request — has no session to set. That one stays `pikkuSessionlessFunc` and declares its gate in `permissions` (see `pikku-auth`).
259
259
 
260
260
  ## Service-to-Service Bearer Auth (gate-only pattern)
261
261
 
262
262
  Use this when the callee needs to know only THAT the caller is trusted, not WHICH caller it is. If it needs to know which, use the session pattern above.
263
263
 
264
- A server that exposes RPCs only to a trusted caller (e.g. an API calling a machine-agent). Auth lives in a tag middleware — NOT in the function body. Authorization/permission checks belong in the `permissions` field (see `pikku-permissions`), never inside `func`.
264
+ A server that exposes RPCs only to a trusted caller (e.g. an API calling a machine-agent). Auth lives in a tag middleware — NOT in the function body. Authorization/permission checks belong in the `permissions` field (see `pikku-auth`), never inside `func`.
265
265
 
266
266
  **On the server (the service being called):** tag the function, register a `pikkuMiddleware` that reads the `Authorization` header on that tag.
267
267
 
@@ -277,7 +277,7 @@ export const getToken = () => _token
277
277
  ```typescript
278
278
  // wirings/http.wiring.ts
279
279
  import { timingSafeEqual } from 'node:crypto'
280
- import { addTagMiddleware, pikkuMiddleware } from '#pikku/function'
280
+ import { addTagMiddleware, pikkuMiddleware } from '#pikku/middleware'
281
281
  import { UnauthorizedError } from '#pikku/error'
282
282
  import { getToken } from '../lib/host-token.js'
283
283
 
@@ -3,7 +3,6 @@ name: pikku-n8n-import
3
3
  description: 'Use to import an n8n workflow JSON export into a runnable Pikku workflow. Triggers when the user says "import this n8n workflow", "convert this n8n export to pikku", points at an n8n `.json` export or a directory of them, or picks up after `pikku import n8n` left throwing stub functions (`STUB — generated from n8n …`, `— implement me`) or a `<workflow>.integrations.json` manifest. Owns the whole flow: run the importer, triage what it could not map, fill each stub, report any missing `@pikku/addon-*` integrations, and verify the result compiles and runs with no surviving stubs. DO NOT TRIGGER for hand-written addon wiring unrelated to an n8n import (use pikku-addon), or for authoring workflows from scratch (use pikku-workflow).'
4
4
  metadata:
5
5
  version: 1.0.0
6
- installGroups: [fabric]
7
6
  ---
8
7
 
9
8
  # n8n → Pikku Import
@@ -1,313 +1,65 @@
1
1
  ---
2
2
  name: pikku-react
3
- description: 'Set up @pikku/react in a React app: PikkuProvider context, createPikku factory, and the usePikkuRPC / usePikkuFetch hooks for direct (non-React-Query) calls. TRIGGER when: the user is bootstrapping a React frontend that talks to a Pikku backend, asks how to wire `PikkuProvider`, or needs to make one-off RPC calls outside of useQuery/useMutation. TRIGGER when: user asks about the dev actor switcher, "sign in as" / quick-login UI, useDevActors, VITE_DEV_ACTORS, or the app-missing-actor-quick-login validate finding. DO NOT TRIGGER when: the user is asking about useQuery/useMutation hooks (use pikku-react-query) or about workflows (use pikku-workflows-client).'
3
+ description: >-
4
+ Use when a React frontend talks to a Pikku backend — PikkuProvider and createPikku at the app
5
+ root, the generated React Query hooks (usePikkuQuery, usePikkuMutation, usePikkuInfiniteQuery),
6
+ direct usePikkuRPC / usePikkuFetch calls, realtime subscriptions, agent and workflow hooks, and
7
+ the dev actor switcher. TRIGGER when: writing a React component that fetches or mutates backend
8
+ data, wiring PikkuProvider, paginating, running or tracking a workflow from the client, or
9
+ asking about useDevActors / VITE_DEV_ACTORS / quick login. DO NOT TRIGGER when: working on the
10
+ backend (use pikku-wiring), defining the workflow itself (use pikku-workflow), or writing
11
+ user-facing copy (use pikku-i18n).
4
12
  installGroups: [client]
5
13
  ---
6
14
 
7
15
  # Pikku React
8
16
 
9
- ## Agent Operating Procedure
17
+ The hook names and their argument types come from your generated `api.gen.ts` —
18
+ read it for what this app actually exposes. This skill is the part it cannot
19
+ tell you: which hook a given need calls for, and where the generated client
20
+ stops.
10
21
 
11
- Use this skill as an execution checklist, not reference material.
22
+ ## Pick the reference
12
23
 
13
- 1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
14
- 2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
15
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
16
- 4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
17
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
24
+ | You are… | Read |
25
+ | --- | --- |
26
+ | Wiring the app root, resolving the server URL, authenticating, or subscribing to realtime | `references/client.md` |
27
+ | Fetching, mutating or paginating data | `references/react-query.md` |
28
+ | Starting a workflow and showing its progress | `references/workflows.md` |
18
29
 
19
- `@pikku/react` is the smallest possible binding: a Context provider plus
20
- two hooks. It does **not** depend on React Query — that's a separate
21
- opt-in via the generated `api.gen.ts`. Use this skill when setting up the
22
- provider or making direct RPC calls.
30
+ ## Reach for what
23
31
 
24
- ## What ships
32
+ | Need | Use |
33
+ | --- | --- |
34
+ | Render data, dedupe and cache | `usePikkuQuery` |
35
+ | Trigger a write and wait for the result | `usePikkuMutation` |
36
+ | Paginate | `usePikkuInfiniteQuery` |
37
+ | One-off call from an event handler | `usePikkuRPC()` |
38
+ | Hit a REST endpoint rather than an RPC | `usePikkuFetch()` |
39
+ | Talk to one named AI agent | `usePikkuAgent(name)` → `.run` / `.stream` / `.approve` |
40
+ | Run one named workflow | `usePikkuWorkflow(name)` → `.start` / `.run` / `.status` |
41
+ | A workflow long enough to need progress UI | `references/workflows.md` |
42
+ | Subscribe to events, SSE or a channel | `usePikkuRealtime()` |
25
43
 
26
- ```tsx
27
- import {
28
- PikkuProvider,
29
- createPikku,
30
- usePikkuFetch,
31
- usePikkuRPC,
32
- usePikkuRealtime,
33
- usePikkuAgent,
34
- usePikkuWorkflow,
35
- asI18n,
36
- } from '@pikku/react'
37
- ```
38
-
39
- `usePikkuRealtime` is only valid when you wired a `PikkuRealtime` class via
40
- `createPikku` — see the setup section. `usePikkuAgent` and `usePikkuWorkflow`
41
- are thin bindings over the RPC client that pin one agent/workflow name, so a
42
- component never repeats it. `asI18n` is the i18n brand (see **pikku-i18n**).
43
-
44
- ## Resolving the server URL
45
-
46
- Every client (`createPikku`, realtime, the auth client) resolves its base
47
- through one shared helper in `src/lib/env.ts`. Write this once:
48
-
49
- ```ts
50
- // Endpoints come from env, never hardcoded.
51
- export function apiUrl(): string {
52
- // SSR: the client hooks only run in the browser, so a placeholder is fine.
53
- if (import.meta.env.SSR) {
54
- return import.meta.env.VITE_API_URL ?? '/__api'
55
- }
56
- return import.meta.env.VITE_API_URL ?? `${window.location.origin}/api`
57
- }
58
- ```
59
-
60
- **Never fall back to `http://localhost:3000`.** `import.meta.env.VITE_API_URL`
61
- is substituted by Vite at _build_ time, so any deploy that supplies the URL as
62
- a _runtime_ env var or platform binding leaves it `undefined` in the shipped
63
- bundle — the fallback is then the only branch that ever runs in the browser. A
64
- localhost fallback means every request from a deployed app goes to the user's
65
- own machine. `origin + '/api'` is same-origin, needs no build-time knowledge of
66
- the domain, and is correct wherever the app is served from.
67
-
68
- For local dev, set `VITE_API_URL`, or proxy `/api` → your backend in
69
- `vite.config.ts` under `server.proxy`. One `/api` entry also covers
70
- `/api/auth/*`; only add more entries for root-level routes outside `/api`.
71
-
72
- ## Setup at the app root
73
-
74
- ```tsx
75
- import { createPikku, PikkuProvider } from '@pikku/react'
76
- import { PikkuFetch } from './pikku/pikku-fetch.gen'
77
- import { PikkuRPC } from './pikku/pikku-rpc.gen'
78
- import { apiUrl } from './lib/env'
79
-
80
- const pikku = createPikku(PikkuFetch, PikkuRPC, {
81
- serverUrl: apiUrl(),
82
- })
83
-
84
- createRoot(document.getElementById('root')!).render(
85
- <PikkuProvider pikku={pikku}>
86
- <App />
87
- </PikkuProvider>
88
- )
89
- ```
90
-
91
- If the project also exposes realtime events (see **pikku-realtime**), pass
92
- the `PikkuRealtime` class as the third argument and the instance gets a
93
- `realtime` field too:
94
-
95
- ```tsx
96
- import { PikkuRealtime } from './pikku/realtime.gen'
97
-
98
- const pikku = createPikku(PikkuFetch, PikkuRPC, PikkuRealtime, {
99
- serverUrl: apiUrl(),
100
- })
101
- // pikku.fetch / pikku.rpc / pikku.realtime — all share the same fetch
102
- // (server URL + auth configured once).
103
- ```
104
-
105
- The generated classes come from your `pikku.config.json`:
106
-
107
- | config field | generated file |
108
- | ---------------------------- | ----------------------------------------------------- |
109
- | `clientFiles.fetchFile` | typed HTTP client (`PikkuFetch` class) |
110
- | `clientFiles.rpcWiringsFile` | RPC client (`PikkuRPC` class) calling all exposed fns |
111
- | `clientFiles.realtimeFile` | `PikkuRealtime` (websocket events + SSE + channels) |
112
-
113
- If a file isn't being generated, that field is missing from the config —
114
- add it and re-run `pikku all`.
115
-
116
- `createPikku(...)` accepts the same `CorePikkuFetchOptions` as `PikkuFetch`
117
- plus `serverUrl`. Auth headers, request interceptors, etc. are configured
118
- on the fetch instance — RPC and realtime inherit them automatically.
119
-
120
- ## Calling an RPC directly (no React Query)
121
-
122
- Inside a component:
123
-
124
- ```tsx
125
- import { usePikkuRPC } from '@pikku/react'
126
-
127
- function Logout() {
128
- const rpc = usePikkuRPC()
129
- return <button onClick={() => rpc.invoke('logoutUser', {})}>Sign out</button>
130
- }
131
- ```
132
-
133
- `rpc.invoke(name, data)` is typed against `FlattenedRPCMap` — `name` must
134
- be an exposed function id, `data` matches the input schema, return value
135
- matches the output schema.
136
-
137
- You also have `rpc.<funcName>(data)` if the generated RPC client builds
138
- direct methods (project-dependent).
139
-
140
- ## Calling fetch directly
141
-
142
- ```tsx
143
- const fetch = usePikkuFetch()
144
- const data = await fetch.get('/some-rest-route', { searchParams: {...} })
145
- ```
146
-
147
- Use this only when the function is wired via HTTP (REST shape) and you
148
- need a path-style call. For RPC calls, `usePikkuRPC()` is cleaner.
149
-
150
- ## Realtime subscriptions
151
-
152
- If you wired a `PikkuRealtime` class into `createPikku`, use
153
- `usePikkuRealtime()` to grab the shared instance:
154
-
155
- ```tsx
156
- import { usePikkuRealtime } from '@pikku/react'
157
- import type { PikkuRealtime } from './pikku/realtime.gen'
158
-
159
- function TodoList() {
160
- const realtime = usePikkuRealtime<PikkuRealtime>()
161
- useEffect(() => {
162
- return realtime.subscribe('todo-created', ({ todo }) => {
163
- /* ... */
164
- })
165
- }, [realtime])
166
- // ...
167
- }
168
- ```
169
-
170
- The hook throws if no `PikkuRealtime` was wired — that's how you know to
171
- add it to `createPikku(...)`. Full event-hub setup, publishing, and SSE
172
- helpers live in **pikku-realtime**.
173
-
174
- ## When to reach for what
175
-
176
- | Need | Use |
177
- | ----------------------------------- | -------------------------------------------------- |
178
- | Render data, dedupe + cache | **usePikkuQuery** (react-query) |
179
- | Trigger a write, wait for result | **usePikkuMutation** (react-query) |
180
- | Paginate | **usePikkuInfiniteQuery** (react-query) |
181
- | One-off call from an event handler | `usePikkuRPC()` direct |
182
- | Hit a REST endpoint (not RPC) | `usePikkuFetch()` |
183
- | Run one named workflow | `usePikkuWorkflow('name')` → `.start/.run/.status` |
184
- | Talk to one named AI agent | `usePikkuAgent('name')` → `.run/.stream/.approve` |
185
- | Longer-running workflow UX | **pikku-workflows-client** |
186
- | Subscribe to events / SSE / channel | `usePikkuRealtime()` (see **pikku-realtime**) |
187
-
188
- The first three live in your generated `api.gen.ts` (see the
189
- **pikku-react-query** skill). This skill covers the rest.
190
-
191
- `usePikkuAgent` and `usePikkuWorkflow` bind the name once and hand back the
192
- call methods with it already applied:
193
-
194
- ```tsx
195
- const agent = usePikkuAgent('todo-agent')
196
- const { text } = await agent.run({ message, threadId })
197
-
198
- const workflow = usePikkuWorkflow('onboardUser')
199
- const { runId } = await workflow.start({ email })
200
- const state = await workflow.status(runId)
201
- ```
202
-
203
- ## Authentication
204
-
205
- Auth is handled at the `PikkuFetch` layer, and `createPikku`'s options object
206
- _is_ `CorePikkuFetchOptions` plus `serverUrl` — flat, not nested under a
207
- `fetchOptions` key:
208
-
209
- ```tsx
210
- const pikku = createPikku(PikkuFetch, PikkuRPC, {
211
- serverUrl: apiUrl(),
212
- credentials: 'include', // cookie sessions
213
- authHeaders: { jwt: token }, // or { apiKey }
214
- transformDate: true,
215
- })
216
- ```
217
-
218
- There is no request-interceptor hook. For a token that changes after startup,
219
- call the setter on the shared instance — RPC and realtime pick it up because
220
- they hold the same fetch:
221
-
222
- ```tsx
223
- pikku.fetch.setAuthorizationJWT(token) // null clears it
224
- pikku.fetch.setAPIKey(key)
225
- pikku.fetch.setHeader('x-tenant', tenantId)
226
- ```
227
-
228
- `authHeaders.jwt` becomes `Authorization: Bearer …` and `authHeaders.apiKey`
229
- becomes `X-API-KEY`; setting a JWT takes precedence over an API key.
230
-
231
- ### Dev actor sign-in (`useDevActors`)
232
-
233
- The dev-only "Sign in as …" control: one click signs in as a declared scenario
234
- persona with no password, so the app can be reviewed as each kind of user.
235
- `pikku fabric validate` **requires** any frontend with a login screen to ship one
236
- (`app-missing-actor-quick-login-<app>`) — without it a reviewer is locked out of
237
- their own sandbox.
238
-
239
- ```tsx
240
- import { useDevActors } from '@pikku/react'
241
-
242
- const { actors, signInAs, pendingEmail, isPending, error } = useDevActors({
243
- // Gate both reads on the bundler's dev flag so no credential can reach a
244
- // production bundle. The sandbox dev server bakes them from your personas.
245
- actors: import.meta.env.DEV ? import.meta.env.VITE_DEV_ACTORS : undefined,
246
- secrets: import.meta.env.DEV
247
- ? import.meta.env.VITE_DEV_ACTOR_SECRETS
248
- : undefined,
249
- apiUrl: apiUrl(),
250
- onSignedIn: () => navigate({ to: '/' }),
251
- })
252
- ```
253
-
254
- - **It is UI-free**, so render it however you like. For the default rendering use
255
- `<DevActorSwitcher />` from `@pikku/mantine/dev` — a separate entry point from
256
- `@pikku/mantine/core`, whose contract is "drop-in alias for `@mantine/core`"
257
- and so must not export components Mantine has no counterpart for.
258
- - **`secrets` is `{ address: credential }`, not one shared value** — a
259
- credential opens the one persona it was minted for (see
260
- **pikku-better-auth**). `actors` is empty unless the host supplied both a list
261
- and the credentials for it, and an actor with no credential is not offered, so
262
- a production build renders nothing without you testing for it.
263
- - **It takes `onSignedIn` rather than a router**, and takes the env values rather
264
- than reading them, because how env is spelled is a bundler fact
265
- (`import.meta.env.VITE_*` vs `process.env.NEXT_PUBLIC_*`).
266
- - The underlying `signInAsActor()` and `parseDevActors()` are exported too, for a
267
- non-React caller. The endpoint only accepts rows flagged `actor: true`, so it
268
- can never impersonate a real user — see **pikku-better-auth**.
269
-
270
- Do not hand-write the `devActors()` / `signInAsActor()` pair per app; that
271
- copy-paste, including the `import.meta.env.DEV` gate, is exactly what this
272
- replaced.
273
-
274
- ### Linking from a Mantine element: `renderRoot`, not `component`
275
-
276
- Handing TanStack's `Link` to a Mantine element as `component={Link}` compiles,
277
- renders, and navigates — and silently unties the type. Mantine's polymorphic
278
- `component` prop widens the router generic to `AnyRouter`, so `to` and
279
- `params` stop being checked against your actual routes. Renaming a route then
280
- breaks the running app instead of the build, which is the one thing the typed
281
- router exists to prevent.
282
-
283
- Wrap the typed `Link` once and reach it through `renderRoot`, which passes the
284
- props through without re-typing the element:
285
-
286
- ```tsx
287
- // components/links.tsx — one wrapper the whole app links through
288
- import { Link } from '@tanstack/react-router'
289
-
290
- export const AssessmentLink = (props: { assessmentId: string; children: React.ReactNode }) => (
291
- <Link to="/assessments/$assessmentId" params={{ assessmentId: props.assessmentId }}>
292
- {props.children}
293
- </Link>
294
- )
295
- ```
296
-
297
- ```tsx
298
- <Button renderRoot={(p) => <AssessmentLink assessmentId={id} {...p} />}>
299
- Open
300
- </Button>
301
- ```
302
-
303
- The wrapper is where `to` and `params` are checked, and it is checked once.
44
+ A workflow that finishes in a moment can be awaited; one that does not needs
45
+ start-plus-observe, or the component holds a pending state with nothing to show.
304
46
 
305
47
  ## What NOT to do
306
48
 
307
- - Don't instantiate `PikkuFetch`/`PikkuRPC` inside a component — `createPikku`
308
- goes once at the app root, the instance flows through Context.
309
- - Don't call `usePikkuRPC()` outside a `<PikkuProvider>` — it throws.
310
- - Don't write a custom RPC client. The generated one already covers every
311
- exposed function with full types.
312
- - Don't hardcode user-facing strings. Every display string goes through an
313
- i18n token — see **pikku-i18n** for the setup (it's English-only by default).
49
+ - **Do not write a client.** The generated one covers every exposed function
50
+ with full types; a hand-rolled RPC client or a hand-written
51
+ `useQuery({ queryKey, queryFn })` reimplements it worse.
52
+ - **Do not instantiate `PikkuFetch`/`PikkuRPC` in a component.** `createPikku`
53
+ runs once at the app root and the instance flows through context — and
54
+ `usePikkuRPC()` outside `<PikkuProvider>` throws.
55
+ - **Do not call the RPC client inside a `useEffect`.** The hooks handle
56
+ deduplication, caching and unmounting; a manual effect handles none of them.
57
+ - **Do not construct a hook name at runtime.** Hook names are the RPC names known
58
+ at generation time, and a computed one is not type-checked.
59
+ - **Do not poll a workflow with `setInterval`.** `useWorkflowStatus` with a
60
+ `refetchInterval` callback dedupes across components and stops on a terminal
61
+ state in one place.
62
+ - **Do not reach for `as any` when a hook's types disagree with you.** The
63
+ mismatch is the backend's input/output schema; fix it there.
64
+ - **Do not hardcode a user-facing string.** Every display string goes through an
65
+ i18n message — see `pikku-i18n`.