mulmoclaude 0.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 (408) hide show
  1. package/README.md +44 -0
  2. package/bin/mulmoclaude.js +202 -0
  3. package/bin/prepare-dist.js +93 -0
  4. package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +1 -0
  5. package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +5 -0
  6. package/client/assets/index-D8rhwXLq.js +4906 -0
  7. package/client/assets/index-KNLBjwuh.css +1 -0
  8. package/client/assets/index.es-D4YyL_Dg-BfRHLTZV.js +5 -0
  9. package/client/assets/material-icons-Dr0goTwe.woff +0 -0
  10. package/client/assets/material-icons-kAwBdRge.woff2 +0 -0
  11. package/client/assets/material-icons-outlined-BpWbwl2n.woff +0 -0
  12. package/client/assets/material-icons-outlined-DZhiGvEA.woff2 +0 -0
  13. package/client/assets/material-icons-round-BDlwx-sv.woff +0 -0
  14. package/client/assets/material-icons-round-DrirKXBx.woff2 +0 -0
  15. package/client/assets/material-icons-sharp-CH1KkVu7.woff +0 -0
  16. package/client/assets/material-icons-sharp-gidztirS.woff2 +0 -0
  17. package/client/assets/material-icons-two-tone-B7wz7mED.woff +0 -0
  18. package/client/assets/material-icons-two-tone-DuNIpaEj.woff2 +0 -0
  19. package/client/assets/mulmo_bw-ERmkSv0a.png +0 -0
  20. package/client/assets/purify.es-Fx1Nqyry-PeS5RUhs.js +2 -0
  21. package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +1 -0
  22. package/client/index.html +28 -0
  23. package/package.json +66 -0
  24. package/server/agent/attachmentConverter.ts +270 -0
  25. package/server/agent/config.ts +414 -0
  26. package/server/agent/index.ts +260 -0
  27. package/server/agent/mcp-server.ts +412 -0
  28. package/server/agent/mcp-tools/index.ts +63 -0
  29. package/server/agent/mcp-tools/x.ts +188 -0
  30. package/server/agent/plugin-names.ts +75 -0
  31. package/server/agent/prompt.ts +349 -0
  32. package/server/agent/resumeFailover.ts +129 -0
  33. package/server/agent/sandboxMounts.ts +329 -0
  34. package/server/agent/stream.ts +194 -0
  35. package/server/api/auth/bearerAuth.ts +61 -0
  36. package/server/api/auth/token.ts +98 -0
  37. package/server/api/csrfGuard.ts +85 -0
  38. package/server/api/routes/agent.ts +478 -0
  39. package/server/api/routes/chart.ts +98 -0
  40. package/server/api/routes/chat-index.ts +46 -0
  41. package/server/api/routes/config.ts +258 -0
  42. package/server/api/routes/dispatchResponse.ts +79 -0
  43. package/server/api/routes/files.ts +812 -0
  44. package/server/api/routes/html.ts +101 -0
  45. package/server/api/routes/image.ts +169 -0
  46. package/server/api/routes/mulmo-script.ts +712 -0
  47. package/server/api/routes/mulmoScriptValidate.ts +101 -0
  48. package/server/api/routes/notifications.ts +69 -0
  49. package/server/api/routes/pdf.ts +163 -0
  50. package/server/api/routes/plugins.ts +276 -0
  51. package/server/api/routes/presentHtml.ts +48 -0
  52. package/server/api/routes/roles.ts +125 -0
  53. package/server/api/routes/scheduler.ts +153 -0
  54. package/server/api/routes/schedulerHandlers.ts +151 -0
  55. package/server/api/routes/schedulerTasks.ts +163 -0
  56. package/server/api/routes/sessions.ts +294 -0
  57. package/server/api/routes/sessionsCursor.ts +59 -0
  58. package/server/api/routes/skills.ts +195 -0
  59. package/server/api/routes/sources.ts +540 -0
  60. package/server/api/routes/todos.ts +263 -0
  61. package/server/api/routes/todosColumnsHandlers.ts +347 -0
  62. package/server/api/routes/todosHandlers.ts +274 -0
  63. package/server/api/routes/todosItemsHandlers.ts +386 -0
  64. package/server/api/routes/wiki/pageIndex.ts +53 -0
  65. package/server/api/routes/wiki.ts +363 -0
  66. package/server/api/sandboxStatus.ts +64 -0
  67. package/server/events/notifications.ts +160 -0
  68. package/server/events/pub-sub/index.ts +45 -0
  69. package/server/events/relay-client.ts +288 -0
  70. package/server/events/scheduler-adapter.ts +302 -0
  71. package/server/events/session-store/index.ts +492 -0
  72. package/server/events/task-manager/index.ts +181 -0
  73. package/server/index.ts +572 -0
  74. package/server/system/config.ts +243 -0
  75. package/server/system/credentials.ts +220 -0
  76. package/server/system/docker.ts +97 -0
  77. package/server/system/env.ts +109 -0
  78. package/server/system/logger/config.ts +112 -0
  79. package/server/system/logger/formatters.ts +40 -0
  80. package/server/system/logger/index.ts +53 -0
  81. package/server/system/logger/rotation.ts +37 -0
  82. package/server/system/logger/sinks.ts +101 -0
  83. package/server/system/logger/types.ts +29 -0
  84. package/server/utils/date.ts +57 -0
  85. package/server/utils/errors.ts +7 -0
  86. package/server/utils/fetch.ts +27 -0
  87. package/server/utils/files/atomic.ts +125 -0
  88. package/server/utils/files/html-io.ts +20 -0
  89. package/server/utils/files/image-store.ts +66 -0
  90. package/server/utils/files/index.ts +45 -0
  91. package/server/utils/files/journal-io.ts +213 -0
  92. package/server/utils/files/json.ts +69 -0
  93. package/server/utils/files/markdown-store.ts +33 -0
  94. package/server/utils/files/naming.ts +50 -0
  95. package/server/utils/files/reference-dirs-io.ts +45 -0
  96. package/server/utils/files/roles-io.ts +45 -0
  97. package/server/utils/files/safe.ts +106 -0
  98. package/server/utils/files/scheduler-io.ts +20 -0
  99. package/server/utils/files/scheduler-overrides-io.ts +64 -0
  100. package/server/utils/files/session-io.ts +136 -0
  101. package/server/utils/files/spreadsheet-store.ts +63 -0
  102. package/server/utils/files/todos-io.ts +29 -0
  103. package/server/utils/files/user-tasks-io.ts +25 -0
  104. package/server/utils/files/workspace-io.ts +221 -0
  105. package/server/utils/gemini.ts +59 -0
  106. package/server/utils/gitignore.ts +69 -0
  107. package/server/utils/http.ts +15 -0
  108. package/server/utils/httpError.ts +61 -0
  109. package/server/utils/id.ts +16 -0
  110. package/server/utils/json.ts +83 -0
  111. package/server/utils/logBackgroundError.ts +22 -0
  112. package/server/utils/markdown.ts +82 -0
  113. package/server/utils/request.ts +29 -0
  114. package/server/utils/slug.ts +50 -0
  115. package/server/utils/spawn.ts +62 -0
  116. package/server/utils/time.ts +34 -0
  117. package/server/utils/types.ts +47 -0
  118. package/server/workspace/chat-index/index.ts +153 -0
  119. package/server/workspace/chat-index/indexer.ts +209 -0
  120. package/server/workspace/chat-index/paths.ts +34 -0
  121. package/server/workspace/chat-index/summarizer.ts +247 -0
  122. package/server/workspace/chat-index/types.ts +38 -0
  123. package/server/workspace/custom-dirs.ts +220 -0
  124. package/server/workspace/helps/business.md +104 -0
  125. package/server/workspace/helps/github.md +23 -0
  126. package/server/workspace/helps/index.md +60 -0
  127. package/server/workspace/helps/mulmoscript.md +249 -0
  128. package/server/workspace/helps/sandbox.md +90 -0
  129. package/server/workspace/helps/spreadsheet.md +43 -0
  130. package/server/workspace/helps/telegram.md +135 -0
  131. package/server/workspace/helps/wiki.md +131 -0
  132. package/server/workspace/journal/archivist.ts +386 -0
  133. package/server/workspace/journal/dailyPass.ts +743 -0
  134. package/server/workspace/journal/diff.ts +71 -0
  135. package/server/workspace/journal/index.ts +185 -0
  136. package/server/workspace/journal/indexFile.ts +136 -0
  137. package/server/workspace/journal/linkRewrite.ts +4 -0
  138. package/server/workspace/journal/memoryExtractor.ts +130 -0
  139. package/server/workspace/journal/optimizationPass.ts +160 -0
  140. package/server/workspace/journal/paths.ts +76 -0
  141. package/server/workspace/journal/state.ts +125 -0
  142. package/server/workspace/paths.ts +158 -0
  143. package/server/workspace/reference-dirs.ts +252 -0
  144. package/server/workspace/roles.ts +37 -0
  145. package/server/workspace/skills/discovery.ts +125 -0
  146. package/server/workspace/skills/index.ts +10 -0
  147. package/server/workspace/skills/parser.ts +144 -0
  148. package/server/workspace/skills/paths.ts +41 -0
  149. package/server/workspace/skills/scheduler.ts +149 -0
  150. package/server/workspace/skills/types.ts +30 -0
  151. package/server/workspace/skills/user-tasks.ts +257 -0
  152. package/server/workspace/skills/writer.ts +189 -0
  153. package/server/workspace/sources/arxivDiscovery.ts +182 -0
  154. package/server/workspace/sources/classifier.ts +268 -0
  155. package/server/workspace/sources/fetchers/arxiv.ts +170 -0
  156. package/server/workspace/sources/fetchers/github.ts +106 -0
  157. package/server/workspace/sources/fetchers/githubIssues.ts +208 -0
  158. package/server/workspace/sources/fetchers/githubReleases.ts +186 -0
  159. package/server/workspace/sources/fetchers/index.ts +71 -0
  160. package/server/workspace/sources/fetchers/registerAll.ts +15 -0
  161. package/server/workspace/sources/fetchers/rss.ts +141 -0
  162. package/server/workspace/sources/fetchers/rssParser.ts +295 -0
  163. package/server/workspace/sources/httpFetcher.ts +230 -0
  164. package/server/workspace/sources/interests.ts +120 -0
  165. package/server/workspace/sources/paths.ts +110 -0
  166. package/server/workspace/sources/pipeline/dedup.ts +60 -0
  167. package/server/workspace/sources/pipeline/fetch.ts +136 -0
  168. package/server/workspace/sources/pipeline/index.ts +249 -0
  169. package/server/workspace/sources/pipeline/notify.ts +72 -0
  170. package/server/workspace/sources/pipeline/plan.ts +66 -0
  171. package/server/workspace/sources/pipeline/summarize.ts +189 -0
  172. package/server/workspace/sources/pipeline/write.ts +185 -0
  173. package/server/workspace/sources/rateLimiter.ts +148 -0
  174. package/server/workspace/sources/registry.ts +326 -0
  175. package/server/workspace/sources/robots.ts +271 -0
  176. package/server/workspace/sources/sourceState.ts +135 -0
  177. package/server/workspace/sources/taxonomy.ts +74 -0
  178. package/server/workspace/sources/types.ts +144 -0
  179. package/server/workspace/sources/urls.ts +112 -0
  180. package/server/workspace/tool-trace/classify.ts +114 -0
  181. package/server/workspace/tool-trace/index.ts +250 -0
  182. package/server/workspace/tool-trace/writeSearch.ts +98 -0
  183. package/server/workspace/wiki-backlinks/index.ts +107 -0
  184. package/server/workspace/wiki-backlinks/sessionBacklinks.ts +144 -0
  185. package/server/workspace/workspace.ts +66 -0
  186. package/src/App.vue +720 -0
  187. package/src/assets/mulmo_bw.png +0 -0
  188. package/src/components/CanvasViewToggle.vue +27 -0
  189. package/src/components/ChatAttachmentPreview.vue +45 -0
  190. package/src/components/ChatImagePreview.vue +17 -0
  191. package/src/components/ChatInput.vue +208 -0
  192. package/src/components/FileContentHeader.vue +49 -0
  193. package/src/components/FileContentRenderer.vue +162 -0
  194. package/src/components/FileTree.vue +115 -0
  195. package/src/components/FileTreePane.vue +85 -0
  196. package/src/components/FilesView.vue +206 -0
  197. package/src/components/LockStatusPopup.vue +111 -0
  198. package/src/components/NotificationBell.vue +131 -0
  199. package/src/components/NotificationToast.vue +72 -0
  200. package/src/components/PluginLauncher.vue +138 -0
  201. package/src/components/RightSidebar.vue +113 -0
  202. package/src/components/RoleSelector.vue +64 -0
  203. package/src/components/SessionHistoryPanel.vue +176 -0
  204. package/src/components/SessionTabBar.vue +81 -0
  205. package/src/components/SettingsMcpTab.vue +350 -0
  206. package/src/components/SettingsModal.vue +275 -0
  207. package/src/components/SettingsReferenceDirsTab.vue +173 -0
  208. package/src/components/SettingsWorkspaceDirsTab.vue +174 -0
  209. package/src/components/SidebarHeader.vue +69 -0
  210. package/src/components/StackView.vue +360 -0
  211. package/src/components/SuggestionsPanel.vue +65 -0
  212. package/src/components/TodoExplorer.vue +358 -0
  213. package/src/components/ToolResultsPanel.vue +77 -0
  214. package/src/components/todo/TodoAddDialog.vue +131 -0
  215. package/src/components/todo/TodoEditDialog.vue +47 -0
  216. package/src/components/todo/TodoEditPanel.vue +113 -0
  217. package/src/components/todo/TodoKanbanView.vue +249 -0
  218. package/src/components/todo/TodoListView.vue +79 -0
  219. package/src/components/todo/TodoTableView.vue +177 -0
  220. package/src/composables/useActiveSession.ts +40 -0
  221. package/src/composables/useAppApi.ts +45 -0
  222. package/src/composables/useCanvasViewMode.ts +121 -0
  223. package/src/composables/useChatScroll.ts +47 -0
  224. package/src/composables/useClickOutside.ts +26 -0
  225. package/src/composables/useClipboardCopy.ts +44 -0
  226. package/src/composables/useContentDisplay.ts +52 -0
  227. package/src/composables/useDebugBeat.ts +23 -0
  228. package/src/composables/useDynamicFavicon.ts +115 -0
  229. package/src/composables/useEventListeners.ts +42 -0
  230. package/src/composables/useExpandedDirs.ts +64 -0
  231. package/src/composables/useFaviconState.ts +30 -0
  232. package/src/composables/useFileSelection.ts +115 -0
  233. package/src/composables/useFileSortMode.ts +24 -0
  234. package/src/composables/useFileTree.ts +85 -0
  235. package/src/composables/useFreshPluginData.ts +89 -0
  236. package/src/composables/useHealth.ts +38 -0
  237. package/src/composables/useImeAwareEnter.ts +57 -0
  238. package/src/composables/useKeyNavigation.ts +60 -0
  239. package/src/composables/useMarkdownLinkHandler.ts +46 -0
  240. package/src/composables/useMarkdownMode.ts +17 -0
  241. package/src/composables/useMcpTools.ts +71 -0
  242. package/src/composables/useMergedSessions.ts +27 -0
  243. package/src/composables/useNotifications.ts +90 -0
  244. package/src/composables/usePdfDownload.ts +60 -0
  245. package/src/composables/usePendingCalls.ts +77 -0
  246. package/src/composables/usePubSub.ts +85 -0
  247. package/src/composables/useRightSidebar.ts +23 -0
  248. package/src/composables/useRoles.ts +34 -0
  249. package/src/composables/useSandboxStatus.ts +67 -0
  250. package/src/composables/useSelectedResult.ts +49 -0
  251. package/src/composables/useSessionDerived.ts +51 -0
  252. package/src/composables/useSessionHistory.ts +81 -0
  253. package/src/composables/useSessionSync.ts +57 -0
  254. package/src/composables/useViewLayout.ts +55 -0
  255. package/src/config/apiRoutes.ts +173 -0
  256. package/src/config/pubsubChannels.ts +45 -0
  257. package/src/config/roles.ts +335 -0
  258. package/src/config/schedulerActions.ts +25 -0
  259. package/src/config/toolNames.ts +71 -0
  260. package/src/config/workspacePaths.ts +24 -0
  261. package/src/index.css +107 -0
  262. package/src/main.ts +25 -0
  263. package/src/plugins/canvas/Preview.vue +13 -0
  264. package/src/plugins/canvas/View.vue +333 -0
  265. package/src/plugins/canvas/definition.ts +38 -0
  266. package/src/plugins/canvas/index.ts +36 -0
  267. package/src/plugins/chart/Preview.vue +49 -0
  268. package/src/plugins/chart/View.vue +143 -0
  269. package/src/plugins/chart/definition.ts +58 -0
  270. package/src/plugins/chart/index.ts +52 -0
  271. package/src/plugins/editImage/Preview.vue +13 -0
  272. package/src/plugins/editImage/View.vue +13 -0
  273. package/src/plugins/editImage/definition.ts +27 -0
  274. package/src/plugins/editImage/index.ts +36 -0
  275. package/src/plugins/generateImage/Preview.vue +13 -0
  276. package/src/plugins/generateImage/View.vue +33 -0
  277. package/src/plugins/generateImage/definition.ts +32 -0
  278. package/src/plugins/generateImage/index.ts +56 -0
  279. package/src/plugins/manageRoles/Preview.vue +49 -0
  280. package/src/plugins/manageRoles/View.vue +525 -0
  281. package/src/plugins/manageRoles/definition.ts +43 -0
  282. package/src/plugins/manageRoles/index.ts +47 -0
  283. package/src/plugins/manageSkills/Preview.vue +21 -0
  284. package/src/plugins/manageSkills/View.vue +321 -0
  285. package/src/plugins/manageSkills/definition.ts +49 -0
  286. package/src/plugins/manageSkills/index.ts +49 -0
  287. package/src/plugins/manageSource/Preview.vue +33 -0
  288. package/src/plugins/manageSource/View.vue +697 -0
  289. package/src/plugins/manageSource/definition.ts +63 -0
  290. package/src/plugins/manageSource/index.ts +66 -0
  291. package/src/plugins/markdown/Preview.vue +77 -0
  292. package/src/plugins/markdown/View.vue +476 -0
  293. package/src/plugins/markdown/definition.ts +50 -0
  294. package/src/plugins/markdown/index.ts +36 -0
  295. package/src/plugins/presentHtml/Preview.vue +25 -0
  296. package/src/plugins/presentHtml/View.vue +52 -0
  297. package/src/plugins/presentHtml/definition.ts +27 -0
  298. package/src/plugins/presentHtml/helpers.ts +72 -0
  299. package/src/plugins/presentHtml/index.ts +41 -0
  300. package/src/plugins/presentMulmoScript/Preview.vue +23 -0
  301. package/src/plugins/presentMulmoScript/View.vue +1166 -0
  302. package/src/plugins/presentMulmoScript/definition.ts +95 -0
  303. package/src/plugins/presentMulmoScript/helpers.ts +162 -0
  304. package/src/plugins/presentMulmoScript/index.ts +40 -0
  305. package/src/plugins/scheduler/Preview.vue +67 -0
  306. package/src/plugins/scheduler/TasksTab.vue +205 -0
  307. package/src/plugins/scheduler/View.vue +565 -0
  308. package/src/plugins/scheduler/definition.ts +57 -0
  309. package/src/plugins/scheduler/index.ts +45 -0
  310. package/src/plugins/scheduler/viewModes.ts +26 -0
  311. package/src/plugins/spreadsheet/Preview.vue +29 -0
  312. package/src/plugins/spreadsheet/View.vue +997 -0
  313. package/src/plugins/spreadsheet/cellHighlights.ts +79 -0
  314. package/src/plugins/spreadsheet/definition.ts +121 -0
  315. package/src/plugins/spreadsheet/engine/calculator.ts +459 -0
  316. package/src/plugins/spreadsheet/engine/cellBuilder.ts +81 -0
  317. package/src/plugins/spreadsheet/engine/date-parser.ts +220 -0
  318. package/src/plugins/spreadsheet/engine/date-utils.ts +56 -0
  319. package/src/plugins/spreadsheet/engine/engine.ts +176 -0
  320. package/src/plugins/spreadsheet/engine/evaluator.ts +390 -0
  321. package/src/plugins/spreadsheet/engine/formatter.ts +172 -0
  322. package/src/plugins/spreadsheet/engine/formulaRefs.ts +101 -0
  323. package/src/plugins/spreadsheet/engine/functions/date.ts +299 -0
  324. package/src/plugins/spreadsheet/engine/functions/financial.ts +387 -0
  325. package/src/plugins/spreadsheet/engine/functions/index.ts +16 -0
  326. package/src/plugins/spreadsheet/engine/functions/logical.ts +262 -0
  327. package/src/plugins/spreadsheet/engine/functions/lookup.ts +400 -0
  328. package/src/plugins/spreadsheet/engine/functions/mathematical.ts +297 -0
  329. package/src/plugins/spreadsheet/engine/functions/statistical.ts +338 -0
  330. package/src/plugins/spreadsheet/engine/functions/text.ts +389 -0
  331. package/src/plugins/spreadsheet/engine/index.ts +27 -0
  332. package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +111 -0
  333. package/src/plugins/spreadsheet/engine/parser.ts +143 -0
  334. package/src/plugins/spreadsheet/engine/registry.ts +150 -0
  335. package/src/plugins/spreadsheet/engine/responseDecoder.ts +67 -0
  336. package/src/plugins/spreadsheet/engine/types.ts +64 -0
  337. package/src/plugins/spreadsheet/index.ts +36 -0
  338. package/src/plugins/textResponse/Preview.vue +94 -0
  339. package/src/plugins/textResponse/View.vue +503 -0
  340. package/src/plugins/textResponse/definition.ts +34 -0
  341. package/src/plugins/textResponse/index.ts +27 -0
  342. package/src/plugins/textResponse/plugin.ts +29 -0
  343. package/src/plugins/textResponse/samples.ts +97 -0
  344. package/src/plugins/textResponse/types.ts +11 -0
  345. package/src/plugins/todo/Preview.vue +63 -0
  346. package/src/plugins/todo/View.vue +364 -0
  347. package/src/plugins/todo/composables/useTodos.ts +177 -0
  348. package/src/plugins/todo/definition.ts +45 -0
  349. package/src/plugins/todo/index.ts +61 -0
  350. package/src/plugins/todo/labels.ts +163 -0
  351. package/src/plugins/todo/priority.ts +98 -0
  352. package/src/plugins/todo/viewModes.ts +19 -0
  353. package/src/plugins/ui-image/ImagePreview.vue +23 -0
  354. package/src/plugins/ui-image/ImageView.vue +34 -0
  355. package/src/plugins/ui-image/index.ts +3 -0
  356. package/src/plugins/ui-image/types.ts +4 -0
  357. package/src/plugins/wiki/Preview.vue +65 -0
  358. package/src/plugins/wiki/View.vue +342 -0
  359. package/src/plugins/wiki/definition.ts +25 -0
  360. package/src/plugins/wiki/helpers.ts +59 -0
  361. package/src/plugins/wiki/index.ts +52 -0
  362. package/src/router/guards.ts +61 -0
  363. package/src/router/index.ts +50 -0
  364. package/src/tools/index.ts +52 -0
  365. package/src/tools/types.ts +27 -0
  366. package/src/types/events.ts +16 -0
  367. package/src/types/fileTree.ts +13 -0
  368. package/src/types/notification.ts +67 -0
  369. package/src/types/session.ts +116 -0
  370. package/src/types/sse.ts +90 -0
  371. package/src/types/toolCallHistory.ts +13 -0
  372. package/src/utils/agent/eventDispatch.ts +74 -0
  373. package/src/utils/agent/request.ts +55 -0
  374. package/src/utils/agent/toolCalls.ts +62 -0
  375. package/src/utils/api.ts +218 -0
  376. package/src/utils/canvas/viewMode.ts +46 -0
  377. package/src/utils/dom/authTokenMeta.ts +20 -0
  378. package/src/utils/dom/clickOutside.ts +11 -0
  379. package/src/utils/dom/externalLink.ts +57 -0
  380. package/src/utils/dom/scrollable.ts +24 -0
  381. package/src/utils/errors.ts +11 -0
  382. package/src/utils/files/expandedDirs.ts +25 -0
  383. package/src/utils/files/filename.ts +12 -0
  384. package/src/utils/files/sortChildren.ts +20 -0
  385. package/src/utils/filesPreview/schedulerPreview.ts +38 -0
  386. package/src/utils/filesPreview/todoPreview.ts +40 -0
  387. package/src/utils/format/date.ts +85 -0
  388. package/src/utils/format/frontmatter.ts +80 -0
  389. package/src/utils/format/jsonSyntax.ts +109 -0
  390. package/src/utils/html/previewCsp.ts +65 -0
  391. package/src/utils/image/resolve.ts +8 -0
  392. package/src/utils/image/rewriteMarkdownImageRefs.ts +182 -0
  393. package/src/utils/markdown/extractFirstH1.ts +39 -0
  394. package/src/utils/notification/dispatch.ts +22 -0
  395. package/src/utils/path/relativeLink.ts +130 -0
  396. package/src/utils/role/icon.ts +20 -0
  397. package/src/utils/role/merge.ts +10 -0
  398. package/src/utils/role/plugins.ts +12 -0
  399. package/src/utils/session/mergeSessions.ts +103 -0
  400. package/src/utils/session/seedRoleDefault.ts +35 -0
  401. package/src/utils/session/sessionEntries.ts +121 -0
  402. package/src/utils/session/sessionFactory.ts +22 -0
  403. package/src/utils/session/sessionHelpers.ts +99 -0
  404. package/src/utils/tools/dedup.ts +17 -0
  405. package/src/utils/tools/mcp.ts +33 -0
  406. package/src/utils/tools/pendingCalls.ts +16 -0
  407. package/src/utils/tools/result.ts +40 -0
  408. package/src/utils/types.ts +44 -0
@@ -0,0 +1,386 @@
1
+ // Thin wrapper around the Claude Code CLI used as the journal's
2
+ // summarizer. The default `runClaudeCli` spawns `claude -p` as a
3
+ // subprocess so summarization draws from the user's subscription
4
+ // quota rather than the API key budget.
5
+ //
6
+ // The rest of the journal module receives a `Summarize` function
7
+ // via dependency injection — tests supply a deterministic fake, the
8
+ // production path supplies `runClaudeCli`.
9
+
10
+ import { spawn } from "node:child_process";
11
+ import { CLI_SUBPROCESS_TIMEOUT_MS } from "../../utils/time.js";
12
+
13
+ // (systemPrompt, userPrompt) → raw model output as a string.
14
+ // The daily/optimization passes parse JSON out of the string
15
+ // themselves; this layer stays transport-only.
16
+ export type Summarize = (systemPrompt: string, userPrompt: string) => Promise<string>;
17
+
18
+ // Wall-clock cap per CLI invocation. 5 minutes is comfortably above
19
+ // the worst-case summarization run we've seen and still short enough
20
+ // that a wedged subprocess doesn't tie up resources forever.
21
+ const CLI_TIMEOUT_MS = CLI_SUBPROCESS_TIMEOUT_MS;
22
+
23
+ // Sentinel we throw on ENOENT so maybeRunJournal can disable the
24
+ // feature for the rest of the server lifetime instead of retrying
25
+ // on every session-end.
26
+ export class ClaudeCliNotFoundError extends Error {
27
+ constructor() {
28
+ super("[journal] `claude` CLI is not available on PATH — journal disabled");
29
+ this.name = "ClaudeCliNotFoundError";
30
+ }
31
+ }
32
+
33
+ export class ClaudeCliFailedError extends Error {
34
+ readonly exitCode: number | null;
35
+ readonly stderr: string;
36
+ constructor(exitCode: number | null, stderr: string) {
37
+ super(`[journal] \`claude\` CLI exited ${exitCode ?? "(killed)"}: ${stderr.slice(0, 500)}`);
38
+ this.name = "ClaudeCliFailedError";
39
+ this.exitCode = exitCode;
40
+ this.stderr = stderr;
41
+ }
42
+ }
43
+
44
+ // Default summarizer. Spawns `claude -p` and pipes the combined
45
+ // system + user prompt to stdin so we don't hit shell-argv limits
46
+ // for large day excerpts.
47
+ export const runClaudeCli: Summarize = async (systemPrompt, userPrompt) => {
48
+ return new Promise((resolve, reject) => {
49
+ const child = spawn("claude", ["-p", "--output-format", "text"], {
50
+ stdio: ["pipe", "pipe", "pipe"],
51
+ });
52
+
53
+ let stdout = "";
54
+ let stderr = "";
55
+ let timedOut = false;
56
+ let settled = false;
57
+
58
+ const timeout = setTimeout(() => {
59
+ timedOut = true;
60
+ child.kill("SIGKILL");
61
+ }, CLI_TIMEOUT_MS);
62
+
63
+ child.stdout.on("data", (d: Buffer) => {
64
+ stdout += d.toString();
65
+ });
66
+ child.stderr.on("data", (d: Buffer) => {
67
+ stderr += d.toString();
68
+ });
69
+
70
+ child.on("error", (err: Error & { code?: string }) => {
71
+ if (settled) return;
72
+ settled = true;
73
+ clearTimeout(timeout);
74
+ if (err.code === "ENOENT") {
75
+ reject(new ClaudeCliNotFoundError());
76
+ } else {
77
+ reject(err);
78
+ }
79
+ });
80
+
81
+ child.on("close", (code) => {
82
+ if (settled) return;
83
+ settled = true;
84
+ clearTimeout(timeout);
85
+ if (timedOut) {
86
+ reject(new ClaudeCliFailedError(null, `timed out after ${CLI_TIMEOUT_MS}ms\n${stderr}`));
87
+ return;
88
+ }
89
+ if (code === 0) {
90
+ resolve(stdout);
91
+ } else {
92
+ reject(new ClaudeCliFailedError(code, stderr));
93
+ }
94
+ });
95
+
96
+ // Surface stdin write errors (e.g. EPIPE if the child exited
97
+ // before we finished writing) instead of silently dropping them.
98
+ child.stdin.on("error", (err: Error) => {
99
+ if (settled) return;
100
+ settled = true;
101
+ clearTimeout(timeout);
102
+ reject(err);
103
+ });
104
+
105
+ // Send the full prompt in one write. If Node's stream layer
106
+ // signals backpressure (write returns false), wait for "drain"
107
+ // before calling end() so we don't close stdin while the buffer
108
+ // still has data to flush. For typical archivist prompts this
109
+ // path rarely fires, but very large session excerpts can reach
110
+ // it.
111
+ const payload = `${systemPrompt}\n\n---\n\n${userPrompt}`;
112
+ const flushed = child.stdin.write(payload);
113
+ if (flushed) {
114
+ child.stdin.end();
115
+ } else {
116
+ child.stdin.once("drain", () => child.stdin.end());
117
+ }
118
+ });
119
+ };
120
+
121
+ // --- Daily archivist contract ---------------------------------------
122
+
123
+ export interface SessionEventExcerpt {
124
+ source: string; // "user" | "assistant" | "tool" | ...
125
+ type: string; // "text" | "tool_result" | ...
126
+ // One-line human-readable rendering of the event, already
127
+ // truncated to a sane length by the caller.
128
+ content: string;
129
+ }
130
+
131
+ export interface SessionExcerpt {
132
+ sessionId: string;
133
+ roleId: string;
134
+ events: SessionEventExcerpt[];
135
+ // Workspace-relative file paths produced by the session's tool
136
+ // calls (e.g. "stories/foo.json", "HTMLs/bar.html",
137
+ // "wiki/pages/baz.md"). Surfaced so the archivist can emit
138
+ // navigable markdown links to them in the summaries.
139
+ artifactPaths: string[];
140
+ }
141
+
142
+ export interface ExistingTopicSnapshot {
143
+ slug: string;
144
+ content: string;
145
+ }
146
+
147
+ export interface DailyArchivistInput {
148
+ date: string; // YYYY-MM-DD
149
+ existingDailySummary: string | null;
150
+ existingTopicSummaries: ExistingTopicSnapshot[];
151
+ sessionExcerpts: SessionExcerpt[];
152
+ }
153
+
154
+ export type TopicUpdateAction = "create" | "append" | "rewrite";
155
+
156
+ export interface TopicUpdate {
157
+ slug: string;
158
+ action: TopicUpdateAction;
159
+ content: string;
160
+ }
161
+
162
+ export interface DailyArchivistOutput {
163
+ dailySummaryMarkdown: string;
164
+ topicUpdates: TopicUpdate[];
165
+ }
166
+
167
+ // System prompt for the daily pass. Written long-form because the
168
+ // model does a much better job with explicit rules and an example
169
+ // than with a terse instruction.
170
+ export const DAILY_SYSTEM_PROMPT = `You are the journal archivist for a personal MulmoClaude workspace.
171
+ Your job: given raw session excerpts for a single day, produce
172
+ (1) a daily summary and (2) updates to long-running topic notes.
173
+
174
+ OUTPUT FORMAT
175
+ You must emit a single JSON object wrapped in a \`\`\`json code fence.
176
+ Schema:
177
+ {
178
+ "dailySummaryMarkdown": "...",
179
+ "topicUpdates": [
180
+ { "slug": "kebab-case-slug", "action": "create" | "append" | "rewrite", "content": "..." }
181
+ ]
182
+ }
183
+ No prose outside the fence. No extra keys.
184
+
185
+ DAILY SUMMARY RULES
186
+ - Write in the same language as the source sessions. Japanese stays Japanese. English stays English.
187
+ - Start with a top-level \`# <date>\` heading using the date passed in.
188
+ - Use short bullet sections per theme or per session, not a prose wall.
189
+ - If an existing daily summary was provided, treat it as a prior draft to REWRITE, not append to — your output replaces it entirely.
190
+ - Be terse. Facts and decisions only, no filler.
191
+
192
+ TOPIC UPDATE RULES
193
+ - Prefer the existing topic list. Only invent a new slug if nothing fits.
194
+ - Slugs are lowercase kebab-case ASCII (e.g. "video-generation"). No spaces, no unicode.
195
+ - Use \`append\` for incremental facts: your content will be concatenated to the existing topic file after a blank line.
196
+ - Use \`create\` only when the slug is new.
197
+ - Use \`rewrite\` sparingly — only when the existing topic has become incoherent and needs a full replacement.
198
+ - If a session has no clear topical hook, emit zero topic updates rather than forcing one.
199
+
200
+ ARTIFACT LINKS
201
+ - The prompt may list "ARTIFACTS REFERENCED" — workspace-relative paths produced by the day's sessions (e.g. \`stories/foo.json\`, \`wiki/pages/bar.md\`, \`HTMLs/baz.html\`).
202
+ - When your summary mentions one of those artifacts, embed a markdown link to it using a **workspace-absolute path** beginning with a single forward slash.
203
+ - Correct: \`[wiki page on X](/wiki/pages/x.md)\`
204
+ - Wrong: \`[wiki page](wiki/pages/x.md)\` (missing leading slash)
205
+ - Wrong: \`[wiki page](/home/user/.../x.md)\` (filesystem absolute)
206
+ - The post-processor converts these to true relative paths before writing the file to disk, so don't do the relative-path math yourself.
207
+ - Only link to artifacts listed in "ARTIFACTS REFERENCED". Don't invent paths.
208
+
209
+ SESSION LINKS
210
+ - When your summary refers to a specific session (the ones listed under "SESSION EXCERPTS" with their \`session <id>\` header), link to that session using \`/chat/<sessionId>.jsonl\`.
211
+ - Example: "— discussed in [session 550e8400](/chat/550e8400-e29b-41d4-a716-446655440000.jsonl)"
212
+ - The file viewer recognises this pattern and switches the sidebar chat to that session when the link is clicked, so the reader can pick up where the session left off.
213
+ - You do not have to link every session you mention, but linking at least the first reference per session is helpful.
214
+
215
+ LANGUAGE
216
+ - Match the language of the source sessions. Always.`;
217
+
218
+ // Build the user-side prompt for one day's worth of content.
219
+ // Pure string construction — safe to unit test if we ever want to.
220
+ export function buildDailyUserPrompt(input: DailyArchivistInput): string {
221
+ const parts: string[] = [];
222
+ parts.push(`DATE: ${input.date}`);
223
+ parts.push("");
224
+
225
+ if (input.existingDailySummary !== null) {
226
+ parts.push("EXISTING DAILY SUMMARY (replace this with your new version):");
227
+ parts.push("```md");
228
+ parts.push(input.existingDailySummary);
229
+ parts.push("```");
230
+ parts.push("");
231
+ }
232
+
233
+ parts.push("EXISTING TOPICS:");
234
+ if (input.existingTopicSummaries.length === 0) {
235
+ parts.push("(none yet)");
236
+ } else {
237
+ for (const t of input.existingTopicSummaries) {
238
+ parts.push(`- ${t.slug}`);
239
+ }
240
+ }
241
+ parts.push("");
242
+
243
+ // Union of all workspace-relative artifact paths the day's
244
+ // sessions produced, deduped and sorted. Given to the archivist
245
+ // so it can link to them from the summary text.
246
+ const allArtifacts = new Set<string>();
247
+ for (const s of input.sessionExcerpts) {
248
+ for (const p of s.artifactPaths) allArtifacts.add(p);
249
+ }
250
+ parts.push("ARTIFACTS REFERENCED:");
251
+ if (allArtifacts.size === 0) {
252
+ parts.push("(none)");
253
+ } else {
254
+ for (const p of [...allArtifacts].sort()) {
255
+ parts.push(`- ${p}`);
256
+ }
257
+ }
258
+ parts.push("");
259
+
260
+ parts.push("SESSION EXCERPTS:");
261
+ for (const s of input.sessionExcerpts) {
262
+ parts.push(`### session ${s.sessionId} (role: ${s.roleId})`);
263
+ for (const e of s.events) {
264
+ parts.push(`- [${e.source}/${e.type}] ${e.content}`);
265
+ }
266
+ parts.push("");
267
+ }
268
+
269
+ parts.push("Produce the JSON described in the system prompt now.");
270
+ return parts.join("\n");
271
+ }
272
+
273
+ // --- Optimization archivist contract --------------------------------
274
+
275
+ export interface OptimizationTopicSnapshot {
276
+ slug: string;
277
+ // First ~500 chars of the topic file, enough for the model to
278
+ // judge similarity without blowing up prompt size.
279
+ headContent: string;
280
+ }
281
+
282
+ export interface OptimizationInput {
283
+ topics: OptimizationTopicSnapshot[];
284
+ }
285
+
286
+ export interface TopicMerge {
287
+ from: string[];
288
+ into: string;
289
+ newContent: string;
290
+ }
291
+
292
+ export interface OptimizationOutput {
293
+ merges: TopicMerge[];
294
+ archives: string[];
295
+ }
296
+
297
+ export const OPTIMIZATION_SYSTEM_PROMPT = `You are the journal optimizer for a personal MulmoClaude workspace.
298
+ Your job: review the current topic list and decide which topics should be merged together and which should be archived.
299
+
300
+ OUTPUT FORMAT
301
+ A single JSON object wrapped in a \`\`\`json code fence:
302
+ {
303
+ "merges": [
304
+ { "from": ["slug-a", "slug-b"], "into": "merged-slug", "newContent": "..." }
305
+ ],
306
+ "archives": ["stale-slug"]
307
+ }
308
+ No prose outside the fence.
309
+
310
+ MERGE RULES
311
+ - Only merge topics that are clearly duplicates or near-duplicates (e.g. "video-gen" and "video-generation").
312
+ - "into" may be one of the "from" slugs (keeping an existing file) or a brand-new slug (creating a new file).
313
+ - "newContent" is the full replacement body for the target file, in markdown.
314
+ - Be conservative: if in doubt, leave things alone.
315
+
316
+ ARCHIVE RULES
317
+ - Archive only topics that look stale AND uninteresting. Err on the side of keeping things.
318
+ - Do not archive a topic you also listed in a merge's "from" — the merge already moves it.
319
+
320
+ LANGUAGE
321
+ - Match the language of the source content for "newContent".
322
+ - If no changes are needed, return \`{ "merges": [], "archives": [] }\`. That is a valid and expected outcome.`;
323
+
324
+ export function buildOptimizationUserPrompt(input: OptimizationInput): string {
325
+ const parts: string[] = [];
326
+ parts.push("CURRENT TOPICS:");
327
+ for (const t of input.topics) {
328
+ parts.push(`### ${t.slug}`);
329
+ parts.push("```md");
330
+ parts.push(t.headContent);
331
+ parts.push("```");
332
+ parts.push("");
333
+ }
334
+ parts.push("Produce the JSON described in the system prompt now.");
335
+ return parts.join("\n");
336
+ }
337
+
338
+ // --- JSON extraction ------------------------------------------------
339
+
340
+ // Tolerant JSON extractor: prefers a ```json fenced block; falls back
341
+ // to scanning for the first balanced `{ ... }` block. Returns `null`
342
+ // on failure so callers can log-and-skip instead of crash.
343
+ //
344
+ // JSON extraction helpers moved to server/utils/json.ts.
345
+ // Re-export for backwards compatibility with callers that import
346
+ // from this module (dailyPass.ts, optimizationPass.ts).
347
+ export { extractJsonObject, findBalancedBraceBlock } from "../../utils/json.js";
348
+
349
+ import { isRecord } from "../../utils/types.js";
350
+
351
+ // Type guards used by callers to validate parsed output. Written as
352
+ // guards rather than `as` casts per project conventions.
353
+ export function isDailyArchivistOutput(value: unknown): value is DailyArchivistOutput {
354
+ if (!isRecord(value)) return false;
355
+ const v = value as Record<string, unknown>;
356
+ if (typeof v.dailySummaryMarkdown !== "string") return false;
357
+ if (!Array.isArray(v.topicUpdates)) return false;
358
+ return v.topicUpdates.every(isTopicUpdate);
359
+ }
360
+
361
+ function isTopicUpdate(value: unknown): value is TopicUpdate {
362
+ if (!isRecord(value)) return false;
363
+ const v = value as Record<string, unknown>;
364
+ if (typeof v.slug !== "string") return false;
365
+ if (typeof v.content !== "string") return false;
366
+ return v.action === "create" || v.action === "append" || v.action === "rewrite";
367
+ }
368
+
369
+ export function isOptimizationOutput(value: unknown): value is OptimizationOutput {
370
+ if (!isRecord(value)) return false;
371
+ const v = value as Record<string, unknown>;
372
+ if (!Array.isArray(v.merges)) return false;
373
+ if (!Array.isArray(v.archives)) return false;
374
+ if (!v.merges.every(isTopicMerge)) return false;
375
+ return v.archives.every((a: unknown) => typeof a === "string");
376
+ }
377
+
378
+ function isTopicMerge(value: unknown): value is TopicMerge {
379
+ if (!isRecord(value)) return false;
380
+ const v = value as Record<string, unknown>;
381
+ if (!Array.isArray(v.from)) return false;
382
+ if (!v.from.every((f: unknown) => typeof f === "string")) return false;
383
+ if (typeof v.into !== "string") return false;
384
+ if (typeof v.newContent !== "string") return false;
385
+ return true;
386
+ }