@pikku/skills 0.12.30 → 0.12.32

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/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@pikku/skills",
3
- "version": "0.12.30",
3
+ "version": "0.12.32",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/pikkujs/pikku.git",
7
+ "directory": "packages/skills"
8
+ },
4
9
  "description": "The Pikku agent skills — the instruction set coding agents read to build, wire and deploy Pikku projects",
5
10
  "author": "yasser.fadl@gmail.com",
6
11
  "license": "MIT",
@@ -10,13 +15,13 @@
10
15
  "type": "module",
11
16
  "scripts": {
12
17
  "embed": "node scripts/embed.mjs",
13
- "tsc": "yarn embed && tsc",
14
- "build": "yarn embed && tsc -b",
18
+ "tsc": "bun run embed && tsc",
19
+ "build": "bun run embed && tsc -b",
15
20
  "ncu": "npx npm-check-updates",
16
21
  "test": "bash run-tests.sh",
17
22
  "test:watch": "bash run-tests.sh --watch",
18
23
  "test:coverage": "bash run-tests.sh --coverage",
19
- "prepublishOnly": "yarn build"
24
+ "prepublishOnly": "bun run build"
20
25
  },
21
26
  "exports": {
22
27
  ".": "./dist/index.js"
@@ -27,10 +32,9 @@
27
32
  ],
28
33
  "devDependencies": {
29
34
  "@types/node": "^24.13.3",
30
- "typescript": "^6.0.3",
31
35
  "yaml": "^2.9.0"
32
36
  },
33
37
  "engines": {
34
38
  "node": ">=24"
35
39
  }
36
- }
40
+ }
@@ -324,6 +324,11 @@ wireAddon({ name: 'todos', package: '@my-org/addon-todos' })
324
324
 
325
325
  After registration, run `yarn pikku all` to generate types for the addon's functions.
326
326
 
327
+ Give each addon its own wiring file. A deployment unit imports a `wireAddon`
328
+ file only while at least one addon that file wires survives the unit's filter,
329
+ so wiring two addons from one file means a unit needing either one registers
330
+ both and bundles both packages' dependencies.
331
+
327
332
  If the addon ships tables, `pikku db generate` then writes one migration per
328
333
  addon — named after the package, carrying the addon's own SQL — after Better
329
334
  Auth's and the runtime's, so an addon table may reference `user` or a runtime
@@ -23,6 +23,85 @@ how a worker deploy fails at runtime instead of at build.
23
23
  Always run `plan` before `apply`, and read it. It names what will be created,
24
24
  updated and deleted — the deletions are the reason to look.
25
25
 
26
+ ### How many workers you get
27
+
28
+ By default a unit holds every function that builds the same set of singleton
29
+ services. One unit per function is available too, but on a large app it means a
30
+ hundred workers whose bundles are mostly the same framework code repeated, and a
31
+ build and an upload for each. `deploy.grouping` in `pikku.config.json` sets the
32
+ shape:
33
+
34
+ ```json
35
+ "deploy": {
36
+ "grouping": {
37
+ "strategy": "single",
38
+ "rules": [
39
+ { "unit": "console", "addon": "console" },
40
+ { "unit": "pdf", "tags": ["pdf"] }
41
+ ]
42
+ }
43
+ }
44
+ ```
45
+
46
+ `strategy` is the fallback for a function no rule matches:
47
+
48
+ | strategy | fallback |
49
+ | --- | --- |
50
+ | `services` | one unit per distinct set of singleton services (the default) |
51
+ | `function` | one unit each |
52
+ | `single` | one shared unit |
53
+
54
+ Rules are ordered, first match wins, and match on `tags`, an `addon` namespace
55
+ or `routes` globs. A rule always beats the strategy, so under `function` a rule
56
+ merges and under `single` or `services` it carves out.
57
+
58
+ `services` is the default because it is the middle ground: `single` is too
59
+ coarse to reason about and `function` pays a cold start and a deploy step per
60
+ function. Functions are keyed
61
+ on the singleton services their bodies destructure, minus the ones every unit
62
+ builds anyway (`config`, `logger`, `variables`, `schema`, `secrets`, and the
63
+ per-request `rpc`/`mcp`/`channel`/`userSession`). Units are named for that set —
64
+ `svc-todo-store`, `svc-event-hub-todo-store`, `svc-base` for functions needing
65
+ nothing else — and each records it as `servicesKey` in the manifest. On the
66
+ `templates/functions` app it turns 44 units into 10.
67
+
68
+ The deploy target is part of the key, so a `server` unit is named `-server` and
69
+ can never merge with its serverless twin. That is what keeps `services` from
70
+ tripping the mixed-target refusal below: two functions can carry identical
71
+ services and still run in different places, because a function may name
72
+ `deploy: 'server'` itself without any service crossing it.
73
+
74
+ Read the shape before trusting it. The partition depends entirely on how varied
75
+ the app's service use is: an app where nearly every function reaches the same
76
+ database collapses to roughly `single` with a few carve-outs. Run `pikku deploy
77
+ plan` and look at the unit list; set `"strategy": "function"` if you want a unit
78
+ per function back.
79
+
80
+ Changing the strategy renames units, and a unit name is what a queue consumer, a
81
+ scheduled task and a `dependsOn` point at. The manifest rewrites all three, but
82
+ anything holding a unit name outside the manifest does not follow. Units dropped
83
+ from the manifest are not deleted by OSS `deploy()` either (pikkujs/pikku#543) —
84
+ fabric sweeps its dispatch namespace after each deploy, plain pikku leaves the
85
+ old workers in place.
86
+
87
+ `tags` matches the tags a function inherits from its wirings — `wireHTTP({ ...,
88
+ tags: ['pdf'] })` — as well as any on the function itself, which is where almost
89
+ every project writes them.
90
+
91
+ Two things that bite:
92
+
93
+ - **Grouping cannot change a deploy target.** Put a `serverlessIncompatible`
94
+ function in a group with serverless ones and the build fails naming both
95
+ sides. Give it its own rule — that refusal is the design, not a bug. To find
96
+ the culprit, read `deployment-manifest.json`: a unit carries `targetForcedBy`
97
+ naming the services that crossed it to `server`, and `groupedBy` naming the
98
+ rule that made it, so you can see which rule to carve the function out of.
99
+ - **Grouping widens secret scope.** Every function in a unit reads every secret
100
+ that unit is granted, so treat a merge as a security decision too.
101
+
102
+ Group by something that means something — a domain, a secret scope, a deploy
103
+ cadence. There is deliberately no automatic packing by bundle size.
104
+
26
105
  The frontends build independently (`bun run build` at the root builds every
27
106
  workspace). Serve each behind its own hostname, and put the API behind `/api` on
28
107
  **all of them**, mirroring the Vite proxy from the multi-app reference: `/api/auth/*` keeps its
@@ -284,6 +284,20 @@ pikku knowledge plan defer <milestone> <item> -r "<why>"
284
284
 
285
285
  `progress` reconciles the plan against pikku's generated meta — set membership, never anyone's status — and exits non-zero while the first pass is short, or while anything already built contradicts the plan. Unbuilt work in a later pass is reported, not blocked; a function that shipped wide open against a planned permission rule blocks from any pass, because that is a hole rather than a backlog. Writing a plan is its own seat: read `pikku-architect`. Building against one is `pikku-build`.
286
286
 
287
+ ### A finished milestone is a tombstone
288
+
289
+ **Once a milestone reaches `built`, its note and its plan are closed. Do not edit either.** Not to correct the wording, not to fold in what the build actually turned out to need, not to add the item everyone agrees should have been there. A finished milestone is the record of what was agreed and what was measured against it, and a record that can be revised afterwards measures nothing.
290
+
291
+ This is the rule the shape of the thing already implies. `progress` reconciles a plan against generated meta and fails when what shipped contradicts it — a check with no force at all if the losing side of the contradiction may simply be rewritten. `attempts:` brakes a note nothing can satisfy, and refunds that budget when the note's content really changes; a `built` note that keeps changing is that brake removed. Both only work while the plan stays still.
292
+
293
+ So when a `built` milestone turns out to be wrong or incomplete, **the answer is always a new note, never an edit to the old one**:
294
+
295
+ - It needed more than it said → a new milestone, which may name the old one.
296
+ - It was built differently than planned → that is what `progress` is for. Reconcile forward, or record a decision saying why the plan was not the right shape.
297
+ - It was simply wrong → a decision note that supersedes it. The wrong milestone stays where it is; a base whose history is edited cannot answer *why* anything is the way it is, which is most of what a base is for.
298
+
299
+ The exception, and it is narrow: bookkeeping the loop owns. `statusAt:` and `attempts:` are written by whatever moved the note, at any status, and are bookkeeping rather than content. Nothing else about a `built` note moves again.
300
+
287
301
  ## Profiles built on this one
288
302
 
289
303
  OKF permits frontmatter fields a reader does not know, and the parser ignores them rather than failing. That is the extension point: a tool layered on Pikku can add its own sections and fields on top of everything above without forking the format.
@@ -1,6 +1,5 @@
1
1
  # Pikku Services (Dependency Injection)
2
2
 
3
-
4
3
  ## Before You Start
5
4
 
6
5
  ```bash
@@ -195,6 +194,13 @@ const createSingletonServices = pikkuServices(async (config) => {
195
194
  })
196
195
  ```
197
196
 
197
+ A deployment split regenerates this manifest **per unit**, so the same
198
+ `services.ts` sees a different manifest in each bundle and each unit builds only
199
+ what its own functions, middleware and permissions reach. A `services.ts` that
200
+ constructs unconditionally gets none of that: every unit pays for every service,
201
+ and the split buys nothing but duplicated bytes. Branching on the manifest is
202
+ what makes the split real.
203
+
198
204
  ### Audit Wire Service
199
205
 
200
206
  `createInvocationAudit` + `createAuditedKysely` add per-request audit buffering that flushes on request close (no-op if `audit` is unconfigured). For the full pattern, no-op behavior, custom-event usage, and Fabric notes, read `references/audit-wire-service.md`.