mulmoclaude 0.9.0 → 0.9.2

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 (458) hide show
  1. package/README.md +95 -38
  2. package/bin/mulmoclaude.js +1 -2
  3. package/client/assets/JsonEditor-CPJfn76E.css +1 -0
  4. package/client/assets/JsonEditor-CoWGJG3Y.js +10 -0
  5. package/client/assets/abnfDiagram-VRR7QNED-6nNByj6v-By7GJ3xW.js +1 -0
  6. package/client/assets/abnfDiagram-VRR7QNED-BrQlVixL.js +1 -0
  7. package/client/assets/arc-BXutyUAX.js +1 -0
  8. package/client/assets/arc-zT0wd74V-DulGJNnb.js +1 -0
  9. package/client/assets/architecture-TIHT7OUA-CYMWc3UT-C7HoFzWN.js +1 -0
  10. package/client/assets/architecture-TIHT7OUA-aml8u-G9.js +1 -0
  11. package/client/assets/architectureDiagram-ZJ3FMSHR-Bgnyaj_i-CCpEsCHN.js +36 -0
  12. package/client/assets/architectureDiagram-ZJ3FMSHR-DJTFpPjB.js +36 -0
  13. package/client/assets/array-BifhSqXX.js +1 -0
  14. package/client/assets/array-CApNbSU9-BifhSqXX.js +1 -0
  15. package/client/assets/blockDiagram-677ZJIJ3-DQ35o5E4-BXS11D3J.js +132 -0
  16. package/client/assets/blockDiagram-677ZJIJ3-aYvCODxv.js +132 -0
  17. package/client/assets/c4Diagram-LMCZKHZV-Bau6RMBb.js +10 -0
  18. package/client/assets/c4Diagram-LMCZKHZV-ClWZeiWo-DXGnOOUf.js +10 -0
  19. package/client/assets/channel-D9VSi_QV.js +1 -0
  20. package/client/assets/channel-Di5rtkx0-C3gZcytF.js +1 -0
  21. package/client/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  22. package/client/assets/chunk-2Q5K7J3B-DfKpwyl9-DTxwf5E9.js +1 -0
  23. package/client/assets/chunk-32BRIVSS-CCt9wtYd-Cwa_IP35.js +1 -0
  24. package/client/assets/chunk-32BRIVSS-CxzHeys5.js +1 -0
  25. package/client/assets/chunk-52WLFC77-BtNjbTdw.js +10 -0
  26. package/client/assets/chunk-52WLFC77-FbBbR4uI-wsNC5d-f.js +10 -0
  27. package/client/assets/chunk-5VM5RSS4-BYnnUiN6-C4rOnu9A.js +15 -0
  28. package/client/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  29. package/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  30. package/client/assets/chunk-7BUUIJ7U-hh8aCuGX-mGEpPzQy.js +1 -0
  31. package/client/assets/chunk-C7G6YPKG-BE8ehsnc.js +1 -0
  32. package/client/assets/chunk-C7G6YPKG-C87hlS9c-BitbJpTa.js +1 -0
  33. package/client/assets/{chunk-D8eiyYIV-BY16KEZc.js → chunk-D8eiyYIV-BeeEsA1B.js} +1 -1
  34. package/client/assets/chunk-EX3LRPZG-Bhgskzyy.js +231 -0
  35. package/client/assets/chunk-EX3LRPZG-BqGqMXLN-Cl2Eo1_u.js +231 -0
  36. package/client/assets/chunk-FWX5IMBZ-BkbSAAuW-C2r4AuAO.js +2 -0
  37. package/client/assets/chunk-FWX5IMBZ-D9sskZb_.js +2 -0
  38. package/client/assets/chunk-HOUHSVGY-CBCXk7rb.js +1 -0
  39. package/client/assets/chunk-HOUHSVGY-Cxu0eDlh-owg_DcY0.js +1 -0
  40. package/client/assets/chunk-ICXQ74PX-DTqPpDQ9.js +2 -0
  41. package/client/assets/chunk-ICXQ74PX-hiraF_Xj-CdaEwVin.js +2 -0
  42. package/client/assets/chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js +1 -0
  43. package/client/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  44. package/client/assets/chunk-KEIR6QF5-DNzq6p3w.js +161 -0
  45. package/client/assets/chunk-MOJQB5TN-CqxshQHA-Cn_EpDyZ.js +88 -0
  46. package/client/assets/chunk-MOJQB5TN-D1s_zUGz.js +88 -0
  47. package/client/assets/chunk-OGEWGWER-CjCr7ceX-Dfk5-70y.js +1 -0
  48. package/client/assets/chunk-OGEWGWER-DzfhxoQd.js +1 -0
  49. package/client/assets/chunk-PUDLZKDR-BZlebNhn.js +156 -0
  50. package/client/assets/chunk-PUDLZKDR-Dx6M-vz1-CLyVurOz.js +156 -0
  51. package/client/assets/chunk-Q4XR5HBZ-BcJb7mwA.js +70 -0
  52. package/client/assets/chunk-Q4XR5HBZ-CZd-9lTB-qPEYoRNc.js +11 -0
  53. package/client/assets/chunk-RYQCIY6F-B_IgMG7T.js +1 -0
  54. package/client/assets/chunk-RYQCIY6F-YkbemkCt-CX4fncTQ.js +1 -0
  55. package/client/assets/chunk-V7JOEXUC-Buce04o6.js +206 -0
  56. package/client/assets/chunk-V7JOEXUC-rI0xlC_O-C607vJXy.js +206 -0
  57. package/client/assets/chunk-VAUOI2AC-CxhkzXN0.js +1 -0
  58. package/client/assets/chunk-VAUOI2AC-DrcykVNK-CSROJWdE.js +1 -0
  59. package/client/assets/chunk-VR4S4FIN-D5afNrsS.js +1 -0
  60. package/client/assets/chunk-VR4S4FIN-O6iF8Yvf-CLIwrgqU.js +1 -0
  61. package/client/assets/chunk-WYO6CB5R-B83L_z6I-Cs8mg9If.js +127 -0
  62. package/client/assets/chunk-WYO6CB5R-BXTIdTMw.js +125 -0
  63. package/client/assets/chunk-XXDRQBXY-DFXH_lWn-B5HjCp2c.js +1 -0
  64. package/client/assets/chunk-XXDRQBXY-DXMD6ofA.js +1 -0
  65. package/client/assets/chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js +1 -0
  66. package/client/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  67. package/client/assets/chunk-ZGVPDNZ5-BpFv9JSP-CHYySMjV.js +62 -0
  68. package/client/assets/chunk-ZGVPDNZ5-CYusI0J_.js +62 -0
  69. package/client/assets/chunk-ZIRB5QZD-Biy6ZNFG-D1maAiXb.js +32 -0
  70. package/client/assets/chunk-ZIRB5QZD-C6fEPe3t.js +32 -0
  71. package/client/assets/classDiagram-OUVF2IWQ-BgAZMSbT-BgxEE1D3.js +1 -0
  72. package/client/assets/classDiagram-OUVF2IWQ-CBD3Eidl.js +1 -0
  73. package/client/assets/classDiagram-v2-EOCWNBFH-CBD3Eidl.js +1 -0
  74. package/client/assets/classDiagram-v2-EOCWNBFH-DxHTyui1-BgxEE1D3.js +1 -0
  75. package/client/assets/cose-bilkent-JH36ORCC-CuSL2tA8-aMIqISfj.js +1 -0
  76. package/client/assets/cose-bilkent-JH36ORCC-WAJDBXv1.js +1 -0
  77. package/client/assets/cynefin-VYW2F7L2-DH2qkJKw.js +1 -0
  78. package/client/assets/cynefin-VYW2F7L2-DqA3n9nY-BNWhIK_n.js +1 -0
  79. package/client/assets/cynefinDiagram-TSTJHNR4-CUvPV9MV.js +62 -0
  80. package/client/assets/cynefinDiagram-TSTJHNR4-x0-0sQ15-Cq1gFcFu.js +62 -0
  81. package/client/assets/cytoscape.esm-CzdwbRaj-Djp6vQyU.js +321 -0
  82. package/client/assets/cytoscape.esm-Djp6vQyU.js +321 -0
  83. package/client/assets/dagre-CXRCoUWR.js +1 -0
  84. package/client/assets/dagre-VKFMJZFB-CQdfl-bx-CzZZuace.js +4 -0
  85. package/client/assets/dagre-VKFMJZFB-C_9IS7mB.js +4 -0
  86. package/client/assets/dagre-wczQIDso-CfevN9RO.js +1 -0
  87. package/client/assets/defaultLocale-C8Fc0cco.js +1 -0
  88. package/client/assets/defaultLocale-DUNguUWs-BbDo_yJX.js +1 -0
  89. package/client/assets/diagram-FQU43EPY-BOSB6VUb-isPdQ8WX.js +3 -0
  90. package/client/assets/diagram-FQU43EPY-BP8N00-b.js +3 -0
  91. package/client/assets/diagram-G47NLZAW-DLXrcXsN-BIqj7RKy.js +24 -0
  92. package/client/assets/diagram-G47NLZAW-ulE1JlWG.js +24 -0
  93. package/client/assets/diagram-NH7WQ7WH-BMQp1rkF-D6-fOq_v.js +24 -0
  94. package/client/assets/diagram-NH7WQ7WH-D_IXrL3i.js +24 -0
  95. package/client/assets/diagram-OA4YK3LP-C-pC6Eyu.js +30 -0
  96. package/client/assets/diagram-OA4YK3LP-D1wQ0vUj-BNi6QLCK.js +30 -0
  97. package/client/assets/diagram-WEI45ONY-Di9m35i-.js +41 -0
  98. package/client/assets/diagram-WEI45ONY-RR0DpF8R-BS3nrb3w.js +41 -0
  99. package/client/assets/dist-BQBs5pjy-BmtZ7Oc2.js +1 -0
  100. package/client/assets/dist-CQ3HaWOk.js +1 -0
  101. package/client/assets/ebnfDiagram-CCIWWBDH-Cbwnim2x.js +1 -0
  102. package/client/assets/ebnfDiagram-CCIWWBDH-M123uVJ8-9-dq55nQ.js +1 -0
  103. package/client/assets/erDiagram-Q63AITRT-BWx_-PXG-CWGfG4z5.js +85 -0
  104. package/client/assets/erDiagram-Q63AITRT-CMbtO3Sm.js +85 -0
  105. package/client/assets/eventmodeling-45OFAUF4--32SIpkL.js +1 -0
  106. package/client/assets/eventmodeling-45OFAUF4-_BVSjAXf-DQdL0Icr.js +1 -0
  107. package/client/assets/flowDiagram-23GEKE2U-BeOc_anm-CuVzKmmU.js +1 -0
  108. package/client/assets/flowDiagram-23GEKE2U-Dofa_qxG.js +1 -0
  109. package/client/assets/ganttDiagram-NO4QXBWP-BOoJ1eTw-TWoNmSvq.js +292 -0
  110. package/client/assets/ganttDiagram-NO4QXBWP-BgoAVKuc.js +292 -0
  111. package/client/assets/gitGraph-TEB2WS4Q-CH12KLTN-Bz4frAhV.js +1 -0
  112. package/client/assets/gitGraph-TEB2WS4Q-DIMvNvqt.js +1 -0
  113. package/client/assets/gitGraphDiagram-IHSO6WYX-B2CJhk_G-ClOKkjxw.js +106 -0
  114. package/client/assets/gitGraphDiagram-IHSO6WYX-Dmb6KnPz.js +106 -0
  115. package/client/assets/graphlib-B8gBHxth.js +1 -0
  116. package/client/assets/graphlib-hY-1btwe-DQjxxcnr.js +1 -0
  117. package/client/assets/{html2canvas-CDGcmOD3-CKJ6vKPo.js → html2canvas-CDGcmOD3-DRL9pFVl.js} +2 -2
  118. package/client/assets/{index-9lmYSaus.js → index-Dc0R-HW5.js} +129 -190
  119. package/client/assets/index-Dxo1Zdd-.css +2 -0
  120. package/client/assets/{index.es-DqtpmBm8-DFXjJgCa.js → index.es-DqtpmBm8-EQk3NgR8.js} +1 -1
  121. package/client/assets/info-DKCQHKI2-Cbw3mbiK-GsDZz9IO.js +1 -0
  122. package/client/assets/info-DKCQHKI2-ViCqobGo.js +1 -0
  123. package/client/assets/infoDiagram-FWYZ7A6U-HKV7LIG-.js +2 -0
  124. package/client/assets/infoDiagram-FWYZ7A6U-Mp1X3pBP-ATJv87Ur.js +2 -0
  125. package/client/assets/init-D6jRqBbL.js +1 -0
  126. package/client/assets/init-DEsX3bhM-D6jRqBbL.js +1 -0
  127. package/client/assets/ishikawaDiagram-FXEZZL3T-BNG7tkJu-CGCeOIlJ.js +70 -0
  128. package/client/assets/ishikawaDiagram-FXEZZL3T-CVDUj46f.js +70 -0
  129. package/client/assets/journeyDiagram-5HDEW3XC-BA-ESGLP.js +139 -0
  130. package/client/assets/journeyDiagram-5HDEW3XC-Dbp_hY9X-Bx-IoCe9.js +139 -0
  131. package/client/assets/kanban-definition-HUTT4EX6-Bqaet01L.js +89 -0
  132. package/client/assets/kanban-definition-HUTT4EX6-DSTc5u3q-C0JGckt1.js +89 -0
  133. package/client/assets/katex-CddkPoXu.js +257 -0
  134. package/client/assets/katex-M0IxphGf-CddkPoXu.js +257 -0
  135. package/client/assets/lib-4Tgx7rAy.js +114 -0
  136. package/client/assets/line-B1wBwzrY-f5wxpqoF.js +1 -0
  137. package/client/assets/line-CkyHfW7d.js +1 -0
  138. package/client/assets/linear-B9fuEF4c.js +1 -0
  139. package/client/assets/linear-lrGinF5_-CqNBync7.js +1 -0
  140. package/client/assets/map-CgrwEyH7-DWQFomlZ.js +1 -0
  141. package/client/assets/map-DsCK-0Cs.js +1 -0
  142. package/client/assets/marked.esm-CIU5FDdN.js +64 -0
  143. package/client/assets/marp-CSq0PPfK.js +3448 -0
  144. package/client/assets/material-symbols-outlined-DdRFxZLh.woff2 +0 -0
  145. package/client/assets/mermaid-parser.core-DC7NPJ_M-Ca6XzwfM.js +166 -0
  146. package/client/assets/mermaid-parser.core-SAwSf4_o.js +7 -0
  147. package/client/assets/mermaid.core-BBEQfkdJ.js +11 -0
  148. package/client/assets/mermaid.core-DZM3Ha-E-BxOo-RYG.js +11 -0
  149. package/client/assets/mindmap-definition-LN4V7U3C-B4BN0efL.js +96 -0
  150. package/client/assets/mindmap-definition-LN4V7U3C-DYtgcMsY-DynyLTNv.js +96 -0
  151. package/client/assets/ordinal-B9_Llu10-CU85fhaB.js +1 -0
  152. package/client/assets/ordinal-CopWnP7w.js +1 -0
  153. package/client/assets/packet-7NZHBO7P-CY4XRk9N.js +1 -0
  154. package/client/assets/packet-7NZHBO7P-lwb58iYx--vqSg65J.js +1 -0
  155. package/client/assets/path-BWPyau1x.js +1 -0
  156. package/client/assets/path-COivZ1Gw-C5riojK2.js +1 -0
  157. package/client/assets/pegDiagram-2B236MQR-BOKErjUu.js +1 -0
  158. package/client/assets/pegDiagram-2B236MQR-C43eIpKM-Bi_5Sxjt.js +1 -0
  159. package/client/assets/pie-RZYD4A2V-6BHB7bAx.js +1 -0
  160. package/client/assets/pie-RZYD4A2V-B1UWb4Gu-Dmn2hLWH.js +1 -0
  161. package/client/assets/pieDiagram-ENE6RG2P-BkTqgJyR-Bcdm19kR.js +39 -0
  162. package/client/assets/pieDiagram-ENE6RG2P-DtNjvz-U.js +39 -0
  163. package/client/assets/preload-helper-CZgWQFsJ.js +1 -0
  164. package/client/assets/purify.es-DY32g7DN.js +3 -0
  165. package/client/assets/quadrantDiagram-ABIIQ3AL-BQmAJL2v.js +7 -0
  166. package/client/assets/quadrantDiagram-ABIIQ3AL-Bm1Zjm45-B3orqx_f.js +7 -0
  167. package/client/assets/radar-I7S5WNFK-7CKcb_l--CqKdnR0v.js +1 -0
  168. package/client/assets/radar-I7S5WNFK-R7Jhjfdt.js +1 -0
  169. package/client/assets/railroad-3IZDKUUU-BepSPyHI.js +1 -0
  170. package/client/assets/railroad-3IZDKUUU-gCySKdnW-CKU26HTP.js +1 -0
  171. package/client/assets/railroad-abnf-AHOZXSZD-BlsHsp3c.js +1 -0
  172. package/client/assets/railroad-abnf-AHOZXSZD-BophH4r--CGkGHq54.js +1 -0
  173. package/client/assets/railroad-ebnf-EBAXGLYW-AZNjl_Zu-B7bn_PF3.js +1 -0
  174. package/client/assets/railroad-ebnf-EBAXGLYW-B4fMcGEH.js +1 -0
  175. package/client/assets/railroad-peg-LSFZ7HO6-B1ZrjJIu.js +1 -0
  176. package/client/assets/railroad-peg-LSFZ7HO6-BSiEEyeb-CP_B-N5g.js +1 -0
  177. package/client/assets/railroadDiagram-RFXS5EU6-8cz0Iby3.js +1 -0
  178. package/client/assets/railroadDiagram-RFXS5EU6-BkfbdeAs-Di6jH5I6.js +1 -0
  179. package/client/assets/requirementDiagram-TGXJPOKE-CrGTTjYg-DmIC7Shp.js +84 -0
  180. package/client/assets/requirementDiagram-TGXJPOKE-Dlqoe9TY.js +84 -0
  181. package/client/assets/rough.esm-CSKSodPl.js +1 -0
  182. package/client/assets/rough.esm-wVmwlXu7-CSKSodPl.js +1 -0
  183. package/client/assets/runtime-protocol-vue-WG3JLsjz.js +1 -0
  184. package/client/assets/runtime-vue-lHsY5zMj.js +1 -0
  185. package/client/assets/sankeyDiagram-HTMAVEWB-BkTKgu8Q.js +40 -0
  186. package/client/assets/sankeyDiagram-HTMAVEWB-rWXPf03Z-CjV9pCSJ.js +40 -0
  187. package/client/assets/{schemas-D_RbFtuQ.js → schemas-DGvl73AE.js} +3 -3
  188. package/client/assets/sequenceDiagram-DBY2YBRQ-BpuXUcFy.js +162 -0
  189. package/client/assets/sequenceDiagram-DBY2YBRQ-nkJYWO2m-BqS_sTCH.js +162 -0
  190. package/client/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  191. package/client/assets/sizeCapture-X5ZJPWSS-F5SadAuT-VHIb4_NU.js +1 -0
  192. package/client/assets/src-Brzfja-q-DNc4Or4z.js +1 -0
  193. package/client/assets/src-Cqa3Jy1j.js +1 -0
  194. package/client/assets/stateDiagram-2N3HPSRC-CdlGXuo7.js +1 -0
  195. package/client/assets/stateDiagram-2N3HPSRC-T4-clK8b-DQRiB4va.js +1 -0
  196. package/client/assets/stateDiagram-v2-6OUMAXLB-BN8Jr3ZM.js +1 -0
  197. package/client/assets/stateDiagram-v2-6OUMAXLB-DIp7nhRd-DbrOhUn_.js +1 -0
  198. package/client/assets/swimlanes-5IMT3BWC-HmQNEntu-CNBgZdur.js +2 -0
  199. package/client/assets/swimlanes-5IMT3BWC-iEJg5gki.js +2 -0
  200. package/client/assets/swimlanesDiagram-G3AALYLV-BeoZhwg7-DpqgeNRA.js +8 -0
  201. package/client/assets/swimlanesDiagram-G3AALYLV-o-sS6aEP.js +8 -0
  202. package/client/assets/timeline-definition-FHXFAJF6-Bjh-7QmL.js +120 -0
  203. package/client/assets/timeline-definition-FHXFAJF6-CNc9jSTP-BKNSymgY.js +120 -0
  204. package/client/assets/treeView-QDETBFTQ-D3cqRl6k.js +1 -0
  205. package/client/assets/treeView-QDETBFTQ-nIQcG1h9-O_ri9HxD.js +1 -0
  206. package/client/assets/treemap-6X3UGDF4-BnsvC8yL-Cfsty1e3.js +1 -0
  207. package/client/assets/treemap-6X3UGDF4-DGkZcvDG.js +1 -0
  208. package/client/assets/v4-B29ayslu.js +1 -0
  209. package/client/assets/vennDiagram-L72KCM5P-Cz3dbe7b.js +34 -0
  210. package/client/assets/vennDiagram-L72KCM5P-Dr3pTJ_0-C38xwcVo.js +34 -0
  211. package/client/assets/vue-C6d2VveO.js +1 -0
  212. package/client/assets/vue-i18n-CL-ejuNu.js +3 -0
  213. package/client/assets/vue.runtime.esm-bundler-BeoTfMNc.js +4 -0
  214. package/client/assets/wardley-OPB4EBWU-8Odxkx6V-CCDIVuBs.js +1 -0
  215. package/client/assets/wardley-OPB4EBWU-CZsCDvKk.js +1 -0
  216. package/client/assets/wardleyDiagram-EHGQE667-DSFc7ZZa-mlWXRiWx.js +78 -0
  217. package/client/assets/wardleyDiagram-EHGQE667-DiyoyO_X.js +78 -0
  218. package/client/assets/xychartDiagram-FW5EYKEG-BP0Nn4Pp-DRRiStGZ.js +7 -0
  219. package/client/assets/xychartDiagram-FW5EYKEG-DhY1_UGM.js +7 -0
  220. package/client/index.html +16 -12
  221. package/package.json +17 -19
  222. package/server/agent/backend/types.ts +1 -1
  223. package/server/agent/backgroundSessions.ts +43 -0
  224. package/server/agent/config.ts +3 -2
  225. package/server/agent/mcp-tools/manageCollection.ts +4 -4
  226. package/server/agent/sandboxMounts.ts +1 -1
  227. package/server/agent/stream.ts +1 -3
  228. package/server/api/auth/viewToken.ts +1 -1
  229. package/server/api/bridge/sessionRole.ts +47 -0
  230. package/server/api/routes/agent.ts +79 -2
  231. package/server/api/routes/collections.ts +60 -7
  232. package/server/api/routes/collectionsRegistry.ts +113 -0
  233. package/server/api/routes/dashboard.ts +47 -0
  234. package/server/api/routes/feeds.ts +2 -2
  235. package/server/api/routes/files.ts +1 -2
  236. package/server/api/routes/pdf.ts +48 -9
  237. package/server/api/routes/plugins.ts +2 -2
  238. package/server/api/routes/remoteHost.ts +55 -0
  239. package/server/api/routes/sessions.ts +20 -11
  240. package/server/api/routes/share.ts +105 -0
  241. package/server/api/routes/transcribe.ts +1 -1
  242. package/server/api/routes/wiki/history.ts +2 -2
  243. package/server/api/routes/wiki.ts +29 -177
  244. package/server/api/sandboxStatus.ts +1 -1
  245. package/server/build/dispatcher.mjs +25 -14928
  246. package/server/events/collection-change.ts +4 -4
  247. package/server/events/file-change.ts +2 -2
  248. package/server/events/scheduler-adapter.ts +3 -3
  249. package/server/events/task-manager/index.ts +3 -3
  250. package/server/index.ts +53 -17
  251. package/server/notifier/engine.ts +3 -3
  252. package/server/notifier/types.ts +2 -2
  253. package/server/plugins/html-builtin.ts +15 -1
  254. package/server/prompts/system/system.md +4 -0
  255. package/server/remoteHost/auth.ts +29 -0
  256. package/server/remoteHost/commandChannel.ts +56 -0
  257. package/server/remoteHost/firebase.ts +16 -0
  258. package/server/remoteHost/handlers/collectionPage.ts +29 -0
  259. package/server/remoteHost/handlers/getCollection.ts +32 -0
  260. package/server/remoteHost/handlers/getFeed.ts +35 -0
  261. package/server/remoteHost/handlers/index.ts +19 -0
  262. package/server/remoteHost/handlers/listCollections.ts +29 -0
  263. package/server/remoteHost/handlers/listFeeds.ts +37 -0
  264. package/server/remoteHost/handlers/listShortcuts.ts +24 -0
  265. package/server/remoteHost/handlers/startChat.ts +78 -0
  266. package/server/remoteHost/hostRunner.ts +122 -0
  267. package/server/remoteHost/index.ts +117 -0
  268. package/server/system/config.ts +1 -1
  269. package/server/system/credentials.ts +2 -3
  270. package/server/system/docker.ts +9 -8
  271. package/server/system/env.ts +8 -0
  272. package/server/system/whisper/index.ts +4 -4
  273. package/server/utils/claudeConfigPath.ts +57 -0
  274. package/server/utils/files/dashboard-io.ts +87 -0
  275. package/server/utils/files/session-io.ts +11 -0
  276. package/server/utils/markdown/frontmatter.ts +8 -4
  277. package/server/utils/regex.ts +4 -3
  278. package/server/utils/share/packHtml.ts +108 -0
  279. package/server/utils/share/rewriteAssets.ts +178 -0
  280. package/server/workspace/chat-index/paths.ts +8 -2
  281. package/server/workspace/chat-index/summarizer.ts +23 -15
  282. package/server/workspace/collections/configure.ts +5 -3
  283. package/server/workspace/collections/index.ts +8 -6
  284. package/server/workspace/collections/notifications.ts +3 -3
  285. package/server/workspace/collections/types.ts +2 -2
  286. package/server/workspace/collections/watcher.ts +2 -2
  287. package/server/workspace/feeds/configure.ts +24 -0
  288. package/server/workspace/hooks/handlers/skillBridge.ts +2 -2
  289. package/server/workspace/hooks/handlers/wikiSnapshot.ts +1 -1
  290. package/server/workspace/paths.ts +8 -1
  291. package/server/workspace/skills/catalog.ts +1 -3
  292. package/server/workspace/skills/external/catalog.ts +1 -3
  293. package/server/workspace/skills/paths.ts +4 -3
  294. package/server/workspace/skills/writer.ts +1 -4
  295. package/server/workspace/skills-preset.ts +3 -3
  296. package/server/workspace/wiki-pages/io.ts +2 -2
  297. package/server/workspace/workspace.ts +2 -2
  298. package/src/App.vue +41 -18
  299. package/src/components/ChatInput.vue +1 -1
  300. package/src/components/DashboardView.vue +372 -0
  301. package/src/components/FileContentRenderer.vue +32 -7
  302. package/src/components/JsonEditor.vue +1 -1
  303. package/src/components/PluginLauncher.vue +122 -79
  304. package/src/components/RemoteHostControl.vue +159 -0
  305. package/src/components/SessionCountBadges.vue +32 -0
  306. package/src/components/SessionHeaderControls.vue +1 -8
  307. package/src/components/SessionHistoryPanel.vue +2 -0
  308. package/src/components/SessionHistoryToggleButton.vue +1 -15
  309. package/src/components/SidebarHeader.vue +2 -0
  310. package/src/components/collectionTypes.ts +2 -2
  311. package/src/composables/accountingHost.ts +24 -0
  312. package/src/composables/collections/uiHost.ts +17 -2
  313. package/src/composables/useDashboard.ts +191 -0
  314. package/src/composables/useMarkdownZip.ts +51 -0
  315. package/src/composables/useNotifications.ts +1 -4
  316. package/src/composables/useSharePack.ts +60 -0
  317. package/src/composables/useTranslatedQueries.ts +15 -95
  318. package/src/composables/useTranslatedStrings.ts +68 -0
  319. package/src/composables/useVoiceInput.ts +3 -3
  320. package/src/config/apiRoutes.ts +57 -9
  321. package/src/config/createFilePolicy.ts +1 -1
  322. package/src/config/firebase.ts +12 -0
  323. package/src/config/firebaseConfig.ts +18 -0
  324. package/src/config/historyFilters.ts +7 -0
  325. package/src/config/pubsubChannels.ts +7 -27
  326. package/src/config/roles.ts +12 -10
  327. package/src/config/workspacePaths.ts +10 -0
  328. package/src/index.css +1 -1
  329. package/src/lang/de.ts +39 -220
  330. package/src/lang/en.ts +39 -191
  331. package/src/lang/es.ts +39 -218
  332. package/src/lang/fr.ts +39 -220
  333. package/src/lang/ja.ts +39 -216
  334. package/src/lang/ko.ts +39 -216
  335. package/src/lang/pt-BR.ts +39 -217
  336. package/src/lang/zh.ts +39 -215
  337. package/src/main.ts +3 -0
  338. package/src/plugins/accounting/definition.ts +12 -8
  339. package/src/plugins/accounting/index.ts +7 -4
  340. package/src/plugins/accounting/meta.ts +23 -60
  341. package/src/plugins/manageSkills/View.vue +9 -1
  342. package/src/plugins/photoLocations/View.vue +2 -2
  343. package/src/plugins/presentCollection/definition.ts +2 -2
  344. package/src/plugins/presentCollection/plugin.ts +2 -2
  345. package/src/plugins/presentCollection/types.ts +2 -2
  346. package/src/plugins/presentHtml/definition.ts +1 -1
  347. package/src/plugins/presentMulmoScript/View.vue +2 -2
  348. package/src/plugins/presentMulmoScript/helpers.ts +1 -1
  349. package/src/plugins/presentSVG/View.vue +2 -2
  350. package/src/plugins/scheduler/TasksTab.vue +1 -2
  351. package/src/plugins/skill/View.vue +7 -3
  352. package/src/plugins/spreadsheet/View.vue +3 -3
  353. package/src/plugins/spreadsheet/engine/functions/logical.ts +8 -1
  354. package/src/plugins/textResponse/View.vue +43 -16
  355. package/src/plugins/wiki/View.vue +29 -2
  356. package/src/plugins/wiki/components/WikiGraphView.vue +1 -1
  357. package/src/plugins/wiki/components/WikiPageBody.vue +4 -1
  358. package/src/plugins/wiki/helpers.ts +13 -81
  359. package/src/plugins/wiki/index.ts +1 -1
  360. package/src/router/guards.ts +1 -1
  361. package/src/router/index.ts +12 -0
  362. package/src/router/pageRoutes.ts +2 -0
  363. package/src/types/dashboard.ts +40 -0
  364. package/src/types/session.ts +3 -0
  365. package/src/utils/blobDownload.ts +19 -0
  366. package/src/utils/collections/presentSeed.ts +1 -1
  367. package/src/utils/html/customViewSrcdoc.ts +31 -3
  368. package/src/utils/html/previewCsp.ts +1 -1
  369. package/src/utils/id.ts +1 -1
  370. package/src/utils/image/htmlSrcAttrs.ts +1 -1
  371. package/src/utils/markdown/frontmatter.ts +12 -7
  372. package/src/utils/markdown/highlight.ts +22 -0
  373. package/src/utils/markdown/marpCustomSize.ts +2 -2
  374. package/src/utils/markdown/mermaidExtension.ts +56 -0
  375. package/src/utils/markdown/mermaidRender.ts +147 -0
  376. package/src/utils/markdown/setup.ts +17 -4
  377. package/src/utils/markdown/useMermaid.ts +33 -0
  378. package/src/utils/session/longRunning.ts +33 -0
  379. package/src/utils/session/mergeSessions.ts +1 -1
  380. package/client/assets/JsonEditor-Di5xGeZY.css +0 -1
  381. package/client/assets/JsonEditor-o5--tPQH.js +0 -10
  382. package/client/assets/index-tOu5ArRZ.css +0 -2
  383. package/client/assets/lib-D6Xy0IFc.js +0 -114
  384. package/client/assets/marp-D6GXA-EB.js +0 -3452
  385. package/client/assets/material-symbols-outlined-DtIK7AQn.woff2 +0 -0
  386. package/client/assets/runtime-protocol-vue-pU0Mw7Zm.js +0 -1
  387. package/client/assets/runtime-vue-fFYhnNg3.js +0 -1
  388. package/client/assets/vue-UDIWDtr8.js +0 -1
  389. package/client/assets/vue-i18n-CQbxVmNs.js +0 -3
  390. package/client/assets/vue.runtime.esm-bundler-BTyIdNAI.js +0 -4
  391. package/server/accounting/accountNormalize.ts +0 -32
  392. package/server/accounting/defaultAccounts.ts +0 -87
  393. package/server/accounting/eventPublisher.ts +0 -52
  394. package/server/accounting/journal.ts +0 -252
  395. package/server/accounting/openingBalances.ts +0 -114
  396. package/server/accounting/report.ts +0 -237
  397. package/server/accounting/service.ts +0 -718
  398. package/server/accounting/snapshotCache.ts +0 -334
  399. package/server/accounting/timeSeries.ts +0 -265
  400. package/server/accounting/types.ts +0 -148
  401. package/server/api/routes/accounting.ts +0 -373
  402. package/server/api/routes/wiki/frontmatter.ts +0 -34
  403. package/server/api/routes/wiki/pageIndex.ts +0 -53
  404. package/server/system/logs/aaa +0 -737
  405. package/server/system/logs/bb +0 -446
  406. package/server/utils/files/accounting-io.ts +0 -294
  407. package/server/workspace/feeds/engine.ts +0 -143
  408. package/server/workspace/feeds/fetch/httpClient.ts +0 -127
  409. package/server/workspace/feeds/fetch/rssParser.ts +0 -117
  410. package/server/workspace/feeds/index.ts +0 -8
  411. package/server/workspace/feeds/ingestTypes.ts +0 -62
  412. package/server/workspace/feeds/pathResolver.ts +0 -74
  413. package/server/workspace/feeds/paths.ts +0 -30
  414. package/server/workspace/feeds/projectItem.ts +0 -92
  415. package/server/workspace/feeds/registry.ts +0 -43
  416. package/server/workspace/feeds/retrievers/httpJson.ts +0 -19
  417. package/server/workspace/feeds/retrievers/index.ts +0 -27
  418. package/server/workspace/feeds/retrievers/registerAll.ts +0 -5
  419. package/server/workspace/feeds/retrievers/rss.ts +0 -24
  420. package/server/workspace/feeds/state.ts +0 -55
  421. package/src/composables/useAccountingChannel.ts +0 -58
  422. package/src/lib/wiki-page/graph.ts +0 -108
  423. package/src/lib/wiki-page/index-parse.ts +0 -221
  424. package/src/lib/wiki-page/link.ts +0 -62
  425. package/src/lib/wiki-page/lint.ts +0 -105
  426. package/src/lib/wiki-page/paths.ts +0 -35
  427. package/src/lib/wiki-page/slug.ts +0 -54
  428. package/src/plugins/accounting/Preview.vue +0 -103
  429. package/src/plugins/accounting/View.vue +0 -633
  430. package/src/plugins/accounting/actions.ts +0 -34
  431. package/src/plugins/accounting/api.ts +0 -301
  432. package/src/plugins/accounting/components/AccountEditor.vue +0 -250
  433. package/src/plugins/accounting/components/AccountRow.vue +0 -50
  434. package/src/plugins/accounting/components/AccountsList.vue +0 -102
  435. package/src/plugins/accounting/components/AccountsModal.vue +0 -301
  436. package/src/plugins/accounting/components/BalanceSheet.vue +0 -186
  437. package/src/plugins/accounting/components/BookSettings.vue +0 -284
  438. package/src/plugins/accounting/components/BookSwitcher.vue +0 -78
  439. package/src/plugins/accounting/components/DateRangePicker.vue +0 -140
  440. package/src/plugins/accounting/components/JournalEntryForm.vue +0 -505
  441. package/src/plugins/accounting/components/JournalList.vue +0 -554
  442. package/src/plugins/accounting/components/Ledger.vue +0 -206
  443. package/src/plugins/accounting/components/NewBookForm.vue +0 -211
  444. package/src/plugins/accounting/components/OpeningBalancesForm.vue +0 -272
  445. package/src/plugins/accounting/components/ProfitLoss.vue +0 -160
  446. package/src/plugins/accounting/components/accountDraft.ts +0 -13
  447. package/src/plugins/accounting/components/accountNumbering.ts +0 -103
  448. package/src/plugins/accounting/components/accountValidation.ts +0 -75
  449. package/src/plugins/accounting/components/useLatestRequest.ts +0 -44
  450. package/src/plugins/accounting/countries.ts +0 -158
  451. package/src/plugins/accounting/currencies.ts +0 -77
  452. package/src/plugins/accounting/dates.ts +0 -51
  453. package/src/plugins/accounting/fiscalYear.ts +0 -136
  454. package/src/plugins/accounting/timeSeriesEnums.ts +0 -16
  455. package/src/plugins/wiki/route.ts +0 -137
  456. /package/client/assets/{_plugin-vue_export-helper-B67ILkmu.js → _plugin-vue_export-helper-BDNMzG2s.js} +0 -0
  457. /package/client/assets/{purify.es-B27wDFIb-51iYcXuK.js → purify.es-B27wDFIb-Bu4Grnl0.js} +0 -0
  458. /package/client/assets/{typeof-DBp4T-Ny-z2wCIsir.js → typeof-DBp4T-Ny-B5XbjTb1.js} +0 -0
@@ -34,6 +34,7 @@ interface SessionMeta {
34
34
  hasUnread?: boolean;
35
35
  isBookmarked?: boolean;
36
36
  origin?: SessionOrigin;
37
+ userQueryCount?: number;
37
38
  }
38
39
 
39
40
  async function readSessionMeta(__chatDir: string, sessionId: string): Promise<SessionMeta | null> {
@@ -77,6 +78,9 @@ export interface SessionSummary {
77
78
  // User-toggled bookmark — surfaced in the history panel as a
78
79
  // dedicated filter chip and a green role-icon tint.
79
80
  isBookmarked?: boolean;
81
+ // Number of user turns sent to this session. Lets the history panel
82
+ // tell a one-shot (1) apart from a long-running conversation.
83
+ userQueryCount?: number;
80
84
  // Live state from the in-memory session store. Absent when the
81
85
  // session has no active entry in the store (i.e. idle / historical).
82
86
  //
@@ -130,6 +134,20 @@ interface SessionRow {
130
134
  changeMs: number;
131
135
  }
132
136
 
137
+ // Fold the live in-memory session state onto a summary. Extracted so
138
+ // `buildSessionSummary` stays under the cognitive-complexity threshold.
139
+ function applyLiveState(summary: SessionSummary, live: NonNullable<ReturnType<typeof getSession>>): void {
140
+ // Background generations (image/audio/movie) keep the session "busy"
141
+ // even when the agent turn has ended, so the sidebar indicator stays
142
+ // lit across view navigation.
143
+ summary.isRunning = live.isRunning || Object.keys(live.pendingGenerations).length > 0;
144
+ // Narrow predicate — must stay byte-identical to the DELETE 409 gate
145
+ // (`getSession(sessionId)?.isRunning`) so a caller polling this can
146
+ // trust "false ⇒ DELETE will be accepted".
147
+ summary.liveIsRunning = live.isRunning;
148
+ summary.statusMessage = live.statusMessage;
149
+ }
150
+
133
151
  // Build a SessionSummary from the gathered inputs. Conditional spreads
134
152
  // honour the server tsconfig's `exactOptionalPropertyTypes`; that's
135
153
  // why each optional field is set with `if (… !== undefined)` rather
@@ -153,19 +171,10 @@ function buildSessionSummary(
153
171
  };
154
172
  if (meta.origin) summary.origin = meta.origin;
155
173
  if (meta.isBookmarked) summary.isBookmarked = true;
174
+ if (typeof meta.userQueryCount === "number") summary.userQueryCount = meta.userQueryCount;
156
175
  if (indexEntry?.summary !== undefined) summary.summary = indexEntry.summary;
157
176
  if (indexEntry?.keywords !== undefined) summary.keywords = indexEntry.keywords;
158
- if (live) {
159
- // Background generations (image/audio/movie) keep the session
160
- // "busy" even when the agent turn has ended, so the sidebar
161
- // indicator stays lit across view navigation.
162
- summary.isRunning = live.isRunning || Object.keys(live.pendingGenerations).length > 0;
163
- // Narrow predicate — must stay byte-identical to the DELETE
164
- // 409 gate (`getSession(sessionId)?.isRunning`) so a caller
165
- // polling this can trust "false ⇒ DELETE will be accepted".
166
- summary.liveIsRunning = live.isRunning;
167
- summary.statusMessage = live.statusMessage;
168
- }
177
+ if (live) applyLiveState(summary, live);
169
178
  return summary;
170
179
  }
171
180
 
@@ -0,0 +1,105 @@
1
+ import { Router, Request, Response } from "express";
2
+ import { API_ROUTES } from "../../../src/config/apiRoutes.js";
3
+ import { WORKSPACE_DIRS } from "../../workspace/paths.js";
4
+ import { packHtmlZip, zipBundle, safeZipName } from "../../utils/share/packHtml.js";
5
+ import { renderMarkdownHtml } from "./pdf.js";
6
+ import { isHtmlPath } from "../../utils/files/html-store.js";
7
+ import { badRequest, serverError } from "../../utils/httpError.js";
8
+ import { errorMessage } from "../../utils/errors.js";
9
+ import { log } from "../../system/logger/index.js";
10
+
11
+ const router = Router();
12
+
13
+ interface PackBody {
14
+ path?: string;
15
+ }
16
+
17
+ // Route-boundary guard: only a canonical `.html` under artifacts/html is
18
+ // packable. Delegates to the repo's shared HTML path policy (`isHtmlPath`
19
+ // = prefix + `.html` ext + canonical form + no `..` + no traversal
20
+ // segment) so malformed input is a deterministic 400, never a 500 from
21
+ // deeper code.
22
+ export function isPackablePath(value: unknown): value is string {
23
+ return typeof value === "string" && isHtmlPath(value);
24
+ }
25
+
26
+ // POST /api/share/pack — bundle an HTML artifact and its referenced
27
+ // local assets into a single self-contained zip (index.html + assets/),
28
+ // returned as a download. Paths are rewritten to be relative so the
29
+ // unzipped folder opens directly over file://.
30
+ router.post(API_ROUTES.share.pack, async (req: Request<object, unknown, PackBody>, res: Response) => {
31
+ const htmlPath = req.body?.path;
32
+ if (!isPackablePath(htmlPath)) {
33
+ badRequest(res, `path must be a canonical ${WORKSPACE_DIRS.htmls}/*.html file`);
34
+ return;
35
+ }
36
+ try {
37
+ const { filename, zip } = await packHtmlZip(htmlPath);
38
+ log.info("share", "pack: ok", { path: htmlPath, bytes: zip.length });
39
+ res.setHeader("Content-Type", "application/zip");
40
+ res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
41
+ res.send(zip);
42
+ } catch (err) {
43
+ // Log the detail server-side; return a generic message so an internal
44
+ // path / stack never reaches the client.
45
+ log.error("share", "pack: threw", { path: htmlPath, error: errorMessage(err) });
46
+ serverError(res, "failed to pack HTML bundle");
47
+ }
48
+ });
49
+
50
+ interface PackMarkdownBody {
51
+ markdown?: string;
52
+ filename?: string;
53
+ baseDir?: string;
54
+ stripFrontmatter?: boolean;
55
+ marp?: boolean;
56
+ }
57
+
58
+ // A shared markdown zip is opened directly by the recipient (unlike the
59
+ // PDF path, which puppeteer renders once), so neutralize any script the
60
+ // source markdown carries — `marked` passes raw HTML through. A strict
61
+ // CSP blocks `<script>` / inline handlers / `javascript:` and plugins
62
+ // without stripping content or touching images/styles.
63
+ const SHARE_MARKDOWN_CSP = "script-src 'none'; object-src 'none'; base-uri 'none'";
64
+
65
+ export function withScriptCsp(html: string): string {
66
+ return html.replace(/<head>/i, `<head><meta http-equiv="Content-Security-Policy" content="${SHARE_MARKDOWN_CSP}">`);
67
+ }
68
+
69
+ async function buildMarkdownZip(body: PackMarkdownBody): Promise<{ filename: string; zip: Buffer }> {
70
+ const markdown = typeof body.markdown === "string" ? body.markdown : "";
71
+ const baseDir = typeof body.baseDir === "string" ? body.baseDir : undefined;
72
+ const html = withScriptCsp(await renderMarkdownHtml({ markdown, baseDir, stripFrontmatter: body.stripFrontmatter === true, marp: body.marp === true }));
73
+ const zip = await zipBundle([{ bundlePath: "index.html", bytes: Buffer.from(html, "utf-8") }]);
74
+ const filename = typeof body.filename === "string" ? body.filename : "document";
75
+ return { filename: safeZipName(filename.replace(/\.(md|markdown|html?|pdf)$/i, "")), zip };
76
+ }
77
+
78
+ // POST /api/share/pack-markdown — render markdown (or a wiki page) to a
79
+ // self-contained HTML (CSS inlined, images embedded as data URIs, scripts
80
+ // neutralized) and return it zipped as index.html. Shares the render path
81
+ // with the PDF route (`renderMarkdownHtml`). `baseDir` resolves relative
82
+ // image refs; traversal is rejected downstream by the shared image resolver.
83
+ router.post(API_ROUTES.share.packMarkdown, async (req: Request<object, unknown, PackMarkdownBody>, res: Response) => {
84
+ const { body } = req;
85
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
86
+ badRequest(res, "request body must be a JSON object");
87
+ return;
88
+ }
89
+ if (typeof body.markdown !== "string" || !body.markdown) {
90
+ badRequest(res, "markdown is required");
91
+ return;
92
+ }
93
+ try {
94
+ const { filename, zip } = await buildMarkdownZip(body);
95
+ log.info("share", "pack-markdown: ok", { bytes: zip.length, marp: body.marp === true });
96
+ res.setHeader("Content-Type", "application/zip");
97
+ res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
98
+ res.send(zip);
99
+ } catch (err) {
100
+ log.error("share", "pack-markdown: threw", { error: errorMessage(err) });
101
+ serverError(res, "failed to pack markdown bundle");
102
+ }
103
+ });
104
+
105
+ export default router;
@@ -5,7 +5,7 @@
5
5
  // POST /api/transcribe/model/download start the model download (opt-in)
6
6
  //
7
7
  // All transcription happens on the machine running this server; no
8
- // audio leaves it. See plans/feat-voice-input.md.
8
+ // audio leaves it. See plans/done/feat-voice-input.md.
9
9
 
10
10
  import { Router, Request, Response } from "express";
11
11
  import { API_ROUTES } from "../../../src/config/apiRoutes.js";
@@ -8,7 +8,7 @@
8
8
  //
9
9
  // Path safety: both `:slug` and `:stamp` are validated *before*
10
10
  // they are joined with the workspace root. The slug check is the
11
- // shared `isSafeSlug` from `src/lib/wiki-page/slug.ts`; the stamp
11
+ // shared `isSafeSlug` from `@mulmoclaude/core/wiki`; the stamp
12
12
  // check is the `FILENAME_RE` shape exposed via `isSafeStamp`.
13
13
 
14
14
  import { Router, type Request, type Response } from "express";
@@ -25,7 +25,7 @@ import { workspacePath } from "../../../workspace/workspace.js";
25
25
  import { pushToolResult } from "../../../events/session-store/index.js";
26
26
  import { log } from "../../../system/logger/index.js";
27
27
  import { errorMessage } from "../../../utils/errors.js";
28
- import { isSafeSlug } from "../../../../src/lib/wiki-page/slug.js";
28
+ import { isSafeSlug } from "@mulmoclaude/core/wiki";
29
29
 
30
30
  const router = Router();
31
31
 
@@ -1,10 +1,7 @@
1
1
  import { Router, Request, Response } from "express";
2
2
  import path from "path";
3
- import { WORKSPACE_PATHS } from "../../workspace/paths.js";
4
- import { readTextSafeSync, readTextSafe } from "../../utils/files/safe.js";
3
+ import { workspacePath } from "../../workspace/paths.js";
5
4
  import { writeWikiPage } from "../../workspace/wiki-pages/io.js";
6
- import { getPageIndex } from "./wiki/pageIndex.js";
7
- import { parseFrontmatterTags } from "./wiki/frontmatter.js";
8
5
  import { badRequest, notFound } from "../../utils/httpError.js";
9
6
  import { getOptionalStringQuery } from "../../utils/request.js";
10
7
  import { API_ROUTES } from "../../../src/config/apiRoutes.js";
@@ -15,115 +12,28 @@ import { previewSnippet } from "../../utils/logPreview.js";
15
12
  // different name avoids the no-shadow clash without renaming the
16
13
  // long-standing local.
17
14
  import { errorMessage as formatError } from "../../utils/errors.js";
18
- // All wiki-text helpers (slug rules, index parsing, lint rules)
19
- // live in `src/lib/wiki-page/` as pure modules so the frontend can
20
- // share them without pulling in server-only deps. This route is
21
- // just the HTTP shell on top of those.
22
- import { parseWikiLink } from "../../../src/lib/wiki-page/link.js";
23
- import { wikiSlugify } from "../../../src/lib/wiki-page/slug.js";
24
- import { type WikiPageEntry, parseIndexEntries } from "../../../src/lib/wiki-page/index-parse.js";
25
- import { findBrokenLinksInPage, findMissingFiles, findOrphanPages, findTagDrift, formatLintReport } from "../../../src/lib/wiki-page/lint.js";
26
- import { buildWikiGraph, type WikiGraph } from "../../../src/lib/wiki-page/graph.js";
15
+ // Pure wiki-text helpers (slug rules, lint formatting, types) live in
16
+ // `@mulmoclaude/core/wiki`; the workspace-injected fs read-engine
17
+ // (resolve / index / page / log / graph / lint) lives in
18
+ // `@mulmoclaude/core/wiki/server`, shared with MulmoTerminal. This
19
+ // route is the thin HTTP shell that shapes their output into the
20
+ // canvas response envelope.
21
+ import { wikiSlugify, formatLintReport, type WikiPageEntry, type WikiGraph } from "@mulmoclaude/core/wiki";
22
+ import { resolvePagePath, readWikiIndex, readWikiPage, readWikiLog, loadWikiGraph, collectLintIssues } from "@mulmoclaude/core/wiki/server";
27
23
 
28
24
  const router = Router();
29
25
 
30
- const pagesDir = () => WORKSPACE_PATHS.wikiPages;
31
- const indexFile = () => WORKSPACE_PATHS.wikiIndex;
32
- const logFile = () => WORKSPACE_PATHS.wikiLog;
26
+ // Re-exports kept here for legacy callers that import wiki-text
27
+ // helpers via this route module (e.g. tests in
28
+ // `test/routes/test_wikiHelpers.ts`).
29
+ export { wikiSlugify, buildTableColumnMap, extractHashTags, extractSlugFromBulletHref, parseIndexEntries, parseTagsCell } from "@mulmoclaude/core/wiki";
30
+ export type { WikiPageEntry } from "@mulmoclaude/core/wiki";
31
+ export { findBrokenLinksInPage, findMissingFiles, findOrphanPages, findTagDrift, formatLintReport } from "@mulmoclaude/core/wiki";
33
32
 
34
- function readFileOrEmpty(absPath: string): string {
35
- return readTextSafeSync(absPath) ?? "";
36
- }
37
-
38
- // Wiki-text helpers (slugify, index parsing, lint rules) now live
39
- // under `src/lib/wiki-page/` as pure modules — see imports above.
40
- // Re-exports kept here for legacy callers that import via this
41
- // route module (e.g. tests in `test/routes/test_wikiHelpers.ts`).
42
- export { wikiSlugify } from "../../../src/lib/wiki-page/slug.js";
43
- export type { WikiPageEntry } from "../../../src/lib/wiki-page/index-parse.js";
44
- export { buildTableColumnMap, extractHashTags, extractSlugFromBulletHref, parseIndexEntries, parseTagsCell } from "../../../src/lib/wiki-page/index-parse.js";
45
- export { findBrokenLinksInPage, findMissingFiles, findOrphanPages, findTagDrift, formatLintReport } from "../../../src/lib/wiki-page/lint.js";
46
-
47
- // Resolve a page name to an absolute `.md` path using the in-memory
48
- // page index (see ./wiki/pageIndex.ts). Index is kept fresh via
49
- // pagesDir mtime, so zero readdir cost on cache hit.
50
- //
51
- // `pageName` may carry the `[[target|display]]` form on legacy
52
- // codepaths (e.g. an old chat-history entry that pre-dates the
53
- // renderer fix). `parseWikiLink` strips the display half so the
54
- // lookup uses just the target — same parser the renderer + lint
55
- // agree on, which is the whole point of the #1297 refactor.
56
- // Below this length the fuzzy `includes` step skips altogether.
57
- // CJK / emoji-only / very-short page names get slugified down to a
58
- // short noise tail (e.g. "日本語タイトル-chromium-{nonce}" →
59
- // "-chromium-{nonce}"), and that noise will partial-match almost
60
- // any page that happens to share the suffix. Skipping fuzzy for
61
- // these slugs avoids silent miss-resolves; the index.md title-match
62
- // fallback below still handles the legitimate non-ASCII case (#1194).
63
- const MIN_FUZZY_SLUG_LEN = 6;
64
-
65
- async function resolvePagePath(pageName: string): Promise<string | null> {
66
- const dir = pagesDir();
67
- const { slugs } = await getPageIndex(dir);
68
- if (slugs.size === 0) return null;
69
-
70
- const { target } = parseWikiLink(pageName);
71
- const slug = wikiSlugify(target);
72
-
73
- if (slug.length > 0) {
74
- const exact = slugs.get(slug);
75
- if (exact) return path.join(dir, exact);
76
-
77
- const fuzzy = pickFuzzyMatch(slug, slugs);
78
- if (fuzzy) return path.join(dir, fuzzy);
79
- }
80
-
81
- // Non-ASCII page names (e.g. Japanese [[wiki links]]) produce empty
82
- // slugs after slugification. Fall back to matching by title in the
83
- // wiki index so the link resolves to its page file.
84
- const indexContent = readFileOrEmpty(indexFile());
85
- const entries = parseIndexEntries(indexContent);
86
- const titleMatch = entries.find((entry) => entry.title === target);
87
- if (titleMatch) {
88
- const file = slugs.get(titleMatch.slug);
89
- if (file) return path.join(dir, file);
90
- }
91
-
92
- return null;
93
- }
94
-
95
- // Walk every indexed slug looking for an `includes`-style match.
96
- // Returns the single best candidate, or null when the slug is too
97
- // short to be meaningful OR multiple candidates tie at the top score
98
- // (ambiguous — leave the resolution to the caller's title-match
99
- // fallback rather than silently picking iteration-order-first).
100
- //
101
- // Score = min(slug.length, key.length) / max(slug.length, key.length).
102
- // A perfect match where both strings are identical scores 1.0 (but
103
- // that path is taken by the exact `slugs.get` above, never reaches
104
- // here). Otherwise the highest-scoring partial match wins, and the
105
- // score is decoupled from Map iteration order (= filesystem readdir
106
- // order) so the resolver is deterministic across hosts.
107
- export function pickFuzzyMatch(slug: string, slugs: ReadonlyMap<string, string>): string | null {
108
- if (slug.length < MIN_FUZZY_SLUG_LEN) return null;
109
- let bestFile: string | null = null;
110
- let bestScore = 0;
111
- let bestIsTied = false;
112
- for (const [key, file] of slugs) {
113
- if (!slug.includes(key) && !key.includes(slug)) continue;
114
- const shorter = Math.min(slug.length, key.length);
115
- const longer = Math.max(slug.length, key.length);
116
- const score = shorter / longer;
117
- if (score > bestScore) {
118
- bestScore = score;
119
- bestFile = file;
120
- bestIsTied = false;
121
- } else if (score === bestScore) {
122
- bestIsTied = true;
123
- }
124
- }
125
- return bestIsTied ? null : bestFile;
126
- }
33
+ // `resolvePagePath` / `pickFuzzyMatch` now live in
34
+ // `@mulmoclaude/core/wiki/server` (shared with MulmoTerminal). This
35
+ // route resolves pages through the imported `readWikiPage` /
36
+ // `resolvePagePath`.
127
37
 
128
38
  router.get(API_ROUTES.wiki.base, async (req: Request, res: Response<WikiResponse | ErrorResponse>) => {
129
39
  const slug = getOptionalStringQuery(req, "slug");
@@ -144,8 +54,7 @@ router.get(API_ROUTES.wiki.base, async (req: Request, res: Response<WikiResponse
144
54
  return;
145
55
  }
146
56
  log.info("wiki", "GET index: start");
147
- const content = readFileOrEmpty(indexFile());
148
- const pageEntries = parseIndexEntries(content);
57
+ const { content, entries: pageEntries } = readWikiIndex(workspacePath);
149
58
  log.info("wiki", "GET index: ok", { pages: pageEntries.length, bytes: content.length });
150
59
  res.json({
151
60
  data: { action: "index", title: "Wiki Index", content, pageEntries },
@@ -187,8 +96,7 @@ interface ErrorResponse {
187
96
  }
188
97
 
189
98
  function buildIndexResponse(action: string): WikiResponse {
190
- const content = readFileOrEmpty(indexFile());
191
- const pageEntries = parseIndexEntries(content);
99
+ const { content, entries: pageEntries } = readWikiIndex(workspacePath);
192
100
  return {
193
101
  data: { action, title: "Wiki Index", content, pageEntries },
194
102
  message: content ? `Wiki index — ${pageEntries.length} page(s)` : "Wiki index is empty.",
@@ -261,13 +169,12 @@ export function toPageResponse(args: { action: string; pageName: string; filePat
261
169
  }
262
170
 
263
171
  async function buildPageResponse(action: string, pageName: string): Promise<WikiResponse> {
264
- const filePath = await resolvePagePath(pageName);
265
- const content = filePath ? readFileOrEmpty(filePath) : "";
172
+ const { filePath, content } = await readWikiPage(workspacePath, pageName);
266
173
  return toPageResponse({ action, pageName, filePath, content });
267
174
  }
268
175
 
269
176
  function buildLogResponse(action: string): WikiResponse {
270
- const content = readFileOrEmpty(logFile());
177
+ const content = readWikiLog(workspacePath);
271
178
  return {
272
179
  data: { action, title: "Activity Log", content },
273
180
  message: content ? "Wiki activity log" : "Activity log is empty.",
@@ -277,67 +184,12 @@ function buildLogResponse(action: string): WikiResponse {
277
184
  };
278
185
  }
279
186
 
280
- // Pure lint helpers (findOrphanPages, findMissingFiles,
281
- // findBrokenLinksInPage, findTagDrift, formatLintReport) now live
282
- // in `src/lib/wiki-page/lint.ts` and are re-exported above. The
283
- // route below is the thin filesystem shell around them.
284
-
285
- async function collectLintIssues(): Promise<string[]> {
286
- const dir = pagesDir();
287
- const { slugs } = await getPageIndex(dir);
288
- if (slugs.size === 0) {
289
- return ["- Wiki `pages/` directory does not exist yet. Start ingesting sources."];
290
- }
291
- const indexContent = readFileOrEmpty(indexFile());
292
- const pageEntries = parseIndexEntries(indexContent);
293
- const indexedSlugs = new Set(pageEntries.map((entry) => entry.slug));
294
- const pageFiles = [...slugs.values()];
295
- const fileSlugs = new Set(slugs.keys());
296
-
297
- const issues: string[] = [];
298
- issues.push(...findOrphanPages(fileSlugs, indexedSlugs));
299
- issues.push(...findMissingFiles(pageEntries, fileSlugs));
300
- // Parallel read: N small markdown files, ~50 KB each. Bounded by
301
- // the number of wiki pages, not by CPU.
302
- const contents = await Promise.all(
303
- pageFiles.map(async (fileName) => {
304
- const content = await readTextSafe(path.join(dir, fileName));
305
- return content ?? "";
306
- }),
307
- );
308
- const frontmatterTagsBySlug = new Map<string, string[]>();
309
- for (let i = 0; i < pageFiles.length; i++) {
310
- issues.push(...findBrokenLinksInPage(pageFiles[i], contents[i], fileSlugs));
311
- // Lowercase the map key so a `MyPage.md` filename still matches
312
- // an `entry.slug` of `mypage` produced by `wikiSlugify` on the
313
- // wiki-link parser path. `findTagDrift` lowercases the lookup
314
- // side to match.
315
- const slug = pageFiles[i].replace(/\.md$/i, "").toLowerCase();
316
- frontmatterTagsBySlug.set(slug, parseFrontmatterTags(contents[i]));
317
- }
318
- issues.push(...findTagDrift(pageEntries, frontmatterTagsBySlug));
319
- return issues;
320
- }
321
-
322
- // Read every page + the index and build the page→page link graph
323
- // (#wiki-backlinks-graph). Same parallel-read shape as
324
- // `collectLintIssues` — the link resolution is shared with lint via
325
- // the pure `buildWikiGraph` helper. No cache: the `graph` action is
326
- // explicit (user opens the Graph tab / a page's backlinks), and a
327
- // page-content edit doesn't advance the pagesDir mtime that the page
328
- // index caches on, so a content-keyed cache would go stale silently.
329
- async function loadWikiGraph(): Promise<WikiGraph> {
330
- const dir = pagesDir();
331
- const { slugs } = await getPageIndex(dir);
332
- const fileEntries = [...slugs.entries()];
333
- const contents = await Promise.all(fileEntries.map(async ([, fileName]) => (await readTextSafe(path.join(dir, fileName))) ?? ""));
334
- const pages = fileEntries.map(([slug], i) => ({ slug, content: contents[i] }));
335
- const indexEntries = parseIndexEntries(readFileOrEmpty(indexFile()));
336
- return buildWikiGraph(pages, indexEntries);
337
- }
187
+ // `collectLintIssues` and `loadWikiGraph` (the read-all-pages loops
188
+ // over the pure lint rules / `buildWikiGraph`) now live in
189
+ // `@mulmoclaude/core/wiki/server`, shared with MulmoTerminal.
338
190
 
339
191
  async function buildGraphResponse(action: string): Promise<WikiResponse> {
340
- const graph = await loadWikiGraph();
192
+ const graph = await loadWikiGraph(workspacePath);
341
193
  log.info("wiki", "POST graph: ok", { nodes: graph.nodes.length, edges: graph.edges.length });
342
194
  return {
343
195
  data: { action, title: "Wiki Graph", content: "", graph },
@@ -353,7 +205,7 @@ async function buildGraphResponse(action: string): Promise<WikiResponse> {
353
205
  type SaveOutcome = { ok: true; absPath: string } | { ok: false; reason: "not-found" };
354
206
 
355
207
  async function saveExistingPage(pageName: string, content: string): Promise<SaveOutcome> {
356
- const absPath = await resolvePagePath(pageName);
208
+ const absPath = await resolvePagePath(workspacePath, pageName);
357
209
  if (!absPath) return { ok: false, reason: "not-found" };
358
210
  // Funnel through the wiki-page write helper. Atomic write is
359
211
  // guaranteed inside; the helper also routes the (old, new) pair
@@ -400,7 +252,7 @@ async function handleSaveAction(
400
252
  }
401
253
 
402
254
  async function buildLintReportResponse(action: string): Promise<WikiResponse> {
403
- const issues = await collectLintIssues();
255
+ const issues = await collectLintIssues(workspacePath);
404
256
  const report = formatLintReport(issues);
405
257
  const healthy = issues.length === 0;
406
258
  return {
@@ -32,7 +32,7 @@ export interface BuildSandboxStatusParams {
32
32
  sandboxEnabled: boolean;
33
33
  sshAgentForward: boolean;
34
34
  configMountNames: readonly string[];
35
- sshAuthSock?: string | undefined;
35
+ sshAuthSock?: string;
36
36
  home?: string;
37
37
  /** Injected for tests; defaults to `process.platform`. */
38
38
  platform?: typeof process.platform;