@pikku/cli 0.12.156 → 0.12.160

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 (280) hide show
  1. package/console-app/assets/{HelpPanel-DaE02BQE.js → HelpPanel-C06T226y.js} +1 -1
  2. package/console-app/assets/{abnfDiagram-VCTEODGH-CExRuljg.js → abnfDiagram-VCTEODGH-BVwdT3Es.js} +1 -1
  3. package/console-app/assets/architecture-7GRP2DOG-3y7bWtag.js +1 -0
  4. package/console-app/assets/{architectureDiagram-5GKGNRK7-uKjmdPIQ.js → architectureDiagram-5GKGNRK7-XfzqZ3mW.js} +1 -1
  5. package/console-app/assets/{blockDiagram-I7D4REHJ-DNgIFbT4.js → blockDiagram-I7D4REHJ-B5omSqDK.js} +1 -1
  6. package/console-app/assets/{c4Diagram-7LVT6UL2-0vP9sLld.js → c4Diagram-7LVT6UL2-DkPe7xCg.js} +1 -1
  7. package/console-app/assets/channel-CTiluQVw.js +1 -0
  8. package/console-app/assets/{chunk-4HAMMTFA-BJ59eqJI.js → chunk-4HAMMTFA-QfSXT_X7.js} +1 -1
  9. package/console-app/assets/{chunk-75Z2AOVW-Bge6xoqX.js → chunk-75Z2AOVW-h7UnPWNo.js} +1 -1
  10. package/console-app/assets/{chunk-DU6HZSFF-CkmYNhC5.js → chunk-DU6HZSFF-VaMy-LOI.js} +1 -1
  11. package/console-app/assets/{chunk-F27PBJKO-C44ef28t.js → chunk-F27PBJKO-toywqOqS.js} +1 -1
  12. package/console-app/assets/{chunk-GMAD6QVW-Bmqdx1l0.js → chunk-GMAD6QVW-Cpa-YKLB.js} +1 -1
  13. package/console-app/assets/{chunk-GVQU2GXP-AQp_KNx4.js → chunk-GVQU2GXP-BWSHPLNL.js} +1 -1
  14. package/console-app/assets/{chunk-IMKFNOWR-Ca3GoHnO.js → chunk-IMKFNOWR-yhQzfQuj.js} +1 -1
  15. package/console-app/assets/{chunk-L3NEJ4N5-C_GhayZt.js → chunk-L3NEJ4N5-C8pAfDlo.js} +1 -1
  16. package/console-app/assets/{chunk-OSK3NFVY-DGzfF4JV.js → chunk-OSK3NFVY-749yk2fa.js} +1 -1
  17. package/console-app/assets/{chunk-P2QGCYS3-N8xMDiFw.js → chunk-P2QGCYS3-C1Y9GyYT.js} +1 -1
  18. package/console-app/assets/{chunk-POPQ4Y6H-CdhIikwj.js → chunk-POPQ4Y6H-DzI9aRsX.js} +1 -1
  19. package/console-app/assets/{chunk-PWAF6VOD-CdTJmOYg.js → chunk-PWAF6VOD-D3fAPSwz.js} +1 -1
  20. package/console-app/assets/{chunk-SHT3W25Y-Ctj8zSAF.js → chunk-SHT3W25Y-D9w7LzM9.js} +1 -1
  21. package/console-app/assets/{chunk-SVP7TREG-DTNKveWo.js → chunk-SVP7TREG-D55MjOJw.js} +1 -1
  22. package/console-app/assets/{chunk-TICWLB2K-Df6e0LIf.js → chunk-TICWLB2K-DV8q7H-z.js} +1 -1
  23. package/console-app/assets/chunk-XXDRQBXY-DBtYQykd.js +1 -0
  24. package/console-app/assets/classDiagram-ZZMXUADV-BnWa-i9n.js +1 -0
  25. package/console-app/assets/classDiagram-v2-VYDZK3BY-BnWa-i9n.js +1 -0
  26. package/console-app/assets/{cose-bilkent-JH36ORCC-DBd_emhQ.js → cose-bilkent-JH36ORCC-CLedPW9a.js} +1 -1
  27. package/console-app/assets/{cynefin-OW5HDTMX-3aBVpBMI.js → cynefin-OW5HDTMX-CS5NKw2p.js} +1 -1
  28. package/console-app/assets/{cynefinDiagram-5FMLGOSQ-5BZzBehJ.js → cynefinDiagram-5FMLGOSQ-DGTHW90E.js} +1 -1
  29. package/console-app/assets/{dagre-DATELzSq.js → dagre-BNQdos_2.js} +1 -1
  30. package/console-app/assets/{dagre-GXQ25YYZ-orKIUXIt.js → dagre-GXQ25YYZ-DHZ8wtwQ.js} +1 -1
  31. package/console-app/assets/{diagram-S7CK7UJ4-CwwxVQ_N.js → diagram-S7CK7UJ4-C41Begsy.js} +1 -1
  32. package/console-app/assets/{diagram-UQ7AKVKN-CcEJxIEU.js → diagram-UQ7AKVKN-CU8D-sRT.js} +1 -1
  33. package/console-app/assets/{diagram-VSXAHHWV-DGfXNB6v.js → diagram-VSXAHHWV-DTTiXYnQ.js} +1 -1
  34. package/console-app/assets/{diagram-VX7I27RA-Kqkord1q.js → diagram-VX7I27RA-BBy91pxm.js} +1 -1
  35. package/console-app/assets/{diagram-Z3DM3KII-Dy5rfOxu.js → diagram-Z3DM3KII-D50xTYe5.js} +1 -1
  36. package/console-app/assets/{ebnfDiagram-PWID7BFC-y1bmj1ry.js → ebnfDiagram-PWID7BFC-BHjnFZgE.js} +1 -1
  37. package/console-app/assets/{erDiagram-RLTQ6QDP-CNk_w5Ib.js → erDiagram-RLTQ6QDP-B4-yVo9b.js} +1 -1
  38. package/console-app/assets/eventmodeling-NTZA5JFV-C_IpFSCw.js +1 -0
  39. package/console-app/assets/flowDiagram-HODETNUW-Q90rgVpx.js +1 -0
  40. package/console-app/assets/{ganttDiagram-EL5Y4UJY-DdVaRGRy.js → ganttDiagram-EL5Y4UJY-D1T-2BlO.js} +1 -1
  41. package/console-app/assets/{gitGraph-4MIJSDKK-DpSAepQv.js → gitGraph-4MIJSDKK-ClqKTUfQ.js} +1 -1
  42. package/console-app/assets/{gitGraphDiagram-WWUBYQGX-CIhGnwZW.js → gitGraphDiagram-WWUBYQGX-ruq2MAhM.js} +1 -1
  43. package/console-app/assets/{graphlib-CRp93vEd.js → graphlib-D-vLYPdw.js} +1 -1
  44. package/console-app/assets/{index-B34OoL_f.js → index-BIEZPuyK.js} +5 -5
  45. package/console-app/assets/{info-A6RAGUB7-DckDPZ56.js → info-A6RAGUB7-BGI-8HNB.js} +1 -1
  46. package/console-app/assets/{infoDiagram-27XIBGKW-DMk6mpjP.js → infoDiagram-27XIBGKW-BGURgKHv.js} +1 -1
  47. package/console-app/assets/{ishikawaDiagram-5VMMS53U-Dqr5CEc9.js → ishikawaDiagram-5VMMS53U-DzgAaPTR.js} +1 -1
  48. package/console-app/assets/{journeyDiagram-3NMN7TZE-Kv0orI9j.js → journeyDiagram-3NMN7TZE-BsyWad8z.js} +1 -1
  49. package/console-app/assets/{kanban-definition-UXKFOSKX-BrYb0IpS.js → kanban-definition-UXKFOSKX-g1nO2rWF.js} +1 -1
  50. package/console-app/assets/{line-D2jQ449E.js → line-CeFoQ5y-.js} +1 -1
  51. package/console-app/assets/{linear-BKu0S9-y.js → linear-C4Pk-YMX.js} +1 -1
  52. package/console-app/assets/{mermaid-parser.core-DhWf8x6B.js → mermaid-parser.core-U7wquvx8.js} +3 -3
  53. package/console-app/assets/{mermaid.core-RkgzUTr1.js → mermaid.core-8TRpFRm9.js} +4 -4
  54. package/console-app/assets/{mindmap-definition-YA3MSWOX-BKATx3X1.js → mindmap-definition-YA3MSWOX-C7lzwqBX.js} +1 -1
  55. package/console-app/assets/{packet-AYTQ26CC-Dek-5xov.js → packet-AYTQ26CC-DiQV_rU3.js} +1 -1
  56. package/console-app/assets/{pegDiagram-XKGWAZYB-CkEJ5MeF.js → pegDiagram-XKGWAZYB-63ajP7-W.js} +1 -1
  57. package/console-app/assets/{pie-WAS4IAKB-BIoN9DHQ.js → pie-WAS4IAKB-B5q_o6dr.js} +1 -1
  58. package/console-app/assets/{pieDiagram-E7YTZNPT-CEPQgMPk.js → pieDiagram-E7YTZNPT-BTbvj-by.js} +1 -1
  59. package/console-app/assets/{quadrantDiagram-AXDQQJYC-eJxMa_50.js → quadrantDiagram-AXDQQJYC-CulqsAYd.js} +1 -1
  60. package/console-app/assets/{radar-RG4KPBEZ-BwJnovJ0.js → radar-RG4KPBEZ-CW_KoURS.js} +1 -1
  61. package/console-app/assets/{railroad-74A4TZTK-D1_fi4Md.js → railroad-74A4TZTK-CUmF6uBQ.js} +1 -1
  62. package/console-app/assets/railroad-abnf-HS5TGJTU-CcPw9q8a.js +1 -0
  63. package/console-app/assets/railroad-ebnf-LZEXJU2U-Ci_fZics.js +1 -0
  64. package/console-app/assets/railroad-peg-WCYAUIDC-DTsDykSf.js +1 -0
  65. package/console-app/assets/{railroadDiagram-O6MQD6OU-BpOx1Ffe.js → railroadDiagram-O6MQD6OU-WlkBu_N3.js} +1 -1
  66. package/console-app/assets/{requirementDiagram-BXWQKSXE-D-IEFxpk.js → requirementDiagram-BXWQKSXE-CJK_CbPR.js} +1 -1
  67. package/console-app/assets/{sankeyDiagram-P5KCCOFB-SdALSgLt.js → sankeyDiagram-P5KCCOFB-C466WOH8.js} +1 -1
  68. package/console-app/assets/{sequenceDiagram-WJ2MYXX4-Burhioru.js → sequenceDiagram-WJ2MYXX4-CV0dCcj0.js} +1 -1
  69. package/console-app/assets/{src-BI-J79xw.js → src-P4Nw8sY0.js} +1 -1
  70. package/console-app/assets/{stateDiagram-D77RDMKH-BhWb5zb7.js → stateDiagram-D77RDMKH-HaLGm8V9.js} +1 -1
  71. package/console-app/assets/stateDiagram-v2-MP3YSRHH--GM14Hz5.js +1 -0
  72. package/console-app/assets/{swimlanes-42K2YHIH-BoaeXC6b.js → swimlanes-42K2YHIH-Bi-O5vWa.js} +1 -1
  73. package/console-app/assets/swimlanesDiagram-VR7AAH4N-_6rRi6Wt.js +8 -0
  74. package/console-app/assets/{timeline-definition-24CTP7MA-uuBDVcEL.js → timeline-definition-24CTP7MA-B4h-gx-i.js} +1 -1
  75. package/console-app/assets/{treeView-Q6P3EWNA-BRZawPMp.js → treeView-Q6P3EWNA-CXJ_pHQw.js} +1 -1
  76. package/console-app/assets/{treemap-WGGIJYW6-WSINjp5g.js → treemap-WGGIJYW6-M84p2a_3.js} +1 -1
  77. package/console-app/assets/{vennDiagram-4TSXK5OY-K6UjE9uq.js → vennDiagram-4TSXK5OY-BesF4k_r.js} +1 -1
  78. package/console-app/assets/{wardley-WFR3VGLG-BMecQ-Z6.js → wardley-WFR3VGLG-n9ulickP.js} +1 -1
  79. package/console-app/assets/{wardleyDiagram-VM6X3IG4-9blTcGMt.js → wardleyDiagram-VM6X3IG4-BG0nnWDf.js} +1 -1
  80. package/console-app/assets/{xychartDiagram-S5SC5T6Z-DSnHhis6.js → xychartDiagram-S5SC5T6Z-BtOW7fpF.js} +1 -1
  81. package/console-app/index.html +1 -1
  82. package/dist/.pikku/addon/index.d.ts +1 -1
  83. package/dist/.pikku/addon/index.js +1 -1
  84. package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
  85. package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
  86. package/dist/.pikku/agent/index.d.ts +1 -1
  87. package/dist/.pikku/agent/index.js +1 -1
  88. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  89. package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
  90. package/dist/.pikku/analytics/index.d.ts +1 -1
  91. package/dist/.pikku/analytics/index.js +1 -1
  92. package/dist/.pikku/analytics/pikku-analytics-types.gen.d.ts +1 -1
  93. package/dist/.pikku/analytics/pikku-analytics-types.gen.js +1 -1
  94. package/dist/.pikku/auth/index.d.ts +1 -1
  95. package/dist/.pikku/auth/index.js +1 -1
  96. package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +1 -1
  97. package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
  98. package/dist/.pikku/channel/index.d.ts +1 -1
  99. package/dist/.pikku/channel/index.js +1 -1
  100. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  101. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  102. package/dist/.pikku/cli/index.d.ts +1 -1
  103. package/dist/.pikku/cli/index.js +1 -1
  104. package/dist/.pikku/cli/pikku-cli-channel.js +6 -1
  105. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  106. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  107. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  108. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  109. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +11 -1
  110. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  111. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  112. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  113. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +55 -3
  114. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  115. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  116. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  117. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  118. package/dist/.pikku/error/index.d.ts +1 -1
  119. package/dist/.pikku/error/index.js +1 -1
  120. package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
  121. package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
  122. package/dist/.pikku/function/index.d.ts +1 -1
  123. package/dist/.pikku/function/index.js +1 -1
  124. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  125. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  126. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  127. package/dist/.pikku/function/pikku-functions-meta.gen.json +38 -21
  128. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  129. package/dist/.pikku/gateway/index.d.ts +1 -1
  130. package/dist/.pikku/gateway/index.js +1 -1
  131. package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
  132. package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
  133. package/dist/.pikku/http/index.d.ts +1 -1
  134. package/dist/.pikku/http/index.js +1 -1
  135. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  136. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  137. package/dist/.pikku/mcp/index.d.ts +1 -1
  138. package/dist/.pikku/mcp/index.js +1 -1
  139. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  140. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  141. package/dist/.pikku/middleware/index.d.ts +1 -1
  142. package/dist/.pikku/middleware/index.js +1 -1
  143. package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +1 -1
  144. package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
  145. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  146. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  147. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  148. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  149. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  150. package/dist/.pikku/queue/index.d.ts +1 -1
  151. package/dist/.pikku/queue/index.js +1 -1
  152. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  153. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  154. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  155. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  156. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  157. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  158. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -0
  159. package/dist/.pikku/scenarios/index.d.ts +1 -1
  160. package/dist/.pikku/scenarios/index.js +1 -1
  161. package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
  162. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  163. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  164. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +7 -1
  165. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  166. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  167. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  168. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  169. package/dist/.pikku/scheduler/index.d.ts +1 -1
  170. package/dist/.pikku/scheduler/index.js +1 -1
  171. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  172. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  173. package/dist/.pikku/schemas/register.gen.js +3 -1
  174. package/dist/.pikku/schemas/schemas/FabricChangesAskInput.schema.json +1 -1
  175. package/dist/.pikku/schemas/schemas/FabricInitInput.schema.json +1 -1
  176. package/dist/.pikku/schemas/schemas/FabricLinkInput.schema.json +1 -1
  177. package/dist/.pikku/schemas/schemas/KnowledgeReconcileInput.schema.json +1 -1
  178. package/dist/.pikku/schemas/schemas/KnowledgeReconcileOutput.schema.json +1 -1
  179. package/dist/.pikku/schemas/schemas/PikkuSkillsInstallInput.schema.json +1 -1
  180. package/dist/.pikku/schemas/schemas/ScenarioGuideInput.schema.json +1 -0
  181. package/dist/.pikku/scopes/index.d.ts +1 -1
  182. package/dist/.pikku/scopes/index.js +1 -1
  183. package/dist/.pikku/scopes/pikku-flags.gen.d.ts +1 -1
  184. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  185. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  186. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  187. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  188. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  189. package/dist/.pikku/secrets/index.d.ts +1 -1
  190. package/dist/.pikku/secrets/index.js +1 -1
  191. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  192. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  193. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  194. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  195. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  196. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  197. package/dist/.pikku/setup/index.d.ts +1 -1
  198. package/dist/.pikku/setup/index.js +1 -1
  199. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  200. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  201. package/dist/.pikku/trigger/index.d.ts +1 -1
  202. package/dist/.pikku/trigger/index.js +1 -1
  203. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  204. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  205. package/dist/.pikku/variables/index.d.ts +1 -1
  206. package/dist/.pikku/variables/index.js +1 -1
  207. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  208. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  209. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  210. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  211. package/dist/.pikku/workflow/index.d.ts +1 -1
  212. package/dist/.pikku/workflow/index.js +1 -1
  213. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  214. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  215. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  216. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  217. package/dist/bin/pikku-bin.mjs +3 -3
  218. package/dist/src/cli.wiring.js +44 -2
  219. package/dist/src/deploy/analyzer/analyzer.js +93 -4
  220. package/dist/src/deploy/build-pipeline.d.ts +2 -0
  221. package/dist/src/deploy/build-pipeline.js +4 -3
  222. package/dist/src/deploy/codegen/per-unit-codegen.js +24 -0
  223. package/dist/src/fabric/fabric-commands.d.ts +9 -0
  224. package/dist/src/fabric/fabric-commands.js +11 -1
  225. package/dist/src/fabric/functions/changes-ask.function.d.ts +4 -0
  226. package/dist/src/fabric/functions/changes-ask.function.js +7 -1
  227. package/dist/src/fabric/functions/init.function.d.ts +4 -0
  228. package/dist/src/fabric/functions/init.function.js +5 -1
  229. package/dist/src/fabric/functions/link.function.d.ts +4 -0
  230. package/dist/src/fabric/functions/link.function.js +9 -1
  231. package/dist/src/fabric/lib/organization.d.ts +8 -0
  232. package/dist/src/fabric/lib/organization.js +26 -0
  233. package/dist/src/functions/commands/deploy-apply.js +129 -13
  234. package/dist/src/functions/commands/deploy-plan.js +1 -0
  235. package/dist/src/functions/commands/dev-mcp-manifests.d.ts +27 -0
  236. package/dist/src/functions/commands/dev-mcp-manifests.js +52 -0
  237. package/dist/src/functions/commands/dev.js +10 -30
  238. package/dist/src/functions/commands/knowledge-reconcile.d.ts +15 -3
  239. package/dist/src/functions/commands/knowledge-reconcile.js +1 -1
  240. package/dist/src/functions/commands/scenario-guide.d.ts +184 -0
  241. package/dist/src/functions/commands/scenario-guide.js +313 -0
  242. package/dist/src/functions/commands/scenario-ladder.d.ts +3 -1
  243. package/dist/src/functions/commands/scenario-ladder.js +1 -0
  244. package/dist/src/functions/commands/scenario-plan.d.ts +17 -1
  245. package/dist/src/functions/commands/scenario-plan.js +15 -0
  246. package/dist/src/functions/commands/scenario.d.ts +30 -0
  247. package/dist/src/functions/commands/scenario.js +187 -23
  248. package/dist/src/functions/commands/skill-agents.d.ts +43 -0
  249. package/dist/src/functions/commands/skill-agents.js +135 -0
  250. package/dist/src/functions/commands/skills.d.ts +9 -0
  251. package/dist/src/functions/commands/skills.js +29 -2
  252. package/dist/src/functions/db/postgres/postgres-introspector.d.ts +6 -0
  253. package/dist/src/functions/db/postgres/postgres-introspector.js +7 -1
  254. package/dist/src/functions/knowledge/render.d.ts +1 -1
  255. package/dist/src/functions/knowledge/render.js +25 -2
  256. package/dist/src/functions/knowledge/schemas.d.ts +5 -1
  257. package/dist/src/functions/wirings/mcp/pikku-command-mcp-json.js +32 -5
  258. package/dist/src/functions/wirings/scenarios/serialize-feature-meta.js +1 -0
  259. package/dist/src/functions/wirings/workflow/serialize-scenario-types.js +6 -0
  260. package/dist/src/server/bun-server-runner.js +1 -0
  261. package/dist/src/server/dev-server-runner.interface.d.ts +13 -0
  262. package/dist/src/server/node-server-runner.js +1 -0
  263. package/dist/src/services.js +3 -1
  264. package/dist/src/utils/mcp-surface.d.ts +8 -0
  265. package/dist/src/utils/mcp-surface.js +9 -0
  266. package/dist/tsconfig.tsbuildinfo +1 -1
  267. package/package.json +12 -12
  268. package/surface.json +19 -13
  269. package/console-app/assets/architecture-7GRP2DOG-DMiKIh61.js +0 -1
  270. package/console-app/assets/channel-D3pFDQ-Y.js +0 -1
  271. package/console-app/assets/chunk-XXDRQBXY-CPBzG1Ie.js +0 -1
  272. package/console-app/assets/classDiagram-ZZMXUADV-Bh5HK4MN.js +0 -1
  273. package/console-app/assets/classDiagram-v2-VYDZK3BY-Bh5HK4MN.js +0 -1
  274. package/console-app/assets/eventmodeling-NTZA5JFV-DLjVcr4S.js +0 -1
  275. package/console-app/assets/flowDiagram-HODETNUW-CCiyVZIU.js +0 -1
  276. package/console-app/assets/railroad-abnf-HS5TGJTU-D-_q3UaA.js +0 -1
  277. package/console-app/assets/railroad-ebnf-LZEXJU2U-kh7JN-2k.js +0 -1
  278. package/console-app/assets/railroad-peg-WCYAUIDC-rrwoksLW.js +0 -1
  279. package/console-app/assets/stateDiagram-v2-MP3YSRHH-OjHOAFS5.js +0 -1
  280. package/console-app/assets/swimlanesDiagram-VR7AAH4N-CkKXVcij.js +0 -8
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { resolve, join } from 'node:path';
3
- import { mkdirSync, writeFileSync } from 'node:fs';
2
+ import { resolve, join, dirname, relative, sep } from 'node:path';
3
+ import { mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs';
4
+ import { glob } from 'tinyglobby';
4
5
  import { pikkuSessionlessFunc } from '#pikku/function';
5
6
  import { InMemoryWorkflowService } from '@pikku/core/services';
6
7
  import { FileScenarioRunStore } from '@pikku/core/services';
@@ -14,7 +15,8 @@ import { resolveScenarioBrowserProvider, scenarioBrowserLifecycle, } from './sce
14
15
  import { resolvePersonas } from '../../utils/resolve-personas.js';
15
16
  import { resolvePersonaCredentials } from '../../utils/persona-credentials.js';
16
17
  import { spawnDevServer } from '../../server/spawn-dev-server.js';
17
- import { buildScenarioPlan } from './scenario-plan.js';
18
+ import { checkGuideCoverage, featureEvidence, guideStep, parseGuideLock, parseGuidePage, renderGuideLock, renderGuidePage, } from './scenario-guide.js';
19
+ import { buildScenarioPlan, identifyScenarioResult } from './scenario-plan.js';
18
20
  import { resolveEnvironment, isLocalUrl } from './environment.js';
19
21
  import { readDevAddress } from './dev-address.js';
20
22
  import { createDevAgentRunner } from './dev-agent-runner.js';
@@ -236,7 +238,7 @@ export const scenarioRun = pikkuSessionlessFunc({
236
238
  apiUrl: env.apiUrl,
237
239
  appUrl: env.appUrl,
238
240
  });
239
- scenarioService.setRunSurface(runSurface);
241
+ scenarioService.setRunSurface(runSurface, strict);
240
242
  // Scenario steps run here, not on the target — everything they touch of the
241
243
  // app goes over HTTP through an actor, which is what `guardRpc` below
242
244
  // enforces. `actor.converse` is the exception, and the reason this is not
@@ -354,11 +356,18 @@ export const scenarioRun = pikkuSessionlessFunc({
354
356
  const runStore = new FileScenarioRunStore({ dir: captureDir });
355
357
  const startedAtIso = new Date().toISOString();
356
358
  try {
359
+ const selection = {
360
+ ...(split(flows) ? { flows: split(flows) } : {}),
361
+ ...(split(features) ? { features: split(features) } : {}),
362
+ ...(split(tags) ? { tags: split(tags) } : {}),
363
+ ...(split(excludeTags) ? { excludeTags: split(excludeTags) } : {}),
364
+ };
357
365
  await runStore.start({
358
366
  runId: captureRunId,
359
367
  environment,
360
368
  surface: runSurface,
361
369
  status: 'running',
370
+ ...(Object.keys(selection).length > 0 ? { selection } : {}),
362
371
  startedAt: startedAtIso,
363
372
  results: [],
364
373
  skipped,
@@ -371,6 +380,11 @@ export const scenarioRun = pikkuSessionlessFunc({
371
380
  * is the formatter's job.
372
381
  */
373
382
  const readRunSteps = async (service, runId, flowName) => {
383
+ // Taken from the run rather than derived from the ladder: the video
384
+ // clock starts when an actor's window opens, which is somewhere after
385
+ // step one, and every step that never touched a browser burns scenario
386
+ // time while the recording sits still.
387
+ const videoOffsets = scenarioService.takeStepVideoOffsets(runId);
374
388
  const prose = collectScenarioStepProse(state.workflows?.meta?.[flowName], functionsMeta, state.personas?.definitions ?? []);
375
389
  const steps = (await service.getRunSteps(runId)).map((step) => ({
376
390
  stepName: step.stepName,
@@ -383,6 +397,7 @@ export const scenarioRun = pikkuSessionlessFunc({
383
397
  expected: step.error?.expected,
384
398
  input: step.data,
385
399
  stepFunc: step.rpcName,
400
+ video: videoOffsets.get(step.stepName),
386
401
  }));
387
402
  return {
388
403
  rows: scenarioStepRows(steps, prose),
@@ -427,33 +442,29 @@ export const scenarioRun = pikkuSessionlessFunc({
427
442
  };
428
443
  const hookFailures = [];
429
444
  /**
430
- * What a result carries beyond its own outcome: which registration ran,
431
- * which feature grouped it, and the tags it was selected by. Snapshotted
432
- * into the record because a run read back next week is describing a suite
445
+ * Which registration ran and which feature grouped it, snapshotted into
446
+ * the record because a run read back next week is describing a suite
433
447
  * whose source has moved on.
434
448
  */
435
- const identify = (result, scenarioName, feature) => {
436
- const tags = state.workflows?.meta?.[scenarioName]?.tags;
437
- return {
438
- ...result,
439
- scenarioName,
440
- ...(feature ? { feature } : {}),
441
- ...(tags?.length ? { tags } : {}),
442
- };
443
- };
444
- const runEntry = async (label, scenarioName, data, feature) => {
449
+ const identityOf = (scenarioName, group) => ({
450
+ scenarioName,
451
+ featureId: group?.featureId,
452
+ featureName: group?.featureName,
453
+ tags: state.workflows?.meta?.[scenarioName]?.tags,
454
+ });
455
+ const runEntry = async (label, scenarioName, data, identity) => {
445
456
  const startedAt = Date.now();
446
457
  if (databaseBaseline) {
447
458
  try {
448
459
  await databaseBaseline.restore();
449
460
  }
450
461
  catch (e) {
451
- const result = identify({
462
+ const result = identifyScenarioResult({
452
463
  name: label,
453
464
  status: 'failed',
454
465
  durationMs: Date.now() - startedAt,
455
466
  error: `database reset failed: ${e?.message ?? e}`,
456
- }, scenarioName, feature);
467
+ }, identity);
457
468
  results.push(result);
458
469
  await runStore.recordScenario(captureRunId, result);
459
470
  return;
@@ -534,7 +545,7 @@ export const scenarioRun = pikkuSessionlessFunc({
534
545
  // Told here, acted on at the next scenario's reset — that is what closes
535
546
  // these windows and finalises the video this outcome decides the fate of.
536
547
  browserLifecycle.endScenario(result.status);
537
- Object.assign(result, identify(result, scenarioName, feature));
548
+ Object.assign(result, identifyScenarioResult(result, identity));
538
549
  await runStore.recordScenario(captureRunId, result);
539
550
  if (coverageActive) {
540
551
  const report = await invokeCoverage('pikkuScenarioTakeLiveCoverage');
@@ -582,19 +593,19 @@ export const scenarioRun = pikkuSessionlessFunc({
582
593
  // Setup failed, so nothing in the group ran. Reporting them as failed
583
594
  // rather than skipped is the honest reading: they did not pass.
584
595
  for (const entry of group.entries) {
585
- const result = identify({
596
+ const result = identifyScenarioResult({
586
597
  name: label(entry),
587
598
  status: 'failed',
588
599
  durationMs: 0,
589
600
  error: `${groupName ? `feature '${groupName}' ` : ''}${beforeStage} failed: ${beforeError?.message ?? beforeError}`,
590
- }, entry.scenarioName, groupName);
601
+ }, identityOf(entry.scenarioName, group));
591
602
  results.push(result);
592
603
  await runStore.recordScenario(captureRunId, result);
593
604
  }
594
605
  }
595
606
  else {
596
607
  for (const entry of group.entries) {
597
- await runEntry(label(entry), entry.scenarioName, entry.data, groupName);
608
+ await runEntry(label(entry), entry.scenarioName, entry.data, identityOf(entry.scenarioName, group));
598
609
  }
599
610
  }
600
611
  }
@@ -699,3 +710,156 @@ export const scenarioRun = pikkuSessionlessFunc({
699
710
  }
700
711
  },
701
712
  });
713
+ /**
714
+ * `pikku scenario guide` — the suite, written out as markdown.
715
+ *
716
+ * Structure comes from the registry (which features exist, what their scenarios
717
+ * are called), and evidence from the latest run record (what the steps actually
718
+ * said, and what they filed). Editorial prose comes from the project's own
719
+ * `docs/` sources, which cite a feature by leaving the marker pair where its
720
+ * block belongs; this merges the three and writes one markdown file per source.
721
+ * It renders nothing, resolves no asset URLs and calls no model: the writing is
722
+ * somebody else's concern, and this is the compiler that keeps it honest.
723
+ */
724
+ export const scenarioGuide = pikkuSessionlessFunc({
725
+ func: async ({ logger, config, getInspectorState }, { docs = 'docs', output, runId, allowUndocumented = false, artifactBase }) => {
726
+ const state = await getInspectorState(false, false, false, true);
727
+ const outDir = resolve(config.rootDir, config.outDir);
728
+ await loadScenarioBootstrap(outDir);
729
+ const { features: registeredFeatures } = collectRegisteredWirings();
730
+ const runsDir = join(outDir, 'scenario-runs');
731
+ const runStore = new FileScenarioRunStore({ dir: runsDir });
732
+ const latest = runId ?? (await runStore.list({ limit: 1 }))[0]?.runId;
733
+ const record = latest ? await runStore.get(latest) : undefined;
734
+ if (!record) {
735
+ logger.error(runId
736
+ ? `No run '${runId}' under ${runsDir}.`
737
+ : `No scenario run under ${runsDir} — run \`pikku scenario run <environment> --screenshots\` first, since a guide is written out of what a run recorded.`);
738
+ process.exitCode = 1;
739
+ return;
740
+ }
741
+ if (record.status !== 'passed') {
742
+ logger.error(`Run '${record.runId}' is ${record.status}. A guide is a claim that the product does what the page says, so it is only ever written out of a run that passed.`);
743
+ process.exitCode = 1;
744
+ return;
745
+ }
746
+ if (record.selection) {
747
+ const narrowed = Object.entries(record.selection)
748
+ .map(([flag, values]) => `--${flag} ${values.join(',')}`)
749
+ .join(' ');
750
+ logger.error(`Run '${record.runId}' was narrowed (${narrowed}), so it is missing scenarios the suite has. Guide pages would be written as though those flows do not exist — run the whole suite, or pass --run-id for one that was.`);
751
+ process.exitCode = 1;
752
+ return;
753
+ }
754
+ const workflowsMeta = state.workflows?.meta ?? {};
755
+ const features = [...registeredFeatures].map(([id, feature]) => {
756
+ const name = feature.name ?? id;
757
+ return {
758
+ id,
759
+ name,
760
+ ...(feature.description ? { description: feature.description } : {}),
761
+ document: feature.document !== false,
762
+ scenarios: record.results
763
+ // By id, never by the display name: a title is rewritten freely,
764
+ // and two features are allowed to share one.
765
+ .filter((result) => result.featureId === id ||
766
+ (result.featureId === undefined && result.feature === name))
767
+ .map((result) => {
768
+ const meta = result.scenarioName
769
+ ? workflowsMeta[result.scenarioName]
770
+ : undefined;
771
+ return {
772
+ name: result.scenarioName ?? result.name,
773
+ title: meta?.title ?? result.name,
774
+ ...(meta?.description ? { description: meta.description } : {}),
775
+ steps: (result.steps ?? []).map((step) => guideStep(step.sentence)),
776
+ screenshots: (result.artifacts ?? [])
777
+ .filter((artifact) => artifact.kind === 'screenshot')
778
+ .map((artifact) => ({
779
+ ...(artifact.id ? { id: artifact.id } : {}),
780
+ ...(artifact.name ? { name: artifact.name } : {}),
781
+ path: artifact.path,
782
+ })),
783
+ videos: (result.artifacts ?? [])
784
+ .filter((artifact) => artifact.kind === 'video')
785
+ .map((artifact) => ({
786
+ ...(artifact.id ? { id: artifact.id } : {}),
787
+ ...(artifact.actor ? { actor: artifact.actor } : {}),
788
+ path: artifact.path,
789
+ })),
790
+ };
791
+ }),
792
+ };
793
+ });
794
+ const docsDir = resolve(config.rootDir, docs);
795
+ const sources = (await glob('**/*.md', { cwd: docsDir, onlyFiles: true })).sort();
796
+ const pages = [];
797
+ for (const source of sources) {
798
+ pages.push(parseGuidePage(source, readFileSync(join(docsDir, source), 'utf-8')));
799
+ }
800
+ const lockPath = join(docsDir, '.guide.lock');
801
+ const lock = existsSync(lockPath)
802
+ ? parseGuideLock(readFileSync(lockPath, 'utf-8'))
803
+ : {};
804
+ const coverage = checkGuideCoverage(features, pages, lock);
805
+ for (const { path, featureId } of coverage.unknown) {
806
+ logger.error(`${join(docs, path)} cites '${featureId}', which is not a registered feature — a page describing something that no longer exists.`);
807
+ }
808
+ for (const { path, featureId } of coverage.optedOut) {
809
+ logger.error(`${join(docs, path)} cites '${featureId}', which declares \`document: false\`.`);
810
+ }
811
+ for (const { path, featureId } of coverage.figureless) {
812
+ logger.warn(`${join(docs, path)} cites '${featureId}', whose run filed no screenshot — the block renders empty. Take one with \`actor.screenshot(...)\` in a scenario the feature owns.`);
813
+ }
814
+ for (const { path, featureId, locked, current } of coverage.stale) {
815
+ logger.warn(`${join(docs, path)} was written against '${featureId}' at ${locked}, which is now ${current} — the flow moved, so re-read the prose around that block.`);
816
+ }
817
+ for (const featureId of coverage.missing) {
818
+ const message = `Feature '${featureId}' is cited by no page. Place \`<!-- pikku:guide feature=${featureId} -->\` and \`<!-- /pikku:guide -->\` in a page under ${docs}/, or set \`document: false\` on the feature.`;
819
+ if (allowUndocumented) {
820
+ logger.warn(message);
821
+ }
822
+ else {
823
+ logger.error(message);
824
+ }
825
+ }
826
+ // A compiler that has found an error does not emit. Undocumented features
827
+ // are the one failure that can be downgraded, because a suite mid-way
828
+ // through being written still wants its pages built.
829
+ if (coverage.unknown.length > 0 ||
830
+ coverage.optedOut.length > 0 ||
831
+ (!allowUndocumented && coverage.missing.length > 0)) {
832
+ process.exitCode = 1;
833
+ return;
834
+ }
835
+ const byId = new Map(features.map((feature) => [feature.id, feature]));
836
+ const outputDir = output
837
+ ? resolve(config.rootDir, output)
838
+ : join(outDir, 'guide');
839
+ const artifactRoot = join(runsDir, record.runId);
840
+ for (const page of pages) {
841
+ const target = join(outputDir, page.path);
842
+ // Relative, forward-slashed and computed per page: a guide is markdown
843
+ // with ordinary image refs, and whoever consumes it rewrites the paths.
844
+ // `artifactBase` replaces it with one prefix for every page, for a host
845
+ // that serves the artifacts at a fixed address rather than beside the
846
+ // markdown — and it stays as given, since only the caller knows whether
847
+ // it is a path, a route or an origin.
848
+ const base = artifactBase
849
+ ? artifactBase.endsWith('/')
850
+ ? artifactBase
851
+ : `${artifactBase}/`
852
+ : `${relative(dirname(target), artifactRoot).split(sep).join('/')}/`;
853
+ const markdown = renderGuidePage(page, byId, base);
854
+ mkdirSync(dirname(target), { recursive: true });
855
+ writeFileSync(target, markdown);
856
+ }
857
+ writeFileSync(lockPath, renderGuideLock(features, coverage.cited));
858
+ logger.info(`${pages.length} page(s) → ${outputDir} (run ${record.runId}, ${coverage.cited.length} documented feature(s))`);
859
+ for (const feature of features) {
860
+ if (feature.document) {
861
+ logger.debug(` ${feature.id} ${featureEvidence(feature)}`);
862
+ }
863
+ }
864
+ },
865
+ });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The frontmatter a skill adds to become a subagent as well as a skill.
3
+ *
4
+ * A skill is instructions a session reads; an agent is a child process a host
5
+ * dispatches and then judges. The difference is entirely the gate, so the gate is
6
+ * the only thing a skill has to declare — everything else about the agent is
7
+ * derived from the skill it already is.
8
+ */
9
+ export interface SkillAgentSpec {
10
+ tools?: string;
11
+ timeoutMs?: number;
12
+ acceptance?: Record<string, unknown>;
13
+ }
14
+ interface SkillFrontmatter {
15
+ name?: string;
16
+ description?: string;
17
+ agent?: SkillAgentSpec;
18
+ }
19
+ export declare function parseSkill(content: string): {
20
+ frontmatter: SkillFrontmatter;
21
+ body: string;
22
+ } | null;
23
+ /**
24
+ * Write out an agent for every installed skill that declares one.
25
+ *
26
+ * Skills without an `agent:` block are not projected: a reference skill has no
27
+ * post-condition a host could check, and an agent whose acceptance is `none` is a
28
+ * subagent dispatched to read something the caller could have read itself.
29
+ */
30
+ export declare function installSkillAgents(skills: string[], agentRoot: string, skillDir: string, update: boolean, existing: (path: string) => boolean, read: (path: string) => Promise<string | null>,
31
+ /**
32
+ * Host extensions every projected agent loads, as pi resolves them.
33
+ *
34
+ * They are passed in rather than declared by the skill because they belong to
35
+ * whoever is running the agent, not to the instructions it follows: a sandbox
36
+ * fences its writers and routes their model, and the same skill run on a
37
+ * developer's own checkout needs neither.
38
+ */
39
+ extensions?: string[]): Promise<{
40
+ written: string[];
41
+ skipped: string[];
42
+ }>;
43
+ export {};
@@ -0,0 +1,135 @@
1
+ import { mkdir, writeFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ import { parse, stringify } from 'yaml';
4
+ /**
5
+ * The tools that can change the workspace, and so decide the agent's role.
6
+ *
7
+ * `bash` counts because a shell is a superset of the rest: `sed -i`, `echo >` and
8
+ * `git checkout` all mutate without touching a file tool.
9
+ */
10
+ const MUTATING_TOOLS = [
11
+ 'bash',
12
+ 'shell',
13
+ 'write',
14
+ 'edit',
15
+ 'write_files',
16
+ 'edit_files',
17
+ 'patch',
18
+ ];
19
+ const DEFAULT_TOOLS = 'read, write, edit, bash, grep';
20
+ /**
21
+ * The role is derived from the tools rather than declared beside them.
22
+ *
23
+ * It is what acceptance is inferred from, so a skill that declares the two
24
+ * independently can declare them inconsistently — a read-only role holding `write`
25
+ * is an agent the host judges by rules its tools do not match.
26
+ */
27
+ function acceptanceRole(tools) {
28
+ const declared = tools.split(',').map((tool) => tool.trim());
29
+ return declared.some((tool) => MUTATING_TOOLS.includes(tool))
30
+ ? 'writer'
31
+ : 'read-only';
32
+ }
33
+ const FRONTMATTER = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
34
+ export function parseSkill(content) {
35
+ const match = content.match(FRONTMATTER);
36
+ if (!match)
37
+ return null;
38
+ try {
39
+ return { frontmatter: parse(match[1]) ?? {}, body: match[2] ?? '' };
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ }
45
+ /**
46
+ * The first paragraph of the skill's own description, as the one line a host shows
47
+ * when it is choosing between agents.
48
+ *
49
+ * A skill description is written for a trigger matcher and carries TRIGGER/DO NOT
50
+ * TRIGGER clauses that mean nothing to a dispatcher, so they are cut rather than
51
+ * shipped as noise into every agent listing.
52
+ */
53
+ function agentDescription(description) {
54
+ return description
55
+ .split(/\bTRIGGER when\b|\bDO NOT TRIGGER\b/)[0]
56
+ .replace(/\s+/g, ' ')
57
+ .trim()
58
+ .replace(/[,;]\s*$/, '');
59
+ }
60
+ /**
61
+ * pi reads `acceptance` as a JSON string on one line, and ignores any frontmatter key
62
+ * it does not know — a post-condition under a key pi has no reader for runs nowhere and
63
+ * says nothing about it. Emitting through the one key pi acts on is what makes the gate
64
+ * real rather than decorative.
65
+ */
66
+ function renderAgent(name, frontmatter, skillDir, extensions) {
67
+ const agent = frontmatter.agent;
68
+ const tools = agent.tools ?? DEFAULT_TOOLS;
69
+ const lines = [
70
+ '---',
71
+ `name: ${name}`,
72
+ `description: ${JSON.stringify(agentDescription(frontmatter.description ?? name))}`,
73
+ `tools: ${tools}`,
74
+ ];
75
+ if (extensions.length > 0) {
76
+ lines.push(`extensions: ${extensions.join(', ')}`);
77
+ }
78
+ if (agent.acceptance) {
79
+ lines.push('acceptance:');
80
+ lines.push(stringify(agent.acceptance, { indent: 2 })
81
+ .trimEnd()
82
+ .split('\n')
83
+ .map((line) => ` ${line}`)
84
+ .join('\n'));
85
+ }
86
+ lines.push(`acceptanceRole: ${acceptanceRole(tools)}`);
87
+ if (agent.timeoutMs) {
88
+ lines.push(`timeoutMs: ${agent.timeoutMs}`);
89
+ }
90
+ lines.push('---', '');
91
+ const verify = (agent.acceptance?.verify ?? []);
92
+ lines.push(`Your instructions are \`${skillDir}/${name}/SKILL.md\`. Read it in full before you act, and follow it as written rather than improvising around it.`, '', 'Your caller dispatched you because this stage of the pipeline has a gate it can check, not because it wanted prose back. So the gate is the job.', '');
93
+ if (verify.length > 0) {
94
+ lines.push('You are not done until every one of these exits 0, and you have run them yourself:', '', ...verify.map((v) => `- \`${v.command}\``), '', 'A refusal is the gate working. Read what it says is wrong, fix that, and run it again — do not stop at the first one, and do not report success on a command you have not seen pass.', '');
95
+ }
96
+ lines.push('Report what you ran and what it said, what you changed, and anything in the skill that was wrong, missing or ambiguous. If you could not pass the gate, say so outright and say where you got stuck — a truthful failure is worth more to your caller than a pass it cannot trust.', '');
97
+ return lines.join('\n');
98
+ }
99
+ /**
100
+ * Write out an agent for every installed skill that declares one.
101
+ *
102
+ * Skills without an `agent:` block are not projected: a reference skill has no
103
+ * post-condition a host could check, and an agent whose acceptance is `none` is a
104
+ * subagent dispatched to read something the caller could have read itself.
105
+ */
106
+ export async function installSkillAgents(skills, agentRoot, skillDir, update, existing, read,
107
+ /**
108
+ * Host extensions every projected agent loads, as pi resolves them.
109
+ *
110
+ * They are passed in rather than declared by the skill because they belong to
111
+ * whoever is running the agent, not to the instructions it follows: a sandbox
112
+ * fences its writers and routes their model, and the same skill run on a
113
+ * developer's own checkout needs neither.
114
+ */
115
+ extensions = []) {
116
+ const written = [];
117
+ const skipped = [];
118
+ for (const name of skills) {
119
+ const content = await read(`${name}/SKILL.md`);
120
+ if (content === null)
121
+ continue;
122
+ const parsed = parseSkill(content);
123
+ if (!parsed?.frontmatter.agent)
124
+ continue;
125
+ const target = join(agentRoot, `${name}.md`);
126
+ if (existing(target) && !update) {
127
+ skipped.push(name);
128
+ continue;
129
+ }
130
+ await mkdir(agentRoot, { recursive: true });
131
+ await writeFile(target, renderAgent(name, parsed.frontmatter, skillDir, extensions), 'utf-8');
132
+ written.push(name);
133
+ }
134
+ return { written, skipped };
135
+ }
@@ -12,6 +12,9 @@ export declare const pikkuSkillsInstall: import("#pikku/function").PikkuFunction
12
12
  fabric?: boolean;
13
13
  client?: boolean;
14
14
  update?: boolean;
15
+ agentExtensions?: string;
16
+ agentDir?: string;
17
+ agentSkillDir?: string;
15
18
  }, void, "rpc" | "session", import("#pikku/function").PikkuFunction<{
16
19
  agent?: string;
17
20
  only?: string;
@@ -19,6 +22,9 @@ export declare const pikkuSkillsInstall: import("#pikku/function").PikkuFunction
19
22
  fabric?: boolean;
20
23
  client?: boolean;
21
24
  update?: boolean;
25
+ agentExtensions?: string;
26
+ agentDir?: string;
27
+ agentSkillDir?: string;
22
28
  }, void, "rpc" | "session", import("#pikku/function").WiredServices> | import("#pikku/function").PikkuFunctionSessionless<{
23
29
  agent?: string;
24
30
  only?: string;
@@ -26,4 +32,7 @@ export declare const pikkuSkillsInstall: import("#pikku/function").PikkuFunction
26
32
  fabric?: boolean;
27
33
  client?: boolean;
28
34
  update?: boolean;
35
+ agentExtensions?: string;
36
+ agentDir?: string;
37
+ agentSkillDir?: string;
29
38
  }, void, "rpc" | "session", import("#pikku/function").WiredServices, unknown>, undefined, undefined>;
@@ -1,7 +1,8 @@
1
1
  import { readdir, mkdir, writeFile } from 'fs/promises';
2
2
  import { existsSync } from 'fs';
3
- import { dirname, join, sep } from 'path';
3
+ import { dirname, isAbsolute, join, sep } from 'path';
4
4
  import { listSkillFiles, listSkillNames, readSkillFile } from '@pikku/skills';
5
+ import { installSkillAgents } from './skill-agents.js';
5
6
  import { pikkuSessionlessFunc } from '#pikku/function';
6
7
  /**
7
8
  * Write one skill's files out to `dest`, reading through @pikku/skills rather
@@ -33,6 +34,17 @@ const AGENT_SKILL_DIRS = {
33
34
  opencode: join('.opencode', 'skills'),
34
35
  pi: join('.pi', 'skills'),
35
36
  };
37
+ /**
38
+ * Where a harness that dispatches subagents reads them from.
39
+ *
40
+ * Only harnesses that can enforce a post-condition are listed. Installing an agent
41
+ * for a harness that cannot run its `acceptance` gate ships the instruction to verify
42
+ * with nothing that verifies, which reads as stricter than the skill it came from
43
+ * while being exactly as strict.
44
+ */
45
+ const AGENT_DIRS = {
46
+ pi: join('.pi', 'agents'),
47
+ };
36
48
  function parseInstallGroups(frontmatter) {
37
49
  const inline = frontmatter.match(/^\s*installGroups\s*:\s*\[([^\]]*)\]/m);
38
50
  if (inline) {
@@ -118,7 +130,7 @@ export const pikkuSkillsList = pikkuSessionlessFunc({
118
130
  },
119
131
  });
120
132
  export const pikkuSkillsInstall = pikkuSessionlessFunc({
121
- func: async ({ logger }, { agent = 'claude', only, core = false, fabric = false, client = false, update = false, }) => {
133
+ func: async ({ logger }, { agent = 'claude', only, core = false, fabric = false, client = false, update = false, agentExtensions, agentDir, agentSkillDir, }) => {
122
134
  const supportedAgents = Object.keys(AGENT_SKILL_DIRS);
123
135
  if (!supportedAgents.includes(agent)) {
124
136
  logger.error(`Agent "${agent}" is not yet supported. Supported: ${supportedAgents.join(', ')}. ` +
@@ -156,5 +168,20 @@ export const pikkuSkillsInstall = pikkuSessionlessFunc({
156
168
  logger.info(`Installed ${installed} skill(s) into ${destLabel}${skipped > 0
157
169
  ? ` (skipped ${skipped} already present — pass --update to overwrite)`
158
170
  : ''}`);
171
+ const agentRelative = agentDir ?? AGENT_DIRS[agent];
172
+ if (!agentRelative)
173
+ return;
174
+ const projected = await installSkillAgents(wanted, isAbsolute(agentRelative)
175
+ ? agentRelative
176
+ : join(process.cwd(), agentRelative), agentSkillDir ?? AGENT_SKILL_DIRS[agent].split(sep).join('/'), update, existsSync, readSkillFile, agentExtensions
177
+ ?.split(',')
178
+ .map((path) => path.trim())
179
+ .filter(Boolean));
180
+ if (projected.written.length === 0 && projected.skipped.length === 0)
181
+ return;
182
+ const agentLabel = `${agentRelative.split(sep).join('/')}/`;
183
+ logger.info(`Projected ${projected.written.length} agent(s) into ${agentLabel}${projected.skipped.length > 0
184
+ ? ` (skipped ${projected.skipped.length} already present — pass --update to overwrite)`
185
+ : ''}`);
159
186
  },
160
187
  });
@@ -27,6 +27,12 @@ export declare class PostgresIntrospector implements DbIntrospector {
27
27
  */
28
28
  private getCheckEnums;
29
29
  getForeignKeys(table: string): Promise<ForeignKeyInfo[]>;
30
+ /**
31
+ * `pk_cols` is fenced with `AS MATERIALIZED` deliberately. Referenced once,
32
+ * Postgres 12+ inlines it, and the constraint views it joins are then
33
+ * re-derived per candidate row — quadratic in the number of tables, minutes
34
+ * instead of milliseconds on a large schema.
35
+ */
30
36
  getAllColumns(): Promise<Map<string, ColumnInfo[]>>;
31
37
  getAllForeignKeys(): Promise<Map<string, ForeignKeyInfo[]>>;
32
38
  listEnums(): Promise<EnumInfo[]>;
@@ -167,8 +167,14 @@ export class PostgresIntrospector {
167
167
  foreignColumn: r.foreign_column_name,
168
168
  }));
169
169
  }
170
+ /**
171
+ * `pk_cols` is fenced with `AS MATERIALIZED` deliberately. Referenced once,
172
+ * Postgres 12+ inlines it, and the constraint views it joins are then
173
+ * re-derived per candidate row — quadratic in the number of tables, minutes
174
+ * instead of milliseconds on a large schema.
175
+ */
170
176
  async getAllColumns() {
171
- const result = await this.client.query(`WITH pk_cols AS (
177
+ const result = await this.client.query(`WITH pk_cols AS MATERIALIZED (
172
178
  SELECT tc.table_schema, tc.table_name, kcu.column_name
173
179
  FROM information_schema.table_constraints tc
174
180
  JOIN information_schema.key_column_usage kcu
@@ -16,4 +16,4 @@ export declare const renderKnowledgePlanDefer: (_services: unknown, { ok, messag
16
16
  * rendered as a picker: this is the fallback every harness has, and one that can do
17
17
  * better reads the same answer as JSON.
18
18
  */
19
- export declare const renderKnowledgeReconcile: (_services: unknown, { kind, reason, note, hold, notes, question }: KnowledgeReconcileResult) => void;
19
+ export declare const renderKnowledgeReconcile: (_services: unknown, { kind, reason, note, hold, notes, question, required, satisfied, }: KnowledgeReconcileResult) => void;
@@ -1,9 +1,15 @@
1
1
  import { added, changed, dim, removed } from '../../fabric/lib/output.js';
2
+ const ORPHANS_SHOWN = 10;
2
3
  export const renderKnowledgeValidate = (_services, { ok, notes, findings }) => {
3
4
  const errors = findings.filter((f) => f.severity === 'error');
4
5
  const warns = findings.filter((f) => f.severity === 'warn');
5
6
  const infos = findings.filter((f) => f.severity === 'info');
6
- for (const finding of [...errors, ...warns, ...infos]) {
7
+ // Orphans are one finding each so a consumer can act on one at a time, but a
8
+ // project with no knowledge base yet reports every function it has — printing
9
+ // those in full buries the findings somebody can actually fix.
10
+ const orphans = infos.filter((f) => f.id.startsWith('knowledge-orphan-'));
11
+ const rest = infos.filter((f) => !f.id.startsWith('knowledge-orphan-'));
12
+ for (const finding of [...errors, ...warns, ...rest]) {
7
13
  const icon = finding.severity === 'error'
8
14
  ? removed('✗')
9
15
  : finding.severity === 'warn'
@@ -13,6 +19,18 @@ export const renderKnowledgeValidate = (_services, { ok, notes, findings }) => {
13
19
  console.log(` ${dim('fix:')} ${finding.fixHint}`);
14
20
  console.log();
15
21
  }
22
+ if (orphans.length) {
23
+ const shown = orphans.slice(0, ORPHANS_SHOWN);
24
+ console.log(`${dim('ℹ')} ${orphans.length} thing${orphans.length !== 1 ? 's' : ''} in the code that no note describes`);
25
+ for (const orphan of shown) {
26
+ console.log(` ${dim(orphan.id.slice('knowledge-orphan-'.length))}`);
27
+ }
28
+ if (orphans.length > shown.length) {
29
+ console.log(` ${dim(`… and ${orphans.length - shown.length} more`)}`);
30
+ }
31
+ console.log(` ${dim('fix:')} ${shown[0].fixHint}`);
32
+ console.log();
33
+ }
16
34
  const counts = [dim(`${notes} note${notes !== 1 ? 's' : ''}`)];
17
35
  if (errors.length) {
18
36
  counts.push(removed(`${errors.length} error${errors.length !== 1 ? 's' : ''}`));
@@ -148,7 +166,12 @@ export const renderKnowledgePlanDefer = (_services, { ok, message }) => {
148
166
  * rendered as a picker: this is the fallback every harness has, and one that can do
149
167
  * better reads the same answer as JSON.
150
168
  */
151
- export const renderKnowledgeReconcile = (_services, { kind, reason, note, hold, notes, question }) => {
169
+ export const renderKnowledgeReconcile = (_services, { kind, reason, note, hold, notes, question, required, satisfied, }) => {
170
+ if (required && !satisfied) {
171
+ process.exitCode = 1;
172
+ console.log(`${removed('✗')} next is ${kind}, and this check requires ${required.join(' or ')}`);
173
+ console.log();
174
+ }
152
175
  if (kind === 'idle') {
153
176
  console.log(`${dim('=')} ${dim(reason)}`);
154
177
  return;