@pikku/skills 0.12.9 → 0.12.11
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 +768 -0
- package/dist/skills.gen.js +1 -1
- package/package.json +4 -4
- package/skills/pikku-addon/SKILL.md +20 -14
- package/skills/pikku-addon/references/addon-package-manifest.md +2 -2
- package/skills/{pikku-ai-agent → pikku-agent}/SKILL.md +24 -24
- package/skills/pikku-ai-vercel/SKILL.md +18 -18
- package/skills/pikku-ai-voice/SKILL.md +15 -15
- 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 +10 -7
- 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-deploy-cloudflare/SKILL.md +9 -9
- package/skills/pikku-deploy-uws/SKILL.md +5 -2
- package/skills/pikku-deps/SKILL.md +42 -3
- package/skills/pikku-emails/SKILL.md +5 -5
- 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-kysely/SKILL.md +68 -41
- package/skills/pikku-machine-auth/SKILL.md +10 -10
- package/skills/pikku-mcp/SKILL.md +23 -20
- package/skills/pikku-middleware/SKILL.md +19 -12
- package/skills/pikku-middleware/references/middleware-patterns.md +14 -8
- package/skills/pikku-mongodb/SKILL.md +11 -11
- package/skills/pikku-n8n-import/SKILL.md +12 -12
- package/skills/pikku-n8n-import/SPEC.md +3 -0
- package/skills/pikku-n8n-import/references/addon-mapping.md +14 -8
- 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/SKILL.md +83 -73
- 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 +51 -19
- package/skills/pikku-rpc/SKILL.md +1 -1
- package/skills/pikku-rtl/SKILL.md +1 -1
- package/skills/pikku-scenario/SKILL.md +164 -44
- 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/SKILL.md +27 -23
- package/skills/pikku-software-archaeology/references/blueprint.schema.json +580 -102
- 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-versioning/SKILL.md +87 -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
- package/skills/pikku-ws/SKILL.md +5 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
# @pikku/skills
|
|
2
|
+
|
|
3
|
+
## 0.12.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7722ceb: Split the addon leaf so an application cannot shadow a linked addon's own
|
|
8
|
+
|
|
9
|
+
An addon authored its services through `#pikku/addon`, and so did an
|
|
10
|
+
application installing one. Node keeps those apart — `#pikku/*` is a
|
|
11
|
+
package-private subpath import, resolved against the addon's own
|
|
12
|
+
`package.json` — but tsconfig `paths` are global to a tsx process, and every
|
|
13
|
+
runtime template maps `#pikku/*` onto a sibling package. A linked addon's
|
|
14
|
+
`#pikku/addon` was resolved against the _application's_ leaf, which holds the
|
|
15
|
+
install half and none of the authoring exports, and every template failed to
|
|
16
|
+
boot with `does not provide an export named 'pikkuAddonServices'`.
|
|
17
|
+
|
|
18
|
+
The authoring half now sits at `#pikku/addon/setup`. An application generates a
|
|
19
|
+
flat `.pikku/<leaf>`, so there is nothing there for that specifier to match and
|
|
20
|
+
the resolver falls back to Node, which reads the addon's own imports. Addons
|
|
21
|
+
declaring themselves import `pikkuAddonConfig`, `pikkuAddonServices` and
|
|
22
|
+
`pikkuAddonWireServices` from `#pikku/addon/setup`; `wireAddon` and
|
|
23
|
+
`wireRemoteAddon` stay at `#pikku/addon`.
|
|
24
|
+
|
|
25
|
+
`wireAddon` and `wireRemoteAddon` also move off `@pikku/core/rpc` onto
|
|
26
|
+
`@pikku/core/addon`. Being reached over rpc is how an addon is called rather
|
|
27
|
+
than what it is, and it put the whole addon surface behind the rpc subpath for
|
|
28
|
+
consumers that only wanted to install one.
|
|
29
|
+
|
|
30
|
+
- 6eef0a0: Bump every dependency to its latest compatible minor/patch across the monorepo.
|
|
31
|
+
- 3b1164a: feat(react,mantine): ship the dev actor switcher instead of making every app copy it
|
|
32
|
+
|
|
33
|
+
The dev-only "Sign in as …" control — one click signs in as a declared scenario
|
|
34
|
+
persona, no password — was hand-copied into every app that needed it, because
|
|
35
|
+
`pikku fabric validate` requires any frontend with a login screen to have one.
|
|
36
|
+
The `devActors()` / `signInAsActor()` pair was byte-identical everywhere it
|
|
37
|
+
landed, including the `import.meta.env.DEV` gate that keeps the shared secret out
|
|
38
|
+
of production bundles. That is not a thing each app should be re-deriving from a
|
|
39
|
+
copy-paste.
|
|
40
|
+
|
|
41
|
+
Split along the dependency line:
|
|
42
|
+
|
|
43
|
+
- `@pikku/react` gains `useDevActors()`, `signInAsActor()` and `parseDevActors()`.
|
|
44
|
+
UI-free, so it stays inside the package's react-only dependency budget.
|
|
45
|
+
- `@pikku/mantine/dev` gains `<DevActorSwitcher />`, built on that hook. It is a
|
|
46
|
+
new entry point rather than part of `/core`, because `/core`'s contract is
|
|
47
|
+
"drop-in alias for `@mantine/core`" and exporting a component Mantine has no
|
|
48
|
+
counterpart for would break it.
|
|
49
|
+
|
|
50
|
+
The component takes `onSignedIn` rather than depending on a router, and the
|
|
51
|
+
actors/secret are passed in rather than read from env — how env is spelled is a
|
|
52
|
+
bundler fact (`import.meta.env.VITE_*` vs `process.env.NEXT_PUBLIC_*`), and a
|
|
53
|
+
package that guesses gets it wrong for half its consumers.
|
|
54
|
+
|
|
55
|
+
The skills document it in the four places an agent would look: `pikku-better-auth`
|
|
56
|
+
for the `actor` plugin's endpoint (which had only `/dev/quick-login` before, and
|
|
57
|
+
so sent agents to the wrong control), `pikku-scenario` for the actor list being
|
|
58
|
+
the same one a human signs in through, `pikku-react` for the hook, and
|
|
59
|
+
`pikku-fabric` for the validate rule that requires it.
|
|
60
|
+
|
|
61
|
+
`fabric validate` now also accepts a `useDevActors()` call site as evidence the
|
|
62
|
+
control is wired, so apps that want their own UI on the shared logic pass. The
|
|
63
|
+
hand-rolled shape still passes too — nothing existing breaks. Its fix text no
|
|
64
|
+
longer tells you to hand-write the helper, which would have become wrong advice
|
|
65
|
+
the day this shipped.
|
|
66
|
+
|
|
67
|
+
- 266e3bc: `#pikku` is a namespace, not a module: one subpath per wiring
|
|
68
|
+
|
|
69
|
+
The bare `#pikku` specifier resolved to `.pikku/pikku-types.gen.ts`, a hub that
|
|
70
|
+
re-exported all twelve wiring leaves with `export *` — undoing the split the
|
|
71
|
+
leaves exist for, each of which still says so in its own generated header
|
|
72
|
+
("HTTP-specific type definitions for tree-shaking optimization"). Reaching that
|
|
73
|
+
hub put 33 distinct `@pikku/core` subpaths into the module graph, and neither
|
|
74
|
+
consumer could drop them again: bundlers keep `export *` chains because the app
|
|
75
|
+
declares no `sideEffects`, and Node and tsx do not tree-shake at all, so an app
|
|
76
|
+
with no queues still executed `@pikku/core/queue` at boot.
|
|
77
|
+
|
|
78
|
+
The hub is gone. An app now imports the leaf the name belongs to —
|
|
79
|
+
`#pikku/function`, `#pikku/http`, `#pikku/workflow` — and a project's `imports`
|
|
80
|
+
map declares two patterns, because both resolvers pick the more specific one:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
"#pikku/*.js": "./.pikku/*.ts",
|
|
84
|
+
"#pikku/*": "./.pikku/*/index.ts"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
A source tree names the `.ts` on both. Webpack, esbuild and Bun all rewrite a
|
|
88
|
+
`.js` specifier to the `.ts` beside it for a relative import but not for an
|
|
89
|
+
imports-map target, so a `.js` target there resolves to a file that does not
|
|
90
|
+
exist. The two places that keep `.js` are the ones where it is the real file: a
|
|
91
|
+
published addon, whose map points into `dist`, and a project that imports a
|
|
92
|
+
declaration-only generated file such as `pikku-rpc-wirings-map.gen.d.ts`, where
|
|
93
|
+
naming the `.js` lets the type resolver's own mapping reach the `.d.ts`.
|
|
94
|
+
|
|
95
|
+
`pikku` generates the leaf indexes and removes the hub, and `pikku validate`
|
|
96
|
+
reports a barrel import as an error. The split also turns the addon boundary
|
|
97
|
+
from advice into a rule: an addon never generates the wiring leaves, so
|
|
98
|
+
`#pikku/http` fails at the specifier rather than yielding "no exported member"
|
|
99
|
+
from a hub that quietly dropped the re-export.
|
|
100
|
+
|
|
101
|
+
- 9fce0f1: Give a persona step its actor instead of making it unwrap one
|
|
102
|
+
|
|
103
|
+
`requireActor(scenarioStep)` was the first line of every step that acts as
|
|
104
|
+
somebody, and it existed because the actor lived on the `scenarioStep` wire as
|
|
105
|
+
an optional property. A property of a wire member is either optional for every
|
|
106
|
+
binding or required for all of them, so the only expressible answer was
|
|
107
|
+
"optional", and each step paid for it with a guard.
|
|
108
|
+
|
|
109
|
+
The actor is now its own wire member, `wire.actor`, injected by the runner. Wire
|
|
110
|
+
members can be required per binding, so a step declares whether it runs as
|
|
111
|
+
somebody and the type follows:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
export const buysAnApple = pikkuScenarioStep<
|
|
115
|
+
{ qty: number },
|
|
116
|
+
{ orderId: string }
|
|
117
|
+
>({
|
|
118
|
+
name: 'buysAnApple',
|
|
119
|
+
actor: true,
|
|
120
|
+
default: async (_services, { qty }, { actor }) =>
|
|
121
|
+
actor.invoke('placeOrder', { qty }),
|
|
122
|
+
})
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
A `browser` binding implies it — a window is opened as somebody, so every
|
|
126
|
+
binding of a step that has one gets the actor too. A step that declares neither
|
|
127
|
+
has no `actor` on its wire at all, rather than an optional one: a pure assertion
|
|
128
|
+
over what an earlier step returned has nobody to be, and `attemptsSignIn`
|
|
129
|
+
deliberately posts credentials instead of reusing an actor's established
|
|
130
|
+
session. That distinction is why the requirement is declared per step rather
|
|
131
|
+
than inferred from the step being a persona step — "persona step ⇒ has an actor"
|
|
132
|
+
is false, and a guard built on it rejects the 61 steps in the e2e suite that
|
|
133
|
+
correctly run without one.
|
|
134
|
+
|
|
135
|
+
Dispatching a step that declared an actor without `{ actor: actors.x }` now
|
|
136
|
+
fails before the body runs, with `ScenarioActorRequired` naming the step.
|
|
137
|
+
`ScenarioBrowserActorRequired` is replaced by it, and `requireActor` is gone
|
|
138
|
+
from `@pikku/core/scenario` and the generated `#pikku/scenario` barrel.
|
|
139
|
+
|
|
140
|
+
- 9fce0f1: Say what a scenario step is actually given, and stop the skill teaching an RPC call that throws
|
|
141
|
+
|
|
142
|
+
The `pikku-scenario` skill's two `default` witnesses destructured `rpc` from
|
|
143
|
+
services and called `rpc.invoke`. That is exactly what the scenario runner
|
|
144
|
+
refuses: steps run in the CLI process, and `guardRpc` answers every member with
|
|
145
|
+
_"Scenario tried to run 'getOrder' as an internal step. Every workflow.do in a
|
|
146
|
+
scenario must carry { actor: actors.x }"_. Both examples now go through
|
|
147
|
+
`actor.invoke` off the step's wire, which is the path the surrounding prose
|
|
148
|
+
already described.
|
|
149
|
+
|
|
150
|
+
Adds a **What a step is given** section, because nothing said it. The services
|
|
151
|
+
object is built by hand in `scenario.ts` and holds `logger`, `workflowService`,
|
|
152
|
+
`workflowRunService` and — only when the project declares agents — `agentRunner`.
|
|
153
|
+
There is no `kysely`, no `variables`, no `secrets` and none of the project's own
|
|
154
|
+
services, so a step that destructures one gets `undefined` and fails on first
|
|
155
|
+
use, which reads like a broken container and is not. The section names the three
|
|
156
|
+
ways in (`invoke`, `invokeRaw`, a plain `fetch` at `env.apiUrl`), the two
|
|
157
|
+
consequences that shape how steps get written, and the condition on
|
|
158
|
+
`agentRunner` — `createDevAgentRunner` needs a base URL _and_ a key together, so
|
|
159
|
+
a project with only `OPENAI_API_KEY` set gets `undefined` and every conversing
|
|
160
|
+
scenario fails before the persona says anything.
|
|
161
|
+
|
|
162
|
+
Adds **Declaring personas in TypeScript**, covering the one-call rule and the
|
|
163
|
+
trap underneath it: `definePersonas` is read from source and never evaluated, so
|
|
164
|
+
every value must be statically knowable — but only `name` is validated. A
|
|
165
|
+
computed `personality` is dropped in silence and the persona runs with a blank
|
|
166
|
+
temperament. `stringProperty` accepts `ts.isStringLiteralLike`, so a
|
|
167
|
+
no-substitution template literal is read and is the way to write a long
|
|
168
|
+
personality across several lines; a `+` concatenation is not. Also records that
|
|
169
|
+
`actorInstructions` builds the conversing persona's prompt from `name`,
|
|
170
|
+
`jobTitle`, `personality` and the scenario's `task` only — `disposition`,
|
|
171
|
+
`goals` and `roles` are stored and shown but never reach it.
|
|
172
|
+
|
|
173
|
+
Finally, the three `import ... from '#pikku/workflow/pikku-workflow-types.gen.js'`
|
|
174
|
+
lines now point at `#pikku/scenarios/pikku-scenario-types.gen.js`, which is where
|
|
175
|
+
the scenario surface moved when the barrel was split.
|
|
176
|
+
|
|
177
|
+
Also corrects three import specifiers the skill still taught from before the
|
|
178
|
+
`#pikku` leaves landed: `#pikku/scenarios/pikku-scenario-types.gen.js` and
|
|
179
|
+
`@pikku/core/workflow` both become `#pikku/scenario`, which is the one door the
|
|
180
|
+
leaf exists to be and the specifier every step file in the e2e suite already
|
|
181
|
+
uses.
|
|
182
|
+
|
|
183
|
+
- 727671b: `wireAddon` and `wireRemoteAddon` move from `#pikku/function` to `#pikku/addon`.
|
|
184
|
+
|
|
185
|
+
Installing an addon and authoring one are the same concept from opposite ends,
|
|
186
|
+
so they are one import: an application's `#pikku/addon` carries the two install
|
|
187
|
+
functions, an addon package's carries `pikkuAddonConfig`, `pikkuAddonServices`,
|
|
188
|
+
`pikkuAddonWireServices` and `AddonBaseServices`.
|
|
189
|
+
|
|
190
|
+
Two generation fixes came with it:
|
|
191
|
+
|
|
192
|
+
- `CredentialsMap` is generated as a type alias rather than an interface. An
|
|
193
|
+
interface has no implicit index signature, so it was never assignable to the
|
|
194
|
+
`Record<string, unknown>` that `GetCredential` is constrained by, and every
|
|
195
|
+
generated project reported two errors on its own function types.
|
|
196
|
+
- An unresolved `SingletonServices` type is now `PKU724` instead of a services
|
|
197
|
+
map with no entries in it. Written out, the empty map made every service
|
|
198
|
+
optional and the real failure resurfaced as unrelated "possibly undefined"
|
|
199
|
+
errors in files nobody had touched.
|
|
200
|
+
|
|
201
|
+
## 0.12.10
|
|
202
|
+
|
|
203
|
+
### Patch Changes
|
|
204
|
+
|
|
205
|
+
- 7406bfe: Rename the agent runtime from `AI*` to `Agent*` (#596)
|
|
206
|
+
|
|
207
|
+
`AI` described the model provider, not the thing being named. Every symbol that
|
|
208
|
+
belongs to the agent runtime now says `Agent`; the symbols that genuinely wrap a
|
|
209
|
+
model provider — `AIEmbeddingService`, `AIProviderOptions`, `AIEmbedParams`,
|
|
210
|
+
`AITranscriptionParams`, `AIGenerateImageParams` and their siblings, and the
|
|
211
|
+
`@pikku/ai-vercel` / `@pikku/ai-deepinfra` / `@pikku/ai-voice` packages — keep
|
|
212
|
+
their names.
|
|
213
|
+
|
|
214
|
+
**Wiring**
|
|
215
|
+
- `pikkuAIAgent` → `pikkuAgent`, `pikkuAIScorer` → `pikkuAgentScorer`,
|
|
216
|
+
`pikkuAIJudge` → `pikkuAgentJudge`
|
|
217
|
+
- `CoreAIAgent` → `CoreAgent`, `AIAgentInput` → `AgentInput`, `AIAgentStep` →
|
|
218
|
+
`AgentStep`, `AIMessage` → `AgentMessage`, and the rest of the agent types
|
|
219
|
+
- `AIAgentRunnerService` → `AgentRunnerService`, `AIStorageService` →
|
|
220
|
+
`AgentStorageService`, `AIRunStateService` → `AgentRunStateService`
|
|
221
|
+
|
|
222
|
+
**Entry points**
|
|
223
|
+
|
|
224
|
+
`@pikku/core/agent` → `@pikku/core/agent`, `@pikku/core/agent-scorer` →
|
|
225
|
+
`@pikku/core/agent-scorer`.
|
|
226
|
+
|
|
227
|
+
**Queues**
|
|
228
|
+
|
|
229
|
+
The scorer queues are now `agent-score-fast` and `agent-score-slow`. Drain the
|
|
230
|
+
old `ai-score-fast` / `ai-score-slow` queues before deploying — jobs still
|
|
231
|
+
sitting on them when the new workers start will never be picked up.
|
|
232
|
+
|
|
233
|
+
**Scaffolds**
|
|
234
|
+
|
|
235
|
+
The agent scaffold pikku wrote for your project — `<scaffold>/agent/agent.gen.ts`
|
|
236
|
+
and its schemas file — imports `@pikku/core/ai-agent`, which no longer exists. A
|
|
237
|
+
scaffold is normally written once and then left alone, so `pikku all` would find
|
|
238
|
+
it present and leave the broken import in place. It now deletes an agent scaffold
|
|
239
|
+
importing either removed entry point and regenerates it in the same run. Anything
|
|
240
|
+
you added to that file goes with it, so move local edits out first.
|
|
241
|
+
|
|
242
|
+
**Database**
|
|
243
|
+
|
|
244
|
+
The agent tables are renamed: `ai_threads`, `ai_message`, `ai_tool_call`,
|
|
245
|
+
`ai_working_memory`, `ai_run` and `ai_run_score` become `agent_threads`,
|
|
246
|
+
`agent_message`, `agent_tool_call`, `agent_working_memory`, `agent_run` and
|
|
247
|
+
`agent_run_score`, along with their indexes and the `ai_working_memory_pk`
|
|
248
|
+
constraint. The same rename applies to the MongoDB collections.
|
|
249
|
+
|
|
250
|
+
`ensurePikkuSchema` creates tables it cannot find, so an existing database will
|
|
251
|
+
get empty `agent_*` tables and leave the old data stranded in `ai_*`. Rename
|
|
252
|
+
them before the first boot on the new version:
|
|
253
|
+
|
|
254
|
+
```sql
|
|
255
|
+
ALTER TABLE ai_threads RENAME TO agent_threads;
|
|
256
|
+
ALTER TABLE ai_message RENAME TO agent_message;
|
|
257
|
+
ALTER TABLE ai_tool_call RENAME TO agent_tool_call;
|
|
258
|
+
ALTER TABLE ai_working_memory RENAME TO agent_working_memory;
|
|
259
|
+
ALTER TABLE ai_run RENAME TO agent_run;
|
|
260
|
+
ALTER TABLE ai_run_score RENAME TO agent_run_score;
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
- e7e5319: Add `pikku semver`, which derives a release's semver from a diff against a deployed surface and writes `.pikku/changes.gen.json`
|
|
264
|
+
|
|
265
|
+
A function or client-facing wiring that disappeared is major, an addition is minor, and a surface that did not move is patch. Where the generated JSON Schemas are available the verdict goes below the id level: a removed field or a newly required input field is breaking, an added optional one is not — direction-aware, so an output field going optional counts even though the same change on an input does not. `versions.pikku.json` is consumed, so a `@v2` bump does not read as a removal while v1 is still published.
|
|
266
|
+
|
|
267
|
+
The baseline is `--against <path|url>`: another `.pikku` directory, a snapshot file, or a snapshot published by `pikku semver --emit`. `--fail-on <level>` turns the verdict into a CI gate.
|
|
268
|
+
|
|
269
|
+
- 411f89a: Add `pikku update`: report which `@pikku/*` dependencies can move forward, and which peers those versions need.
|
|
270
|
+
|
|
271
|
+
Reporting only by default. `--update` writes the new ranges into every covered package.json — the project root plus every workspace it declares — and then runs an install with the package manager the project names (`--no-install` to skip). `--update-peers` additionally writes the ranges unsatisfied peers require; it is separate because a peer bump can cross a major of a third-party package.
|
|
272
|
+
|
|
273
|
+
Peers are read off the version the run lands on rather than the one installed, so an update that needs a companion bump says so before it is applied. Ranges that cannot be substituted into (`workspace:*`, `file:`, unions, x-ranges) are reported and left alone, and a package the registry could not answer for is reported as unresolved rather than current.
|
|
274
|
+
|
|
275
|
+
## 0.12.9
|
|
276
|
+
|
|
277
|
+
### Patch Changes
|
|
278
|
+
|
|
279
|
+
- b5fa1e5: Enumerate addon secret and credential grants in the deployment manifest.
|
|
280
|
+
|
|
281
|
+
`wireAddon`'s `secretGrants` / `credentialGrants` widen an addon's scope the same
|
|
282
|
+
way `globalSecrets` does, only narrower — but the manifest reported the exemption
|
|
283
|
+
and not the grant, so a deployment could not see the secrets an app had lent an
|
|
284
|
+
addon. `grantedSecretAddons` and `grantedCredentialAddons` now list them by name,
|
|
285
|
+
including override keys, since scoping is checked before an override renames.
|
|
286
|
+
|
|
287
|
+
The `pikku-addon` skill documents the whole grant family and the scoping rule
|
|
288
|
+
behind it, rather than the override fields alone.
|
|
289
|
+
|
|
290
|
+
## 0.12.8
|
|
291
|
+
|
|
292
|
+
### Patch Changes
|
|
293
|
+
|
|
294
|
+
- 0ab1a88: feat(knowledge): draw a note's scenario and its decision, and say the vocabulary exists
|
|
295
|
+
|
|
296
|
+
The console already drew ```mermaid fences as diagrams and `> [!NOTE]` blocks as
|
|
297
|
+
callouts, and nothing told the librarian either existed — the skill that governs
|
|
298
|
+
what goes in a note never mentioned them, so notes were written as prose and
|
|
299
|
+
tables into a renderer that would happily have drawn the graph. The gap was the
|
|
300
|
+
guidance, not the format.
|
|
301
|
+
|
|
302
|
+
Two blocks join them. A slice's ```gherkin scenario is drawn rather than
|
|
303
|
+
highlighted: the keywords line up in a column so the shape of the scenario is
|
|
304
|
+
readable before a word of it is, and each quoted persona becomes a chip — which
|
|
305
|
+
also makes a first-person scenario, the form the format rejects, visibly a block
|
|
306
|
+
with no personas in it.
|
|
307
|
+
|
|
308
|
+
A new ```decision fence states what a decision note owes: `chosen`, `rules-out`,
|
|
309
|
+
`because`. The middle one is the half that gets dropped, so `pikku knowledge
|
|
310
|
+
validate` now warns when a fence says what was chosen and never says what it
|
|
311
|
+
closes off. The fence is optional and a decision argued in prose is still a
|
|
312
|
+
decision — validate checks the fences that exist rather than asking every note
|
|
313
|
+
to be reformatted.
|
|
314
|
+
|
|
315
|
+
`Markdown` is exported from `@pikku/console` so the fabric console can render the
|
|
316
|
+
same notes through the same vocabulary instead of a second `<ReactMarkdown>`.
|
|
317
|
+
|
|
318
|
+
- 8978fbd: feat(workflow): let an approval gate declare who may answer it
|
|
319
|
+
|
|
320
|
+
`workflow.approval()` gains `approvers` (`'any' | 'owner' | 'not-initiator'`)
|
|
321
|
+
and `approverScope`, so a gate can require four-eyes sign-off, restrict itself
|
|
322
|
+
to the run's initiator, or require the decider to hold a named scope.
|
|
323
|
+
|
|
324
|
+
Both are enforced when the workflow replays the gate — the same place, and for
|
|
325
|
+
the same reason, the decision payload is validated: the policy is a value on
|
|
326
|
+
the workflow, and a decision can be recorded before the run has ever reached
|
|
327
|
+
the gate. A decision that fails the policy is discarded and the gate stays
|
|
328
|
+
closed. Where the run has already published its policy, the check also runs at
|
|
329
|
+
submission time so the caller gets a 403 rather than silence.
|
|
330
|
+
|
|
331
|
+
An answer is now recorded where it can be answered for later. The settled
|
|
332
|
+
decision carries `decidedBy` and `decidedAt` in its `ApprovalOutcome`, so who
|
|
333
|
+
signed reaches `workflowStep.result` and `workflowStepHistory` rather than
|
|
334
|
+
living only in mutable run state. Every answer — accepted, refused at the door,
|
|
335
|
+
or cleared on replay — is also written to the audit sink as
|
|
336
|
+
`workflow.approval.decided`, which outlives the run: `deleteRun` cascades to
|
|
337
|
+
steps and history, and a refused attempt never reaches a step at all. Projects
|
|
338
|
+
with no audit service wired are unaffected.
|
|
339
|
+
|
|
340
|
+
**This loosens the default.** `approveStep` previously refused anyone but the
|
|
341
|
+
run's initiator, unconditionally. A gate that declares no `approvers` now
|
|
342
|
+
accepts a decision from anyone the approve entrypoint admits — restore the old
|
|
343
|
+
behaviour per-gate with `approvers: 'owner'`, or gate the approve route with
|
|
344
|
+
`auth`/`permissions`. Ownership still governs _reads_ of a run unchanged.
|
|
345
|
+
|
|
346
|
+
## 0.12.7
|
|
347
|
+
|
|
348
|
+
### Patch Changes
|
|
349
|
+
|
|
350
|
+
- e110c55: Add `scenario.expectScore` — grade a finished agent run with a declared scorer and assert on it.
|
|
351
|
+
|
|
352
|
+
An agent's answer cannot be matched against a fixed string, so a scenario grades
|
|
353
|
+
it instead. `expectScore(step, runId, scorer, { atLeast, atMost, reference })`
|
|
354
|
+
runs one declared scorer against the run the scenario just triggered and fails
|
|
355
|
+
with the reason the judge gave. The default bound is `atLeast: 0.5`, so an
|
|
356
|
+
unqualified assertion still fails a run graded zero.
|
|
357
|
+
|
|
358
|
+
Grading goes over the new `pikkuScenarioGradeRun` instrumentation RPC, which the
|
|
359
|
+
dev server registers alongside the coverage and stub RPCs — so it exists only in
|
|
360
|
+
processes that should have it, and never in a deployed bundle. It grades from
|
|
361
|
+
the snapshot the runtime already took when the run finished, which is what makes
|
|
362
|
+
a scenario's grade the same measurement production's sampler makes rather than
|
|
363
|
+
an approximation of it: a run's prompt, answer and tool calls are spread across
|
|
364
|
+
a thread's messages, where the boundary of one run is not recoverable.
|
|
365
|
+
|
|
366
|
+
Two things differ deliberately from live scoring. The sample rate is ignored — a
|
|
367
|
+
scorer grading 1% of traffic still grades every scenario run — and the grade is
|
|
368
|
+
returned rather than recorded, so a test's score never lands among the
|
|
369
|
+
production figures. `reference` supplies the answer key a `requiresReference`
|
|
370
|
+
judge grades against, which is the only way such a judge is reachable at all.
|
|
371
|
+
|
|
372
|
+
- 2f15aad: `pikku workspace validate` is now `pikku validate`, and it checks addon packaging
|
|
373
|
+
|
|
374
|
+
The command no longer needs to be told what kind of project it is looking at.
|
|
375
|
+
Each check declares the condition under which it means anything and runs
|
|
376
|
+
wherever that condition holds, so a repo that is an app, a pile of publishable
|
|
377
|
+
addons, or both gets exactly the checks that apply — and a run that found
|
|
378
|
+
nothing to check says so instead of printing a tick.
|
|
379
|
+
|
|
380
|
+
The new checks are for addons, and both state the same property at a different
|
|
381
|
+
level: every relative import in a shipped generated file, and every `exports` or
|
|
382
|
+
`imports` target, must resolve to a file the package actually publishes.
|
|
383
|
+
|
|
384
|
+
That property was false in every published `@pikku/addon-*`. They shipped
|
|
385
|
+
`dist/.pikku` without the `types/application-types.d.ts` those files import —
|
|
386
|
+
14 typecheck errors inside `node_modules` for any app depending on one — and
|
|
387
|
+
they published a second, dead copy of `.pikku` at the root whose imports reached
|
|
388
|
+
for a `src/` and `types/` the tarball did not contain, behind the very subpath
|
|
389
|
+
consumers import their bootstrap through.
|
|
390
|
+
|
|
391
|
+
Addons now point every entry point at the built copy under `dist`; the addon's
|
|
392
|
+
own build resolves `#pikku` through tsconfig `paths`, so nothing has to reach
|
|
393
|
+
into the source tree. `pikku new-addon` scaffolds that shape, and the addon
|
|
394
|
+
skill teaches it.
|
|
395
|
+
|
|
396
|
+
## 0.12.6
|
|
397
|
+
|
|
398
|
+
### Patch Changes
|
|
399
|
+
|
|
400
|
+
- 2ff07e0: Remove `pikku db seed`. Seeding is now a step of `pikku db reset`, which grew `--no-seed`.
|
|
401
|
+
|
|
402
|
+
`seed` read like something you might point at any environment. It never was. It exists
|
|
403
|
+
for one job: put enough test data into a **dev** database that the app isn't empty on
|
|
404
|
+
first run. Production and staging are provisioned, not seeded — accounts and their role
|
|
405
|
+
grants come from `pikku persona sync` or a migration, and always have.
|
|
406
|
+
|
|
407
|
+
A standalone seed command is also what made seed files unpleasant to write. Because it
|
|
408
|
+
could be run against a database in any state, every seed had to defend itself with
|
|
409
|
+
`INSERT OR IGNORE`, `ON CONFLICT DO NOTHING`, `IF NOT EXISTS`. Folding it into reset
|
|
410
|
+
removes that: `pikku db reset` wipes, migrates, then seeds, so the seed only ever meets
|
|
411
|
+
an empty database and **plain `INSERT`s are correct**. The guarantee is structural now
|
|
412
|
+
rather than a documented convention.
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
pikku db reset # wipe + migrate + test data
|
|
416
|
+
pikku db reset --no-seed # wipe + migrate, empty — for empty-state and onboarding work
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Seeding also inherits reset's guards for free: it refuses `NODE_ENV=production`, and
|
|
420
|
+
refuses a database resolved outside the runtime directory.
|
|
421
|
+
|
|
422
|
+
The seed file keeps a name that says what it is:
|
|
423
|
+
- `db/postgres-seed.sql` → `db/postgres-dev-seed.sql`
|
|
424
|
+
- `db/sqlite-seed.sql` → `db/sqlite-dev-seed.sql`
|
|
425
|
+
|
|
426
|
+
**Migrating:** rename the file, and drop the idempotency guards from it if you like.
|
|
427
|
+
`pikku db seed` no longer exists — use `pikku db reset`. A project that keeps the old
|
|
428
|
+
filename gets no error: reset reports the database is empty, which is the one failure
|
|
429
|
+
mode worth knowing about up front. The Fabric validator's `seed-sql-missing` finding is
|
|
430
|
+
now `dev-seed-sql-missing` and looks for the new name.
|
|
431
|
+
|
|
432
|
+
- 1e74b01: Remove `pikku db seed`. Seeding is now a step of `pikku db reset`, which grew `--no-seed`.
|
|
433
|
+
|
|
434
|
+
`seed` read like something you might point at any environment. It never was. It exists
|
|
435
|
+
for one job: put enough test data into a **dev** database that the app isn't empty on
|
|
436
|
+
first run. Production and staging are provisioned, not seeded — accounts and their role
|
|
437
|
+
grants come from `pikku persona sync` or a migration, and always have.
|
|
438
|
+
|
|
439
|
+
A standalone seed command is also what made seed files unpleasant to write. Because it
|
|
440
|
+
could be run against a database in any state, every seed had to defend itself with
|
|
441
|
+
`INSERT OR IGNORE`, `ON CONFLICT DO NOTHING`, `IF NOT EXISTS`. Folding it into reset
|
|
442
|
+
removes that: `pikku db reset` wipes, migrates, then seeds, so the seed only ever meets
|
|
443
|
+
an empty database and **plain `INSERT`s are correct**. The guarantee is structural now
|
|
444
|
+
rather than a documented convention.
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
pikku db reset # wipe + migrate + test data
|
|
448
|
+
pikku db reset --no-seed # wipe + migrate, empty — for empty-state and onboarding work
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Seeding also inherits reset's guards for free: it refuses `NODE_ENV=production`, and
|
|
452
|
+
refuses a database resolved outside the runtime directory.
|
|
453
|
+
|
|
454
|
+
The seed file keeps a name that says what it is:
|
|
455
|
+
- `db/postgres-seed.sql` → `db/postgres-dev-seed.sql`
|
|
456
|
+
- `db/sqlite-seed.sql` → `db/sqlite-dev-seed.sql`
|
|
457
|
+
|
|
458
|
+
**Migrating:** rename the file, and drop the idempotency guards from it if you like.
|
|
459
|
+
`pikku db seed` no longer exists — use `pikku db reset`. A project that keeps the old
|
|
460
|
+
filename gets no error: reset reports the database is empty, which is the one failure
|
|
461
|
+
mode worth knowing about up front. The Fabric validator's `seed-sql-missing` finding is
|
|
462
|
+
now `dev-seed-sql-missing` and looks for the new name.
|
|
463
|
+
|
|
464
|
+
- 95f6144: Audit the twelve core skills against the shipped APIs and correct the drift.
|
|
465
|
+
- pikku-ai-agent: `instructions` does not exist — the prompt is `role`/`personality`/`goal` (required); tools are `ref()` handles; import from `#pikku/agent/pikku-agent-types.gen.js`; invoke via `rpc.agent.*` rather than `runAIAgent(name, input, { singletonServices })`
|
|
466
|
+
- pikku-scenario: step bodies live under `default`/`browser`/`cli` bindings, not a `func`; `scaffold.scenarios` is a boolean, not the rejected `"auth"` string
|
|
467
|
+
- pikku-addon: there is no `addon()` helper — `ref()` covers local and addon functions
|
|
468
|
+
- pikku-realtime: SSE is `PikkuRealtime.subscribeToTopic`; `publish`'s channelId argument excludes rather than targets
|
|
469
|
+
- pikku-cli: factories come from `#pikku`; documents options parsing, permissions/middleware/auth and the generated websocket backend
|
|
470
|
+
- pikku-services, pikku-config, pikku-middleware, pikku-rpc, pikku-workflow, pikku-queue, pikku-cron, pikku-websocket: corrected option names, wire objects, scopes/secrets coverage and cross-skill routing
|
|
471
|
+
|
|
472
|
+
- facd61f: Audit the remaining skills against the shipped APIs and correct the drift.
|
|
473
|
+
- pikku-mcp: there is no `wireMCPTool` — a tool _is_ the function (`mcp: true` or `pikkuMCPToolFunc`), while `uri`/`title`/`name` belong on `wireMCPResource`/`wireMCPPrompt` rather than on the `pikkuMCP*Func` factories; resources return `{ uri, text }` only; `PikkuMCPServer` takes `(config, logger)`
|
|
474
|
+
- pikku-http: `channel` is on the wire, not services; `sse` is `get`-only and `query` is `post`-only; `docs` was never a `wireHTTP` option; factories come from `#pikku`
|
|
475
|
+
- pikku-security: documents `authBearer`'s static-token mode, `authCookie`'s merged defaults and re-issue rule, and that every strategy is a no-op without an HTTP request or with a session already set
|
|
476
|
+
- pikku-better-auth: the `admin:users:*` scope tree gained create/ban/remove/sessions/password, and `syncProjectedAdminRole` projects them onto `user.role` for better-auth's own `admin()` endpoints; documents dev quick login
|
|
477
|
+
- pikku-react / pikku-react-query / pikku-workflows-client: `createPikku` options are flat `CorePikkuFetchOptions` with `authHeaders` and the `setAuthorizationJWT`/`setAPIKey`/`setHeader` setters (no request interceptor); `useWorkflowStatus` never stops polling on its own
|
|
478
|
+
- pikku-trigger: a source function runs once at startup with singleton services only; documents `InMemoryTriggerService` startup and the skipped-metadata warning
|
|
479
|
+
- pikku-schedule: the singleton is `schedulerService` and `start()` is what registers the cron jobs; documents `scheduleRPC` and the one-off task API
|
|
480
|
+
- pikku-ws: there is no `PikkuWSServer` — `pikkuWebsocketHandler({ server, wss, logger })` over a `noServer: true` `WebSocketServer` is the real API
|
|
481
|
+
- pikku-info: there are only four subcommands, and `--silent` works despite the spurious "Unknown option" warning
|
|
482
|
+
- pikku-versioning: `override` is not required — a matching `V<n>` export suffix is stripped automatically — and the live function must be bumped explicitly; `versions init` writes an empty manifest, so `versions update` has to follow it
|
|
483
|
+
- pikku-audit: documents `audit: { durability }`, the `Safe<>` guard on `auditLog.write`, `createInvocationAudit`'s logger argument, and `createAuditedKysely`'s options
|
|
484
|
+
- pikku-kysely: six packages, not four — `@pikku/kysely-node-sqlite` / `-bun-sqlite` build the instance functions query, while `createSQLiteKysely` is typed to `KyselyPikkuDB` and wires `SerializePlugin`; the secret service config is `{ key, keyVersion, previousKey, audit }`, not `{ kekSecret, salt }`, and `getSecret` returns a `SecretValue`
|
|
485
|
+
- pikku-emails: template variables are always optional and never required-able; unresolved placeholders render blank rather than failing; documents `pikku emails init`
|
|
486
|
+
- pikku-rtl: rewritten off i18next — there is no `t()` or `i18n.changeLanguage` anywhere in the repo; Arabic is a `messages/ar.json` listed in `project.inlang/settings.json`
|
|
487
|
+
- pikku-i18n: enum labels use the singular `enum__<group>__<member>` namespace `@pikku/paraglide` generates from, not hand-written `enums__` maps; notes the console's wrapped `m` as a leftover rather than a pattern, and that the `mKey`/`mList` runtime resolvers have been removed for good
|
|
488
|
+
- pikku-deps: the summary has `totalIssues`/`totalUpdates` and no `info` bucket, issue `url`/`cvssScore`/`recommendedVersion` are nullable rather than optional, lockfile detection covers pnpm and npm too, and a non-zero `bun audit` exit only counts as data when it produced output
|
|
489
|
+
- pikku-feature: stage changed files by path — `git add -A` sweeps up regenerated artifacts and, on a shared checkout, another agent's work
|
|
490
|
+
- pikku-jose: `decode` verifies the signature and expiry (it is not an unchecked read), keys resolve by the token's `kid` rather than being tried in turn, and the algorithm is fixed HS256
|
|
491
|
+
- pikku-machine-auth: documents restricting a key below its owner via `scopes` on the mapped session, the deliberate verify-vs-scope failure split, and that `betterAuthStatelessSession` has no api-key path
|
|
492
|
+
- pikku-redis / pikku-mongodb: the secret-service config is `{ key, keyVersion, previousKey, … }`, not the fabricated `{ kekSecret, salt }`; both packages also ship a `SessionStore`
|
|
493
|
+
- pikku-pino: log methods take trailing meta varargs and are `Safe<>`-guarded against secrets; `debug` takes a string only
|
|
494
|
+
- pikku-aws / pikku-backblaze: every `ContentService` method takes an args object with a logical `bucket` stored as a path prefix, not positional arguments; `S3ContentConfig` is `{ bucketName, region, endpoint }` and `B2ContentConfig` has no `cdnUrl`; documents `signURL` failing open, the fixed 3600s presign, SQS's 900s delay ceiling and throwing `getJob`, and that `AWSSecrets.getSecret` returns a `SecretValue` and reports every failure as the same fatal error
|
|
495
|
+
- pikku-gateway-slack: `SlackGatewayAdapter` takes `{ signingSecret, tokenResolver }` — there is no `botToken`, one adapter serves every workspace; `verifySlackSignature` is `(secret, signature, timestamp, body)` and returns a boolean; `parseSlashCommand` returns camelCase fields with the raw payload on `raw`; the generic `send()` is a no-op, so replies must go through `createBoundSend`/`SlackGatewayHelper`
|
|
496
|
+
- pikku-ai-vercel: model strings are `provider/model`, not `provider:model`; documents the `'*'` catch-all, `withApiKey`, the transcribe/speech/image/embed methods, and that the service key must be `aiAgentRunner`
|
|
497
|
+
- pikku-ai-voice: rewritten — `@pikku/ai-voice` is a deprecated empty package with no `STTService`/`TTSService`; `voiceInput`/`voiceOutput` come from `@pikku/core/ai-agent` and attach via `aiMiddleware`, with per-script voices, `NoSpeechDetectedError`, and speak-only-when-spoken-to
|
|
498
|
+
- both above: there is no `wireAIAgent` — agents are declared with `pikkuAIAgent` from the generated agent types
|
|
499
|
+
- pikku-schema-ajv / pikku-schema-cfworker: the two validators are not drop-in equivalents — AJV caches by name forever and fills defaults in place, cfworker recompiles on a changed schema and applies no defaults; a missing schema throws a bare string rather than an `Error`
|
|
500
|
+
- pikku-n8n-import: the output directory is `--out/-o`, not a positional argument, and `pikku import n8n` already accepts a directory and flattens array/`{workflows:[]}` exports — an un-importable workflow is skipped while the rest of a batch still imports
|
|
501
|
+
- pikku-template-clone: `create-pikku` keeps only the chosen package manager's lockfile and may have written an empty `yarn.lock`, so commit it after the first install rather than as scaffolded
|
|
502
|
+
- pikku-fabric: the wirings file comment claimed a `wireMCPTool` that has never existed, and the conversion checklist named `fabric.config.json` with a `production.branch` — the real file is `pikkufabric.config.json` with `production.domain`; notes that several CLI messages print the shorter name anyway
|
|
503
|
+
- pikku-fabric-debug: `metrics` also requires `--branch`, and `--follow`'s own help text advertises SSE for what is a 2-second client poll
|
|
504
|
+
- pikku-deploy-express: documents `getHttpServer`/`enableReaper`, that the health check is registered in the constructor (before any middleware, so it cannot be wrapped in auth), what `init()` installs and in what order, and that Express buffers the body so the parser limit is the only place `maxBodySize` can stop an oversized request
|
|
505
|
+
- pikku-deploy-fastify: `enableCors` throws `Method not implemented.`; the health check lives in `init()`, not the constructor; the plugin registers a catch-all `fastify.all('/*')` and only sets `bodyLimit` when `maxBodySize` is supplied, so Fastify's stricter 1MB default otherwise stands
|
|
506
|
+
- pikku-deploy-uws: `PikkuUWSServer` has no `enableCors`, static assets or `content`; `init()` registers the health check plus catch-all HTTP and websocket handlers, `httpOptions` never reaches the websocket one and `loadSchemas` is never passed; `stop()` throws a bare string and waits a fixed 2s; documents the byte-counting `maxBodySize` 413 and why `@pikku/ws` needs `noServer: true`
|
|
507
|
+
- pikku-deploy-lambda: `runFetch` is payload v1 and `runFetchV2` v2 — only v2 echoes an origin or returns a 500; scheduled handlers should use `runLambdaScheduled`, which runs every task in the bundle and swallows per-task failures; the SQS worker's `batchItemFailures` needs `ReportBatchItemFailures` to mean anything; websocket handlers return a real `APIGatewayProxyResult` that must not be replaced with a hardcoded 200; documents the handler factories, `SQS_QUEUE_URL_*` resolution and the binary-unsupported/stale-connection eventhub behaviour
|
|
508
|
+
- pikku-deploy-cloudflare: the hand-rolled `setup-services.ts` never called `setSingletonServices`, so every request would have thrown a CF 1101 — use the exported `setupServices(env, factories)` and the handler factories; documents `runFetch`'s 426 upgrade path, `cf-ray` traceId and `exposeErrors: false` default, that `runScheduled` stops after the first cron match, and the `WEBSOCKET_HIBERNATION_SERVER` binding plus the 1008/403 connect-denial path
|
|
509
|
+
- pikku-deploy-nextjs: `pikkuAPIRequest` strips a leading `/api` (toggle with `removeAPIPrefix`) and passes no wiring options; the helper set includes `patch` and has no `staticPatch`/`staticDel`; the static variants differ by `skipUserSession`, not just where they run, and both bubble errors; documents `PikkuNextJSWorkerRPC` and `toNextJsAuthHandler`
|
|
510
|
+
- pikku-deploy-azure: the exports are `AzInvocationLogger` and `PikkuAZTimerRequest` — `PikkuAzFunctionsLogger` never existed; real deployments go through `createAzureHandler(factories, handlerTypes)` returning `{ http, queue, timer }`; `createAzureWebSocketHandler` is a 501 stub; documents the text-flattened HTTP response, `AZURE_QUEUE_NAME_*` resolution, the 7-day visibility cap, the timer running every task without per-task error handling, and `setLevel` being a no-op
|
|
511
|
+
- pikku-product-second-opinion: stop asserting a fixed TanStack Start release stage — `@tanstack/react-start`'s major tracks the Router line, so the version says nothing about maturity; check the vendor at write-up time
|
|
512
|
+
|
|
513
|
+
- 2f72189: Point the `versions check` hints at a command that exists.
|
|
514
|
+
|
|
515
|
+
Three different failures told you to run `npx pikku versions-update`. There is
|
|
516
|
+
no such command — `update` is a subcommand of `versions` — so anyone following
|
|
517
|
+
the hint hit "unknown command" at the moment they were trying to repair a
|
|
518
|
+
contract manifest. It now prints `npx pikku versions update`.
|
|
519
|
+
|
|
520
|
+
The pikku-versioning skill carried a paragraph warning agents the hint was
|
|
521
|
+
wrong; with the hint fixed, that warning is gone.
|
|
522
|
+
|
|
523
|
+
- 7b0da5e: Point `versions check` at a command that exists.
|
|
524
|
+
|
|
525
|
+
Three of its diagnostics told you to run `npx pikku versions-update`. There is
|
|
526
|
+
no such command — `update` is a subcommand of `versions`, so following the hint
|
|
527
|
+
gets an unknown-command error at the exact moment you have a failing check to
|
|
528
|
+
clear. They now print `npx pikku versions update`.
|
|
529
|
+
|
|
530
|
+
The pikku-versioning skill carried a paragraph warning agents off the bad hint.
|
|
531
|
+
With the hint corrected the warning is the only thing left naming a command that
|
|
532
|
+
does not exist, so it goes too.
|
|
533
|
+
|
|
534
|
+
## 0.12.5
|
|
535
|
+
|
|
536
|
+
### Patch Changes
|
|
537
|
+
|
|
538
|
+
- fd72e58: Drop `scenario.step` — a scenario step is now always a `given`, `when` or
|
|
539
|
+
`then`.
|
|
540
|
+
|
|
541
|
+
`step` rendered no keyword, which made it the phase to reach for whenever a
|
|
542
|
+
step did not obviously fit one of the three. That is exactly the step a reader
|
|
543
|
+
cannot check: a scenario is read by people deciding whether it describes the
|
|
544
|
+
behaviour they wanted, and a row that says what it does without saying whether
|
|
545
|
+
it is setup, action or claim tells them nothing to agree or disagree with. It
|
|
546
|
+
was also the escape hatch from the assertion lint — a scenario with no `then`
|
|
547
|
+
could be made to stop complaining by demoting its steps rather than by
|
|
548
|
+
asserting anything.
|
|
549
|
+
|
|
550
|
+
Replace `scenario.step(...)` with whichever of `given`, `when` or `then` the
|
|
551
|
+
step actually is. `then` is not a rename: it makes the step's bindings
|
|
552
|
+
witnesses rather than alternatives, so every declared surface runs and they
|
|
553
|
+
must agree.
|
|
554
|
+
|
|
555
|
+
- 75e81b1: Document `pikkuServerLifecycle` in the skills corpus. `pikku-concepts` now presents both bootstrap paths (letting `pikku dev`/`pikku serve` own the server vs. embedding in your own runtime) instead of only the hand-rolled entrypoint, `pikku-services` gains a `pikkuServerLifecycle` reference covering hook ordering, discovery rules and the `afterStop`-runs-after-services-stop caveat, and `pikku-config` documents the `lint` severity map including `customServerBootstrap`.
|
|
556
|
+
|
|
557
|
+
## 0.12.4
|
|
558
|
+
|
|
559
|
+
### Patch Changes
|
|
560
|
+
|
|
561
|
+
- 8075f6a: Confine `SecretService` to the places an app is wired.
|
|
562
|
+
|
|
563
|
+
`secrets` is now omitted from the services every function, AI agent, workflow,
|
|
564
|
+
permission and wire receives, and the function runner replaces it with a
|
|
565
|
+
throwing accessor so a cast cannot reach past the type. It stays available in
|
|
566
|
+
`pikkuServices`, `pikkuWireServices`, addon service factories and middleware —
|
|
567
|
+
read a secret there, give it to a service, and have the function ask that
|
|
568
|
+
service.
|
|
569
|
+
|
|
570
|
+
Alongside it:
|
|
571
|
+
- `wireSecret` gains `allowedHosts`, refusing a secret attached to a host it was
|
|
572
|
+
not declared for. Permissive by default; strict via
|
|
573
|
+
`config.secrets.requireAllowedHosts`.
|
|
574
|
+
- `pikku-graph`'s `httpRequest` resolves and attaches its credential inside a new
|
|
575
|
+
`httpRequester` service instead of holding the plaintext in the function.
|
|
576
|
+
- New inspector diagnostics: `PKU950` (a `SecretService` exposed under another
|
|
577
|
+
service name), `PKU951` (a secret read that no `wireSecret` declares) and
|
|
578
|
+
`PKU952` (a secret read with a non-literal key).
|
|
579
|
+
|
|
580
|
+
## 0.12.3
|
|
581
|
+
|
|
582
|
+
### Patch Changes
|
|
583
|
+
|
|
584
|
+
- a7b26c5: rename the inspected declarations to `define*`: `wireScope` → `defineScope`, `wireSecret` → `defineSecret`, `wireVariable` → `defineVariable`, `wireCredential` → `defineCredential`
|
|
585
|
+
|
|
586
|
+
`wire*` meant two unrelated things. A transport wiring attaches a function to
|
|
587
|
+
something that can invoke it — `wireHTTP`, `wireChannel`, `wireScheduler`,
|
|
588
|
+
`wireQueueWorker` and the rest — and the thing it wires runs. These four wire
|
|
589
|
+
nothing: they are no-ops that exist only so the call typechecks, they are
|
|
590
|
+
tree-shaken out of the build, and their whole job is to be found by the
|
|
591
|
+
inspector's AST pass and turned into a type union. One word for both left the
|
|
592
|
+
declaration reading like a registration with a runtime.
|
|
593
|
+
|
|
594
|
+
So the vocabulary splits: **`wire*` is a transport, `define*` is an inspected
|
|
595
|
+
declaration.**
|
|
596
|
+
|
|
597
|
+
```ts
|
|
598
|
+
import { defineScope } from '@pikku/core/scope'
|
|
599
|
+
import { defineSecret } from '@pikku/core/secret'
|
|
600
|
+
import { defineVariable } from '@pikku/core/variable'
|
|
601
|
+
import { defineCredential } from '@pikku/core/credential'
|
|
602
|
+
|
|
603
|
+
defineScope({ admin: { scopes: { invoices: { scopes: { create: {} } } } } })
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
**Breaking:** no alias is kept. Rename the four call sites; the module subpaths
|
|
607
|
+
(`@pikku/core/scope`, `/secret`, `/variable`) are unchanged.
|
|
608
|
+
|
|
609
|
+
The inspector matches these by identifier text, so a stale `wire*` call is not a
|
|
610
|
+
type error — it is silently not extracted, and the generated union comes back
|
|
611
|
+
empty. That fails as "this scope isn't declared" on code that was fine a moment
|
|
612
|
+
ago, nowhere near the declaration. Grep for the old names rather than trusting a
|
|
613
|
+
clean build.
|
|
614
|
+
|
|
615
|
+
An addon published with `.pikku` output generated before this release re-exports
|
|
616
|
+
`wireSecret` from `@pikku/core/secret` and will not typecheck against this core
|
|
617
|
+
until it is rebuilt and republished.
|
|
618
|
+
|
|
619
|
+
- 457cb25: Add `definePersonas()`: the people a project's scenarios and virtual users run
|
|
620
|
+
as, declared in code.
|
|
621
|
+
|
|
622
|
+
There used to be three names for two-and-a-bit things — an _actor_ in
|
|
623
|
+
`scenarios.actors`, a _persona_ in `scenarios.personas`, and a _virtual user_
|
|
624
|
+
declared separately against an actor. In practice almost every actor was its own
|
|
625
|
+
kind, so the second set carried no information and the third was a third place
|
|
626
|
+
for a name to drift. There is now one declaration:
|
|
627
|
+
|
|
628
|
+
```ts
|
|
629
|
+
definePersonas({
|
|
630
|
+
shopper: {
|
|
631
|
+
name: 'Sam Shopper',
|
|
632
|
+
jobTitle: 'Shopper',
|
|
633
|
+
personality: 'Buys in a hurry and leaves tabs open',
|
|
634
|
+
roles: ['customer'],
|
|
635
|
+
disposition: 'careless',
|
|
636
|
+
goals: ['Buy something without reading anything'],
|
|
637
|
+
account: {},
|
|
638
|
+
},
|
|
639
|
+
})
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
A persona is a person: what they are like, what they want, the roles they hold,
|
|
643
|
+
and **one** account they sign in with — `account: {}` plus `linkedAccounts` for
|
|
644
|
+
the rare case of more, modelled on how better-auth does linking. A persona with a
|
|
645
|
+
`disposition` is a virtual user; `runnable: false` marks someone who only ever
|
|
646
|
+
exists to be acted upon — banned, shared with, reset — and is never handed a
|
|
647
|
+
session.
|
|
648
|
+
|
|
649
|
+
**A persona names roles, never scopes.** Scopes come from `defineSystemRole()`
|
|
650
|
+
expansion, so the build fails if a persona names a role nobody declared, and
|
|
651
|
+
fails again if a role confers a scope no `defineScope` declares. Running one only
|
|
652
|
+
ever has to check that its roles are still valid.
|
|
653
|
+
|
|
654
|
+
**Addresses are computed, never declared.** `personaEmail(id, domain, runId)`
|
|
655
|
+
derives `<id>[+runId]@<domain>` from `scenarios.emailDomain`, so a seed, a
|
|
656
|
+
scenario run and a virtual-user run cannot disagree about who they are signing in
|
|
657
|
+
as. `scenarios.actors` and `scenarios.personas` are gone from
|
|
658
|
+
`pikku.config.json` — only `emailDomain` remains.
|
|
659
|
+
|
|
660
|
+
`actor` survives in exactly one place: the name of a **slot in a scenario step**,
|
|
661
|
+
which is the role a persona is cast in for that step. `pikkuVirtualUser()`,
|
|
662
|
+
`kind`, `grants` and the `actor` field are removed; the `actors` service is now
|
|
663
|
+
`personas`, and the CLI's `virtual-user` commands are now `pikku persona list` /
|
|
664
|
+
`pikku persona run`. `budget` and `allowApprovalRequired` moved to run flags —
|
|
665
|
+
how much you will spend today is not a fact about a person.
|
|
666
|
+
|
|
667
|
+
`@pikku/cucumber` drops its `Actor` class and `ActorDispatchContext`: a
|
|
668
|
+
hand-rolled cookie jar that a persona's own typed session replaces outright.
|
|
669
|
+
|
|
670
|
+
- 86a50b9: scenario: replace `browser: true` + `func` with per-surface bindings on `pikkuScenarioStep`
|
|
671
|
+
|
|
672
|
+
A step now declares one implementation per surface it can be driven through:
|
|
673
|
+
|
|
674
|
+
```ts
|
|
675
|
+
export const buysTheItem = pikkuScenarioStep<{ sku: string }, { orderId: string }>({
|
|
676
|
+
name: 'buysTheItem',
|
|
677
|
+
description: 'buys the item',
|
|
678
|
+
browser: async (services, data, { browser }) => { ... },
|
|
679
|
+
default: async (services, data, { rpc }) => { ... },
|
|
680
|
+
})
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
`pikku scenario run --run browser|cli|default` picks which surface the run drives,
|
|
684
|
+
and the two phases resolve bindings differently:
|
|
685
|
+
- **Actions** (`given` / `when` / `step`) run exactly one binding — the run
|
|
686
|
+
surface if it has one, otherwise `default`. A step with neither now fails with
|
|
687
|
+
`ScenarioNoSurfaceBinding` instead of silently running server-side.
|
|
688
|
+
- **Assertions** (`then`) are witnesses, not alternatives: every declared binding
|
|
689
|
+
runs and they must agree. Two surfaces reporting different things fails the run
|
|
690
|
+
with `ScenarioWitnessDisagreement` rather than reporting a pass. An assertion
|
|
691
|
+
with no witness the run can execute at all fails with `ScenarioNoWitness` —
|
|
692
|
+
without it the step returns `undefined` and renders as a tick, reporting a pass
|
|
693
|
+
for something nobody checked.
|
|
694
|
+
|
|
695
|
+
A scenario written as a step ladder that never calls `then` is now a **PKU680**
|
|
696
|
+
critical. It proves only that nothing threw, so an assertion-free ladder of
|
|
697
|
+
browser-bound actions would score perfect coverage while checking nothing.
|
|
698
|
+
|
|
699
|
+
The report gains a surface-coverage line — `n/m steps ran on browser`, counted
|
|
700
|
+
over every step, so an action that fell back to the server lowers the ratio
|
|
701
|
+
rather than needing a footnote. That also makes surfaces comparable over one
|
|
702
|
+
denominator: a scenario is `4/4` on a default run and `3/4` on a browser one.
|
|
703
|
+
Assertions that fell back are named separately and gate `--strict`, since a
|
|
704
|
+
sentence claiming the actor saw something nobody looked at is a different problem
|
|
705
|
+
from an action taking a shortcut.
|
|
706
|
+
|
|
707
|
+
**Breaking:** `browser: true` and the third `B extends boolean` type argument are
|
|
708
|
+
gone. Rename `func` to `default` (or to `browser` where the step drove a browser)
|
|
709
|
+
and drop the type argument.
|
|
710
|
+
|
|
711
|
+
## 0.12.2
|
|
712
|
+
|
|
713
|
+
### Patch Changes
|
|
714
|
+
|
|
715
|
+
- b89d3b3: Bring the knowledge base into OSS: a package, a CLI gate, a console browser and a skill
|
|
716
|
+
|
|
717
|
+
`knowledge/` is where a project records the things `pikku meta` cannot tell you —
|
|
718
|
+
what a slice is for, which rule was chosen and what it rules out, what is still an
|
|
719
|
+
open question. Tables, routes, schemas and permissions are generated, so a note
|
|
720
|
+
that repeats them is a copy that will drift, and the profile refuses the sections
|
|
721
|
+
where that happens.
|
|
722
|
+
- **`@pikku/knowledge`** (new) reads the notes, builds the link graph in both
|
|
723
|
+
directions, and validates the app-project profile: every note typed, every
|
|
724
|
+
section indexed, every slice carrying a third-person gherkin scenario and at
|
|
725
|
+
most three entities, and every `resource:` URI resolving against the generated
|
|
726
|
+
meta. The resource check fails closed on drift and open on ignorance — a prefix
|
|
727
|
+
whose meta is absent is skipped rather than called dangling.
|
|
728
|
+
- **`pikku knowledge validate`** and **`pikku knowledge index`** replace the dead
|
|
729
|
+
three-flat-files check. Both exit non-zero on an inconsistent base, so a
|
|
730
|
+
pipeline can stop on one; `index` refreshes each `index.md` listing while
|
|
731
|
+
leaving the prose around it alone, and now gives a section that holds only
|
|
732
|
+
sub-sections an index of its own instead of leaving it unreachable.
|
|
733
|
+
- **The console** gains a read-only Knowledge page: notes grouped by section,
|
|
734
|
+
a rendered document with its tags, resources, links in both directions and the
|
|
735
|
+
findings against it, and intra-bundle markdown links that open the linked note
|
|
736
|
+
instead of leaving the page. Read-only by design — a note is edited in the repo,
|
|
737
|
+
in the same commit as the code it describes.
|
|
738
|
+
- **The `pikku-knowledge` skill** documents the format for agents, and Fabric
|
|
739
|
+
builds on it rather than restating it.
|
|
740
|
+
- **`@pikku/inspector`**: a zod schema imported from a built workspace package
|
|
741
|
+
resolved to that package's `.d.ts`, which has no runtime exports at all, so
|
|
742
|
+
every schema in it was reported missing. The emitted JS beside it is imported
|
|
743
|
+
instead.
|
|
744
|
+
|
|
745
|
+
- e14c530: Drop OpenCode-specific discovery guidance from the bundled skills
|
|
746
|
+
|
|
747
|
+
Step 1 of the execution checklist in 43 skills opened with "Prefer OpenCode
|
|
748
|
+
tools such as `pikku-meta` when available; otherwise run the relevant
|
|
749
|
+
`pikku meta ... --json` command". The skills ship to every agent that reads
|
|
750
|
+
them, most of which have no such tools, so the preferred branch was dead
|
|
751
|
+
advice that an agent had to reason past before reaching the instruction that
|
|
752
|
+
actually applies.
|
|
753
|
+
|
|
754
|
+
The step now just says to run `pikku meta ... --json`. The README still notes
|
|
755
|
+
that the frontmatter shape is the one Claude Code, opencode and pi.dev all
|
|
756
|
+
parse — that is a compatibility fact about the format, not a routing hint.
|
|
757
|
+
|
|
758
|
+
## 0.12.1
|
|
759
|
+
|
|
760
|
+
### Patch Changes
|
|
761
|
+
|
|
762
|
+
- 637e668: Move the bundled agent skills out of `@pikku/cli` into a new MIT-licensed `@pikku/skills` package.
|
|
763
|
+
|
|
764
|
+
The skills are the open core — the instruction set any harness reads to build, wire and deploy a Pikku project — but they shipped inside `@pikku/cli`, whose `files` array carried `skills/` under BUSL-1.1 with no carve-out. Their terms now stand on their own package and no longer depend on the CLI that installs them.
|
|
765
|
+
|
|
766
|
+
This also fixes `pikku skills install` on the native binaries. `bun build --compile` only bundles the JS import graph, so 81 markdown files reached through `readdir` never made it in: every Homebrew install failed with `Could not locate bundled skills directory`, while npm installs worked. `@pikku/skills` ships both the `skills/` directory and an embedded path → contents manifest, and reads prefer the directory when one exists — so skill edits stay live in development, and the binary falls back to the manifest it now carries.
|
|
767
|
+
|
|
768
|
+
No skill content changed, and `pikku skills install` takes the same flags.
|