@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.
- package/dist/skills.gen.js +2 -2
- package/package.json +1 -1
- package/skills/pikku-a11y/SKILL.md +22 -15
- package/skills/pikku-addon/SKILL.md +5 -5
- package/skills/pikku-addon/references/addon-package-manifest.md +7 -0
- package/skills/pikku-addon/references/openapi.md +11 -0
- package/skills/pikku-admin-to-fabric/SKILL.md +7 -21
- package/skills/pikku-agent/SKILL.md +6 -6
- package/skills/pikku-agent/references/agents.md +8 -1
- package/skills/pikku-agent/references/runner-vercel.md +2 -2
- package/skills/pikku-agent/references/voice.md +7 -1
- package/skills/pikku-architect/SKILL.md +1 -1
- package/skills/pikku-architect/references/plan-defects.md +2 -0
- package/skills/pikku-auth/SKILL.md +15 -36
- package/skills/pikku-auth/references/better-auth.md +22 -45
- package/skills/pikku-auth/references/jose.md +6 -0
- package/skills/pikku-auth/references/machine-auth.md +6 -0
- package/skills/pikku-auth/references/sessions.md +6 -0
- package/skills/pikku-blueprint-to-fabric/SKILL.md +6 -21
- package/skills/pikku-blueprint-to-fabric/scripts/inventory.mjs +1 -1
- package/skills/pikku-build/SKILL.md +29 -26
- package/skills/pikku-build/references/app.md +60 -41
- package/skills/pikku-build/references/design.md +31 -19
- package/skills/pikku-build/references/feature.md +14 -0
- package/skills/pikku-build/references/mocks.md +23 -0
- package/skills/pikku-build/references/multi-app.md +9 -2
- package/skills/pikku-build/references/native-app.md +9 -0
- package/skills/pikku-build/references/openapi.md +7 -0
- package/skills/pikku-build/references/platform.md +20 -10
- package/skills/pikku-build/references/quick.md +14 -4
- package/skills/pikku-build/references/scenarios.md +12 -1
- package/skills/pikku-build/references/ship.md +6 -1
- package/skills/pikku-build/references/theming.md +74 -125
- package/skills/pikku-changes/SKILL.md +6 -6
- package/skills/pikku-concepts/SKILL.md +8 -17
- package/skills/pikku-concepts/references/concept-mapping.md +18 -0
- package/skills/pikku-convert-screen/SKILL.md +45 -0
- package/skills/pikku-deploy/references/cloudflare.md +8 -0
- package/skills/pikku-deploy/references/lambda.md +8 -0
- package/skills/pikku-deploy/references/uws.md +1 -1
- package/skills/pikku-deploy/references/ws.md +3 -3
- package/skills/pikku-emails/SKILL.md +3 -9
- package/skills/pikku-fabric/SKILL.md +7 -295
- package/skills/pikku-fabric/references/database.md +128 -0
- package/skills/pikku-fabric/references/debugging.md +9 -5
- package/skills/pikku-fabric/references/deploy.md +179 -0
- package/skills/pikku-guide/SKILL.md +10 -10
- package/skills/pikku-i18n/SKILL.md +7 -7
- package/skills/pikku-i18n/references/enum-labels.md +9 -0
- package/skills/pikku-i18n/references/messages.md +16 -3
- package/skills/pikku-i18n/references/rtl.md +18 -38
- package/skills/pikku-knowledge/SKILL.md +9 -10
- package/skills/pikku-kysely/SKILL.md +3 -21
- package/skills/pikku-list-query/SKILL.md +2 -2
- package/skills/pikku-meta/references/audit.md +9 -0
- package/skills/pikku-meta/references/meta.md +6 -0
- package/skills/pikku-meta/references/versioning.md +10 -0
- package/skills/pikku-middleware/SKILL.md +7 -16
- package/skills/pikku-n8n-import/SKILL.md +9 -1
- package/skills/pikku-n8n-import/references/addon-mapping.md +6 -0
- package/skills/pikku-n8n-import/references/code-translation.md +7 -0
- package/skills/pikku-permissions/SKILL.md +41 -10
- package/skills/pikku-permissions/references/scopes.md +59 -0
- package/skills/pikku-react/SKILL.md +2 -2
- package/skills/pikku-react/references/client.md +1 -34
- package/skills/pikku-react/references/react-query.md +11 -0
- package/skills/pikku-react/references/workflows.md +11 -0
- package/skills/pikku-realtime/SKILL.md +9 -3
- package/skills/pikku-report/SKILL.md +10 -9
- package/skills/pikku-scenario/SKILL.md +11 -17
- package/skills/pikku-scenario/references/persona-run.md +11 -1
- package/skills/pikku-scenario/references/personas.md +7 -1
- package/skills/pikku-seo/SKILL.md +12 -16
- package/skills/pikku-service-backends/SKILL.md +0 -10
- package/skills/pikku-service-backends/references/aws.md +6 -0
- package/skills/pikku-service-backends/references/mongodb.md +7 -0
- package/skills/pikku-service-backends/references/redis.md +6 -0
- package/skills/pikku-services/references/audit.md +10 -0
- package/skills/pikku-services/references/config.md +6 -0
- package/skills/pikku-services/references/services.md +8 -1
- package/skills/pikku-software-archaeology/SKILL.md +9 -15
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +7 -7
- package/skills/pikku-software-archaeology/references/pikku-mapping.md +3 -3
- package/skills/pikku-software-archaeology/references/second-opinion.md +13 -1
- package/skills/pikku-tailwind/SKILL.md +111 -0
- package/skills/pikku-theme/SKILL.md +187 -0
- package/skills/pikku-theme/references/from-a-reference.md +44 -0
- package/skills/pikku-theme/references/presets.md +40 -0
- package/skills/pikku-ui/SKILL.md +41 -0
- package/skills/pikku-wiring/SKILL.md +13 -12
- package/skills/pikku-wiring/references/channel.md +7 -1
- package/skills/pikku-wiring/references/cli.md +7 -1
- package/skills/pikku-wiring/references/gateway-slack.md +6 -0
- package/skills/pikku-wiring/references/http.md +10 -3
- package/skills/pikku-wiring/references/mcp.md +8 -0
- package/skills/pikku-wiring/references/queue.md +6 -0
- package/skills/pikku-wiring/references/realtime.md +34 -4
- package/skills/pikku-wiring/references/rpc.md +6 -0
- package/skills/pikku-wiring/references/scheduler.md +6 -0
- package/skills/pikku-wiring/references/trigger.md +8 -0
- package/skills/pikku-workflow/SKILL.md +8 -6
- package/skills/pikku-auth/references/permissions.md +0 -261
- package/skills/pikku-mantine/SKILL.md +0 -80
- package/skills/pikku-wiring/references/realtime-other-routes.md +0 -23
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Build an app on Pikku, fast
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Agent Operating Procedure
|
|
6
|
+
- 1. One question, then build
|
|
7
|
+
- 2. Personas — 60 seconds, not optional
|
|
8
|
+
- 3. Build
|
|
9
|
+
- 4. Look at it — actually
|
|
10
|
+
- 5. One smoke scenario
|
|
11
|
+
- 6. Hand it over honestly
|
|
12
|
+
|
|
3
13
|
You have a scaffolded project with skills installed. Get it to working, seeded,
|
|
4
14
|
signed-in screens in as few steps as possible.
|
|
5
15
|
|
|
@@ -42,7 +52,7 @@ Identifiers are English in every project, whatever the product's market;
|
|
|
42
52
|
`template`, which the Console renders) stays `en` unless the user already told
|
|
43
53
|
you otherwise. If the request says the app's UI is not English, that is the
|
|
44
54
|
message catalogue only: add the locale and set `defaultLocale`, and leave
|
|
45
|
-
`baseLocale` at `en`.
|
|
55
|
+
`baseLocale` at `en`. the app workflow's §1a has the three axes in full; getting
|
|
46
56
|
them confused is how a project ends up unable to add a second language.
|
|
47
57
|
|
|
48
58
|
## 2. Personas — 60 seconds, not optional
|
|
@@ -186,7 +196,7 @@ taller than the viewport. It is the most likely width your demo gets opened at.
|
|
|
186
196
|
If you have five spare minutes, `npx impeccable install` (Node 22.18+) scores
|
|
187
197
|
each screen against interaction heuristics and names what is wrong. Feed it
|
|
188
198
|
screenshots, not source. It will polish the default look; it will not give the
|
|
189
|
-
app a look — that is
|
|
199
|
+
app a look — that is the app workflow's §8a.
|
|
190
200
|
|
|
191
201
|
## 5. One smoke scenario
|
|
192
202
|
|
|
@@ -230,7 +240,7 @@ it is seeded with, and that this is a quick build — no knowledge base, no
|
|
|
230
240
|
changesets, no design pass, access control clicked-through rather than proven.
|
|
231
241
|
|
|
232
242
|
**Upgrading to a real build is additive, not a rewrite.** If they want it, switch
|
|
233
|
-
to
|
|
243
|
+
to the app workflow (the app reference, see SKILL.md) and do this, in order:
|
|
234
244
|
|
|
235
245
|
1. Write `knowledge/` for what already exists — `entities/` for what you built,
|
|
236
246
|
`decisions/` for what you chose silently, `questions/` for what you guessed
|
|
@@ -239,7 +249,7 @@ to `references/app.md` and do this, in order:
|
|
|
239
249
|
gaps` has something to measure against.
|
|
240
250
|
3. Write the refusal scenarios — the ones proving one persona cannot reach
|
|
241
251
|
another's rows. This is the gap that matters most.
|
|
242
|
-
4. Then pick up
|
|
252
|
+
4. Then pick up the app workflow at its §4 (apps) or §5 (changes) for
|
|
243
253
|
anything new.
|
|
244
254
|
|
|
245
255
|
Nothing built here has to be thrown away to do that — which is the whole reason
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Scenarios — writing journeys that stay proven
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- The shape of a scenario
|
|
6
|
+
- Extraction: what survives, and what silently does not
|
|
7
|
+
- What to assert
|
|
8
|
+
- There is no state reset
|
|
9
|
+
- Steps rot as the app grows
|
|
10
|
+
- Browser scenarios
|
|
11
|
+
- Running them
|
|
12
|
+
- Coverage — which functions have actually been run
|
|
13
|
+
|
|
3
14
|
A scenario is a user journey run as one of your personas, over the real transport, with that
|
|
4
15
|
persona's session. It is the only kind of test worth writing here, because a passing one proves the
|
|
5
16
|
app works the way a signed-in person experiences it.
|
|
@@ -59,7 +70,7 @@ export const tenantReportsAFaultScenario = pikkuScenario<void, { id: string }>({
|
|
|
59
70
|
(server-side by default, plus a `browser` one that drives the page). Reaching for an RPC name in a
|
|
60
71
|
`then` will not resolve.
|
|
61
72
|
|
|
62
|
-
**`SCENARIO_ACTOR_SECRET` must be in `.env`** (app
|
|
73
|
+
**`SCENARIO_ACTOR_SECRET` must be in `.env`** (the app workflow's §6, before the first run). Without it
|
|
63
74
|
`/api/auth/sign-in/actor` is disabled — every scenario then fails at sign-in, before its first step,
|
|
64
75
|
for a reason that reads like an auth bug. `pikku scenario run` reads it from the environment, so
|
|
65
76
|
source `.env` first (`set -a && . ./.env && set +a`) when you run outside `bun run dev`.
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Shipping, and staying Fabric-ready
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Ship it — open source, no platform
|
|
6
|
+
- Staying Fabric-ready
|
|
7
|
+
|
|
3
8
|
Read this when the changes queue is empty and the scenarios are green — it is
|
|
4
9
|
the last phase, and nothing in it is needed before then.
|
|
5
10
|
|
|
@@ -114,7 +119,7 @@ one hostname, first-party cookies — give its `frontends` entry `"serve": {}`
|
|
|
114
119
|
and a standalone deploy then mount its built `dist`. Pikku never builds it, so
|
|
115
120
|
build the frontend first. Only one entry may set `serve`.
|
|
116
121
|
|
|
117
|
-
To ship an app as a desktop or Android app, read
|
|
122
|
+
To ship an app as a desktop or Android app, read the native-app reference (see SKILL.md).
|
|
118
123
|
|
|
119
124
|
Before shipping, run the full gate:
|
|
120
125
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Authoring the theme
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Where the look lives
|
|
6
|
+
- The colours the theme has no token for
|
|
7
|
+
- Choose the neutrals; do not inherit them
|
|
8
|
+
- If the user gave you no direction
|
|
9
|
+
|
|
3
10
|
Read this at the design step, once you have a direction to turn into a theme —
|
|
4
11
|
whether the user described one in words, handed you a reference, or ran their own
|
|
5
12
|
design step whose output you are implementing.
|
|
@@ -11,180 +18,122 @@ job done in JSON.
|
|
|
11
18
|
## Where the look lives
|
|
12
19
|
|
|
13
20
|
```
|
|
14
|
-
packages/
|
|
21
|
+
packages/theme/
|
|
15
22
|
themes/
|
|
16
23
|
default.json # "Neutral" — the shipped scaffold
|
|
17
|
-
index.ts # registers each theme JSON by id
|
|
18
24
|
active.json # { "id": "default" } — which one is live
|
|
19
|
-
|
|
25
|
+
theme.css # generated from the active spec — do not edit
|
|
20
26
|
```
|
|
21
27
|
|
|
22
28
|
Each theme JSON has two halves — `brand` (colours, fonts) and `structure`
|
|
23
|
-
(radius,
|
|
24
|
-
|
|
25
|
-
`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
hand` banner. **That instruction is about Fabric's generator, not about you.**
|
|
29
|
-
Without that tool, hand-authoring is the only route, and the file is a three-line
|
|
30
|
-
registry. Edit it, and replace the banner with a line saying the themes here are
|
|
31
|
-
hand-authored — otherwise the next agent reads the warning and leaves the app on
|
|
32
|
-
Neutral.
|
|
29
|
+
(radius, density, default colour scheme). To give the product an identity, add
|
|
30
|
+
`themes/<name>.json`, point `active.json` at its id, and regenerate theme.css.
|
|
31
|
+
That regenerates `theme.css`, which defines the shadcn tokens (`--background`,
|
|
32
|
+
`--card`, `--border`, `--primary`, `--chart-1` to `--chart-5`, `--radius`) for
|
|
33
|
+
both the light scheme and the `.dark` class.
|
|
33
34
|
|
|
34
35
|
Turning §1's answer into a theme:
|
|
35
36
|
|
|
36
|
-
- **Colour before anything else.** `brand.colors.primary`
|
|
37
|
-
|
|
38
|
-
`@mantine/colors-generator` (already a dependency) expands one hex into a full
|
|
39
|
-
scale.
|
|
37
|
+
- **Colour before anything else.** `brand.colors.primary` carries most of the
|
|
38
|
+
identity; the neutrals, borders and chart colours are derived from its hue.
|
|
40
39
|
- **Fonts are the other half of the register**, and the half people skip. A serif
|
|
41
40
|
heading font against a neutral body is a different product from the system
|
|
42
41
|
stack, and it is one field.
|
|
43
|
-
- **`structure.radius`
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
automatic** — `Card` with `withBorder` everywhere, `NavLink` as `light`. Put
|
|
48
|
-
the repeated decision here rather than on every instance.
|
|
49
|
-
- **`defaultColorScheme` and `darkColors` are a real choice**, not a toggle to
|
|
50
|
-
leave alone. A tool people live in all day is often better dark by default.
|
|
42
|
+
- **`structure.radius` sets the temperature.** Sharp corners read technical;
|
|
43
|
+
large radii read consumer. Neutral's `md` is the middle of the road on purpose.
|
|
44
|
+
- **`defaultColorScheme` is a real choice**, not a toggle to leave alone. A tool
|
|
45
|
+
people live in all day is often better dark by default.
|
|
51
46
|
|
|
52
47
|
Then **write the direction into `knowledge/decisions/design/`** — the words the
|
|
53
48
|
user gave you, what you chose, and what it rules out. The JSON records what the
|
|
54
49
|
theme is; only the note records why.
|
|
55
50
|
|
|
56
|
-
## The colours the theme has no
|
|
51
|
+
## The colours the theme has no token for
|
|
57
52
|
|
|
58
53
|
`brand` is the product's accent. It is not the only colour a screen needs, and
|
|
59
54
|
the missing ones are why "don't hardcode colours per component" gets broken by
|
|
60
55
|
the same agent that wrote it down.
|
|
61
56
|
|
|
62
|
-
A screen has to say
|
|
57
|
+
A screen has to say _covered_ and _still open_, _fine_ and _needs attention_ —
|
|
63
58
|
and those are not the accent. Using the accent for them is worse than a stray
|
|
64
59
|
hex: the brand colour stops meaning "this product" and starts meaning "good", so
|
|
65
60
|
it means nothing. But there is no `brand.covered` field, so the value lands
|
|
66
61
|
inline as `#3f7d5c`, once per component, slightly different each time.
|
|
67
62
|
|
|
68
|
-
Give them a home. A small stylesheet of custom properties, imported once
|
|
69
|
-
|
|
63
|
+
Give them a home. A small stylesheet of custom properties, imported once after
|
|
64
|
+
`theme.css`, exposed to Tailwind through `@theme inline`, is enough:
|
|
70
65
|
|
|
71
66
|
```css
|
|
72
|
-
:root
|
|
73
|
-
--
|
|
74
|
-
--
|
|
75
|
-
--
|
|
76
|
-
--
|
|
67
|
+
:root {
|
|
68
|
+
--covered: #3f7d5c;
|
|
69
|
+
--covered-bg: #e6f1ea;
|
|
70
|
+
--open: #a8701a;
|
|
71
|
+
--open-bg: #fbeedb;
|
|
72
|
+
--sunk: #fdf7f4;
|
|
77
73
|
}
|
|
78
|
-
|
|
79
|
-
--
|
|
80
|
-
--
|
|
81
|
-
--
|
|
82
|
-
--
|
|
74
|
+
.dark {
|
|
75
|
+
--covered: #7fc09a;
|
|
76
|
+
--covered-bg: #1e2f26;
|
|
77
|
+
--open: #e0ab5c;
|
|
78
|
+
--open-bg: #33271a;
|
|
79
|
+
--sunk: #241b18;
|
|
80
|
+
}
|
|
81
|
+
@theme inline {
|
|
82
|
+
--color-covered: var(--covered);
|
|
83
|
+
--color-covered-bg: var(--covered-bg);
|
|
84
|
+
--color-open: var(--open);
|
|
85
|
+
--color-open-bg: var(--open-bg);
|
|
86
|
+
--color-sunk: var(--sunk);
|
|
83
87
|
}
|
|
84
88
|
```
|
|
85
89
|
|
|
86
90
|
Those values are one app's warm direction, not a palette to copy — derive your
|
|
87
|
-
own from yours.
|
|
91
|
+
own from yours. Components then say `bg-covered-bg text-covered`.
|
|
88
92
|
|
|
89
93
|
## Choose the neutrals; do not inherit them
|
|
90
94
|
|
|
91
|
-
The
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
:root:root:root {
|
|
114
|
-
--mantine-color-gray-0: #faf7f5; --mantine-color-gray-5: #b8a49d;
|
|
115
|
-
--mantine-color-gray-1: #f5efec; --mantine-color-gray-6: #93807a;
|
|
116
|
-
--mantine-color-gray-2: #efe6e2; --mantine-color-gray-7: #574a45;
|
|
117
|
-
--mantine-color-gray-3: #e6dad5; --mantine-color-gray-8: #3d332f;
|
|
118
|
-
--mantine-color-gray-4: #d8c8c2; --mantine-color-gray-9: #2a1f1b;
|
|
119
|
-
--mantine-color-dark-0: #f3e9e4; --mantine-color-dark-5: #4a3a34;
|
|
120
|
-
--mantine-color-dark-1: #cdbdb6; --mantine-color-dark-6: #392b26;
|
|
121
|
-
--mantine-color-dark-2: #a8938c; --mantine-color-dark-7: #241b18;
|
|
122
|
-
--mantine-color-dark-3: #7a655e; --mantine-color-dark-8: #1b1512;
|
|
123
|
-
--mantine-color-dark-4: #5c4a44; --mantine-color-dark-9: #120d0b;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/* Ground, text and border are a different colour in each scheme, so each one
|
|
127
|
-
is set in the scheme it belongs to. A scheme-dependent token left in the
|
|
128
|
-
block above is the classic unreadable-in-dark bug. */
|
|
129
|
-
:root:root:root[data-mantine-color-scheme='light'] {
|
|
130
|
-
--mantine-color-body: #fdfaf8;
|
|
131
|
-
--mantine-color-text: #2a1f1b;
|
|
132
|
-
--mantine-color-default-border: #ecdfd9;
|
|
133
|
-
--mantine-color-dimmed: #7a625c; /* keep AA: ~5.4:1 on the body above */
|
|
134
|
-
--mantine-color-placeholder: #826a64; /* ~4.8:1 */
|
|
135
|
-
}
|
|
136
|
-
:root:root:root[data-mantine-color-scheme='dark'] {
|
|
137
|
-
--mantine-color-body: #1b1512;
|
|
138
|
-
--mantine-color-text: #f3e9e4;
|
|
139
|
-
--mantine-color-default-border: #392b26;
|
|
140
|
-
--mantine-color-dimmed: #a8938c; /* ~5.6:1 on the body above */
|
|
141
|
-
--mantine-color-placeholder: #8c7a74; /* ~4.7:1 */
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Check the four text tokens against your own grounds rather than copying these —
|
|
146
|
-
`dimmed` is the most-used text colour in the app and the easiest to drop below
|
|
147
|
-
4.5:1 while making it prettier, and it has to clear the bar on *both* grounds.
|
|
148
|
-
|
|
149
|
-
**The selector is tripled on purpose.** Mantine's `cssVariablesResolver` injects
|
|
150
|
-
its own `:root` block into `<head>` at runtime, which lands *after* your
|
|
151
|
-
stylesheet and wins on source order at equal specificity. A plain `:root` here is
|
|
152
|
-
silently reverted: the file reads correct, the app renders Mantine's defaults,
|
|
153
|
-
and nothing errors. Repeating the pseudo-class raises specificity without adding
|
|
154
|
-
an element to the selector.
|
|
155
|
-
|
|
156
|
-
This one costs a whole pass if you meet it without knowing: you diagnose the
|
|
157
|
-
colours correctly, write the right values, reload, and see no change — so you
|
|
158
|
-
assume the diagnosis was wrong. **The only thing that catches it is looking at a
|
|
159
|
-
screenshot and disbelieving the CSS.**
|
|
160
|
-
|
|
161
|
-
Name them for what they *mean* in this product, never for the colour — `covered`,
|
|
162
|
-
not `green`. The name is the whole value: it survives a change of palette, and it
|
|
163
|
-
is the thing that makes the second use agree with the first. Define both colour
|
|
164
|
-
schemes at once; a token defined only in light is the classic unreadable-in-dark
|
|
165
|
-
bug, and Mantine will happily render it.
|
|
95
|
+
The theme generator derives the neutrals from the primary's hue, so the greys
|
|
96
|
+
already lean toward the accent. That only holds if the primary is chosen first:
|
|
97
|
+
change `brand.colors.primary`, regenerate theme.css, and look at a card
|
|
98
|
+
border against the page, because it is the neutrals that draw card borders,
|
|
99
|
+
dividers, the shell's edges and every disabled control.
|
|
100
|
+
|
|
101
|
+
If the accent is not blue and the greys still read cold, the mismatch lands on
|
|
102
|
+
every screen at once: warm content ruled off in cold lines, off everywhere and
|
|
103
|
+
wrong nowhere in particular, which is the hardest kind of wrong to find. It
|
|
104
|
+
survives a careful critique because no single screen is broken. Fix it in
|
|
105
|
+
`theme.css` inputs, not by overriding `border-gray-200` per component.
|
|
106
|
+
|
|
107
|
+
Check the text tokens against your own grounds rather than trusting them —
|
|
108
|
+
`--muted-foreground` is the most-used secondary text colour in the app and the
|
|
109
|
+
easiest to drop below 4.5:1 while making it prettier, and it has to clear the bar
|
|
110
|
+
on _both_ the light and `.dark` grounds.
|
|
111
|
+
|
|
112
|
+
Name the extra tokens for what they _mean_ in this product, never for the colour
|
|
113
|
+
— `covered`, not `green`. The name is the whole value: it survives a change of
|
|
114
|
+
palette, and it is the thing that makes the second use agree with the first.
|
|
115
|
+
Define both colour schemes at once; a token defined only in light is the classic
|
|
116
|
+
unreadable-in-dark bug.
|
|
166
117
|
|
|
167
118
|
The same file is where a couple of other things belong that the theme JSON has no
|
|
168
|
-
field for and every screen otherwise re-invents: the
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
119
|
+
field for and every screen otherwise re-invents: the recessed surface a form sits
|
|
120
|
+
on so it does not carry the same weight as the content it adds to, and the one
|
|
121
|
+
animation the product is allowed (behind `prefers-reduced-motion`). Two or three
|
|
122
|
+
rules, not a framework.
|
|
172
123
|
|
|
173
124
|
If the app ships template screens you did not write — the error and not-found
|
|
174
|
-
pages usually — read them before you call the palette done.
|
|
175
|
-
|
|
176
|
-
says warm is the single loudest contradiction in the build.
|
|
125
|
+
pages usually — read them before you call the palette done. A stock accent on an
|
|
126
|
+
app whose direction says warm is the single loudest contradiction in the build.
|
|
177
127
|
|
|
178
128
|
**Set the theme once, don't hardcode colours per component.** A screen full of
|
|
179
|
-
inline
|
|
180
|
-
|
|
129
|
+
inline hex values and `bg-blue-500` is why apps look templated. Change the theme,
|
|
130
|
+
not the components — and keep it theme-aware for light and dark.
|
|
181
131
|
|
|
182
132
|
With two apps, **share the theme package and vary the register, not the
|
|
183
133
|
palette.** A back-office can be denser and more tabular; a customer-facing app
|
|
184
134
|
can be roomier and warmer — that is `structure` and layout, not a second `brand`.
|
|
185
135
|
Two unrelated colour schemes read as two products from two companies.
|
|
186
136
|
|
|
187
|
-
|
|
188
137
|
## If the user gave you no direction
|
|
189
138
|
|
|
190
139
|
Neutral is a legitimate answer for an internal tool. But say so out loud when you
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-changes
|
|
3
|
-
description: 'Work a project''s changes queue — the todo list someone filed by circling things on a deployed stage. Covers `pikku changes wait|claim|show|ask|reply|shot|done`: waiting for work without polling, asking instead of guessing, saying why an item is left undone, offering options as images, one commit per item. TRIGGER when: the user says "run the pikkufabric changes", "run the changes against
|
|
3
|
+
description: 'Work a project''s changes queue — the todo list someone filed by circling things on a deployed stage. Covers `pikku changes wait|claim|show|ask|reply|shot|done`: waiting for work without polling, asking instead of guessing, saying why an item is left undone, offering options as images, one commit per item. TRIGGER when: the user says "run the pikkufabric changes", "run the changes against a stage", "work the changes (queue)", "watch the changes", "pick up the changes", names a change by its #number, or you are otherwise idle in a checkout with open changes (`pikku changes list`). DO NOT TRIGGER for git changes, diffs or changelogs, and not for deploying or debugging a stage — use pikku-fabric for those.'
|
|
4
4
|
installGroups: [fabric]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -11,8 +11,7 @@ screenshot of what they saw, and the elements the circle enclosed. You have the
|
|
|
11
11
|
Empty the queue without making them regret filing.
|
|
12
12
|
|
|
13
13
|
Run every command from the checkout. The queue lives in it (`.git/pikku-changes.json`, shared by every
|
|
14
|
-
worktree).
|
|
15
|
-
`--json` works on all of them. Items are addressed as `2`, `#2` or their uuid.
|
|
14
|
+
worktree). `--json` works on all of them. Items are addressed as `2`, `#2` or their uuid.
|
|
16
15
|
|
|
17
16
|
**Launched by `pikku changes next`?** `pikku changes next` picks one agent: a merge conflict or a changeset with no plan
|
|
18
17
|
goes back to a changes agent; open changes go to a changes agent; a pikku version bump goes to an
|
|
@@ -104,8 +103,8 @@ changeset declared no `--creates`/`--alters`.
|
|
|
104
103
|
|
|
105
104
|
When other agents are working changesets at the same time (your work says so), claim with
|
|
106
105
|
`--worktree`: it creates `changeset/<slug>` in its own checkout beside the repo and prints the path.
|
|
107
|
-
Build and commit there and run `done` there; the merge removes the worktree. If the claim is refused
|
|
108
|
-
clash, or stop.
|
|
106
|
+
Build and commit there and run `done` there; the merge removes the worktree. If the claim is refused
|
|
107
|
+
because of a running changeset, claim one that does not clash, or stop.
|
|
109
108
|
|
|
110
109
|
## Reading an item
|
|
111
110
|
|
|
@@ -184,7 +183,8 @@ Scope names the screen they were looking at, not the file you edited.
|
|
|
184
183
|
pikku changes done --change-id 7 --note "What you did, for whoever reads the thread"
|
|
185
184
|
```
|
|
186
185
|
|
|
187
|
-
Branch and commit default to the checkout you are in — run it there, never type a sha. `done` finds
|
|
186
|
+
Branch and commit default to the checkout you are in — run it there, never type a sha. `done` finds
|
|
187
|
+
the item's commit by its `Change-Id` trailer, so close items in any order.
|
|
188
188
|
An item you decided not to do is not `done`: `reply` with why and leave it for a
|
|
189
189
|
human to dismiss.
|
|
190
190
|
|
|
@@ -13,9 +13,7 @@ installGroups: [core]
|
|
|
13
13
|
|
|
14
14
|
# Pikku Framework Concepts
|
|
15
15
|
|
|
16
|
-
##
|
|
17
|
-
|
|
18
|
-
Use this skill as an execution checklist, not reference material.
|
|
16
|
+
## Operating procedure
|
|
19
17
|
|
|
20
18
|
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
19
|
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.
|
|
@@ -43,7 +41,7 @@ that will work anyway.
|
|
|
43
41
|
pikku doc --ai
|
|
44
42
|
```
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
Prints the `#pikku/*` doors, grouped by the job they do, and beside each the
|
|
47
45
|
skill that teaches it. Read that routing table as the index to every other pikku skill — it is
|
|
48
46
|
generated from the installed version, so it never names a skill for a door that no longer exists.
|
|
49
47
|
|
|
@@ -148,7 +146,7 @@ pikkuFunc (pure business logic)
|
|
|
148
146
|
├── wireChannel → WebSocket (real-time)
|
|
149
147
|
├── wireQueueWorker → BullMQ, PgBoss (async jobs)
|
|
150
148
|
├── wireScheduler → Cron (scheduled tasks)
|
|
151
|
-
├──
|
|
149
|
+
├── mcp: true → Model Context Protocol (AI tools; an option on the function, no wire call)
|
|
152
150
|
├── wireCLI → CLI commands
|
|
153
151
|
├── wireTrigger → Event-driven (Redis pub/sub, PG LISTEN/NOTIFY)
|
|
154
152
|
├── pikkuAgent → AI agents / chatbots
|
|
@@ -162,6 +160,8 @@ A `pikkuFunc` receives three things:
|
|
|
162
160
|
2. **Data** — input from any source (HTTP body/query/params, WS message, queue payload, CLI args)
|
|
163
161
|
3. **Wire** — transport context (session, channel, rpc, mcp, http, queue)
|
|
164
162
|
|
|
163
|
+
**MCP tools:** always set `mcp: true` on the normal function; HTTP, RPC and agents can still call it. The one exception is wrapping an EXISTING function as an AI tool purely to reshape its data: use `pikkuMCPToolFunc` (calls the original via `rpc.invoke`), which makes the wrapper MCP-only. The alternative is a second normal function with `mcp: true`. See `pikku-wiring`.
|
|
164
|
+
|
|
165
165
|
The function never imports Express, never reads `req.body`, never touches `ws.send()`. It just works with typed data and services.
|
|
166
166
|
|
|
167
167
|
## Concept Mapping: Generic Backend → Pikku
|
|
@@ -353,7 +353,7 @@ src/
|
|
|
353
353
|
## What Language You Write In
|
|
354
354
|
|
|
355
355
|
Three different things in a Pikku project have a human language, and they are **not** the same
|
|
356
|
-
language. Collapsing them
|
|
356
|
+
language. Collapsing them is the common mistake:
|
|
357
357
|
|
|
358
358
|
| Axis | Covers | Decided by |
|
|
359
359
|
| --------------- | ----------------------------------------------------------------------------- | ---------------------------------------------- |
|
|
@@ -370,8 +370,8 @@ language is cloned from.
|
|
|
370
370
|
|
|
371
371
|
**When a brief tells you the product speaks a language, it is telling you about the third axis and
|
|
372
372
|
nothing else.** `references/language.md` has the three settings that satisfy such a brief, and the
|
|
373
|
-
|
|
374
|
-
RPC names, German tables,
|
|
373
|
+
failure to avoid: reading one sentence about a product's users as an instruction about its codebase
|
|
374
|
+
(German RPC names, German tables, no English catalogue to translate from).
|
|
375
375
|
|
|
376
376
|
## Environment Variables
|
|
377
377
|
|
|
@@ -408,12 +408,3 @@ expect(result.todos).toHaveLength(3)
|
|
|
408
408
|
## Available Packages
|
|
409
409
|
|
|
410
410
|
Pikku ships runtime adapters (`@pikku/express-server`, `@pikku/fastify-server`, `@pikku/next`, `@pikku/aws-lambda`, `@pikku/cloudflare`, `@pikku/uws-server`, `@pikku/modelcontextprotocol`, ...) and service packages (`@pikku/jose`, `@pikku/schema-ajv`, `@pikku/pino`, `@pikku/kysely`, `@pikku/redis`, `@pikku/queue-bullmq`, `@pikku/queue-pg-boss`, ...). For the full list with use cases, read `references/packages.md`.
|
|
411
|
-
|
|
412
|
-
## Key Differences from Traditional Frameworks
|
|
413
|
-
|
|
414
|
-
1. **No decorators** — plain functions + explicit wiring, not `@Get()` or `@Injectable()`
|
|
415
|
-
2. **No classes required** — everything is functions and objects
|
|
416
|
-
3. **Transport is configuration, not code** — business logic doesn't know about HTTP/WS/etc.
|
|
417
|
-
4. **One function, many transports** — same function can serve HTTP, WebSocket, queue, and MCP simultaneously
|
|
418
|
-
5. **Generated type safety** — clients are auto-generated with full types, not manually maintained
|
|
419
|
-
6. **Schema-first validation** — Standard Schema (Zod/Valibot) replaces class-validator decorators
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Concept Mapping: Generic Backend → Pikku
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Quick Reference Table
|
|
6
|
+
- Route Handler / Controller
|
|
7
|
+
- Route Parameters
|
|
8
|
+
- Middleware
|
|
9
|
+
- Auth Guard
|
|
10
|
+
- Authorization / Role Checks
|
|
11
|
+
- DTO / Request Validation
|
|
12
|
+
- Dependency Injection
|
|
13
|
+
- WebSocket Handlers
|
|
14
|
+
- Job Queue Workers
|
|
15
|
+
- Cron / Scheduled Tasks
|
|
16
|
+
- Module / Feature Grouping
|
|
17
|
+
- Error Handling
|
|
18
|
+
- Session Management
|
|
19
|
+
- API Client Generation
|
|
20
|
+
|
|
3
21
|
Authoritative mapping table plus side-by-side code examples showing how common backend patterns translate to Pikku.
|
|
4
22
|
|
|
5
23
|
- [Quick Reference Table](#quick-reference-table)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pikku-convert-screen
|
|
3
|
+
description: Use when an old screen, page or feature (from an old console, another repo, screenshots, or a description) has to be rebuilt in the current app. Extracts what it does as plain business logic, with no layout or code detail, then designs it fresh in the current design system. TRIGGER on "convert this screen", "port the old console page", "we had this before, bring it back", "redesign X from the old console". DO NOT TRIGGER for whole-repo reverse engineering (use pikku-software-archaeology) or for restyling a screen that already exists in the app.
|
|
4
|
+
installGroups: [core]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Convert a screen
|
|
8
|
+
|
|
9
|
+
Keep what the screen does. Drop how it looked and how it was built.
|
|
10
|
+
|
|
11
|
+
## 1. Read the old thing
|
|
12
|
+
|
|
13
|
+
Use whatever is given: a path, a repo, screenshots, a recording, a description. For code, read the page, its hooks, the RPCs or endpoints behind it and any tests. For images, read the labels and states, not the styling. Check whether the app already has it under another name before starting.
|
|
14
|
+
|
|
15
|
+
## 2. Write the spec
|
|
16
|
+
|
|
17
|
+
Save it as `docs/screens/<name>.md`. Business logic only.
|
|
18
|
+
|
|
19
|
+
- Who uses it and what they are trying to get done.
|
|
20
|
+
- What they can see, in the words a non-technical owner would use.
|
|
21
|
+
- Every action: what triggers it, what must be true first, what changes, what the person is told afterwards.
|
|
22
|
+
- Rules and limits: who may do it, what is refused and why.
|
|
23
|
+
- States: empty, loading, working, done, failed. For each failure, what the person can do next.
|
|
24
|
+
- Data it needs and which existing RPCs provide it. Mark any that are missing.
|
|
25
|
+
- Things you were unsure of, marked as open questions. Never present a guess as fact.
|
|
26
|
+
|
|
27
|
+
Leave out: component names, layout, colours, spacing, library names, file structure, modals versus drawers, table versus cards. If a sentence only makes sense by picturing the old UI, rewrite it.
|
|
28
|
+
|
|
29
|
+
Stop and show the spec. Do not design until it is agreed.
|
|
30
|
+
|
|
31
|
+
## 3. Build it in the current design system
|
|
32
|
+
|
|
33
|
+
Design from the spec, not from the old screen. Never copy old markup or styles.
|
|
34
|
+
|
|
35
|
+
- Read the project design system (`DESIGN.md`, theme tokens) and the nearest existing screens first. Match their header, spacing, buttons and wording.
|
|
36
|
+
- Use the components the project already uses. No modal dialogs: forms go in the side panel or a new page.
|
|
37
|
+
- Plain words. The client's own vocabulary, never internal jargon.
|
|
38
|
+
- Every error says what happened in plain terms and offers a way out, with an AI fix where it makes sense.
|
|
39
|
+
- No static data. The screen reads through RPC hooks only. Data a function does not return yet comes from `.mocks/` through the stub hooks (see the pikku-build skill, mocks reference), with a populated, an empty and an error scenario.
|
|
40
|
+
- The same component serves every data state: real, mocked or stubbed.
|
|
41
|
+
- Add the route and a rail entry only where the spec says people reach it.
|
|
42
|
+
|
|
43
|
+
## 4. Check against the spec
|
|
44
|
+
|
|
45
|
+
Walk the spec line by line against the running screen, including the empty and failed states. Say which lines are met, which are not, and which depend on RPCs that do not exist yet. Capture a screenshot from the running screen.
|
|
@@ -69,4 +69,4 @@ app.ws('/*', pikkuWebsocketHandler({ logger, logRoutes: true }))
|
|
|
69
69
|
|
|
70
70
|
Both take `{ logger, logRoutes?, loadSchemas? } & RunHTTPWiringOptions`.
|
|
71
71
|
|
|
72
|
-
For a WebSocket-only server on the `ws` library instead,
|
|
72
|
+
For a WebSocket-only server on the `ws` library instead, use the `ws` runtime.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
`@pikku/ws` connects Pikku's channel system to a Node.js WebSocket server built
|
|
4
4
|
on the [ws](https://github.com/websockets/ws) library. Use it for a
|
|
5
|
-
WebSocket-only server; for HTTP and WebSocket on one port
|
|
6
|
-
the WebSocket server shares a port with an existing HTTP app
|
|
7
|
-
|
|
5
|
+
WebSocket-only server; for HTTP and WebSocket on one port use the uWebSockets.js runtime, and when
|
|
6
|
+
the WebSocket server shares a port with an existing HTTP app use the Express or
|
|
7
|
+
Fastify runtime.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
yarn add @pikku/ws ws
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-emails
|
|
3
3
|
description: >-
|
|
4
|
-
Use when working with Pikku's file-based email templates: authoring
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
SendTemplateEmailInput, LocalEmailService, or imports from .pikku/email/pikku-emails.gen.
|
|
8
|
-
TRIGGER when: the project has an emails/ directory (templates/, locales/, partials/, theme.json)
|
|
9
|
-
or emailTemplatesDir in pikku.config.json. TRIGGER when: user asks to add/edit a transactional
|
|
10
|
-
email (verification, password reset, invitation, receipt), wire email sending, or translate an
|
|
11
|
-
email. DO NOT TRIGGER when: user asks about i18n for the app UI (use pikku-i18n) or auth flows
|
|
12
|
-
in general (use pikku-auth).
|
|
4
|
+
Use when working with Pikku's file-based email templates: authoring html, subject and text templates, locales, partials and theme, running `pikku emails generate`, and rendering and sending them through an EmailService.
|
|
5
|
+
TRIGGER when: code uses renderEmailTemplate, EmailTemplateName, EmailService, SendTemplateEmailInput or LocalEmailService, or imports from .pikku/email/pikku-emails.gen; the project has an emails/ directory (templates/, locales/, partials/, theme.json) or emailTemplatesDir in pikku.config.json; or the user asks to add, edit or translate a transactional email (verification, password reset, invitation, receipt) or wire email sending.
|
|
6
|
+
DO NOT TRIGGER when: the task is i18n for the app UI (use pikku-i18n) or sign-in flows in general (use pikku-auth).
|
|
13
7
|
installGroups: [core]
|
|
14
8
|
---
|
|
15
9
|
|