@pikku/cli 0.12.91 → 0.12.93

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.
Files changed (241) hide show
  1. package/LICENSE +106 -0
  2. package/console-app/assets/{index-D0HG8q0B.css → index-BYj7ZbUU.css} +1 -1
  3. package/console-app/assets/{index-CpkvmAGb.js → index-CmAplHjZ.js} +151 -149
  4. package/console-app/index.html +2 -2
  5. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  6. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  7. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  8. package/dist/.pikku/cli/pikku-cli-channel.js +16 -1
  9. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  10. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  11. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  12. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  13. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +15 -0
  14. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  15. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  16. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  17. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +48 -0
  18. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  19. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  20. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  21. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  22. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  23. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  24. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  25. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  26. package/dist/.pikku/function/pikku-functions-meta.gen.json +73 -40
  27. package/dist/.pikku/function/pikku-functions.gen.js +1 -3
  28. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  29. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  30. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  31. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  32. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +2 -1
  33. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +2 -1
  34. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  35. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  36. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  37. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  38. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  39. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  40. package/dist/.pikku/pikku-types.gen.js +1 -1
  41. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  42. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  43. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  45. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  46. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  47. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +3 -1
  48. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  49. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  50. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  51. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  52. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +4 -0
  53. package/dist/.pikku/scenarios/schemas/register.gen.js +4 -0
  54. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  55. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  56. package/dist/.pikku/schemas/register.gen.js +13 -3
  57. package/dist/.pikku/schemas/schemas/FabricSecretsListOutput.schema.json +1 -1
  58. package/dist/.pikku/schemas/schemas/FabricSecretsRotateInput.schema.json +1 -0
  59. package/dist/.pikku/schemas/schemas/FabricSecretsRotateOutput.schema.json +1 -0
  60. package/dist/.pikku/schemas/schemas/FabricSecretsSetOutput.schema.json +1 -1
  61. package/dist/.pikku/schemas/schemas/KnowledgeIndexInput.schema.json +1 -0
  62. package/dist/.pikku/schemas/schemas/KnowledgeIndexOutput.schema.json +1 -0
  63. package/dist/.pikku/schemas/schemas/KnowledgeValidateInput.schema.json +1 -0
  64. package/dist/.pikku/schemas/schemas/KnowledgeValidateOutput.schema.json +1 -0
  65. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  66. package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +1 -1
  67. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  68. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  69. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  70. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  71. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  72. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  73. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  74. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  75. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  76. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  77. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  78. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  79. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  80. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +2 -8
  81. package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +1 -1
  82. package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +1 -1
  83. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  84. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  85. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  86. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  87. package/dist/bin/pikku-bin.mjs +2 -2
  88. package/dist/src/cli.wiring.js +29 -0
  89. package/dist/src/deploy/analyzer/analyzer.js +22 -5
  90. package/dist/src/deploy/build-pipeline.js +5 -1
  91. package/dist/src/fabric/fabric-commands.d.ts +37 -9
  92. package/dist/src/fabric/fabric-commands.js +12 -0
  93. package/dist/src/fabric/functions/domains-add.function.d.ts +4 -4
  94. package/dist/src/fabric/functions/secrets-list.function.d.ts +16 -4
  95. package/dist/src/fabric/functions/secrets-list.function.js +12 -10
  96. package/dist/src/fabric/functions/secrets-rotate.function.d.ts +24 -0
  97. package/dist/src/fabric/functions/secrets-rotate.function.js +36 -0
  98. package/dist/src/fabric/functions/secrets-set.function.d.ts +8 -4
  99. package/dist/src/fabric/functions/secrets-set.function.js +18 -7
  100. package/dist/src/fabric/functions/validate.function.js +16 -7
  101. package/dist/src/fabric/lib/http.d.ts +7 -1
  102. package/dist/src/fabric/lib/sealed-box.d.ts +16 -0
  103. package/dist/src/fabric/lib/sealed-box.js +72 -0
  104. package/dist/src/functions/commands/dev.js +9 -0
  105. package/dist/src/functions/commands/knowledge-index.d.ts +30 -0
  106. package/dist/src/functions/commands/knowledge-index.js +11 -0
  107. package/dist/src/functions/commands/knowledge-validate.d.ts +33 -0
  108. package/dist/src/functions/commands/knowledge-validate.js +11 -0
  109. package/dist/src/functions/commands/new-addon.d.ts +18 -0
  110. package/dist/src/functions/commands/new-addon.js +40 -24
  111. package/dist/src/functions/commands/pikku-command-bootstrap.js +1 -0
  112. package/dist/src/functions/commands/scenario-browser.d.ts +17 -2
  113. package/dist/src/functions/commands/scenario-browser.js +16 -8
  114. package/dist/src/functions/commands/scenario-environment.d.ts +32 -0
  115. package/dist/src/functions/commands/scenario-environment.js +61 -0
  116. package/dist/src/functions/commands/scenario.d.ts +6 -0
  117. package/dist/src/functions/commands/scenario.js +14 -9
  118. package/dist/src/functions/commands/skills.js +27 -61
  119. package/dist/src/functions/db/db-codegen.d.ts +6 -0
  120. package/dist/src/functions/db/db-codegen.js +9 -0
  121. package/dist/src/functions/db/db-migrator.js +14 -0
  122. package/dist/src/functions/db/local-db.d.ts +8 -0
  123. package/dist/src/functions/db/local-db.js +24 -2
  124. package/dist/src/functions/db/migration-identifiers.d.ts +58 -0
  125. package/dist/src/functions/db/migration-identifiers.js +262 -0
  126. package/dist/src/functions/db/migration-provenance.d.ts +35 -0
  127. package/dist/src/functions/db/migration-provenance.js +80 -0
  128. package/dist/src/functions/db/schema-sql.d.ts +43 -0
  129. package/dist/src/functions/db/schema-sql.js +135 -0
  130. package/dist/src/functions/db/sqlite/sqlite-kysely.js +22 -2
  131. package/dist/src/functions/knowledge/render.d.ts +3 -0
  132. package/dist/src/functions/knowledge/render.js +66 -0
  133. package/dist/src/functions/knowledge/schemas.d.ts +51 -0
  134. package/dist/src/functions/knowledge/schemas.js +25 -0
  135. package/dist/src/functions/wirings/credentials/serialize-credentials-types.js +8 -1
  136. package/dist/src/functions/wirings/functions/schemas.js +13 -1
  137. package/dist/src/functions/wirings/rpc/pikku-command-rpc.js +6 -1
  138. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +10 -0
  139. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +102 -0
  140. package/dist/src/functions/wirings/scenarios/scenario-partition.d.ts +13 -0
  141. package/dist/src/functions/wirings/scenarios/scenario-partition.js +15 -2
  142. package/dist/src/functions/wirings/scenarios/scenario-schema-partition.d.ts +33 -0
  143. package/dist/src/functions/wirings/scenarios/scenario-schema-partition.js +49 -0
  144. package/dist/src/functions/wirings/secrets/serialize-secrets-types.js +10 -3
  145. package/dist/src/functions/wirings/variables/serialize-variables-types.js +8 -1
  146. package/dist/src/functions/workflows/all.workflow.js +2 -7
  147. package/dist/src/utils/file-writer.js +14 -2
  148. package/dist/src/utils/pikku-cli-config.d.ts +14 -0
  149. package/dist/src/utils/pikku-cli-config.js +34 -0
  150. package/dist/src/utils/remove-legacy-scaffold-file.d.ts +14 -0
  151. package/dist/src/utils/remove-legacy-scaffold-file.js +23 -0
  152. package/dist/src/utils/serialize-schemas.d.ts +3 -1
  153. package/dist/src/utils/serialize-schemas.js +39 -4
  154. package/dist/tsconfig.tsbuildinfo +1 -1
  155. package/package.json +10 -9
  156. package/dist/.pikku/schemas/schemas/PikkuScenarioFunctionsOutput.schema.json +0 -1
  157. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.d.ts +0 -1
  158. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +0 -31
  159. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +0 -10
  160. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +0 -104
  161. package/skills/pikku-addon/SKILL.md +0 -243
  162. package/skills/pikku-addon/references/addon-package-manifest.md +0 -63
  163. package/skills/pikku-ai-agent/SKILL.md +0 -231
  164. package/skills/pikku-ai-vercel/SKILL.md +0 -82
  165. package/skills/pikku-ai-voice/SKILL.md +0 -88
  166. package/skills/pikku-audit/SKILL.md +0 -175
  167. package/skills/pikku-aws/SKILL.md +0 -111
  168. package/skills/pikku-backblaze/SKILL.md +0 -71
  169. package/skills/pikku-better-auth/SKILL.md +0 -298
  170. package/skills/pikku-cli/SKILL.md +0 -198
  171. package/skills/pikku-cli/references/complete-example.md +0 -82
  172. package/skills/pikku-concepts/SKILL.md +0 -250
  173. package/skills/pikku-concepts/references/concept-mapping.md +0 -556
  174. package/skills/pikku-concepts/references/packages.md +0 -29
  175. package/skills/pikku-config/SKILL.md +0 -212
  176. package/skills/pikku-cron/SKILL.md +0 -214
  177. package/skills/pikku-deploy-azure/SKILL.md +0 -71
  178. package/skills/pikku-deploy-cloudflare/SKILL.md +0 -82
  179. package/skills/pikku-deploy-express/SKILL.md +0 -86
  180. package/skills/pikku-deploy-fastify/SKILL.md +0 -73
  181. package/skills/pikku-deploy-lambda/SKILL.md +0 -110
  182. package/skills/pikku-deploy-nextjs/SKILL.md +0 -78
  183. package/skills/pikku-deploy-uws/SKILL.md +0 -88
  184. package/skills/pikku-deps/SKILL.md +0 -98
  185. package/skills/pikku-emails/SKILL.md +0 -163
  186. package/skills/pikku-fabric/SKILL.md +0 -318
  187. package/skills/pikku-fabric-debug/SKILL.md +0 -112
  188. package/skills/pikku-feature/SKILL.md +0 -258
  189. package/skills/pikku-gateway-slack/SKILL.md +0 -115
  190. package/skills/pikku-http/SKILL.md +0 -220
  191. package/skills/pikku-http/references/http-options.md +0 -55
  192. package/skills/pikku-i18n/SKILL.md +0 -137
  193. package/skills/pikku-info/SKILL.md +0 -100
  194. package/skills/pikku-jose/SKILL.md +0 -105
  195. package/skills/pikku-kysely/SKILL.md +0 -219
  196. package/skills/pikku-machine-auth/SKILL.md +0 -183
  197. package/skills/pikku-mcp/SKILL.md +0 -241
  198. package/skills/pikku-middleware/SKILL.md +0 -231
  199. package/skills/pikku-middleware/references/middleware-patterns.md +0 -61
  200. package/skills/pikku-mongodb/SKILL.md +0 -105
  201. package/skills/pikku-n8n-import/SKILL.md +0 -109
  202. package/skills/pikku-n8n-import/SPEC.md +0 -84
  203. package/skills/pikku-n8n-import/references/addon-mapping.md +0 -121
  204. package/skills/pikku-n8n-import/references/code-translation.md +0 -121
  205. package/skills/pikku-n8n-import/references/loops-and-control.md +0 -87
  206. package/skills/pikku-paraglide/SKILL.md +0 -117
  207. package/skills/pikku-permissions/SKILL.md +0 -192
  208. package/skills/pikku-pino/SKILL.md +0 -79
  209. package/skills/pikku-product-second-opinion/README.md +0 -43
  210. package/skills/pikku-product-second-opinion/SKILL.md +0 -158
  211. package/skills/pikku-product-second-opinion/example/sample-report.md +0 -90
  212. package/skills/pikku-product-second-opinion/references/report-template.md +0 -73
  213. package/skills/pikku-queue/SKILL.md +0 -240
  214. package/skills/pikku-react/SKILL.md +0 -212
  215. package/skills/pikku-react-query/SKILL.md +0 -242
  216. package/skills/pikku-realtime/SKILL.md +0 -236
  217. package/skills/pikku-realtime/references/other-routes.md +0 -23
  218. package/skills/pikku-redis/SKILL.md +0 -90
  219. package/skills/pikku-rpc/SKILL.md +0 -171
  220. package/skills/pikku-rtl/SKILL.md +0 -219
  221. package/skills/pikku-scenario/SKILL.md +0 -382
  222. package/skills/pikku-schedule/SKILL.md +0 -57
  223. package/skills/pikku-schema-ajv/SKILL.md +0 -62
  224. package/skills/pikku-schema-cfworker/SKILL.md +0 -63
  225. package/skills/pikku-security/SKILL.md +0 -108
  226. package/skills/pikku-services/SKILL.md +0 -248
  227. package/skills/pikku-services/references/audit-wire-service.md +0 -34
  228. package/skills/pikku-software-archaeology/README.md +0 -70
  229. package/skills/pikku-software-archaeology/SKILL.md +0 -186
  230. package/skills/pikku-software-archaeology/references/blueprint.schema.json +0 -625
  231. package/skills/pikku-software-archaeology/references/pikku-mapping.md +0 -49
  232. package/skills/pikku-software-archaeology/scripts/validate.mjs +0 -173
  233. package/skills/pikku-tag-middleware/SKILL.md +0 -13
  234. package/skills/pikku-template-clone/SKILL.md +0 -40
  235. package/skills/pikku-trigger/SKILL.md +0 -181
  236. package/skills/pikku-versioning/SKILL.md +0 -173
  237. package/skills/pikku-websocket/SKILL.md +0 -243
  238. package/skills/pikku-workflow/SKILL.md +0 -172
  239. package/skills/pikku-workflow/references/workflow-reference.md +0 -63
  240. package/skills/pikku-workflows-client/SKILL.md +0 -150
  241. package/skills/pikku-ws/SKILL.md +0 -47
@@ -1,49 +0,0 @@
1
- # How Pikku Consumes a Product Blueprint
2
-
3
- The `.knowledge/` blueprint is designed so each concept maps onto exactly one Pikku primitive. A generator (or an agent following `pikku-feature`) walks the JSON files in this order:
4
-
5
- | Blueprint source | Pikku target |
6
- |---|---|
7
- | `entities.json` attributes + relationships + constraints | Kysely migrations + generated `DB` types; Zod schemas per entity |
8
- | `entities.json` states/transitions | a `status` column + transition guards inside the owning commands (or a state-machine helper) |
9
- | `commands.json` | `pikkuFunc` / `pikkuSessionlessFunc` with `input:` Zod schema built from `input[]`; `preconditions` become guard clauses; name is the camelCased command name (`SendInvoice` → `sendInvoice`) |
10
- | `queries.json` | `pikkuFunc` reads; `scoping` becomes the mandatory `WHERE` / session filter |
11
- | `events.json` | EventHub topics (realtime) or queue messages; `consumedBy` become `wireQueueWorker` handlers — implicit events (`explicit: false`) get promoted to real emissions |
12
- | `policies.json` (authorization) | Pikku `permissions` / middleware; one policy = one named permission function, wired everywhere `enforcedAt` listed — this collapses duplicated legacy checks into a single definition |
13
- | `policies.json` (validation) | Zod schema refinements on the command's `input` |
14
- | `workflows.json` kind=user | frontend flows + the commands they chain |
15
- | `workflows.json` kind=system, with `schedule` | `wireScheduler` entries |
16
- | `workflows.json` multi-step / checkpointing | `pikkuWorkflowFunc` with one `workflow.do(...)` step per blueprint step |
17
- | `workflows.json` `scenarios[]` | **`pikkuUserFlow` stories — this is the canonical target.** Each scenario's given/when/outcome maps 1:1 onto a user-flow step sequence; group scenarios by their workflow into one flow per journey. Only scenarios with no user-facing surface (pure system workflows: cron sweeps, webhook ingest) fall back to API/e2e tests |
18
- | `api.json` | `wireHTTP` routes: keep `path`+`method` for compatibility, point at the mapped command/query func; `auth: none`/capability-URL surfaces get `auth: false` |
19
- | `api.json` kind=webhook-in | `wireHTTP` with `auth: false` + signature-verification middleware from the integration |
20
- | `integrations.json` | services in `services.ts` (constructor-injected classes); `configVia` env vars become `wireSecret` / config; per-user credentials become `wireCredential` |
21
- | `architecture.json` notes | deployment config (ports, raw-body routes, proxy expectations) |
22
- | `invariants.json` enforcedBy=db-constraint | migration constraints (UNIQUE, CHECK, FK) |
23
- | `invariants.json` enforcedBy=code-guard/nothing | guard clauses + a test each; `atRiskBecause` entries get a hardening task |
24
- | `gaps.json` | excluded from generation; `open-product-decision` + `migration.json.decisionsNeeded` go to a human BEFORE generation starts |
25
- | `migration.json.mappings` | the work plan: one mapping = one migration slice |
26
- | `interfaces.json` kind=cli | `wireCLI` entrypoints — the CLI commands are the same funcs the routes expose |
27
- | `interfaces.json` kind=mcp | `wireMCP` — each MCP tool IS a `pikkuFunc` (reuse the command/query funcs; don't author tool duplicates) |
28
- | `interfaces.json` kind=openapi-rest / sdk | generated, not hand-written: the OpenAPI spec + typed client SDK fall out of the `wireHTTP` routes + codegen |
29
- | `interfaces.json` kind=websocket-realtime | `pikku-realtime` EventHub topics / channels |
30
- | `frontend.json` | `apps/app` (TanStack Start) shell: router, `@pikku/mantine` theme, `pikku-react-query` data layer, `better-auth` client — the target stack the legacy UI is rebuilt onto |
31
- | `frontend-routes.json` | TanStack Router routes under `apps/app/src/routes/**` (thin data containers calling `usePikkuQuery`); `dataFrom` names become the generated hooks; subpath routes for rich detail views |
32
- | `frontend-components.json` rebuild=`mantine-standard`/`mantine-composition` | components in `packages/components` composed from `@pikku/mantine` — the trivial/straightforward bulk |
33
- | `frontend-components.json` rebuild=`custom-logic` | the PORT list — each becomes a `packages/components` component that reimplements the bespoke behavior (chart/table/editor); its `dependencies` inform whether the lib is kept or replaced. These are the frontend's real work items |
34
- | `frontend-components.json` rebuild=`custom-style` | normalize to Mantine/theme tokens; usually deleted-and-recomposed, not ported |
35
-
36
- ## Order of generation
37
-
38
- 1. Human resolves `decisionsNeeded`.
39
- 2. Entities → migrations + types.
40
- 3. Policies → permission functions (before commands, so commands can reference them).
41
- 4. Commands + queries → funcs; api.json → wirings.
42
- 5. Events → topics/queues; system workflows → schedulers/workers/workflows.
43
- 6. Scenarios → tests. Run them against the new implementation; they encode the legacy behavior worth preserving.
44
-
45
- ## Uncertainty handling
46
-
47
- - `confidence: high` concepts generate directly.
48
- - `confidence: medium` concepts generate, but are listed for review in the pre-generation report — id, evidence summary, and what is uncertain — rather than carrying a marker comment in the generated code. The report is the review surface; the generated code stays clean.
49
- - `confidence: low` concepts are NOT generated automatically — they surface in the pre-generation review along with `decisionsNeeded`.
@@ -1,173 +0,0 @@
1
- #!/usr/bin/env node
2
- // Validates a .knowledge/ blueprint directory against references/blueprint.schema.json,
3
- // then runs cross-file referential checks (does every command's domain exist, does every
4
- // api surface map to a real command/query, ...). Exit 0 = valid, 1 = errors.
5
- //
6
- // Usage: node validate.mjs <path-to-.knowledge-dir>
7
-
8
- import { readFileSync, existsSync } from 'node:fs';
9
- import { join, dirname } from 'node:path';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- const here = dirname(fileURLToPath(import.meta.url));
13
- const schemaDoc = JSON.parse(readFileSync(join(here, '..', 'references', 'blueprint.schema.json'), 'utf8'));
14
-
15
- const dir = process.argv[2];
16
- if (!dir) { console.error('usage: node validate.mjs <.knowledge dir>'); process.exit(2); }
17
-
18
- const errors = [];
19
- const warnings = [];
20
-
21
- // --- minimal JSON-Schema-subset validator (type, required, properties, items, enum, minItems, pattern, $ref -> $defs) ---
22
- function resolveRef(ref) {
23
- const m = /^#\/\$defs\/(\w+)$/.exec(ref);
24
- if (!m || !schemaDoc.$defs[m[1]]) throw new Error(`unresolvable $ref ${ref}`);
25
- return schemaDoc.$defs[m[1]];
26
- }
27
-
28
- function check(value, schema, path) {
29
- if (schema.$ref) schema = { ...resolveRef(schema.$ref), ...schema, $ref: undefined };
30
- if (schema.enum && !schema.enum.includes(value)) {
31
- errors.push(`${path}: expected one of [${schema.enum.join(', ')}], got ${JSON.stringify(value)}`);
32
- return;
33
- }
34
- const t = schema.type;
35
- if (t === 'object') {
36
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
37
- errors.push(`${path}: expected object`); return;
38
- }
39
- for (const req of schema.required || []) {
40
- if (!(req in value)) errors.push(`${path}: missing required field "${req}"`);
41
- }
42
- for (const [k, v] of Object.entries(value)) {
43
- if (schema.properties?.[k]) check(v, schema.properties[k], `${path}.${k}`);
44
- }
45
- } else if (t === 'array') {
46
- if (!Array.isArray(value)) { errors.push(`${path}: expected array`); return; }
47
- if (schema.minItems && value.length < schema.minItems) {
48
- errors.push(`${path}: needs at least ${schema.minItems} item(s), has ${value.length}`);
49
- }
50
- if (schema.items) value.forEach((v, i) => check(v, schema.items, `${path}[${i}]`));
51
- } else if (t === 'string') {
52
- if (typeof value !== 'string') { errors.push(`${path}: expected string`); return; }
53
- if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
54
- errors.push(`${path}: "${value}" does not match ${schema.pattern}`);
55
- }
56
- } else if (t === 'boolean' && typeof value !== 'boolean') {
57
- errors.push(`${path}: expected boolean`);
58
- } else if (t === 'number' && typeof value !== 'number') {
59
- errors.push(`${path}: expected number`);
60
- }
61
- }
62
-
63
- // --- load + per-file validation ---
64
- // Files marked `x-optional` (the frontend layer) only validate when present, so a
65
- // backend-only repo does not fail for lacking them.
66
- const docs = {};
67
- for (const [filename, fileSchema] of Object.entries(schemaDoc.files)) {
68
- const p = join(dir, filename);
69
- if (!existsSync(p)) {
70
- if (!fileSchema['x-optional']) errors.push(`${filename}: missing`);
71
- continue;
72
- }
73
- try {
74
- docs[filename] = JSON.parse(readFileSync(p, 'utf8'));
75
- } catch (e) {
76
- errors.push(`${filename}: invalid JSON (${e.message})`); continue;
77
- }
78
- check(docs[filename], fileSchema, filename);
79
- }
80
- if (!existsSync(join(dir, 'blueprint.md'))) errors.push('blueprint.md: missing');
81
-
82
- // --- cross-file referential checks ---
83
- if (docs['domains.json'] && docs['commands.json']) {
84
- const domains = new Set((docs['domains.json'].domains || []).map((d) => d.name));
85
- const commandNames = new Set((docs['commands.json'].commands || []).map((c) => c.name));
86
- const queryNames = new Set((docs['queries.json']?.queries || []).map((q) => q.name));
87
- const eventNames = new Set((docs['events.json']?.events || []).map((e) => e.name));
88
-
89
- const wantDomain = (owner, d) => {
90
- if (d && !domains.has(d)) errors.push(`${owner}: domain "${d}" not defined in domains.json`);
91
- };
92
- for (const c of docs['commands.json'].commands || []) {
93
- wantDomain(`commands.json:${c.name}`, c.domain);
94
- for (const ev of c.eventsProduced || []) {
95
- if (!eventNames.has(ev)) warnings.push(`commands.json:${c.name} produces "${ev}" which is not in events.json`);
96
- }
97
- }
98
- for (const q of docs['queries.json']?.queries || []) wantDomain(`queries.json:${q.name}`, q.domain);
99
- for (const e of docs['entities.json']?.entities || []) wantDomain(`entities.json:${e.name}`, e.domain);
100
- for (const ev of docs['events.json']?.events || []) wantDomain(`events.json:${ev.name}`, ev.domain);
101
-
102
- for (const s of docs['api.json']?.surfaces || []) {
103
- const { type, name } = s.mapsTo || {};
104
- if (type === 'command' && !commandNames.has(name)) errors.push(`api.json:${s.method || ''} ${s.path}: maps to unknown command "${name}"`);
105
- if (type === 'query' && !queryNames.has(name)) errors.push(`api.json:${s.method || ''} ${s.path}: maps to unknown query "${name}"`);
106
- if (type === 'event-ingress' && !eventNames.has(name)) errors.push(`api.json:${s.method || ''} ${s.path}: event-ingress maps to unknown event "${name}" (state-changing webhooks should map to a command instead)`);
107
- }
108
- // every domain's listed concepts should exist
109
- for (const d of docs['domains.json'].domains || []) {
110
- for (const c of d.commands || []) if (!commandNames.has(c)) warnings.push(`domains.json:${d.name}: lists command "${c}" not in commands.json`);
111
- for (const q of d.queries || []) if (!queryNames.has(q)) warnings.push(`domains.json:${d.name}: lists query "${q}" not in queries.json`);
112
- for (const e of d.events || []) if (!eventNames.has(e)) warnings.push(`domains.json:${d.name}: lists event "${e}" not in events.json`);
113
- const policyNames = new Set((docs['policies.json']?.policies || []).map((p) => p.name));
114
- for (const p of d.policies || []) if (!policyNames.has(p)) warnings.push(`domains.json:${d.name}: lists policy "${p}" not in policies.json`);
115
- }
116
- // commands with no policies and no preconditions are suspicious for mutating ops
117
- for (const c of docs['commands.json'].commands || []) {
118
- if (!(c.policies || []).length && !(c.preconditions || []).length) {
119
- warnings.push(`commands.json:${c.name}: no policies or preconditions — really unguarded, or missed extraction?`);
120
- }
121
- }
122
- }
123
-
124
- // --- frontend layer cross-checks (only when the optional frontend files exist) ---
125
- if (docs['frontend-components.json']) {
126
- const componentNames = new Set(
127
- (docs['frontend-components.json'].components || []).map((c) => c.name),
128
- );
129
- // routes should reference components that were actually inventoried
130
- for (const r of docs['frontend-routes.json']?.routes || []) {
131
- for (const c of r.usesComponents || []) {
132
- if (!componentNames.has(c)) {
133
- warnings.push(`frontend-routes.json:${r.path}: uses component "${c}" not in frontend-components.json`);
134
- }
135
- }
136
- }
137
- // a component flagged as needing a port must say WHY (the custom logic), or the
138
- // port-risk is unactionable
139
- for (const c of docs['frontend-components.json'].components || []) {
140
- if (c.rebuild === 'custom-logic' && !c.customLogic) {
141
- warnings.push(`frontend-components.json:${c.name}: rebuild=custom-logic but no customLogic description — port risk is unactionable`);
142
- }
143
- }
144
- // data-fetching queries named on routes should resolve to a real query/command
145
- if (docs['queries.json'] || docs['commands.json']) {
146
- const known = new Set([
147
- ...(docs['queries.json']?.queries || []).map((q) => q.name),
148
- ...(docs['commands.json']?.commands || []).map((c) => c.name),
149
- ]);
150
- for (const r of docs['frontend-routes.json']?.routes || []) {
151
- for (const d of r.dataFrom || []) {
152
- if (!known.has(d)) {
153
- warnings.push(`frontend-routes.json:${r.path}: reads "${d}" which is not a known query/command`);
154
- }
155
- }
156
- }
157
- }
158
- }
159
-
160
- // an inconsistent UI with no specific design findings = under-extraction
161
- // (guarded on frontend.json alone — independent of the component inventory)
162
- if (docs['frontend.json']) {
163
- const consistency = docs['frontend.json'].designSystemConsistency;
164
- const findingCount = (docs['frontend.json'].designFindings || []).length;
165
- if ((consistency === 'mixed' || consistency === 'ad-hoc') && findingCount === 0) {
166
- warnings.push(`frontend.json: designSystemConsistency="${consistency}" but designFindings is empty — name the specific broken patterns (interaction/theming/cross-page/…)`);
167
- }
168
- }
169
-
170
- for (const w of warnings) console.log(`WARN ${w}`);
171
- for (const e of errors) console.log(`ERROR ${e}`);
172
- console.log(`\n${errors.length} error(s), ${warnings.length} warning(s) across ${Object.keys(docs).length} files`);
173
- process.exit(errors.length ? 1 : 0);
@@ -1,13 +0,0 @@
1
- ---
2
- name: pikku-tag-middleware
3
- description: 'Deprecated — use pikku-middleware instead. Tag middleware (addTagMiddleware) is now documented as a section within the pikku-middleware skill, alongside global HTTP middleware, execution order, and the service-to-service bearer auth pattern.'
4
- ---
5
-
6
- # Deprecated: use `pikku-middleware`
7
-
8
- Tag middleware is covered in the **`pikku-middleware`** skill, which also covers:
9
- - `addHTTPMiddleware` (global / prefix-based)
10
- - `addTagMiddleware` (tag-scoped)
11
- - Middleware execution order and priority
12
- - Service-to-service bearer auth pattern
13
- - Session-setting middleware pattern
@@ -1,40 +0,0 @@
1
- ---
2
- name: pikku-template-clone
3
- description: 'Standard cleanup to run right after a Pikku template is cloned or scaffolded into a new project. TRIGGER when: a Pikku template was just cloned/scaffolded (via `npm create pikku`, `git clone <template>`, or the user says "I cloned the kanban template / starter / template"), or the working tree still looks like an untouched template (template README, placeholder `@project/*` name in package.json). DO NOT TRIGGER when: working in an established project mid-feature, or editing the template repo itself.'
4
- allowed-tools: Bash(git status *), Bash(git add *), Bash(git commit *), Bash(git rm *), Bash(git mv *), Bash(git log *)
5
- installGroups: [core]
6
- ---
7
-
8
- # Pikku Template Post-Clone Cleanup
9
-
10
- ## Agent Operating Procedure
11
-
12
- Run this **once**, right after a template is cloned or scaffolded into a new
13
- project. The goal is to turn template scaffolding into a real project. Make the
14
- smallest changes and land them as one focused `chore: post-clone cleanup`
15
- commit, separate from any feature work.
16
-
17
- 1. **Replace the template README.** The shipped `README.md` describes the
18
- _template_, not the user's project — leaving it in place is misleading.
19
- Either delete it (`git rm README.md`) or rewrite it with the new project's
20
- name and purpose. Never ship a clone with the generic template README.
21
- 2. **Keep the lockfile committed.** Templates ship a committed `yarn.lock`; do
22
- NOT re-add `yarn.lock` to `.gitignore`. A real project commits its lockfile
23
- for reproducible installs. The correct pattern is `yarn.lock` followed by
24
- `!/yarn.lock`, which commits the root lockfile while keeping generated
25
- per-unit lockfiles under `.deploy/` (and `e2e/`) ignored.
26
- 3. **Rename template identifiers.** Update `name` in the root `package.json`
27
- (and any `@project/*` or other placeholder names) to the real project.
28
- 4. **Drop template-only artifacts.** Remove any `TEMPLATE.md`, demo docs, or
29
- placeholder content that only made sense for the template.
30
-
31
- Do not touch generated files (`.pikku/`, `*.gen.*`) or run a full reinstall as
32
- part of cleanup — this step is project hygiene, not a build.
33
-
34
- ## Why this exists
35
-
36
- Templates are structure-only starting points. Without this pass, clones carry a
37
- misleading README, a placeholder package name, and (historically) a gitignored
38
- lockfile — all of which leak template assumptions into a real project. Running
39
- it immediately after clone keeps every Pikku project, OSS or Fabric, starting
40
- from a clean, honest baseline.
@@ -1,181 +0,0 @@
1
- ---
2
- name: pikku-trigger
3
- description: >-
4
- Use when adding event-driven functions that respond to system events like Redis pub/sub,
5
- PostgreSQL LISTEN/NOTIFY, or custom event sources. Covers wireTrigger, wireTriggerSource, and
6
- pikkuTriggerFunc. TRIGGER when: code uses wireTrigger/wireTriggerSource/pikkuTriggerFunc, user
7
- asks about event-driven functions, Redis pub/sub, PostgreSQL LISTEN/NOTIFY, or reacting to
8
- external events. DO NOT TRIGGER when: user asks about scheduled tasks (use pikku-cron) or
9
- background job queues (use pikku-queue).
10
- installGroups: [core]
11
- ---
12
-
13
- # Pikku Trigger Wiring
14
-
15
- ## Agent Operating Procedure
16
-
17
- Use this skill as an execution checklist, not reference material.
18
-
19
- 1. Discover before editing. Prefer OpenCode tools such as `pikku-meta` when available; otherwise run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
20
- 2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
21
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
22
- 4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
23
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
24
-
25
- Wire Pikku functions to fire when external events occur. Triggers connect event sources (Redis pub/sub, PostgreSQL LISTEN/NOTIFY, polling, webhooks) to Pikku functions.
26
-
27
- ## Before You Start
28
-
29
- ```bash
30
- pikku info functions --verbose # See existing functions and their types
31
- pikku info tags --verbose # Understand project organization
32
- ```
33
-
34
- See `pikku-concepts` for the core mental model.
35
-
36
- ## API Reference
37
-
38
- ### `wireTrigger(config)`
39
-
40
- Define the target function that handles trigger events:
41
-
42
- ```typescript
43
- import { wireTrigger } from '@pikku/core/trigger'
44
-
45
- wireTrigger({
46
- name: string, // Trigger name (matches source)
47
- func: PikkuFunc, // Function to call when event fires
48
- })
49
- ```
50
-
51
- ### `wireTriggerSource(config)`
52
-
53
- Define the event source that fires triggers:
54
-
55
- ```typescript
56
- import { wireTriggerSource } from '@pikku/core/trigger'
57
-
58
- wireTriggerSource({
59
- name: string, // Must match wireTrigger name
60
- func: PikkuTriggerFunc, // Source function (sets up listener)
61
- input: object, // Configuration for the source
62
- })
63
- ```
64
-
65
- ### `pikkuTriggerFunc<TInput, TEvent>`
66
-
67
- Define a trigger source function. Returns a cleanup function.
68
-
69
- ```typescript
70
- import { pikkuTriggerFunc } from '#pikku'
71
-
72
- const source = pikkuTriggerFunc<
73
- InputType, // Configuration input
74
- EventType // Shape of events it emits
75
- >(async (services, input, { trigger }) => {
76
- // Set up listener...
77
- trigger.invoke(eventData) // Fire the trigger
78
-
79
- // Return cleanup function
80
- return async () => {
81
- /* teardown */
82
- }
83
- })
84
- ```
85
-
86
- ## Usage Patterns
87
-
88
- ### Redis Pub/Sub Source
89
-
90
- ```typescript
91
- const redisSubscribe = pikkuTriggerFunc<
92
- { channels: string[] },
93
- { channel: string; message: any }
94
- >(async ({ redis }, { channels }, { trigger }) => {
95
- const subscriber = redis.duplicate()
96
-
97
- subscriber.on('message', (channel, message) => {
98
- trigger.invoke({ channel, message: JSON.parse(message) })
99
- })
100
-
101
- await subscriber.subscribe(...channels)
102
-
103
- return async () => {
104
- await subscriber.unsubscribe()
105
- await subscriber.quit()
106
- }
107
- })
108
-
109
- // Target function
110
- const onOrderEvent = pikkuSessionlessFunc({
111
- title: 'On Order Event',
112
- func: async ({ db, logger }, { channel, message }) => {
113
- logger.info(`Order event on ${channel}`, message)
114
- await db.processOrderEvent(message)
115
- },
116
- })
117
-
118
- // Wire them together
119
- wireTrigger({
120
- name: 'order-events',
121
- func: onOrderEvent,
122
- })
123
-
124
- wireTriggerSource({
125
- name: 'order-events',
126
- func: redisSubscribe,
127
- input: { channels: ['orders:created', 'orders:updated'] },
128
- })
129
- ```
130
-
131
- ### Triggers vs Queues
132
-
133
- | Feature | Trigger | Queue |
134
- | ----------- | ---------------------------------- | ------------------------------ |
135
- | Execution | Synchronous, in-process | Async, distributed |
136
- | Reliability | At-most-once | At-least-once (with retries) |
137
- | Use case | React to events immediately | Reliable background processing |
138
- | Source | External systems (Redis, PG, etc.) | Enqueued programmatically |
139
-
140
- Use triggers for real-time reactions. Use queues for reliable, retryable background work.
141
-
142
- ## Complete Example
143
-
144
- ```typescript
145
- // functions/triggers.functions.ts
146
- const pgListen = pikkuTriggerFunc<{ channel: string }, { payload: any }>(
147
- async ({ db }, { channel }, { trigger }) => {
148
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(channel)) {
149
- throw new Error(`Invalid channel name: ${channel}`)
150
- }
151
- const client = await db.pool.connect()
152
-
153
- client.on('notification', (msg) => {
154
- trigger.invoke({ payload: JSON.parse(msg.payload) })
155
- })
156
-
157
- await client.query(`LISTEN ${channel}`)
158
-
159
- return async () => {
160
- await client.query(`UNLISTEN ${channel}`)
161
- client.release()
162
- }
163
- }
164
- )
165
-
166
- const onUserCreated = pikkuSessionlessFunc({
167
- title: 'On User Created',
168
- func: async ({ emailService, logger }, { payload }) => {
169
- logger.info('New user created', { userId: payload.id })
170
- await emailService.sendWelcome(payload.email)
171
- },
172
- })
173
-
174
- // wirings/triggers.wiring.ts
175
- wireTrigger({ name: 'user-created', func: onUserCreated })
176
- wireTriggerSource({
177
- name: 'user-created',
178
- func: pgListen,
179
- input: { channel: 'user_created' },
180
- })
181
- ```
@@ -1,173 +0,0 @@
1
- ---
2
- name: pikku-versioning
3
- description: >-
4
- Use when versioning Pikku function contracts, detecting breaking changes, or managing API
5
- backward compatibility. Covers the version property, versions.pikku.json manifest, contract
6
- hashing, and CI integration. TRIGGER when: code uses version: on a pikkuFunc, user asks about
7
- API versioning, breaking changes, contract hashes, backward compatibility, or "pikku versions"
8
- CLI commands. DO NOT TRIGGER when: user asks about secrets/variables/OAuth2 (use pikku-config)
9
- or general function definitions (use pikku-concepts).
10
- installGroups: [core]
11
- ---
12
-
13
- # Pikku Function Versioning
14
-
15
- ## Agent Operating Procedure
16
-
17
- Use this skill as an execution checklist, not reference material.
18
-
19
- 1. Discover before editing. Prefer OpenCode tools such as `pikku-meta` when available; otherwise run the relevant `pikku meta ... --json` command and inspect only the focused output you need.
20
- 2. Identify the source files that own the behavior. Do not start by reading generated output, `.pikku`, `node_modules`, vendored packages, or broad build artifacts.
21
- 3. Make the smallest source change that satisfies the task. Keep generated files generated, and avoid hand-editing SDKs, schema output, or typegen.
22
- 4. Validate with the narrowest relevant command first, then run `pikku-verify` or `pikku all` when functions, wirings, schemas, or generated clients may have changed.
23
- 5. If validation fails, fix the source cause and rerun validation. Do not paper over generated errors by editing generated files.
24
-
25
- Track and protect function contracts across releases. Pikku hashes each function's input/output schema into a manifest so you can detect breaking changes before they ship.
26
-
27
- ## Before You Start
28
-
29
- ```bash
30
- pikku info functions --verbose # See existing functions and their versions
31
- ```
32
-
33
- See `pikku-concepts` for the core mental model.
34
-
35
- ## Function Versioning
36
-
37
- When you need to introduce a breaking change, keep the old function as a pinned version and let the new one become the latest.
38
-
39
- **The pattern:**
40
-
41
- 1. Create a new file `my-function-v1.function.ts` — export a variable with the `V1` suffix
42
- 2. Set `override: 'myFunction'` — this is the contract key the manifest groups under
43
- 3. Set `version: 1` — pins this as version 1 of the contract
44
- 4. The existing `my-function.function.ts` (no `version:` field) automatically becomes the latest version
45
-
46
- ```typescript
47
- // my-function-v1.function.ts — old contract, kept for running workflows/agents
48
- export const getBookV1 = pikkuFunc({
49
- override: 'getBook', // REQUIRED — links this to the 'getBook' contract family
50
- version: 1,
51
- input: z.object({ bookId: z.string() }),
52
- output: z.object({ title: z.string() }),
53
- func: async ({ db }, { bookId }) => {
54
- return db.getBook(bookId)
55
- },
56
- })
57
-
58
- // my-function.function.ts — latest contract, no version: field
59
- export const getBook = pikkuFunc({
60
- input: z.object({
61
- bookId: z.string(),
62
- format: z.enum(['full', 'summary']),
63
- }),
64
- output: z.object({
65
- title: z.string(),
66
- author: z.string(),
67
- isbn: z.string(),
68
- }),
69
- func: async ({ db }, { bookId, format }) => {
70
- return db.getBook(bookId, format)
71
- },
72
- })
73
- ```
74
-
75
- **Why `override` is required:** The manifest groups functions by a shared contract key. Without `override: 'getBook'`, `getBookV1` is stored internally as `getBookV1@v1` (key: `getBookV1`), which is a different contract family from `getBook`. With `override: 'getBook'`, it becomes `getBook@v1` (key: `getBook`), which groups with the unversioned `getBook` — and the unversioned one is automatically promoted to `getBook@v2`.
76
-
77
- ## Version Manifest (`versions.pikku.json`)
78
-
79
- Pikku tracks contract hashes to detect breaking changes:
80
-
81
- ```json
82
- {
83
- "manifestVersion": 1,
84
- "contracts": {
85
- "createTodo": {
86
- "latest": 1,
87
- "versions": {
88
- "1": { "inputHash": "a1b2c3d4", "outputHash": "e5f6a7b8" }
89
- }
90
- },
91
- "getTodos": {
92
- "latest": 2,
93
- "versions": {
94
- "1": { "inputHash": "i9j0k1l2", "outputHash": "m3n4o5p6" },
95
- "2": { "inputHash": "q7r8s9t0", "outputHash": "u1v2w3x4" }
96
- }
97
- }
98
- }
99
- }
100
- ```
101
-
102
- Each hash is derived from the function's input and output schemas plus the contract key. If a schema changes without a version bump, `pikku versions check` will fail.
103
-
104
- ## CLI Commands
105
-
106
- ```bash
107
- npx pikku versions init # Initialize versioning manifest (run once)
108
- npx pikku versions check # Detect contract changes (use in CI)
109
- npx pikku versions update # Update contract hashes after version bump
110
- ```
111
-
112
- **Workflow:**
113
-
114
- 1. `pikku versions init` — run once to create `versions.pikku.json`
115
- 2. Develop normally — add/modify functions
116
- 3. `pikku versions check` — CI catches unversioned breaking changes
117
- 4. If intentional: create `my-function-v1.function.ts` with `override` + `version: 1`, then `pikku versions update`
118
-
119
- ## CI Integration
120
-
121
- ```yaml
122
- # .github/workflows/ci.yml
123
- name: CI
124
- on: [push, pull_request]
125
-
126
- jobs:
127
- check:
128
- runs-on: ubuntu-latest
129
- steps:
130
- - uses: actions/checkout@v4
131
- - run: npm ci
132
- - run: npx pikku versions check
133
- ```
134
-
135
- ## Complete Example
136
-
137
- ```typescript
138
- // create-todo-v1.function.ts — v1 locked contract
139
- export const createTodoV1 = pikkuSessionlessFunc({
140
- override: 'createTodo', // groups under 'createTodo' contract family
141
- version: 1,
142
- input: z.object({ title: z.string() }),
143
- output: z.object({ id: z.string(), title: z.string() }),
144
- func: async ({ todoStore }, { title }) => todoStore.add(title),
145
- })
146
-
147
- // create-todo.function.ts — v2 (latest), called by default
148
- export const createTodo = pikkuSessionlessFunc({
149
- input: z.object({
150
- title: z.string(),
151
- priority: z.enum(['low', 'medium', 'high']),
152
- }),
153
- output: z.object({
154
- id: z.string(),
155
- title: z.string(),
156
- priority: z.string(),
157
- }),
158
- func: async ({ todoStore }, { title, priority }) =>
159
- todoStore.add(title, priority),
160
- })
161
- ```
162
-
163
- Result in manifest:
164
-
165
- ```json
166
- "createTodo": {
167
- "latest": 2,
168
- "versions": {
169
- "1": { "inputHash": "...", "outputHash": "..." },
170
- "2": { "inputHash": "...", "outputHash": "..." }
171
- }
172
- }
173
- ```