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.
- package/README.md +44 -0
- package/bin/mulmoclaude.js +202 -0
- package/bin/prepare-dist.js +93 -0
- package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +1 -0
- package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +5 -0
- package/client/assets/index-D8rhwXLq.js +4906 -0
- package/client/assets/index-KNLBjwuh.css +1 -0
- package/client/assets/index.es-D4YyL_Dg-BfRHLTZV.js +5 -0
- package/client/assets/material-icons-Dr0goTwe.woff +0 -0
- package/client/assets/material-icons-kAwBdRge.woff2 +0 -0
- package/client/assets/material-icons-outlined-BpWbwl2n.woff +0 -0
- package/client/assets/material-icons-outlined-DZhiGvEA.woff2 +0 -0
- package/client/assets/material-icons-round-BDlwx-sv.woff +0 -0
- package/client/assets/material-icons-round-DrirKXBx.woff2 +0 -0
- package/client/assets/material-icons-sharp-CH1KkVu7.woff +0 -0
- package/client/assets/material-icons-sharp-gidztirS.woff2 +0 -0
- package/client/assets/material-icons-two-tone-B7wz7mED.woff +0 -0
- package/client/assets/material-icons-two-tone-DuNIpaEj.woff2 +0 -0
- package/client/assets/mulmo_bw-ERmkSv0a.png +0 -0
- package/client/assets/purify.es-Fx1Nqyry-PeS5RUhs.js +2 -0
- package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +1 -0
- package/client/index.html +28 -0
- package/package.json +66 -0
- package/server/agent/attachmentConverter.ts +270 -0
- package/server/agent/config.ts +414 -0
- package/server/agent/index.ts +260 -0
- package/server/agent/mcp-server.ts +412 -0
- package/server/agent/mcp-tools/index.ts +63 -0
- package/server/agent/mcp-tools/x.ts +188 -0
- package/server/agent/plugin-names.ts +75 -0
- package/server/agent/prompt.ts +349 -0
- package/server/agent/resumeFailover.ts +129 -0
- package/server/agent/sandboxMounts.ts +329 -0
- package/server/agent/stream.ts +194 -0
- package/server/api/auth/bearerAuth.ts +61 -0
- package/server/api/auth/token.ts +98 -0
- package/server/api/csrfGuard.ts +85 -0
- package/server/api/routes/agent.ts +478 -0
- package/server/api/routes/chart.ts +98 -0
- package/server/api/routes/chat-index.ts +46 -0
- package/server/api/routes/config.ts +258 -0
- package/server/api/routes/dispatchResponse.ts +79 -0
- package/server/api/routes/files.ts +812 -0
- package/server/api/routes/html.ts +101 -0
- package/server/api/routes/image.ts +169 -0
- package/server/api/routes/mulmo-script.ts +712 -0
- package/server/api/routes/mulmoScriptValidate.ts +101 -0
- package/server/api/routes/notifications.ts +69 -0
- package/server/api/routes/pdf.ts +163 -0
- package/server/api/routes/plugins.ts +276 -0
- package/server/api/routes/presentHtml.ts +48 -0
- package/server/api/routes/roles.ts +125 -0
- package/server/api/routes/scheduler.ts +153 -0
- package/server/api/routes/schedulerHandlers.ts +151 -0
- package/server/api/routes/schedulerTasks.ts +163 -0
- package/server/api/routes/sessions.ts +294 -0
- package/server/api/routes/sessionsCursor.ts +59 -0
- package/server/api/routes/skills.ts +195 -0
- package/server/api/routes/sources.ts +540 -0
- package/server/api/routes/todos.ts +263 -0
- package/server/api/routes/todosColumnsHandlers.ts +347 -0
- package/server/api/routes/todosHandlers.ts +274 -0
- package/server/api/routes/todosItemsHandlers.ts +386 -0
- package/server/api/routes/wiki/pageIndex.ts +53 -0
- package/server/api/routes/wiki.ts +363 -0
- package/server/api/sandboxStatus.ts +64 -0
- package/server/events/notifications.ts +160 -0
- package/server/events/pub-sub/index.ts +45 -0
- package/server/events/relay-client.ts +288 -0
- package/server/events/scheduler-adapter.ts +302 -0
- package/server/events/session-store/index.ts +492 -0
- package/server/events/task-manager/index.ts +181 -0
- package/server/index.ts +572 -0
- package/server/system/config.ts +243 -0
- package/server/system/credentials.ts +220 -0
- package/server/system/docker.ts +97 -0
- package/server/system/env.ts +109 -0
- package/server/system/logger/config.ts +112 -0
- package/server/system/logger/formatters.ts +40 -0
- package/server/system/logger/index.ts +53 -0
- package/server/system/logger/rotation.ts +37 -0
- package/server/system/logger/sinks.ts +101 -0
- package/server/system/logger/types.ts +29 -0
- package/server/utils/date.ts +57 -0
- package/server/utils/errors.ts +7 -0
- package/server/utils/fetch.ts +27 -0
- package/server/utils/files/atomic.ts +125 -0
- package/server/utils/files/html-io.ts +20 -0
- package/server/utils/files/image-store.ts +66 -0
- package/server/utils/files/index.ts +45 -0
- package/server/utils/files/journal-io.ts +213 -0
- package/server/utils/files/json.ts +69 -0
- package/server/utils/files/markdown-store.ts +33 -0
- package/server/utils/files/naming.ts +50 -0
- package/server/utils/files/reference-dirs-io.ts +45 -0
- package/server/utils/files/roles-io.ts +45 -0
- package/server/utils/files/safe.ts +106 -0
- package/server/utils/files/scheduler-io.ts +20 -0
- package/server/utils/files/scheduler-overrides-io.ts +64 -0
- package/server/utils/files/session-io.ts +136 -0
- package/server/utils/files/spreadsheet-store.ts +63 -0
- package/server/utils/files/todos-io.ts +29 -0
- package/server/utils/files/user-tasks-io.ts +25 -0
- package/server/utils/files/workspace-io.ts +221 -0
- package/server/utils/gemini.ts +59 -0
- package/server/utils/gitignore.ts +69 -0
- package/server/utils/http.ts +15 -0
- package/server/utils/httpError.ts +61 -0
- package/server/utils/id.ts +16 -0
- package/server/utils/json.ts +83 -0
- package/server/utils/logBackgroundError.ts +22 -0
- package/server/utils/markdown.ts +82 -0
- package/server/utils/request.ts +29 -0
- package/server/utils/slug.ts +50 -0
- package/server/utils/spawn.ts +62 -0
- package/server/utils/time.ts +34 -0
- package/server/utils/types.ts +47 -0
- package/server/workspace/chat-index/index.ts +153 -0
- package/server/workspace/chat-index/indexer.ts +209 -0
- package/server/workspace/chat-index/paths.ts +34 -0
- package/server/workspace/chat-index/summarizer.ts +247 -0
- package/server/workspace/chat-index/types.ts +38 -0
- package/server/workspace/custom-dirs.ts +220 -0
- package/server/workspace/helps/business.md +104 -0
- package/server/workspace/helps/github.md +23 -0
- package/server/workspace/helps/index.md +60 -0
- package/server/workspace/helps/mulmoscript.md +249 -0
- package/server/workspace/helps/sandbox.md +90 -0
- package/server/workspace/helps/spreadsheet.md +43 -0
- package/server/workspace/helps/telegram.md +135 -0
- package/server/workspace/helps/wiki.md +131 -0
- package/server/workspace/journal/archivist.ts +386 -0
- package/server/workspace/journal/dailyPass.ts +743 -0
- package/server/workspace/journal/diff.ts +71 -0
- package/server/workspace/journal/index.ts +185 -0
- package/server/workspace/journal/indexFile.ts +136 -0
- package/server/workspace/journal/linkRewrite.ts +4 -0
- package/server/workspace/journal/memoryExtractor.ts +130 -0
- package/server/workspace/journal/optimizationPass.ts +160 -0
- package/server/workspace/journal/paths.ts +76 -0
- package/server/workspace/journal/state.ts +125 -0
- package/server/workspace/paths.ts +158 -0
- package/server/workspace/reference-dirs.ts +252 -0
- package/server/workspace/roles.ts +37 -0
- package/server/workspace/skills/discovery.ts +125 -0
- package/server/workspace/skills/index.ts +10 -0
- package/server/workspace/skills/parser.ts +144 -0
- package/server/workspace/skills/paths.ts +41 -0
- package/server/workspace/skills/scheduler.ts +149 -0
- package/server/workspace/skills/types.ts +30 -0
- package/server/workspace/skills/user-tasks.ts +257 -0
- package/server/workspace/skills/writer.ts +189 -0
- package/server/workspace/sources/arxivDiscovery.ts +182 -0
- package/server/workspace/sources/classifier.ts +268 -0
- package/server/workspace/sources/fetchers/arxiv.ts +170 -0
- package/server/workspace/sources/fetchers/github.ts +106 -0
- package/server/workspace/sources/fetchers/githubIssues.ts +208 -0
- package/server/workspace/sources/fetchers/githubReleases.ts +186 -0
- package/server/workspace/sources/fetchers/index.ts +71 -0
- package/server/workspace/sources/fetchers/registerAll.ts +15 -0
- package/server/workspace/sources/fetchers/rss.ts +141 -0
- package/server/workspace/sources/fetchers/rssParser.ts +295 -0
- package/server/workspace/sources/httpFetcher.ts +230 -0
- package/server/workspace/sources/interests.ts +120 -0
- package/server/workspace/sources/paths.ts +110 -0
- package/server/workspace/sources/pipeline/dedup.ts +60 -0
- package/server/workspace/sources/pipeline/fetch.ts +136 -0
- package/server/workspace/sources/pipeline/index.ts +249 -0
- package/server/workspace/sources/pipeline/notify.ts +72 -0
- package/server/workspace/sources/pipeline/plan.ts +66 -0
- package/server/workspace/sources/pipeline/summarize.ts +189 -0
- package/server/workspace/sources/pipeline/write.ts +185 -0
- package/server/workspace/sources/rateLimiter.ts +148 -0
- package/server/workspace/sources/registry.ts +326 -0
- package/server/workspace/sources/robots.ts +271 -0
- package/server/workspace/sources/sourceState.ts +135 -0
- package/server/workspace/sources/taxonomy.ts +74 -0
- package/server/workspace/sources/types.ts +144 -0
- package/server/workspace/sources/urls.ts +112 -0
- package/server/workspace/tool-trace/classify.ts +114 -0
- package/server/workspace/tool-trace/index.ts +250 -0
- package/server/workspace/tool-trace/writeSearch.ts +98 -0
- package/server/workspace/wiki-backlinks/index.ts +107 -0
- package/server/workspace/wiki-backlinks/sessionBacklinks.ts +144 -0
- package/server/workspace/workspace.ts +66 -0
- package/src/App.vue +720 -0
- package/src/assets/mulmo_bw.png +0 -0
- package/src/components/CanvasViewToggle.vue +27 -0
- package/src/components/ChatAttachmentPreview.vue +45 -0
- package/src/components/ChatImagePreview.vue +17 -0
- package/src/components/ChatInput.vue +208 -0
- package/src/components/FileContentHeader.vue +49 -0
- package/src/components/FileContentRenderer.vue +162 -0
- package/src/components/FileTree.vue +115 -0
- package/src/components/FileTreePane.vue +85 -0
- package/src/components/FilesView.vue +206 -0
- package/src/components/LockStatusPopup.vue +111 -0
- package/src/components/NotificationBell.vue +131 -0
- package/src/components/NotificationToast.vue +72 -0
- package/src/components/PluginLauncher.vue +138 -0
- package/src/components/RightSidebar.vue +113 -0
- package/src/components/RoleSelector.vue +64 -0
- package/src/components/SessionHistoryPanel.vue +176 -0
- package/src/components/SessionTabBar.vue +81 -0
- package/src/components/SettingsMcpTab.vue +350 -0
- package/src/components/SettingsModal.vue +275 -0
- package/src/components/SettingsReferenceDirsTab.vue +173 -0
- package/src/components/SettingsWorkspaceDirsTab.vue +174 -0
- package/src/components/SidebarHeader.vue +69 -0
- package/src/components/StackView.vue +360 -0
- package/src/components/SuggestionsPanel.vue +65 -0
- package/src/components/TodoExplorer.vue +358 -0
- package/src/components/ToolResultsPanel.vue +77 -0
- package/src/components/todo/TodoAddDialog.vue +131 -0
- package/src/components/todo/TodoEditDialog.vue +47 -0
- package/src/components/todo/TodoEditPanel.vue +113 -0
- package/src/components/todo/TodoKanbanView.vue +249 -0
- package/src/components/todo/TodoListView.vue +79 -0
- package/src/components/todo/TodoTableView.vue +177 -0
- package/src/composables/useActiveSession.ts +40 -0
- package/src/composables/useAppApi.ts +45 -0
- package/src/composables/useCanvasViewMode.ts +121 -0
- package/src/composables/useChatScroll.ts +47 -0
- package/src/composables/useClickOutside.ts +26 -0
- package/src/composables/useClipboardCopy.ts +44 -0
- package/src/composables/useContentDisplay.ts +52 -0
- package/src/composables/useDebugBeat.ts +23 -0
- package/src/composables/useDynamicFavicon.ts +115 -0
- package/src/composables/useEventListeners.ts +42 -0
- package/src/composables/useExpandedDirs.ts +64 -0
- package/src/composables/useFaviconState.ts +30 -0
- package/src/composables/useFileSelection.ts +115 -0
- package/src/composables/useFileSortMode.ts +24 -0
- package/src/composables/useFileTree.ts +85 -0
- package/src/composables/useFreshPluginData.ts +89 -0
- package/src/composables/useHealth.ts +38 -0
- package/src/composables/useImeAwareEnter.ts +57 -0
- package/src/composables/useKeyNavigation.ts +60 -0
- package/src/composables/useMarkdownLinkHandler.ts +46 -0
- package/src/composables/useMarkdownMode.ts +17 -0
- package/src/composables/useMcpTools.ts +71 -0
- package/src/composables/useMergedSessions.ts +27 -0
- package/src/composables/useNotifications.ts +90 -0
- package/src/composables/usePdfDownload.ts +60 -0
- package/src/composables/usePendingCalls.ts +77 -0
- package/src/composables/usePubSub.ts +85 -0
- package/src/composables/useRightSidebar.ts +23 -0
- package/src/composables/useRoles.ts +34 -0
- package/src/composables/useSandboxStatus.ts +67 -0
- package/src/composables/useSelectedResult.ts +49 -0
- package/src/composables/useSessionDerived.ts +51 -0
- package/src/composables/useSessionHistory.ts +81 -0
- package/src/composables/useSessionSync.ts +57 -0
- package/src/composables/useViewLayout.ts +55 -0
- package/src/config/apiRoutes.ts +173 -0
- package/src/config/pubsubChannels.ts +45 -0
- package/src/config/roles.ts +335 -0
- package/src/config/schedulerActions.ts +25 -0
- package/src/config/toolNames.ts +71 -0
- package/src/config/workspacePaths.ts +24 -0
- package/src/index.css +107 -0
- package/src/main.ts +25 -0
- package/src/plugins/canvas/Preview.vue +13 -0
- package/src/plugins/canvas/View.vue +333 -0
- package/src/plugins/canvas/definition.ts +38 -0
- package/src/plugins/canvas/index.ts +36 -0
- package/src/plugins/chart/Preview.vue +49 -0
- package/src/plugins/chart/View.vue +143 -0
- package/src/plugins/chart/definition.ts +58 -0
- package/src/plugins/chart/index.ts +52 -0
- package/src/plugins/editImage/Preview.vue +13 -0
- package/src/plugins/editImage/View.vue +13 -0
- package/src/plugins/editImage/definition.ts +27 -0
- package/src/plugins/editImage/index.ts +36 -0
- package/src/plugins/generateImage/Preview.vue +13 -0
- package/src/plugins/generateImage/View.vue +33 -0
- package/src/plugins/generateImage/definition.ts +32 -0
- package/src/plugins/generateImage/index.ts +56 -0
- package/src/plugins/manageRoles/Preview.vue +49 -0
- package/src/plugins/manageRoles/View.vue +525 -0
- package/src/plugins/manageRoles/definition.ts +43 -0
- package/src/plugins/manageRoles/index.ts +47 -0
- package/src/plugins/manageSkills/Preview.vue +21 -0
- package/src/plugins/manageSkills/View.vue +321 -0
- package/src/plugins/manageSkills/definition.ts +49 -0
- package/src/plugins/manageSkills/index.ts +49 -0
- package/src/plugins/manageSource/Preview.vue +33 -0
- package/src/plugins/manageSource/View.vue +697 -0
- package/src/plugins/manageSource/definition.ts +63 -0
- package/src/plugins/manageSource/index.ts +66 -0
- package/src/plugins/markdown/Preview.vue +77 -0
- package/src/plugins/markdown/View.vue +476 -0
- package/src/plugins/markdown/definition.ts +50 -0
- package/src/plugins/markdown/index.ts +36 -0
- package/src/plugins/presentHtml/Preview.vue +25 -0
- package/src/plugins/presentHtml/View.vue +52 -0
- package/src/plugins/presentHtml/definition.ts +27 -0
- package/src/plugins/presentHtml/helpers.ts +72 -0
- package/src/plugins/presentHtml/index.ts +41 -0
- package/src/plugins/presentMulmoScript/Preview.vue +23 -0
- package/src/plugins/presentMulmoScript/View.vue +1166 -0
- package/src/plugins/presentMulmoScript/definition.ts +95 -0
- package/src/plugins/presentMulmoScript/helpers.ts +162 -0
- package/src/plugins/presentMulmoScript/index.ts +40 -0
- package/src/plugins/scheduler/Preview.vue +67 -0
- package/src/plugins/scheduler/TasksTab.vue +205 -0
- package/src/plugins/scheduler/View.vue +565 -0
- package/src/plugins/scheduler/definition.ts +57 -0
- package/src/plugins/scheduler/index.ts +45 -0
- package/src/plugins/scheduler/viewModes.ts +26 -0
- package/src/plugins/spreadsheet/Preview.vue +29 -0
- package/src/plugins/spreadsheet/View.vue +997 -0
- package/src/plugins/spreadsheet/cellHighlights.ts +79 -0
- package/src/plugins/spreadsheet/definition.ts +121 -0
- package/src/plugins/spreadsheet/engine/calculator.ts +459 -0
- package/src/plugins/spreadsheet/engine/cellBuilder.ts +81 -0
- package/src/plugins/spreadsheet/engine/date-parser.ts +220 -0
- package/src/plugins/spreadsheet/engine/date-utils.ts +56 -0
- package/src/plugins/spreadsheet/engine/engine.ts +176 -0
- package/src/plugins/spreadsheet/engine/evaluator.ts +390 -0
- package/src/plugins/spreadsheet/engine/formatter.ts +172 -0
- package/src/plugins/spreadsheet/engine/formulaRefs.ts +101 -0
- package/src/plugins/spreadsheet/engine/functions/date.ts +299 -0
- package/src/plugins/spreadsheet/engine/functions/financial.ts +387 -0
- package/src/plugins/spreadsheet/engine/functions/index.ts +16 -0
- package/src/plugins/spreadsheet/engine/functions/logical.ts +262 -0
- package/src/plugins/spreadsheet/engine/functions/lookup.ts +400 -0
- package/src/plugins/spreadsheet/engine/functions/mathematical.ts +297 -0
- package/src/plugins/spreadsheet/engine/functions/statistical.ts +338 -0
- package/src/plugins/spreadsheet/engine/functions/text.ts +389 -0
- package/src/plugins/spreadsheet/engine/index.ts +27 -0
- package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +111 -0
- package/src/plugins/spreadsheet/engine/parser.ts +143 -0
- package/src/plugins/spreadsheet/engine/registry.ts +150 -0
- package/src/plugins/spreadsheet/engine/responseDecoder.ts +67 -0
- package/src/plugins/spreadsheet/engine/types.ts +64 -0
- package/src/plugins/spreadsheet/index.ts +36 -0
- package/src/plugins/textResponse/Preview.vue +94 -0
- package/src/plugins/textResponse/View.vue +503 -0
- package/src/plugins/textResponse/definition.ts +34 -0
- package/src/plugins/textResponse/index.ts +27 -0
- package/src/plugins/textResponse/plugin.ts +29 -0
- package/src/plugins/textResponse/samples.ts +97 -0
- package/src/plugins/textResponse/types.ts +11 -0
- package/src/plugins/todo/Preview.vue +63 -0
- package/src/plugins/todo/View.vue +364 -0
- package/src/plugins/todo/composables/useTodos.ts +177 -0
- package/src/plugins/todo/definition.ts +45 -0
- package/src/plugins/todo/index.ts +61 -0
- package/src/plugins/todo/labels.ts +163 -0
- package/src/plugins/todo/priority.ts +98 -0
- package/src/plugins/todo/viewModes.ts +19 -0
- package/src/plugins/ui-image/ImagePreview.vue +23 -0
- package/src/plugins/ui-image/ImageView.vue +34 -0
- package/src/plugins/ui-image/index.ts +3 -0
- package/src/plugins/ui-image/types.ts +4 -0
- package/src/plugins/wiki/Preview.vue +65 -0
- package/src/plugins/wiki/View.vue +342 -0
- package/src/plugins/wiki/definition.ts +25 -0
- package/src/plugins/wiki/helpers.ts +59 -0
- package/src/plugins/wiki/index.ts +52 -0
- package/src/router/guards.ts +61 -0
- package/src/router/index.ts +50 -0
- package/src/tools/index.ts +52 -0
- package/src/tools/types.ts +27 -0
- package/src/types/events.ts +16 -0
- package/src/types/fileTree.ts +13 -0
- package/src/types/notification.ts +67 -0
- package/src/types/session.ts +116 -0
- package/src/types/sse.ts +90 -0
- package/src/types/toolCallHistory.ts +13 -0
- package/src/utils/agent/eventDispatch.ts +74 -0
- package/src/utils/agent/request.ts +55 -0
- package/src/utils/agent/toolCalls.ts +62 -0
- package/src/utils/api.ts +218 -0
- package/src/utils/canvas/viewMode.ts +46 -0
- package/src/utils/dom/authTokenMeta.ts +20 -0
- package/src/utils/dom/clickOutside.ts +11 -0
- package/src/utils/dom/externalLink.ts +57 -0
- package/src/utils/dom/scrollable.ts +24 -0
- package/src/utils/errors.ts +11 -0
- package/src/utils/files/expandedDirs.ts +25 -0
- package/src/utils/files/filename.ts +12 -0
- package/src/utils/files/sortChildren.ts +20 -0
- package/src/utils/filesPreview/schedulerPreview.ts +38 -0
- package/src/utils/filesPreview/todoPreview.ts +40 -0
- package/src/utils/format/date.ts +85 -0
- package/src/utils/format/frontmatter.ts +80 -0
- package/src/utils/format/jsonSyntax.ts +109 -0
- package/src/utils/html/previewCsp.ts +65 -0
- package/src/utils/image/resolve.ts +8 -0
- package/src/utils/image/rewriteMarkdownImageRefs.ts +182 -0
- package/src/utils/markdown/extractFirstH1.ts +39 -0
- package/src/utils/notification/dispatch.ts +22 -0
- package/src/utils/path/relativeLink.ts +130 -0
- package/src/utils/role/icon.ts +20 -0
- package/src/utils/role/merge.ts +10 -0
- package/src/utils/role/plugins.ts +12 -0
- package/src/utils/session/mergeSessions.ts +103 -0
- package/src/utils/session/seedRoleDefault.ts +35 -0
- package/src/utils/session/sessionEntries.ts +121 -0
- package/src/utils/session/sessionFactory.ts +22 -0
- package/src/utils/session/sessionHelpers.ts +99 -0
- package/src/utils/tools/dedup.ts +17 -0
- package/src/utils/tools/mcp.ts +33 -0
- package/src/utils/tools/pendingCalls.ts +16 -0
- package/src/utils/tools/result.ts +40 -0
- 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
|
+
}
|