@pikku/skills 0.12.52 → 0.12.54

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 (104) hide show
  1. package/dist/skills.gen.js +2 -2
  2. package/package.json +1 -1
  3. package/skills/pikku-a11y/SKILL.md +22 -15
  4. package/skills/pikku-addon/SKILL.md +5 -5
  5. package/skills/pikku-addon/references/addon-package-manifest.md +7 -0
  6. package/skills/pikku-addon/references/openapi.md +11 -0
  7. package/skills/pikku-admin-to-fabric/SKILL.md +7 -21
  8. package/skills/pikku-agent/SKILL.md +6 -6
  9. package/skills/pikku-agent/references/agents.md +8 -1
  10. package/skills/pikku-agent/references/runner-vercel.md +2 -2
  11. package/skills/pikku-agent/references/voice.md +7 -1
  12. package/skills/pikku-architect/SKILL.md +1 -1
  13. package/skills/pikku-architect/references/plan-defects.md +2 -0
  14. package/skills/pikku-auth/SKILL.md +15 -36
  15. package/skills/pikku-auth/references/better-auth.md +22 -45
  16. package/skills/pikku-auth/references/jose.md +6 -0
  17. package/skills/pikku-auth/references/machine-auth.md +6 -0
  18. package/skills/pikku-auth/references/sessions.md +6 -0
  19. package/skills/pikku-blueprint-to-fabric/SKILL.md +6 -21
  20. package/skills/pikku-blueprint-to-fabric/scripts/inventory.mjs +1 -1
  21. package/skills/pikku-build/SKILL.md +29 -26
  22. package/skills/pikku-build/references/app.md +60 -41
  23. package/skills/pikku-build/references/design.md +31 -19
  24. package/skills/pikku-build/references/feature.md +14 -0
  25. package/skills/pikku-build/references/mocks.md +23 -0
  26. package/skills/pikku-build/references/multi-app.md +9 -2
  27. package/skills/pikku-build/references/native-app.md +9 -0
  28. package/skills/pikku-build/references/openapi.md +7 -0
  29. package/skills/pikku-build/references/platform.md +20 -10
  30. package/skills/pikku-build/references/quick.md +14 -4
  31. package/skills/pikku-build/references/scenarios.md +12 -1
  32. package/skills/pikku-build/references/ship.md +6 -1
  33. package/skills/pikku-build/references/theming.md +74 -125
  34. package/skills/pikku-changes/SKILL.md +6 -6
  35. package/skills/pikku-concepts/SKILL.md +8 -17
  36. package/skills/pikku-concepts/references/concept-mapping.md +18 -0
  37. package/skills/pikku-convert-screen/SKILL.md +45 -0
  38. package/skills/pikku-deploy/references/cloudflare.md +8 -0
  39. package/skills/pikku-deploy/references/lambda.md +8 -0
  40. package/skills/pikku-deploy/references/uws.md +1 -1
  41. package/skills/pikku-deploy/references/ws.md +3 -3
  42. package/skills/pikku-emails/SKILL.md +3 -9
  43. package/skills/pikku-fabric/SKILL.md +7 -295
  44. package/skills/pikku-fabric/references/database.md +128 -0
  45. package/skills/pikku-fabric/references/debugging.md +9 -5
  46. package/skills/pikku-fabric/references/deploy.md +179 -0
  47. package/skills/pikku-guide/SKILL.md +10 -10
  48. package/skills/pikku-i18n/SKILL.md +7 -7
  49. package/skills/pikku-i18n/references/enum-labels.md +9 -0
  50. package/skills/pikku-i18n/references/messages.md +16 -3
  51. package/skills/pikku-i18n/references/rtl.md +18 -38
  52. package/skills/pikku-knowledge/SKILL.md +9 -10
  53. package/skills/pikku-kysely/SKILL.md +3 -21
  54. package/skills/pikku-list-query/SKILL.md +2 -2
  55. package/skills/pikku-meta/references/audit.md +9 -0
  56. package/skills/pikku-meta/references/meta.md +6 -0
  57. package/skills/pikku-meta/references/versioning.md +10 -0
  58. package/skills/pikku-middleware/SKILL.md +7 -16
  59. package/skills/pikku-n8n-import/SKILL.md +9 -1
  60. package/skills/pikku-n8n-import/references/addon-mapping.md +6 -0
  61. package/skills/pikku-n8n-import/references/code-translation.md +7 -0
  62. package/skills/pikku-permissions/SKILL.md +41 -10
  63. package/skills/pikku-permissions/references/scopes.md +59 -0
  64. package/skills/pikku-react/SKILL.md +2 -2
  65. package/skills/pikku-react/references/client.md +1 -34
  66. package/skills/pikku-react/references/react-query.md +11 -0
  67. package/skills/pikku-react/references/workflows.md +11 -0
  68. package/skills/pikku-realtime/SKILL.md +9 -3
  69. package/skills/pikku-report/SKILL.md +10 -9
  70. package/skills/pikku-scenario/SKILL.md +11 -17
  71. package/skills/pikku-scenario/references/persona-run.md +11 -1
  72. package/skills/pikku-scenario/references/personas.md +7 -1
  73. package/skills/pikku-seo/SKILL.md +12 -16
  74. package/skills/pikku-service-backends/SKILL.md +0 -10
  75. package/skills/pikku-service-backends/references/aws.md +6 -0
  76. package/skills/pikku-service-backends/references/mongodb.md +7 -0
  77. package/skills/pikku-service-backends/references/redis.md +6 -0
  78. package/skills/pikku-services/references/audit.md +10 -0
  79. package/skills/pikku-services/references/config.md +6 -0
  80. package/skills/pikku-services/references/services.md +8 -1
  81. package/skills/pikku-software-archaeology/SKILL.md +9 -15
  82. package/skills/pikku-software-archaeology/references/blueprint.schema.json +7 -7
  83. package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
  84. package/skills/pikku-software-archaeology/references/second-opinion.md +13 -1
  85. package/skills/pikku-tailwind/SKILL.md +111 -0
  86. package/skills/pikku-theme/SKILL.md +187 -0
  87. package/skills/pikku-theme/references/from-a-reference.md +44 -0
  88. package/skills/pikku-theme/references/presets.md +40 -0
  89. package/skills/pikku-ui/SKILL.md +41 -0
  90. package/skills/pikku-wiring/SKILL.md +13 -12
  91. package/skills/pikku-wiring/references/channel.md +7 -1
  92. package/skills/pikku-wiring/references/cli.md +7 -1
  93. package/skills/pikku-wiring/references/gateway-slack.md +6 -0
  94. package/skills/pikku-wiring/references/http.md +10 -3
  95. package/skills/pikku-wiring/references/mcp.md +8 -0
  96. package/skills/pikku-wiring/references/queue.md +6 -0
  97. package/skills/pikku-wiring/references/realtime.md +34 -4
  98. package/skills/pikku-wiring/references/rpc.md +6 -0
  99. package/skills/pikku-wiring/references/scheduler.md +6 -0
  100. package/skills/pikku-wiring/references/trigger.md +8 -0
  101. package/skills/pikku-workflow/SKILL.md +8 -6
  102. package/skills/pikku-auth/references/permissions.md +0 -261
  103. package/skills/pikku-mantine/SKILL.md +0 -80
  104. package/skills/pikku-wiring/references/realtime-other-routes.md +0 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/skills",
3
- "version": "0.12.52",
3
+ "version": "0.12.54",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/pikkujs/pikku.git",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: pikku-a11y
3
3
  description: >-
4
- Accessibility rules (WCAG 2.2) for the app UI: labeled inputs, real buttons/links, keyboard and focus, contrast and not-color-alone, modals, reduced motion.
4
+ Accessibility rules (WCAG 2.2) for shadcn/Tailwind app UI: labeled inputs, real buttons/links, keyboard and focus, contrast and not-color-alone, modals, reduced motion.
5
5
  TRIGGER when: building forms or any interactive UI, icon-only buttons, modals/drawers, tables/lists with actions, keyboard/focus work, or the user mentions accessibility / screen readers / WCAG.
6
6
  DO NOT TRIGGER when: working on backend functions, database, or deployment with no UI.
7
7
  installGroups: [client]
@@ -9,24 +9,23 @@ installGroups: [client]
9
9
 
10
10
  # Accessibility Rules
11
11
 
12
- Mantine components are accessible ONLY when used properly — the rules below are the
13
- "properly". They apply to every page; heading order, landmarks, and image alt text are
14
- covered in the `pikku-seo` skill and apply app-wide, not just on public pages.
12
+ shadcn components are accessible ONLY when used properly — the rules below are the
13
+ "properly". They apply to every page, public or logged-in. This skill owns heading hierarchy,
14
+ landmarks and image alt text app-wide (below); `pikku-seo` covers only public-route SEO.
15
15
 
16
16
  ## Every input has a label
17
17
 
18
- - Use the `label` prop on every Mantine input — a placeholder is NOT a label (it
18
+ - Give every input a `<Label htmlFor>` — a placeholder is NOT a label (it
19
19
  disappears on input and is never announced as one). Placeholder = example value only.
20
- - Use the `error` and `description` props for validation/help text — Mantine associates
21
- them with the input for screen readers; a loose `<Text c="red">` next to the field
22
- does not.
23
- - Icon-only controls (`ActionIcon`, icon `Button`) MUST have `aria-label={m.key()}`
20
+ - Wire validation and help text to the input with `aria-describedby` (and `aria-invalid`) — a loose
21
+ red paragraph next to the field is not announced with it.
22
+ - Icon-only controls (icon `Button`) MUST have `aria-label={m.key()}`
24
23
  naming the action ("Delete item", not "Trash icon").
25
24
 
26
25
  ## Interactive = a real button or link
27
26
 
28
- - Never `onClick` on a `div`/`Box`/`Card` — it is invisible to keyboard and screen
29
- readers. Use `Button`, `ActionIcon`, `UnstyledButton`, or `<Link>`; navigation is a
27
+ - Never `onClick` on a `div`/`Card` — it is invisible to keyboard and screen
28
+ readers. Use `Button` or `<Link>`; navigation is a
30
29
  link (href), actions are buttons.
31
30
  - Everything reachable by Tab, activatable by Enter/Space. Never remove focus outlines
32
31
  (the theme owns the focus ring), never set `tabIndex` greater than 0, never trap focus
@@ -38,14 +37,14 @@ covered in the `pikku-seo` skill and apply app-wide, not just on public pages.
38
37
 
39
38
  - Status must carry text or an icon, not only a color: a Badge says "Overdue", a form
40
39
  error has a message — a red tint by itself is invisible to colorblind users.
41
- - Contrast comes from the theme; don't undermine it by stacking `c="dimmed"` on small
40
+ - Contrast comes from the theme; don't undermine it by stacking `text-muted-foreground` on small
42
41
  text over tinted backgrounds. Body copy stays at least AA-readable.
43
- - Touch targets: WCAG 2.2 minimum 24px — don't shrink `ActionIcon`/`Checkbox` below
44
- size `sm`, and keep adjacent row actions spaced.
42
+ - Touch targets: WCAG 2.2 minimum 24px — don't shrink icon buttons or checkboxes below
43
+ 24px, and keep adjacent row actions spaced.
45
44
 
46
45
  ## Overlays and motion
47
46
 
48
- - Modals/drawers: use Mantine `Modal`/`Drawer` and ALWAYS pass `title` — that is what
47
+ - Modals/drawers: use shadcn `Sheet`/`Dialog` and ALWAYS include a `Title` — that is what
49
48
  gets announced; focus trap and Escape come built in. (This project uses drawers, not
50
49
  dialogs.)
51
50
  - Landing-page animation (the only custom-CSS surface) respects
@@ -57,3 +56,11 @@ Tab through the page once: every control reachable and visibly focused, every in
57
56
  labeled, every icon button named, every status readable without color. A browser
58
57
  scenario proves the flow works, not that it is reachable without a mouse — this
59
58
  manual pass is the only check that does.
59
+
60
+ ## Headings, landmarks, alt text (app-wide)
61
+
62
+ - Exactly one h1 per page, naming its topic. Below it: h1 → h2 → h3, no skipped levels.
63
+ Never pick a level for its font size — set the size on the correct level
64
+ (`<h2 className="text-sm">`).
65
+ - Landmarks on every page: `<nav>`, `<main>`, `<footer>` where present (render the element itself, `<nav>`, rather than a `<div>` with a role).
66
+ - Every meaningful `<img>` has alt text describing it; decorative images get `alt=""`.
@@ -4,11 +4,11 @@ description: >-
4
4
  Use when creating or consuming reusable function packages (addons) in Pikku. Covers wireAddon,
5
5
  ref(), pikkuAddonServices, pikkuAddonWireServices, addon package structure, addons that ship
6
6
  database tables (pikku db export), generating an addon from an OpenAPI/Swagger spec, and
7
- cross-project function sharing. TRIGGER when: code uses wireAddon/ref()/pikkuAddonServices, user
8
- asks about addons, reusable function packages, cross-project sharing, or addon package structure,
9
- or the user hands over an OpenAPI/Swagger spec (file or URL) to build on. DO NOT
10
- TRIGGER when: user asks about internal function composition (use pikku-wiring) or general function
11
- definitions (use pikku-concepts).
7
+ cross-project function sharing. TRIGGER when: code uses wireAddon/ref()/pikkuAddonServices, the
8
+ user asks about addons, reusable function packages, cross-project sharing or addon package
9
+ structure, or hands over an OpenAPI/Swagger spec (file or URL) to build on. DO NOT TRIGGER when:
10
+ the user asks about internal function composition (use pikku-wiring), general function
11
+ definitions (use pikku-concepts), or importing an n8n export (use pikku-n8n-import).
12
12
  installGroups: [core]
13
13
  ---
14
14
 
@@ -1,5 +1,12 @@
1
1
  # Addon Package Manifest Reference
2
2
 
3
+ ## Contents
4
+
5
+ - Package Structure
6
+ - pikku.config.json
7
+ - package.json (key fields)
8
+ - tsconfig.json (key fields)
9
+
3
10
  `npx pikku new addon` scaffolds these files. You rarely hand-edit them — consult this when wiring exports or config by hand.
4
11
 
5
12
  ## Package Structure
@@ -1,5 +1,16 @@
1
1
  # An addon from an OpenAPI spec
2
2
 
3
+ ## Contents
4
+
5
+ - Recognising one
6
+ - 1 — Look at the spec first
7
+ - 2 — Generate, from the app's root
8
+ - 3 — Check what was generated
9
+ - 4 — Call it from the app
10
+ - 5 — Verify
11
+ - 6 — Check the spec against the real API
12
+ - Then
13
+
3
14
  When you are handed an OpenAPI or Swagger spec — a file, or a URL to one — the
4
15
  API it describes becomes an addon: one function per operation, each with its
5
16
  input and output schemas, behind one service that makes the HTTP calls. Say so
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: pikku-admin-to-fabric
3
- description: 'Port a legacy back-office admin (ActiveAdmin, Django admin, Rails Admin, Laravel Nova, Filament) to Fabric admin screens, driven by a `.knowledge/` Product Blueprint. Covers the admin-DSL→Fabric mapping (resources→screens, index/column→tables, filter→query params, scope→query variants, member_action/collection_action→pikkuFuncs, permit_params→input schemas), the "the admin is half your app" audit, and admin-specific permissions. TRIGGER when: porting/rebuilding a legacy app that has a generated/DSL-driven admin, or the user says "port the admin screens" / "implement the admin". DO NOT TRIGGER when: no legacy admin exists (use pikku-fabric to build screens fresh), or the app is being extended rather than ported (use pikku-build).'
3
+ description: 'Port a legacy back-office admin (ActiveAdmin, Django admin, Rails Admin, Laravel Nova, Filament) to Fabric admin screens, driven by a `.knowledge/` Product Blueprint. Covers the admin-DSL→Fabric mapping (resources→screens, index/column→tables, filter→query params, scope→query variants, member_action/collection_action→pikkuFuncs, permit_params→input schemas), the "the admin is half your app" audit, and admin-specific permissions. TRIGGER when: the legacy app being ported has a generated/DSL-driven admin, or the user says "port the admin screens" / "implement the admin"; used per resource alongside pikku-blueprint-to-fabric, which owns the rest of the port. DO NOT TRIGGER when: no legacy admin exists (use pikku-fabric to build screens fresh), no blueprint exists yet (use pikku-software-archaeology), or the app is being extended rather than ported (use pikku-build).'
4
4
  installGroups: [fabric]
5
5
  argument-hint: '<path to .knowledge/> [resource to port next]'
6
6
  ---
7
7
 
8
8
  # Legacy admin → Fabric admin screens
9
9
 
10
- ## Agent Operating Procedure
10
+ ## Operating procedure
11
11
 
12
12
  1. **Count first.** How many commands cite the admin? That number decides whether this is a chore or a third of the project.
13
13
  2. **The blueprint already has the commands.** Do not re-derive them from the DSL. Map to them.
@@ -71,10 +71,10 @@ grep -rhoE "(member_action|collection_action) :[a-z_]+" app/admin/*.rb | sort -u
71
71
  | Legacy | Also called | Becomes in Fabric |
72
72
  |---|---|---|
73
73
  | `ActiveAdmin.register X` / `class XAdmin` | resource, ModelAdmin, Nova Resource | one TanStack route + one screen |
74
- | `index do … column :x` | `list_display`, `columns()` | a Mantine `Table`/`DataTable`, columns from the query's return type |
74
+ | `index do … column :x` | `list_display`, `columns()` | a shadcn `Table`/data table, columns from the query's return type |
75
75
  | `filter :x` | `list_filter`, `searchable` | typed input fields on the list query |
76
76
  | `scope :active` | `get_queryset` variants, `Nova::Filters` | a named variant of the list query — NOT a new function per scope |
77
- | `form do f.input …` | `fields()`, `fieldsets` | a Mantine form; inputs from the command's zod input schema |
77
+ | `form do f.input …` | `fields()`, `fieldsets` | a shadcn form; inputs from the command's zod input schema |
78
78
  | `permit_params` | `fields`, `$fillable` | you already have this: it is the command's input schema. Cross-check, don't re-derive. |
79
79
  | `member_action :foo` | custom action, `Nova::Actions` | **a `pikkuFunc`** — almost always already in `commands.json` |
80
80
  | `collection_action :foo` | bulk action | a `pikkuFunc` taking a set |
@@ -154,7 +154,7 @@ impersonation and editing an FAQ all guarded identically.
154
154
  - Columns come from the query's return type. If a column exists in the DSL but not
155
155
  in the type, the DSL was computing it in Ruby per row — that is an N+1 wearing a
156
156
  column, and it belongs in the query.
157
- - Reuse the app's Mantine theme. An admin styled differently from the product is
157
+ - Reuse the app's theme. An admin styled differently from the product is
158
158
  how design systems fork.
159
159
  - **Server-computed charts** (chartkick/groupdate and friends) do not port. The
160
160
  aggregation becomes a real query; the plot becomes a chart component. This is the
@@ -190,23 +190,9 @@ Per resource, the parity report records:
190
190
  | "The admin action isn't in commands.json, I'll add it" | Stop. Either the archaeology missed it (fix the blueprint) or it is dead. Both are findings. |
191
191
  | "I'll restyle the admin, it's internal" | An admin off the product's theme is how a design system forks. |
192
192
 
193
- ## Quick Reference
194
-
195
- ```bash
196
- # 1. how much of the app is actually the admin?
197
- node -e "const c=require('./.knowledge/commands.json').commands;console.log(c.filter(x=>(x.evidence||[]).some(e=>(e.file||'').match(/admin/))).length+'/'+c.length)"
198
-
199
- # 2. inventory the capabilities (not the buttons)
200
- grep -rhoE "(member_action|collection_action) :[a-z_]+" app/admin/*.rb | sort -u
201
-
202
- # 3. per resource: map actions -> commands.json, then build the screen
203
- # 4. verify
204
- pikku all && pikku fabric validate --json
205
- ```
206
-
207
193
  ## Related skills
208
194
 
209
195
  - **pikku-software-archaeology** — produces the `.knowledge/` blueprint this needs.
210
196
  - **pikku-blueprint-to-fabric** — the parent port; run this per-domain alongside it.
211
- - **pikku-auth** — roles, ban and impersonation, and the scopes that gate them.
212
- - **pikku-fabric** — screens, theme, Mantine conventions.
197
+ - **pikku-auth** — roles, ban and impersonation; **pikku-permissions** — the scopes that gate them.
198
+ - **pikku-fabric** — screens, theme, shadcn conventions.
@@ -4,11 +4,11 @@ description: >-
4
4
  Use when building AI agents, chatbots or LLM-powered assistants with Pikku — pikkuAgent, ref()
5
5
  tool registration, memory, streaming, tool approval, thread ownership, invocation via rpc.agent,
6
6
  the VercelAgentRunner and its provider map, and the voiceInput/voiceOutput middlewares. TRIGGER
7
- when: code uses pikkuAgent/rpc.agent/runAgent/streamAgent/VercelAgentRunner/voiceInput, user asks
8
- about AI agents, chatbots, tool-calling, agent memory or streaming, model providers, speech in or
9
- out, or `pikku enable agent`. DO NOT TRIGGER when: user asks about MCP tool exposure (use
10
- pikku-wiring), workflows (use pikku-workflow), or general function definitions (use
11
- pikku-concepts).
7
+ when: code uses pikkuAgent/rpc.agent/runAgent/streamAgent/VercelAgentRunner/voiceInput, or the
8
+ user asks about AI agents, chatbots, tool-calling, agent memory or streaming, model providers,
9
+ speech in or out, or `pikku enable agent`. DO NOT TRIGGER when: the user asks about MCP tool
10
+ exposure (use pikku-wiring), workflows (use pikku-workflow), React agent hooks (use pikku-react),
11
+ or general function definitions (use pikku-concepts).
12
12
  installGroups: [core]
13
13
  ---
14
14
 
@@ -49,7 +49,7 @@ session, the credentials and the RPC depth for you.
49
49
  `output` schema, and the picture passed as an `attachments` entry.
50
50
  - **`auth` defaults to `false`**, because agents are normally invoked from an
51
51
  already-authenticated `pikkuFunc`. `scopes` and `permissions` are enforced
52
- either way — see `pikku-auth`.
52
+ either way — see `pikku-permissions`.
53
53
  - **`approvalRequired` sits on the tool function, not on the agent.** The run
54
54
  then resolves `status: 'suspended'` with `pendingApprovals`; answer with
55
55
  `rpc.agent.approve(runId, approvals)`.
@@ -1,5 +1,12 @@
1
1
  # Pikku AI Agent Wiring
2
2
 
3
+ ## Contents
4
+
5
+ - Before You Start
6
+ - API Reference
7
+ - Usage Patterns
8
+ - Complete Example
9
+
3
10
 
4
11
  ## Before You Start
5
12
 
@@ -83,7 +90,7 @@ import cycle.
83
90
 
84
91
  `auth` defaults to `false` because agents are usually invoked from an
85
92
  already-authenticated `pikkuFunc`. `scopes` and `permissions` are enforced either
86
- way — see `pikku-auth`.
93
+ way — see `pikku-permissions`.
87
94
 
88
95
  ### Invoking an agent
89
96
 
@@ -25,7 +25,7 @@ const runner = new VercelAgentRunner(
25
25
 
26
26
  - `stream(params: AgentRunnerParams, channel: AgentStreamChannel): Promise<AgentStepResult>` — Stream AI responses with tool calls
27
27
  - `run(params: AgentRunnerParams): Promise<AgentStepResult>` — Execute a single AI step (non-streaming)
28
- - `transcribe({ model, audio, … })` / `generateSpeech({ model, text, voice, … })` — what `voiceInput`/`voiceOutput` call; see `references/voice.md`
28
+ - `transcribe({ model, audio, … })` / `generateSpeech({ model, text, voice, … })` — what `voiceInput`/`voiceOutput` call; see the voice reference
29
29
  - `generateImage`, `embed`, `embedMany`, `rerank` — the remaining AI SDK surfaces
30
30
  - `withApiKey(apiKey)` — returns a **new** runner built from `providerFactory`; returns `this` unchanged when no factory was supplied or the key is blank. This is the per-user-credential path
31
31
 
@@ -85,7 +85,7 @@ export const assistant = pikkuAgent({
85
85
  ```
86
86
 
87
87
  There is no `wireAgent` — agents are declared with `pikkuAgent` from the
88
- generated agent types. See `references/agents.md` for the full config.
88
+ generated agent types. The full config is in the agents reference.
89
89
 
90
90
  ### Testing without a real provider
91
91
 
@@ -1,5 +1,11 @@
1
1
  # Pikku AI Voice (Speech I/O)
2
2
 
3
+ ## Contents
4
+
5
+ - `@pikku/ai-voice` is deprecated and empty
6
+ - API Reference
7
+ - Usage Pattern
8
+
3
9
 
4
10
  ## `@pikku/ai-voice` is deprecated and empty
5
11
 
@@ -9,7 +15,7 @@ dependency.
9
15
 
10
16
  Voice now lives in **`@pikku/core/agent`** as two AI middlewares, and the
11
17
  speech models are reached through the `agentRunner` (`transcribe` /
12
- `generateSpeech`) rather than through separate services. See `references/runner-vercel.md`.
18
+ `generateSpeech`) rather than through separate services. The runner reference covers that wiring.
13
19
 
14
20
  ## API Reference
15
21
 
@@ -3,7 +3,7 @@ name: pikku-architect
3
3
  description: >-
4
4
  Use to turn one claimed changeset into the technical plan its build is measured against —
5
5
  the tables, functions, wires, roles, scopes, screens and scenarios it owes, split into passes and
6
- written through `pikku knowledge plan set <changeset>`. The plan is the denominator
6
+ written through `pikku knowledge plan set`. The plan is the denominator
7
7
  `pikku knowledge plan progress` divides by, so it is written BEFORE any of the changeset's code
8
8
  exists and never edited afterwards to match what got built. TRIGGER when: `pikku changes
9
9
  claim` says a changeset needs a plan, `changes done` or `pikku changes next` refuses one for having no
@@ -5,6 +5,8 @@ item. These are the ones it cannot, each carried back from a changeset that ship
5
5
  grouped by the question that finds them, and the fastest way to use this file is to read the six
6
6
  questions and only open the group that worries you.
7
7
 
8
+ ## Contents
9
+
8
10
  1. [Is this a plan for THIS note?](#is-this-a-plan-for-this-note)
9
11
  2. [Does pass 1 slice, and does the model fit inside it?](#does-pass-1-slice-and-does-the-model-fit-inside-it)
10
12
  3. [Can each scenario actually be performed?](#can-each-scenario-actually-be-performed)
@@ -1,15 +1,16 @@
1
1
  ---
2
2
  name: pikku-auth
3
3
  description: >-
4
- Use for anything about identity in a Pikku app — authenticating a caller (login, logout,
5
- sessions, cookies, bearer tokens, API keys, JWT, OAuth/social providers, MFA, Better Auth) and
6
- authorizing one (pikkuPermission, pikkuAuth, scopes, defineScope, global permissions). Covers
7
- which of the two a problem actually is, the built-in auth strategies, machine-to-machine auth
8
- and `pikku login`, and the JWT service. TRIGGER when: user asks about login, logout, session,
9
- cookie auth, bearer tokens, API keys, JWT, Better Auth, social providers, restricting who may
10
- call a function, resource ownership, roles, scopes, or hits MissingScopeError or
11
- InvalidSessionError. DO NOT TRIGGER when: user asks about middleware mechanics with no identity
12
- involved (use pikku-middleware) or about secrets and env vars (use pikku-services).
4
+ Use for identity in a Pikku app: authenticating a caller (login, logout, sessions, cookies,
5
+ bearer tokens, API keys, JWT, OAuth/social providers, MFA, Better Auth, machine callers,
6
+ `pikku login`). Covers telling authentication from authorization, the built-in strategies and
7
+ the JWT service. Authorization rules (ownership, roles, scopes, global permissions) live in
8
+ pikku-permissions. TRIGGER when: user asks about login, logout, session, cookie auth, bearer
9
+ tokens, API keys, JWT, Better Auth, social providers, how a CLI/agent/sandbox authenticates,
10
+ or hits InvalidSessionError. DO NOT TRIGGER when: the question is who may call a function,
11
+ such as ownership, roles, per-row checks, scopes or MissingScopeError (use pikku-permissions),
12
+ middleware mechanics with no identity involved (use pikku-middleware), or secrets and env
13
+ vars (use pikku-services).
13
14
  installGroups: [core]
14
15
  ---
15
16
 
@@ -28,7 +29,8 @@ Pikku app.
28
29
  function runs, and ends in a call to `setSession`.
29
30
 
30
31
  **Authorization** answers _may this caller do this_. It is declared on the
31
- function — `scopes` and `permissions` — never checked inside its body.
32
+ function — `scopes` and `permissions` — never checked inside its body. All of it is
33
+ covered by the pikku-permissions skill.
32
34
 
33
35
  A `permissions` entry that verifies a bearer token and returns `true` is
34
36
  authentication wearing an authorization hat: it leaves the function sessionless,
@@ -39,7 +41,7 @@ middleware instead.
39
41
 
40
42
  | You are… | Read |
41
43
  | --- | --- |
42
- | Restricting who may call a function — ownership, roles, scopes | `references/permissions.md` |
44
+ | Restricting who may call a function — ownership, roles, scopes | the pikku-permissions skill |
43
45
  | Reading or setting the session, or wiring `authBearer`/`authCookie`/`authAPIKey` | `references/sessions.md` |
44
46
  | Standing up user sign-in — OAuth, email+password, MFA, organizations | `references/better-auth.md` |
45
47
  | Authenticating a CLI, agent, sandbox or worker — API keys, `pikku login` | `references/machine-auth.md` |
@@ -53,30 +55,8 @@ will not work against them. `references/better-auth.md` has the setup; the
53
55
  built-in strategies in `references/sessions.md` are how a resolved credential
54
56
  becomes a Pikku session, not a replacement for it.
55
57
 
56
- ## The three gates
57
-
58
- Authorization is three independent gates, all of which must pass, in this order:
59
-
60
- 1. **Scopes** (`scopes`) — AND'd, checked before input validation, fails closed.
61
- 2. **Global permissions** (`addGlobalPermission`) — AND'd, an app-wide baseline.
62
- 3. **The function's own `permissions`** — OR'd groups of AND'd entries.
63
-
64
- They are independent: a broad global gate can never satisfy a function's own
65
- requirement, and a scope can only ever narrow access, never grant it.
66
-
67
58
  ## What NOT to do
68
59
 
69
- - **Do not check authorization inside `func`.** The `permissions` field is
70
- visible to the inspector; a check in the body is invisible to `pikku info
71
- permissions` and to an audit. The one sanctioned exception is
72
- `permissionsInBody`, which is purely declarative — see
73
- `references/permissions.md`.
74
- - **Do not write an "is signed in" permission.** `auth: true` (the default on
75
- `pikkuFunc`) already requires the session. A checker returning `!!session`
76
- gates nothing.
77
- - **Do not put `scopes` on a `pikkuSessionlessFunc`.** Scopes fail closed and an
78
- anonymous caller holds none, so it would reject every caller it exists to
79
- serve. Gate those with `permissions`, which receive an optional session.
80
60
  - **Do not expect the built-in strategies to authenticate a non-HTTP caller.**
81
61
  `authBearer`, `authCookie` and `authAPIKey` all step aside when there is no
82
62
  HTTP request — a queue job, a scheduled task or a channel message needs its
@@ -84,6 +64,5 @@ requirement, and a scope can only ever narrow access, never grant it.
84
64
  - **Do not share one header between the human and machine paths.**
85
65
  `Authorization: Bearer` is the human session; `x-api-key` is the machine key.
86
66
  Merging them reintroduces the ambiguity the split exists to remove.
87
- - **Do not reach for wire-, tag- or route-level permissions.** They were removed
88
- in #972. Permissions live on the function, plus the optional global gate; tags
89
- are organizational only.
67
+ - **Do not write authorization here.** Ownership, roles, scopes and global
68
+ permissions belong in the `permissions`/`scopes` fields; see pikku-permissions.
@@ -238,7 +238,7 @@ never needs to know about scopes or roles.
238
238
 
239
239
  ### The plugins `@pikku/better-auth` ships
240
240
 
241
- Five, all imported from the package root and passed to `betterAuth({ plugins })`
241
+ Four, all imported from the package root and passed to `betterAuth({ plugins })`
242
242
  like any other. None is automatic — an app wires the ones it needs.
243
243
 
244
244
  | Plugin | Plugin `id` | Adds | Use it when |
@@ -247,12 +247,11 @@ like any other. None is automatic — an app wires the ones it needs.
247
247
  | `pikkuActor()` | `actor` | `POST /sign-in/actor`, `user.actor` | Scenarios or a dev switcher sign in as a persona |
248
248
  | `pikkuCredentialOAuth()` | `credential-oauth` | `POST /credential-oauth/link`, `/credential-oauth/callback/:providerId` | An app links OAuth2 **API credentials** for a user |
249
249
  | `pikkuDelegatedAuth()` | `delegated-auth` | `POST /sign-in/delegated` | An imported upstream API is the system of record for identity |
250
- | `pikkuFabric()` | `fabric` | `POST /sign-in/fabric` | A Fabric-deployed app lets a control-plane operator in |
251
250
 
252
251
  Every one carries a `pikku` prefix, because a `plugins: [...]` array mixes these
253
252
  with better-auth's own and a bare `actor()` next to `organization()` says
254
253
  nothing about where it came from. The unprefixed names — `ban`, `actor`,
255
- `credentialOAuth`, `delegatedAuth`, `fabric` — are still exported as deprecated
254
+ `credentialOAuth`, `delegatedAuth` — are still exported as deprecated
256
255
  aliases, so existing apps keep working.
257
256
 
258
257
  The plugin's `id` is what better-auth stores; the **export name** is what the
@@ -348,21 +347,6 @@ in the `pikku-build` skill's `references/openapi.md`. When the upstream later
348
347
  refuses the stored token, the addon throws `CredentialRejectedError` (403,
349
348
  `reauth: 'sign-in'`): the UI shows the sign-in again.
350
349
 
351
- #### `pikkuFabric()` — control-plane operator sign-in
352
-
353
- ```typescript
354
- pikkuFabric({ publicKey: FABRIC_AUTH_PUBLIC_KEY, scopeService, logger })
355
- ```
356
-
357
- `POST /sign-in/fabric` verifies a short-lived RS256 token that the Fabric
358
- control plane signed for an operator session, then signs them into a synthetic
359
- `fabric-<id>@fabric.internal` row holding the `admin` scope. Asymmetric on
360
- purpose: the app holds only the public key, so it can never forge an operator
361
- login, and the same `FABRIC_AUTH_PUBLIC_KEY` is distributed to every stage with
362
- no per-environment secret. A missing or empty key disables the endpoint, and a
363
- token whose `purpose` claim is not `fabric-admin` is rejected. Without a
364
- `ScopeService` the operator signs in holding nothing.
365
-
366
350
  ### 2. Production database adapter
367
351
 
368
352
  For real deployments swap `memoryAdapter` for the Kysely adapter backed by an injected DB. Better Auth owns its own tables (`user`, `session`, `account`, `verification`, plus plugin tables) — generate its schema with `npx @better-auth/cli generate` and apply it as a migration.
@@ -534,9 +518,9 @@ an `actor: true` row only under `pikku dev`. With the opt-in set, a stage signs
534
518
  in as the personas the deployment provisioned when it started and refuses
535
519
  everything else (`No actor account exists for that address`), so holding the
536
520
  secret on such a stage does not let anyone invent identities. Those rows are
537
- written by the fabric plugin when an operator asks to act as an address the
538
- stage has no account for, so provisioning needs no actor secret and works on a
539
- stage whose endpoint is shut.
521
+ written by `provisionPersonas` when an operator sign-in route is asked to act
522
+ as an address the stage has no account for, so provisioning needs no actor
523
+ secret and works on a stage whose endpoint is shut.
540
524
 
541
525
  **`SCENARIO_ACTOR_SECRET` is a credential as powerful as the most privileged
542
526
  persona.** Provisioning grants declared roles to actor accounts, so an
@@ -566,32 +550,27 @@ id to `/sign-in/persona`, both served by
566
550
 
567
551
  ### Provisioning personas
568
552
 
569
- Anywhere but `pikku dev`, the accounts have to exist before anyone signs in. The
570
- stage creates them itself, from the personas you hand `pikkuFabric`:
553
+ Anywhere but `pikku dev`, the accounts have to exist before anyone signs in.
554
+ `provisionPersonas` creates them from the generated personas:
571
555
 
572
556
  ```ts
573
- import { pikkuFabric } from '@pikku/better-auth'
557
+ import { provisionPersonas } from '@pikku/better-auth'
574
558
  import {
575
559
  personaConfigs,
576
560
  personaEnvironments,
577
561
  } from '#pikku/pikku-personas.gen.js'
578
562
 
579
- pikkuFabric({
580
- publicKey,
581
- audience,
582
- scopeService,
583
- personas: {
584
- personas: personaConfigs,
585
- environments: personaEnvironments,
586
- },
587
- })
563
+ await provisionPersonas(
564
+ { auth, scopeService, logger },
565
+ { personas: personaConfigs, environments: personaEnvironments }
566
+ )
588
567
  ```
589
568
 
590
- There is nothing else to call and nothing to schedule. The plugin's operator
591
- endpoint resolves the address the caller wants to act as; a miss provisions the
592
- declaration and looks again. On a stage that already holds the persona that is
593
- one query, and the pass only runs when there is genuinely something absent to
594
- create.
569
+ `auth` only needs to resolve `{ $context }`, so the singleton `services.auth`
570
+ works as is. Call it from whatever route resolves the address a caller wants to
571
+ act as: a miss provisions the declaration and looks again. On a stage that
572
+ already holds the persona that is one query, and the pass only runs when there
573
+ is genuinely something absent to create.
595
574
 
596
575
  **Do not reach for `pikkuServerLifecycle`'s `afterStart` for this.** That hook is
597
576
  invoked by `pikku serve` and `pikku dev` and by nothing else — no deploy runtime
@@ -619,16 +598,14 @@ open. By default provisioning warns about those accounts and changes nothing.
619
598
  `orphans: 'ban'` shuts them:
620
599
 
621
600
  ```ts
622
- pikkuFabric({
623
- publicKey,
624
- audience,
625
- scopeService,
626
- personas: {
601
+ await provisionPersonas(
602
+ { auth, scopeService, logger },
603
+ {
627
604
  personas: personaConfigs,
628
605
  environments: personaEnvironments,
629
606
  orphans: 'ban',
630
- },
631
- })
607
+ }
608
+ )
632
609
  ```
633
610
 
634
611
  It writes the same `banned` column the console's ban RPC writes (so it needs the
@@ -1,5 +1,11 @@
1
1
  # Pikku Jose (JWT Service)
2
2
 
3
+ ## Contents
4
+
5
+ - [Installation](#installation)
6
+ - [API Reference](#api-reference)
7
+ - [Usage Patterns](#usage-patterns)
8
+
3
9
 
4
10
  ## Installation
5
11
 
@@ -1,5 +1,11 @@
1
1
  # Pikku Machine Auth
2
2
 
3
+ ## Contents
4
+
5
+ - [Human path — `pikku login`](#human-path-pikku-login)
6
+ - [Machine path — API keys](#machine-path-api-keys)
7
+ - [Gotchas](#gotchas)
8
+
3
9
  Unified authentication for humans **and** machines against a Pikku + better-auth
4
10
  server. Two paths, two headers, one resolver:
5
11
 
@@ -1,5 +1,11 @@
1
1
  # Pikku Security (Authentication & Sessions)
2
2
 
3
+ ## Contents
4
+
5
+ - [Session Management](#session-management)
6
+ - [Built-in Auth Strategies](#built-in-auth-strategies)
7
+ - [Complete Example](#complete-example)
8
+
3
9
 
4
10
  ## Session Management
5
11
 
@@ -1,15 +1,13 @@
1
1
  ---
2
2
  name: pikku-blueprint-to-fabric
3
- description: 'Rebuild a legacy app as a Pikku Fabric app from a `.knowledge/` Product Blueprint (produced by pikku-software-archaeology). Covers the blueprint→Fabric mapping (domains→slices, commands/queries→pikkuFuncs, entities→SQLite migrations, policies→permissions, invariants→DB constraints, workflows→schedulers, frontend-routes→TanStack+Mantine), the decisions gate, and the parity report. TRIGGER when: a `.knowledge/` blueprint exists and the user wants to rebuild/port/recreate that app in Pikku or Fabric, or says "rebuild this from the blueprint". DO NOT TRIGGER when: no blueprint exists (run pikku-software-archaeology first), or the user wants a single new feature in an existing app (use pikku-build).'
3
+ description: 'Rebuild a legacy app as a Pikku Fabric app from a `.knowledge/` Product Blueprint (produced by pikku-software-archaeology). Covers the blueprint→Fabric mapping (domains→slices, commands/queries→pikkuFuncs, entities→SQLite migrations, policies→permissions, invariants→DB constraints, workflows→schedulers, frontend-routes→TanStack+shadcn), the decisions gate, and the parity report. TRIGGER when: a `.knowledge/` blueprint exists and the user wants to rebuild/port/recreate that app in Pikku or Fabric, or says "rebuild this from the blueprint". Admin screens are ported by pikku-admin-to-fabric alongside it. DO NOT TRIGGER when: no blueprint exists (run pikku-software-archaeology first), the user wants a single new feature in an existing app (use pikku-build), or the task is only the legacy admin screens of an already-started port (use pikku-admin-to-fabric).'
4
4
  installGroups: [fabric]
5
5
  argument-hint: '<path to .knowledge/> [domain to slice next]'
6
6
  ---
7
7
 
8
8
  # Blueprint → Fabric
9
9
 
10
- ## Agent Operating Procedure
11
-
12
- Use this skill as an execution checklist, not reference material.
10
+ ## Operating procedure
13
11
 
14
12
  1. **Validate the blueprint before you trust it.** Run the archaeology validator; `0 error(s)` or stop.
15
13
  2. **Clear the decisions gate.** Unresolved `decisionsNeeded` block the domains they touch. Ask; do not invent.
@@ -271,7 +269,7 @@ Two rules:
271
269
 
272
270
  ## Stage 7 — Frontend
273
271
 
274
- Only when `frontend*.json` is present. Target: TanStack Start + Mantine.
272
+ Only when `frontend*.json` is present. Target: TanStack Start + Tailwind + shadcn.
275
273
 
276
274
  `frontend.json` records the legacy stack as facts. **It is context, not a port target** — a bespoke Sass system, a server-rendered template stack, or a different component library all land on the same target. Read `designSystemConsistency` and `designFindings` to know what _not_ to carry: findings are the drift (hardcoded colors, forked-per-locale pages, duplicated components), and the rebuild is the moment they cost nothing to drop.
277
275
 
@@ -287,9 +285,9 @@ Only when `frontend*.json` is present. Target: TanStack Start + Mantine.
287
285
 
288
286
  | `rebuild` | What to do |
289
287
  | --------------------- | ------------------------------------------------------------------------- |
290
- | `mantine-standard` | Use the Mantine component. Do not port. |
291
- | `mantine-composition` | Compose from Mantine primitives. Do not port. |
292
- | `custom-style` | Normalize to Mantine + theme tokens. The divergence is the thing to drop. |
288
+ | `library-standard` | Use the shadcn component. Do not port. |
289
+ | `library-composition` | Compose from shadcn primitives. Do not port. |
290
+ | `custom-style` | Normalize to shadcn + theme tokens. The divergence is the thing to drop. |
293
291
  | **`custom-logic`** | **Port the behaviour.** Read `customLogic` and `dependencies`. |
294
292
 
295
293
  The first three are the bulk and they're cheap — they're a re-expression, not a migration. **`custom-logic` is the actual project**: the bespoke chart, the virtualized table, the map surface, the rich editor, the drag interaction. Each has real behaviour that must survive, and `customLogic` says what it is.
@@ -348,19 +346,6 @@ Per domain:
348
346
  | "Verify once it's all built" | No bisect point. Verify per slice; that's what slices are for. |
349
347
  | "The blueprint has a `low`-confidence entry, I'll build my best guess" | That's inventing product. It's a gate question. |
350
348
 
351
- ## Quick reference
352
-
353
- ```bash
354
- node <archaeology-skill>/scripts/validate.mjs <repo>/.knowledge # Stage 0 — must be 0 errors
355
- # Stage 1 — decisions gate: ask, don't invent
356
- # Stage 2 — clone starter template, then the post-clone cleanup (pikku-build)
357
- # Stage 3 — entities.json -> db/sqlite/NNNN-*.sql ; pikku db migrate
358
- # Stage 4..7 — one domain slice at a time, dependency order
359
- pikku fabric validate --json
360
- yarn pikku all && yarn tsc --noEmit
361
- # Stage 9 — .knowledge/parity-<domain>.md per slice
362
- ```
363
-
364
349
  ## Relationship to the other skills
365
350
 
366
351
  ```
@@ -334,7 +334,7 @@ p('')
334
334
  if (feComponents.length) {
335
335
  p('## Frontend')
336
336
  p('')
337
- p(`- **${cheapComponents.length}** components are a cheap re-expression in Mantine (standard / composition / restyle).`)
337
+ p(`- **${cheapComponents.length}** components are a cheap re-expression in the component library (standard / composition / restyle).`)
338
338
  p(`- **${customLogic.length}** carry \`custom-logic\` and must be **ported**. This is the real frontend project.`)
339
339
  p(`- **${feRoutes.length}** routes → TanStack routes; their \`dataFrom\` names are already the function names above.`)
340
340
  p('')