mulmoclaude 2.6.0 → 2.8.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/client/assets/Graph-ChAxkn9o.js +1 -0
- package/client/assets/{JsonEditor-FKfzzMkt.js → JsonEditor-COQcmjLQ.js} +1 -1
- package/client/assets/dist-Bij44LdC.js +2 -0
- package/client/assets/echarts-GV3U9sVf.js +1 -0
- package/client/assets/echarts-gl-BXx4On0Y.js +4282 -0
- package/client/assets/echarts-wordcloud-_9uQUAfp.js +1 -0
- package/client/assets/emscripten-module-Q67P5WYC-BU3Hlttz.js +2160 -0
- package/client/assets/ffi-4V2b7xu3.js +1 -0
- package/client/assets/{html2canvas-D0m9Y8pD-y1tDAhR3.js → html2canvas-NBfz6Rb9-CIdEBoH2.js} +1 -1
- package/client/assets/{index-gnbD2OHU.js → index-D6xzCZhP.js} +605 -604
- package/client/assets/index-DzMsXDnj.css +2 -0
- package/client/assets/{index.es-HSMDtZ_a-CYtNwG_G.js → index.es-STZX6pYP-BGvt3MI2.js} +3 -3
- package/client/assets/install-C1moNcrn.js +15 -0
- package/client/assets/japan-CyDc-cz7-BgORDJsl.js +1 -0
- package/client/assets/{marp-BVojbGre.js → marp-Du5Sym5b.js} +57 -57
- package/client/assets/module-ES6BEMUI-CDTIpMiA.js +6 -0
- package/client/assets/purify.es-DkTnAgLw-BZxk_wWO.js +3 -0
- package/client/assets/style-BZwVK1dO-BFtilmZh.js +1 -0
- package/client/assets/vue-i18n-YREco4q1.js +3 -0
- package/client/assets/world-R-tMZ4nZ-B1c4T9X2.js +1 -0
- package/client/index.html +6 -4
- package/package.json +13 -12
- package/server/api/routes/plugins.ts +9 -59
- package/server/build/mcp-server.mjs +806 -714
- package/server/index.ts +1 -0
- package/server/plugins/confined-file-ops.ts +56 -0
- package/server/plugins/spreadsheet-builtin.ts +26 -0
- package/server/plugins/spreadsheet-save-outcome.ts +7 -0
- package/server/utils/files/store-resolvers.ts +1 -1
- package/src/lang/de.ts +0 -20
- package/src/lang/en.ts +0 -20
- package/src/lang/es.ts +0 -20
- package/src/lang/fr.ts +0 -20
- package/src/lang/ja.ts +0 -20
- package/src/lang/ko.ts +0 -20
- package/src/lang/pt-BR.ts +0 -20
- package/src/lang/zh.ts +0 -20
- package/src/plugins/spreadsheet/definition.ts +9 -127
- package/src/plugins/spreadsheet/index.ts +7 -4
- package/src/plugins/spreadsheet/meta.ts +2 -4
- package/client/assets/index-BA0fpWIZ.css +0 -2
- package/client/assets/purify.es-BaSjDiWD-Dbm5oOcm.js +0 -3
- package/client/assets/style-CCV5GAnU-D55tgtzm.js +0 -1
- package/client/assets/vue-i18n-2bgWpywp.js +0 -3
- package/server/utils/files/spreadsheet-store.ts +0 -26
- package/src/plugins/spreadsheet/Preview.vue +0 -31
- package/src/plugins/spreadsheet/View.vue +0 -1007
- package/src/plugins/spreadsheet/cellHighlights.ts +0 -78
- package/src/plugins/spreadsheet/engine/calculator.ts +0 -427
- package/src/plugins/spreadsheet/engine/cellBuilder.ts +0 -89
- package/src/plugins/spreadsheet/engine/cellEmpty.ts +0 -26
- package/src/plugins/spreadsheet/engine/cellFormatting.ts +0 -59
- package/src/plugins/spreadsheet/engine/coerce-boolean.ts +0 -26
- package/src/plugins/spreadsheet/engine/condition.ts +0 -182
- package/src/plugins/spreadsheet/engine/date-locale.ts +0 -47
- package/src/plugins/spreadsheet/engine/date-parser.ts +0 -234
- package/src/plugins/spreadsheet/engine/date-utils.ts +0 -73
- package/src/plugins/spreadsheet/engine/datedif.ts +0 -93
- package/src/plugins/spreadsheet/engine/engine.ts +0 -177
- package/src/plugins/spreadsheet/engine/evaluator.ts +0 -494
- package/src/plugins/spreadsheet/engine/financial-math.ts +0 -114
- package/src/plugins/spreadsheet/engine/formatter.ts +0 -158
- package/src/plugins/spreadsheet/engine/formulaError.ts +0 -67
- package/src/plugins/spreadsheet/engine/formulaRefs.ts +0 -202
- package/src/plugins/spreadsheet/engine/functions/date.ts +0 -212
- package/src/plugins/spreadsheet/engine/functions/financial.ts +0 -244
- package/src/plugins/spreadsheet/engine/functions/index.ts +0 -16
- package/src/plugins/spreadsheet/engine/functions/logical.ts +0 -221
- package/src/plugins/spreadsheet/engine/functions/lookup-math.ts +0 -28
- package/src/plugins/spreadsheet/engine/functions/lookup.ts +0 -257
- package/src/plugins/spreadsheet/engine/functions/mathematical.ts +0 -283
- package/src/plugins/spreadsheet/engine/functions/statistical-math.ts +0 -91
- package/src/plugins/spreadsheet/engine/functions/statistical.ts +0 -290
- package/src/plugins/spreadsheet/engine/functions/text.ts +0 -398
- package/src/plugins/spreadsheet/engine/index.ts +0 -34
- package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +0 -111
- package/src/plugins/spreadsheet/engine/math-ops.ts +0 -98
- package/src/plugins/spreadsheet/engine/numericCoercion.ts +0 -61
- package/src/plugins/spreadsheet/engine/parser.ts +0 -40
- package/src/plugins/spreadsheet/engine/registry.ts +0 -201
- package/src/plugins/spreadsheet/engine/responseDecoder.ts +0 -68
- package/src/plugins/spreadsheet/engine/spreadsheet-errors.ts +0 -82
- package/src/plugins/spreadsheet/engine/textFormat.ts +0 -108
- package/src/plugins/spreadsheet/engine/translateFormula.ts +0 -69
- package/src/plugins/spreadsheet/engine/types.ts +0 -81
- package/src/plugins/spreadsheet/keyboardNav.ts +0 -38
package/server/index.ts
CHANGED
|
@@ -55,6 +55,7 @@ import "./plugins/markdown-builtin.js";
|
|
|
55
55
|
// Side-effect: registers the built-in "html" dispatch handler so the
|
|
56
56
|
// presentHtml View's useRuntime().dispatch({ kind }) resolves (phase 2).
|
|
57
57
|
import "./plugins/html-builtin.js";
|
|
58
|
+
import "./plugins/spreadsheet-builtin.js";
|
|
58
59
|
// Side-effect: registers the built-in "shapescript" dispatch handler so the
|
|
59
60
|
// presentShapeScript View's useRuntime().dispatch({ kind }) resolves — the
|
|
60
61
|
// source editor's load / save against artifacts/shapes/.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// FileOps that refuse a path whose real location (symlinks followed) leaves the scope root.
|
|
2
|
+
// `makeArtifactsFileOps` folds `..` lexically only, so a symlink inside the root still reaches out.
|
|
3
|
+
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { mkdir, readFile, readdir, realpath, stat as fsStat, unlink as fsUnlink } from "node:fs/promises";
|
|
6
|
+
import type { FileOps } from "gui-chat-protocol";
|
|
7
|
+
import { writeFileAtomic } from "../utils/files/atomic.js";
|
|
8
|
+
import { resolveWithinRoot, resolveWriteWithinRoot } from "../utils/files/safe.js";
|
|
9
|
+
import { workspacePath } from "../workspace/paths.js";
|
|
10
|
+
|
|
11
|
+
/** An escape reads as a missing file, so callers that treat ENOENT as "not there" stay closed. */
|
|
12
|
+
const notInScope = (rel: string): Error & { code: string } => Object.assign(new Error(`not inside the scope: ${rel}`), { code: "ENOENT" });
|
|
13
|
+
|
|
14
|
+
const realRoot = async (scopeRoot: string): Promise<string> => {
|
|
15
|
+
await mkdir(scopeRoot, { recursive: true });
|
|
16
|
+
return realpath(scopeRoot);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const existingPath = async (scopeRoot: string, rel: string): Promise<string | null> => resolveWithinRoot(await realRoot(scopeRoot), rel);
|
|
20
|
+
|
|
21
|
+
const requireExisting = async (scopeRoot: string, rel: string): Promise<string> => {
|
|
22
|
+
const resolved = await existingPath(scopeRoot, rel);
|
|
23
|
+
if (resolved === null) throw notInScope(rel);
|
|
24
|
+
return resolved;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function makeConfinedFileOps(scopeRoot: string): FileOps {
|
|
28
|
+
return {
|
|
29
|
+
read: async (rel) => readFile(await requireExisting(scopeRoot, rel), "utf-8"),
|
|
30
|
+
readBytes: async (rel) => {
|
|
31
|
+
const buf = await readFile(await requireExisting(scopeRoot, rel));
|
|
32
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
33
|
+
},
|
|
34
|
+
write: async (rel, content) => {
|
|
35
|
+
const target = await resolveWriteWithinRoot(await realRoot(scopeRoot), rel);
|
|
36
|
+
if (target === null) throw new Error(`refusing to write outside the scope: ${rel}`);
|
|
37
|
+
await writeFileAtomic(target, content);
|
|
38
|
+
},
|
|
39
|
+
readDir: async (rel) => {
|
|
40
|
+
const resolved = await existingPath(scopeRoot, rel);
|
|
41
|
+
return resolved === null ? [] : readdir(resolved);
|
|
42
|
+
},
|
|
43
|
+
stat: async (rel) => {
|
|
44
|
+
const { mtimeMs, size } = await fsStat(await requireExisting(scopeRoot, rel));
|
|
45
|
+
return { mtimeMs, size };
|
|
46
|
+
},
|
|
47
|
+
exists: async (rel) => (await existingPath(scopeRoot, rel)) !== null,
|
|
48
|
+
unlink: async (rel) => {
|
|
49
|
+
const resolved = await existingPath(scopeRoot, rel);
|
|
50
|
+
if (resolved !== null) await fsUnlink(resolved);
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The workspace `artifacts/` dir, confined as above. */
|
|
56
|
+
export const makeConfinedArtifactsFileOps = (): FileOps => makeConfinedFileOps(path.join(workspacePath, "artifacts"));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// MulmoClaude's host wiring for the presentSpreadsheet View's dispatch channel.
|
|
2
|
+
// The @gui-chat-plugin/spreadsheet View loads and saves its workbook through
|
|
3
|
+
// `useRuntime().dispatch({ kind: "loadSpreadsheet" | "saveSpreadsheet", … })`;
|
|
4
|
+
// this routes those calls to the package's `executeSpreadsheetDispatch` against
|
|
5
|
+
// the workspace `artifacts/` (confined by realpath, so a symlink cannot lead out), then publishes a file-change event
|
|
6
|
+
// after a save that wrote the file. Imported for side effect at boot (server/index.ts).
|
|
7
|
+
|
|
8
|
+
import { executeSpreadsheetDispatch, isSpreadsheetDispatchArgs } from "@gui-chat-plugin/spreadsheet";
|
|
9
|
+
import { makeConfinedArtifactsFileOps } from "./confined-file-ops.js";
|
|
10
|
+
import { publishFileChange } from "../events/file-change.js";
|
|
11
|
+
import { describeKind, registerBuiltinDispatch } from "./builtin-dispatch.js";
|
|
12
|
+
import { wroteSpreadsheetFile } from "./spreadsheet-save-outcome.js";
|
|
13
|
+
|
|
14
|
+
/** Matches `wrapWithScope("spreadsheet", …)` in `src/plugins/spreadsheet/index.ts`. */
|
|
15
|
+
const SPREADSHEET_SCOPE = "spreadsheet";
|
|
16
|
+
|
|
17
|
+
registerBuiltinDispatch(SPREADSHEET_SCOPE, async (args) => {
|
|
18
|
+
if (!isSpreadsheetDispatchArgs(args)) {
|
|
19
|
+
throw new Error(`spreadsheet plugin: unrecognised dispatch payload (kind=${describeKind(args)})`);
|
|
20
|
+
}
|
|
21
|
+
const result = await executeSpreadsheetDispatch(makeConfinedArtifactsFileOps(), args);
|
|
22
|
+
if (wroteSpreadsheetFile(args, result.data)) {
|
|
23
|
+
void publishFileChange(args.path);
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Whether a spreadsheet dispatch changed the file on disk, so the host should announce it.
|
|
2
|
+
// A save refused for a conflict (the file moved on since the View's baseVersion) wrote nothing.
|
|
3
|
+
|
|
4
|
+
import type { SpreadsheetDispatchArgs, SpreadsheetFileData } from "@gui-chat-plugin/spreadsheet";
|
|
5
|
+
|
|
6
|
+
export const wroteSpreadsheetFile = (args: SpreadsheetDispatchArgs, data: SpreadsheetFileData | undefined): boolean =>
|
|
7
|
+
args.kind === "saveSpreadsheet" && data !== undefined && data.conflict !== true;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Shared resolver factory for file-store modules (attachment-store,
|
|
2
|
-
// image-store
|
|
2
|
+
// image-store). Each store used to define its own
|
|
3
3
|
// `safeResolve(relativePath)` wrapper of identical shape: ensure the
|
|
4
4
|
// root dir exists, cache its realpath, strip the leading store
|
|
5
5
|
// prefix, hand off to `resolveWithinRoot`. That left three name-cloned
|
package/src/lang/de.ts
CHANGED
|
@@ -1099,26 +1099,6 @@ const deMessages = {
|
|
|
1099
1099
|
pluginSkill: {
|
|
1100
1100
|
noDescription: "(keine Beschreibung)",
|
|
1101
1101
|
},
|
|
1102
|
-
pluginSpreadsheet: {
|
|
1103
|
-
previewUntitled: "Tabelle",
|
|
1104
|
-
previewSheets: "{count} Blatt | {count} Blätter",
|
|
1105
|
-
untitled: "Tabelle",
|
|
1106
|
-
excel: "Excel",
|
|
1107
|
-
valuePlaceholder: "Wert",
|
|
1108
|
-
valueOrFormulaPlaceholder: "Wert oder Formel (z. B. 100 oder SUM(B2:B11))",
|
|
1109
|
-
formatPlaceholder: "Format (z. B. $#,##0.00)",
|
|
1110
|
-
loading: "Tabelle wird geladen...",
|
|
1111
|
-
noData: "Keine Tabellendaten verfügbar",
|
|
1112
|
-
editData: "Tabellendaten bearbeiten",
|
|
1113
|
-
applyChanges: "Änderungen übernehmen",
|
|
1114
|
-
dataMustBeArray: "Daten müssen ein Array von Blättern sein",
|
|
1115
|
-
loadFailed: "Laden der Tabelle fehlgeschlagen: {error}",
|
|
1116
|
-
invalidJsonAlert: "Ungültiges JSON-Format: {error}",
|
|
1117
|
-
unknownError: "Unbekannter Fehler",
|
|
1118
|
-
update: "Aktualisieren",
|
|
1119
|
-
stringType: "Zeichenkette",
|
|
1120
|
-
formulaType: "Formel",
|
|
1121
|
-
},
|
|
1122
1102
|
app: {
|
|
1123
1103
|
startConversation: "Gespräch beginnen",
|
|
1124
1104
|
thinking: "Denkt nach…",
|
package/src/lang/en.ts
CHANGED
|
@@ -1109,26 +1109,6 @@ const enMessages = {
|
|
|
1109
1109
|
pluginSkill: {
|
|
1110
1110
|
noDescription: "(no description)",
|
|
1111
1111
|
},
|
|
1112
|
-
pluginSpreadsheet: {
|
|
1113
|
-
previewUntitled: "Spreadsheet",
|
|
1114
|
-
previewSheets: "{count} sheet | {count} sheets",
|
|
1115
|
-
untitled: "Spreadsheet",
|
|
1116
|
-
excel: "Excel",
|
|
1117
|
-
valuePlaceholder: "Value",
|
|
1118
|
-
valueOrFormulaPlaceholder: "Value or Formula (e.g., 100 or SUM(B2:B11))",
|
|
1119
|
-
formatPlaceholder: "Format (e.g., $#,##0.00)",
|
|
1120
|
-
loading: "Loading spreadsheet...",
|
|
1121
|
-
noData: "No spreadsheet data available",
|
|
1122
|
-
editData: "Edit Spreadsheet Data",
|
|
1123
|
-
applyChanges: "Apply Changes",
|
|
1124
|
-
dataMustBeArray: "Data must be an array of sheets",
|
|
1125
|
-
loadFailed: "Failed to load spreadsheet: {error}",
|
|
1126
|
-
invalidJsonAlert: "Invalid JSON format: {error}",
|
|
1127
|
-
unknownError: "Unknown error",
|
|
1128
|
-
update: "Update",
|
|
1129
|
-
stringType: "String",
|
|
1130
|
-
formulaType: "Formula",
|
|
1131
|
-
},
|
|
1132
1112
|
app: {
|
|
1133
1113
|
startConversation: "Start a conversation",
|
|
1134
1114
|
thinking: "Thinking…",
|
package/src/lang/es.ts
CHANGED
|
@@ -1097,26 +1097,6 @@ const esMessages = {
|
|
|
1097
1097
|
pluginSkill: {
|
|
1098
1098
|
noDescription: "(sin descripción)",
|
|
1099
1099
|
},
|
|
1100
|
-
pluginSpreadsheet: {
|
|
1101
|
-
previewUntitled: "Hoja de cálculo",
|
|
1102
|
-
previewSheets: "{count} hoja | {count} hojas",
|
|
1103
|
-
untitled: "Hoja de cálculo",
|
|
1104
|
-
excel: "Excel",
|
|
1105
|
-
valuePlaceholder: "Valor",
|
|
1106
|
-
valueOrFormulaPlaceholder: "Valor o Fórmula (ej. 100 o SUM(B2:B11))",
|
|
1107
|
-
formatPlaceholder: "Formato (ej. $#,##0.00)",
|
|
1108
|
-
loading: "Cargando la hoja de cálculo...",
|
|
1109
|
-
noData: "No hay datos de hoja de cálculo disponibles",
|
|
1110
|
-
editData: "Editar datos de la hoja de cálculo",
|
|
1111
|
-
applyChanges: "Aplicar cambios",
|
|
1112
|
-
dataMustBeArray: "Los datos deben ser una matriz de hojas",
|
|
1113
|
-
loadFailed: "Error al cargar la hoja de cálculo: {error}",
|
|
1114
|
-
invalidJsonAlert: "Formato JSON no válido: {error}",
|
|
1115
|
-
unknownError: "Error desconocido",
|
|
1116
|
-
update: "Actualizar",
|
|
1117
|
-
stringType: "Texto",
|
|
1118
|
-
formulaType: "Fórmula",
|
|
1119
|
-
},
|
|
1120
1100
|
app: {
|
|
1121
1101
|
startConversation: "Inicia una conversación",
|
|
1122
1102
|
thinking: "Pensando…",
|
package/src/lang/fr.ts
CHANGED
|
@@ -1089,26 +1089,6 @@ const frMessages = {
|
|
|
1089
1089
|
pluginSkill: {
|
|
1090
1090
|
noDescription: "(aucune description)",
|
|
1091
1091
|
},
|
|
1092
|
-
pluginSpreadsheet: {
|
|
1093
|
-
previewUntitled: "Tableur",
|
|
1094
|
-
previewSheets: "{count} feuille | {count} feuilles",
|
|
1095
|
-
untitled: "Tableur",
|
|
1096
|
-
excel: "Excel",
|
|
1097
|
-
valuePlaceholder: "Valeur",
|
|
1098
|
-
valueOrFormulaPlaceholder: "Valeur ou formule (ex. 100 ou SUM(B2:B11))",
|
|
1099
|
-
formatPlaceholder: "Format (ex. $#,##0.00)",
|
|
1100
|
-
loading: "Chargement du tableur...",
|
|
1101
|
-
noData: "Aucune donnée de tableur disponible",
|
|
1102
|
-
editData: "Modifier les données du tableur",
|
|
1103
|
-
applyChanges: "Appliquer les modifications",
|
|
1104
|
-
dataMustBeArray: "Les données doivent être un tableau de feuilles",
|
|
1105
|
-
loadFailed: "Échec du chargement du tableur : {error}",
|
|
1106
|
-
invalidJsonAlert: "Format JSON non valide : {error}",
|
|
1107
|
-
unknownError: "Erreur inconnue",
|
|
1108
|
-
update: "Mettre à jour",
|
|
1109
|
-
stringType: "Texte",
|
|
1110
|
-
formulaType: "Formule",
|
|
1111
|
-
},
|
|
1112
1092
|
app: {
|
|
1113
1093
|
startConversation: "Démarrer une conversation",
|
|
1114
1094
|
thinking: "Réflexion…",
|
package/src/lang/ja.ts
CHANGED
|
@@ -1083,26 +1083,6 @@ const jaMessages = {
|
|
|
1083
1083
|
pluginSkill: {
|
|
1084
1084
|
noDescription: "(説明なし)",
|
|
1085
1085
|
},
|
|
1086
|
-
pluginSpreadsheet: {
|
|
1087
|
-
previewUntitled: "スプレッドシート",
|
|
1088
|
-
previewSheets: "{count} シート",
|
|
1089
|
-
untitled: "スプレッドシート",
|
|
1090
|
-
excel: "Excel",
|
|
1091
|
-
valuePlaceholder: "値",
|
|
1092
|
-
valueOrFormulaPlaceholder: "値または数式(例: 100, SUM(B2:B11))",
|
|
1093
|
-
formatPlaceholder: "書式(例: $#,##0.00)",
|
|
1094
|
-
loading: "スプレッドシート読み込み中...",
|
|
1095
|
-
noData: "スプレッドシートのデータがありません",
|
|
1096
|
-
editData: "スプレッドシートデータを編集",
|
|
1097
|
-
applyChanges: "変更を適用",
|
|
1098
|
-
dataMustBeArray: "データはシートの配列である必要があります",
|
|
1099
|
-
loadFailed: "スプレッドシートの読み込みに失敗: {error}",
|
|
1100
|
-
invalidJsonAlert: "不正な JSON 形式です: {error}",
|
|
1101
|
-
unknownError: "不明なエラー",
|
|
1102
|
-
update: "更新",
|
|
1103
|
-
stringType: "文字列",
|
|
1104
|
-
formulaType: "数式",
|
|
1105
|
-
},
|
|
1106
1086
|
app: {
|
|
1107
1087
|
startConversation: "会話を開始してください",
|
|
1108
1088
|
thinking: "考え中…",
|
package/src/lang/ko.ts
CHANGED
|
@@ -1081,26 +1081,6 @@ const koMessages = {
|
|
|
1081
1081
|
pluginSkill: {
|
|
1082
1082
|
noDescription: "(설명 없음)",
|
|
1083
1083
|
},
|
|
1084
|
-
pluginSpreadsheet: {
|
|
1085
|
-
previewUntitled: "스프레드시트",
|
|
1086
|
-
previewSheets: "{count}개 시트",
|
|
1087
|
-
untitled: "스프레드시트",
|
|
1088
|
-
excel: "Excel",
|
|
1089
|
-
valuePlaceholder: "값",
|
|
1090
|
-
valueOrFormulaPlaceholder: "값 또는 수식 (예: 100 또는 SUM(B2:B11))",
|
|
1091
|
-
formatPlaceholder: "형식 (예: $#,##0.00)",
|
|
1092
|
-
loading: "스프레드시트를 불러오는 중...",
|
|
1093
|
-
noData: "사용 가능한 스프레드시트 데이터가 없습니다",
|
|
1094
|
-
editData: "스프레드시트 데이터 편집",
|
|
1095
|
-
applyChanges: "변경 사항 적용",
|
|
1096
|
-
dataMustBeArray: "데이터는 시트 배열이어야 합니다",
|
|
1097
|
-
loadFailed: "스프레드시트 불러오기 실패: {error}",
|
|
1098
|
-
invalidJsonAlert: "잘못된 JSON 형식: {error}",
|
|
1099
|
-
unknownError: "알 수 없는 오류",
|
|
1100
|
-
update: "업데이트",
|
|
1101
|
-
stringType: "문자열",
|
|
1102
|
-
formulaType: "수식",
|
|
1103
|
-
},
|
|
1104
1084
|
app: {
|
|
1105
1085
|
startConversation: "대화 시작",
|
|
1106
1086
|
thinking: "생각 중…",
|
package/src/lang/pt-BR.ts
CHANGED
|
@@ -1085,26 +1085,6 @@ const ptBRMessages = {
|
|
|
1085
1085
|
pluginSkill: {
|
|
1086
1086
|
noDescription: "(sem descrição)",
|
|
1087
1087
|
},
|
|
1088
|
-
pluginSpreadsheet: {
|
|
1089
|
-
previewUntitled: "Planilha",
|
|
1090
|
-
previewSheets: "{count} aba | {count} abas",
|
|
1091
|
-
untitled: "Planilha",
|
|
1092
|
-
excel: "Excel",
|
|
1093
|
-
valuePlaceholder: "Valor",
|
|
1094
|
-
valueOrFormulaPlaceholder: "Valor ou Fórmula (ex.: 100 ou SUM(B2:B11))",
|
|
1095
|
-
formatPlaceholder: "Formato (ex.: $#,##0.00)",
|
|
1096
|
-
loading: "Carregando planilha...",
|
|
1097
|
-
noData: "Nenhum dado de planilha disponível",
|
|
1098
|
-
editData: "Editar dados da planilha",
|
|
1099
|
-
applyChanges: "Aplicar alterações",
|
|
1100
|
-
dataMustBeArray: "Os dados devem ser uma matriz de planilhas",
|
|
1101
|
-
loadFailed: "Falha ao carregar a planilha: {error}",
|
|
1102
|
-
invalidJsonAlert: "Formato JSON inválido: {error}",
|
|
1103
|
-
unknownError: "Erro desconhecido",
|
|
1104
|
-
update: "Atualizar",
|
|
1105
|
-
stringType: "Texto",
|
|
1106
|
-
formulaType: "Fórmula",
|
|
1107
|
-
},
|
|
1108
1088
|
app: {
|
|
1109
1089
|
startConversation: "Iniciar uma conversa",
|
|
1110
1090
|
thinking: "Pensando…",
|
package/src/lang/zh.ts
CHANGED
|
@@ -1065,26 +1065,6 @@ const zhMessages = {
|
|
|
1065
1065
|
pluginSkill: {
|
|
1066
1066
|
noDescription: "(无描述)",
|
|
1067
1067
|
},
|
|
1068
|
-
pluginSpreadsheet: {
|
|
1069
|
-
previewUntitled: "电子表格",
|
|
1070
|
-
previewSheets: "{count} 个工作表",
|
|
1071
|
-
untitled: "电子表格",
|
|
1072
|
-
excel: "Excel",
|
|
1073
|
-
valuePlaceholder: "值",
|
|
1074
|
-
valueOrFormulaPlaceholder: "值或公式(例如 100 或 SUM(B2:B11))",
|
|
1075
|
-
formatPlaceholder: "格式(例如 $#,##0.00)",
|
|
1076
|
-
loading: "正在加载电子表格...",
|
|
1077
|
-
noData: "没有可用的电子表格数据",
|
|
1078
|
-
editData: "编辑电子表格数据",
|
|
1079
|
-
applyChanges: "应用更改",
|
|
1080
|
-
dataMustBeArray: "数据必须是工作表数组",
|
|
1081
|
-
loadFailed: "加载电子表格失败: {error}",
|
|
1082
|
-
invalidJsonAlert: "无效的 JSON 格式: {error}",
|
|
1083
|
-
unknownError: "未知错误",
|
|
1084
|
-
update: "更新",
|
|
1085
|
-
stringType: "字符串",
|
|
1086
|
-
formulaType: "公式",
|
|
1087
|
-
},
|
|
1088
1068
|
app: {
|
|
1089
1069
|
startConversation: "开始对话",
|
|
1090
1070
|
thinking: "思考中…",
|
|
@@ -1,137 +1,19 @@
|
|
|
1
|
+
import { TOOL_DEFINITION as PACKAGE_TOOL_DEFINITION } from "@gui-chat-plugin/spreadsheet";
|
|
1
2
|
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
3
|
import { META } from "./meta";
|
|
3
4
|
import type { ResolvedRoute } from "../meta-types";
|
|
4
|
-
import { errorMessage } from "../../utils/errors";
|
|
5
5
|
|
|
6
|
+
// presentSpreadsheet's schema, validation, engine and View live in
|
|
7
|
+
// @gui-chat-plugin/spreadsheet (also consumed by MulmoTerminal). This built-in
|
|
8
|
+
// keeps MulmoClaude's routing META and the prompt, which points the agent at
|
|
9
|
+
// the workspace help file for cell formats and functions.
|
|
6
10
|
export const TOOL_NAME = META.toolName;
|
|
7
|
-
export type SpreadsheetEndpoints = { readonly [K in keyof typeof META.apiRoutes]: ResolvedRoute };
|
|
8
|
-
|
|
9
|
-
/** Mirrors the engine's `StoredCellValue`. Declaring it narrower than what the
|
|
10
|
-
* engine stores and renders is what forced the View to cast its decoded
|
|
11
|
-
* sheets back to this type. */
|
|
12
|
-
export type SpreadsheetCellValue = string | number | boolean;
|
|
13
|
-
|
|
14
|
-
export interface SpreadsheetCell {
|
|
15
|
-
v: SpreadsheetCellValue;
|
|
16
|
-
f?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** A cell as a stored workbook actually holds it. The engine's `normalizeData`
|
|
20
|
-
* folds all four forms, and models and hand edits write all four, so a
|
|
21
|
-
* workbook read back off disk cannot promise `{ v }` for every cell. The tool
|
|
22
|
-
* SCHEMA is deliberately stricter — it says what a caller should emit, not
|
|
23
|
-
* what an existing file may contain. */
|
|
24
|
-
export type StorableCell = SpreadsheetCell | { v?: null; f?: string } | SpreadsheetCellValue | null;
|
|
25
11
|
|
|
26
|
-
export
|
|
27
|
-
name: string;
|
|
28
|
-
data: StorableCell[][];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface SpreadsheetToolData {
|
|
32
|
-
sheets: SpreadsheetSheet[] | string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface SpreadsheetArgs {
|
|
36
|
-
title: string;
|
|
37
|
-
sheets: SpreadsheetSheet[];
|
|
38
|
-
}
|
|
12
|
+
export type SpreadsheetEndpoints = { readonly [K in keyof typeof META.apiRoutes]: ResolvedRoute };
|
|
39
13
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
name: TOOL_NAME,
|
|
43
|
-
description: "Display an Excel-like spreadsheet with formulas and calculations.",
|
|
14
|
+
export const TOOL_DEFINITION: ToolDefinition = {
|
|
15
|
+
...PACKAGE_TOOL_DEFINITION,
|
|
44
16
|
prompt: `Use ${TOOL_NAME} when the user asks for a spreadsheet, table with calculations, or what-if analysis. Use formulas and cell references instead of pre-calculated values so the spreadsheet stays interactive. For cell format details and available functions, read \`config/helps/spreadsheet.md\` in the workspace.`,
|
|
45
|
-
parameters: {
|
|
46
|
-
type: "object",
|
|
47
|
-
properties: {
|
|
48
|
-
title: {
|
|
49
|
-
type: "string",
|
|
50
|
-
description: "Title for the spreadsheet",
|
|
51
|
-
},
|
|
52
|
-
sheets: {
|
|
53
|
-
type: "array",
|
|
54
|
-
description: "Sheets to render as spreadsheet tabs. Each sheet includes a name and 2D array of cells (rows x columns).",
|
|
55
|
-
items: {
|
|
56
|
-
type: "object",
|
|
57
|
-
properties: {
|
|
58
|
-
name: {
|
|
59
|
-
type: "string",
|
|
60
|
-
description: "Sheet name (e.g., 'Sales Q1', 'Summary')",
|
|
61
|
-
},
|
|
62
|
-
data: {
|
|
63
|
-
type: "array",
|
|
64
|
-
description:
|
|
65
|
-
'Rows of cells. Each cell is an object with \'v\' (value) and \'f\' (format). Use Excel-style A1 notation in formulas: columns are letters (A, B, C...), rows are 1-based numbers (1, 2, 3...). Values can be text, numbers, dates, or formulas. Examples: [{"v": "Product"}, {"v": 2024, "f": "#,##0"}, {"v": "01/15/2025", "f": "MM/DD/YYYY"}, {"v": "=B2*1.05", "f": "$#,##0.00"}]. Format codes: \'$#,##0.00\' (currency), \'#,##0\' (integer), \'0.00%\' (percent), \'0.00\' (decimal), \'MM/DD/YYYY\' (date), \'DD-MMM-YYYY\' (date), \'YYYY-MM-DD\' (ISO date).',
|
|
66
|
-
items: {
|
|
67
|
-
type: "array",
|
|
68
|
-
description: "Row of cells. Each cell is an object with value and format.",
|
|
69
|
-
items: {
|
|
70
|
-
type: "object",
|
|
71
|
-
description: "Cell object with value and optional format. If value is a string starting with '=', it's treated as a formula.",
|
|
72
|
-
properties: {
|
|
73
|
-
v: {
|
|
74
|
-
oneOf: [{ type: "string" }, { type: "number" }, { type: "boolean" }],
|
|
75
|
-
description:
|
|
76
|
-
"Cell value. Can be text, number, boolean, date, or formula (string starting with '='). Examples: 'Revenue', 1500000, true, '01/15/2025', '=SUM(A1:A10)', '=B2-TODAY()'. Date strings like '01/15/2025' are automatically parsed to date serial numbers.",
|
|
77
|
-
},
|
|
78
|
-
f: {
|
|
79
|
-
type: "string",
|
|
80
|
-
description:
|
|
81
|
-
"Optional format code for displaying the value. Common formats: '$#,##0.00' (currency), '#,##0' (integer), '0.00%' (percent), '0.00' (decimal), 'MM/DD/YYYY' (date), 'DD-MMM-YYYY' (date), 'YYYY-MM-DD' (ISO date)",
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
required: ["v"],
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
required: ["name", "data"],
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
required: ["title", "sheets"],
|
|
94
|
-
},
|
|
95
17
|
};
|
|
96
18
|
|
|
97
|
-
export default
|
|
98
|
-
|
|
99
|
-
export const executeSpreadsheet = async (
|
|
100
|
-
args: SpreadsheetArgs,
|
|
101
|
-
): Promise<{
|
|
102
|
-
message: string;
|
|
103
|
-
title: string;
|
|
104
|
-
data: SpreadsheetToolData;
|
|
105
|
-
instructions: string;
|
|
106
|
-
}> => {
|
|
107
|
-
const { title } = args;
|
|
108
|
-
let { sheets } = args;
|
|
109
|
-
|
|
110
|
-
// Handle case where LLM accidentally stringifies the sheets array
|
|
111
|
-
if (typeof sheets === "string") {
|
|
112
|
-
try {
|
|
113
|
-
sheets = JSON.parse(sheets);
|
|
114
|
-
} catch (error) {
|
|
115
|
-
throw new Error(`Invalid sheets format: sheets must be an array, not a string. Parse error: ${errorMessage(error)}`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Validate that sheets are provided
|
|
120
|
-
if (!Array.isArray(sheets) || sheets.length === 0) {
|
|
121
|
-
throw new Error("At least one sheet is required. Sheets must be an array of sheet objects.");
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Validate each sheet has data
|
|
125
|
-
for (const sheet of sheets) {
|
|
126
|
-
if (!sheet.name || !sheet.data || sheet.data.length === 0) {
|
|
127
|
-
throw new Error(`Invalid sheet: ${sheet.name || "unnamed"}. Each sheet must have a name and data array.`);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
message: `Created spreadsheet: ${title}`,
|
|
133
|
-
title,
|
|
134
|
-
data: { sheets },
|
|
135
|
-
instructions: "Acknowledge that the spreadsheet has been created and is displayed to the user.",
|
|
136
|
-
};
|
|
137
|
-
};
|
|
19
|
+
export default TOOL_DEFINITION;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { PluginRegistration, ToolPlugin } from "../../tools/types";
|
|
2
|
-
import
|
|
2
|
+
import { View, Preview, type SpreadsheetToolData } from "@gui-chat-plugin/spreadsheet/vue";
|
|
3
|
+
// Vite lib mode does not inject the package's stylesheet; the consumer imports it.
|
|
4
|
+
import "@gui-chat-plugin/spreadsheet/style.css";
|
|
5
|
+
import { TOOL_DEFINITION, TOOL_NAME, type SpreadsheetEndpoints } from "./definition";
|
|
3
6
|
import { makeRouteExecute } from "../execute";
|
|
4
7
|
import { wrapWithScope } from "../scope";
|
|
5
|
-
import View from "./View.vue";
|
|
6
|
-
import Preview from "./Preview.vue";
|
|
7
8
|
|
|
9
|
+
// wrapWithScope provides the runtime the package View needs: dispatch for
|
|
10
|
+
// loading and saving the workbook, and the host locale for its strings.
|
|
8
11
|
const spreadsheetPlugin: ToolPlugin<SpreadsheetToolData> = {
|
|
9
|
-
toolDefinition,
|
|
12
|
+
toolDefinition: TOOL_DEFINITION,
|
|
10
13
|
|
|
11
14
|
execute: makeRouteExecute<SpreadsheetEndpoints, SpreadsheetToolData>("spreadsheet", "create", TOOL_NAME),
|
|
12
15
|
|
|
@@ -4,11 +4,9 @@ export const META = definePluginMeta({
|
|
|
4
4
|
toolName: "presentSpreadsheet",
|
|
5
5
|
apiNamespace: "spreadsheet",
|
|
6
6
|
apiRoutes: {
|
|
7
|
-
/** POST /api/spreadsheet — create a new spreadsheet.
|
|
7
|
+
/** POST /api/spreadsheet — create a new spreadsheet. Cell edits are
|
|
8
|
+
* saved through the View's dispatch (server/plugins/spreadsheet-builtin.ts). */
|
|
8
9
|
create: { method: "POST", path: "" },
|
|
9
|
-
/** PUT /api/spreadsheet/update — overwrite an existing
|
|
10
|
-
* spreadsheet. Body carries the workspace-relative path. */
|
|
11
|
-
update: { method: "PUT", path: "/update" },
|
|
12
10
|
},
|
|
13
11
|
mcpDispatch: "create",
|
|
14
12
|
});
|