@pikku/skills 0.12.34 → 0.12.37
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/README.md +9 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +9 -5
- package/dist/skills.gen.d.ts +1 -0
- package/dist/skills.gen.js +5 -3
- package/dist/snippets.d.ts +26 -0
- package/dist/snippets.js +148 -0
- package/package.json +2 -2
- package/skills/pikku-addon/SKILL.md +41 -30
- package/skills/pikku-addon/references/addon-package-manifest.md +9 -4
- package/skills/pikku-addon/references/openapi.md +99 -0
- package/skills/pikku-agent/references/agents.md +3 -1
- package/skills/pikku-architect/SKILL.md +12 -0
- package/skills/pikku-auth/references/better-auth.md +16 -0
- package/skills/pikku-build/SKILL.md +29 -0
- package/skills/pikku-build/references/app.md +52 -4
- package/skills/pikku-build/references/feature.md +23 -96
- package/skills/pikku-build/references/quick.md +12 -3
- package/skills/pikku-changes/SKILL.md +172 -0
- package/skills/pikku-concepts/SKILL.md +33 -138
- package/skills/pikku-concepts/references/bootstrap.md +58 -0
- package/skills/pikku-concepts/references/concept-mapping.md +16 -0
- package/skills/pikku-concepts/references/language.md +87 -0
- package/skills/pikku-deploy/SKILL.md +1 -1
- package/skills/pikku-fabric/SKILL.md +26 -13
- package/skills/pikku-guide/SKILL.md +264 -0
- package/skills/pikku-knowledge/SKILL.md +10 -0
- package/skills/pikku-kysely/SKILL.md +1 -1
- package/skills/pikku-mantine/SKILL.md +80 -0
- package/skills/pikku-n8n-import/SKILL.md +4 -3
- package/skills/pikku-react/references/client.md +12 -0
- package/skills/pikku-realtime/SKILL.md +6 -6
- package/skills/pikku-report/SKILL.md +143 -0
- package/skills/pikku-scenario/SKILL.md +71 -563
- package/skills/pikku-scenario/references/browser.md +59 -0
- package/skills/pikku-scenario/references/coverage.md +70 -0
- package/skills/pikku-scenario/references/personas.md +87 -0
- package/skills/pikku-scenario/references/steps.md +366 -0
- package/skills/pikku-service-backends/SKILL.md +1 -1
- package/skills/pikku-wiring/SKILL.md +1 -1
- package/skills/pikku-wiring/references/http.md +8 -0
- package/skills/pikku-wiring/references/mcp.md +59 -0
- package/skills/pikku-workflow/SKILL.md +7 -8
|
@@ -494,6 +494,35 @@ Rules that are not optional:
|
|
|
494
494
|
render the failure inline next to the control that triggered it — not a toast.
|
|
495
495
|
- An exposed function with no session and no permission is reachable by anyone
|
|
496
496
|
over `POST /rpc/:rpcName` (PKU574). Either gate it or drop `expose: true`.
|
|
497
|
+
- A public, signed-out read (a homepage's programme, a price list) is a
|
|
498
|
+
`pikkuSessionlessFunc`. `pikkuFunc` with `auth: false` still answers
|
|
499
|
+
`MissingSessionError` over `/rpc` to a caller with no session.
|
|
500
|
+
- Better Auth already owns the `user`, `session`, `account` and `verification`
|
|
501
|
+
tables. A domain table with one of those names — a class *session*, a drop-in
|
|
502
|
+
*session* — collides in the migration. Name it for the domain instead
|
|
503
|
+
(`evening`, `class_meeting`) and keep the word in the UI copy.
|
|
504
|
+
- The template's `/` redirects to `/app`, so the login screen — and its "Sign in
|
|
505
|
+
as …" switcher — is what a signed-out visitor sees first. Replace `/` with a
|
|
506
|
+
public homepage and that stops being true: mount `<DevActorSwitcher />` in the
|
|
507
|
+
public layout as well, or a reviewer lands on a site with no way in.
|
|
508
|
+
|
|
509
|
+
Before the first run, make sure `.env` at the project root holds the two
|
|
510
|
+
secrets the local stack needs. `bun run dev` appends whichever is missing, but
|
|
511
|
+
check anyway — a project scaffolded from an older template, or a `.env` copied
|
|
512
|
+
in from elsewhere, can lack one, and neither failure names the variable:
|
|
513
|
+
|
|
514
|
+
- `BETTER_AUTH_SECRET` — without it the first sign-up is a 500.
|
|
515
|
+
- `SCENARIO_ACTOR_SECRET` — without it `/api/auth/sign-in/actor` is disabled:
|
|
516
|
+
every scenario fails at sign-in before its first step, and the "Sign in as …"
|
|
517
|
+
switcher renders nothing.
|
|
518
|
+
|
|
519
|
+
```sh
|
|
520
|
+
grep -q '^BETTER_AUTH_SECRET=' .env 2>/dev/null || echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" >> .env
|
|
521
|
+
grep -q '^SCENARIO_ACTOR_SECRET=' .env 2>/dev/null || echo "SCENARIO_ACTOR_SECRET=$(openssl rand -base64 32)" >> .env
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
`.env` is gitignored and local only — never commit it. A deployed stage gets its
|
|
525
|
+
secrets from the platform (`pikku fabric secrets`), not from this file.
|
|
497
526
|
|
|
498
527
|
Then run it:
|
|
499
528
|
|
|
@@ -505,6 +534,19 @@ That starts the API on :3000 and every frontend in `pikkufabric.config.json`. A
|
|
|
505
534
|
frontend running against a dead API looks exactly like an app bug, so if every
|
|
506
535
|
request fails, check that both halves came up.
|
|
507
536
|
|
|
537
|
+
**Start the stack through `bun run dev`, not by launching `vite` or `pikku dev`
|
|
538
|
+
yourself.** The dev script reads the personas, derives one credential per
|
|
539
|
+
persona from `SCENARIO_ACTOR_SECRET`, and hands both to the frontend as
|
|
540
|
+
`VITE_DEV_ACTORS` / `VITE_DEV_ACTOR_SECRETS`. Vite reads those once, at boot. A
|
|
541
|
+
frontend started any other way — or restarted by hand later — has an empty
|
|
542
|
+
actor list, and the switcher silently disappears from every page. If you do
|
|
543
|
+
start the frontend on its own (say :3000 is taken by another project), you owe
|
|
544
|
+
it three things: the two `VITE_DEV_*` values the dev script would have computed,
|
|
545
|
+
and `VITE_API_PROXY` pointing at your API — the dev proxy defaults to
|
|
546
|
+
`http://localhost:3000`, so beside another project's server your sign-ins go to
|
|
547
|
+
*its* API and come back `401 Invalid actor secret`, which reads like a bad
|
|
548
|
+
credential rather than the wrong server.
|
|
549
|
+
|
|
508
550
|
The `--bun` in `bunx --bun pikku …` is load-bearing — keep it. Without it the
|
|
509
551
|
CLI's `#!/usr/bin/env node` shebang hands the process to whatever Node is on
|
|
510
552
|
PATH, which fails below Node 24 with `ERR_UNKNOWN_BUILTIN_MODULE: No such
|
|
@@ -614,10 +656,11 @@ export const tenantReportsAFaultScenario = pikkuScenario<void, { id: string }>({
|
|
|
614
656
|
- **Write the refusals.** The third step above is the whole point of §4: one
|
|
615
657
|
persona reaching for another's row has to be rejected, and that rejection is a
|
|
616
658
|
scenario. It is how you prove access control instead of asserting it.
|
|
617
|
-
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
659
|
+
- **`SCENARIO_ACTOR_SECRET` must be in `.env`** (§6, before the first run).
|
|
660
|
+
Without it `/api/auth/sign-in/actor` is disabled — every scenario then fails
|
|
661
|
+
at sign-in, before its first step, for a reason that reads like an auth bug.
|
|
662
|
+
`pikku scenario run` reads it from the environment, so source `.env` first
|
|
663
|
+
(`set -a && . ./.env && set +a`) when you run outside `bun run dev`.
|
|
621
664
|
- **There is no state reset.** A scenario runs against a live server: scope what
|
|
622
665
|
you create to your own rows and unique ids, and never assume a clean database.
|
|
623
666
|
|
|
@@ -786,6 +829,11 @@ standalone`, `cloudflare`, `aws`), how to serve several frontends behind one
|
|
|
786
829
|
API, the pre-release gate to run, and the contract that keeps `pikku fabric init`
|
|
787
830
|
a one-command import later rather than a migration.
|
|
788
831
|
|
|
832
|
+
The app is not handed over without its user guide. The scenarios you wrote are
|
|
833
|
+
already its skeleton: read **pikku-guide**, write one page per audience citing
|
|
834
|
+
every feature, and build it from a full, passing `--run browser --screenshots`
|
|
835
|
+
run.
|
|
836
|
+
|
|
789
837
|
Two things from it are worth knowing before you get there, because they are
|
|
790
838
|
cheaper to honour than to retrofit:
|
|
791
839
|
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Pikku Create-a-Feature
|
|
2
2
|
|
|
3
|
+
The stages, in order:
|
|
4
|
+
|
|
5
|
+
- [Agent Operating Procedure](#agent-operating-procedure)
|
|
6
|
+
- [Stage 1 — Discover](#stage-1--discover)
|
|
7
|
+
- [Stage 2 — State intent in plain English (BEFORE writing code)](#stage-2--state-intent-in-plain-english-before-writing-code)
|
|
8
|
+
- [Stage 3 — Branch off](#stage-3--branch-off)
|
|
9
|
+
- [Stage 4 — Implement](#stage-4--implement)
|
|
10
|
+
- [Stage 5 — Verify](#stage-5--verify)
|
|
11
|
+
- [Stage 6 — Commit](#stage-6--commit)
|
|
12
|
+
- [Stage 7 — Hand off](#stage-7--hand-off)
|
|
13
|
+
- [Report what fought you](#report-what-fought-you)
|
|
14
|
+
- [Hard constraints](#hard-constraints)
|
|
15
|
+
- [Output discipline](#output-discipline)
|
|
16
|
+
|
|
3
17
|
## Agent Operating Procedure
|
|
4
18
|
|
|
5
19
|
Use this skill as an execution checklist, not reference material.
|
|
@@ -7,7 +21,7 @@ Use this skill as an execution checklist, not reference material.
|
|
|
7
21
|
1. Discover before editing. Run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
|
|
8
22
|
2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
|
|
9
23
|
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
10
|
-
4. Validate with the narrowest relevant command first, then run `pikku
|
|
24
|
+
4. Validate with the narrowest relevant command first, then run `pikku all` when functions, wirings, schemas, or generated clients may have changed.
|
|
11
25
|
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
12
26
|
6. Report anything about pikku itself that cost you time, the moment it happens — see **Report what fought you**.
|
|
13
27
|
|
|
@@ -242,103 +256,16 @@ Do not push without explicit confirmation. Do not merge.
|
|
|
242
256
|
|
|
243
257
|
## Report what fought you
|
|
244
258
|
|
|
245
|
-
When pikku itself is what cost you time
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
**Report at the moment it happens**, not at the end from memory — a run that
|
|
250
|
-
falls over never reaches its end. One finding per thing that fought you.
|
|
251
|
-
|
|
252
|
-
### The ladder
|
|
253
|
-
|
|
254
|
-
1. **Find the quicker workaround.** The user is paying for their feature, not
|
|
255
|
-
for pikku's health.
|
|
256
|
-
2. **Investigate** only when there is no workaround, or when the user asks why
|
|
257
|
-
something is slow or wrong.
|
|
258
|
-
3. **Report at the depth you already reached.** Never spend extra effort to
|
|
259
|
-
file; never throw away effort you already spent. If the investigation took
|
|
260
|
-
you to the mechanism, the finding says so — named file, named function, what
|
|
261
|
-
is actually happening, and what pikku should do instead.
|
|
262
|
-
|
|
263
|
-
**Never fix pikku itself.** Not a patch in `node_modules`, not a linked
|
|
264
|
-
checkout, not a branch in the framework repo. Many agents each patching pikku to
|
|
265
|
-
unblock themselves is many divergent copies and a merge problem nobody signed up
|
|
266
|
-
for. Work around it in the app, report it, and let the fix happen once.
|
|
267
|
-
|
|
268
|
-
### What counts
|
|
269
|
-
|
|
270
|
-
Anything that cost you time and would cost the next person the same. Most of
|
|
271
|
-
these never produce an error: output that is quietly wrong, a generated type
|
|
272
|
-
that disagrees with the runtime, a check that passes when it should not, a
|
|
273
|
-
narrowing you had to write by hand because the framework should have written it.
|
|
274
|
-
**Having to write code the framework should have written for you is a finding.**
|
|
275
|
-
|
|
276
|
-
So is anything that only shows up in one place — invisible locally, fatal
|
|
277
|
-
deployed, or the reverse. Say which, with `--surface`.
|
|
278
|
-
|
|
279
|
-
Not a finding: a preference, a thing you would have designed differently, or
|
|
280
|
-
baseline noise that was already failing before you started.
|
|
281
|
-
|
|
282
|
-
### Two kinds
|
|
283
|
-
|
|
284
|
-
- `--kind product` — pikku behaved wrongly. Fixing it is a change to the
|
|
285
|
-
framework.
|
|
286
|
-
- `--kind harness` — a skill misled you: it told you to run something that does
|
|
287
|
-
not exist, described a flag that is spelled differently, or contradicted what
|
|
288
|
-
the CLI actually did. Pass `--skill <name>` and `--passage "<the line or
|
|
289
|
-
section>"`. This is the most useful kind to file, because it is fixable
|
|
290
|
-
immediately — so file it even when the cost was small.
|
|
291
|
-
|
|
292
|
-
### When there was no workaround
|
|
293
|
-
|
|
294
|
-
Report it anyway with `--unresolved`, and put what you tried and how each
|
|
295
|
-
attempt failed in `--tried`. That is what stops the next person walking the same
|
|
296
|
-
dead ends. Tell the user what you did instead — abandoned it, shipped something
|
|
297
|
-
degraded, or stopped.
|
|
298
|
-
|
|
299
|
-
`--unresolved` means **no workaround was found**. It does not mean the
|
|
300
|
-
workaround was unpleasant.
|
|
301
|
-
|
|
302
|
-
### The command
|
|
303
|
-
|
|
304
|
-
Send it as JSON on stdin. Most of a finding is prose, and prose carries
|
|
305
|
-
apostrophes, quotes, backticks and newlines — each one a shell metacharacter
|
|
306
|
-
before it is a character in your sentence. A stack trace passed to `--error`
|
|
307
|
-
breaks the command at its first newline; a backtick in `--actual` runs whatever
|
|
308
|
-
follows it. Quote the heredoc delimiter (`<<'EOF'`, never `<<EOF`) so the shell
|
|
309
|
-
leaves the body alone.
|
|
310
|
-
|
|
311
|
-
```bash
|
|
312
|
-
pikku fabric report --stdin <<'EOF'
|
|
313
|
-
{
|
|
314
|
-
"title": "<one-line title>",
|
|
315
|
-
"kind": "product",
|
|
316
|
-
"model": "<the model you are>",
|
|
317
|
-
"expected": "<what you expected pikku to do>",
|
|
318
|
-
"actual": "<what it did instead>",
|
|
319
|
-
"command": "<the command you ran>",
|
|
320
|
-
"workaround": "<what you did instead, inside the app>"
|
|
321
|
-
}
|
|
322
|
-
EOF
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
Add whichever of these you actually have: `error` (the error's message line,
|
|
326
|
-
verbatim), `repro` (the shortest way to reach it again), `proposal` (what pikku
|
|
327
|
-
should do), `area`, `surface` (`local`, `deployed` or `both`), `cost` (measured
|
|
328
|
-
if you measured it — "98s vs 20s steady" ranks; "slow" does not), `run` (an id
|
|
329
|
-
shared by every finding from this build), `deployTarget`.
|
|
330
|
-
|
|
331
|
-
The same fields exist as flags — `--kind`, `--expected` and so on — for a
|
|
332
|
-
finding short enough to type. Anything with a newline or a quote in it goes
|
|
333
|
-
through `--stdin`.
|
|
259
|
+
When pikku itself is what cost you time — a wrong generated type, a check that
|
|
260
|
+
passed when it should not, a skill that misled you — file it with `pikku fabric
|
|
261
|
+
report`. The `pikku-report` skill owns the ladder, the two kinds, the JSON-on-
|
|
262
|
+
stdin form and the local spool; read it before filing.
|
|
334
263
|
|
|
335
|
-
|
|
336
|
-
|
|
264
|
+
Reporting at all is permitted here (see **Hard constraints**) and is the one
|
|
265
|
+
network call a build may make. Nothing is written to the repo. Never patch pikku
|
|
266
|
+
itself — not `node_modules`, not a linked checkout — work around it in the app,
|
|
267
|
+
report it, and let the fix happen once.
|
|
337
268
|
|
|
338
|
-
Reporting never fails a build. A finding that cannot be sent — logged out, or
|
|
339
|
-
fabric unreachable — is held on the machine and goes out with the next report
|
|
340
|
-
that succeeds, so nothing you file is lost. If it says the finding was queued,
|
|
341
|
-
carry on with the feature; do not try to fix it, and do not file it again.
|
|
342
269
|
|
|
343
270
|
## Hard constraints
|
|
344
271
|
|
|
@@ -149,7 +149,17 @@ than they save:
|
|
|
149
149
|
keystroke now and a rewrite later.
|
|
150
150
|
- Never hardcode a host or port — the API base resolves to same-origin `/api`.
|
|
151
151
|
|
|
152
|
-
|
|
152
|
+
Before the first run, make sure `.env` holds both local secrets — `bun run dev`
|
|
153
|
+
appends missing ones, but an older scaffold or a copied `.env` may not have them:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
grep -q '^BETTER_AUTH_SECRET=' .env 2>/dev/null || echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" >> .env
|
|
157
|
+
grep -q '^SCENARIO_ACTOR_SECRET=' .env 2>/dev/null || echo "SCENARIO_ACTOR_SECRET=$(openssl rand -base64 32)" >> .env
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Never commit `.env`. Then run it — through `bun run dev`, never `vite` on its
|
|
161
|
+
own, because the dev script is what hands the frontend the persona list for the
|
|
162
|
+
"Sign in as …" switcher; without it the switcher silently renders nothing:
|
|
153
163
|
|
|
154
164
|
```sh
|
|
155
165
|
bun run prebuild && bun run dev
|
|
@@ -200,8 +210,7 @@ export const ownerCreatesAndSeesItScenario = pikkuScenario<void, { id: string }>
|
|
|
200
210
|
`pikkuScenarioStep`.** An RPC name in a `then` will not resolve.
|
|
201
211
|
- **Every scenario must assert.** A ladder with no `then` is a PKU680 critical —
|
|
202
212
|
it fails `pikku all`, stopping codegen rather than a test.
|
|
203
|
-
-
|
|
204
|
-
only a `BETTER_AUTH_SECRET`; without the actor secret
|
|
213
|
+
- **`SCENARIO_ACTOR_SECRET` must be in `.env`** (above). Without it
|
|
205
214
|
`/api/auth/sign-in/actor` is disabled and every scenario fails at sign-in, for
|
|
206
215
|
a reason that reads like an auth bug.
|
|
207
216
|
- **There is no state reset** — scope what you create to unique ids.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pikku-changes
|
|
3
|
+
description: 'Work a project''s changes queue — the todo list someone filed by walking a deployed stage. Covers `pikku fabric changes list|claim|show|ask|shot|done`, when to ask a question instead of guessing, and how to offer options as images. TRIGGER when: the user says "work the changes", "pick up the changes queue", names a change by its #number, or you are otherwise idle in a repo that has a pikkufabric.config.json. DO NOT TRIGGER for git changes, diffs or changelogs, and not for deploying or debugging a stage — use pikku-fabric for those.'
|
|
4
|
+
installGroups: [fabric]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Working a changes queue
|
|
8
|
+
|
|
9
|
+
Someone walked the deployed app and circled twenty things. Each one is a row with their
|
|
10
|
+
words, a picture of what they were looking at, and the elements the circle enclosed. You
|
|
11
|
+
have the repo and the app running locally. Your job is to empty the queue without making
|
|
12
|
+
them regret filing.
|
|
13
|
+
|
|
14
|
+
## The loop
|
|
15
|
+
|
|
16
|
+
Every argument is a flag; nothing is positional. `--json` works on any of them. The
|
|
17
|
+
project comes from the local `pikkufabric.config.json`, so `--project-id` is only needed
|
|
18
|
+
when you are not in the checkout.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pikku fabric changes list --pickup-only --json
|
|
22
|
+
pikku fabric changes claim --change-ids <id>,<id> --title "Checkout pass" --claimed-by claude-code
|
|
23
|
+
pikku fabric changes show --change-id <id>
|
|
24
|
+
pikku fabric changes ask --change-id <id> --question "…" --option "…" --option "…" --author-name claude-code
|
|
25
|
+
pikku fabric changes shot --change-id <id> --label "Bigger" --kind option --image a.png
|
|
26
|
+
pikku fabric changes done --change-id <id> --note "What you did"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`list --pickup-only` is the one a harness wants: it skips items still inside the grace
|
|
30
|
+
window, so a batch someone is mid-way through typing is picked up together rather than item
|
|
31
|
+
by item as it lands.
|
|
32
|
+
|
|
33
|
+
Deciding what belongs together is yours: `claim` with `--change-ids` and no `--group-id`
|
|
34
|
+
forms the group. Claim an existing one with `--group-id`.
|
|
35
|
+
|
|
36
|
+
Claim before working. The lease expires (30 minutes by default, `--lease-minutes` to
|
|
37
|
+
change it), so an abandoned claim returns to the queue rather than parking the work
|
|
38
|
+
forever — but a second harness picking up something you are halfway through is the failure
|
|
39
|
+
this prevents.
|
|
40
|
+
|
|
41
|
+
## Reading an item
|
|
42
|
+
|
|
43
|
+
`show` gives you four things, in descending order of trustworthiness:
|
|
44
|
+
|
|
45
|
+
1. **Their words.** The title and body are the requirement. Everything else is evidence.
|
|
46
|
+
2. **The screenshot.** What they actually saw, at their width, with their data. When the
|
|
47
|
+
other addresses disagree with the picture, the picture is right.
|
|
48
|
+
3. **The circled elements** — a testid, a source anchor, a CSS path. The testid greps
|
|
49
|
+
straight to a component because it is the i18n message key.
|
|
50
|
+
4. **The source anchor**, printed as `src/routes/app.orders.tsx:42 as of a91c4e2`. That line
|
|
51
|
+
number is where the JSX was **at that commit**. Read it as a starting point and find
|
|
52
|
+
today's equivalent; never edit line 42 of today's file because the anchor said 42.
|
|
53
|
+
|
|
54
|
+
Resolution is a guess and the panel says so. If the circle and the anchor point at
|
|
55
|
+
different things, believe the circle.
|
|
56
|
+
|
|
57
|
+
## When to ask
|
|
58
|
+
|
|
59
|
+
Ask when the item admits more than one reasonable implementation and you would be **picking
|
|
60
|
+
for them**. Do not ask to confirm something the item already says.
|
|
61
|
+
|
|
62
|
+
Ask:
|
|
63
|
+
- "Make the total stand out" — bigger, bolder, coloured, or moved above the fold?
|
|
64
|
+
- "This should be faster" — is it the spinner, the request, or the number of steps?
|
|
65
|
+
- Anything that changes what data is stored, what an existing user sees, or what something costs.
|
|
66
|
+
|
|
67
|
+
Do not ask:
|
|
68
|
+
- "Should I use flexbox or grid?" — that is yours.
|
|
69
|
+
- "Do you want me to fix the typo?" — they filed it; fix it.
|
|
70
|
+
- "Can you confirm you want the button blue?" — they said blue.
|
|
71
|
+
|
|
72
|
+
A question costs them a context switch, not typing. That is the budget you are spending.
|
|
73
|
+
|
|
74
|
+
## What a good question looks like
|
|
75
|
+
|
|
76
|
+
One decision. Their vocabulary, not the codebase's. And the choices in `--option`, not in
|
|
77
|
+
the sentence.
|
|
78
|
+
|
|
79
|
+
> **Bad:** "How would you like me to handle the ambiguity in the checkout total component's
|
|
80
|
+
> emphasis requirement?"
|
|
81
|
+
>
|
|
82
|
+
> **Good:** `--question "Make the total stand out — which way?"`
|
|
83
|
+
> `--option "Bigger" --option "Move it above the delivery line"`
|
|
84
|
+
|
|
85
|
+
**A choice written into the prose is not a choice.** Every `--option` becomes a button in
|
|
86
|
+
the panel and the console, and clicking one records the answer; a question that says
|
|
87
|
+
"(a) build it, (b) leave existing bookings, (c) hold" makes them re-type in free text what
|
|
88
|
+
they should have been able to click, and leaves you parsing prose to find out which one
|
|
89
|
+
they meant. If you can enumerate them in the sentence, you can pass them as flags.
|
|
90
|
+
|
|
91
|
+
Pass them even when there are only two, and even when one is "hold until I check" — that
|
|
92
|
+
last one is a real option and it is the one most often left off. The filer can always
|
|
93
|
+
choose "say something else", so the list constrains nothing.
|
|
94
|
+
|
|
95
|
+
Batch per group. Three questions about one checkout flow go out together; three separate
|
|
96
|
+
asks about the same screen is three interruptions for one context switch.
|
|
97
|
+
|
|
98
|
+
Then **park it**. `ask` flips the item to `needs_answer` and you move to the next item. Do
|
|
99
|
+
not sit waiting — pick answers up on your next `show`, and bound your polling so an
|
|
100
|
+
unanswered item does not spin forever.
|
|
101
|
+
|
|
102
|
+
## When to show instead of ask
|
|
103
|
+
|
|
104
|
+
If the answer is visual and you can build it, build all of them and attach images:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pikku fabric changes shot --change-id <id> --label "Bigger" --kind option --image a.png
|
|
108
|
+
pikku fabric changes shot --change-id <id> --label "Above the line" --kind option --image b.png
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The panel turns a set of `option` attachments into a pick-one they open full-screen, and
|
|
112
|
+
picking one writes the choice into the thread. Capture every variant in **one pass at one
|
|
113
|
+
width**, including the baseline — variants shot at different sizes are not comparable, and
|
|
114
|
+
comparing is the whole point.
|
|
115
|
+
|
|
116
|
+
`--kind evidence` is the other use: a picture that proves something, rendered inline rather
|
|
117
|
+
than as a choice.
|
|
118
|
+
|
|
119
|
+
## Committing
|
|
120
|
+
|
|
121
|
+
One item, one commit. `done` records a single `head_commit`, and that sha is what a human
|
|
122
|
+
reverts when they change their mind — so an item folded in with three others cannot be
|
|
123
|
+
undone without taking the other three with it. Land unrelated work separately.
|
|
124
|
+
|
|
125
|
+
The subject carries the short id the way a GitHub issue number does, and the uuid goes in a
|
|
126
|
+
trailer so `git log --grep` has an exact handle:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
feat(login): #4 make the sign-in heading brown
|
|
130
|
+
|
|
131
|
+
Change-Id: 0f3c8a12-9b44-4d2e-8f01-27c6a1d9e5b3
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Both ids come from `show`. The type and scope are the usual conventional-commit ones —
|
|
135
|
+
`feat`, `fix`, `style`, `refactor` — with the scope naming the screen or area they were
|
|
136
|
+
looking at, not the file you edited.
|
|
137
|
+
|
|
138
|
+
More:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
fix(booking): #7 stop the date picker closing on the first click
|
|
142
|
+
style(nav): #12 tighten the spacing around the logo
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Reverting one later is then:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
git revert $(git log --grep="Change-Id: <uuid>" --format=%H -1)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Finishing
|
|
152
|
+
|
|
153
|
+
`done` records the branch and commit that closed it, which is what strikes the item through
|
|
154
|
+
on the page it was filed on and tells them where the fix landed. Both default to the
|
|
155
|
+
checkout you are standing in, so run it from there and let it read git:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
pikku fabric changes done --change-id <id> \
|
|
159
|
+
--note "What you did, for whoever reads the thread later"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`--branch` and `--head-commit` override them, for the case where the fix landed somewhere
|
|
163
|
+
other than where you are. Never type a sha by hand — one that does not exist points the
|
|
164
|
+
filer at nothing.
|
|
165
|
+
|
|
166
|
+
An item you decided not to do is not `done`. Say why in the thread and leave it for a human
|
|
167
|
+
to dismiss.
|
|
168
|
+
|
|
169
|
+
## Scope
|
|
170
|
+
|
|
171
|
+
Writes need the `changes:project:write` scope on your bearer. `list` and `show` are reads.
|
|
172
|
+
The project comes from the local `pikkufabric.config.json`, so run these from the checkout.
|
|
@@ -20,7 +20,7 @@ Use this skill as an execution checklist, not reference material.
|
|
|
20
20
|
1. Discover before editing. Run `pikku doc --ai` for the installed API surface, and the relevant `pikku meta ... --json` for what this project has wired.
|
|
21
21
|
2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
|
|
22
22
|
3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
|
|
23
|
-
4. Validate with the narrowest relevant command first, then run `pikku
|
|
23
|
+
4. Validate with the narrowest relevant command first, then run `pikku all` when functions, wirings, schemas, or generated clients may have changed.
|
|
24
24
|
5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
|
|
25
25
|
|
|
26
26
|
Pikku is a TypeScript framework that separates business logic from transport mechanisms. You define a function once, then wire it to HTTP, WebSocket, queues, schedulers, MCP, CLI, or RPC — without the function knowing how it's being called.
|
|
@@ -286,62 +286,22 @@ Schemas serve triple duty: runtime validation, TypeScript types, and OpenAPI doc
|
|
|
286
286
|
|
|
287
287
|
## Server Bootstrap
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
Two ways to start a Pikku app, and the choice is whether you need to own the HTTP server.
|
|
290
290
|
|
|
291
|
-
**
|
|
291
|
+
**Let Pikku own it** — `pikku dev` and `pikku serve` create the config and singleton services,
|
|
292
|
+
start the server and shut it down cleanly, so you write no bootstrap code at all. Startup and
|
|
293
|
+
shutdown work goes in one exported `pikkuServerLifecycle` (`beforeStart` / `afterStart` /
|
|
294
|
+
`beforeStop`). **Only `dev` and `serve` invoke those hooks** — no deploy runtime does, so anything
|
|
295
|
+
a Workers or serverless stage needs done belongs on the request path that needs it.
|
|
292
296
|
|
|
293
|
-
|
|
297
|
+
**Bootstrap it yourself** — required for Express, Fastify, uWS, Lambda, Cloudflare and Next.js,
|
|
298
|
+
where Pikku is embedded in a server you own. Lifecycle hooks do not run on this path; do the
|
|
299
|
+
startup work in the entrypoint.
|
|
294
300
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
import { pikkuServerLifecycle } from '@pikku/core'
|
|
298
|
-
import type { SingletonServices } from '../types/application-types.js'
|
|
299
|
-
|
|
300
|
-
export const lifecycle = pikkuServerLifecycle<SingletonServices>({
|
|
301
|
-
beforeStart: async ({ kysely }) => {
|
|
302
|
-
await runMigrations(kysely)
|
|
303
|
-
},
|
|
304
|
-
afterStart: async ({ logger }) => {
|
|
305
|
-
logger.info('accepting traffic')
|
|
306
|
-
},
|
|
307
|
-
beforeStop: async ({ queueService }) => {
|
|
308
|
-
await queueService.drain()
|
|
309
|
-
},
|
|
310
|
-
})
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
Export exactly one `pikkuServerLifecycle` from anywhere in `srcDirectories` — the inspector finds it by the wrapper call. Every hook is optional and receives the already-created singleton services. See pikku-services for the ordering and the `afterStop` caveat.
|
|
314
|
-
|
|
315
|
-
**Only `pikku dev` and `pikku serve` invoke these hooks.** No deploy runtime does, so anything a Workers or serverless stage needs done cannot live here — put it on the request path that needs it, guarded by a cheap check.
|
|
316
|
-
|
|
317
|
-
**2. Bootstrap it yourself (required for a specific runtime)**
|
|
301
|
+
`pikku validate` warns when a project starts a server by hand *and* depends on no runtime adapter,
|
|
302
|
+
because that means the first path was available and unused.
|
|
318
303
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
```typescript
|
|
322
|
-
import '../../functions/.pikku/pikku-bootstrap.gen.js' // Generated — registers all wirings
|
|
323
|
-
|
|
324
|
-
const config = await createConfig()
|
|
325
|
-
const singletonServices = await createSingletonServices(config)
|
|
326
|
-
|
|
327
|
-
// Pick your runtime:
|
|
328
|
-
const server = new PikkuFastifyServer(
|
|
329
|
-
config,
|
|
330
|
-
singletonServices,
|
|
331
|
-
createWireServices
|
|
332
|
-
)
|
|
333
|
-
// or: new PikkuExpressServer(config, singletonServices, createWireServices)
|
|
334
|
-
// or: pikkuAWSLambdaHandler(singletonServices)
|
|
335
|
-
// or: PikkuCloudflareHandler(singletonServices)
|
|
336
|
-
// or: pikkuNextHandler(singletonServices)
|
|
337
|
-
|
|
338
|
-
await server.init()
|
|
339
|
-
await server.start()
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
**Lifecycle hooks do not run on this path** — only `pikku dev` and `pikku serve` invoke them. Do your startup work directly in the entrypoint instead.
|
|
343
|
-
|
|
344
|
-
`pikku validate` warns when a project starts a server by hand _and_ depends on no runtime adapter, since that combination means path 1 was available and unused. Silence it with `"lint": { "customServerBootstrap": "off" }` in `pikku.config.json`.
|
|
304
|
+
**`references/bootstrap.md`** has both entrypoints in full.
|
|
345
305
|
|
|
346
306
|
## Code Generation
|
|
347
307
|
|
|
@@ -392,91 +352,26 @@ src/
|
|
|
392
352
|
|
|
393
353
|
## What Language You Write In
|
|
394
354
|
|
|
395
|
-
Three different things in a Pikku project have a human language, and they are
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
|
401
|
-
|
|
|
402
|
-
| **
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
generated SQL types, every skill and every agent that ever picks the project up.
|
|
416
|
-
A `vorgang` table types as `Vorgang` in Kysely and reads as noise to everyone who
|
|
417
|
-
did not name it, and unlike a string it cannot be translated later — renaming an
|
|
418
|
-
identifier is a migration, not an edit.
|
|
419
|
-
|
|
420
|
-
### Meta follows `metaLocale`, and that is what the field is for
|
|
421
|
-
|
|
422
|
-
```json
|
|
423
|
-
{ "metaLocale": "de" }
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
Meta is the one part of a project the **Pikku Console** renders back to a human.
|
|
427
|
-
A team reviewing their own functions, features and scenario reports in the
|
|
428
|
-
Console is reading meta and nothing else, so a team whose working language is
|
|
429
|
-
German should be able to read their Console in German. That is the entire reason
|
|
430
|
-
the field exists.
|
|
431
|
-
|
|
432
|
-
Read it before you author meta, and write descriptions, titles and templates in
|
|
433
|
-
it. Absent, it is `en`. It is a BCP-47 tag (`en`, `de`, `pt-BR` — a hyphen, not
|
|
434
|
-
an underscore), and the CLI rejects anything else by name.
|
|
435
|
-
|
|
436
|
-
`metaLocale` is **not** licence to rename anything. `metaLocale: "de"` buys a German
|
|
437
|
-
`description: 'Zeigt die Arbeitsliste'` on a function still called
|
|
438
|
-
`getWorklist`.
|
|
439
|
-
|
|
440
|
-
### Product UI language lives in the catalogue, and only there
|
|
441
|
-
|
|
442
|
-
What the app says to its users is a translation concern, not a code concern. It
|
|
443
|
-
belongs in `messages/<locale>.json`; `pikku-i18n` owns the details. The one rule
|
|
444
|
-
worth repeating here: **`baseLocale` in `project.inlang/settings.json` stays
|
|
445
|
-
`en`.** It names the message _source_ — the catalogue every other language is
|
|
446
|
-
cloned from and translated against — so a project that sets it to anything else
|
|
447
|
-
has no English catalogue to translate from and can never gain a second language
|
|
448
|
-
without re-authoring every key.
|
|
449
|
-
|
|
450
|
-
### The failure this comes from
|
|
451
|
-
|
|
452
|
-
An agent was asked to build a doctor's portal for a German practice. The brief
|
|
453
|
-
said "the entire UI is German, no English strings visible anywhere". The agent
|
|
454
|
-
read one sentence about the product's users as an instruction about the
|
|
455
|
-
codebase, and produced:
|
|
456
|
-
|
|
457
|
-
- `project.inlang/settings.json` with `baseLocale: "de"` and `locales: ["de"]`,
|
|
458
|
-
no `en.json` at all — which silently broke `--add-locale` forever
|
|
459
|
-
- RPC functions `getUebersicht` and `getPatientendetail`
|
|
460
|
-
- React components `Zeitstrahl` and `AufmerksamkeitStreifen`
|
|
461
|
-
- database tables `vorgang` and `ereignis`, with German columns
|
|
462
|
-
|
|
463
|
-
Every one of those is wrong, and the brief was satisfied by none of them: a
|
|
464
|
-
German UI needs German _messages_. What that project actually wanted was three
|
|
465
|
-
settings, each on its own axis:
|
|
466
|
-
|
|
467
|
-
```jsonc
|
|
468
|
-
// project.inlang/settings.json — the message source stays English
|
|
469
|
-
{ "baseLocale": "en", "locales": ["en", "de"] }
|
|
470
|
-
|
|
471
|
-
// apps/app/src/i18n/active.json — what a first-time visitor opens in
|
|
472
|
-
{ "defaultLocale": "de" }
|
|
473
|
-
|
|
474
|
-
// pikku.config.json — the language the team reads their Console in
|
|
475
|
-
{ "metaLocale": "de" }
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
Identifiers stay English throughout. When a brief tells you the product speaks a
|
|
479
|
-
language, it is telling you about axis three and nothing else.
|
|
355
|
+
Three different things in a Pikku project have a human language, and they are **not** the same
|
|
356
|
+
language. Collapsing them has already shipped in a real product:
|
|
357
|
+
|
|
358
|
+
| Axis | Covers | Decided by |
|
|
359
|
+
| --------------- | ----------------------------------------------------------------------------- | ---------------------------------------------- |
|
|
360
|
+
| **Identifiers** | Function, component, type and file names; tables and columns; commit messages | Nothing. **Always English.** There is no setting |
|
|
361
|
+
| **Meta** | Prose authored inside the code — `description`, `title`, step `template` | `metaLocale` in `pikku.config.json` (default `en`) |
|
|
362
|
+
| **Product UI** | Every string the app shows a user | `messages/<locale>.json`, and `active.json`'s `defaultLocale` |
|
|
363
|
+
|
|
364
|
+
Identifiers are the surface every other tool binds to — the generated clients, the RPC map a
|
|
365
|
+
scenario is typed over, the SQL types — and unlike a string an identifier cannot be translated
|
|
366
|
+
later: renaming one is a migration. `metaLocale` exists so a team can read their own Console in
|
|
367
|
+
their own language; it is not licence to rename anything. And `baseLocale` in
|
|
368
|
+
`project.inlang/settings.json` stays `en`, because it names the message *source* every other
|
|
369
|
+
language is cloned from.
|
|
370
|
+
|
|
371
|
+
**When a brief tells you the product speaks a language, it is telling you about the third axis and
|
|
372
|
+
nothing else.** `references/language.md` has the three settings that satisfy such a brief, and the
|
|
373
|
+
build that read one sentence about a product's users as an instruction about its codebase — German
|
|
374
|
+
RPC names, German tables, and no English catalogue to ever translate from.
|
|
480
375
|
|
|
481
376
|
## Environment Variables
|
|
482
377
|
|