@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.
- package/CHANGELOG.md +125 -0
- package/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-addon/SKILL.md +10 -9
- package/skills/pikku-agent/SKILL.md +67 -316
- package/skills/pikku-agent/references/agents.md +299 -0
- package/skills/{pikku-ai-vercel/SKILL.md → pikku-agent/references/runner-vercel.md} +2 -24
- package/skills/{pikku-ai-voice/SKILL.md → pikku-agent/references/voice.md} +1 -22
- package/skills/pikku-architect/SKILL.md +264 -0
- package/skills/pikku-auth/SKILL.md +89 -0
- package/skills/{pikku-better-auth/SKILL.md → pikku-auth/references/better-auth.md} +42 -47
- package/skills/{pikku-jose/SKILL.md → pikku-auth/references/jose.md} +1 -23
- package/skills/{pikku-machine-auth/SKILL.md → pikku-auth/references/machine-auth.md} +0 -23
- package/skills/{pikku-permissions/SKILL.md → pikku-auth/references/permissions.md} +5 -24
- package/skills/{pikku-security/SKILL.md → pikku-auth/references/sessions.md} +3 -23
- package/skills/pikku-build/SKILL.md +87 -0
- package/skills/{pikku-build-app/SKILL.md → pikku-build/references/app.md} +77 -25
- package/skills/{pikku-feature/SKILL.md → pikku-build/references/feature.md} +1 -8
- package/skills/{pikku-build-app → pikku-build}/references/multi-app.md +1 -1
- package/skills/{pikku-build-platform/SKILL.md → pikku-build/references/platform.md} +22 -37
- package/skills/{pikku-template-clone/SKILL.md → pikku-build/references/post-clone.md} +0 -6
- package/skills/{pikku-build-quick/SKILL.md → pikku-build/references/quick.md} +6 -22
- package/skills/{pikku-build-app → pikku-build}/references/ship.md +7 -1
- package/skills/pikku-concepts/SKILL.md +75 -8
- package/skills/pikku-concepts/references/concept-mapping.md +8 -8
- package/skills/pikku-deploy/SKILL.md +158 -0
- package/skills/{pikku-deploy-azure/SKILL.md → pikku-deploy/references/azure.md} +18 -50
- package/skills/pikku-deploy/references/cloudflare.md +104 -0
- package/skills/pikku-deploy/references/express.md +92 -0
- package/skills/{pikku-deploy-fastify/SKILL.md → pikku-deploy/references/fastify.md} +8 -32
- package/skills/{pikku-deploy-lambda/SKILL.md → pikku-deploy/references/lambda.md} +6 -27
- package/skills/{pikku-deploy-nextjs/SKILL.md → pikku-deploy/references/nextjs.md} +9 -33
- package/skills/pikku-deploy/references/uws.md +72 -0
- package/skills/pikku-deploy/references/ws.md +75 -0
- package/skills/pikku-emails/SKILL.md +3 -2
- package/skills/pikku-fabric/SKILL.md +20 -10
- package/skills/{pikku-fabric-debug/SKILL.md → pikku-fabric/references/debugging.md} +0 -6
- package/skills/pikku-i18n/SKILL.md +60 -207
- package/skills/{pikku-paraglide/SKILL.md → pikku-i18n/references/enum-labels.md} +0 -6
- package/skills/pikku-i18n/references/messages.md +218 -0
- package/skills/{pikku-rtl/SKILL.md → pikku-i18n/references/rtl.md} +3 -9
- package/skills/pikku-knowledge/SKILL.md +14 -0
- package/skills/pikku-kysely/SKILL.md +13 -13
- package/skills/pikku-meta/SKILL.md +58 -130
- package/skills/{pikku-deps/SKILL.md → pikku-meta/references/audit.md} +1 -17
- package/skills/pikku-meta/references/meta.md +114 -0
- package/skills/{pikku-versioning/SKILL.md → pikku-meta/references/versioning.md} +0 -26
- package/skills/pikku-middleware/SKILL.md +8 -8
- package/skills/pikku-n8n-import/SKILL.md +0 -1
- package/skills/pikku-react/SKILL.md +50 -298
- package/skills/pikku-react/references/client.md +293 -0
- package/skills/{pikku-react-query/SKILL.md → pikku-react/references/react-query.md} +2 -22
- package/skills/{pikku-workflows-client/SKILL.md → pikku-react/references/workflows.md} +1 -22
- package/skills/pikku-scenario/SKILL.md +64 -49
- package/skills/pikku-scenario/references/persona-run.md +148 -0
- package/skills/pikku-service-backends/SKILL.md +154 -0
- package/skills/pikku-service-backends/references/aws.md +106 -0
- package/skills/pikku-service-backends/references/backblaze.md +57 -0
- package/skills/pikku-service-backends/references/mongodb.md +90 -0
- package/skills/pikku-service-backends/references/redis.md +75 -0
- package/skills/pikku-service-backends/references/schema.md +63 -0
- package/skills/pikku-services/SKILL.md +68 -291
- package/skills/{pikku-audit/SKILL.md → pikku-services/references/audit.md} +0 -22
- package/skills/{pikku-config/SKILL.md → pikku-services/references/config.md} +1 -25
- package/skills/{pikku-pino/SKILL.md → pikku-services/references/pino.md} +0 -20
- package/skills/pikku-services/references/services.md +272 -0
- package/skills/pikku-software-archaeology/README.md +5 -1
- package/skills/pikku-software-archaeology/SKILL.md +15 -2
- package/skills/{pikku-product-second-opinion/example/sample-report.md → pikku-software-archaeology/example/second-opinion-sample-report.md} +1 -1
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +1 -1
- package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
- package/skills/{pikku-product-second-opinion/SKILL.md → pikku-software-archaeology/references/second-opinion.md} +4 -9
- package/skills/pikku-webhook/SKILL.md +199 -0
- package/skills/pikku-wiring/SKILL.md +180 -0
- package/skills/{pikku-websocket/SKILL.md → pikku-wiring/references/channel.md} +2 -35
- package/skills/{pikku-cli/SKILL.md → pikku-wiring/references/cli.md} +1 -33
- package/skills/{pikku-gateway-slack/SKILL.md → pikku-wiring/references/gateway-slack.md} +0 -23
- package/skills/{pikku-http/SKILL.md → pikku-wiring/references/http.md} +4 -40
- package/skills/{pikku-mcp/SKILL.md → pikku-wiring/references/mcp.md} +0 -33
- package/skills/{pikku-queue/SKILL.md → pikku-wiring/references/queue.md} +1 -33
- package/skills/{pikku-realtime/SKILL.md → pikku-wiring/references/realtime.md} +2 -25
- package/skills/{pikku-rpc/SKILL.md → pikku-wiring/references/rpc.md} +0 -32
- package/skills/{pikku-schedule/SKILL.md → pikku-wiring/references/scheduler.md} +1 -35
- package/skills/{pikku-trigger/SKILL.md → pikku-wiring/references/trigger.md} +0 -43
- package/skills/pikku-workflow/SKILL.md +3 -3
- package/skills/pikku-aws/SKILL.md +0 -161
- package/skills/pikku-backblaze/SKILL.md +0 -104
- package/skills/pikku-deploy-cloudflare/SKILL.md +0 -123
- package/skills/pikku-deploy-express/SKILL.md +0 -122
- package/skills/pikku-deploy-uws/SKILL.md +0 -144
- package/skills/pikku-mongodb/SKILL.md +0 -113
- package/skills/pikku-product-second-opinion/README.md +0 -43
- package/skills/pikku-redis/SKILL.md +0 -99
- package/skills/pikku-schema-ajv/SKILL.md +0 -83
- package/skills/pikku-schema-cfworker/SKILL.md +0 -82
- package/skills/pikku-ws/SKILL.md +0 -87
- /package/skills/{pikku-build-app → pikku-build}/references/theming.md +0 -0
- /package/skills/{pikku-product-second-opinion/references/report-template.md → pikku-software-archaeology/references/second-opinion-report-template.md} +0 -0
- /package/skills/{pikku-cli/references/complete-example.md → pikku-wiring/references/cli-complete-example.md} +0 -0
- /package/skills/{pikku-http → pikku-wiring}/references/http-options.md +0 -0
- /package/skills/{pikku-realtime/references/other-routes.md → pikku-wiring/references/realtime-other-routes.md} +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Pikku i18n (Paraglide JS)
|
|
2
|
+
|
|
3
|
+
## Agent Operating Procedure
|
|
4
|
+
|
|
5
|
+
Use this skill as an execution checklist, not reference material.
|
|
6
|
+
|
|
7
|
+
1. Every user-facing string in a frontend is a message. Never hardcode display text — add a key to `messages/en.json` and render `m.the__key()`. This holds even when the app ships only English; the messages are the seam a second language slots into later.
|
|
8
|
+
2. One `messages/<locale>.json` per language at the app root (NOT under `src/`), declared in `project.inlang/settings.json`. English (`en`) is `baseLocale` and the only locale until someone adds another. **`baseLocale` stays `en` whatever language the product speaks** — see [The product's language is not the code's language](#the-products-language-is-not-the-codes-language), which is the first thing to read if the brief says the app is not in English.
|
|
9
|
+
3. Messages compile to typed ESM functions in `src/paraglide/` (generated, self-gitignored — never edit or commit it). The Vite plugin compiles during `dev`/`build` with HMR on message edits; run the CLI compile only when you need `tsc` before Vite has ever run.
|
|
10
|
+
4. Validate with the app's own `tsc` then its `build`. The deploy pipeline compiles Paraglide and runs each frontend's `tsc` before building it — an i18n mistake blocks the deploy.
|
|
11
|
+
|
|
12
|
+
## The product's language is not the code's language
|
|
13
|
+
|
|
14
|
+
A brief that says "the entire UI is German, no English strings visible anywhere"
|
|
15
|
+
is a statement about **one** of three separate things, and reading it as a
|
|
16
|
+
statement about the codebase is the single most expensive mistake available in
|
|
17
|
+
this skill. Three axes:
|
|
18
|
+
|
|
19
|
+
| Axis | What it covers | What sets it |
|
|
20
|
+
| --------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
21
|
+
| **Identifiers** | Function, component, type and file names; database tables and columns | Nothing — always English, no setting |
|
|
22
|
+
| **Meta** | `description` / `name` / `title` / `template` authored inside the code, which the Pikku Console renders | `metaLocale` in `pikku.config.json`, default `en` |
|
|
23
|
+
| **Product UI** | Every string the app shows a user | `messages/<locale>.json` + `defaultLocale` — **this axis only** |
|
|
24
|
+
|
|
25
|
+
A non-English product moves the third row and nothing else.
|
|
26
|
+
|
|
27
|
+
### `baseLocale` stays `en`
|
|
28
|
+
|
|
29
|
+
`baseLocale` in `project.inlang/settings.json` does not mean "the language the
|
|
30
|
+
app is in". It names the message **source** — the catalogue every other locale is
|
|
31
|
+
cloned from and translated against. Setting it to the product's language looks
|
|
32
|
+
like it works, because the app does come up in that language, and then:
|
|
33
|
+
|
|
34
|
+
- there is no `en.json`, so `--add-locale` has no catalogue to translate from
|
|
35
|
+
- the app can never gain a second language without re-authoring every key
|
|
36
|
+
- a message missing from a locale falls back to a catalogue nobody wrote
|
|
37
|
+
|
|
38
|
+
The setting that actually decides what a first-time visitor sees is
|
|
39
|
+
`defaultLocale`, held in `apps/app/src/i18n/active.json` in the Fabric app
|
|
40
|
+
template and read by `src/i18n/config.ts`. It is deliberately a separate file
|
|
41
|
+
from `settings.json` for exactly this reason — the source language and the
|
|
42
|
+
served language are different questions.
|
|
43
|
+
|
|
44
|
+
So a German medical portal is **three** settings, not one:
|
|
45
|
+
|
|
46
|
+
```jsonc
|
|
47
|
+
// project.inlang/settings.json — the source catalogue is English
|
|
48
|
+
{ "baseLocale": "en", "locales": ["en", "de"] }
|
|
49
|
+
|
|
50
|
+
// apps/app/src/i18n/active.json — what a visitor opens in
|
|
51
|
+
{ "defaultLocale": "de" }
|
|
52
|
+
|
|
53
|
+
// pikku.config.json — the language the team reads their Console in
|
|
54
|
+
{ "metaLocale": "de" }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
In the Fabric template both of the first two have a command, so you rarely edit
|
|
58
|
+
them by hand:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
fabric i18n --add-locale de # adds "de" to locales, seeds messages/de.json from en.json
|
|
62
|
+
fabric i18n --default-locale de # writes active.json — the app now OPENS in German
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### The failure this is written from
|
|
66
|
+
|
|
67
|
+
A real build, from this template. The brief said the UI was German; the agent
|
|
68
|
+
set `baseLocale: "de"` with `locales: ["de"]` and no `en.json`, then carried the
|
|
69
|
+
same reading into the code — RPC functions `getUebersicht` and
|
|
70
|
+
`getPatientendetail`, components `Zeitstrahl` and `AufmerksamkeitStreifen`,
|
|
71
|
+
helpers `datumDeutsch` and `voraussichtlichFertig`, database tables `vorgang`
|
|
72
|
+
and `ereignis` with German columns.
|
|
73
|
+
|
|
74
|
+
The German UI it was asked for needed none of that. It needed German **values**
|
|
75
|
+
in a catalogue whose keys and source stayed English. What it got instead was a
|
|
76
|
+
project that cannot add a second language and cannot be picked up by anyone who
|
|
77
|
+
does not read German.
|
|
78
|
+
|
|
79
|
+
If you find a project in this state, say so plainly rather than working around
|
|
80
|
+
it: `baseLocale` cannot be repointed without re-keying every message, so it is a
|
|
81
|
+
migration someone has to agree to, not a fix to slip in.
|
|
82
|
+
|
|
83
|
+
## The moving parts (starter-template layout)
|
|
84
|
+
|
|
85
|
+
- `messages/en.json` — flat keys, `{param}` interpolation, inlang message-format:
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
89
|
+
"auth__login__title": "Sign in",
|
|
90
|
+
"auth__login__description": "Welcome back to {name}."
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
Key convention: lower snake_case, `__` (double underscore) between namespace segments, `_` within a segment — `auth__login__title`, `common__email_placeholder`.
|
|
94
|
+
- `project.inlang/settings.json` — `baseLocale`, `locales`, the `@inlang/plugin-message-format` module, `pathPattern: "./messages/{locale}.json"`.
|
|
95
|
+
- `vite.config.ts` — `paraglideVitePlugin({ project: './project.inlang', outdir: './src/paraglide' })` from `@inlang/paraglide-js` (devDependency), FIRST in the plugins array.
|
|
96
|
+
- `src/paraglide/` — compiled output (`messages.js`, `runtime.js`, per-locale `messages/*.js`). Generated; it writes its own `.gitignore`.
|
|
97
|
+
- `src/i18n/config.ts` — locale plumbing, and the ONLY hand-written i18n module: `supportedLocales`/`defaultLocale` (re-exported from `../paraglide/runtime.js`), `detectLocale`, `localeDir` (RTL for ar/he/fa/ur), a reactive locale store (`overwriteGetLocale` bridged to `useSyncExternalStore`), `setActiveLocale`, `useLocale()`. This is not a wrapper over messages — Paraglide's `getLocale()` is a module global with no React reactivity, and this bridges it. Wire `overwriteGetLocale` or `m.*()` will resolve a different locale than the app thinks is active.
|
|
98
|
+
- `tsconfig.json` — `"allowJs": true, "checkJs": false` so `tsc` can consume Paraglide's JSDoc-typed JS output.
|
|
99
|
+
|
|
100
|
+
## Using messages in components
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
import { m } from '../paraglide/messages.js'
|
|
104
|
+
import { useLocale } from '@/i18n/config'
|
|
105
|
+
|
|
106
|
+
function LoginPage() {
|
|
107
|
+
useLocale() // subscribe: re-render m.*() when the locale switches
|
|
108
|
+
return (
|
|
109
|
+
<>
|
|
110
|
+
<Title>{m.auth__login__title()}</Title>
|
|
111
|
+
<Text>{m.auth__login__description({ name: m.app__name() })}</Text>
|
|
112
|
+
</>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- Params: `{name}` in the JSON → `m.auth__login__description({ name })`. Params are typed per message.
|
|
118
|
+
- Any component that renders `m.*()` calls `useLocale()` (bare call is enough); it also returns `{ locale, dir, setLocale }` for switchers.
|
|
119
|
+
- Non-component helpers (formatters, status maps) call `m.some__key()` directly — the functions are plain ESM, no hook needed; the render-time subscription lives in the component that displays the result.
|
|
120
|
+
- Locale switching: the root route persists to localStorage, sets `<html lang dir>` (`localeDir`), and calls `setActiveLocale` — in-SPA re-render, no page reload. Mirror `routes/__root.tsx` in the starter template.
|
|
121
|
+
|
|
122
|
+
## Keys only known at runtime (enum labels, status maps)
|
|
123
|
+
|
|
124
|
+
A DB value picking a label is the one case a generated message can't express.
|
|
125
|
+
Paraglide's README (§ "What about dynamic or CMS-driven keys?") is explicit: use
|
|
126
|
+
an **explicit mapping from value to message function**. Key it on the enum type,
|
|
127
|
+
never `string`:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { m } from '../paraglide/messages.js'
|
|
131
|
+
|
|
132
|
+
const DOCUMENT_STATUS_LABEL: Record<DocumentStatus, () => string> = {
|
|
133
|
+
completed: m.enum__document_status__completed,
|
|
134
|
+
in_progress: m.enum__document_status__in_progress,
|
|
135
|
+
required: m.enum__document_status__required,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// call site — no fallback, because there is no missing case
|
|
139
|
+
DOCUMENT_STATUS_LABEL[status]()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`Record<DocumentStatus, …>` is exhaustive: add a value to the enum without a
|
|
143
|
+
label and the build fails. That is the entire point.
|
|
144
|
+
|
|
145
|
+
**Don't write these maps by hand.** `@pikku/paraglide` generates them from the
|
|
146
|
+
`enum__<group>__<member>` keys in the catalog and types each one against the DB
|
|
147
|
+
enum it mirrors, so a migration adding a status is a compile error rather than a
|
|
148
|
+
map someone forgot. Use the namespace above (singular `enum`, `__` between
|
|
149
|
+
segments) so the generator picks the group up, and read `references/enum-labels.md` before
|
|
150
|
+
adding one.
|
|
151
|
+
|
|
152
|
+
Do NOT write `Record<string, () => string>` with a `?? status` fallback, and do
|
|
153
|
+
NOT index the namespace with a computed key (`m[\`enums__${name}__${value}\`]`).
|
|
154
|
+
Both compile, both render the raw identifier to users when a label is missing,
|
|
155
|
+
and both reintroduce exactly the silent-fallback failure Paraglide exists to
|
|
156
|
+
eliminate. If you find yourself writing a `resolveDynamicKey(key: string)`
|
|
157
|
+
helper, stop — that helper IS the bug.
|
|
158
|
+
|
|
159
|
+
## Type safety — and why deploys block on i18n
|
|
160
|
+
|
|
161
|
+
A message IS a function: a typo'd or deleted key (`m.auth__login__titel()`) is a missing export — a **TypeScript error**, not a silent runtime fallback string. Params are typed too. The deploy pipeline compiles Paraglide then runs each frontend's `tsc` (`"tsc": "tsc --noEmit"` script — keep it in every frontend's `package.json`) **before** building; a type error aborts the deploy. `vite build` does not type-check on its own, so this gate is the only thing standing between a broken message and production.
|
|
162
|
+
|
|
163
|
+
The gate catches _invalid_ messages but not _inlined_ strings. The `@pikku/mantine` `I18nNode` prop typing catches those: a raw string literal fails to compile on a gated prop, because `I18nString` is a branded type a bare `string` can't satisfy. Between the two, `tsc` is the whole safety net — there is no runtime fallback to inspect, by design.
|
|
164
|
+
|
|
165
|
+
## Compile step
|
|
166
|
+
|
|
167
|
+
- **Dev/build:** the Vite plugin compiles automatically; editing `messages/*.json` under a running dev server recompiles + HMRs.
|
|
168
|
+
- **Standalone `tsc` before Vite has run** (fresh clone, CI):
|
|
169
|
+
```sh
|
|
170
|
+
npx @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/paraglide
|
|
171
|
+
```
|
|
172
|
+
This is exactly what the deploy CI does before the per-app `tsc`.
|
|
173
|
+
|
|
174
|
+
## Adding a second language
|
|
175
|
+
|
|
176
|
+
1. `messages/fr.json` mirroring `en.json`'s keys (translate the values, keep `{param}` names identical).
|
|
177
|
+
2. Add `"fr"` to `locales` in `project.inlang/settings.json`. **Leave `baseLocale` at `en`** — step 1 only works because there is an English catalogue to mirror.
|
|
178
|
+
3. Recompile (restart/`vite dev` or the CLI compile). A locale file missing keys falls back to the base locale per message.
|
|
179
|
+
4. Content is reachable via the `/<lang>` URL prefix (`detectLocale` already resolves it); the base locale needs no prefix. Expose the switcher via `useLocale().setLocale`.
|
|
180
|
+
5. Only if the app should **open** in the new language rather than merely offer it: set `defaultLocale` (`active.json` / `fabric i18n --default-locale fr`). Adding a locale and changing the default are different asks — do the second only when asked.
|
|
181
|
+
|
|
182
|
+
## i18n debug mode (find inlined strings)
|
|
183
|
+
|
|
184
|
+
`tsc` catches invalid messages, and the `@pikku/mantine` gate catches raw strings on gated props — but neither sees a hardcoded string in plain JSX, an `aria-label`, `alt`, `document.title`, or anything passed to a non-Mantine component. Debug mode covers that gap: render every message as block glyphs (`█`), and whatever is still readable never went through a message.
|
|
185
|
+
|
|
186
|
+
**Build it as a generated locale, never as a runtime wrapper.** Masked text is text, and rendering different text per locale is what Paraglide already does:
|
|
187
|
+
|
|
188
|
+
1. A script generates `messages/zz.json` from `en.json`, replacing `\S` with `█` while leaving `{placeholders}` intact (they are message inputs — mangling them changes the compiled signature). Run it before `paraglide-js compile`; gitignore the output.
|
|
189
|
+
2. Add `"zz"` to `locales` in `project.inlang/settings.json`.
|
|
190
|
+
3. Switch to it in the locale bridge:
|
|
191
|
+
```ts
|
|
192
|
+
overwriteGetLocale(() => (isI18nDebug() ? 'zz' : activeLocale))
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Keep `zz` out of the app's own `supportedLocales` — that drives URL prefixes, hreflang and any backend `locale` param, none of which should see it.
|
|
196
|
+
|
|
197
|
+
Generate the catalogue in dev only. With `messages/zz.json` absent, Paraglide compiles `zz` to an alias of the base locale (`const zz_x = en_x` — one line per message, no duplicated strings), so a production bundle carries the locale at effectively zero cost.
|
|
198
|
+
|
|
199
|
+
Both the generator and the store bridge are being upstreamed (pikkujs/pikku#1036, #1035).
|
|
200
|
+
|
|
201
|
+
The wrapper alternative — a module that walks the namespace and pipes each message through a `mask()` — is what this replaces. It defeats tree-shaking (touching every export), adds a check on every call, and forces every component to import `m` from the wrapper instead of Paraglide.
|
|
202
|
+
|
|
203
|
+
## What NOT to do
|
|
204
|
+
|
|
205
|
+
- Don't hardcode display strings "just for now" — the message is the work.
|
|
206
|
+
- Don't set `baseLocale` to anything but `en`, whatever language the product speaks. It names the source catalogue, and a project without one can never add a language. Set `defaultLocale` instead.
|
|
207
|
+
- Don't let a non-English UI reach the identifiers. Functions, components, types, files, tables and columns are English in every project; the product's language lives in `messages/*.json` and nowhere else.
|
|
208
|
+
- Don't translate message **keys**. `auth__login__title` stays English in `de.json`; only the value changes.
|
|
209
|
+
- Don't edit or commit anything under `src/paraglide/` — it's regenerated; change `messages/*.json` instead.
|
|
210
|
+
- **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 `@pikku/mantine` `I18nNode` gate natively. A wrapper adds nothing and costs per-message tree-shaking.
|
|
211
|
+
|
|
212
|
+
`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.
|
|
213
|
+
|
|
214
|
+
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.
|
|
215
|
+
|
|
216
|
+
- 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.
|
|
217
|
+
- Don't reach for i18next/react-i18next or a runtime-fetch translation loader — Paraglide's compiled functions are the whole delivery mechanism.
|
|
218
|
+
- Don't tokenize backend error messages or logs here — those are not frontend display strings.
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-rtl
|
|
3
|
-
description: 'Make a Pikku frontend work in both English (LTR) and Arabic / right-to-left languages. Direction is derived from the active locale, applied once at the document root, and the layout mirrors itself — but only if styling is written flow-relative (margin-inline-start, text-align: start, Mantine ms/me) instead of left/right. TRIGGER when: adding Arabic (or Hebrew/Farsi/Urdu), asked to "support RTL / right-to-left / bidi / mirror the layout", or writing layout styles in an app that may run RTL. Builds on pikku-i18n (an RTL language is just another locale file). DO NOT TRIGGER for backend functions or for LTR-only copy changes.'
|
|
4
|
-
installGroups: [client]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
1
|
# Pikku RTL (Arabic + English)
|
|
8
2
|
|
|
9
|
-
This
|
|
3
|
+
This reference sits **on top of** `references/messages.md`. That one compiles a locale's
|
|
10
4
|
messages into typed `m.*()` functions; this one adds the second axis: a locale
|
|
11
5
|
also has a **direction**. Arabic is not special-cased — it is just another
|
|
12
6
|
`messages/ar.json` listed in `project.inlang/settings.json`, plus the document
|
|
@@ -23,7 +17,7 @@ _layout_ code — directional icons still need one manual step, covered below.
|
|
|
23
17
|
## Agent Operating Procedure
|
|
24
18
|
|
|
25
19
|
1. **Messages first.** Every visible string is already an `m.*()` message via
|
|
26
|
-
`
|
|
20
|
+
`references/messages.md`. Arabic copy goes in `messages/ar.json`, mirroring `en.json`'s
|
|
27
21
|
keys with the `{param}` names kept identical.
|
|
28
22
|
2. **Add the direction helper** to the i18n config (one home for locale→dir):
|
|
29
23
|
```ts
|
|
@@ -219,5 +213,5 @@ left` with the flow-relative equivalent; revert any manual `row-reverse`.
|
|
|
219
213
|
- Don't set `dir` on individual components — it belongs on `<html>` so the whole
|
|
220
214
|
document (and Mantine) agrees.
|
|
221
215
|
- Don't translate Arabic copy outside the message system; an RTL language is a
|
|
222
|
-
normal locale, governed by `
|
|
216
|
+
normal locale, governed by `references/messages.md`. There is no `t()` and no i18next in a
|
|
223
217
|
Pikku frontend — the string comes from `m.some__key()`.
|
|
@@ -242,6 +242,20 @@ pikku knowledge index --check # report stale indexes without writing (CI gate)
|
|
|
242
242
|
|
|
243
243
|
`index` rewrites only the block between `<!-- pikku:knowledge-index -->` markers, creating a scaffolded `index.md` for a section that has none. It is idempotent — running it twice changes nothing.
|
|
244
244
|
|
|
245
|
+
### The milestone plan
|
|
246
|
+
|
|
247
|
+
A milestone note says what the app must DO. Its **plan** — JSON beside the note, not prose — says what has to exist for it, and is what a finished build is measured against:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
pikku knowledge plan schema # the format, in full
|
|
251
|
+
pikku knowledge plan set <milestone> <file> # validate and write it
|
|
252
|
+
pikku knowledge plan show <milestone> --for-build # the ordered work a build follows
|
|
253
|
+
pikku knowledge plan progress <milestone> # what it still owes, read from .pikku/
|
|
254
|
+
pikku knowledge plan defer <milestone> <item> -r "<why>"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`progress` reconciles the plan against pikku's generated meta — set membership, never anyone's status — and exits non-zero while the first pass is short, or while anything already built contradicts the plan. Unbuilt work in a later pass is reported, not blocked; a function that shipped wide open against a planned permission rule blocks from any pass, because that is a hole rather than a backlog. Writing a plan is its own seat: read `pikku-architect`. Building against one is `pikku-build`.
|
|
258
|
+
|
|
245
259
|
## Profiles built on this one
|
|
246
260
|
|
|
247
261
|
OKF permits frontmatter fields a reader does not know, and the parser ignores them rather than failing. That is the extension point: a tool layered on Pikku can add its own sections and fields on top of everything above without forking the format.
|
|
@@ -9,9 +9,9 @@ description: >-
|
|
|
9
9
|
non-trivial kysely query (a join, an aggregate/count/sum, groupBy, subquery, transaction, or
|
|
10
10
|
conditional query), the injected `kysely` service is used in a function body, or code uses
|
|
11
11
|
PikkuKysely, KyselyChannelStore, KyselyWorkflowService, KyselySecretService, or the user asks
|
|
12
|
-
about SQL setup with Pikku. DO NOT TRIGGER when: user asks about MongoDB
|
|
13
|
-
|
|
14
|
-
installGroups: [
|
|
12
|
+
about SQL setup with Pikku. DO NOT TRIGGER when: user asks about MongoDB or Redis-backed
|
|
13
|
+
services (use pikku-service-backends).
|
|
14
|
+
installGroups: [core]
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
# Pikku Kysely (SQL Database Services)
|
|
@@ -216,15 +216,15 @@ Each database variant exports these services with a prefix (`Pg`, `MySQL`, `SQLi
|
|
|
216
216
|
A handful more live only on the base package — there is no `Pg`/`MySQL`/`SQLite`
|
|
217
217
|
variant to reach for, you import them from `@pikku/kysely` whatever the engine:
|
|
218
218
|
|
|
219
|
-
| Service | Purpose
|
|
220
|
-
| ---------------------------- |
|
|
221
|
-
| `KyselySessionStore` | Persisted user sessions
|
|
222
|
-
| `KyselyScopeService` | Scope and role storage
|
|
223
|
-
| `KyselyWebhookService` |
|
|
224
|
-
| `KyselyCredentialService` | Encrypted third-party credentials
|
|
225
|
-
| `KyselyAgentRunStateService` | AI run state (also implemented by AIStorage)
|
|
226
|
-
| `KyselyWorkflowMirror` | Mirrors workflow runs into queryable tables
|
|
227
|
-
| `KyselyAuditService` | Durable audit sink (see `pikku-
|
|
219
|
+
| Service | Purpose |
|
|
220
|
+
| ---------------------------- | ------------------------------------------------------------------- |
|
|
221
|
+
| `KyselySessionStore` | Persisted user sessions |
|
|
222
|
+
| `KyselyScopeService` | Scope and role storage |
|
|
223
|
+
| `KyselyWebhookService` | Outgoing webhook deliveries + attempt history (see `pikku-webhook`) |
|
|
224
|
+
| `KyselyCredentialService` | Encrypted third-party credentials |
|
|
225
|
+
| `KyselyAgentRunStateService` | AI run state (also implemented by AIStorage) |
|
|
226
|
+
| `KyselyWorkflowMirror` | Mirrors workflow runs into queryable tables |
|
|
227
|
+
| `KyselyAuditService` | Durable audit sink (see `pikku-services`) |
|
|
228
228
|
|
|
229
229
|
All services take a `Kysely<KyselyPikkuDB>` instance in their constructor and have an `init()` method that creates tables if needed.
|
|
230
230
|
|
|
@@ -257,7 +257,7 @@ const newVersion = await secrets.rotateKEK()
|
|
|
257
257
|
|
|
258
258
|
`getSecret` hands back a `SecretValue<T>`, not the bare value — it serializes as
|
|
259
259
|
`[secret]` until something reveals it, which is what stops a secret drifting into
|
|
260
|
-
a log line or an audit row. See `pikku-
|
|
260
|
+
a log line or an audit row. See `pikku-services` for the reveal rules.
|
|
261
261
|
|
|
262
262
|
## Usage Patterns
|
|
263
263
|
|
|
@@ -1,139 +1,67 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-meta
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
Use to inspect or evolve a project you did not just write — `pikku meta` and `pikku info` for
|
|
5
|
+
what the project declares (functions, schemas, wires, workflows, middleware, permissions) and
|
|
6
|
+
`pikku meta apply` to change it, `pikku versions` / `pikku semver` for contract hashes,
|
|
7
|
+
breaking-change detection and the semver a release should get, and `pikku audit` / `pikku
|
|
8
|
+
update` for dependency advisories and moving Pikku forward. TRIGGER when: user asks what
|
|
9
|
+
functions or routes exist, wants a function's input/output shape, wants to retag a function or
|
|
10
|
+
set config on a declaration, asks about API versioning, breaking changes, what semver a release
|
|
11
|
+
deserves, dependency vulnerabilities, the console Security screen, or upgrading Pikku. DO NOT
|
|
12
|
+
TRIGGER when: user is writing a new function or wiring (use the wiring skill) or asking about
|
|
13
|
+
Pikku concepts (use pikku-concepts).
|
|
11
14
|
installGroups: [core]
|
|
12
15
|
allowed-tools: Bash(yarn pikku meta *), Bash(yarn pikku info *)
|
|
13
|
-
argument-hint: '[context|functions|schemas|workflows|middleware|permissions|wires|apply]'
|
|
16
|
+
argument-hint: '[context|functions|schemas|workflows|middleware|permissions|wires|apply|versions|semver|audit|update]'
|
|
14
17
|
---
|
|
15
18
|
|
|
16
19
|
# Pikku Project Metadata
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"kind": "functionConfig",
|
|
66
|
-
"sourceFile": "src/functions/todos.functions.ts",
|
|
67
|
-
"exportedName": "listTodos",
|
|
68
|
-
"changes": { "title": "List Todos", "tags": ["todos", "read"] }
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
{
|
|
72
|
-
"kind": "functionConfig",
|
|
73
|
-
"sourceFile": "src/functions/todos.functions.ts",
|
|
74
|
-
"exportedName": "listTodos",
|
|
75
|
-
"changes": {
|
|
76
|
-
"permissions": {
|
|
77
|
-
"functionLevel": {
|
|
78
|
-
"name": "isTodoOwner",
|
|
79
|
-
"from": "../permissions.js"
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Three kinds: `functionConfig`, `agentConfig`, `functionBody`. Every operation names
|
|
89
|
-
a `sourceFile` and the `exportedName` declared in it.
|
|
90
|
-
|
|
91
|
-
`functionConfig` changes: `title`, `description`, `summary`, `tags`, `errors`,
|
|
92
|
-
`expose`, `remote`, `mcp`, `readonly`, `approvalRequired`, `permissions`.
|
|
93
|
-
`agentConfig` changes: `name`, `description`, `instructions`, `role`, `personality`,
|
|
94
|
-
`goal`, `model`, `maxSteps`, `temperature`, `toolChoice`, `tools`, `tags`.
|
|
95
|
-
|
|
96
|
-
`null` removes a property. Edits are spliced into the original text, so formatting,
|
|
97
|
-
comments and JSDoc survive.
|
|
98
|
-
|
|
99
|
-
`permissions` and `tools` are written as identifiers rather than literals, so each
|
|
100
|
-
one carries the module it comes from (`{"name": "isTodoOwner", "from": "../permissions.js"}`)
|
|
101
|
-
and the missing import is added for you — widening an existing import from that
|
|
102
|
-
module rather than adding a second one.
|
|
103
|
-
|
|
104
|
-
### Why batch
|
|
105
|
-
|
|
106
|
-
The whole batch either lands or it does not: every operation is resolved before
|
|
107
|
-
anything is written, so a failure leaves every file untouched and names the
|
|
108
|
-
operation that caused it. Batching is also what makes one codegen pass correct —
|
|
109
|
-
**run `pikku all` once after the batch**, not once per property. The response tells
|
|
110
|
-
you whether it is needed:
|
|
111
|
-
|
|
112
|
-
```json
|
|
113
|
-
{
|
|
114
|
-
"schemaVersion": "meta-apply.v1",
|
|
115
|
-
"applied": 2,
|
|
116
|
-
"files": ["src/functions/todos.functions.ts"],
|
|
117
|
-
"generatedMetaIsStale": true
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## Human-readable tables (`pikku info`)
|
|
122
|
-
|
|
123
|
-
Four subcommands only — `functions`, `tags`, `middleware`, `permissions`. Routes,
|
|
124
|
-
channels, schedulers and queues are not subcommands; they are the _transport_ column
|
|
125
|
-
of `info functions --verbose`.
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
yarn pikku info functions --verbose --silent
|
|
129
|
-
yarn pikku info tags --silent
|
|
130
|
-
yarn pikku info middleware --verbose --silent
|
|
131
|
-
yarn pikku info permissions --verbose --silent
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
`--silent` suppresses the banner and inspector diagnostics. It works, but it is not
|
|
135
|
-
declared as an option, so every run also prints `Warning: Unknown option: --silent
|
|
136
|
-
(ignored)` — the warning is wrong. Ignore that one line.
|
|
137
|
-
|
|
138
|
-
`--limit N` caps rows (default 50); the footer says how many were withheld.
|
|
139
|
-
On `tags`, `--verbose` swaps counts for names; elsewhere it adds columns.
|
|
21
|
+
The project already knows what it declares. Ask it rather than grepping for it,
|
|
22
|
+
and change it through the write path rather than by hand.
|
|
23
|
+
|
|
24
|
+
## Pick the reference
|
|
25
|
+
|
|
26
|
+
| You are… | Read |
|
|
27
|
+
| --- | --- |
|
|
28
|
+
| Asking what exists, or setting config on a declaration | `references/meta.md` |
|
|
29
|
+
| Versioning a contract, or deciding a release's semver | `references/versioning.md` |
|
|
30
|
+
| Chasing a dependency advisory, or upgrading Pikku | `references/audit.md` |
|
|
31
|
+
|
|
32
|
+
## Start with `pikku meta context`
|
|
33
|
+
|
|
34
|
+
It answers in one call what a planner needs — functions, wires, middleware,
|
|
35
|
+
permissions, workflows, capabilities, layout. Reach for `pikku meta` when you
|
|
36
|
+
are going to act on the output and `pikku info` when a person will read it;
|
|
37
|
+
they are the same ground in two shapes.
|
|
38
|
+
|
|
39
|
+
## Direction decides whether a change is breaking
|
|
40
|
+
|
|
41
|
+
An input is contravariant (the caller writes it) and an output is covariant (the
|
|
42
|
+
caller reads it), so the same edit is not the same event on both. Adding a
|
|
43
|
+
required field breaks an input and is compatible on an output; making a field
|
|
44
|
+
optional is the reverse. `pikku semver` reads the generated JSON Schemas with
|
|
45
|
+
that asymmetry built in, so let it decide rather than eyeballing a diff.
|
|
46
|
+
|
|
47
|
+
## What NOT to do
|
|
48
|
+
|
|
49
|
+
- **Do not infer a function's input or output by reading its body**, and do not
|
|
50
|
+
cast a call site to make it compile. The schema is the type; `pikku meta
|
|
51
|
+
functions get <id>` has it.
|
|
52
|
+
- **Do not expect an unversioned function to be promoted for you.** Without an
|
|
53
|
+
explicit `version: 2` it is version 1 of its contract, collides with the
|
|
54
|
+
pinned `@v1`, and `pikku versions check` reports the published contract as
|
|
55
|
+
modified.
|
|
56
|
+
- **Do not reach for `override` by default.** The contract key already drops a
|
|
57
|
+
matching `V<n>` suffix from the export name, so `getBookV1` keys under
|
|
58
|
+
`getBook`. `override` is for an export that cannot follow that convention.
|
|
59
|
+
- **Do not shell out to the package manager from a function.** The audit is a
|
|
60
|
+
generated artifact — read `.pikku/audit.json` through
|
|
61
|
+
`metaService.readFile('audit.json')`.
|
|
62
|
+
- **Do not redeclare the audit report's shape.** `SecurityAuditReport` and its
|
|
63
|
+
companions come from `@pikku/core`; the CLI writes it, the addon reads it, the
|
|
64
|
+
UI renders it.
|
|
65
|
+
- **Do not treat a failed audit run as a clean one.** `bun audit` exits non-zero
|
|
66
|
+
when it *finds* advisories and still writes its payload, so non-zero with
|
|
67
|
+
output is data; non-zero with no output throws on purpose.
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pikku-deps
|
|
3
|
-
description: >-
|
|
4
|
-
Use for the Pikku dependency security audit: the `pikku audit` CLI command, the
|
|
5
|
-
`.pikku/audit.json` artifact, the `SecurityAuditReport` type in @pikku/core, and the console
|
|
6
|
-
Security screen (getSecurityAudit / runSecurityAudit / updateDependency + SecurityAuditView).
|
|
7
|
-
Also covers `pikku update`, which moves the @pikku/* dependency set forward and reports the
|
|
8
|
-
peers those versions need.
|
|
9
|
-
TRIGGER when: user asks about `pikku audit` or `pikku update`, dependency
|
|
10
|
-
vulnerabilities/advisories, outdated dependencies, upgrading Pikku itself, peer dependency
|
|
11
|
-
conflicts, the Security screen/page in the console, updating a vulnerable dependency, or
|
|
12
|
-
reading/rendering audit.json. DO NOT TRIGGER when: user asks about authentication/sessions/JWT
|
|
13
|
-
(use pikku-security), permissions (use pikku-permissions), or secrets/env vars (use
|
|
14
|
-
pikku-config).
|
|
15
|
-
---
|
|
16
|
-
|
|
17
1
|
# Pikku Dependency Audit
|
|
18
2
|
|
|
19
3
|
## Agent Operating Procedure
|
|
@@ -98,7 +82,7 @@ lockfiles, because that field states intent before a lockfile exists and a
|
|
|
98
82
|
project can carry a stale one from another tool. Guessing wrong is not a soft
|
|
99
83
|
failure: the spawn dies with `Executable not found in $PATH`.
|
|
100
84
|
Like every console RPC these require an **authenticated session** (the console
|
|
101
|
-
is admin-only), so the host must have Better Auth wired — see `pikku-
|
|
85
|
+
is admin-only), so the host must have Better Auth wired — see `pikku-auth`.
|
|
102
86
|
|
|
103
87
|
- `getSecurityAudit` — reads `.pikku/audit.json`, returns the report (or `null`).
|
|
104
88
|
- `runSecurityAudit` — runs `pikku audit --outdated` server-side (regenerates the
|