@pikku/skills 0.12.57 → 0.12.59

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 (72) hide show
  1. package/dist/skills.gen.js +2 -2
  2. package/package.json +10 -7
  3. package/skills/pikku-a11y/SKILL.md +5 -3
  4. package/skills/pikku-addon/SKILL.md +8 -9
  5. package/skills/pikku-agent/SKILL.md +16 -13
  6. package/skills/pikku-agent/references/runner-vercel.md +1 -1
  7. package/skills/pikku-architect/SKILL.md +7 -10
  8. package/skills/pikku-auth/SKILL.md +16 -20
  9. package/skills/pikku-auth/references/better-auth.md +74 -37
  10. package/skills/pikku-auth/references/sessions.md +8 -0
  11. package/skills/pikku-build/SKILL.md +36 -68
  12. package/skills/pikku-build/references/app.md +27 -828
  13. package/skills/pikku-build/references/feature.md +3 -5
  14. package/skills/pikku-build/references/phase-1-what-it-is.md +167 -0
  15. package/skills/pikku-build/references/phase-2-people-and-apps.md +133 -0
  16. package/skills/pikku-build/references/phase-3-plan.md +62 -0
  17. package/skills/pikku-build/references/phase-4-build.md +249 -0
  18. package/skills/pikku-build/references/phase-5-prove-and-ship.md +148 -0
  19. package/skills/pikku-build/references/platform.md +5 -6
  20. package/skills/pikku-build/references/quick.md +2 -2
  21. package/skills/pikku-build/references/ship.md +4 -5
  22. package/skills/pikku-changes/SKILL.md +13 -7
  23. package/skills/pikku-concepts/SKILL.md +19 -258
  24. package/skills/pikku-concepts/references/cli.md +63 -0
  25. package/skills/pikku-concepts/references/functions.md +117 -0
  26. package/skills/pikku-concepts/references/language.md +5 -5
  27. package/skills/pikku-concepts/references/packages.md +6 -4
  28. package/skills/pikku-concepts/references/testing.md +14 -0
  29. package/skills/pikku-convert-screen/SKILL.md +7 -1
  30. package/skills/pikku-deploy/SKILL.md +29 -30
  31. package/skills/pikku-deploy/references/ws.md +1 -1
  32. package/skills/pikku-emails/SKILL.md +7 -3
  33. package/skills/pikku-guide/SKILL.md +7 -10
  34. package/skills/pikku-i18n/SKILL.md +18 -19
  35. package/skills/pikku-i18n/references/messages.md +6 -8
  36. package/skills/pikku-knowledge/SKILL.md +42 -45
  37. package/skills/pikku-kysely/SKILL.md +50 -13
  38. package/skills/pikku-list-query/SKILL.md +27 -14
  39. package/skills/pikku-meta/SKILL.md +13 -16
  40. package/skills/pikku-meta/references/audit.md +1 -45
  41. package/skills/pikku-meta/references/versioning.md +1 -1
  42. package/skills/pikku-middleware/SKILL.md +23 -13
  43. package/skills/pikku-middleware/references/middleware-patterns.md +2 -2
  44. package/skills/pikku-n8n-import/SKILL.md +7 -8
  45. package/skills/pikku-permissions/SKILL.md +14 -8
  46. package/skills/pikku-react/SKILL.md +7 -6
  47. package/skills/pikku-realtime/SKILL.md +16 -11
  48. package/skills/pikku-report/SKILL.md +8 -10
  49. package/skills/pikku-scenario/SKILL.md +15 -18
  50. package/skills/pikku-scenario/references/steps.md +3 -4
  51. package/skills/pikku-scenario/references/virtual-users.md +1 -1
  52. package/skills/pikku-seo/SKILL.md +16 -8
  53. package/skills/pikku-service-backends/SKILL.md +26 -28
  54. package/skills/pikku-services/SKILL.md +8 -9
  55. package/skills/pikku-services/references/services.md +1 -1
  56. package/skills/pikku-software-archaeology/README.md +12 -1
  57. package/skills/pikku-software-archaeology/SKILL.md +9 -2
  58. package/skills/pikku-software-archaeology/references/pikku-mapping.md +1 -1
  59. package/skills/pikku-software-archaeology/references/sources/express.md +75 -0
  60. package/skills/pikku-software-archaeology/references/sources/laravel.md +77 -0
  61. package/skills/pikku-software-archaeology/references/sources/nestjs.md +73 -0
  62. package/skills/pikku-software-archaeology/references/sources/rails.md +72 -0
  63. package/skills/pikku-software-archaeology/references/tests-to-scenarios.md +72 -0
  64. package/skills/pikku-tailwind/SKILL.md +15 -16
  65. package/skills/pikku-theme/SKILL.md +15 -17
  66. package/skills/pikku-ui/SKILL.md +4 -3
  67. package/skills/pikku-webhook/SKILL.md +10 -11
  68. package/skills/pikku-wiring/SKILL.md +45 -47
  69. package/skills/pikku-wiring/references/channel.md +1 -1
  70. package/skills/pikku-wiring/references/scheduler.md +1 -1
  71. package/skills/pikku-wiring/references/trigger.md +24 -1
  72. package/skills/pikku-workflow/SKILL.md +7 -8
@@ -0,0 +1,117 @@
1
+ # Functions and schemas
2
+
3
+ ## Functions
4
+
5
+ Three main function types:
6
+
7
+ ```typescript
8
+ // Requires authentication — receives session in wire context.
9
+ // input/output are Zod schemas; the data + return types are inferred from them.
10
+ const updateTodo = pikkuFunc({
11
+ input: UpdateTodoInput,
12
+ output: TodoOutput,
13
+ func: async (services, data, wire) => {
14
+ const { session } = wire
15
+ return services.todoStore.update(data.id, data)
16
+ },
17
+ })
18
+
19
+ // No authentication required
20
+ const listTodos = pikkuSessionlessFunc({
21
+ input: ListTodosInput,
22
+ output: TodoListOutput,
23
+ func: async (services, data) => {
24
+ return { todos: services.todoStore.list(data.filters) }
25
+ },
26
+ })
27
+
28
+ // No input or output (for scheduled tasks, lifecycle hooks)
29
+ const cleanup = pikkuVoidFunc(async (services) => {
30
+ services.todoStore.cleanOldItems()
31
+ })
32
+ ```
33
+
34
+ Services can be destructured inline in the `func` signature (e.g. `async ({ logger, todoStore }, { title }) => ...`). Full config options:
35
+
36
+ ```typescript
37
+ pikkuFunc({
38
+ // Identity and documentation — prose, so it follows `metaLocale` in
39
+ // pikku.config.json (default `en`). The identifier does not; see
40
+ // "What Language You Write In".
41
+ title?: string, // Human-readable name
42
+ description?: string, // What the function does
43
+ version?: number, // Contract version (see pikku-meta)
44
+ override?: string, // Logical name override, so several exports share a versioned base
45
+ tags?: string[], // For grouping and middleware targeting
46
+
47
+ // Contract
48
+ input?: ZodSchema, // Input validation schema
49
+ output?: ZodSchema, // Output validation schema
50
+ errors?: Array<typeof PikkuError>, // Errors this function may throw
51
+
52
+ // Reachability
53
+ expose?: boolean, // Allow external RPC calls (see pikku-wiring)
54
+ remote?: boolean, // Allow remote RPC calls
55
+ mcp?: boolean, // Expose as MCP tool (see pikku-wiring)
56
+ readonly?: boolean, // Declares the function performs no writes
57
+ deploy?: 'serverless' | 'server' | 'auto',
58
+
59
+ // Authorization — see pikku-auth
60
+ auth?: boolean, // Override default auth requirement
61
+ scopes?: ScopeId[], // AND-ed, checked before permissions; session required
62
+ permissions?: PermissionGroup, // OR-ed pool
63
+ permissionsInBody?: boolean, // Last resort; needs allow.permissionsInBody in config
64
+ middleware?: PikkuMiddleware[], // See pikku-middleware
65
+
66
+ // Agent tooling — see pikku-agent
67
+ approvalRequired?: boolean,
68
+ approvalDescription?: (services, data) => Promise<string>,
69
+
70
+ // Workflow step behavior — see pikku-workflow
71
+ workflowQueued?: boolean, // Dispatch via queue instead of inline
72
+ workflowRetries?: number,
73
+ workflowTimeout?: string, // e.g. '30s', '5m'
74
+
75
+ audit?: boolean | { durability?: 'best-effort' | 'transactional' },
76
+
77
+ func: async (services, data, wire) => { ... },
78
+ })
79
+ ```
80
+
81
+ `scopes` is the one option `pikkuSessionlessFunc` does not accept, and the
82
+ omission is deliberate: scopes are AND-ed and fail closed, so an anonymous
83
+ caller holds none and satisfies none — a sessionless function with scopes would
84
+ reject every caller it exists to serve. Gate those with `permissions`, which
85
+ receive the optional session and may pass anonymous.
86
+
87
+ **Generics XOR `input`/`output` — never both.** A function's data and return
88
+ types come from _one_ source: either the `input`/`output` schemas (preferred —
89
+ they double as runtime validation and OpenAPI) or type generics
90
+ (`pikkuFunc<In, Out>({ ... })`). Passing both makes the two disagree and forces
91
+ `as any` casts. Do not annotate the `func` return type inline either — let the
92
+ `output` schema (or the generic) be the single source of truth for the type.
93
+
94
+ ```typescript
95
+ // Correct — schema-based (no generics, no inline return type)
96
+ pikkuFunc({ input: MyInput, output: MyOutput, func: async (s, d) => { ... } })
97
+ // Correct — generic-based (no input/output)
98
+ pikkuFunc<MyIn, MyOut>({ func: async (s, d) => { ... } })
99
+ // WRONG — mixing the two
100
+ pikkuFunc<MyIn, MyOut>({ input: MyInput as any, func: async (s, d) => { ... } })
101
+ ```
102
+
103
+ ## Schemas (Validation)
104
+
105
+ Pikku uses Standard Schema — works with Zod, Valibot, ArkType:
106
+
107
+ ```typescript
108
+ import { z } from 'zod'
109
+
110
+ const CreateTodoInputSchema = z.object({
111
+ title: z.string().min(1).max(200),
112
+ priority: z.enum(['low', 'medium', 'high']).optional(),
113
+ tags: z.array(z.string()).optional(),
114
+ })
115
+ ```
116
+
117
+ Schemas serve triple duty: runtime validation, TypeScript types, and OpenAPI documentation.
@@ -31,10 +31,10 @@ identifier is a migration, not an edit.
31
31
  { "metaLocale": "de" }
32
32
  ```
33
33
 
34
- Meta is the one part of a project the **Pikku Console** renders back to a human.
35
- A team reviewing their own functions, features and scenario reports in the
36
- Console is reading meta and nothing else, so a team whose working language is
37
- German should be able to read their Console in German. That is the entire reason
34
+ Meta is the one part of a project that is read back to a human. A team
35
+ reviewing their own functions, features and scenario reports is reading meta
36
+ and nothing else, so a team whose working language is German should be able to
37
+ read it in German. That is the entire reason
38
38
  the field exists.
39
39
 
40
40
  Read it before you author meta, and write descriptions, titles and templates in
@@ -79,7 +79,7 @@ settings, each on its own axis:
79
79
  // apps/app/src/i18n/active.json — what a first-time visitor opens in
80
80
  { "defaultLocale": "de" }
81
81
 
82
- // pikku.config.json — the language the team reads their Console in
82
+ // pikku.config.json — the language the team reads their meta in
83
83
  { "metaLocale": "de" }
84
84
  ```
85
85
 
@@ -4,14 +4,16 @@
4
4
 
5
5
  | Package | Use Case |
6
6
  | ----------------------------- | ------------------------------------- |
7
- | `@pikku/express-server` | Express standalone server |
7
+ | `@pikku/express` | Express standalone server |
8
8
  | `@pikku/express-middleware` | Express as middleware in existing app |
9
- | `@pikku/fastify-server` | Fastify standalone |
9
+ | `@pikku/fastify` | Fastify standalone |
10
10
  | `@pikku/fastify-plugin` | Fastify plugin |
11
11
  | `@pikku/next` | Next.js API routes |
12
- | `@pikku/aws-lambda` | AWS Lambda handlers |
12
+ | `@pikku/lambda` | AWS Lambda handlers |
13
13
  | `@pikku/cloudflare` | Cloudflare Workers |
14
- | `@pikku/uws-server` | uWebSockets.js (high perf) |
14
+ | `@pikku/uws` | uWebSockets.js (high perf) |
15
+ | `@pikku/ws` | `ws` WebSocket server |
16
+ | `@pikku/azure-functions` | Azure Functions |
15
17
  | `@pikku/modelcontextprotocol` | MCP server |
16
18
 
17
19
  ## Service Packages
@@ -0,0 +1,14 @@
1
+ # Testing
2
+
3
+ Functions are easily testable because they're pure:
4
+
5
+ ```typescript
6
+ const mockServices = {
7
+ logger: new MockLogger(),
8
+ todoStore: new MockTodoStore(),
9
+ }
10
+
11
+ // Call function directly — no HTTP, no framework
12
+ const result = await listTodos.func(mockServices, { userId: 'test' })
13
+ expect(result.todos).toHaveLength(3)
14
+ ```
@@ -1,6 +1,12 @@
1
1
  ---
2
2
  name: pikku-convert-screen
3
- description: Use when an old screen, page or feature (from an old console, another repo, screenshots, or a description) has to be rebuilt in the current app. Extracts what it does as plain business logic, with no layout or code detail, then designs it fresh in the current design system. TRIGGER on "convert this screen", "port the old console page", "we had this before, bring it back", "redesign X from the old console". DO NOT TRIGGER for whole-repo reverse engineering (use pikku-software-archaeology) or for restyling a screen that already exists in the app.
3
+ description: >-
4
+ Rebuild an old screen, page or feature (from an old console, another repo, screenshots or a
5
+ description) in the current app: extract what it does as plain business logic, no layout or code
6
+ detail, then design it fresh in the current design system. TRIGGER on "convert this screen", "port
7
+ the old console page", "we had this before, bring it back", "redesign X from the old console". DO
8
+ NOT TRIGGER for whole-repo reverse engineering (use pikku-software-archaeology) or restyling a
9
+ screen already in the app.
4
10
  installGroups: [core]
5
11
  ---
6
12
 
@@ -1,14 +1,13 @@
1
1
  ---
2
2
  name: pikku-deploy
3
3
  description: >-
4
- Use when deploying a Pikku app to a runtime — Express, Fastify, uWebSockets.js, the `ws` library,
5
- Next.js, AWS Lambda, Cloudflare Workers or Azure Functions. Covers the bootstrap every runtime
6
- shares, choosing between them, and the behaviour that differs: which accept
7
- `RunHTTPWiringOptions`, how each one runs scheduled tasks, and where CORS and health checks live.
8
- TRIGGER when: writing or debugging `start.ts` / a worker entry / a Lambda handler, code imports
9
- `@pikku/express`, `@pikku/fastify`, `@pikku/uws`, `@pikku/ws`, `@pikku/next`, `@pikku/lambda`,
10
- `@pikku/cloudflare` or `@pikku/azure-functions`, or the user asks how to serve, host or deploy a
11
- Pikku app. DO NOT TRIGGER when: defining functions or wirings with no runtime-specific code.
4
+ Deploying a Pikku app to a runtime: Express, Fastify, uWebSockets.js, `ws`, Next.js, AWS Lambda,
5
+ Cloudflare Workers, Azure Functions. Covers the shared bootstrap, choosing a runtime, which accept
6
+ RunHTTPWiringOptions, scheduled tasks per runtime, CORS and health checks. TRIGGER when: writing
7
+ or debugging `start.ts`, a worker entry or Lambda handler, importing
8
+ @pikku/express|fastify|uws|ws|next|lambda|cloudflare|azure-functions, or asked how to serve, host
9
+ or deploy a Pikku app. DO NOT TRIGGER when: defining functions or wirings with no runtime-specific
10
+ code.
12
11
  installGroups: [core]
13
12
  ---
14
13
 
@@ -35,23 +34,23 @@ Two families, and the difference decides how you write the entry file.
35
34
  **Long-running servers** own a process. Services are built once at startup and
36
35
  live in module scope for the life of the server.
37
36
 
38
- | Runtime | Package | Reach for it when |
39
- | --- | --- | --- |
40
- | Express | `@pikku/express` | An existing Express app, or you want static assets and upload handling |
41
- | Fastify | `@pikku/fastify` | An existing Fastify app, or you want its stricter defaults |
42
- | uWebSockets.js | `@pikku/uws` | Highest throughput, HTTP and WebSocket on one port |
43
- | ws | `@pikku/ws` | WebSocket only, attached to an `http.Server` you own |
37
+ | Runtime | Package | Reach for it when |
38
+ | -------------- | ---------------- | ---------------------------------------------------------------------- |
39
+ | Express | `@pikku/express` | An existing Express app, or you want static assets and upload handling |
40
+ | Fastify | `@pikku/fastify` | An existing Fastify app, or you want its stricter defaults |
41
+ | uWebSockets.js | `@pikku/uws` | Highest throughput, HTTP and WebSocket on one port |
42
+ | ws | `@pikku/ws` | WebSocket only, attached to an `http.Server` you own |
44
43
 
45
44
  **Per-invocation runtimes** are handed a request and torn down. Services are
46
45
  cached in module scope across warm invocations, and the deploy codegen writes
47
46
  that caching for you.
48
47
 
49
- | Runtime | Package | Reach for it when |
50
- | --- | --- | --- |
51
- | AWS Lambda | `@pikku/lambda` | API Gateway, EventBridge, SQS |
52
- | Cloudflare Workers | `@pikku/cloudflare` | Edge, Durable Objects for channels |
53
- | Azure Functions | `@pikku/azure-functions` | Azure hosting — note channels are not implemented |
54
- | Next.js | `@pikku/next` | Pikku behind Next routes, or RPC from Server Components |
48
+ | Runtime | Package | Reach for it when |
49
+ | ------------------ | ------------------------ | ------------------------------------------------------- |
50
+ | AWS Lambda | `@pikku/lambda` | API Gateway, EventBridge, SQS |
51
+ | Cloudflare Workers | `@pikku/cloudflare` | Edge, Durable Objects for channels |
52
+ | Azure Functions | `@pikku/azure-functions` | Azure hosting — note channels are not implemented |
53
+ | Next.js | `@pikku/next` | Pikku behind Next routes, or RPC from Server Components |
55
54
 
56
55
  Then read the reference for the one you picked: `references/express.md`,
57
56
  `references/fastify.md`, `references/uws.md`, `references/ws.md`,
@@ -84,16 +83,16 @@ hand-written handlers are for the cases the deploy codegen does not cover.
84
83
  `maxBodySize`, `respondWith404`, `coerceDataFromSchema` and `bubbleErrors` reach
85
84
  the request only on the runtimes that thread them through.
86
85
 
87
- | Runtime | Accepts options | Where an oversized body is actually stopped |
88
- | --- | --- | --- |
89
- | Express | `init(httpOptions)` | The `express.json` parser limit, fed by `maxBodySize` |
90
- | Fastify | `init(httpOptions)` | Fastify's own `bodyLimit`, set from `maxBodySize` |
91
- | uWS | HTTP handler only | The handler counts bytes itself and answers `413` |
92
- | ws | Yes, on the handler | `maxPayload` on the `WebSocketServer` |
93
- | Next.js | Only via your own `PikkuNextJS` handler | Next's own limits |
94
- | Lambda | **No** | API Gateway's payload limit |
95
- | Cloudflare | Partially — `runFetch(request, hibernation, options)` | The platform's limit |
96
- | Azure | **No** | Azure's request limits |
86
+ | Runtime | Accepts options | Where an oversized body is actually stopped |
87
+ | ---------- | ----------------------------------------------------- | ----------------------------------------------------- |
88
+ | Express | `init(httpOptions)` | The `express.json` parser limit, fed by `maxBodySize` |
89
+ | Fastify | `init(httpOptions)` | Fastify's own `bodyLimit`, set from `maxBodySize` |
90
+ | uWS | HTTP handler only | The handler counts bytes itself and answers `413` |
91
+ | ws | Yes, on the handler | `maxPayload` on the `WebSocketServer` |
92
+ | Next.js | Only via your own `PikkuNextJS` handler | Next's own limits |
93
+ | Lambda | **No** | API Gateway's payload limit |
94
+ | Cloudflare | Partially — `runFetch(request, hibernation, options)` | The platform's limit |
95
+ | Azure | **No** | Azure's request limits |
97
96
 
98
97
  On Fastify, leaving `maxBodySize` unset keeps Fastify's stricter 1MB default
99
98
  rather than loosening it to Pikku's 10MB fallback. On uWS the chunks are dropped
@@ -16,7 +16,7 @@ the upgrade and message plumbing to them.
16
16
 
17
17
  ```typescript
18
18
  import { DEFAULT_WS_MAX_PAYLOAD, pikkuWebsocketHandler } from '@pikku/ws'
19
- import { stopSingletonServices } from '@pikku/core'
19
+ import { stopSingletonServices } from '@pikku/core/utils'
20
20
  import { Server } from 'http'
21
21
  import { WebSocketServer } from 'ws'
22
22
 
@@ -1,9 +1,13 @@
1
1
  ---
2
2
  name: pikku-emails
3
3
  description: >-
4
- Use when working with Pikku's file-based email templates: authoring html, subject and text templates, locales, partials and theme, running `pikku emails generate`, and rendering and sending them through an EmailService.
5
- TRIGGER when: code uses renderEmailTemplate, EmailTemplateName, EmailService, SendTemplateEmailInput or LocalEmailService, or imports from .pikku/email/pikku-emails.gen; the project has an emails/ directory (templates/, locales/, partials/, theme.json) or emailTemplatesDir in pikku.config.json; or the user asks to add, edit or translate a transactional email (verification, password reset, invitation, receipt) or wire email sending.
6
- DO NOT TRIGGER when: the task is i18n for the app UI (use pikku-i18n) or sign-in flows in general (use pikku-auth).
4
+ Pikku's file-based email templates: html/subject/text templates, locales, partials, theme, `pikku
5
+ emails generate`, rendering and sending via EmailService. TRIGGER when: code uses
6
+ renderEmailTemplate, EmailTemplateName, EmailService, SendTemplateEmailInput, LocalEmailService or
7
+ imports .pikku/email/pikku-emails.gen; the project has an emails/ directory or emailTemplatesDir
8
+ in pikku.config.json; or the user adds, edits or translates a transactional email (verification,
9
+ reset, invitation, receipt). DO NOT TRIGGER for app UI i18n (use pikku-i18n) or sign-in flows in
10
+ general (use pikku-auth).
7
11
  installGroups: [core]
8
12
  ---
9
13
 
@@ -1,16 +1,13 @@
1
1
  ---
2
2
  name: pikku-guide
3
3
  description: >-
4
- Use when writing, rewriting or regenerating a Pikku project's end-user guide, built from the
5
- scenario suite with `pikku scenario guide`. Pages are hand-written markdown, one section per
6
- task, each followed by a `pikku:guide` HTML-comment marker (feature and scenario attributes) that
7
- pulls in the screenshot and recording that scenario filed. Covers planning pages from the suite,
8
- writing task prose in the app's language, per-section markers, the capture step, seed data that
9
- appears on camera, `.guide.lock` staleness, `document: false`, and the traps that make a guide
10
- come out empty, refused, or as a wall of videos. TRIGGER when: user asks for a user guide, help
11
- pages, docs with screenshots, "document the app", or complains that the /docs pages are bad,
12
- thin, or all videos. DO NOT TRIGGER when: user asks about API reference docs, README files, the
13
- Pikku framework's own documentation site, or writing the scenarios themselves (use pikku-scenario).
4
+ Write, rewrite or regenerate a project's end-user guide from the scenario suite (`pikku scenario
5
+ guide`): hand-written markdown with `pikku:guide` markers that pull in scenario screenshots and
6
+ recordings. Covers planning, markers, capture, seed data, `.guide.lock`, `document: false`, and
7
+ traps that leave a guide empty, refused or all videos. TRIGGER when: asked for a user guide, help
8
+ pages, docs with screenshots, "document the app", or the /docs pages are bad, thin or all videos.
9
+ DO NOT TRIGGER for API reference, READMEs, the Pikku framework's own docs, or writing scenarios
10
+ (use pikku-scenario).
14
11
  installGroups: [core]
15
12
  ---
16
13
 
@@ -1,13 +1,12 @@
1
1
  ---
2
2
  name: pikku-i18n
3
3
  description: >-
4
- Use when writing user-facing text in a Pikku frontend, or making one speak another language.
5
- Covers Paraglide JS message functions compiled from per-locale messages JSON files, adding a second
6
- language, generated enum-label maps with @pikku/paraglide, and right-to-left support (document direction, flow-relative
7
- styles) for Arabic, Hebrew, Farsi and Urdu. TRIGGER when: scaffolding or editing a frontend and writing display
8
- text, asked to make copy translatable, adding a language, labelling an enum/status/role value,
9
- or asked to support RTL / mirror the layout. DO NOT TRIGGER for backend functions, error
10
- messages thrown from functions, or log output — none of those are display strings.
4
+ Writing user-facing text in a Pikku frontend or making it speak another language: Paraglide JS
5
+ message functions from per-locale JSON, adding a language, generated enum-label maps
6
+ (@pikku/paraglide), and RTL support (Arabic, Hebrew, Farsi, Urdu). TRIGGER when: writing display
7
+ text in a frontend, making copy translatable, adding a language, labelling an enum/status/role
8
+ value, or supporting RTL. DO NOT TRIGGER for backend functions, error messages thrown from
9
+ functions, or log output.
11
10
  installGroups: [client]
12
11
  ---
13
12
 
@@ -22,11 +21,11 @@ type-checks them, so an i18n mistake blocks the build rather than the release.
22
21
 
23
22
  ## Pick the reference
24
23
 
25
- | You are… | Read |
26
- | --- | --- |
27
- | Writing copy, wiring Paraglide, or adding a language | `references/messages.md` |
28
- | Labelling an enum, status, kind or role value | `references/enum-labels.md` |
29
- | Adding Arabic (or Hebrew, Farsi, Urdu), or writing layout styles | `references/rtl.md` |
24
+ | You are… | Read |
25
+ | ---------------------------------------------------------------- | --------------------------- |
26
+ | Writing copy, wiring Paraglide, or adding a language | `references/messages.md` |
27
+ | Labelling an enum, status, kind or role value | `references/enum-labels.md` |
28
+ | Adding Arabic (or Hebrew, Farsi, Urdu), or writing layout styles | `references/rtl.md` |
30
29
 
31
30
  ## Three axes, and a brief usually means only one
32
31
 
@@ -34,14 +33,14 @@ type-checks them, so an i18n mistake blocks the build rather than the release.
34
33
  codebase. Reading it as one about the codebase is the most expensive mistake
35
34
  available here.
36
35
 
37
- | Axis | What it covers | What sets it |
38
- | --- | --- | --- |
39
- | **Identifiers** | Function, component, type and file names; tables and columns | Nothing — always English |
40
- | **Meta** | `description` / `name` / `title` authored in code, rendered by the console | `metaLocale` in `pikku.config.json` |
41
- | **Product UI** | Every string the app shows a user | `messages/<locale>.json` + `defaultLocale` |
36
+ | Axis | What it covers | What sets it |
37
+ | --------------- | ------------------------------------------------------------------------ | ------------------------------------------ |
38
+ | **Identifiers** | Function, component, type and file names; tables and columns | Nothing — always English |
39
+ | **Meta** | `description` / `name` / `title` authored in code, read back to the team | `metaLocale` in `pikku.config.json` |
40
+ | **Product UI** | Every string the app shows a user | `messages/<locale>.json` + `defaultLocale` |
42
41
 
43
42
  `baseLocale` stays `en` whatever language the product speaks — it names the
44
- message *source* catalogue every other locale is derived from, not the language
43
+ message _source_ catalogue every other locale is derived from, not the language
45
44
  the app is in. Set `defaultLocale` instead.
46
45
 
47
46
  ## Direction is one setting, not per-component work
@@ -59,7 +58,7 @@ is what makes an RTL language just another locale file.
59
58
  `m['enum__' + x]()`, no key-string resolver. A computed key cannot be
60
59
  type-checked or tree-shaken, so a renamed message degrades to silent runtime
61
60
  text. Where the key is genuinely dynamic, map the discriminant to a message
62
- *function* — the map is checked, a string is not.
61
+ _function_ — the map is checked, a string is not.
63
62
  - **Do not `asI18n()` a hardcoded English string.** `asI18n` exists to pass
64
63
  opaque server data (a name, a slug, an id) through the i18n gate. An enum value
65
64
  goes through its generated label map.
@@ -29,11 +29,11 @@ is a statement about **one** of three separate things, and reading it as a
29
29
  statement about the codebase is the single most expensive mistake available in
30
30
  this skill. Three axes:
31
31
 
32
- | Axis | What it covers | What sets it |
33
- | --------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
34
- | **Identifiers** | Function, component, type and file names; database tables and columns | Nothing — always English, no setting |
35
- | **Meta** | `description` / `name` / `title` / `template` authored inside the code, which the Pikku Console renders | `metaLocale` in `pikku.config.json`, default `en` |
36
- | **Product UI** | Every string the app shows a user | `messages/<locale>.json` + `defaultLocale` — **this axis only** |
32
+ | Axis | What it covers | What sets it |
33
+ | --------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
34
+ | **Identifiers** | Function, component, type and file names; database tables and columns | Nothing — always English, no setting |
35
+ | **Meta** | `description` / `name` / `title` / `template` authored inside the code, read back to the team | `metaLocale` in `pikku.config.json`, default `en` |
36
+ | **Product UI** | Every string the app shows a user | `messages/<locale>.json` + `defaultLocale` — **this axis only** |
37
37
 
38
38
  A non-English product moves the third row and nothing else.
39
39
 
@@ -63,7 +63,7 @@ So a German medical portal is **three** settings, not one:
63
63
  // apps/app/src/i18n/active.json — what a visitor opens in
64
64
  { "defaultLocale": "de" }
65
65
 
66
- // pikku.config.json — the language the team reads their Console in
66
+ // pikku.config.json — the language the team reads their meta in
67
67
  { "metaLocale": "de" }
68
68
  ```
69
69
 
@@ -221,8 +221,6 @@ The wrapper alternative — a module that walks the namespace and pipes each mes
221
221
  - Don't edit or commit anything under `src/paraglide/` — it's regenerated; change `messages/*.json` instead.
222
222
  - **Don't wrap `m`.** No re-export module, no branding layer, no resolver. Components import `m` from `../paraglide/messages.js` and call it. `@pikku/react`'s `I18nString` is declared as `string & { readonly __brand: 'LocalizedString' }` — deliberately identical to Paraglide's own `LocalizedString` — so `m.some__key()` satisfies the template's `I18nNode` gate natively. A wrapper adds nothing and costs per-message tree-shaking.
223
223
 
224
- `packages/console` is the one place in this repo that still wraps it, in `src/i18n/messages.ts`, to keep the debug mask (`█`) it carried over from i18next. That wrapper is a leftover, not a pattern — the generated-locale approach above is how a new app gets the same masking without touching every export. Don't copy it.
225
-
226
224
  The `mKey`/`mList` runtime resolvers that used to live beside it are **gone**, and must not come back. `mList` resolved indexed `prefix.0` keys that no longer exist; `mKey` took a computed string, which is exactly the type safety Paraglide exists to provide. Where a key really is dynamic, map the discriminant to a message _function_ and call it — the map is type-checked, a string is not.
227
225
 
228
226
  - Don't re-resolve messages by string key or re-implement `{param}` interpolation. A key-string resolver turns a missing key back into silent runtime text, surrendering the type safety that is the entire reason to use Paraglide.
@@ -1,16 +1,14 @@
1
1
  ---
2
2
  name: pikku-knowledge
3
3
  description: >-
4
- Use when writing, reading, reorganising or validating a project's knowledge/ directory — the
5
- notes that say what the app is, in the language its users use. Covers the Open Knowledge Format
6
- note (path-as-identity markdown, only `type` required), the sections (entities, decisions,
7
- questions, wishlist), how notes become changes (`pikku knowledge
8
- gaps`), the `resource:` URI scheme tying a note to its code, and `pikku knowledge validate|index|gaps`. TRIGGER when: user asks to write down a decision,
9
- requirement, entity or open question; asks what the app does or is; asks about knowledge/
10
- notes or a diagram, callout or decision block; or hands over a product brief to record. DO NOT TRIGGER when: user asks what functions, routes, tables or permissions exist
11
- (`pikku meta` / `pikku info`, never a note), wants a scenario test (use pikku-scenario), writes a
12
- changeset plan (use pikku-architect), or extracts a `.knowledge/` blueprint from a legacy repo
13
- (use pikku-software-archaeology).
4
+ Write, read, reorganise or validate a project's knowledge/ directory: Open Knowledge Format notes
5
+ (path-as-identity markdown, only `type` required), sections (entities, decisions, questions,
6
+ wishlist), notes becoming changes (`pikku knowledge gaps`), the `resource:` URI scheme, `pikku
7
+ knowledge validate|index|gaps`. TRIGGER when: asked to record a decision, requirement, entity or
8
+ open question, what the app does or is, a knowledge note/diagram/callout, or a product brief. DO
9
+ NOT TRIGGER for what functions/routes/tables exist (`pikku meta`/`pikku info`), scenarios (use
10
+ pikku-scenario), changeset plans (use pikku-architect) or legacy blueprint extraction (use
11
+ pikku-software-archaeology).
14
12
  installGroups: [core]
15
13
  agent:
16
14
  tools: read, write, edit, bash, grep
@@ -21,7 +19,6 @@ agent:
21
19
  verify:
22
20
  - id: knowledge-consistent
23
21
  command: pikku knowledge validate
24
-
25
22
  ---
26
23
 
27
24
  # Pikku Knowledge
@@ -72,14 +69,14 @@ worse than one of them losing access mid-session.
72
69
 
73
70
  Frontmatter fields:
74
71
 
75
- | Field | Meaning |
76
- | ------------- | ----------------------------------------------------------------------------------------------------------------------- |
72
+ | Field | Meaning |
73
+ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
77
74
  | `type` | **The only required field.** One of `entity`, `decision`, `note`, `overview`. Lowercase — every gate compares it literally, so a near-miss is a note no command can see. |
78
- | `title` | What to call the note in a listing. Falls back to the first heading, then the filename. |
79
- | `description` | One line, used as the note's subtitle in a section index. |
80
- | `resource` | Comma-separated `<kind>:<id>` URIs — the code this note is about. See below. |
81
- | `tags` | Flow list (`[a, b]`) or a `- item` block; both are read. |
82
- | `timestamp` | When it was written, if it matters. |
75
+ | `title` | What to call the note in a listing. Falls back to the first heading, then the filename. |
76
+ | `description` | One line, used as the note's subtitle in a section index. |
77
+ | `resource` | Comma-separated `<kind>:<id>` URIs — the code this note is about. See below. |
78
+ | `tags` | Flow list (`[a, b]`) or a `- item` block; both are read. |
79
+ | `timestamp` | When it was written, if it matters. |
83
80
 
84
81
  `index.md` and `log.md` are **reserved**: an `index.md` maps a directory, a `log.md` is an append-only record. Neither is ever listed as a note by an index.
85
82
 
@@ -109,13 +106,13 @@ knowledge/
109
106
 
110
107
  Each section answers exactly one question, which is what lets a reader find a note without an index of indexes:
111
108
 
112
- | Section | The question it answers |
113
- | --------------------- | ------------------------------------------------------------------ |
114
- | `entities/` | What is this thing, in the words users use for it? |
115
- | `decisions/` | What was chosen, and what does that rule out? |
116
- | `decisions/security/` | Who may do what? |
117
- | `questions/` | What has been asked and not yet answered? |
118
- | `wishlist/` | What does somebody want that nobody has asked to be built? |
109
+ | Section | The question it answers |
110
+ | --------------------- | ---------------------------------------------------------- |
111
+ | `entities/` | What is this thing, in the words users use for it? |
112
+ | `decisions/` | What was chosen, and what does that rule out? |
113
+ | `decisions/security/` | Who may do what? |
114
+ | `questions/` | What has been asked and not yet answered? |
115
+ | `wishlist/` | What does somebody want that nobody has asked to be built? |
119
116
 
120
117
  **Create a section the turn you have a note for it** — never a scaffold of empty directories, and never a section without its own `index.md`. A section index says in one line what belongs in it; that sentence is the reason the file exists, so `pikku knowledge index` writes only the note listing and leaves your prose alone.
121
118
 
@@ -193,19 +190,19 @@ It is a **summary, not the note** — the argument continues in prose underneath
193
190
 
194
191
  **Every kind resolves.** That is the whole design: a kind that cannot be checked lets notes accumulate references nothing validates, and the graph rots into fiction exactly where it looks most authoritative.
195
192
 
196
- | Kind | An id is | Where it resolves |
197
- | ----------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------ |
198
- | `func:` | a function id | generated function meta |
199
- | `workflow:` | a workflow name | generated workflow meta |
200
- | `schema:` | a schema name | generated schemas |
201
- | `http:` | a route, `method:route`, or the function behind it | generated http wirings |
202
- | `queue:` | a queue name | generated queue wirings |
203
- | `cron:` | a scheduled task name | generated scheduler wirings |
204
- | `channel:` | a channel name | generated channel meta |
205
- | `table:` | a table name | the generated db schema |
206
- | `addon:` | `@pikku/addon-x` or bare `x` | the manifests that declare the dependency |
207
- | `scope:` | a scope name | the `scopes:` a function gates itself with, plus the scopes a `defineSystemRole()` confers |
208
- | `virtualUser:` | a virtual user name | `defineVirtualUsers()` |
193
+ | Kind | An id is | Where it resolves |
194
+ | -------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------ |
195
+ | `func:` | a function id | generated function meta |
196
+ | `workflow:` | a workflow name | generated workflow meta |
197
+ | `schema:` | a schema name | generated schemas |
198
+ | `http:` | a route, `method:route`, or the function behind it | generated http wirings |
199
+ | `queue:` | a queue name | generated queue wirings |
200
+ | `cron:` | a scheduled task name | generated scheduler wirings |
201
+ | `channel:` | a channel name | generated channel meta |
202
+ | `table:` | a table name | the generated db schema |
203
+ | `addon:` | `@pikku/addon-x` or bare `x` | the manifests that declare the dependency |
204
+ | `scope:` | a scope name | the `scopes:` a function gates itself with, plus the scopes a `defineSystemRole()` confers |
205
+ | `virtualUser:` | a virtual user name | `defineVirtualUsers()` |
209
206
 
210
207
  Ids are case-sensitive: `createEntry` is not `createentry`.
211
208
 
@@ -217,14 +214,14 @@ There is no kind for a service, a middleware or a component. Say it in prose ins
217
214
 
218
215
  These are all things that exist somewhere better, so a note is always the copy that drifts:
219
216
 
220
- | Do not write | Because it lives in |
221
- | ----------------------------------- | ---------------------------------------------------------- |
222
- | a `virtual users/` section | `defineVirtualUsers()` in the project's own code |
217
+ | Do not write | Because it lives in |
218
+ | ----------------------------------- | ------------------------------------------------------------ |
219
+ | a `virtual users/` section | `defineVirtualUsers()` in the project's own code |
223
220
  | a `scenarios/` section | the `scenarios` of the plan for the changeset that builds it |
224
- | a `permissions/` section | a decision note under `decisions/security/` |
225
- | a list of tables, columns or routes | `pikku meta` — the generated schema _is_ the schema |
226
- | a changelog | `CHANGELOG.md` at the repo root |
227
- | **secrets or credentials** | a secrets service. Never here — `knowledge/` is committed. |
221
+ | a `permissions/` section | a decision note under `decisions/security/` |
222
+ | a list of tables, columns or routes | `pikku meta` — the generated schema _is_ the schema |
223
+ | a changelog | `CHANGELOG.md` at the repo root |
224
+ | **secrets or credentials** | a secrets service. Never here — `knowledge/` is committed. |
228
225
 
229
226
  And two shapes that look like a knowledge base but are not:
230
227