blun-king-cli 9.0.0 → 9.1.0

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 (238) hide show
  1. package/LIESMICH.txt +32 -13
  2. package/README.md +39 -20
  3. package/bin/blun.js +248 -160
  4. package/bin/core-bootstrap.js +47 -0
  5. package/bin/king.js +277 -1
  6. package/bin/launcher-mode.js +15 -3
  7. package/bin/launcher-runtime.js +293 -0
  8. package/bin/plugin-bootstrap.js +11 -4
  9. package/bin/private-paths.js +1577 -10
  10. package/bin/update-lease.js +399 -0
  11. package/bin/update-notice.js +1245 -0
  12. package/blun.mjs +26606 -25239
  13. package/dist-web/assets/{CodeBlockNode-Bsvp6ZEy.js → CodeBlockNode-CXnTLMNr.js} +1 -1
  14. package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-ta7hLjia.js → CodeBlockShell.vue_vue_type_style_index_0_lang-CCF8Ntnq.js} +1 -1
  15. package/dist-web/assets/{DesignSystemView-BmDDe28A.js → DesignSystemView-BbMOu-XO.js} +1 -1
  16. package/dist-web/assets/{Tooltip-nEEfaYVY.js → Tooltip-CFQEzqC-.js} +1 -1
  17. package/dist-web/assets/abnfDiagram-N423BO3Z-CsByy-GW.js +1 -0
  18. package/dist-web/assets/abnfDiagram-N423BO3Z-Z1APZG5m.js +1 -0
  19. package/dist-web/assets/arc-C-LXCaja.js +1 -0
  20. package/dist-web/assets/arc-C4VyWloT.js +1 -0
  21. package/dist-web/assets/architectureDiagram-T3A2C74G-DZq4L18e.js +36 -0
  22. package/dist-web/assets/architectureDiagram-T3A2C74G-DpddbUcM.js +36 -0
  23. package/dist-web/assets/blockDiagram-VBNYF7ZC-BV4sIf98.js +132 -0
  24. package/dist-web/assets/blockDiagram-VBNYF7ZC-BqccOSMr.js +132 -0
  25. package/dist-web/assets/{c4Diagram-AAUBKEIU-DtzacMnD.js → c4Diagram-5PPSVZJV-0MRkZlgP.js} +1 -1
  26. package/dist-web/assets/{c4Diagram-AAUBKEIU-D1VdMx8d.js → c4Diagram-5PPSVZJV-OHAcJrTy.js} +1 -1
  27. package/dist-web/assets/channel-B2VOQOI-.js +1 -0
  28. package/dist-web/assets/channel-DKjwM05G.js +1 -0
  29. package/dist-web/assets/{chunk-ND2GUHAM-DJoY9wNJ.js → chunk-2GRJ4B5K-DqJHs8Fw.js} +1 -1
  30. package/dist-web/assets/{chunk-ND2GUHAM-C4-rwdcv.js → chunk-2GRJ4B5K-oO6nqeLA.js} +1 -1
  31. package/dist-web/assets/{chunk-QZHKN3VN-C7YGaShq.js → chunk-2Q5K7J3B-B1nCd91o.js} +1 -1
  32. package/dist-web/assets/{chunk-QZHKN3VN-_Rz9_NuS.js → chunk-2Q5K7J3B-BC0Hsfqy.js} +1 -1
  33. package/dist-web/assets/chunk-5RXB4S5H-BfZa4yDH.js +231 -0
  34. package/dist-web/assets/chunk-5RXB4S5H-neG70kZx.js +231 -0
  35. package/dist-web/assets/{chunk-FMBD7UC4-DlLNDUx6.js → chunk-5VM5RSS4-l7biGqeH.js} +1 -1
  36. package/dist-web/assets/{chunk-FMBD7UC4-B2zs_Y-d.js → chunk-5VM5RSS4-u00AcpiJ.js} +1 -1
  37. package/dist-web/assets/chunk-6Q2QTUOP-BO-F11Gs.js +88 -0
  38. package/dist-web/assets/chunk-6Q2QTUOP-RUVRPBzG.js +88 -0
  39. package/dist-web/assets/{chunk-727SXJPM-Dn5wkDO_.js → chunk-GF5L2VYU-Ba-86KTd.js} +5 -5
  40. package/dist-web/assets/{chunk-727SXJPM-Dk2Xa3bA.js → chunk-GF5L2VYU-BtC69fw0.js} +5 -5
  41. package/dist-web/assets/{chunk-4BX2VUAB-DxZViaHS.js → chunk-JWPE2WC7-DIOubEqr.js} +1 -1
  42. package/dist-web/assets/{chunk-4BX2VUAB-WqrE2gaw.js → chunk-JWPE2WC7-u8Pcfhwh.js} +1 -1
  43. package/dist-web/assets/{chunk-2J33WTMH-Dm_s0_4f.js → chunk-KBJHAD2P-DDUeH_Ba.js} +1 -1
  44. package/dist-web/assets/{chunk-2J33WTMH-w4sdiKFO.js → chunk-KBJHAD2P-DS3BL64y.js} +1 -1
  45. package/dist-web/assets/chunk-RYQCIY6F-B9gFMVbS.js +1 -0
  46. package/dist-web/assets/chunk-RYQCIY6F-xg3EO0le.js +1 -0
  47. package/dist-web/assets/{chunk-55IACEB6-B5dE1-Um.js → chunk-XXDRQBXY-B60H0rRC.js} +1 -1
  48. package/dist-web/assets/{chunk-55IACEB6-B12zGIFz.js → chunk-XXDRQBXY-Bn1Xz6TQ.js} +1 -1
  49. package/dist-web/assets/classDiagram-JCYQIIEL-BA1TjAxj.js +1 -0
  50. package/dist-web/assets/classDiagram-JCYQIIEL-BNU7Qn4k.js +1 -0
  51. package/dist-web/assets/classDiagram-v2-OCEON4UE-BA1TjAxj.js +1 -0
  52. package/dist-web/assets/classDiagram-v2-OCEON4UE-BNU7Qn4k.js +1 -0
  53. package/dist-web/assets/{cose-bilkent-S5V4N54A-Bi19n8m-.js → cose-bilkent-JH36ORCC-4Ybddg08.js} +1 -1
  54. package/dist-web/assets/{cose-bilkent-S5V4N54A-c8o8NFp5.js → cose-bilkent-JH36ORCC-DqigO3T4.js} +1 -1
  55. package/dist-web/assets/cynefin-VYW2F7L2-C-EfYT07.js +178 -0
  56. package/dist-web/assets/cynefin-VYW2F7L2-rpD93tQ-.js +178 -0
  57. package/dist-web/assets/cynefinDiagram-MW4NZA55-DWZPLJcD.js +62 -0
  58. package/dist-web/assets/cynefinDiagram-MW4NZA55-DbTqnkVJ.js +62 -0
  59. package/dist-web/assets/dagre-VZM6K2ZE-DcM8aEVY.js +4 -0
  60. package/dist-web/assets/dagre-VZM6K2ZE-M_HVNtqP.js +4 -0
  61. package/dist-web/assets/diagram-7IWD3JNH-DoNximNF.js +30 -0
  62. package/dist-web/assets/diagram-7IWD3JNH-UymgsBNa.js +30 -0
  63. package/dist-web/assets/{diagram-KO2AKTUF-CFQs13i5.js → diagram-B4RE2ZJO-BVA2EhnC.js} +3 -3
  64. package/dist-web/assets/{diagram-KO2AKTUF-agFIuPJW.js → diagram-B4RE2ZJO-BiYu5WPF.js} +3 -3
  65. package/dist-web/assets/{diagram-LMA3HP47-TMIXizxR.js → diagram-LBJQPF4R-BD3kkgOO.js} +2 -2
  66. package/dist-web/assets/{diagram-LMA3HP47-He5zlyMt.js → diagram-LBJQPF4R-m3a1Euj3.js} +2 -2
  67. package/dist-web/assets/diagram-Q27KOJAE-D1mYQ_cI.js +24 -0
  68. package/dist-web/assets/diagram-Q27KOJAE-hy_T73PK.js +24 -0
  69. package/dist-web/assets/diagram-UB23O5K3-BWkoQZ4A.js +41 -0
  70. package/dist-web/assets/diagram-UB23O5K3-BtX8u6HG.js +41 -0
  71. package/dist-web/assets/ebnfDiagram-BXEA7PRR-BKs9JUHW.js +1 -0
  72. package/dist-web/assets/ebnfDiagram-BXEA7PRR-CBAnFkRF.js +1 -0
  73. package/dist-web/assets/erDiagram-JOGREHBK-3vKcWsxm.js +85 -0
  74. package/dist-web/assets/erDiagram-JOGREHBK-BIpd6dTf.js +85 -0
  75. package/dist-web/assets/flowDiagram-UKHOOZJN-D9wBbHm2.js +156 -0
  76. package/dist-web/assets/flowDiagram-UKHOOZJN-Z83i2UWI.js +156 -0
  77. package/dist-web/assets/ganttDiagram-PKOTCBZU-Cd_dViF-.js +292 -0
  78. package/dist-web/assets/ganttDiagram-PKOTCBZU-DFrb_S1d.js +292 -0
  79. package/dist-web/assets/{gitGraphDiagram-PVQCEYII-BSDFfwdY.js → gitGraphDiagram-DS77QQ5N-BiEEtTo7.js} +3 -3
  80. package/dist-web/assets/{gitGraphDiagram-PVQCEYII-DMbTATyw.js → gitGraphDiagram-DS77QQ5N-Dp3Ar-hi.js} +3 -3
  81. package/dist-web/assets/{graph-BwjfAU3j.js → graph-CAUpWmoK.js} +1 -1
  82. package/dist-web/assets/{graph--OzhPTMs.js → graph-DOmOIIwC.js} +1 -1
  83. package/dist-web/assets/{index-W6VnSrkB.js → index-Ba1OvLjf.js} +2 -2
  84. package/dist-web/assets/{index-CkVgGDpx.js → index-CiPA8Kex.js} +4 -4
  85. package/dist-web/assets/{index-BzK4vJBu.js → index-DdEakFJM.js} +1 -1
  86. package/dist-web/assets/{index10-Czg_oF-W.js → index10-CNr4U-Nw.js} +1 -1
  87. package/dist-web/assets/{index11-BjzCZuCr.js → index11-Dto_yZNz.js} +1 -1
  88. package/dist-web/assets/{index5-DlVoNkCD.js → index5-B2PldaO2.js} +1 -1
  89. package/dist-web/assets/{index6-BxiXA2yo.js → index6-CBdZmmyx.js} +1 -1
  90. package/dist-web/assets/{index7-BPdza6d1.js → index7-Dz9oCsUF.js} +1 -1
  91. package/dist-web/assets/{index8-Q_XzGC1Z.js → index8-B6k25jtU.js} +2 -2
  92. package/dist-web/assets/{infoDiagram-5YYISTIA-2JF3XEdD.js → infoDiagram-6WML65LV-BSa2g04G.js} +1 -1
  93. package/dist-web/assets/{infoDiagram-5YYISTIA-UrQusrJq.js → infoDiagram-6WML65LV-D4ph2sIH.js} +1 -1
  94. package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-7RVUL-vB.js → ishikawaDiagram-WSZJBQD7-DjKjbMm9.js} +5 -5
  95. package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-C5OstRVV.js → ishikawaDiagram-WSZJBQD7-Hm36UOQe.js} +5 -5
  96. package/dist-web/assets/{journeyDiagram-JHISSGLW-B34fCMu9.js → journeyDiagram-NVQOT4AX-DttHDfPQ.js} +1 -1
  97. package/dist-web/assets/{journeyDiagram-JHISSGLW-yctdo4bX.js → journeyDiagram-NVQOT4AX-MvWWPNDV.js} +1 -1
  98. package/dist-web/assets/{kanban-definition-UN3LZRKU-BS4FBZsQ.js → kanban-definition-27J2QSJJ-C5gB-W0d.js} +7 -7
  99. package/dist-web/assets/{kanban-definition-UN3LZRKU-CxBCv27E.js → kanban-definition-27J2QSJJ-Cu-E_cXA.js} +5 -5
  100. package/dist-web/assets/layout-CvP6o9AK.js +1 -0
  101. package/dist-web/assets/layout-D-LzfAck.js +1 -0
  102. package/dist-web/assets/{linear-3mB6q2-g.js → linear-B3HWgQI2.js} +1 -1
  103. package/dist-web/assets/{linear-BKCnUo31.js → linear-BRGe4-d6.js} +1 -1
  104. package/dist-web/assets/map-CWKZqZjr.js +1 -0
  105. package/dist-web/assets/map-DxJ2ADlA.js +1 -0
  106. package/dist-web/assets/mermaid.core-D-9Sx1No.js +314 -0
  107. package/dist-web/assets/mermaidParser.worker-PTFfzpoB.js +318 -0
  108. package/dist-web/assets/{mindmap-definition-RKZ34NQL-BmJZpjh6.js → mindmap-definition-FAOFIHXS-C4vnms3i.js} +2 -2
  109. package/dist-web/assets/{mindmap-definition-RKZ34NQL-1mjWUcf6.js → mindmap-definition-FAOFIHXS-Cg_hoVIq.js} +1 -1
  110. package/dist-web/assets/pegDiagram-VL7TDLO6-B2fFEFUH.js +1 -0
  111. package/dist-web/assets/pegDiagram-VL7TDLO6-CSX9hitD.js +1 -0
  112. package/dist-web/assets/pieDiagram-7S7Q4E2Y-0L0mxahZ.js +39 -0
  113. package/dist-web/assets/pieDiagram-7S7Q4E2Y-C7T0Cmw1.js +39 -0
  114. package/dist-web/assets/{quadrantDiagram-W4KKPZXB-BmT_qM09.js → quadrantDiagram-CIZ2JOQS-BBy7w4kD.js} +3 -3
  115. package/dist-web/assets/{quadrantDiagram-W4KKPZXB-C0kPuyNP.js → quadrantDiagram-CIZ2JOQS-U483z0er.js} +3 -3
  116. package/dist-web/assets/railroadDiagram-AXF67PYL-D80a2_DJ.js +1 -0
  117. package/dist-web/assets/railroadDiagram-AXF67PYL-DX8y0MzW.js +1 -0
  118. package/dist-web/assets/{requirementDiagram-4Y6WPE33-C0YP_Amt.js → requirementDiagram-LRYGKXZP-CeyHfq8t.js} +3 -3
  119. package/dist-web/assets/{requirementDiagram-4Y6WPE33-CZrxH1Y2.js → requirementDiagram-LRYGKXZP-DGD5J_4k.js} +3 -3
  120. package/dist-web/assets/sankeyDiagram-W5VNT64P-CCISkkrR.js +40 -0
  121. package/dist-web/assets/sankeyDiagram-W5VNT64P-CT5PdZz2.js +40 -0
  122. package/dist-web/assets/sequenceDiagram-SI44F4Z6-BeMzHWXI.js +162 -0
  123. package/dist-web/assets/sequenceDiagram-SI44F4Z6-jQfm2XS7.js +162 -0
  124. package/dist-web/assets/sizeCapture-X5ZJPWSS-BQsTdtrv.js +1 -0
  125. package/dist-web/assets/sizeCapture-X5ZJPWSS-lRZsmCoS.js +1 -0
  126. package/dist-web/assets/stateDiagram-OKZ733FA-ClCgTdUm.js +1 -0
  127. package/dist-web/assets/stateDiagram-OKZ733FA-Cv59z5YT.js +1 -0
  128. package/dist-web/assets/stateDiagram-v2-UEYNNEHI-R8jTc--F.js +1 -0
  129. package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fi_lrTAx.js +1 -0
  130. package/dist-web/assets/swimlanes-SLNWSIFB-BCdOUN05.js +2 -0
  131. package/dist-web/assets/swimlanes-SLNWSIFB-Cu5l7SuR.js +1 -0
  132. package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-BUd_uYK3.js +8 -0
  133. package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-CalrsgPK.js +8 -0
  134. package/dist-web/assets/{timeline-definition-PNZ67QCA-DsVZdrZa.js → timeline-definition-Z64GVDOM-CMjnh4bu.js} +4 -4
  135. package/dist-web/assets/{timeline-definition-PNZ67QCA-DnB6Usl0.js → timeline-definition-Z64GVDOM-DpWutSKa.js} +4 -4
  136. package/dist-web/assets/vennDiagram-T6HMQDX7-DKJW_8oU.js +34 -0
  137. package/dist-web/assets/vennDiagram-T6HMQDX7-DVC5JVsc.js +34 -0
  138. package/dist-web/assets/{vue.runtime.esm-bundler-pgHGheCW.js → vue.runtime.esm-bundler-BwwRtQwQ.js} +1 -1
  139. package/dist-web/assets/{wardleyDiagram-YWT4CUSO-oBauTCEw.js → wardleyDiagram-T6FBY63Y-BK2mrq7L.js} +3 -3
  140. package/dist-web/assets/{wardleyDiagram-YWT4CUSO-DCiyDQNB.js → wardleyDiagram-T6FBY63Y-oUvgepFn.js} +3 -3
  141. package/dist-web/assets/xychartDiagram-ELKLHX3M-DI6n79q0.js +7 -0
  142. package/dist-web/assets/xychartDiagram-ELKLHX3M-DUJu9F7-.js +7 -0
  143. package/dist-web/index.html +1 -1
  144. package/dist-web/vis/index.html.gz +0 -0
  145. package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  146. package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  147. package/package.json +9 -11
  148. package/skills/screenshot-lesen/SKILL.md +0 -1
  149. package/skills/web-lesen/SKILL.md +0 -1
  150. package/telegram-plugin/dist/bridge.mjs +7 -25
  151. package/telegram-plugin/dist/mcp-server.mjs +236 -71
  152. package/dist-web/assets/arc-4xPfymbf.js +0 -1
  153. package/dist-web/assets/arc-Doj0wRZ0.js +0 -1
  154. package/dist-web/assets/architectureDiagram-3BPJPVTR-BGih283a.js +0 -36
  155. package/dist-web/assets/architectureDiagram-3BPJPVTR-CQ09RrbH.js +0 -36
  156. package/dist-web/assets/blockDiagram-GPEHLZMM-3ShMf1gu.js +0 -132
  157. package/dist-web/assets/blockDiagram-GPEHLZMM-CAtDjkJt.js +0 -132
  158. package/dist-web/assets/channel-DNkUo9e6.js +0 -1
  159. package/dist-web/assets/channel-DcnUTIGB.js +0 -1
  160. package/dist-web/assets/chunk-AQP2D5EJ-FS8-f8lF.js +0 -231
  161. package/dist-web/assets/chunk-AQP2D5EJ-UKhb4V4r.js +0 -231
  162. package/dist-web/assets/classDiagram-4FO5ZUOK-B1ZO8EbE.js +0 -1
  163. package/dist-web/assets/classDiagram-4FO5ZUOK-xk6cM9Kq.js +0 -1
  164. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-B1ZO8EbE.js +0 -1
  165. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-xk6cM9Kq.js +0 -1
  166. package/dist-web/assets/dagre-BM42HDAG-CmUO9qKN.js +0 -4
  167. package/dist-web/assets/dagre-BM42HDAG-DLMfLCoV.js +0 -4
  168. package/dist-web/assets/diagram-2AECGRRQ-DQTst0OH.js +0 -43
  169. package/dist-web/assets/diagram-2AECGRRQ-L74iLMqd.js +0 -43
  170. package/dist-web/assets/diagram-5GNKFQAL-BiBm7CJR.js +0 -10
  171. package/dist-web/assets/diagram-5GNKFQAL-DoE6q7H1.js +0 -10
  172. package/dist-web/assets/diagram-OG6HWLK6-BHCt1E0n.js +0 -24
  173. package/dist-web/assets/diagram-OG6HWLK6-DRyxjFMa.js +0 -24
  174. package/dist-web/assets/erDiagram-TEJ5UH35-BoDczjGG.js +0 -85
  175. package/dist-web/assets/erDiagram-TEJ5UH35-yd6SOv_7.js +0 -85
  176. package/dist-web/assets/flowDiagram-I6XJVG4X-DHoceI7r.js +0 -162
  177. package/dist-web/assets/flowDiagram-I6XJVG4X-DkYNQv0R.js +0 -162
  178. package/dist-web/assets/ganttDiagram-6RSMTGT7-Cg1TQBc0.js +0 -292
  179. package/dist-web/assets/ganttDiagram-6RSMTGT7-Z6klo7KK.js +0 -292
  180. package/dist-web/assets/layout-C1ojF0zw.js +0 -1
  181. package/dist-web/assets/layout-SsrduOYp.js +0 -1
  182. package/dist-web/assets/mermaid.core-D_lKT0dR.js +0 -309
  183. package/dist-web/assets/mermaidParser.worker-Dx4jPi9z.js +0 -313
  184. package/dist-web/assets/pieDiagram-4H26LBE5-BU41t2A_.js +0 -30
  185. package/dist-web/assets/pieDiagram-4H26LBE5-BkwDAvbt.js +0 -30
  186. package/dist-web/assets/sankeyDiagram-5OEKKPKP-De_o7hDr.js +0 -40
  187. package/dist-web/assets/sankeyDiagram-5OEKKPKP-Dvuj6bMA.js +0 -40
  188. package/dist-web/assets/sequenceDiagram-3UESZ5HK-DuCrgPyA.js +0 -162
  189. package/dist-web/assets/sequenceDiagram-3UESZ5HK-VAxffBe7.js +0 -162
  190. package/dist-web/assets/stateDiagram-AJRCARHV-CYLupxM7.js +0 -1
  191. package/dist-web/assets/stateDiagram-AJRCARHV-nDExQydZ.js +0 -1
  192. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-BhEc7Thx.js +0 -1
  193. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-BzwD_BIl.js +0 -1
  194. package/dist-web/assets/vennDiagram-CIIHVFJN-BBHQmCFf.js +0 -34
  195. package/dist-web/assets/vennDiagram-CIIHVFJN-Cfj-K9NW.js +0 -34
  196. package/dist-web/assets/wardley-L42UT6IY-BJFn8eDD.js +0 -173
  197. package/dist-web/assets/wardley-L42UT6IY-BSu8-4hg.js +0 -173
  198. package/dist-web/assets/xychartDiagram-2RQKCTM6-C2iZpl9I.js +0 -7
  199. package/dist-web/assets/xychartDiagram-2RQKCTM6-Db_VoDBV.js +0 -7
  200. package/mnemo/access_routes.js +0 -692
  201. package/mnemo/agent_governance.js +0 -4242
  202. package/mnemo/agent_mail.js +0 -901
  203. package/mnemo/bootstrap_auto.js +0 -137
  204. package/mnemo/brief_coordination.js +0 -226
  205. package/mnemo/code_read_tools.js +0 -375
  206. package/mnemo/context_preview_tools.js +0 -603
  207. package/mnemo/embeddings.js +0 -66
  208. package/mnemo/external_repo_ops.js +0 -575
  209. package/mnemo/facts/example-project-rules.json +0 -90
  210. package/mnemo/facts/example.json +0 -34
  211. package/mnemo/identity_schema.sql +0 -139
  212. package/mnemo/journal_schema.js +0 -561
  213. package/mnemo/loop_doctor_tools.js +0 -661
  214. package/mnemo/mail_secret_refs.js +0 -150
  215. package/mnemo/mcp.js +0 -9309
  216. package/mnemo/memory_consolidation.js +0 -1914
  217. package/mnemo/memory_health_tools.js +0 -165
  218. package/mnemo/package.json +0 -79
  219. package/mnemo/protected_scope_gate.js +0 -627
  220. package/mnemo/resource_access_control.js +0 -684
  221. package/mnemo/runtime_governance.js +0 -1256
  222. package/mnemo/runtime_turn_gate.js +0 -862
  223. package/mnemo/sandbox.js +0 -143
  224. package/mnemo/schema.sql +0 -389
  225. package/mnemo/shared_utils.js +0 -763
  226. package/mnemo/skills/agent-auto-resume/SKILL.md +0 -56
  227. package/mnemo/skills/agent_hand/SKILL.md +0 -43
  228. package/mnemo/skills/agent_hand/run.js +0 -63
  229. package/mnemo/skills/book_flight/SKILL.md +0 -34
  230. package/mnemo/skills/external_repo_review/SKILL.md +0 -43
  231. package/mnemo/skills/external_repo_review/run.js +0 -73
  232. package/mnemo/skills/pay_invoice/SKILL.md +0 -34
  233. package/mnemo/team_quality_ops.js +0 -944
  234. package/mnemo/timeline_report_tools.js +0 -810
  235. package/mnemo/write_gate_risk.js +0 -80
  236. package/mnemo/writer_health.js +0 -152
  237. package/skills/doku-ingestion/SKILL.md +0 -48
  238. package/skills/doku-ingestion/ingest_docs.py +0 -133
@@ -1,763 +0,0 @@
1
- /**
2
- * Shared pure utilities — single source of truth for helpers used
3
- * by daemon.js, mcp.js, and timeline_report_tools.js.
4
- *
5
- * Usage:
6
- * const { parseMaybeJson, deepMergePlain, uniqueIntegers } = require("./shared_utils");
7
- */
8
-
9
- function parseMaybeJson(value, fallback) {
10
- if (value == null || value === "") return fallback;
11
- if (typeof value !== "string") return value;
12
- try { return JSON.parse(value); } catch { return fallback === undefined ? value : fallback; }
13
- }
14
-
15
- function uniqueIntegers(values) {
16
- return Array.from(new Set((Array.isArray(values) ? values : [values])
17
- .map(v => parseInt(v, 10))
18
- .filter(v => Number.isInteger(v) && v > 0)));
19
- }
20
-
21
- function deepMergePlain(base, override) {
22
- const out = Object.assign({}, base || {});
23
- for (const [k, v] of Object.entries(override || {})) {
24
- if (v && typeof v === "object" && !Array.isArray(v) && out[k] && typeof out[k] === "object" && !Array.isArray(out[k])) {
25
- out[k] = deepMergePlain(out[k], v);
26
- } else {
27
- out[k] = v;
28
- }
29
- }
30
- return out;
31
- }
32
-
33
- // Strip memory-private blocks from text before SQLite persist.
34
- function stripPrivate(text) {
35
- if (typeof text !== "string" || !text) return { text, hadPrivate: false };
36
- const patterns = [
37
- { re: /<private\b[^>]*>[\s\S]*?<\/private>/gi, marker: "[private]" },
38
- { re: /<mnemo-private\b[^>]*>[\s\S]*?<\/mnemo-private>/gi, marker: "[private]" },
39
- { re: /<memory-private\b[^>]*>[\s\S]*?<\/memory-private>/gi, marker: "[private]" },
40
- { re: /<no-memory\b[^>]*>[\s\S]*?<\/no-memory>/gi, marker: "[no-memory]" },
41
- { re: /<nomemory\b[^>]*>[\s\S]*?<\/nomemory>/gi, marker: "[no-memory]" },
42
- { re: /\[private\][\s\S]*?\[\/private\]/gi, marker: "[private]" },
43
- { re: /\[no-memory\][\s\S]*?\[\/no-memory\]/gi, marker: "[no-memory]" },
44
- { re: /<!--\s*(?:mnemo:)?private\s*-->[\s\S]*?<!--\s*\/(?:mnemo:)?private\s*-->/gi, marker: "[private]" },
45
- { re: /<!--\s*(?:mnemo:)?no-memory\s*-->[\s\S]*?<!--\s*\/(?:mnemo:)?no-memory\s*-->/gi, marker: "[no-memory]" },
46
- ];
47
- let out = text;
48
- let hadPrivate = false;
49
- for (const pattern of patterns) {
50
- pattern.re.lastIndex = 0;
51
- if (pattern.re.test(out)) {
52
- hadPrivate = true;
53
- pattern.re.lastIndex = 0;
54
- out = out.replace(pattern.re, pattern.marker);
55
- }
56
- }
57
- return { text: out, hadPrivate };
58
- }
59
-
60
- function parseAgentCsv(value) {
61
- return String(value || "")
62
- .split(",")
63
- .map((s) => s.trim())
64
- .filter(Boolean);
65
- }
66
-
67
- function normalizeAgentName(name) {
68
- return String(name || "").trim().toLowerCase();
69
- }
70
-
71
- function jsonSafe(value, maxChars = 12000) {
72
- if (value === undefined) return null;
73
- try {
74
- const raw = typeof value === "string" ? value : JSON.stringify(value);
75
- if (!raw) return null;
76
- return raw.length > maxChars ? raw.slice(0, maxChars) + "...[truncated]" : raw;
77
- } catch {
78
- const raw = String(value);
79
- return raw.length > maxChars ? raw.slice(0, maxChars) + "...[truncated]" : raw;
80
- }
81
- }
82
-
83
- function compactContent(value, maxChars = 8000) {
84
- if (value == null) return null;
85
- const raw = typeof value === "string" ? value : jsonSafe(value, maxChars);
86
- if (!raw) return null;
87
- const scrubbed = stripPrivate(raw).text || "";
88
- return scrubbed.length > maxChars ? scrubbed.slice(0, maxChars) + "...[truncated]" : scrubbed;
89
- }
90
-
91
- function parseMetaJson(metaJson) {
92
- try { return JSON.parse(metaJson || "{}"); } catch { return {}; }
93
- }
94
-
95
- function isoOrNull(value) {
96
- if (!value) return null;
97
- let raw = String(value).trim();
98
- if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) raw += "T09:00:00";
99
- const d = new Date(raw);
100
- return Number.isNaN(d.getTime()) ? null : d.toISOString();
101
- }
102
-
103
- function parseBriefTitle(content) {
104
- const lines = String(content || "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
105
- const first = lines.find((line) => !/^#{1,6}\s*$/.test(line)) || "Brief";
106
- return first.replace(/^#{1,6}\s*/, "").slice(0, 140) || "Brief";
107
- }
108
-
109
- // --- Brief contract constants & helpers ---
110
-
111
- const TEAM_BRIEF_ALIASES = new Set(["all", "crew", "everyone", "group", "gruppe", "team"]);
112
- const BRIEF_CONTRACT_VERSION = "firm-brief-v1";
113
- const BRIEF_REQUIRED_HEADINGS = ["## Title", "## Project", "## Request", "## Acceptance", "## Report Back"];
114
-
115
- function cleanScope(scope) {
116
- return String(scope || "default").toLowerCase().replace(/[^a-z0-9_-]/g, "") || "default";
117
- }
118
-
119
- function uniqueAgentNames(names) {
120
- const seen = new Set();
121
- const out = [];
122
- for (const name of names || []) {
123
- const cleaned = String(name || "").trim();
124
- const key = cleaned.toLowerCase();
125
- if (!cleaned || TEAM_BRIEF_ALIASES.has(key) || seen.has(key)) continue;
126
- seen.add(key);
127
- out.push(key);
128
- }
129
- return out;
130
- }
131
-
132
- function isTeamBriefTarget(name) {
133
- return TEAM_BRIEF_ALIASES.has(String(name || "").trim().toLowerCase());
134
- }
135
-
136
- function hasCanonicalBriefShape(content) {
137
- const text = String(content || "");
138
- return BRIEF_REQUIRED_HEADINGS.filter((heading) => text.includes(heading)).length >= 4;
139
- }
140
-
141
- function normalizeBriefMeta(meta, extras = {}) {
142
- const base = meta && typeof meta === "object" ? { ...meta } : {};
143
- return {
144
- ...base,
145
- ...extras,
146
- brief_contract_version: BRIEF_CONTRACT_VERSION,
147
- brief_contract_required: true,
148
- };
149
- }
150
-
151
- function normalizeBriefContent(content, meta, extras = {}) {
152
- const body = String(content || "").trim();
153
- const normalizedMeta = normalizeBriefMeta(meta, extras);
154
- if (!body) return { content: body, meta: normalizedMeta };
155
- if (hasCanonicalBriefShape(body)) return { content: body, meta: normalizedMeta };
156
- const project = String(normalizedMeta.project || normalizedMeta.scope || normalizedMeta.portal || "unspecified").trim() || "unspecified";
157
- const constraints = []
158
- .concat(Array.isArray(normalizedMeta.constraints) ? normalizedMeta.constraints : [])
159
- .concat(Array.isArray(normalizedMeta.guardrails) ? normalizedMeta.guardrails : [])
160
- .filter(Boolean);
161
- const acceptance = []
162
- .concat(Array.isArray(normalizedMeta.acceptance) ? normalizedMeta.acceptance : [])
163
- .concat(Array.isArray(normalizedMeta.acceptance_criteria) ? normalizedMeta.acceptance_criteria : [])
164
- .filter(Boolean);
165
- const reportBack = []
166
- .concat(Array.isArray(normalizedMeta.report_back) ? normalizedMeta.report_back : [])
167
- .concat(["what changed", "what was checked", "what is still open"]);
168
- return {
169
- content: [
170
- "# Brief",
171
- "",
172
- "## Title",
173
- parseBriefTitle(body),
174
- "",
175
- "## Project",
176
- project,
177
- "",
178
- "## Request",
179
- body,
180
- "",
181
- "## Constraints",
182
- ...(constraints.length ? constraints.map((item) => `- ${item}`) : ["- follow project rules", "- no duplicate work", "- stay in assigned lane"]),
183
- "",
184
- "## Acceptance",
185
- ...(acceptance.length ? acceptance.map((item) => `- ${item}`) : ["- requested outcome is implemented", "- no regressions introduced", "- result is reported in the standard report area"]),
186
- "",
187
- "## Report Back",
188
- ...reportBack.map((item) => `- ${item}`),
189
- ].join("\n"),
190
- meta: normalizedMeta,
191
- };
192
- }
193
-
194
- // --- File / media helpers ---
195
-
196
- function baseName(p) {
197
- const raw = String(p || "").split(/[\\/]/).pop() || "";
198
- return raw.trim();
199
- }
200
-
201
- function extensionName(p) {
202
- const name = baseName(p);
203
- const idx = name.lastIndexOf(".");
204
- return idx >= 0 ? name.slice(idx + 1).toLowerCase() : "";
205
- }
206
-
207
- function inferMediaKind(a, meta, payload, fileName, ext) {
208
- const eventKind = String(a.event_kind || "").toLowerCase();
209
- const hinted = String(a.media_kind || meta.media_kind || payload.media_kind || "").toLowerCase();
210
- if (hinted) return hinted;
211
- if (eventKind.includes("screenshot")) return "screenshot";
212
- if (eventKind.includes("video")) return "video";
213
- if (eventKind.includes("audio") || eventKind.includes("voice")) return "audio";
214
- if (eventKind.includes("photo") || eventKind.includes("image")) return "image";
215
- if (eventKind.includes("document") || eventKind.includes("pdf")) return "document";
216
- if (eventKind.includes("file") || eventKind.includes("attachment")) return "file";
217
- if (["png","jpg","jpeg","webp","gif","bmp"].includes(ext)) return "screenshot";
218
- if (["mp4","mov","webm","mkv","avi","m4v","mpeg","mpg"].includes(ext)) return "video";
219
- if (["mp3","wav","m4a","aac","ogg","oga","opus","flac"].includes(ext)) return "audio";
220
- if (["pdf","doc","docx","txt","md","rtf","html","htm","csv","tsv","json","jsonl","xml","log"].includes(ext)) return "document";
221
- if (fileName) return "file";
222
- return "";
223
- }
224
-
225
- function inferMediaType(ext, kind) {
226
- if (kind === "screenshot" || kind === "image") return "image";
227
- if (kind === "video") return "video";
228
- if (kind === "audio") return "audio";
229
- if (kind === "document") return "document";
230
- return ext ? "file" : "";
231
- }
232
-
233
- function uniqueStrings(list) {
234
- return Array.from(new Set((Array.isArray(list) ? list : [list]).map(x => String(x || "").trim()).filter(Boolean)));
235
- }
236
-
237
- function compactTitleText(value, max = 90) {
238
- const raw = String(value || "")
239
- .replace(/<[^>]+>/g, " ")
240
- .replace(/[`*_#>\[\](){}]/g, " ")
241
- .replace(/\s+/g, " ")
242
- .trim();
243
- if (!raw) return "";
244
- return raw.length > max ? raw.slice(0, max - 1).trim() + "…" : raw;
245
- }
246
-
247
- function captureSourceLabel(source, channel) {
248
- const s = String(source || "").toLowerCase();
249
- const c = String(channel || "").toLowerCase();
250
- if (s.includes("telegram") || c.includes("telegram") || c.includes("chat")) return "Chat";
251
- if (s.includes("email") || c.includes("mail")) return "Email";
252
- if (s.includes("browser")) return "Browser";
253
- if (s.includes("brief")) return "Brief";
254
- if (s.includes("manual")) return "Manual";
255
- return source ? String(source).replace(/[-_]+/g, " ").replace(/\b\w/g, m => m.toUpperCase()) : "Capture";
256
- }
257
-
258
- function formatCaptureDisplayTime(value) {
259
- const d = value ? new Date(value) : new Date();
260
- if (!Number.isFinite(d.getTime())) return "";
261
- const pad = (n) => String(n).padStart(2, "0");
262
- return `${pad(d.getDate())}.${pad(d.getMonth() + 1)}.${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
263
- }
264
-
265
- function formatCaptureFileTime(value) {
266
- const d = value ? new Date(value) : new Date();
267
- if (!Number.isFinite(d.getTime())) return "";
268
- const pad = (n) => String(n).padStart(2, "0");
269
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}-${pad(d.getHours())}-${pad(d.getMinutes())}`;
270
- }
271
-
272
- function slugFilePart(value, max = 96) {
273
- const s = String(value || "")
274
- .normalize("NFKD")
275
- .replace(/[\u0300-\u036f]/g, "")
276
- .toLowerCase()
277
- .replace(/[^a-z0-9]+/g, "-")
278
- .replace(/^-+|-+$/g, "")
279
- .slice(0, max)
280
- .replace(/-+$/g, "");
281
- return s || "media";
282
- }
283
-
284
- function buildMediaTitle(input = {}) {
285
- const explicit = input.title || input.meta && input.meta.title || input.payload && input.payload.title;
286
- if (explicit) return compactTitleText(explicit, 160);
287
- const sourceLabel = captureSourceLabel(input.source, input.channel);
288
- const stamp = formatCaptureDisplayTime(input.occurred_at);
289
- const context = compactTitleText(
290
- input.context_text || input.content || input.text ||
291
- input.meta && (input.meta.context_text || input.meta.caption || input.meta.message_text || input.meta.notes) ||
292
- input.payload && (input.payload.context_text || input.payload.caption || input.payload.message_text || input.payload.notes) ||
293
- "",
294
- 100
295
- );
296
- const fallback = compactTitleText([input.project, input.media_kind, input.route, input.page_url, input.file_name].filter(Boolean).join(" "), 100);
297
- return [sourceLabel, stamp, context || fallback].filter(Boolean).join(" ").trim();
298
- }
299
-
300
- function buildCanonicalMediaFileName(input = {}) {
301
- const ext = String(input.file_ext || extensionName(input.file_name || input.media_path) || "asset").toLowerCase();
302
- const title = input.title || buildMediaTitle(input);
303
- const stamp = formatCaptureFileTime(input.occurred_at);
304
- const label = captureSourceLabel(input.source, input.channel);
305
- const source = slugFilePart(label, 24);
306
- const body = slugFilePart(title.replace(formatCaptureDisplayTime(input.occurred_at), "").replace(new RegExp("^" + label + "\\s*", "i"), ""), 100);
307
- const base = [source, stamp, body].filter(Boolean).join("-");
308
- return `${base || "media"}${ext ? "." + ext.replace(/^\./, "") : ""}`;
309
- }
310
-
311
- // --- Contract validation constants ---
312
-
313
- const AUTH_CONTRACT_REQUIRED_FIELDS = ["status", "mode", "provider", "canonical_project", "canonical_login_url", "shared_identity_scope", "shared_accounts"];
314
- const UI_CONTRACT_REQUIRED_FIELDS = [
315
- "status",
316
- "canonical_brand_project",
317
- "canonical_header_project",
318
- "canonical_button_project",
319
- "canonical_font_source_project",
320
- "canonical_font_display",
321
- "canonical_font_body",
322
- "canonical_logo_light_asset",
323
- "canonical_logo_dark_asset",
324
- "canonical_logo_size_rule",
325
- "canonical_button_size_rule",
326
- "light_mode_required",
327
- "dark_mode_required",
328
- "shared_ui_family",
329
- "no_local_ui_interpretation"
330
- ];
331
-
332
- // --- Sensitivity detectors ---
333
-
334
- function authSensitiveTask(input) {
335
- const text = [
336
- input && input.task,
337
- input && input.summary,
338
- Array.isArray(input && input.topics) ? input.topics.join(" ") : "",
339
- Array.isArray(input && input.files) ? input.files.join(" ") : "",
340
- input && input.action_type
341
- ].filter(Boolean).join(" ");
342
- return /\b(auth|login|sso|signup|signin|sign-in|session|cookie|oauth|password|reset|forgot|verify|onboarding|account)\b/i.test(text);
343
- }
344
-
345
- function uiSensitiveTask(input) {
346
- const text = [
347
- input && input.task,
348
- input && input.summary,
349
- Array.isArray(input && input.topics) ? input.topics.join(" ") : "",
350
- Array.isArray(input && input.files) ? input.files.join(" ") : "",
351
- input && input.action_type
352
- ].filter(Boolean).join(" ");
353
- return /\b(ui|frontend|header|headder|footer|menu|menue|nav|navigation|button|buttons|theme|light|dark|logo|style|design|layout|mobile|responsive|landing)\b/i.test(text);
354
- }
355
-
356
- function wizardTargetGate(input, projectRules) {
357
- const rules = projectRules && !projectRules.error ? projectRules : {};
358
- const requiredGates = parseMaybeJson(rules.required_gates, []) || [];
359
- const deployRules = parseMaybeJson(rules.deploy_rules, {}) || {};
360
- const canonicalNav = parseMaybeJson(rules.canonical_nav, {}) || {};
361
- const required = Boolean(
362
- deployRules.wizard_target_required
363
- || deployRules.ambiguous_wizard_task_blocks
364
- || requiredGates.includes("explicit_wizard_target")
365
- || (canonicalNav.builder_v1 && canonicalNav.builder_v2)
366
- );
367
- if (!required) return { required: false, status: "ok", reason: "wizard target gate not configured" };
368
-
369
- const resourceText = Array.isArray(input && input.resources)
370
- ? input.resources.map((resource) => [
371
- resource && resource.resource_key,
372
- resource && resource.resource_kind,
373
- resource && resource.label,
374
- resource && resource.name
375
- ].filter(Boolean).join(" ")).join(" ")
376
- : "";
377
- const text = [
378
- input && input.project,
379
- input && input.task,
380
- input && input.summary,
381
- input && input.scope,
382
- input && input.action_type,
383
- input && input.tool_name,
384
- Array.isArray(input && input.topics) ? input.topics.join(" ") : "",
385
- Array.isArray(input && input.files) ? input.files.join(" ") : "",
386
- Array.isArray(input && input.routes) ? input.routes.join(" ") : "",
387
- Array.isArray(input && input.urls) ? input.urls.join(" ") : "",
388
- Array.isArray(input && input.system_names) ? input.system_names.join(" ") : "",
389
- resourceText
390
- ].filter(Boolean).join(" ").toLowerCase();
391
-
392
- const mentionsWizard = /\bwiz(?:ard|rad)\b/.test(text)
393
- || /\bwiz(?:ard|rad)\s*[12]\b/.test(text)
394
- || /\bbuilder[_-]?v?[12]\b/.test(text)
395
- || /apps\.example(?:corp)?(?:\.ai)?:builder[_-]?v?[12]\b/.test(text)
396
- || /\/dashboard\/builder[_-]?v?[12]\b/.test(text);
397
- if (!mentionsWizard) return { required: true, status: "ok", reason: "no wizard surface mentioned" };
398
-
399
- const builder_v1 = /\bwiz(?:ard|rad)\s*1\b/.test(text)
400
- || /\bwiz(?:ard|rad)1\b/.test(text)
401
- || /\bbuilder[_-]?v?1\b/.test(text)
402
- || /apps\.example(?:corp)?(?:\.ai)?:builder[_-]?v?1\b/.test(text)
403
- || /\/dashboard\/builder[_-]?v?1\b/.test(text);
404
- const builder_v2 = /\bwiz(?:ard|rad)\s*2\b/.test(text)
405
- || /\bwiz(?:ard|rad)2\b/.test(text)
406
- || /\bbuilder[_-]?v?2\b/.test(text)
407
- || /apps\.example(?:corp)?(?:\.ai)?:builder[_-]?v?2\b/.test(text)
408
- || /\/dashboard\/builder[_-]?v?2\b/.test(text);
409
-
410
- if (builder_v1 && builder_v2) {
411
- return {
412
- required: true,
413
- status: "block",
414
- reason: "multiple wizard targets mentioned; create separate Work Orders for builder_v1 and builder_v2",
415
- target: "mixed"
416
- };
417
- }
418
- if (builder_v1 || builder_v2) {
419
- return {
420
- required: true,
421
- status: "ok",
422
- reason: "explicit wizard target present",
423
- target: builder_v2 ? "apps.example:builder_v2" : "apps.example:builder_v1"
424
- };
425
- }
426
- return {
427
- required: true,
428
- status: "block",
429
- reason: "ambiguous wizard target; task must explicitly name apps.example:builder_v1 or apps.example:builder_v2",
430
- target: null
431
- };
432
- }
433
-
434
- // --- Contract report builders ---
435
- // These take an ensureTables callback so callers wire in their own schema bootstrap.
436
-
437
- function authContractReport(tdb, project, ensureTables) {
438
- if (ensureTables) ensureTables(tdb);
439
- if (!project) return { error: "project required" };
440
- const row = tdb.prepare("SELECT project, auth_matrix, updated_at, updated_by FROM project_rules WHERE project=?").get(project);
441
- if (!row) return { error: "project_rules_missing", project, blockers: ["project rules missing"], hint: "Set auth_matrix in mem_project_rules_set before auth/login work." };
442
- const contract = parseMaybeJson(row.auth_matrix, {}) || {};
443
- const missing = [];
444
- if (!contract || typeof contract !== "object" || Array.isArray(contract)) {
445
- missing.push("auth_matrix");
446
- } else {
447
- for (const field of AUTH_CONTRACT_REQUIRED_FIELDS) {
448
- const value = contract[field];
449
- if (value === undefined || value === null || value === "" || value === "unknown") missing.push(field);
450
- }
451
- }
452
- const canonicalProject = contract.canonical_project || project;
453
- const identityScope = contract.shared_identity_scope || null;
454
- const peers = [];
455
- const mismatches = [];
456
- const rows = tdb.prepare("SELECT project, auth_matrix FROM project_rules WHERE auth_matrix IS NOT NULL").all();
457
- for (const peerRow of rows) {
458
- const peer = parseMaybeJson(peerRow.auth_matrix, {}) || {};
459
- if (!peer || typeof peer !== "object" || Array.isArray(peer)) continue;
460
- const sameCanonical = (peer.canonical_project || peerRow.project) === canonicalProject;
461
- const sameScope = identityScope && peer.shared_identity_scope && peer.shared_identity_scope === identityScope;
462
- const linkedPortal = Array.isArray(contract.portals) && contract.portals.includes(peerRow.project);
463
- if (!(sameCanonical || sameScope || linkedPortal || peerRow.project === project)) continue;
464
- peers.push({
465
- project: peerRow.project,
466
- provider: peer.provider || null,
467
- canonical_login_url: peer.canonical_login_url || null,
468
- shared_identity_scope: peer.shared_identity_scope || null,
469
- session_cookie_scope: peer.session_cookie_scope || null,
470
- shared_accounts: peer.shared_accounts
471
- });
472
- if (peerRow.project === project) continue;
473
- for (const field of ["provider", "canonical_login_url", "shared_identity_scope", "session_cookie_scope", "shared_accounts"]) {
474
- const here = contract[field];
475
- const there = peer[field];
476
- if (here !== undefined && there !== undefined && here !== null && there !== null && JSON.stringify(here) !== JSON.stringify(there)) {
477
- mismatches.push({ peer_project: peerRow.project, field, expected: here, actual: there });
478
- }
479
- }
480
- }
481
- const blockers = [];
482
- if (contract.status === "unknown" || contract.status === "draft" || contract.status == null) blockers.push("auth contract status is not active");
483
- if (missing.length) blockers.push("auth contract missing required fields: " + missing.join(", "));
484
- if (mismatches.length) blockers.push("auth contract mismatch across linked portals: " + mismatches.map(m => `${m.peer_project}.${m.field}`).join(", "));
485
- return {
486
- ok: blockers.length === 0,
487
- status: blockers.length ? "block" : "ok",
488
- project,
489
- canonical_project: canonicalProject,
490
- contract,
491
- missing,
492
- mismatches,
493
- peers,
494
- blockers,
495
- hint: blockers.length ? "Do not change login/SSO until the canonical auth contract matches across linked portals." : "Canonical auth contract is consistent."
496
- };
497
- }
498
-
499
- function uiContractReport(tdb, project, ensureTables) {
500
- if (ensureTables) ensureTables(tdb);
501
- if (!project) return { error: "project required" };
502
- const row = tdb.prepare("SELECT project, canonical_nav, design_rules, updated_at, updated_by FROM project_rules WHERE project=?").get(project);
503
- if (!row) return { error: "project_rules_missing", project, blockers: ["project rules missing"], hint: "Set canonical_nav + design_rules before frontend/header/button work." };
504
- const design = parseMaybeJson(row.design_rules, {}) || {};
505
- const nav = parseMaybeJson(row.canonical_nav, null);
506
- const missing = [];
507
- if (!design || typeof design !== "object" || Array.isArray(design)) {
508
- missing.push("design_rules");
509
- } else {
510
- for (const field of UI_CONTRACT_REQUIRED_FIELDS) {
511
- const value = design[field];
512
- if (value === undefined || value === null || value === "" || value === "unknown") missing.push(field);
513
- }
514
- }
515
- const navItems = Array.isArray(nav) ? nav : [nav?.primary, nav?.items, nav?.menu, nav?.links].find(items => Array.isArray(items)) || [];
516
- if (navItems.length === 0) missing.push("canonical_nav");
517
- const family = design.shared_ui_family || null;
518
- const peers = [];
519
- const mismatches = [];
520
- const rows = tdb.prepare("SELECT project, design_rules, canonical_nav FROM project_rules WHERE design_rules IS NOT NULL").all();
521
- for (const peerRow of rows) {
522
- const peerDesign = parseMaybeJson(peerRow.design_rules, {}) || {};
523
- if (!peerDesign || typeof peerDesign !== "object" || Array.isArray(peerDesign)) continue;
524
- const sameFamily = family && peerDesign.shared_ui_family === family;
525
- const linkedPortal = Array.isArray(design.portals) && design.portals.includes(peerRow.project);
526
- if (!(sameFamily || linkedPortal || peerRow.project === project)) continue;
527
- peers.push({
528
- project: peerRow.project,
529
- canonical_header_project: peerDesign.canonical_header_project || null,
530
- canonical_button_project: peerDesign.canonical_button_project || null,
531
- canonical_brand_project: peerDesign.canonical_brand_project || null,
532
- canonical_font_source_project: peerDesign.canonical_font_source_project || null,
533
- canonical_font_display: peerDesign.canonical_font_display || null,
534
- canonical_font_body: peerDesign.canonical_font_body || null,
535
- canonical_logo_light_asset: peerDesign.canonical_logo_light_asset || null,
536
- canonical_logo_dark_asset: peerDesign.canonical_logo_dark_asset || null,
537
- canonical_logo_size_rule: peerDesign.canonical_logo_size_rule || null,
538
- canonical_button_size_rule: peerDesign.canonical_button_size_rule || null,
539
- light_mode_required: peerDesign.light_mode_required,
540
- dark_mode_required: peerDesign.dark_mode_required,
541
- shared_ui_family: peerDesign.shared_ui_family || null,
542
- no_local_ui_interpretation: peerDesign.no_local_ui_interpretation
543
- });
544
- if (peerRow.project === project) continue;
545
- for (const field of [
546
- "canonical_brand_project",
547
- "canonical_header_project",
548
- "canonical_button_project",
549
- "canonical_font_source_project",
550
- "canonical_font_display",
551
- "canonical_font_body",
552
- "canonical_logo_light_asset",
553
- "canonical_logo_dark_asset",
554
- "canonical_logo_size_rule",
555
- "canonical_button_size_rule",
556
- "light_mode_required",
557
- "dark_mode_required",
558
- "shared_ui_family",
559
- "no_local_ui_interpretation"
560
- ]) {
561
- const here = design[field];
562
- const there = peerDesign[field];
563
- if (here !== undefined && there !== undefined && here !== null && there !== null && JSON.stringify(here) !== JSON.stringify(there)) {
564
- mismatches.push({ peer_project: peerRow.project, field, expected: here, actual: there });
565
- }
566
- }
567
- }
568
- const blockers = [];
569
- if (design.status === "unknown" || design.status === "draft" || design.status == null) blockers.push("ui contract status is not active");
570
- if (missing.length) blockers.push("ui contract missing required fields: " + missing.join(", "));
571
- if (mismatches.length) blockers.push("ui contract mismatch across linked portals: " + mismatches.map(m => `${m.peer_project}.${m.field}`).join(", "));
572
- if (design.no_local_ui_interpretation !== true) blockers.push("ui contract must explicitly forbid local reinterpretation");
573
- return {
574
- ok: blockers.length === 0,
575
- status: blockers.length ? "block" : "ok",
576
- project,
577
- contract: design,
578
- nav_items_count: navItems.length,
579
- missing,
580
- mismatches,
581
- peers,
582
- blockers,
583
- hint: blockers.length ? "Do not change header/buttons/theme until the canonical UI contract matches example.com across linked portals." : "Canonical UI contract is consistent."
584
- };
585
- }
586
-
587
- // --- Reminder pure helpers ---
588
-
589
- function normalizeReminderText(text) {
590
- return String(text || "")
591
- .toLowerCase()
592
- .normalize("NFD")
593
- .replace(/[\u0300-\u036f]/g, "")
594
- .replace(/ß/g, "ss");
595
- }
596
-
597
- function parseReminderTime(norm) {
598
- let m = norm.match(/\b(?:um\s*)?(\d{1,2})[:.](\d{2})\b/);
599
- if (m) {
600
- const hour = parseInt(m[1], 10);
601
- const minute = parseInt(m[2], 10);
602
- if (hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59) return { hour, minute, explicit: true };
603
- }
604
- m = norm.match(/\bum\s+(\d{1,2})(?:\s*uhr)?(?:\s*(\d{1,2}))?\b/) || norm.match(/\b(\d{1,2})\s*uhr(?:\s*(\d{1,2}))?\b/);
605
- if (m) {
606
- const hour = parseInt(m[1], 10);
607
- const minute = m[2] != null ? parseInt(m[2], 10) : 0;
608
- if (hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59) return { hour, minute, explicit: true };
609
- }
610
- return { hour: 9, minute: 0, explicit: false };
611
- }
612
-
613
- function applyReminderTime(date, time) {
614
- const d = new Date(date.getTime());
615
- d.setHours(time.hour, time.minute, 0, 0);
616
- return d;
617
- }
618
-
619
- function parseReminderDue(text, baseTime) {
620
- const raw = String(text || "");
621
- const norm = normalizeReminderText(raw);
622
- const base = baseTime ? new Date(baseTime) : new Date();
623
- const start = Number.isNaN(base.getTime()) ? new Date() : base;
624
- const time = parseReminderTime(norm);
625
- const finish = (date, dueText, precision) => ({
626
- due_at: applyReminderTime(date, time).toISOString(),
627
- due_text: dueText,
628
- due_precision: time.explicit ? "datetime" : precision,
629
- confidence: precision === "unknown" ? "low" : (precision === "week" ? "medium" : "high"),
630
- });
631
- let m = raw.match(/\b(\d{4}-\d{2}-\d{2})(?:[ T](\d{1,2})(?::(\d{2}))?)?\b/);
632
- if (m) {
633
- const d = new Date(m[1] + "T00:00:00");
634
- const t = m[2] ? { hour: parseInt(m[2], 10), minute: m[3] ? parseInt(m[3], 10) : 0, explicit: true } : time;
635
- return { due_at: applyReminderTime(d, t).toISOString(), due_text: m[0], due_precision: t.explicit ? "datetime" : "date", confidence: "high" };
636
- }
637
- m = norm.match(/\b(\d{1,2})\.(\d{1,2})(?:\.(\d{2,4}))?\b/);
638
- if (m) {
639
- let year = m[3] ? parseInt(m[3], 10) : start.getFullYear();
640
- if (year < 100) year += 2000;
641
- let d = new Date(year, parseInt(m[2], 10) - 1, parseInt(m[1], 10));
642
- if (!m[3] && d.getTime() < start.getTime() - 86400000) d = new Date(year + 1, parseInt(m[2], 10) - 1, parseInt(m[1], 10));
643
- if (!Number.isNaN(d.getTime())) return finish(d, m[0], "date");
644
- }
645
- m = norm.match(/\bin\s+(\d+)\s*(minuten?|mins?|stunden?|hours?|tage?|days?|wochen?|weeks?)\b/);
646
- if (m) {
647
- const n = parseInt(m[1], 10);
648
- const unit = m[2];
649
- const d = new Date(start.getTime());
650
- if (/min/.test(unit)) d.setMinutes(d.getMinutes() + n);
651
- else if (/stund|hour/.test(unit)) d.setHours(d.getHours() + n);
652
- else if (/tag|day/.test(unit)) d.setDate(d.getDate() + n);
653
- else d.setDate(d.getDate() + n * 7);
654
- return { due_at: d.toISOString(), due_text: m[0], due_precision: "relative", confidence: "high" };
655
- }
656
- if (/\bubermorgen\b/.test(norm)) {
657
- const d = new Date(start.getTime());
658
- d.setDate(d.getDate() + 2);
659
- return finish(d, "ubermorgen", "day");
660
- }
661
- if (/\bmorgen\b/.test(norm)) {
662
- const d = new Date(start.getTime());
663
- d.setDate(d.getDate() + 1);
664
- return finish(d, "morgen", "day");
665
- }
666
- if (/\bheute\b/.test(norm)) return finish(start, "heute", "day");
667
- const weekdays = [
668
- { day: 0, names: ["sonntag", "sunday"] },
669
- { day: 1, names: ["montag", "monday"] },
670
- { day: 2, names: ["dienstag", "tuesday"] },
671
- { day: 3, names: ["mittwoch", "wednesday"] },
672
- { day: 4, names: ["donnerstag", "thursday"] },
673
- { day: 5, names: ["freitag", "friday"] },
674
- { day: 6, names: ["samstag", "saturday"] },
675
- ];
676
- for (const w of weekdays) {
677
- const name = w.names.find((n) => new RegExp("\\b" + n + "\\b").test(norm));
678
- if (!name) continue;
679
- let delta = (w.day - start.getDay() + 7) % 7;
680
- if (delta === 0 || /\b(nachste|naechste|next)\b/.test(norm)) delta += 7;
681
- const d = new Date(start.getTime());
682
- d.setDate(d.getDate() + delta);
683
- return finish(d, name, /\bwoche|week\b/.test(norm) ? "week" : "day");
684
- }
685
- if (/\b(nachste|naechste|next)\s+woche\b/.test(norm)) {
686
- const d = new Date(start.getTime());
687
- let delta = (1 - start.getDay() + 7) % 7;
688
- if (delta === 0) delta = 7;
689
- d.setDate(d.getDate() + delta);
690
- return finish(d, "nachste woche", "week");
691
- }
692
- return { due_at: null, due_text: null, due_precision: "unknown", confidence: "low" };
693
- }
694
-
695
- function reminderTitleFromText(text) {
696
- const cleaned = String(text || "").replace(/\s+/g, " ").trim();
697
- return cleaned ? cleaned.slice(0, 180) : "Reminder";
698
- }
699
-
700
- function reminderRow(row) {
701
- if (!row) return null;
702
- return Object.assign({}, row, { meta: parseMetaJson(row.meta_json) });
703
- }
704
-
705
- // --- Readiness / flag helpers ---
706
-
707
- function boolFlag(value, fallback = false) {
708
- if (value === undefined || value === null || value === "") return fallback;
709
- if (typeof value === "boolean") return value;
710
- if (typeof value === "number") return value !== 0;
711
- const normalized = String(value).trim().toLowerCase();
712
- if (["1", "true", "yes", "y", "on", "enabled"].includes(normalized)) return true;
713
- if (["0", "false", "no", "n", "off", "disabled"].includes(normalized)) return false;
714
- return fallback;
715
- }
716
-
717
- function isoAgeDays(isoText) {
718
- const ms = Date.parse(String(isoText || ""));
719
- if (!Number.isFinite(ms)) return null;
720
- return Math.max(0, Math.floor((Date.now() - ms) / 86400000));
721
- }
722
-
723
- function freshnessFromAgeDays(ageDays, warnDays, criticalDays) {
724
- if (ageDays == null) return "unknown";
725
- if (ageDays >= criticalDays) return "critical";
726
- if (ageDays >= warnDays) return "stale";
727
- return "fresh";
728
- }
729
-
730
- function capabilityMatrixForDepartments(departments) {
731
- const set = new Set((departments || []).map((name) => String(name || "").toLowerCase()));
732
- return {
733
- read: true,
734
- report: true,
735
- edit: set.size > 0,
736
- deploy: set.has("deploy-ops") || set.has("strategy-review"),
737
- billing: set.has("billing") || set.has("strategy-review"),
738
- auth: set.has("backend") || set.has("strategy-review"),
739
- production: set.has("deploy-ops") || set.has("strategy-review"),
740
- };
741
- }
742
-
743
- module.exports = {
744
- parseMaybeJson, deepMergePlain, uniqueIntegers,
745
- stripPrivate, parseAgentCsv, normalizeAgentName,
746
- jsonSafe, compactContent, parseMetaJson, isoOrNull, parseBriefTitle,
747
- // Brief contract
748
- TEAM_BRIEF_ALIASES, BRIEF_CONTRACT_VERSION, BRIEF_REQUIRED_HEADINGS,
749
- cleanScope, uniqueAgentNames, isTeamBriefTarget,
750
- hasCanonicalBriefShape, normalizeBriefMeta, normalizeBriefContent,
751
- // File / media
752
- baseName, extensionName, inferMediaKind, inferMediaType, uniqueStrings, compactTitleText, captureSourceLabel, formatCaptureDisplayTime, formatCaptureFileTime, slugFilePart, buildMediaTitle, buildCanonicalMediaFileName,
753
- // Readiness / flags
754
- boolFlag, isoAgeDays, freshnessFromAgeDays, capabilityMatrixForDepartments,
755
- // Contract validation
756
- AUTH_CONTRACT_REQUIRED_FIELDS, UI_CONTRACT_REQUIRED_FIELDS,
757
- authSensitiveTask, uiSensitiveTask,
758
- wizardTargetGate,
759
- authContractReport, uiContractReport,
760
- // Reminder helpers
761
- normalizeReminderText, parseReminderTime, applyReminderTime,
762
- parseReminderDue, reminderTitleFromText, reminderRow,
763
- };