mulmoclaude 0.1.2 → 0.3.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 (120) hide show
  1. package/bin/mulmoclaude.js +1 -1
  2. package/client/assets/{index-KNLBjwuh.css → index-Bm70FDU2.css} +1 -1
  3. package/client/assets/{index-D8rhwXLq.js → index-eHWB79u5.js} +3 -3
  4. package/client/index.html +2 -2
  5. package/package.json +1 -1
  6. package/server/agent/config.ts +12 -12
  7. package/server/agent/mcp-server.ts +19 -19
  8. package/server/agent/mcp-tools/x.ts +5 -5
  9. package/server/agent/prompt.ts +9 -4
  10. package/server/agent/sandboxMounts.ts +7 -7
  11. package/server/agent/stream.ts +4 -4
  12. package/server/api/routes/files.ts +9 -9
  13. package/server/api/routes/scheduler.ts +8 -8
  14. package/server/api/routes/schedulerHandlers.ts +12 -12
  15. package/server/api/routes/schedulerTasks.ts +14 -14
  16. package/server/api/routes/sessions.ts +24 -24
  17. package/server/api/routes/todosColumnsHandlers.ts +30 -30
  18. package/server/api/routes/wiki.ts +14 -14
  19. package/server/events/scheduler-adapter.ts +20 -20
  20. package/server/events/session-store/index.ts +10 -10
  21. package/server/events/task-manager/index.ts +7 -7
  22. package/server/index.ts +19 -19
  23. package/server/utils/date.ts +18 -18
  24. package/server/utils/files/atomic.ts +9 -9
  25. package/server/utils/files/html-io.ts +5 -5
  26. package/server/utils/files/image-store.ts +2 -2
  27. package/server/utils/files/journal-io.ts +2 -2
  28. package/server/utils/files/naming.ts +2 -2
  29. package/server/utils/files/roles-io.ts +10 -10
  30. package/server/utils/files/scheduler-io.ts +5 -5
  31. package/server/utils/files/session-io.ts +35 -35
  32. package/server/utils/files/spreadsheet-store.ts +2 -2
  33. package/server/utils/files/todos-io.ts +9 -9
  34. package/server/utils/files/user-tasks-io.ts +5 -5
  35. package/server/workspace/chat-index/indexer.ts +15 -15
  36. package/server/workspace/custom-dirs.ts +11 -11
  37. package/server/workspace/journal/archivist.ts +35 -35
  38. package/server/workspace/journal/dailyPass.ts +31 -28
  39. package/server/workspace/journal/indexFile.ts +29 -25
  40. package/server/workspace/reference-dirs.ts +18 -18
  41. package/server/workspace/roles.ts +6 -6
  42. package/server/workspace/skills/discovery.ts +4 -4
  43. package/server/workspace/skills/user-tasks.ts +34 -34
  44. package/server/workspace/sources/arxivDiscovery.ts +8 -8
  45. package/server/workspace/sources/classifier.ts +7 -7
  46. package/server/workspace/sources/fetchers/arxiv.ts +7 -7
  47. package/server/workspace/sources/fetchers/githubIssues.ts +7 -7
  48. package/server/workspace/sources/fetchers/githubReleases.ts +7 -7
  49. package/server/workspace/sources/interests.ts +9 -9
  50. package/server/workspace/sources/pipeline/index.ts +6 -6
  51. package/server/workspace/sources/pipeline/plan.ts +5 -5
  52. package/server/workspace/sources/registry.ts +16 -16
  53. package/server/workspace/sources/robots.ts +14 -14
  54. package/server/workspace/sources/sourceState.ts +11 -9
  55. package/server/workspace/tool-trace/index.ts +1 -1
  56. package/server/workspace/tool-trace/writeSearch.ts +26 -16
  57. package/server/workspace/wiki-backlinks/index.ts +8 -8
  58. package/server/workspace/wiki-backlinks/sessionBacklinks.ts +15 -15
  59. package/src/App.vue +30 -30
  60. package/src/components/ChatInput.vue +7 -7
  61. package/src/components/LockStatusPopup.vue +2 -2
  62. package/src/components/NotificationToast.vue +2 -2
  63. package/src/components/RoleSelector.vue +2 -2
  64. package/src/components/SessionHistoryPanel.vue +6 -6
  65. package/src/components/SettingsMcpTab.vue +7 -7
  66. package/src/components/SettingsModal.vue +3 -3
  67. package/src/components/SettingsReferenceDirsTab.vue +10 -10
  68. package/src/components/SettingsWorkspaceDirsTab.vue +5 -5
  69. package/src/components/SuggestionsPanel.vue +2 -2
  70. package/src/components/todo/TodoAddDialog.vue +2 -2
  71. package/src/components/todo/TodoEditPanel.vue +2 -2
  72. package/src/components/todo/TodoListView.vue +5 -5
  73. package/src/composables/useCanvasViewMode.ts +5 -5
  74. package/src/composables/useClickOutside.ts +2 -2
  75. package/src/composables/useFreshPluginData.ts +3 -3
  76. package/src/composables/useKeyNavigation.ts +11 -11
  77. package/src/composables/useMcpTools.ts +2 -2
  78. package/src/composables/useNotifications.ts +3 -3
  79. package/src/composables/usePdfDownload.ts +4 -4
  80. package/src/composables/usePendingCalls.ts +1 -1
  81. package/src/composables/usePubSub.ts +10 -10
  82. package/src/composables/useRoles.ts +1 -1
  83. package/src/composables/useSandboxStatus.ts +1 -1
  84. package/src/composables/useSessionDerived.ts +3 -3
  85. package/src/composables/useSessionSync.ts +8 -8
  86. package/src/composables/useViewLayout.ts +2 -2
  87. package/src/config/roles.ts +2 -2
  88. package/src/plugins/chart/Preview.vue +4 -4
  89. package/src/plugins/manageSkills/View.vue +3 -3
  90. package/src/plugins/manageSource/Preview.vue +1 -1
  91. package/src/plugins/markdown/View.vue +2 -2
  92. package/src/plugins/presentHtml/helpers.ts +8 -8
  93. package/src/plugins/presentMulmoScript/View.vue +4 -4
  94. package/src/plugins/presentMulmoScript/helpers.ts +1 -1
  95. package/src/plugins/scheduler/Preview.vue +6 -6
  96. package/src/plugins/scheduler/TasksTab.vue +4 -4
  97. package/src/plugins/textResponse/View.vue +2 -2
  98. package/src/plugins/todo/Preview.vue +2 -2
  99. package/src/plugins/todo/View.vue +11 -11
  100. package/src/plugins/todo/composables/useTodos.ts +5 -5
  101. package/src/plugins/wiki/Preview.vue +5 -5
  102. package/src/plugins/wiki/helpers.ts +4 -4
  103. package/src/router/guards.ts +12 -12
  104. package/src/types/session.ts +4 -3
  105. package/src/utils/agent/request.ts +3 -3
  106. package/src/utils/dom/scrollable.ts +2 -2
  107. package/src/utils/files/expandedDirs.ts +1 -1
  108. package/src/utils/files/sortChildren.ts +6 -6
  109. package/src/utils/format/frontmatter.ts +6 -6
  110. package/src/utils/image/rewriteMarkdownImageRefs.ts +5 -5
  111. package/src/utils/markdown/extractFirstH1.ts +2 -2
  112. package/src/utils/path/relativeLink.ts +15 -15
  113. package/src/utils/role/icon.ts +2 -2
  114. package/src/utils/role/merge.ts +2 -2
  115. package/src/utils/role/plugins.ts +1 -1
  116. package/src/utils/session/sessionFactory.ts +2 -2
  117. package/src/utils/session/sessionHelpers.ts +2 -2
  118. package/src/utils/tools/dedup.ts +4 -4
  119. package/src/utils/tools/result.ts +3 -3
  120. package/src/utils/types.ts +2 -2
@@ -66,15 +66,15 @@ function parseValue(raw: string): FrontmatterValue {
66
66
  if (arrayMatch) {
67
67
  return arrayMatch[1]
68
68
  .split(",")
69
- .map((s) => unquote(s.trim()))
70
- .filter((s) => s.length > 0);
69
+ .map((item) => unquote(item.trim()))
70
+ .filter((item) => item.length > 0);
71
71
  }
72
72
  return unquote(raw);
73
73
  }
74
74
 
75
- function unquote(s: string): string {
76
- if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
77
- return s.slice(1, -1);
75
+ function unquote(str: string): string {
76
+ if ((str.startsWith('"') && str.endsWith('"')) || (str.startsWith("'") && str.endsWith("'"))) {
77
+ return str.slice(1, -1);
78
78
  }
79
- return s;
79
+ return str;
80
80
  }
@@ -51,7 +51,7 @@ function shouldSkip(url: string): boolean {
51
51
  function resolveWorkspacePath(basePath: string, url: string): string | null {
52
52
  // Absolute-within-workspace (e.g. "/images/foo.png") — reset base.
53
53
  const isAbsolute = url.startsWith("/");
54
- const baseSegs = isAbsolute ? [] : basePath.split("/").filter((s) => s !== "" && s !== ".");
54
+ const baseSegs = isAbsolute ? [] : basePath.split("/").filter((seg) => seg !== "" && seg !== ".");
55
55
  const segs = [...baseSegs];
56
56
 
57
57
  const urlSegs = (isAbsolute ? url.slice(1) : url).split("/");
@@ -76,9 +76,9 @@ function extractBracketedAlt(raw: string): string | null {
76
76
  if (!raw.startsWith("![")) return null;
77
77
  let depth = 1;
78
78
  for (let i = 2; i < raw.length; i++) {
79
- const c = raw[i];
80
- if (c === "[") depth++;
81
- else if (c === "]") {
79
+ const char = raw[i];
80
+ if (char === "[") depth++;
81
+ else if (char === "]") {
82
82
  depth--;
83
83
  if (depth === 0) return raw.slice(2, i);
84
84
  }
@@ -123,7 +123,7 @@ function getContainerChildren(token: Token): Token[] | null {
123
123
  // Returns true if the container was rendered via its children, false
124
124
  // if the caller should fall back to emitting the parent's raw.
125
125
  function renderContainerChildren(raw: string, children: Token[], basePath: string, out: string[]): boolean {
126
- const joined = children.map((c) => (c as { raw?: string }).raw ?? "").join("");
126
+ const joined = children.map((token) => (token as { raw?: string }).raw ?? "").join("");
127
127
  if (joined === "") return false;
128
128
  const idx = raw.indexOf(joined);
129
129
  if (idx < 0) return false;
@@ -30,8 +30,8 @@ export function extractFirstH1(markdown: string): string | null {
30
30
  return null;
31
31
  }
32
32
 
33
- function splitLines(s: string): string[] {
34
- return s.split(/\r\n|\r|\n/);
33
+ function splitLines(str: string): string[] {
34
+ return str.split(/\r\n|\r|\n/);
35
35
  }
36
36
 
37
37
  function isInlineSpace(code: number): boolean {
@@ -73,13 +73,13 @@ export function resolveWorkspaceLink(currentFilePath: string, href: string): str
73
73
 
74
74
  // Drop any trailing #fragment or ?query from a path-like string.
75
75
  // Whichever marker comes first wins.
76
- function stripFragmentAndQuery(s: string): string {
77
- const hashIdx = s.indexOf("#");
78
- const queryIdx = s.indexOf("?");
79
- let end = s.length;
76
+ function stripFragmentAndQuery(str: string): string {
77
+ const hashIdx = str.indexOf("#");
78
+ const queryIdx = str.indexOf("?");
79
+ let end = str.length;
80
80
  if (hashIdx !== -1 && hashIdx < end) end = hashIdx;
81
81
  if (queryIdx !== -1 && queryIdx < end) end = queryIdx;
82
- return s.slice(0, end);
82
+ return str.slice(0, end);
83
83
  }
84
84
 
85
85
  // If `resolvedPath` points at a chat session log (e.g.
@@ -95,26 +95,26 @@ export function extractSessionIdFromPath(resolvedPath: string): string | null {
95
95
  const JSONL_SUFFIX = ".jsonl";
96
96
  if (!resolvedPath.startsWith(CHAT_PREFIX)) return null;
97
97
  if (!resolvedPath.endsWith(JSONL_SUFFIX)) return null;
98
- const id = resolvedPath.slice(CHAT_PREFIX.length, resolvedPath.length - JSONL_SUFFIX.length);
99
- if (id.length === 0) return null;
100
- if (id.includes("/")) return null;
101
- return id;
98
+ const sessionId = resolvedPath.slice(CHAT_PREFIX.length, resolvedPath.length - JSONL_SUFFIX.length);
99
+ if (sessionId.length === 0) return null;
100
+ if (sessionId.includes("/")) return null;
101
+ return sessionId;
102
102
  }
103
103
 
104
104
  // POSIX-style dirname. The file viewer always uses "/" separators
105
105
  // so we don't need to worry about Windows paths.
106
- function posixDirname(p: string): string {
107
- const i = p.lastIndexOf("/");
108
- return i === -1 ? "" : p.slice(0, i);
106
+ function posixDirname(path: string): string {
107
+ const i = path.lastIndexOf("/");
108
+ return i === -1 ? "" : path.slice(0, i);
109
109
  }
110
110
 
111
111
  // Collapse "./" and "../" in a workspace path. Rejects paths that
112
112
  // escape above the workspace root. Returns null for the empty-path
113
113
  // case so the caller can bail out. Callers are expected to strip
114
114
  // #fragment / ?query before invoking this function.
115
- function normalizeWorkspacePath(p: string): string | null {
116
- if (p.length === 0) return null;
117
- const parts = p.split("/");
115
+ function normalizeWorkspacePath(path: string): string | null {
116
+ if (path.length === 0) return null;
117
+ const parts = path.split("/");
118
118
  const stack: string[] = [];
119
119
  for (const part of parts) {
120
120
  if (part === "" || part === ".") continue;
@@ -11,10 +11,10 @@ import type { Role } from "../../config/roles";
11
11
  const MATERIAL_ICON_RE = /^[a-z_]+$/;
12
12
 
13
13
  export function roleIcon(roles: Role[], roleId: string): string {
14
- const icon = roles.find((r) => r.id === roleId)?.icon ?? "star";
14
+ const icon = roles.find((role) => role.id === roleId)?.icon ?? "star";
15
15
  return MATERIAL_ICON_RE.test(icon) ? icon : "smart_toy";
16
16
  }
17
17
 
18
18
  export function roleName(roles: Role[], roleId: string): string {
19
- return roles.find((r) => r.id === roleId)?.name ?? roleId;
19
+ return roles.find((role) => role.id === roleId)?.name ?? roleId;
20
20
  }
@@ -5,6 +5,6 @@
5
5
  import type { Role } from "../../config/roles";
6
6
 
7
7
  export function mergeRoles(builtin: Role[], custom: Role[]): Role[] {
8
- const customIds = new Set(custom.map((r) => r.id));
9
- return [...builtin.filter((r) => !customIds.has(r.id)), ...custom];
8
+ const customIds = new Set(custom.map((role) => role.id));
9
+ return [...builtin.filter((role) => !customIds.has(role.id)), ...custom];
10
10
  }
@@ -8,5 +8,5 @@ const GEMINI_PLUGINS = new Set<ToolName>([TOOL_NAMES.generateImage, TOOL_NAMES.p
8
8
 
9
9
  /** Whether the given role uses any plugin that requires a Gemini API key. */
10
10
  export function needsGemini(roles: Role[], roleId: string): boolean {
11
- return (roles.find((r) => r.id === roleId)?.availablePlugins ?? []).some((p) => GEMINI_PLUGINS.has(p));
11
+ return (roles.find((role) => role.id === roleId)?.availablePlugins ?? []).some((plugin) => GEMINI_PLUGINS.has(plugin));
12
12
  }
@@ -2,10 +2,10 @@
2
2
 
3
3
  import type { ActiveSession } from "../../types/session";
4
4
 
5
- export function createEmptySession(id: string, roleId: string): ActiveSession {
5
+ export function createEmptySession(sessionId: string, roleId: string): ActiveSession {
6
6
  const now = new Date().toISOString();
7
7
  return {
8
- id,
8
+ id: sessionId,
9
9
  roleId,
10
10
  toolResults: [],
11
11
  resultTimestamps: new Map(),
@@ -79,7 +79,7 @@ export function applyTextEvent(session: ActiveSession, message: string, source:
79
79
  /** In-place update a result that was re-emitted by a plugin view
80
80
  * (e.g. after the user edits a chart config). */
81
81
  export function updateResult(session: ActiveSession, updatedResult: ToolResultComplete): void {
82
- const index = session.toolResults.findIndex((r) => r.uuid === updatedResult.uuid);
82
+ const index = session.toolResults.findIndex((result) => result.uuid === updatedResult.uuid);
83
83
  if (index !== -1) {
84
84
  Object.assign(session.toolResults[index], updatedResult);
85
85
  }
@@ -89,7 +89,7 @@ export function updateResult(session: ActiveSession, updatedResult: ToolResultCo
89
89
  * result list. Selects the result only on insert; in-place updates
90
90
  * preserve the user's current selection. */
91
91
  export function applyToolResultToSession(session: ActiveSession, result: ToolResultComplete): void {
92
- const idx = session.toolResults.findIndex((r) => r.uuid === result.uuid);
92
+ const idx = session.toolResults.findIndex((existing) => existing.uuid === result.uuid);
93
93
  if (idx >= 0) {
94
94
  session.toolResults[idx] = result;
95
95
  } else {
@@ -7,11 +7,11 @@
7
7
  import type { ToolResultComplete } from "gui-chat-protocol/vue";
8
8
 
9
9
  export function deduplicateResults(all: ToolResultComplete[]): ToolResultComplete[] {
10
- return all.filter((r, i) => {
11
- if (r.toolName === "text-response") return true;
10
+ return all.filter((result, i) => {
11
+ if (result.toolName === "text-response") return true;
12
12
  const next = all[i + 1];
13
13
  if (!next) return true;
14
- if (next.toolName !== r.toolName) return true;
15
- return !(r.updating === true && next.updating === true);
14
+ if (next.toolName !== result.toolName) return true;
15
+ return !(result.updating === true && next.updating === true);
16
16
  });
17
17
  }
@@ -9,9 +9,9 @@ import { isRecord } from "../types";
9
9
  // Type guard: a text-response entry whose `data.role` is `"user"`.
10
10
  // Used by App.vue to find the first user message in a live session
11
11
  // when building the merged history list.
12
- export function isUserTextResponse(r: ToolResultComplete): boolean {
13
- if (r.toolName !== "text-response") return false;
14
- const data = r.data;
12
+ export function isUserTextResponse(res: ToolResultComplete): boolean {
13
+ if (res.toolName !== "text-response") return false;
14
+ const data = res.data;
15
15
  if (!isRecord(data)) return false;
16
16
  return data.role === "user";
17
17
  }
@@ -20,12 +20,12 @@ export function isNonEmptyString(value: unknown): value is string {
20
20
  /** Record whose values are all strings. */
21
21
  export function isStringRecord(value: unknown): value is Record<string, string> {
22
22
  if (!isRecord(value)) return false;
23
- return Object.values(value).every((v) => typeof v === "string");
23
+ return Object.values(value).every((val) => typeof val === "string");
24
24
  }
25
25
 
26
26
  /** String array (every element is a string). */
27
27
  export function isStringArray(value: unknown): value is string[] {
28
- return Array.isArray(value) && value.every((v) => typeof v === "string");
28
+ return Array.isArray(value) && value.every((val) => typeof val === "string");
29
29
  }
30
30
 
31
31
  /** Error-like object with a `code` property. */