@pikku/skills 0.12.10 → 0.12.12
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 +819 -0
- package/dist/skills.gen.js +1 -1
- package/package.json +4 -4
- package/skills/pikku-addon/SKILL.md +17 -11
- package/skills/pikku-addon/references/addon-package-manifest.md +2 -2
- package/skills/pikku-agent/SKILL.md +4 -5
- package/skills/pikku-audit/SKILL.md +28 -13
- package/skills/pikku-aws/SKILL.md +2 -2
- package/skills/pikku-better-auth/SKILL.md +97 -17
- package/skills/pikku-build-app/SKILL.md +621 -0
- package/skills/pikku-build-app/references/multi-app.md +117 -0
- package/skills/pikku-build-app/references/ship.md +98 -0
- package/skills/pikku-build-app/references/theming.md +70 -0
- package/skills/pikku-build-platform/SKILL.md +239 -0
- package/skills/pikku-build-quick/SKILL.md +238 -0
- package/skills/pikku-cli/SKILL.md +7 -7
- package/skills/pikku-cli/references/complete-example.md +1 -1
- package/skills/pikku-concepts/SKILL.md +5 -2
- package/skills/pikku-concepts/references/concept-mapping.md +1 -1
- package/skills/pikku-config/SKILL.md +5 -3
- package/skills/pikku-deploy-azure/SKILL.md +5 -4
- package/skills/pikku-emails/SKILL.md +28 -7
- package/skills/pikku-fabric/SKILL.md +27 -3
- package/skills/pikku-fabric-debug/SKILL.md +1 -1
- package/skills/pikku-feature/SKILL.md +5 -4
- package/skills/pikku-http/SKILL.md +4 -4
- package/skills/pikku-http/references/http-options.md +13 -13
- package/skills/pikku-i18n/SKILL.md +2 -1
- package/skills/pikku-info/SKILL.md +1 -1
- package/skills/pikku-knowledge/SKILL.md +13 -13
- package/skills/pikku-mcp/SKILL.md +4 -4
- package/skills/pikku-middleware/SKILL.md +5 -5
- package/skills/pikku-middleware/references/middleware-patterns.md +14 -8
- package/skills/pikku-n8n-import/SKILL.md +12 -12
- package/skills/pikku-n8n-import/SPEC.md +3 -0
- package/skills/pikku-n8n-import/references/code-translation.md +26 -22
- package/skills/pikku-n8n-import/references/loops-and-control.md +7 -7
- package/skills/pikku-paraglide/SKILL.md +11 -6
- package/skills/pikku-permissions/SKILL.md +19 -15
- package/skills/pikku-product-second-opinion/README.md +3 -3
- package/skills/pikku-product-second-opinion/example/sample-report.md +15 -12
- package/skills/pikku-product-second-opinion/references/report-template.md +10 -7
- package/skills/pikku-queue/SKILL.md +1 -1
- package/skills/pikku-react/SKILL.md +53 -13
- package/skills/pikku-realtime/SKILL.md +52 -21
- package/skills/pikku-rpc/SKILL.md +4 -2
- package/skills/pikku-rtl/SKILL.md +1 -1
- package/skills/pikku-scenario/SKILL.md +131 -20
- package/skills/pikku-schedule/SKILL.md +6 -1
- package/skills/pikku-schema-ajv/SKILL.md +2 -2
- package/skills/pikku-schema-cfworker/SKILL.md +1 -1
- package/skills/pikku-security/SKILL.md +9 -5
- package/skills/pikku-services/SKILL.md +27 -18
- package/skills/pikku-services/references/audit-wire-service.md +14 -8
- package/skills/pikku-software-archaeology/scripts/validate.mjs +146 -69
- package/skills/pikku-tag-middleware/SKILL.md +1 -0
- package/skills/pikku-template-clone/SKILL.md +2 -1
- package/skills/pikku-trigger/SKILL.md +3 -3
- package/skills/pikku-websocket/SKILL.md +4 -3
- package/skills/pikku-workflow/SKILL.md +2 -2
- package/skills/pikku-workflow/references/workflow-reference.md +24 -10
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Adding a second frontend
|
|
2
|
+
|
|
3
|
+
Read this when the split you recorded in Phase 2 is "separate apps" and you have
|
|
4
|
+
reached the milestone that needs the second one. **Not before.** Cloning
|
|
5
|
+
`apps/app` materialises a directory of copied screens; doing it during planning
|
|
6
|
+
leaves `pikkufabric.config.json` pointing at an app nobody has designed yet.
|
|
7
|
+
|
|
8
|
+
If the split is "one app with paths", you never need this file — add route
|
|
9
|
+
segments under `/app` and give each audience its own entries in `useNavItems()`.
|
|
10
|
+
|
|
11
|
+
## The clone
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cp -R apps/app apps/admin
|
|
15
|
+
rm -rf apps/admin/node_modules apps/admin/src/paraglide
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`src/paraglide` is compiled from `messages/` by the Vite plugin on first run;
|
|
19
|
+
copying it forward ships one app's compiled strings inside another.
|
|
20
|
+
|
|
21
|
+
Then, in order:
|
|
22
|
+
|
|
23
|
+
### 1. `apps/admin/package.json`
|
|
24
|
+
|
|
25
|
+
- `name` → `@project/admin`
|
|
26
|
+
- `dev` and `preview` ports → `7105`. Every frontend needs its own, or the second
|
|
27
|
+
one fails to bind and the dev runner looks like it hung.
|
|
28
|
+
- the `--tsBuildInfoFile` path inside the **`tsc` script** → `admin-tsc.tsbuildinfo`.
|
|
29
|
+
In this template it is a CLI flag on that script (`tsc --noEmit --incremental
|
|
30
|
+
--tsBuildInfoFile node_modules/.cache/app-tsc.tsbuildinfo`), not a
|
|
31
|
+
`compilerOptions` entry — `tsconfig.json` needs no change. Left alone, the two
|
|
32
|
+
apps fight over one incremental cache and you get type errors that vanish on a
|
|
33
|
+
clean build: an hour of debugging for a one-word edit.
|
|
34
|
+
|
|
35
|
+
### 2. `pikkufabric.config.json`
|
|
36
|
+
|
|
37
|
+
This file is the source of truth for what apps exist, and it is read whether or
|
|
38
|
+
not you ever deploy to Fabric.
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"projectId": "__PROJECT_ID__",
|
|
43
|
+
"frontends": {
|
|
44
|
+
"app": {
|
|
45
|
+
"cwd": "apps/app", "primary": true, "deploy": true, "kind": "ssr",
|
|
46
|
+
"dev": { "command": ["bun", "run", "dev"], "port": 7104, "healthPath": "/" },
|
|
47
|
+
"serves": "tenant",
|
|
48
|
+
"personas": ["visitor", "chidi"]
|
|
49
|
+
},
|
|
50
|
+
"admin": {
|
|
51
|
+
"cwd": "apps/admin", "primary": false, "deploy": true, "kind": "ssr",
|
|
52
|
+
"dev": { "command": ["bun", "run", "dev"], "port": 7105, "healthPath": "/" },
|
|
53
|
+
"serves": "owner",
|
|
54
|
+
"personas": ["amina", "bilal"]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Two things to fix while you are in here, not just add:
|
|
61
|
+
|
|
62
|
+
- **The shipped `app` entry may say `["yarn", "dev"]`** while the rest of the
|
|
63
|
+
project is driven with bun. Correct it. A frontend that starts under a package
|
|
64
|
+
manager the project does not use is a failure that only appears on someone
|
|
65
|
+
else's machine.
|
|
66
|
+
- **`serves` and `personas` name real personas.** Every persona should appear
|
|
67
|
+
under exactly one frontend. A persona listed nowhere is a person with no way
|
|
68
|
+
in, and that is a design bug worth seeing now rather than at review.
|
|
69
|
+
|
|
70
|
+
### 3. The dev runner
|
|
71
|
+
|
|
72
|
+
`dev.mjs`, under the project's scripts directory, spawns `@project/app` **by
|
|
73
|
+
name** and will silently never start your second app — the frontend simply is not
|
|
74
|
+
there, with no error to explain it.
|
|
75
|
+
|
|
76
|
+
Make it read the `frontends` map and spawn one child per entry, rather than
|
|
77
|
+
adding a second hardcoded line. Two sources of truth for "which apps exist" is
|
|
78
|
+
the drift this whole file is trying to avoid.
|
|
79
|
+
|
|
80
|
+
### 4. `pikku.config.json` → `environments`
|
|
81
|
+
|
|
82
|
+
`local.appUrl` points at one app. Add an environment per frontend (`local`,
|
|
83
|
+
`local-admin`) so the browser scenario pass can drive either one. A browser
|
|
84
|
+
scenario run against the wrong `appUrl` fails on a missing element and reads like
|
|
85
|
+
a UI bug rather than a config one.
|
|
86
|
+
|
|
87
|
+
### 5. Re-run `bun install`
|
|
88
|
+
|
|
89
|
+
`apps/*` is already globbed in the root workspaces, so this just links the new
|
|
90
|
+
one.
|
|
91
|
+
|
|
92
|
+
## Sessions across two origins
|
|
93
|
+
|
|
94
|
+
Better Auth lives once, at `/api/auth/*`, and every app proxies to it (see
|
|
95
|
+
`vite.config.ts` — `/api/auth` keeps its prefix, everything else under `/api` is
|
|
96
|
+
rewritten to the pikku dev server).
|
|
97
|
+
|
|
98
|
+
- **In local dev, cookies are scoped by host and ignore the port**, so
|
|
99
|
+
`localhost:7104` and `localhost:7105` share a session. Convenient, and a trap:
|
|
100
|
+
the app boundary is invisible in dev and only the role check is doing work.
|
|
101
|
+
That is the correct design — but do not read a working dev session as evidence
|
|
102
|
+
the permission check exists. The refusal scenario is the evidence.
|
|
103
|
+
- **In production on two subdomains**, the session cookie needs a parent domain
|
|
104
|
+
(`.example.com`) or each app gets its own login. Decide which, set it per the
|
|
105
|
+
`pikku-better-auth` skill, and record it in `knowledge/decisions/security/`.
|
|
106
|
+
- **Never hardcode a host or port.** The API base resolves to same-origin `/api`.
|
|
107
|
+
|
|
108
|
+
## Building the second app's screens
|
|
109
|
+
|
|
110
|
+
Same rules as the first: pages in `apps/admin/src/pages/`, routes in
|
|
111
|
+
`apps/admin/src/routes/`, the same generated hooks from
|
|
112
|
+
`@project/functions-sdk/pikku/api.gen`, its own `useNavItems()`, its own
|
|
113
|
+
`messages/` directory.
|
|
114
|
+
|
|
115
|
+
A string used by both apps belongs to whichever app renders it. Duplicating it
|
|
116
|
+
beats a shared bundle that couples the two apps together — the moment they share
|
|
117
|
+
a string file, they share a release.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Shipping, and staying Fabric-ready
|
|
2
|
+
|
|
3
|
+
Read this when the milestones are built and the scenarios are green — it is
|
|
4
|
+
the last phase, and nothing in it is needed before then.
|
|
5
|
+
|
|
6
|
+
## Ship it — open source, no platform
|
|
7
|
+
|
|
8
|
+
`pikku deploy` builds and ships without any hosted service:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
bunx --bun pikku deploy plan --provider standalone --runtime bun
|
|
12
|
+
bunx --bun pikku deploy apply --provider standalone --runtime bun
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`standalone` comes from the installed `@pikku/deploy-standalone` adapter: it
|
|
16
|
+
bundles the project into a single unit and emits either a `bundle.js` you run
|
|
17
|
+
with Node, or a self-contained executable compiled with `bun build --compile`.
|
|
18
|
+
`cloudflare` (the default) and `aws` are the other providers — read the
|
|
19
|
+
`pikku-deploy-cloudflare` skill before using it, as it ships the handler
|
|
20
|
+
factories the deploy codegen expects, and hand-rolling an `ExportedHandler` is
|
|
21
|
+
how a worker deploy fails at runtime instead of at build.
|
|
22
|
+
|
|
23
|
+
Always run `plan` before `apply`, and read it. It names what will be created,
|
|
24
|
+
updated and deleted — the deletions are the reason to look.
|
|
25
|
+
|
|
26
|
+
The frontends build independently (`bun run build` at the root builds every
|
|
27
|
+
workspace). Serve each behind its own hostname, and put the API behind `/api` on
|
|
28
|
+
**all of them**, mirroring the Vite proxy from the multi-app reference: `/api/auth/*` keeps its
|
|
29
|
+
prefix, everything else under `/api/*` reaches the pikku server unprefixed. Get
|
|
30
|
+
this wrong and sign-in fails on one app only, which is a miserable thing to debug.
|
|
31
|
+
|
|
32
|
+
Before shipping, run the full gate:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
bunx --bun pikku all --tsc-summary --fail-on-warn
|
|
36
|
+
bunx --bun pikku validate
|
|
37
|
+
bunx --bun pikku knowledge validate
|
|
38
|
+
bunx --bun pikku scenario run local --spawn --coverage
|
|
39
|
+
bunx --bun pikku scenario run local --spawn --run browser
|
|
40
|
+
bun run build # every frontend workspace, type-checked
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Keep `--coverage` on the release run even though you have been reading it per
|
|
44
|
+
milestone (§7a). Each of those readings only covered the functions that
|
|
45
|
+
milestone added; this is the first time the whole surface is measured at once,
|
|
46
|
+
and it is where a function orphaned by a later refactor shows up.
|
|
47
|
+
|
|
48
|
+
**The last two lines are not optional, and one of them is easy to talk yourself
|
|
49
|
+
out of.** The server-side pass proves the functions; it renders nothing. The
|
|
50
|
+
pages are client-rendered, so a component that throws still returns HTTP 200
|
|
51
|
+
with an empty shell — the same trap §6 warns about, and the release gate is
|
|
52
|
+
exactly where it gets shipped past. Run the browser pass, and run it **for every
|
|
53
|
+
environment in `pikkufabric.config.json`**, not just the first:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
bunx --bun pikku scenario run local-admin --spawn --run browser
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`bun run build` is what type-checks each frontend (each app's `tsc` script runs
|
|
60
|
+
`--noEmit`). `pikku all --tsc-summary` covers the functions package; it does not
|
|
61
|
+
reach into the apps, so a broken screen passes every pikku command and fails on
|
|
62
|
+
the deploy.
|
|
63
|
+
|
|
64
|
+
`pikku all --security --fail-on-error` additionally runs the data-classification
|
|
65
|
+
lint over function return types, catching a `Pii`/`Secret` field that leaks
|
|
66
|
+
through an exposed function. Expensive, so run it before a release rather than on
|
|
67
|
+
every save — but run it.
|
|
68
|
+
|
|
69
|
+
## Staying Fabric-ready
|
|
70
|
+
|
|
71
|
+
Everything above is open source. This is the contract that keeps
|
|
72
|
+
`pikku fabric init` a one-command import later, instead of a migration.
|
|
73
|
+
|
|
74
|
+
- **`pikkufabric.config.json` describes reality.** Every app has an entry with
|
|
75
|
+
the right `cwd`, `port`, `kind` and `dev.command`; exactly one is `primary`;
|
|
76
|
+
`serves` and `personas` name real personas from the personas section. Leave `projectId` as
|
|
77
|
+
`__PROJECT_ID__` — that placeholder means "unlinked", and `fabric init` writes
|
|
78
|
+
the real one. Do not invent a value to make it look configured.
|
|
79
|
+
- **One `definePersonas` call**, every persona reachable through exactly one
|
|
80
|
+
frontend. Fabric materialises these as its virtual users; a persona nobody
|
|
81
|
+
serves imports as a person with no way in.
|
|
82
|
+
- **`knowledge/` passes `validate`, with every milestone at `built`.** This is
|
|
83
|
+
the part Fabric itself reads and continues from.
|
|
84
|
+
- **Every milestone has a passing scenario**, including its refusals.
|
|
85
|
+
- **Permissions live in the `permissions` field**, not in function bodies and not
|
|
86
|
+
in the frontends. A check hidden in a component does not survive a new client.
|
|
87
|
+
- **Nothing hardcodes a host, a port, or a `process.env` read inside a
|
|
88
|
+
function.** Secrets go through `defineSecret` and the injected `secrets`
|
|
89
|
+
service. This is the single most common reason a working local project fails
|
|
90
|
+
its first deploy — on any platform.
|
|
91
|
+
- **Generated files stay generated.** No hand edits to `.pikku/`, `*.gen.*`, or
|
|
92
|
+
the SDK.
|
|
93
|
+
- **`pikku validate` is clean**, and `pikku all` has no critical diagnostics.
|
|
94
|
+
|
|
95
|
+
What you deliberately do NOT do: run any `pikku fabric` subcommand, add a card,
|
|
96
|
+
or link a project. None of it is needed to build, test, critique, or deploy — it
|
|
97
|
+
is needed the day the user wants Fabric to host and build it, and on that day, if
|
|
98
|
+
this section holds, that day is one command long.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Authoring the theme
|
|
2
|
+
|
|
3
|
+
Read this at the design step, once you have a direction to turn into a theme —
|
|
4
|
+
whether the user described one in words, handed you a reference, or ran their own
|
|
5
|
+
design step whose output you are implementing.
|
|
6
|
+
|
|
7
|
+
Nothing in the open-source toolchain authors a theme for you. Fabric has the
|
|
8
|
+
`fabric-theme` tool; without it, hand-authoring is the route, and it is a small
|
|
9
|
+
job done in JSON.
|
|
10
|
+
|
|
11
|
+
## Where the look lives
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
packages/mantine-theme/
|
|
15
|
+
themes/
|
|
16
|
+
default.json # "Neutral" — the shipped scaffold
|
|
17
|
+
index.ts # registers each theme JSON by id
|
|
18
|
+
active.json # { "id": "default" } — which one is live
|
|
19
|
+
index.ts
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each theme JSON has two halves — `brand` (colours, fonts) and `structure`
|
|
23
|
+
(radius, shadows, spacing, per-component `defaultProps`). To give the product an
|
|
24
|
+
identity, add `themes/<name>.json`, register it in `themes/index.ts`, and point
|
|
25
|
+
`active.json` at its id.
|
|
26
|
+
|
|
27
|
+
`themes/index.ts` carries a `Generated by the fabric-theme tool — do not edit by
|
|
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.
|
|
33
|
+
|
|
34
|
+
Turning §1's answer into a theme:
|
|
35
|
+
|
|
36
|
+
- **Colour before anything else.** `brand.colors.primary` plus
|
|
37
|
+
`structure.primaryShade` and `autoContrast` carry most of the identity;
|
|
38
|
+
`@mantine/colors-generator` (already a dependency) expands one hex into a full
|
|
39
|
+
scale.
|
|
40
|
+
- **Fonts are the other half of the register**, and the half people skip. A serif
|
|
41
|
+
heading font against a neutral body is a different product from the system
|
|
42
|
+
stack, and it is one field.
|
|
43
|
+
- **`structure.radius` and `structure.shadows` set the temperature.** Sharp
|
|
44
|
+
corners and flat surfaces read technical; large radii and soft shadows read
|
|
45
|
+
consumer. Neutral's `md: 10px` is the middle of the road on purpose.
|
|
46
|
+
- **`structure.components` defaultProps is where a design decision becomes
|
|
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.
|
|
51
|
+
|
|
52
|
+
Then **write the direction into `knowledge/decisions/design/`** — the words the
|
|
53
|
+
user gave you, what you chose, and what it rules out. The JSON records what the
|
|
54
|
+
theme is; only the note records why.
|
|
55
|
+
|
|
56
|
+
**Set the theme once, don't hardcode colours per component.** A screen full of
|
|
57
|
+
inline `color="blue"` and one-off hex values is why apps look templated. Change
|
|
58
|
+
the theme, not the components — and keep it theme-aware for light and dark.
|
|
59
|
+
|
|
60
|
+
With two apps, **share the theme package and vary the register, not the
|
|
61
|
+
palette.** A back-office can be denser and more tabular; a customer-facing app
|
|
62
|
+
can be roomier and warmer — that is `structure` and layout, not a second `brand`.
|
|
63
|
+
Two unrelated colour schemes read as two products from two companies.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## If the user gave you no direction
|
|
67
|
+
|
|
68
|
+
Neutral is a legitimate answer for an internal tool. But say so out loud when you
|
|
69
|
+
hand the work over — an unremarked default reads as a choice, and the user will
|
|
70
|
+
assume someone decided.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pikku-build-platform
|
|
3
|
+
description: >-
|
|
4
|
+
Build an app that exercises every Pikku surface — workflows, schedules, queues, an AI agent,
|
|
5
|
+
realtime, MCP, multiple locales, contract versioning and scenario coverage — on top of the full
|
|
6
|
+
pikku-build-app workflow. For proving what the platform does, not for shipping the smallest
|
|
7
|
+
thing that works. TRIGGER when: the user picked "Platform", asked for a showcase or reference
|
|
8
|
+
app, or asked to demonstrate what Pikku can do. DO NOT TRIGGER when: the user wants a product
|
|
9
|
+
built (use pikku-build-app), something quick (use pikku-build-quick), or one specific surface
|
|
10
|
+
wired into an existing app — a single workflow, cron job or agent (use that surface's own skill,
|
|
11
|
+
e.g. pikku-workflow, pikku-cron, pikku-agent).
|
|
12
|
+
installGroups: [core]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Build a platform showcase on Pikku
|
|
16
|
+
|
|
17
|
+
**This skill is a delta. `pikku-build-app` is the base — read it and follow it in
|
|
18
|
+
full.** Everything there applies: knowledge base first, personas and roles,
|
|
19
|
+
milestones planned then built one at a time, scenarios, design pass, deploy
|
|
20
|
+
gates, Fabric-readiness. This file adds the surfaces that turn an app into a
|
|
21
|
+
demonstration of the platform, and says where each one slots into that workflow.
|
|
22
|
+
|
|
23
|
+
Read `pikku-build-app` now, then come back. Do not blend the two into one plan —
|
|
24
|
+
the phases below hang off its phases by number.
|
|
25
|
+
|
|
26
|
+
## What "platform" means here
|
|
27
|
+
|
|
28
|
+
Breadth is the deliverable. A showcase that does one thing beautifully has
|
|
29
|
+
failed; a showcase where every surface is a stub has also failed. The bar for
|
|
30
|
+
each surface below: **it does something the app genuinely needs, and a scenario
|
|
31
|
+
proves it.** A cron job that logs "tick" is not a schedule — it is a comment.
|
|
32
|
+
|
|
33
|
+
Budget the extra surfaces at one milestone each. They are not free, and a
|
|
34
|
+
half-wired workflow engine is worse than no workflow engine.
|
|
35
|
+
|
|
36
|
+
## Choosing surfaces — during `pikku-build-app` §5 (planning)
|
|
37
|
+
|
|
38
|
+
When you plan milestones, each surface below becomes its own milestone note in
|
|
39
|
+
`knowledge/milestones/`, ordered after the spine it depends on.
|
|
40
|
+
|
|
41
|
+
**Five are required, and if the domain does not motivate them you chose the
|
|
42
|
+
wrong domain:** workflows, schedules, queues, an AI agent, and realtime. They are
|
|
43
|
+
what "platform" means here, and a showcase missing one of them is a showcase of
|
|
44
|
+
something else. Pick a domain that needs all five — that choice is yours to make
|
|
45
|
+
at §1, and it is much cheaper than contriving a use later.
|
|
46
|
+
|
|
47
|
+
The rest — MCP, triggers and webhooks, extra locales, contract versioning,
|
|
48
|
+
addons — are chosen on merit. Write the motivation into the note. If you cannot
|
|
49
|
+
name what one of *those* is for in this app, drop it and say why in
|
|
50
|
+
`knowledge/decisions/`: a documented omission is a stronger showcase than a
|
|
51
|
+
contrived inclusion.
|
|
52
|
+
|
|
53
|
+
What is never acceptable is a required surface present as a stub. A cron job
|
|
54
|
+
that logs `tick` does not become a schedule by existing, and it is worse than
|
|
55
|
+
the documented omission because it claims to be finished.
|
|
56
|
+
|
|
57
|
+
Most surfaces are switched on by the CLI rather than hand-wired:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
bunx --bun pikku enable workflow # workflow workers
|
|
61
|
+
bunx --bun pikku enable agent # public agent endpoints
|
|
62
|
+
bunx --bun pikku enable events # realtime events channel + SSE stream
|
|
63
|
+
bunx --bun pikku enable remote-rpc # internal RPC queue worker + HTTP endpoint
|
|
64
|
+
bunx --bun pikku enable webhook # outgoing webhook delivery queue worker
|
|
65
|
+
bunx --bun pikku enable scenarios # scenario instrumentation
|
|
66
|
+
bunx --bun pikku enable console # console functions
|
|
67
|
+
bunx --bun pikku enable rpc # public RPC endpoint
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Each scaffolds a `*.gen.ts` and wires it. Run the enable, then `pikku all`, then
|
|
71
|
+
write the function — not the other way round.
|
|
72
|
+
|
|
73
|
+
## The surfaces
|
|
74
|
+
|
|
75
|
+
Each has an installed skill that is authoritative on its API. Read it before
|
|
76
|
+
writing the wiring; this section says what the surface is *for* and how to prove
|
|
77
|
+
it, not how to call it.
|
|
78
|
+
|
|
79
|
+
### Workflows — `pikku-workflow`
|
|
80
|
+
|
|
81
|
+
The one that most changes how an app is built. A workflow is a durable,
|
|
82
|
+
resumable, multi-step process — approval chains, onboarding, anything that waits
|
|
83
|
+
on a human or a timer and must survive a restart.
|
|
84
|
+
|
|
85
|
+
- **Motivation test:** is there a process here with more than one step and a
|
|
86
|
+
gap in the middle? If every operation completes in one request, you do not need
|
|
87
|
+
workflows and forcing one is noise.
|
|
88
|
+
- **Prove it:** a scenario that starts the workflow, advances it as a second
|
|
89
|
+
persona, and asserts the end state. `pikku-workflows-client` covers driving it
|
|
90
|
+
from the UI.
|
|
91
|
+
- Three workflows ship with the template. Read them before writing yours.
|
|
92
|
+
|
|
93
|
+
### Schedules — `pikku-schedule` / `pikku-cron`
|
|
94
|
+
|
|
95
|
+
Recurring work: a nightly rollup, a reminder sweep, an expiry pass.
|
|
96
|
+
|
|
97
|
+
- **Motivation test:** something in the domain becomes true with the passage of
|
|
98
|
+
time rather than a user action. Rent falls due. A trial ends. A report is
|
|
99
|
+
monthly.
|
|
100
|
+
- **Prove it:** invoke the scheduled function directly in a scenario and assert
|
|
101
|
+
its effect. Do not test by waiting.
|
|
102
|
+
|
|
103
|
+
### Queues — `pikku-queue`
|
|
104
|
+
|
|
105
|
+
Work that must happen but not now, and may retry: email fan-out, image
|
|
106
|
+
processing, third-party calls that fail.
|
|
107
|
+
|
|
108
|
+
- **Motivation test:** an operation the user should not wait for, or one that
|
|
109
|
+
fails in ways worth retrying.
|
|
110
|
+
- **Prove it:** enqueue in one scenario step, assert the effect in a `then`.
|
|
111
|
+
|
|
112
|
+
### An AI agent — `pikku-agent`, `pikku-ai-vercel`
|
|
113
|
+
|
|
114
|
+
The template ships agent wiring and `@ai-sdk/openai`. An agent that answers
|
|
115
|
+
questions over the app's own data is the showcase; a general chatbot is not.
|
|
116
|
+
|
|
117
|
+
- **Give it real tools** — your own exposed RPCs, so it answers from the
|
|
118
|
+
database rather than from the model. `pikku all --strict-meta` fails a tool
|
|
119
|
+
with no description, which is the quality gate here: an undescribed tool is
|
|
120
|
+
offered to the model under its bare name and it will misuse it.
|
|
121
|
+
- **Gate it.** `getAgentThreads` ships exposed and sessionless — PKU574 flags it.
|
|
122
|
+
Deciding who may reach the agent is part of building it.
|
|
123
|
+
- **Prove it** with a scenario that asks something only the database knows.
|
|
124
|
+
- Needs a model key. Read it through the injected `secrets` service with a
|
|
125
|
+
matching `defineSecret`, never `process.env`, or deploy has nothing to
|
|
126
|
+
provision (PKU951).
|
|
127
|
+
|
|
128
|
+
### Realtime and events — `pikku-realtime`, `pikku-websocket`
|
|
129
|
+
|
|
130
|
+
`pikku enable events` gives a realtime channel plus an SSE stream, and the
|
|
131
|
+
generated typed client.
|
|
132
|
+
|
|
133
|
+
- **Motivation test:** two people looking at the same thing at the same time, or
|
|
134
|
+
a long operation whose progress matters.
|
|
135
|
+
- **Prove it:** a browser scenario is the only honest proof — assert the second
|
|
136
|
+
persona's screen changed without a reload.
|
|
137
|
+
|
|
138
|
+
### MCP — `pikku-mcp`
|
|
139
|
+
|
|
140
|
+
Exposes functions as Model Context Protocol tools, so an outside agent can drive
|
|
141
|
+
the app. Cheap once functions exist, and a genuine differentiator to show.
|
|
142
|
+
|
|
143
|
+
### Triggers and webhooks — `pikku-trigger`, `pikku enable webhook`
|
|
144
|
+
|
|
145
|
+
Inbound triggers and outgoing webhook delivery. This is where `wireHTTP` is
|
|
146
|
+
correct rather than a smell: a third-party caller needs a real REST shape.
|
|
147
|
+
|
|
148
|
+
### Locales — `pikku-i18n`, `pikku-paraglide`
|
|
149
|
+
|
|
150
|
+
`pikku-build-app` already requires every string to be a key. **Here, ship three
|
|
151
|
+
locales, and make one of them RTL** (`pikku-rtl`). Two LTR locales prove the
|
|
152
|
+
plumbing; an RTL one proves the layout, and it will find real bugs — mirrored
|
|
153
|
+
icons, hardcoded `marginLeft`, a nav that opens on the wrong side.
|
|
154
|
+
|
|
155
|
+
Adding a language means adding a locale file. If it means touching components,
|
|
156
|
+
that is the finding.
|
|
157
|
+
|
|
158
|
+
### Emails — `pikku-emails`
|
|
159
|
+
|
|
160
|
+
Templates in `emails/`, rendered and sent through the injected `email` service,
|
|
161
|
+
localised like every other string. The base workflow asks for one; **a showcase
|
|
162
|
+
sends three** — a welcome, a transactional confirmation, and one sent from a
|
|
163
|
+
schedule or queue rather than a request, because that is the interesting path.
|
|
164
|
+
|
|
165
|
+
### Contract versioning — `pikku-versioning`
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
bunx --bun pikku versions init
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The CLI suggests this on every run of a project without it. Versioning a function
|
|
172
|
+
contract, then changing it, is a short milestone that shows something no
|
|
173
|
+
scaffold demonstrates on its own. `pikku semver` derives the release version by
|
|
174
|
+
comparing this build's surface against a deployed one.
|
|
175
|
+
|
|
176
|
+
### Addons — `pikku-addon`
|
|
177
|
+
|
|
178
|
+
`pikku new addon` scaffolds a publishable addon package. Worth one milestone if
|
|
179
|
+
the domain has a piece that genuinely belongs to no single app.
|
|
180
|
+
|
|
181
|
+
## Coverage — where the bar is higher than the base workflow
|
|
182
|
+
|
|
183
|
+
`pikku-build-app` §7a already has the mechanics and the per-milestone habit:
|
|
184
|
+
run the server instrumented, run the scenarios against it, read
|
|
185
|
+
`coverage/scenario-coverage.json`, and triage every gap as a missing scenario, a
|
|
186
|
+
function that should not exist, or a documented deferral. Do all of that here.
|
|
187
|
+
|
|
188
|
+
Two things change in a showcase:
|
|
189
|
+
|
|
190
|
+
- **Every surface you enabled has to appear in the coverage, not just every
|
|
191
|
+
function.** A workflow, a schedule, a queue worker and an agent each run on
|
|
192
|
+
their own path; a green scenario suite that never advances the workflow past
|
|
193
|
+
step one is the difference between "the platform does workflows" and "there is
|
|
194
|
+
a workflow file in this repo". Check the surfaces by name, because a coverage
|
|
195
|
+
percentage in the nineties hides an entire unexercised surface comfortably.
|
|
196
|
+
- **The number is part of the deliverable.** A showcase is read as evidence, so
|
|
197
|
+
publish the figure alongside it. An unreported number invites the reader to
|
|
198
|
+
assume the worst, and in a demo repo they are usually right to.
|
|
199
|
+
|
|
200
|
+
## The full gate
|
|
201
|
+
|
|
202
|
+
Everything in `pikku-build-app` §9, plus the checks a showcase should be able to
|
|
203
|
+
survive:
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
bunx --bun pikku all --tsc-summary --fail-on-warn --strict-meta
|
|
207
|
+
bunx --bun pikku all --security --fail-on-error
|
|
208
|
+
bunx --bun pikku validate
|
|
209
|
+
bunx --bun pikku knowledge validate
|
|
210
|
+
bunx --bun pikku audit
|
|
211
|
+
bunx --bun pikku scenario run local --spawn --coverage
|
|
212
|
+
bunx --bun pikku scenario run local --spawn --run browser
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- `--strict-meta` fails an agent tool with no description.
|
|
216
|
+
- `--security` runs the data-classification lint over function return types,
|
|
217
|
+
catching a `Pii`/`Secret` field leaking through an exposed function. Expensive;
|
|
218
|
+
worth it here.
|
|
219
|
+
- `pikku audit` reports dependency advisories (`--outdated` adds available
|
|
220
|
+
updates) into `.pikku/audit.json`.
|
|
221
|
+
|
|
222
|
+
## Deploy
|
|
223
|
+
|
|
224
|
+
`pikku-build-app` §9 covers the open-source paths (`--provider standalone`,
|
|
225
|
+
`cloudflare`, `aws`). One thing specific to this mode: **the extra surfaces are
|
|
226
|
+
extra deploy units.** Workflow workers, queue workers, schedules and the events
|
|
227
|
+
channel each appear in `pikku deploy plan` as their own entries. Read the plan
|
|
228
|
+
before applying — that list is also the clearest inventory of what you actually
|
|
229
|
+
built.
|
|
230
|
+
|
|
231
|
+
## Reference
|
|
232
|
+
|
|
233
|
+
- Base workflow: `pikku-build-app` — read it first, follow it in full
|
|
234
|
+
- Per-surface skills: `pikku-workflow`, `pikku-schedule`, `pikku-cron`,
|
|
235
|
+
`pikku-queue`, `pikku-agent`, `pikku-ai-vercel`, `pikku-realtime`,
|
|
236
|
+
`pikku-websocket`, `pikku-mcp`, `pikku-trigger`, `pikku-i18n`, `pikku-rtl`,
|
|
237
|
+
`pikku-emails`, `pikku-versioning`, `pikku-addon`, `pikku-security`,
|
|
238
|
+
`pikku-audit`
|
|
239
|
+
- Every feature, end to end: https://pikkufabric.com/llm-all-features.txt
|