@pikku/cli 0.12.100 → 0.12.102

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 (246) hide show
  1. package/console-app/assets/{abnfDiagram-VRR7QNED-Icjp9hnB.js → abnfDiagram-VRR7QNED-BneJZPuq.js} +1 -1
  2. package/console-app/assets/{arc-BfGls--L.js → arc-B6oXAx95.js} +1 -1
  3. package/console-app/assets/architecture-TIHT7OUA-BIpQbDwm.js +1 -0
  4. package/console-app/assets/{architectureDiagram-ZJ3FMSHR-CpnsLBns.js → architectureDiagram-ZJ3FMSHR-Dn6JKcGL.js} +1 -1
  5. package/console-app/assets/{blockDiagram-677ZJIJ3-ByKRMGSr.js → blockDiagram-677ZJIJ3-CQtakjN5.js} +1 -1
  6. package/console-app/assets/{c4Diagram-LMCZKHZV-CzJSbsUo.js → c4Diagram-LMCZKHZV-DRBlz-sO.js} +1 -1
  7. package/console-app/assets/channel-D9eZ2wja.js +1 -0
  8. package/console-app/assets/{chunk-32BRIVSS-D_tc4HCY.js → chunk-32BRIVSS-DIionvWe.js} +1 -1
  9. package/console-app/assets/{chunk-52WLFC77-wkQkuUXG.js → chunk-52WLFC77-DwrMMV53.js} +1 -1
  10. package/console-app/assets/{chunk-C7G6YPKG-BFW3xG3R.js → chunk-C7G6YPKG-C7_SHoee.js} +1 -1
  11. package/console-app/assets/{chunk-EX3LRPZG-Car090Sw.js → chunk-EX3LRPZG-Brqi3de0.js} +1 -1
  12. package/console-app/assets/{chunk-FWX5IMBZ-c2ud9kQ-.js → chunk-FWX5IMBZ-CGXYdLJv.js} +2 -2
  13. package/console-app/assets/{chunk-HOUHSVGY-RYmQka5R.js → chunk-HOUHSVGY-C-mHnXIa.js} +1 -1
  14. package/console-app/assets/{chunk-ICXQ74PX-C-dqYC_9.js → chunk-ICXQ74PX-x3psPzuY.js} +1 -1
  15. package/console-app/assets/{chunk-MOJQB5TN-Cu3FUWwl.js → chunk-MOJQB5TN-2uXUItkq.js} +1 -1
  16. package/console-app/assets/{chunk-OGEWGWER-Z7f1yArD.js → chunk-OGEWGWER-eXRvfFkE.js} +1 -1
  17. package/console-app/assets/{chunk-PUDLZKDR-Bp68DuRj.js → chunk-PUDLZKDR-CyN33Wwe.js} +1 -1
  18. package/console-app/assets/{chunk-Q4XR5HBZ-CdKSYWZ4.js → chunk-Q4XR5HBZ-uyzxeIyh.js} +1 -1
  19. package/console-app/assets/{chunk-RYQCIY6F-9ISCAKm2.js → chunk-RYQCIY6F-CB9wv1YB.js} +1 -1
  20. package/console-app/assets/{chunk-V7JOEXUC-2EOc1nQB.js → chunk-V7JOEXUC-DLMzjIkb.js} +1 -1
  21. package/console-app/assets/chunk-VAUOI2AC--YVrsYCZ.js +1 -0
  22. package/console-app/assets/{chunk-VR4S4FIN-BPvN0PUm.js → chunk-VR4S4FIN-DtBvdHYI.js} +1 -1
  23. package/console-app/assets/{chunk-WYO6CB5R-DOpurCTP.js → chunk-WYO6CB5R-B_EVxwHf.js} +1 -1
  24. package/console-app/assets/chunk-XXDRQBXY-By-5aH9h.js +1 -0
  25. package/console-app/assets/{chunk-ZGVPDNZ5-Bu2weVOL.js → chunk-ZGVPDNZ5-CGfMGxHR.js} +1 -1
  26. package/console-app/assets/classDiagram-OUVF2IWQ-Bg6U871E.js +1 -0
  27. package/console-app/assets/classDiagram-v2-EOCWNBFH-Bg6U871E.js +1 -0
  28. package/console-app/assets/{cose-bilkent-JH36ORCC-nau-QGCX.js → cose-bilkent-JH36ORCC-B_3kCkWC.js} +1 -1
  29. package/console-app/assets/{cynefin-VYW2F7L2-BBw-E7lg.js → cynefin-VYW2F7L2-Buqf60VA.js} +1 -1
  30. package/console-app/assets/{cynefinDiagram-TSTJHNR4-BvnMvOvH.js → cynefinDiagram-TSTJHNR4-DOjw2x6X.js} +1 -1
  31. package/console-app/assets/{dagre-VKFMJZFB-CHEqPR29.js → dagre-VKFMJZFB-CTZTYT0b.js} +1 -1
  32. package/console-app/assets/{diagram-FQU43EPY-JijKRWyb.js → diagram-FQU43EPY-BZh4LC_G.js} +1 -1
  33. package/console-app/assets/{diagram-G47NLZAW-G5UsB31f.js → diagram-G47NLZAW-DDHD69gC.js} +1 -1
  34. package/console-app/assets/{diagram-NH7WQ7WH-BhuJ6fPu.js → diagram-NH7WQ7WH-YnfI_N0A.js} +1 -1
  35. package/console-app/assets/{diagram-OA4YK3LP-DaUF-8N4.js → diagram-OA4YK3LP-ByfLxp7x.js} +1 -1
  36. package/console-app/assets/{diagram-WEI45ONY-wLsJIhoM.js → diagram-WEI45ONY-CghNIRYp.js} +1 -1
  37. package/console-app/assets/{dist-fbBkbDNG.js → dist-DCLZxlZv.js} +1 -1
  38. package/console-app/assets/{ebnfDiagram-CCIWWBDH-BZCEa3Dz.js → ebnfDiagram-CCIWWBDH-D5_G_ZqU.js} +1 -1
  39. package/console-app/assets/{erDiagram-Q63AITRT-4XoEUtEh.js → erDiagram-Q63AITRT-B5ZtoDJs.js} +1 -1
  40. package/console-app/assets/eventmodeling-45OFAUF4-eU2Q2DYK.js +1 -0
  41. package/console-app/assets/flowDiagram-23GEKE2U-CtRJ8nYJ.js +1 -0
  42. package/console-app/assets/{ganttDiagram-NO4QXBWP-BXU7pfq6.js → ganttDiagram-NO4QXBWP-R-TggPMB.js} +1 -1
  43. package/console-app/assets/{gitGraph-TEB2WS4Q-BdMAmvpU.js → gitGraph-TEB2WS4Q-Cjhl-pwb.js} +1 -1
  44. package/console-app/assets/{gitGraphDiagram-IHSO6WYX-3Hn0zwZR.js → gitGraphDiagram-IHSO6WYX-DnQe5wWr.js} +1 -1
  45. package/console-app/assets/{index-Din2vBeb.js → index-BZTTf18Y.js} +150 -150
  46. package/console-app/assets/{index-BXMjqLQ5.css → index-r763CZB4.css} +1 -1
  47. package/console-app/assets/{info-DKCQHKI2-Dc_oQkp5.js → info-DKCQHKI2-DlSWT4UP.js} +1 -1
  48. package/console-app/assets/{infoDiagram-FWYZ7A6U-B3v4xCUn.js → infoDiagram-FWYZ7A6U-xqpfzGnj.js} +1 -1
  49. package/console-app/assets/{ishikawaDiagram-FXEZZL3T-DzWq9Vtp.js → ishikawaDiagram-FXEZZL3T-M_pUeGWs.js} +1 -1
  50. package/console-app/assets/{journeyDiagram-5HDEW3XC-C0buWi_a.js → journeyDiagram-5HDEW3XC-Dm6FR2DD.js} +1 -1
  51. package/console-app/assets/{kanban-definition-HUTT4EX6-DRAN9HhV.js → kanban-definition-HUTT4EX6-CbhkDdQH.js} +1 -1
  52. package/console-app/assets/{line-UqgRNiAC.js → line-C98Z2IVv.js} +1 -1
  53. package/console-app/assets/{linear-FTAGuk_3.js → linear-fYIkDCbB.js} +1 -1
  54. package/console-app/assets/{mermaid-parser.core-DIHI27FL.js → mermaid-parser.core-Dkioi2z2.js} +3 -3
  55. package/console-app/assets/{mermaid.core-D4q8kvcu.js → mermaid.core-CeJcbw8_.js} +3 -3
  56. package/console-app/assets/{mindmap-definition-LN4V7U3C-DN-_CNLb.js → mindmap-definition-LN4V7U3C-DDitGESW.js} +1 -1
  57. package/console-app/assets/{packet-7NZHBO7P-8a3PoQAb.js → packet-7NZHBO7P-DWZKJ9Us.js} +1 -1
  58. package/console-app/assets/{pegDiagram-2B236MQR-Bh2MhzCN.js → pegDiagram-2B236MQR-CZGjPLXH.js} +1 -1
  59. package/console-app/assets/{pie-RZYD4A2V-Bi6zbJDl.js → pie-RZYD4A2V-DT4-E-uE.js} +1 -1
  60. package/console-app/assets/{pieDiagram-ENE6RG2P-D2Z0W5UU.js → pieDiagram-ENE6RG2P-bgr82Jyt.js} +1 -1
  61. package/console-app/assets/{quadrantDiagram-ABIIQ3AL-DNOKka0p.js → quadrantDiagram-ABIIQ3AL-Dcw3uNG0.js} +1 -1
  62. package/console-app/assets/{radar-I7S5WNFK-CAxa68Sg.js → radar-I7S5WNFK-CFAbRVFk.js} +1 -1
  63. package/console-app/assets/{railroad-3IZDKUUU-CZb1usP7.js → railroad-3IZDKUUU-CjkCoEjH.js} +1 -1
  64. package/console-app/assets/railroad-abnf-AHOZXSZD-BM21cj3I.js +1 -0
  65. package/console-app/assets/railroad-ebnf-EBAXGLYW-CDjo5TUI.js +1 -0
  66. package/console-app/assets/railroad-peg-LSFZ7HO6-CiKQmfgW.js +1 -0
  67. package/console-app/assets/{railroadDiagram-RFXS5EU6-B1K2hbTD.js → railroadDiagram-RFXS5EU6-BUoL0dsw.js} +1 -1
  68. package/console-app/assets/{requirementDiagram-TGXJPOKE-K8wcOO0Q.js → requirementDiagram-TGXJPOKE-C27qMKlr.js} +1 -1
  69. package/console-app/assets/{sankeyDiagram-HTMAVEWB-WBPBkgNx.js → sankeyDiagram-HTMAVEWB-BYEhrrY0.js} +1 -1
  70. package/console-app/assets/{sequenceDiagram-DBY2YBRQ-Dz80qZZv.js → sequenceDiagram-DBY2YBRQ-BCuL4Pl6.js} +1 -1
  71. package/console-app/assets/{src-CnJCXDL0.js → src-jNbD6Nj3.js} +1 -1
  72. package/console-app/assets/{stateDiagram-2N3HPSRC-DMur7lZf.js → stateDiagram-2N3HPSRC-C8Ywa3Ah.js} +1 -1
  73. package/console-app/assets/stateDiagram-v2-6OUMAXLB-BIGwMDyj.js +1 -0
  74. package/console-app/assets/{swimlanes-5IMT3BWC-BlLdYcPU.js → swimlanes-5IMT3BWC-C12iYfdp.js} +1 -1
  75. package/console-app/assets/swimlanesDiagram-G3AALYLV-BsoUfqN-.js +8 -0
  76. package/console-app/assets/{timeline-definition-FHXFAJF6-CBOit0JY.js → timeline-definition-FHXFAJF6-BFQotHp7.js} +1 -1
  77. package/console-app/assets/{treeView-QDETBFTQ-DM1i1ast.js → treeView-QDETBFTQ-CGbwDLTd.js} +1 -1
  78. package/console-app/assets/{treemap-6X3UGDF4-ACrBEfY6.js → treemap-6X3UGDF4-DxSGskiF.js} +1 -1
  79. package/console-app/assets/{vennDiagram-L72KCM5P-BmeFWb5N.js → vennDiagram-L72KCM5P-D4hlUYc9.js} +1 -1
  80. package/console-app/assets/{wardley-OPB4EBWU-BpIbeh58.js → wardley-OPB4EBWU-XlXl32Vn.js} +1 -1
  81. package/console-app/assets/{wardleyDiagram-EHGQE667-CxL7Mv7y.js → wardleyDiagram-EHGQE667-ttCeFzYx.js} +1 -1
  82. package/console-app/assets/{xychartDiagram-FW5EYKEG-DkV75xT2.js → xychartDiagram-FW5EYKEG-BMG4kbfv.js} +1 -1
  83. package/console-app/index.html +2 -2
  84. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +6 -2
  85. package/dist/.pikku/agent/pikku-agent-types.gen.js +3 -0
  86. package/dist/.pikku/agent/pikku-model-aliases.gen.d.ts +1 -0
  87. package/dist/.pikku/agent/pikku-model-aliases.gen.js +5 -0
  88. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  89. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  90. package/dist/.pikku/cli/pikku-cli-channel.js +13 -8
  91. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  92. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  93. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  94. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  95. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  96. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  97. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  98. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +18 -12
  99. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  100. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  101. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  102. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  103. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  104. package/dist/.pikku/function/pikku-function-types.gen.d.ts +6 -25
  105. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  106. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  107. package/dist/.pikku/function/pikku-functions-meta.gen.json +51 -27
  108. package/dist/.pikku/function/pikku-functions.gen.js +3 -1
  109. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  110. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  111. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  112. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  113. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  114. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  115. package/dist/.pikku/pikku-bootstrap.gen.d.ts +2 -1
  116. package/dist/.pikku/pikku-bootstrap.gen.js +2 -1
  117. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  118. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  119. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  120. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  121. package/dist/.pikku/pikku-types.gen.js +1 -1
  122. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  123. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  124. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  125. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  126. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  127. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  128. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +2 -1
  129. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  130. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  131. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  132. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  133. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  134. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  135. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  136. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  137. package/dist/.pikku/schemas/register.gen.js +7 -5
  138. package/dist/.pikku/schemas/schemas/DevInput.schema.json +1 -1
  139. package/dist/.pikku/schemas/schemas/PikkuAIScorerOutput.schema.json +1 -0
  140. package/dist/.pikku/schemas/schemas/ServeInput.schema.json +1 -1
  141. package/dist/.pikku/schemas/schemas/ValidateOutput.schema.json +1 -0
  142. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  143. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  144. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  145. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  146. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  147. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  148. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  149. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  150. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  151. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  152. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  153. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  154. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  155. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  156. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  157. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +8 -2
  158. package/dist/.pikku/workflow/pikku-personas.gen.js +1 -1
  159. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +9 -17
  160. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  161. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  162. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  163. package/dist/bin/pikku-bin.mjs +2 -2
  164. package/dist/src/cli.wiring.js +20 -11
  165. package/dist/src/deploy/analyzer/analyzer.js +21 -0
  166. package/dist/src/deploy/analyzer/manifest.d.ts +18 -0
  167. package/dist/src/fabric/functions/smoke.function.js +7 -4
  168. package/dist/src/fabric/functions/validate.function.js +1 -1
  169. package/dist/src/functions/commands/db-export.js +1 -1
  170. package/dist/src/functions/commands/db-shared.d.ts +0 -14
  171. package/dist/src/functions/commands/dev.d.ts +3 -0
  172. package/dist/src/functions/commands/dev.js +41 -2
  173. package/dist/src/functions/commands/knowledge-index.d.ts +3 -3
  174. package/dist/src/functions/commands/new-addon.js +6 -3
  175. package/dist/src/functions/commands/scenario-browser.d.ts +24 -1
  176. package/dist/src/functions/commands/scenario-browser.js +5 -1
  177. package/dist/src/functions/commands/scenario.d.ts +6 -0
  178. package/dist/src/functions/commands/scenario.js +26 -5
  179. package/dist/src/functions/commands/serve.d.ts +3 -0
  180. package/dist/src/functions/commands/serve.js +15 -1
  181. package/dist/src/functions/commands/{workspace-validate.d.ts → validate.d.ts} +18 -3
  182. package/dist/src/functions/commands/validate.js +9 -0
  183. package/dist/src/functions/db/local-db.d.ts +2 -1
  184. package/dist/src/functions/db/local-db.js +2 -2
  185. package/dist/src/functions/validate/addon-package-checks.d.ts +26 -0
  186. package/dist/src/functions/validate/addon-package-checks.js +258 -0
  187. package/dist/src/functions/validate/persona-checks.d.ts +1 -1
  188. package/dist/src/functions/validate/persona-checks.js +1 -1
  189. package/dist/src/functions/validate/scenario-file-checks.js +33 -0
  190. package/dist/src/functions/validate/shared-checks.d.ts +1 -1
  191. package/dist/src/functions/validate/shared-checks.js +1 -1
  192. package/dist/src/functions/validate/validate-registry.d.ts +52 -0
  193. package/dist/src/functions/validate/validate-registry.js +100 -0
  194. package/dist/src/functions/validate/validate.d.ts +47 -0
  195. package/dist/src/functions/validate/{workspace-validate.js → validate.js} +46 -12
  196. package/dist/src/functions/wirings/ai-agent/pikku-command-ai-agent-types.js +3 -2
  197. package/dist/src/functions/wirings/ai-agent/pikku-command-ai-agent.js +5 -1
  198. package/dist/src/functions/wirings/ai-agent/serialize-ai-agent-types.d.ts +1 -1
  199. package/dist/src/functions/wirings/ai-agent/serialize-ai-agent-types.js +15 -2
  200. package/dist/src/functions/wirings/ai-agent/serialize-model-aliases.d.ts +5 -0
  201. package/dist/src/functions/wirings/ai-agent/serialize-model-aliases.js +12 -0
  202. package/dist/src/functions/wirings/ai-scorer/pikku-command-ai-scorer.d.ts +1 -0
  203. package/dist/src/functions/wirings/ai-scorer/pikku-command-ai-scorer.js +51 -0
  204. package/dist/src/functions/wirings/ai-scorer/serialize-scorer-names.d.ts +10 -0
  205. package/dist/src/functions/wirings/ai-scorer/serialize-scorer-names.js +17 -0
  206. package/dist/src/functions/wirings/auth/serialize-auth-gen.js +27 -10
  207. package/dist/src/functions/wirings/cli/serialize-channel-cli.js +9 -4
  208. package/dist/src/functions/wirings/console/serialize-console-functions.js +9 -56
  209. package/dist/src/functions/wirings/functions/serialize-function-types.js +7 -30
  210. package/dist/src/functions/wirings/middleware/serialize-middleware-imports.js +5 -1
  211. package/dist/src/functions/wirings/package/pikku-command-package.js +12 -1
  212. package/dist/src/functions/wirings/package/serialize-package.d.ts +1 -1
  213. package/dist/src/functions/wirings/package/serialize-package.js +5 -2
  214. package/dist/src/functions/wirings/rpc/serialize-typed-rpc-map.js +1 -6
  215. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +30 -0
  216. package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +12 -4
  217. package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +8 -16
  218. package/dist/src/functions/workflows/all.workflow.js +6 -1
  219. package/dist/src/server/bun-server-runner.js +3 -0
  220. package/dist/src/server/dev-server-runner.interface.d.ts +10 -0
  221. package/dist/src/server/node-server-runner.js +3 -0
  222. package/dist/src/services.js +1 -0
  223. package/dist/src/utils/meta-diff.d.ts +1 -1
  224. package/dist/src/utils/meta-diff.js +2 -0
  225. package/dist/src/utils/model-alias-override.d.ts +7 -0
  226. package/dist/src/utils/model-alias-override.js +33 -0
  227. package/dist/src/utils/pikku-cli-config.js +16 -0
  228. package/dist/tsconfig.tsbuildinfo +1 -1
  229. package/package.json +17 -17
  230. package/console-app/assets/architecture-TIHT7OUA-C8l7I5Lm.js +0 -1
  231. package/console-app/assets/channel-BgJ-Z0cx.js +0 -1
  232. package/console-app/assets/chunk-VAUOI2AC-DwKsbf70.js +0 -1
  233. package/console-app/assets/chunk-XXDRQBXY-RuYSFsQb.js +0 -1
  234. package/console-app/assets/classDiagram-OUVF2IWQ-ohoLCI8g.js +0 -1
  235. package/console-app/assets/classDiagram-v2-EOCWNBFH-ohoLCI8g.js +0 -1
  236. package/console-app/assets/eventmodeling-45OFAUF4-DZ7z4YJO.js +0 -1
  237. package/console-app/assets/flowDiagram-23GEKE2U-rd7-NwZT.js +0 -1
  238. package/console-app/assets/railroad-abnf-AHOZXSZD-DkQZciri.js +0 -1
  239. package/console-app/assets/railroad-ebnf-EBAXGLYW-CPBeASA9.js +0 -1
  240. package/console-app/assets/railroad-peg-LSFZ7HO6-CXEVIZ0g.js +0 -1
  241. package/console-app/assets/stateDiagram-v2-6OUMAXLB-BDgLCrot.js +0 -1
  242. package/console-app/assets/swimlanesDiagram-G3AALYLV-CZ5rQIcE.js +0 -8
  243. package/dist/.pikku/schemas/schemas/WorkspaceValidateOutput.schema.json +0 -1
  244. package/dist/src/functions/commands/workspace-validate.js +0 -9
  245. package/dist/src/functions/validate/workspace-validate.d.ts +0 -33
  246. /package/dist/.pikku/schemas/schemas/{WorkspaceValidateInput.schema.json → ValidateInput.schema.json} +0 -0
@@ -13,6 +13,8 @@ export declare const scenarioRun: import("#pikku").PikkuFunctionConfig<{
13
13
  spawn?: boolean;
14
14
  keepAlive?: boolean;
15
15
  trace?: boolean;
16
+ screenshots?: boolean;
17
+ video?: boolean;
16
18
  apiUrl?: string;
17
19
  appUrl?: string;
18
20
  }, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<{
@@ -28,6 +30,8 @@ export declare const scenarioRun: import("#pikku").PikkuFunctionConfig<{
28
30
  spawn?: boolean;
29
31
  keepAlive?: boolean;
30
32
  trace?: boolean;
33
+ screenshots?: boolean;
34
+ video?: boolean;
31
35
  apiUrl?: string;
32
36
  appUrl?: string;
33
37
  }, void, "rpc" | "session", import("#pikku").WiredServices, unknown> | import("#pikku").PikkuFunction<{
@@ -43,6 +47,8 @@ export declare const scenarioRun: import("#pikku").PikkuFunctionConfig<{
43
47
  spawn?: boolean;
44
48
  keepAlive?: boolean;
45
49
  trace?: boolean;
50
+ screenshots?: boolean;
51
+ video?: boolean;
46
52
  apiUrl?: string;
47
53
  appUrl?: string;
48
54
  }, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from 'node:crypto';
1
2
  import { resolve, join } from 'node:path';
2
3
  import { mkdirSync, writeFileSync } from 'node:fs';
3
4
  import { pikkuSessionlessFunc } from '#pikku';
@@ -87,7 +88,7 @@ export const scenarioList = pikkuSessionlessFunc({
87
88
  },
88
89
  });
89
90
  export const scenarioRun = pikkuSessionlessFunc({
90
- func: async ({ logger, config, getInspectorState, variables }, { environment, flows, features, tags, excludeTags, coverage, browser = true, run: runSurface = 'default', strict = false, spawn = false, keepAlive = false, trace = false, apiUrl, appUrl, }) => {
91
+ func: async ({ logger, config, getInspectorState, variables }, { environment, flows, features, tags, excludeTags, coverage, browser = true, run: runSurface = 'default', strict = false, spawn = false, keepAlive = false, trace = false, screenshots = false, video = false, apiUrl, appUrl, }) => {
91
92
  if (!SCENARIO_SURFACES.includes(runSurface)) {
92
93
  throw new Error(`Unknown --run surface '${runSurface}'. Expected one of: ${SCENARIO_SURFACES.join(', ')}.`);
93
94
  }
@@ -285,6 +286,17 @@ export const scenarioRun = pikkuSessionlessFunc({
285
286
  const needsBrowser = effectiveSurface === 'browser' &&
286
287
  groups.some((group) => group.entries.some((entry) => browserStepsByFlow.has(entry.scenarioName)));
287
288
  const failureDir = join(resolve(config.rootDir, config.outDir), 'scenario-failures');
289
+ // Artifacts are filed under the run, not the scenario, so one run's output
290
+ // is one folder to open, keep or delete. `--video` implies capture is on:
291
+ // recording without somewhere to put it is not a thing anyone wants.
292
+ const captureDir = join(resolve(config.rootDir, config.outDir), 'scenario-runs');
293
+ // One id for the whole invocation, distinct from a scenario's own runId:
294
+ // reviewing artifacts means opening what `pikku scenario run` just produced,
295
+ // not hunting for one scenario's folder among many.
296
+ const captureRunId = randomUUID();
297
+ const capture = screenshots || video
298
+ ? { dir: captureDir, runId: captureRunId, video, compress: true }
299
+ : undefined;
288
300
  const browserLifecycle = scenarioBrowserLifecycle(needsBrowser
289
301
  ? await (async () => {
290
302
  const provider = await resolveScenarioBrowserProvider({
@@ -295,6 +307,7 @@ export const scenarioRun = pikkuSessionlessFunc({
295
307
  actors: scenarioActors,
296
308
  signInPath: env.signInPath,
297
309
  failureDir,
310
+ capture,
298
311
  browserScenarios: [...browserStepsByFlow.keys()],
299
312
  driver: config.scenarios?.browserDriver,
300
313
  });
@@ -353,10 +366,9 @@ export const scenarioRun = pikkuSessionlessFunc({
353
366
  * scenario body gets, the CLI's own singletons included, and its result is
354
367
  * discarded.
355
368
  *
356
- * The context is *feature*-scoped: `before` and `after` of one feature share
357
- * it, so setup can hand teardown the ids it created. It is deliberately not
358
- * the context the scenarios in the group see sharing one bag across a
359
- * group is the invisible coupling a Cucumber world had.
369
+ * Its context is *feature*-scoped shared by that feature's `before` and
370
+ * `after`, and deliberately not the context the group's scenarios see:
371
+ * one bag across a group is the invisible coupling a Cucumber world had.
360
372
  */
361
373
  const singletonServices = pikkuState(null, 'package', 'singletonServices');
362
374
  const runFeatureHook = async (hook, context) => {
@@ -371,6 +383,9 @@ export const scenarioRun = pikkuSessionlessFunc({
371
383
  // Before the scenario, not after it: the last scenario's window is left
372
384
  // open for headed debugging, while this one still starts clean.
373
385
  await browserLifecycle.reset();
386
+ // After the reset, which is what closes the previous scenario's context
387
+ // and finalises its video.
388
+ browserLifecycle.beginScenario(label);
374
389
  if (coverageActive) {
375
390
  const reset = await invokeCoverage('pikkuScenarioResetLiveCoverage');
376
391
  if (reset && reset.enabled === false) {
@@ -495,6 +510,12 @@ export const scenarioRun = pikkuSessionlessFunc({
495
510
  }
496
511
  }
497
512
  await browserLifecycle.close();
513
+ // Reported after the browser has closed, because video is only finalised
514
+ // when its context is. A capture nobody can find is a capture nobody
515
+ // looks at, and looking at them is the entire point of the flags.
516
+ if (capture) {
517
+ logger.info(`Captures → ${join(capture.dir, capture.runId)}`);
518
+ }
498
519
  if (coverage && Object.keys(scenarioCoverage).length > 0) {
499
520
  const coverageDir = join(resolve(config.rootDir, config.outDir), 'coverage');
500
521
  mkdirSync(coverageDir, { recursive: true });
@@ -1,10 +1,13 @@
1
1
  export declare const serve: import("#pikku").PikkuFunctionConfig<{
2
2
  port?: string;
3
3
  console?: boolean;
4
+ model?: string;
4
5
  }, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<{
5
6
  port?: string;
6
7
  console?: boolean;
8
+ model?: string;
7
9
  }, void, "rpc" | "session", import("#pikku").WiredServices, unknown> | import("#pikku").PikkuFunction<{
8
10
  port?: string;
9
11
  console?: boolean;
12
+ model?: string;
10
13
  }, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
@@ -4,19 +4,24 @@ import { ConsoleLogger, LocalEmailService, InMemoryQueueService, InMemoryWorkflo
4
4
  import { KyselyAIStorageService, KyselyAIRunStateService, KyselyAgentRunService, } from '@pikku/kysely';
5
5
  import { stopSingletonServices } from '@pikku/core';
6
6
  import { pikkuState } from '@pikku/core/ecosystem';
7
+ import { wireAIScorerQueueWorkers } from '@pikku/core/ai-scorer';
7
8
  import { LocalMetaService } from '@pikku/core/services/local-meta';
8
9
  import { LocalContent, } from '@pikku/core/services/local-content';
9
10
  import { InMemorySchedulerService } from '@pikku/schedule';
10
11
  import { resolveDb, createKysely, parseDatabaseUrl, } from '../db/local-db.js';
11
12
  import { loadUserBootstrap, loadUserModule } from './load-user-project.js';
13
+ import { registerScenarioInstrumentation } from '../wirings/scenarios/register-scenario-instrumentation.js';
14
+ import { resolveScaffoldFeature } from '../../utils/resolve-scaffold-feature.js';
12
15
  import { createDevAIAgentRunner } from './dev-ai-runner.js';
13
16
  import { resolveConsoleMount } from './serve-console.js';
14
17
  import { serverReadyLine } from '../../server/server-ready.js';
15
18
  import { createEphemeralContentSigningJWT } from '../../server/content-signing-jwt.js';
19
+ import { applyModelAliasOverride } from '../../utils/model-alias-override.js';
16
20
  export const serve = pikkuSessionlessFunc({
17
21
  remote: true,
18
- func: async ({ logger, config, getInspectorState, variables, devServerRunner }, { port, console: serveConsole }) => {
22
+ func: async ({ logger, config, getInspectorState, variables, devServerRunner }, { port, console: serveConsole, model }) => {
19
23
  process.env.PIKKU_DEV_QUICK_LOGIN ??= 'true';
24
+ applyModelAliasOverride(logger, model, config.models);
20
25
  const resolvedPort = parseInt(port || '3000', 10);
21
26
  const hostname = 'localhost';
22
27
  const bindHostname = '127.0.0.1';
@@ -28,6 +33,14 @@ export const serve = pikkuSessionlessFunc({
28
33
  return;
29
34
  }
30
35
  await loadUserBootstrap(pikkuDir);
36
+ // Scenario instrumentation exists only on a locally served project: the
37
+ // runner calls it over RPC to reset and snapshot coverage and stub calls.
38
+ // It is registered here, after the app bootstrap, rather than generated
39
+ // into the project — an app bootstrap is what a deployed bundle imports,
40
+ // and coverage endpoints have no business in one.
41
+ if (config.scaffold?.scenarios) {
42
+ registerScenarioInstrumentation(resolveScaffoldFeature('scenarios', config.scaffold.scenarios).auth);
43
+ }
31
44
  const workflowService = new InMemoryWorkflowService();
32
45
  const configModule = await loadUserModule(pikkuConfigFactory.file);
33
46
  const servicesModule = await loadUserModule(singletonServicesFactory.file);
@@ -115,6 +128,7 @@ export const serve = pikkuSessionlessFunc({
115
128
  };
116
129
  pikkuState(null, 'package', 'singletonServices', resolvedServices);
117
130
  resolvedServices.workflowService?.wireQueueWorkers?.();
131
+ wireAIScorerQueueWorkers();
118
132
  const { serverLifecycleFactory } = inspectorState.filesAndMethods;
119
133
  const loadLifecycle = async () => {
120
134
  if (!serverLifecycleFactory)
@@ -1,7 +1,12 @@
1
- import { renderWorkspaceValidate } from '../validate/workspace-validate.js';
2
- export declare const workspaceValidate: import("#pikku").PikkuFunctionConfig<Record<string, never>, {
1
+ import { renderValidate } from '../validate/validate.js';
2
+ export declare const validate: import("#pikku").PikkuFunctionConfig<Record<string, never>, {
3
3
  ok: boolean;
4
4
  root: string;
5
+ ran: {
6
+ checkId: string;
7
+ subject: string;
8
+ target: string;
9
+ }[];
5
10
  findings: {
6
11
  id: string;
7
12
  severity: "warn" | "error" | "info";
@@ -12,6 +17,11 @@ export declare const workspaceValidate: import("#pikku").PikkuFunctionConfig<Rec
12
17
  }, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<Record<string, never>, {
13
18
  ok: boolean;
14
19
  root: string;
20
+ ran: {
21
+ checkId: string;
22
+ subject: string;
23
+ target: string;
24
+ }[];
15
25
  findings: {
16
26
  id: string;
17
27
  severity: "warn" | "error" | "info";
@@ -22,6 +32,11 @@ export declare const workspaceValidate: import("#pikku").PikkuFunctionConfig<Rec
22
32
  }, "rpc" | "session", import("#pikku").WiredServices, unknown> | import("#pikku").PikkuFunction<Record<string, never>, {
23
33
  ok: boolean;
24
34
  root: string;
35
+ ran: {
36
+ checkId: string;
37
+ subject: string;
38
+ target: string;
39
+ }[];
25
40
  findings: {
26
41
  id: string;
27
42
  severity: "warn" | "error" | "info";
@@ -30,4 +45,4 @@ export declare const workspaceValidate: import("#pikku").PikkuFunctionConfig<Rec
30
45
  fixHint: string;
31
46
  }[];
32
47
  }, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
33
- export { renderWorkspaceValidate };
48
+ export { renderValidate };
@@ -0,0 +1,9 @@
1
+ import { pikkuSessionlessFunc } from '#pikku';
2
+ import { ValidateInput, ValidateOutput, renderValidate, runProjectValidate, } from '../validate/validate.js';
3
+ export const validate = pikkuSessionlessFunc({
4
+ description: 'Check this project against every Pikku check that applies to it — app structure, and the published file set of any addon it contains. Prints what was checked, with fix hints for anything wrong.',
5
+ input: ValidateInput,
6
+ output: ValidateOutput,
7
+ func: async () => runProjectValidate(),
8
+ });
9
+ export { renderValidate };
@@ -40,7 +40,7 @@ export interface ResolvedPostgresDb extends ResolvedDbBase {
40
40
  devSeedFile: string;
41
41
  /**
42
42
  * Postgres extensions the embedded PGlite databases must load, as declared by
43
- * `pgliteExtensions` in the project's config. See
43
+ * `db.pgliteExtensions` in the project's pikku.config.json. See
44
44
  * {@link loadPGliteExtensions}.
45
45
  *
46
46
  * Carried even in `url` mode: the shadow database the CLI migrates and
@@ -72,6 +72,7 @@ export declare function parseDatabaseUrl(url: string): Pick<UserConfigShape, 'sq
72
72
  export declare function resolveDb(userConfig: UserConfigShape, rootDir: string, outDir: string, runtimeDir?: string, dbConfig?: {
73
73
  schema?: string;
74
74
  defaultSchema?: string;
75
+ pgliteExtensions?: string[];
75
76
  } | string): ResolvedDb | null;
76
77
  /** @deprecated Use resolveDb(userConfig, ...) instead. */
77
78
  export declare function resolveLocalDb(sqliteDb: string | undefined, rootDir: string, outDir: string, runtimeDir?: string): ResolvedSqliteDb | null;
@@ -68,7 +68,7 @@ export function resolveDb(userConfig, rootDir, outDir, runtimeDir, dbConfig) {
68
68
  if (userConfig.postgresUrl && userConfig.sqliteDb) {
69
69
  throw new Error('Both postgresUrl and sqliteDb are set. Configure exactly one database dialect.');
70
70
  }
71
- const pgliteExtensions = userConfig.pgliteExtensions ?? [];
71
+ const pgliteExtensions = (typeof dbConfig === 'string' ? undefined : dbConfig?.pgliteExtensions) ?? [];
72
72
  if (userConfig.postgresUrl) {
73
73
  return {
74
74
  dialect: 'postgres',
@@ -268,7 +268,7 @@ function explainMissingExtension(error, declared) {
268
268
  return new Error(`The embedded PGlite database has no '${name}' extension. ` +
269
269
  `The CLI migrates a PGlite shadow database to type and diff your schema, ` +
270
270
  `so every extension your migrations use has to be declared as ` +
271
- `pgliteExtensions in createConfig — e.g. pgliteExtensions: ['${name}'] ` +
271
+ `db.pgliteExtensions in pikku.config.json — e.g. "db": { "pgliteExtensions": ["${name}"] } ` +
272
272
  `for a PGlite contrib extension, or the package that publishes it ` +
273
273
  `('@electric-sql/pglite-pgvector' for pgvector).` +
274
274
  (declared.length > 0
@@ -0,0 +1,26 @@
1
+ import type { ValidateFinding as Finding } from './persona-checks.js';
2
+ /**
3
+ * A package that publishes generated pikku output.
4
+ *
5
+ * The generated files carry relative imports to hand-written sources, so what
6
+ * the package ships and what it references have to agree — which is what the
7
+ * checks below are for.
8
+ *
9
+ * Having a `.pikku` directory is not enough on its own: an app's
10
+ * packages/functions has one too, and it is codegen output for that app rather
11
+ * than something anybody installs. The question is whether the package *ships*
12
+ * it, so the signal is a `files`/`exports` that carries it into the tarball.
13
+ * A private package is excluded for the same reason — nobody installs it, so
14
+ * its published file set is not a thing that can be wrong.
15
+ */
16
+ export declare function isAddonPackage(pkgDir: string): Promise<boolean>;
17
+ /**
18
+ * Every relative import in a shipped generated file must resolve to a file
19
+ * that is itself shipped.
20
+ *
21
+ * Stated as a property rather than as "the build script must copy types/",
22
+ * because the build script is not the only way to get this wrong and will not
23
+ * always be a `cp`. The generated files declare what they need; the check just
24
+ * holds the package to it.
25
+ */
26
+ export declare function runAddonPackageChecks(pkgDir: string): Promise<Finding[]>;
@@ -0,0 +1,258 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir } from 'node:fs/promises';
3
+ import { dirname, join, relative, resolve, sep } from 'node:path';
4
+ import { readJsonSafe, readTextSafe } from './shared-checks.js';
5
+ /** Where the CLI writes generated output, both in source and once built. */
6
+ const GENERATED_DIRS = ['.pikku', join('dist', '.pikku')];
7
+ /** Roots that carry generated output into the tarball when listed in `files`. */
8
+ const SHIPPING_ROOTS = new Set(['dist', '.pikku']);
9
+ /**
10
+ * A package that publishes generated pikku output.
11
+ *
12
+ * The generated files carry relative imports to hand-written sources, so what
13
+ * the package ships and what it references have to agree — which is what the
14
+ * checks below are for.
15
+ *
16
+ * Having a `.pikku` directory is not enough on its own: an app's
17
+ * packages/functions has one too, and it is codegen output for that app rather
18
+ * than something anybody installs. The question is whether the package *ships*
19
+ * it, so the signal is a `files`/`exports` that carries it into the tarball.
20
+ * A private package is excluded for the same reason — nobody installs it, so
21
+ * its published file set is not a thing that can be wrong.
22
+ */
23
+ export async function isAddonPackage(pkgDir) {
24
+ const pkg = await readJsonSafe(join(pkgDir, 'package.json'));
25
+ if (!pkg || pkg.private)
26
+ return false;
27
+ if (!GENERATED_DIRS.some((dir) => existsSync(join(pkgDir, dir))))
28
+ return false;
29
+ const shipsViaFiles = (pkg.files ?? []).some((entry) => SHIPPING_ROOTS.has(entry.replace(/^\.\//, '').split('/')[0]));
30
+ return shipsViaFiles || JSON.stringify(pkg.exports ?? null).includes('.pikku');
31
+ }
32
+ async function walk(dir) {
33
+ const out = [];
34
+ let entries;
35
+ try {
36
+ entries = await readdir(dir, { withFileTypes: true });
37
+ }
38
+ catch {
39
+ return out;
40
+ }
41
+ for (const entry of entries) {
42
+ const path = join(dir, entry.name);
43
+ if (entry.isDirectory())
44
+ out.push(...(await walk(path)));
45
+ else
46
+ out.push(path);
47
+ }
48
+ return out;
49
+ }
50
+ const IMPORT_RE = /(?:^|[\s(};])(?:from|import|require)\s*\(?\s*['"]([^'"]+)['"]/g;
51
+ function relativeSpecifiers(source) {
52
+ const found = new Set();
53
+ for (const match of source.matchAll(IMPORT_RE)) {
54
+ const specifier = match[1];
55
+ if (specifier.startsWith('.'))
56
+ found.add(specifier);
57
+ }
58
+ return [...found];
59
+ }
60
+ /**
61
+ * The on-disk file a specifier names, under TypeScript's NodeNext rules.
62
+ *
63
+ * `./x.js` may be satisfied by `x.ts` or `x.d.ts`, and pikku's generated files
64
+ * import hand-written declarations as `./x.d.js` — the `.js` extension the
65
+ * compiler wants, on a file that only ever exists as `.d.ts`.
66
+ */
67
+ function resolveCandidates(specifier, fromDir) {
68
+ const base = resolve(fromDir, specifier);
69
+ if (base.endsWith('.d.js'))
70
+ return [base.slice(0, -5) + '.d.ts'];
71
+ if (base.endsWith('.js')) {
72
+ const stem = base.slice(0, -3);
73
+ return [base, stem + '.ts', stem + '.d.ts', stem + '.tsx'];
74
+ }
75
+ // `.mjs` and `.cjs` are scanned as generated files, so they are also imported
76
+ // as one, and each has its own TypeScript twin rather than sharing `.ts`.
77
+ if (base.endsWith('.mjs') || base.endsWith('.cjs')) {
78
+ const stem = base.slice(0, -4);
79
+ const kind = base.slice(-3, -2);
80
+ return [base, `${stem}.${kind}ts`, `${stem}.d.${kind}ts`];
81
+ }
82
+ if (/\.(ts|tsx|json)$/.test(base))
83
+ return [base];
84
+ return [
85
+ base + '.ts',
86
+ base + '.d.ts',
87
+ base + '.js',
88
+ join(base, 'index.ts'),
89
+ join(base, 'index.d.ts'),
90
+ join(base, 'index.js'),
91
+ ];
92
+ }
93
+ /**
94
+ * The directory a `files` entry is rooted at.
95
+ *
96
+ * `dist`, `dist/`, `dist/**` and `dist/*` all publish the same tree, so the
97
+ * root is what decides both whether a path ships and whether the entry has
98
+ * anything behind it. Anything more elaborate than a trailing wildcard is left
99
+ * to npm.
100
+ */
101
+ function packedRoot(entry) {
102
+ return entry
103
+ .replace(/^\.\//, '')
104
+ .replace(/\/\*+$/, '')
105
+ .replace(/\/$/, '');
106
+ }
107
+ /**
108
+ * Whether `files` carries this path into the published tarball.
109
+ *
110
+ * A package with no `files` publishes everything, so the question only has
111
+ * teeth when the field is present.
112
+ */
113
+ function isPacked(absPath, pkgDir, files) {
114
+ if (!files)
115
+ return true;
116
+ const rel = relative(pkgDir, absPath);
117
+ if (rel.startsWith('..'))
118
+ return false;
119
+ return files.some((entry) => {
120
+ const normalized = packedRoot(entry);
121
+ return rel === normalized || rel.startsWith(normalized + sep);
122
+ });
123
+ }
124
+ /**
125
+ * Every `./…` target reachable through an `exports` or `imports` map, paired
126
+ * with the entry-point name that leads to it.
127
+ *
128
+ * Both fields nest arbitrarily deep — a subpath holds a condition object, which
129
+ * may hold another — so the walk collects strings rather than assuming a shape.
130
+ * A `*` is left in place: the wildcard's own directory is what has to ship.
131
+ */
132
+ function entryPointTargets(map, name = '') {
133
+ if (typeof map === 'string') {
134
+ return map.startsWith('./') ? [{ name, target: map }] : [];
135
+ }
136
+ if (!map || typeof map !== 'object')
137
+ return [];
138
+ return Object.entries(map).flatMap(([key, value]) => entryPointTargets(value, name || key));
139
+ }
140
+ /**
141
+ * Every relative import in a shipped generated file must resolve to a file
142
+ * that is itself shipped.
143
+ *
144
+ * Stated as a property rather than as "the build script must copy types/",
145
+ * because the build script is not the only way to get this wrong and will not
146
+ * always be a `cp`. The generated files declare what they need; the check just
147
+ * holds the package to it.
148
+ */
149
+ export async function runAddonPackageChecks(pkgDir) {
150
+ const findings = [];
151
+ const pkg = await readJsonSafe(join(pkgDir, 'package.json'));
152
+ if (!pkg)
153
+ return findings;
154
+ // `files` is the package's own statement of what it publishes, so an entry
155
+ // with nothing behind it means the build has not run — and validating the
156
+ // published file set of a package that has not been built proves nothing.
157
+ const absent = (pkg.files ?? []).filter((entry) => !existsSync(join(pkgDir, packedRoot(entry))));
158
+ if (absent.length > 0) {
159
+ findings.push({
160
+ id: 'addon-not-built',
161
+ severity: 'info',
162
+ message: `${pkg.name ?? relative(dirname(pkgDir), pkgDir)} lists ${absent.join(', ')} in "files" with nothing on disk — packaging was not verified because the package is not built`,
163
+ path: join(pkgDir, absent[0]),
164
+ fixHint: 'Run the package build first, then validate. In CI, `build && pikku validate` is the order that checks what actually publishes.',
165
+ });
166
+ return findings;
167
+ }
168
+ // The entry points are the package's promise about what can be reached from
169
+ // outside it, and nothing inside dist/.pikku mentions them — so the import
170
+ // walk below cannot see a `#pikku` that still points at the source tree.
171
+ for (const { name, target } of [
172
+ ...entryPointTargets(pkg.imports),
173
+ ...entryPointTargets(pkg.exports),
174
+ ]) {
175
+ const withoutWildcard = target.slice(2).split('*')[0];
176
+ if (!isPacked(join(pkgDir, withoutWildcard), pkgDir, pkg.files)) {
177
+ findings.push({
178
+ id: 'addon-entry-point-not-packed',
179
+ severity: 'error',
180
+ message: `"${name}" resolves to ${target}, which "files" does not publish — the entry point exists here and not for anyone who installs ${pkg.name ?? 'this addon'}`,
181
+ path: join(pkgDir, withoutWildcard),
182
+ fixHint: `Point it at the built copy under dist, or add ${withoutWildcard.split('/')[0]} to "files".`,
183
+ });
184
+ continue;
185
+ }
186
+ // Shipping the directory is not the same as producing the file. A wildcard
187
+ // names a directory whose contents are the point, so only a concrete
188
+ // target can be held to existing.
189
+ if (!target.includes('*') && !existsSync(join(pkgDir, withoutWildcard))) {
190
+ findings.push({
191
+ id: 'addon-entry-point-missing',
192
+ severity: 'error',
193
+ message: `"${name}" resolves to ${target}, which does not exist — importing it fails for anyone who installs ${pkg.name ?? 'this addon'}`,
194
+ path: join(pkgDir, withoutWildcard),
195
+ fixHint: 'Point the entry point at a file the build produces, or drop it from "exports".',
196
+ });
197
+ }
198
+ }
199
+ // Both generated directories ship, and they are not the same check twice.
200
+ // `exports` maps `./.pikku/*` to the root one, making it a public entry
201
+ // point whose imports climb to `<pkg>/types`, while the copy under dist
202
+ // reaches `<pkg>/dist/types` — two roots, two ways to fall outside the
203
+ // tarball.
204
+ const generatedRoots = GENERATED_DIRS.map((dir) => join(pkgDir, dir)).filter((dir) => existsSync(dir) && isPacked(dir, pkgDir, pkg.files));
205
+ // One missing file is imported by every generated file that needs its types,
206
+ // so reporting per import turns a single packaging mistake into a page of
207
+ // findings. Group by what is actually wrong: the thing that is not shipped.
208
+ const unresolved = new Map();
209
+ const unpacked = new Map();
210
+ const shippedFiles = (await Promise.all(generatedRoots.map((root) => walk(root)))).flat();
211
+ for (const file of shippedFiles) {
212
+ if (!/\.(ts|tsx|js|mjs|cjs)$/.test(file))
213
+ continue;
214
+ const source = await readTextSafe(file);
215
+ if (!source)
216
+ continue;
217
+ for (const specifier of relativeSpecifiers(source)) {
218
+ const candidates = resolveCandidates(specifier, dirname(file));
219
+ const target = candidates.find((candidate) => existsSync(candidate));
220
+ if (!target) {
221
+ // Keyed by the file that is missing rather than by the specifier: the
222
+ // same absent file is spelled `../types/x` from one directory and
223
+ // `../../types/x` from another, and it is still one thing to fix.
224
+ const missing = candidates[0];
225
+ const seen = unresolved.get(missing) ?? { specifier, files: [] };
226
+ seen.files.push(file);
227
+ unresolved.set(missing, seen);
228
+ }
229
+ else if (!isPacked(target, pkgDir, pkg.files)) {
230
+ const seen = unpacked.get(target) ?? { target, files: [] };
231
+ seen.files.push(file);
232
+ unpacked.set(target, seen);
233
+ }
234
+ }
235
+ }
236
+ const alsoIn = (files) => files.length > 1
237
+ ? ` (and ${files.length - 1} other generated file${files.length > 2 ? 's' : ''})`
238
+ : '';
239
+ for (const [missing, { specifier, files }] of unresolved) {
240
+ findings.push({
241
+ id: 'addon-shipped-import-unresolved',
242
+ severity: 'error',
243
+ message: `${relative(pkgDir, missing)} is not in the package, but ${files.length} shipped generated file${files.length === 1 ? '' : 's'} import${files.length === 1 ? 's' : ''} it (as "${specifier}" from ${relative(pkgDir, files[0])}) — anything installing ${pkg.name ?? 'this addon'} typechecks against a module that is not there`,
244
+ path: missing,
245
+ fixHint: 'Ship the file it names. The generated output is copied into dist, so whatever it imports has to be copied too — e.g. `cp -r .pikku types dist/` rather than `cp -r .pikku dist/`.',
246
+ });
247
+ }
248
+ for (const { target, files } of unpacked.values()) {
249
+ findings.push({
250
+ id: 'addon-shipped-import-not-packed',
251
+ severity: 'error',
252
+ message: `${relative(pkgDir, files[0])} imports ${relative(pkgDir, target)}${alsoIn(files)}, which exists but is excluded by "files" — it resolves here and not for anyone who installs the package`,
253
+ path: target,
254
+ fixHint: `Add ${relative(pkgDir, target).split(sep)[0]} to "files" in package.json, or move the file under a path already listed there.`,
255
+ });
256
+ }
257
+ return findings;
258
+ }
@@ -15,7 +15,7 @@ export type PersonaCheckConfig = {
15
15
  /**
16
16
  * The people this project can run as, and whether it can run as them.
17
17
  *
18
- * Shared by `pikku workspace validate` and `pikku fabric validate` — the two
18
+ * Shared by `pikku validate` and `pikku fabric validate` — the two
19
19
  * validators are separate implementations, and a check that only one of them
20
20
  * makes is a check half the projects never see.
21
21
  *
@@ -100,7 +100,7 @@ const hasActorSignIn = async (sourceFiles) => {
100
100
  /**
101
101
  * The people this project can run as, and whether it can run as them.
102
102
  *
103
- * Shared by `pikku workspace validate` and `pikku fabric validate` — the two
103
+ * Shared by `pikku validate` and `pikku fabric validate` — the two
104
104
  * validators are separate implementations, and a check that only one of them
105
105
  * makes is a check half the projects never see.
106
106
  *
@@ -36,6 +36,16 @@ const SCENARIO_FILE = /\.(scenario|scenarios|steps)\.tsx?$/;
36
36
  */
37
37
  const VIRTUAL_USER_DECLARATIONS = ['definePersonas', 'runVirtualUser'];
38
38
  const VIRTUAL_USER_FILE = /\.(virtual-user|vu)\.tsx?$/;
39
+ /**
40
+ * The declarations that grade an agent, and where they live.
41
+ *
42
+ * A scorer is a test of the agent that is also shipped, which is exactly why it
43
+ * needs its own file rather than sitting beside the agent it grades: the two
44
+ * are edited for opposite reasons, and a rubric buried in an agent definition
45
+ * is one nobody reviews as a rubric.
46
+ */
47
+ const SCORER_DECLARATIONS = ['pikkuAIScorer', 'pikkuAIJudge'];
48
+ const SCORER_FILE = /\.scorers?\.tsx?$/;
39
49
  const isGenerated = (path) => path.includes('.gen.') || path.includes(`${'/'}.pikku${'/'}`);
40
50
  const listSourceFiles = async (dir) => {
41
51
  if (!existsSync(dir))
@@ -119,5 +129,28 @@ export const runScenarioFileChecks = async (root, srcDirectories) => {
119
129
  ].join('\n'),
120
130
  });
121
131
  }
132
+ for (const file of files) {
133
+ if (SCORER_FILE.test(file) || isGenerated(file))
134
+ continue;
135
+ const text = await readTextSafe(file);
136
+ if (!text)
137
+ continue;
138
+ const used = SCORER_DECLARATIONS.filter((name) => new RegExp(`\\b${name}\\s*\\(`).test(text));
139
+ if (used.length === 0)
140
+ continue;
141
+ const rel = relative(root, file);
142
+ findings.push({
143
+ id: 'scorer-declared-outside-scorer-file',
144
+ severity: 'error',
145
+ message: `${rel} declares ${used.join(', ')} — scorers and judges must live in a *.scorer.ts file, not mixed into application code`,
146
+ path: file,
147
+ fixHint: [
148
+ 'Move the declaration into a dedicated file, e.g.:',
149
+ ` ${rel.replace(/[^/\\]+$/, 'grading.scorer.ts')}`,
150
+ 'A scorer grades the agent it sits next to, and is edited for the',
151
+ 'opposite reason — keeping it separate is what keeps it reviewable.',
152
+ ].join('\n'),
153
+ });
154
+ }
122
155
  return findings;
123
156
  };
@@ -48,7 +48,7 @@ export declare function readTextSafe(path: string): Promise<string | null>;
48
48
  /**
49
49
  * Every check that is true of any pikku project, regardless of where it deploys.
50
50
  *
51
- * `pikku workspace validate` is exactly this; `pikku fabric validate` is this
51
+ * `pikku validate`'s app-project check is exactly this; `pikku fabric validate` is this
52
52
  * plus the deploy-shaped checks (themes, frontends, the Cloudflare adapter, the
53
53
  * .gitignore contract). Before this module the two were separate walks over the
54
54
  * same project that duplicated sixteen findings verbatim, and each carried
@@ -85,7 +85,7 @@ const REQUIRED_SCAFFOLD = [
85
85
  /**
86
86
  * Every check that is true of any pikku project, regardless of where it deploys.
87
87
  *
88
- * `pikku workspace validate` is exactly this; `pikku fabric validate` is this
88
+ * `pikku validate`'s app-project check is exactly this; `pikku fabric validate` is this
89
89
  * plus the deploy-shaped checks (themes, frontends, the Cloudflare adapter, the
90
90
  * .gitignore contract). Before this module the two were separate walks over the
91
91
  * same project that duplicated sixteen findings verbatim, and each carried