gogcli-mcp-slides 2.0.4 → 2.0.7
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +65 -59
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/tools/slides-extra.ts +110 -2
- package/tests/tools/slides-extra.test.ts +216 -6
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "Extended Google Slides for Claude via gogcli — auth + full Slides support (create, edit, export, templates, markdown)",
|
|
10
|
-
"version": "2.0.
|
|
10
|
+
"version": "2.0.7"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "gogcli (Slides)",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "Extended Google Slides for Claude via gogcli — auth + full Slides support (create, edit, export, templates, markdown)",
|
|
18
|
-
"version": "2.0.
|
|
18
|
+
"version": "2.0.7",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-slides",
|
|
3
3
|
"displayName": "gogcli (Slides)",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.7",
|
|
5
5
|
"description": "Extended Google Slides for Claude via gogcli — auth + full Slides support (create, edit, export, templates, markdown)",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/dist/index.js
CHANGED
|
@@ -30864,6 +30864,11 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
30864
30864
|
// ../gogcli-mcp/src/runner.ts
|
|
30865
30865
|
import { spawn } from "node:child_process";
|
|
30866
30866
|
var TIMEOUT_MS = 3e4;
|
|
30867
|
+
function envOrUndefined(key) {
|
|
30868
|
+
const value = process.env[key];
|
|
30869
|
+
if (!value || value.startsWith("${")) return void 0;
|
|
30870
|
+
return value;
|
|
30871
|
+
}
|
|
30867
30872
|
function formatTimeout(ms) {
|
|
30868
30873
|
const seconds = Math.round(ms / 1e3);
|
|
30869
30874
|
if (seconds >= 60) {
|
|
@@ -30874,7 +30879,7 @@ function formatTimeout(ms) {
|
|
|
30874
30879
|
}
|
|
30875
30880
|
async function run(args, options = {}) {
|
|
30876
30881
|
const { account, spawner = spawn, interactive = false, timeout } = options;
|
|
30877
|
-
const effectiveAccount = account ??
|
|
30882
|
+
const effectiveAccount = account ?? envOrUndefined("GOG_ACCOUNT");
|
|
30878
30883
|
const fullArgs = ["--json", "--color=never"];
|
|
30879
30884
|
if (!interactive) {
|
|
30880
30885
|
fullArgs.push("--no-input");
|
|
@@ -30886,7 +30891,7 @@ async function run(args, options = {}) {
|
|
|
30886
30891
|
const effectiveTimeout = timeout ?? TIMEOUT_MS;
|
|
30887
30892
|
return new Promise((resolve, reject) => {
|
|
30888
30893
|
const { GOG_ACCESS_TOKEN: _, ...cleanEnv } = process.env;
|
|
30889
|
-
const child = spawner(
|
|
30894
|
+
const child = spawner(envOrUndefined("GOG_PATH") ?? "gog", fullArgs, { env: cleanEnv });
|
|
30890
30895
|
const stdoutChunks = [];
|
|
30891
30896
|
const stderrChunks = [];
|
|
30892
30897
|
let settled = false;
|
|
@@ -31068,6 +31073,64 @@ function registerSlidesTools(server2) {
|
|
|
31068
31073
|
if (template) args.push(`--template=${template}`);
|
|
31069
31074
|
return runOrDiagnose(args, { account });
|
|
31070
31075
|
});
|
|
31076
|
+
server2.registerTool("gog_slides_copy", {
|
|
31077
|
+
description: "Copy a Google Slides presentation to a new presentation with the given title.",
|
|
31078
|
+
inputSchema: {
|
|
31079
|
+
presentationId: external_exports.string().describe("Presentation ID to copy"),
|
|
31080
|
+
title: external_exports.string().describe("Title for the new copy"),
|
|
31081
|
+
parent: external_exports.string().optional().describe("Destination folder ID"),
|
|
31082
|
+
account: accountParam
|
|
31083
|
+
}
|
|
31084
|
+
}, async ({ presentationId, title, parent, account }) => {
|
|
31085
|
+
const args = ["slides", "copy", presentationId, title];
|
|
31086
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
31087
|
+
return runOrDiagnose(args, { account });
|
|
31088
|
+
});
|
|
31089
|
+
server2.registerTool("gog_slides_list_slides", {
|
|
31090
|
+
description: "List slides in a Google Slides presentation.",
|
|
31091
|
+
annotations: { readOnlyHint: true },
|
|
31092
|
+
inputSchema: {
|
|
31093
|
+
presentationId: external_exports.string().describe("Presentation ID"),
|
|
31094
|
+
account: accountParam
|
|
31095
|
+
}
|
|
31096
|
+
}, async ({ presentationId, account }) => {
|
|
31097
|
+
return runOrDiagnose(["slides", "list-slides", presentationId], { account });
|
|
31098
|
+
});
|
|
31099
|
+
server2.registerTool("gog_slides_read_slide", {
|
|
31100
|
+
description: "Read the content of a slide (text, shapes, speaker notes).",
|
|
31101
|
+
annotations: { readOnlyHint: true },
|
|
31102
|
+
inputSchema: {
|
|
31103
|
+
presentationId: external_exports.string().describe("Presentation ID"),
|
|
31104
|
+
slideId: external_exports.string().describe("Slide ID to read"),
|
|
31105
|
+
account: accountParam
|
|
31106
|
+
}
|
|
31107
|
+
}, async ({ presentationId, slideId, account }) => {
|
|
31108
|
+
return runOrDiagnose(["slides", "read-slide", presentationId, slideId], { account });
|
|
31109
|
+
});
|
|
31110
|
+
server2.registerTool("gog_slides_run", {
|
|
31111
|
+
description: "Run any gog slides subcommand not covered by the other tools. Run `gog slides --help` for the full list of subcommands, or `gog slides <subcommand> --help` for flags on a specific subcommand.",
|
|
31112
|
+
annotations: { destructiveHint: true },
|
|
31113
|
+
inputSchema: {
|
|
31114
|
+
subcommand: external_exports.string().describe("The gog slides subcommand to run"),
|
|
31115
|
+
args: external_exports.array(external_exports.string()).describe("Additional positional args and flags"),
|
|
31116
|
+
account: accountParam
|
|
31117
|
+
}
|
|
31118
|
+
}, async ({ subcommand, args, account }) => {
|
|
31119
|
+
return runOrDiagnose(["slides", subcommand, ...args], { account });
|
|
31120
|
+
});
|
|
31121
|
+
}
|
|
31122
|
+
|
|
31123
|
+
// ../gogcli-mcp/src/server.ts
|
|
31124
|
+
var VERSION = true ? "2.0.7" : "0.0.0";
|
|
31125
|
+
function createServer(options) {
|
|
31126
|
+
return new McpServer({
|
|
31127
|
+
name: options?.name ?? "gogcli",
|
|
31128
|
+
version: options?.version ?? VERSION
|
|
31129
|
+
});
|
|
31130
|
+
}
|
|
31131
|
+
|
|
31132
|
+
// src/tools/slides-extra.ts
|
|
31133
|
+
function registerExtraSlidesTools(server2) {
|
|
31071
31134
|
server2.registerTool("gog_slides_create_from_markdown", {
|
|
31072
31135
|
description: "Create a new Google Slides presentation from markdown content (inline or from a file).",
|
|
31073
31136
|
inputSchema: {
|
|
@@ -31109,19 +31172,6 @@ function registerSlidesTools(server2) {
|
|
|
31109
31172
|
if (exact) args.push("--exact");
|
|
31110
31173
|
return runOrDiagnose(args, { account });
|
|
31111
31174
|
});
|
|
31112
|
-
server2.registerTool("gog_slides_copy", {
|
|
31113
|
-
description: "Copy a Google Slides presentation to a new presentation with the given title.",
|
|
31114
|
-
inputSchema: {
|
|
31115
|
-
presentationId: external_exports.string().describe("Presentation ID to copy"),
|
|
31116
|
-
title: external_exports.string().describe("Title for the new copy"),
|
|
31117
|
-
parent: external_exports.string().optional().describe("Destination folder ID"),
|
|
31118
|
-
account: accountParam
|
|
31119
|
-
}
|
|
31120
|
-
}, async ({ presentationId, title, parent, account }) => {
|
|
31121
|
-
const args = ["slides", "copy", presentationId, title];
|
|
31122
|
-
if (parent) args.push(`--parent=${parent}`);
|
|
31123
|
-
return runOrDiagnose(args, { account });
|
|
31124
|
-
});
|
|
31125
31175
|
server2.registerTool("gog_slides_add_slide", {
|
|
31126
31176
|
description: "Add a new slide to a presentation from a local image, with optional speaker notes.",
|
|
31127
31177
|
inputSchema: {
|
|
@@ -31139,16 +31189,6 @@ function registerSlidesTools(server2) {
|
|
|
31139
31189
|
if (before) args.push(`--before=${before}`);
|
|
31140
31190
|
return runOrDiagnose(args, { account });
|
|
31141
31191
|
});
|
|
31142
|
-
server2.registerTool("gog_slides_list_slides", {
|
|
31143
|
-
description: "List slides in a Google Slides presentation.",
|
|
31144
|
-
annotations: { readOnlyHint: true },
|
|
31145
|
-
inputSchema: {
|
|
31146
|
-
presentationId: external_exports.string().describe("Presentation ID"),
|
|
31147
|
-
account: accountParam
|
|
31148
|
-
}
|
|
31149
|
-
}, async ({ presentationId, account }) => {
|
|
31150
|
-
return runOrDiagnose(["slides", "list-slides", presentationId], { account });
|
|
31151
|
-
});
|
|
31152
31192
|
server2.registerTool("gog_slides_delete_slide", {
|
|
31153
31193
|
description: "Delete a slide from a Google Slides presentation.",
|
|
31154
31194
|
annotations: { destructiveHint: true },
|
|
@@ -31160,17 +31200,6 @@ function registerSlidesTools(server2) {
|
|
|
31160
31200
|
}, async ({ presentationId, slideId, account }) => {
|
|
31161
31201
|
return runOrDiagnose(["slides", "delete-slide", presentationId, slideId], { account });
|
|
31162
31202
|
});
|
|
31163
|
-
server2.registerTool("gog_slides_read_slide", {
|
|
31164
|
-
description: "Read the content of a slide (text, shapes, speaker notes).",
|
|
31165
|
-
annotations: { readOnlyHint: true },
|
|
31166
|
-
inputSchema: {
|
|
31167
|
-
presentationId: external_exports.string().describe("Presentation ID"),
|
|
31168
|
-
slideId: external_exports.string().describe("Slide ID to read"),
|
|
31169
|
-
account: accountParam
|
|
31170
|
-
}
|
|
31171
|
-
}, async ({ presentationId, slideId, account }) => {
|
|
31172
|
-
return runOrDiagnose(["slides", "read-slide", presentationId, slideId], { account });
|
|
31173
|
-
});
|
|
31174
31203
|
server2.registerTool("gog_slides_update_notes", {
|
|
31175
31204
|
description: "Update the speaker notes on a slide (inline text or from a file).",
|
|
31176
31205
|
annotations: { destructiveHint: true },
|
|
@@ -31204,29 +31233,6 @@ function registerSlidesTools(server2) {
|
|
|
31204
31233
|
if (notesFile) args.push(`--notes-file=${notesFile}`);
|
|
31205
31234
|
return runOrDiagnose(args, { account });
|
|
31206
31235
|
});
|
|
31207
|
-
server2.registerTool("gog_slides_run", {
|
|
31208
|
-
description: "Run any gog slides subcommand not covered by the other tools. Run `gog slides --help` for the full list of subcommands, or `gog slides <subcommand> --help` for flags on a specific subcommand.",
|
|
31209
|
-
inputSchema: {
|
|
31210
|
-
subcommand: external_exports.string().describe("The gog slides subcommand to run"),
|
|
31211
|
-
args: external_exports.array(external_exports.string()).describe("Additional positional args and flags"),
|
|
31212
|
-
account: accountParam
|
|
31213
|
-
}
|
|
31214
|
-
}, async ({ subcommand, args, account }) => {
|
|
31215
|
-
return runOrDiagnose(["slides", subcommand, ...args], { account });
|
|
31216
|
-
});
|
|
31217
|
-
}
|
|
31218
|
-
|
|
31219
|
-
// ../gogcli-mcp/src/server.ts
|
|
31220
|
-
var VERSION = true ? "2.0.4" : "0.0.0";
|
|
31221
|
-
function createServer(options) {
|
|
31222
|
-
return new McpServer({
|
|
31223
|
-
name: options?.name ?? "gogcli",
|
|
31224
|
-
version: options?.version ?? VERSION
|
|
31225
|
-
});
|
|
31226
|
-
}
|
|
31227
|
-
|
|
31228
|
-
// src/tools/slides-extra.ts
|
|
31229
|
-
function registerExtraSlidesTools(_server) {
|
|
31230
31236
|
}
|
|
31231
31237
|
|
|
31232
31238
|
// src/index.ts
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"manifest_version": "0.3",
|
|
4
4
|
"name": "gogcli-mcp-slides",
|
|
5
5
|
"display_name": "gogcli (Slides)",
|
|
6
|
-
"version": "2.0.
|
|
6
|
+
"version": "2.0.7",
|
|
7
7
|
"description": "Extended Google Slides for Claude via gogcli — auth + full Slides support (create, edit, export, templates, markdown)",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-slides",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"mcpName": "io.github.chrischall/gogcli-mcp-slides",
|
|
5
5
|
"description": "Extended Google Slides MCP server via gogcli — auth + full Slides support",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/gogcli-mcp-slides"
|
|
9
9
|
},
|
|
10
|
-
"version": "2.0.
|
|
10
|
+
"version": "2.0.7",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-slides",
|
|
15
|
-
"version": "2.0.
|
|
15
|
+
"version": "2.0.7",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
|
@@ -1,5 +1,113 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { accountParam, runOrDiagnose } from '../../../gogcli-mcp/src/lib.js';
|
|
2
4
|
|
|
3
|
-
export function registerExtraSlidesTools(
|
|
4
|
-
|
|
5
|
+
export function registerExtraSlidesTools(server: McpServer): void {
|
|
6
|
+
server.registerTool('gog_slides_create_from_markdown', {
|
|
7
|
+
description: 'Create a new Google Slides presentation from markdown content (inline or from a file).',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
title: z.string().describe('Presentation title'),
|
|
10
|
+
content: z.string().optional().describe('Inline markdown content'),
|
|
11
|
+
contentFile: z.string().optional().describe('Path to a markdown file'),
|
|
12
|
+
parent: z.string().optional().describe('Destination folder ID'),
|
|
13
|
+
debug: z.boolean().optional().describe('Enable debug output'),
|
|
14
|
+
account: accountParam,
|
|
15
|
+
},
|
|
16
|
+
}, async ({ title, content, contentFile, parent, debug, account }) => {
|
|
17
|
+
const args = ['slides', 'create-from-markdown', title];
|
|
18
|
+
if (content) args.push(`--content=${content}`);
|
|
19
|
+
if (contentFile) args.push(`--content-file=${contentFile}`);
|
|
20
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
21
|
+
if (debug) args.push('--debug');
|
|
22
|
+
return runOrDiagnose(args, { account });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
server.registerTool('gog_slides_create_from_template', {
|
|
26
|
+
description: 'Create a new Google Slides presentation from a template, with optional placeholder replacements.',
|
|
27
|
+
inputSchema: {
|
|
28
|
+
templateId: z.string().describe('Template presentation ID'),
|
|
29
|
+
title: z.string().describe('New presentation title'),
|
|
30
|
+
replacements: z.record(z.string(), z.string()).optional().describe('Placeholder replacements as a key/value object (emitted as --replace=k=v for each entry)'),
|
|
31
|
+
replacementsFile: z.string().optional().describe('Path to a JSON file containing replacements'),
|
|
32
|
+
parent: z.string().optional().describe('Destination folder ID'),
|
|
33
|
+
exact: z.boolean().optional().describe('Require exact placeholder matches'),
|
|
34
|
+
account: accountParam,
|
|
35
|
+
},
|
|
36
|
+
}, async ({ templateId, title, replacements, replacementsFile, parent, exact, account }) => {
|
|
37
|
+
const args = ['slides', 'create-from-template', templateId, title];
|
|
38
|
+
if (replacements) {
|
|
39
|
+
for (const [k, v] of Object.entries(replacements)) {
|
|
40
|
+
args.push(`--replace=${k}=${v}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (replacementsFile) args.push(`--replacements=${replacementsFile}`);
|
|
44
|
+
if (parent) args.push(`--parent=${parent}`);
|
|
45
|
+
if (exact) args.push('--exact');
|
|
46
|
+
return runOrDiagnose(args, { account });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
server.registerTool('gog_slides_add_slide', {
|
|
50
|
+
description: 'Add a new slide to a presentation from a local image, with optional speaker notes.',
|
|
51
|
+
inputSchema: {
|
|
52
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
53
|
+
image: z.string().describe('Path to the local image file'),
|
|
54
|
+
notes: z.string().optional().describe('Speaker notes text'),
|
|
55
|
+
notesFile: z.string().optional().describe('Path to a file containing speaker notes'),
|
|
56
|
+
before: z.string().optional().describe('Insert before this slide ID (default: append at end)'),
|
|
57
|
+
account: accountParam,
|
|
58
|
+
},
|
|
59
|
+
}, async ({ presentationId, image, notes, notesFile, before, account }) => {
|
|
60
|
+
const args = ['slides', 'add-slide', presentationId, image];
|
|
61
|
+
if (notes) args.push(`--notes=${notes}`);
|
|
62
|
+
if (notesFile) args.push(`--notes-file=${notesFile}`);
|
|
63
|
+
if (before) args.push(`--before=${before}`);
|
|
64
|
+
return runOrDiagnose(args, { account });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
server.registerTool('gog_slides_delete_slide', {
|
|
68
|
+
description: 'Delete a slide from a Google Slides presentation.',
|
|
69
|
+
annotations: { destructiveHint: true },
|
|
70
|
+
inputSchema: {
|
|
71
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
72
|
+
slideId: z.string().describe('Slide ID to delete'),
|
|
73
|
+
account: accountParam,
|
|
74
|
+
},
|
|
75
|
+
}, async ({ presentationId, slideId, account }) => {
|
|
76
|
+
return runOrDiagnose(['slides', 'delete-slide', presentationId, slideId], { account });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
server.registerTool('gog_slides_update_notes', {
|
|
80
|
+
description: 'Update the speaker notes on a slide (inline text or from a file).',
|
|
81
|
+
annotations: { destructiveHint: true },
|
|
82
|
+
inputSchema: {
|
|
83
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
84
|
+
slideId: z.string().describe('Slide ID'),
|
|
85
|
+
notes: z.string().optional().describe('New speaker notes text'),
|
|
86
|
+
notesFile: z.string().optional().describe('Path to a file containing new speaker notes'),
|
|
87
|
+
account: accountParam,
|
|
88
|
+
},
|
|
89
|
+
}, async ({ presentationId, slideId, notes, notesFile, account }) => {
|
|
90
|
+
const args = ['slides', 'update-notes', presentationId, slideId];
|
|
91
|
+
if (notes) args.push(`--notes=${notes}`);
|
|
92
|
+
if (notesFile) args.push(`--notes-file=${notesFile}`);
|
|
93
|
+
return runOrDiagnose(args, { account });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
server.registerTool('gog_slides_replace_slide', {
|
|
97
|
+
description: 'Replace the image content of an existing slide, with optional speaker notes.',
|
|
98
|
+
annotations: { destructiveHint: true },
|
|
99
|
+
inputSchema: {
|
|
100
|
+
presentationId: z.string().describe('Presentation ID'),
|
|
101
|
+
slideId: z.string().describe('Slide ID to replace'),
|
|
102
|
+
image: z.string().describe('Path to the new local image file'),
|
|
103
|
+
notes: z.string().optional().describe('Speaker notes text'),
|
|
104
|
+
notesFile: z.string().optional().describe('Path to a file containing speaker notes'),
|
|
105
|
+
account: accountParam,
|
|
106
|
+
},
|
|
107
|
+
}, async ({ presentationId, slideId, image, notes, notesFile, account }) => {
|
|
108
|
+
const args = ['slides', 'replace-slide', presentationId, slideId, image];
|
|
109
|
+
if (notes) args.push(`--notes=${notes}`);
|
|
110
|
+
if (notesFile) args.push(`--notes-file=${notesFile}`);
|
|
111
|
+
return runOrDiagnose(args, { account });
|
|
112
|
+
});
|
|
5
113
|
}
|
|
@@ -1,10 +1,220 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
2
|
import { registerExtraSlidesTools } from '../../src/tools/slides-extra.js';
|
|
3
|
+
import * as lib from '../../../gogcli-mcp/src/lib.js';
|
|
4
|
+
import { setupExtrasHandlers, toText, type ToolHandler } from '../../../gogcli-mcp/tests/helpers/extras-harness.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
vi.mock('../../../gogcli-mcp/src/lib.js', async (importOriginal) => {
|
|
7
|
+
const actual = await importOriginal<typeof lib>();
|
|
8
|
+
return {
|
|
9
|
+
...actual,
|
|
10
|
+
runOrDiagnose: vi.fn(),
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
let handlers: Map<string, ToolHandler>;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
19
|
+
handlers = setupExtrasHandlers(registerExtraSlidesTools);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('gog_slides_create_from_markdown', () => {
|
|
23
|
+
it('calls runOrDiagnose with title only', async () => {
|
|
24
|
+
await handlers.get('gog_slides_create_from_markdown')!({ title: 'Deck' });
|
|
25
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
26
|
+
['slides', 'create-from-markdown', 'Deck'],
|
|
27
|
+
{ account: undefined },
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('passes all optional flags', async () => {
|
|
32
|
+
await handlers.get('gog_slides_create_from_markdown')!({
|
|
33
|
+
title: 'Deck',
|
|
34
|
+
content: '# Slide 1',
|
|
35
|
+
contentFile: '/tmp/deck.md',
|
|
36
|
+
parent: 'folder1',
|
|
37
|
+
debug: true,
|
|
38
|
+
});
|
|
39
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
40
|
+
[
|
|
41
|
+
'slides', 'create-from-markdown', 'Deck',
|
|
42
|
+
'--content=# Slide 1',
|
|
43
|
+
'--content-file=/tmp/deck.md',
|
|
44
|
+
'--parent=folder1',
|
|
45
|
+
'--debug',
|
|
46
|
+
],
|
|
47
|
+
{ account: undefined },
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('omits --debug when false', async () => {
|
|
52
|
+
await handlers.get('gog_slides_create_from_markdown')!({ title: 'Deck', debug: false });
|
|
53
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
54
|
+
['slides', 'create-from-markdown', 'Deck'],
|
|
55
|
+
{ account: undefined },
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('gog_slides_create_from_template', () => {
|
|
61
|
+
it('calls runOrDiagnose with templateId and title only', async () => {
|
|
62
|
+
await handlers.get('gog_slides_create_from_template')!({ templateId: 'tpl1', title: 'Deck' });
|
|
63
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
64
|
+
['slides', 'create-from-template', 'tpl1', 'Deck'],
|
|
65
|
+
{ account: undefined },
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('passes --replace for a single replacement entry', async () => {
|
|
70
|
+
await handlers.get('gog_slides_create_from_template')!({
|
|
71
|
+
templateId: 'tpl1',
|
|
72
|
+
title: 'Deck',
|
|
73
|
+
replacements: { name: 'Alice' },
|
|
74
|
+
});
|
|
75
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
76
|
+
['slides', 'create-from-template', 'tpl1', 'Deck', '--replace=name=Alice'],
|
|
77
|
+
{ account: undefined },
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('passes --replace for each entry in replacements', async () => {
|
|
82
|
+
await handlers.get('gog_slides_create_from_template')!({
|
|
83
|
+
templateId: 'tpl1',
|
|
84
|
+
title: 'Deck',
|
|
85
|
+
replacements: { name: 'Alice', company: 'Acme' },
|
|
86
|
+
});
|
|
87
|
+
const call = vi.mocked(lib.runOrDiagnose).mock.calls[0]!;
|
|
88
|
+
expect(call[0]).toEqual(expect.arrayContaining(['--replace=name=Alice', '--replace=company=Acme']));
|
|
89
|
+
expect(call[1]).toEqual({ account: undefined });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('passes --replacements, --parent, and --exact', async () => {
|
|
93
|
+
await handlers.get('gog_slides_create_from_template')!({
|
|
94
|
+
templateId: 'tpl1',
|
|
95
|
+
title: 'Deck',
|
|
96
|
+
replacementsFile: '/tmp/r.json',
|
|
97
|
+
parent: 'folder1',
|
|
98
|
+
exact: true,
|
|
99
|
+
});
|
|
100
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
101
|
+
[
|
|
102
|
+
'slides', 'create-from-template', 'tpl1', 'Deck',
|
|
103
|
+
'--replacements=/tmp/r.json',
|
|
104
|
+
'--parent=folder1',
|
|
105
|
+
'--exact',
|
|
106
|
+
],
|
|
107
|
+
{ account: undefined },
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('omits --exact when false', async () => {
|
|
112
|
+
await handlers.get('gog_slides_create_from_template')!({
|
|
113
|
+
templateId: 'tpl1',
|
|
114
|
+
title: 'Deck',
|
|
115
|
+
exact: false,
|
|
116
|
+
});
|
|
117
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
118
|
+
['slides', 'create-from-template', 'tpl1', 'Deck'],
|
|
119
|
+
{ account: undefined },
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('gog_slides_add_slide', () => {
|
|
125
|
+
it('calls runOrDiagnose with presentationId and image', async () => {
|
|
126
|
+
await handlers.get('gog_slides_add_slide')!({ presentationId: 'p1', image: '/tmp/img.png' });
|
|
127
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
128
|
+
['slides', 'add-slide', 'p1', '/tmp/img.png'],
|
|
129
|
+
{ account: undefined },
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('passes --notes, --notes-file, and --before', async () => {
|
|
134
|
+
await handlers.get('gog_slides_add_slide')!({
|
|
135
|
+
presentationId: 'p1',
|
|
136
|
+
image: '/tmp/img.png',
|
|
137
|
+
notes: 'Speaker note',
|
|
138
|
+
notesFile: '/tmp/notes.txt',
|
|
139
|
+
before: 'slide5',
|
|
140
|
+
});
|
|
141
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
142
|
+
[
|
|
143
|
+
'slides', 'add-slide', 'p1', '/tmp/img.png',
|
|
144
|
+
'--notes=Speaker note',
|
|
145
|
+
'--notes-file=/tmp/notes.txt',
|
|
146
|
+
'--before=slide5',
|
|
147
|
+
],
|
|
148
|
+
{ account: undefined },
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('gog_slides_delete_slide', () => {
|
|
154
|
+
it('calls runOrDiagnose with presentationId and slideId', async () => {
|
|
155
|
+
await handlers.get('gog_slides_delete_slide')!({ presentationId: 'p1', slideId: 's1' });
|
|
156
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
157
|
+
['slides', 'delete-slide', 'p1', 's1'],
|
|
158
|
+
{ account: undefined },
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('gog_slides_update_notes', () => {
|
|
164
|
+
it('calls runOrDiagnose with presentationId and slideId', async () => {
|
|
165
|
+
await handlers.get('gog_slides_update_notes')!({ presentationId: 'p1', slideId: 's1' });
|
|
166
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
167
|
+
['slides', 'update-notes', 'p1', 's1'],
|
|
168
|
+
{ account: undefined },
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('passes --notes and --notes-file when provided', async () => {
|
|
173
|
+
await handlers.get('gog_slides_update_notes')!({
|
|
174
|
+
presentationId: 'p1',
|
|
175
|
+
slideId: 's1',
|
|
176
|
+
notes: 'speak clearly',
|
|
177
|
+
notesFile: '/tmp/n.txt',
|
|
178
|
+
});
|
|
179
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
180
|
+
[
|
|
181
|
+
'slides', 'update-notes', 'p1', 's1',
|
|
182
|
+
'--notes=speak clearly',
|
|
183
|
+
'--notes-file=/tmp/n.txt',
|
|
184
|
+
],
|
|
185
|
+
{ account: undefined },
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('gog_slides_replace_slide', () => {
|
|
191
|
+
it('calls runOrDiagnose with presentationId, slideId, and image', async () => {
|
|
192
|
+
await handlers.get('gog_slides_replace_slide')!({
|
|
193
|
+
presentationId: 'p1',
|
|
194
|
+
slideId: 's1',
|
|
195
|
+
image: '/tmp/img.png',
|
|
196
|
+
});
|
|
197
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
198
|
+
['slides', 'replace-slide', 'p1', 's1', '/tmp/img.png'],
|
|
199
|
+
{ account: undefined },
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('passes --notes and --notes-file when provided', async () => {
|
|
204
|
+
await handlers.get('gog_slides_replace_slide')!({
|
|
205
|
+
presentationId: 'p1',
|
|
206
|
+
slideId: 's1',
|
|
207
|
+
image: '/tmp/img.png',
|
|
208
|
+
notes: 'updated',
|
|
209
|
+
notesFile: '/tmp/n.txt',
|
|
210
|
+
});
|
|
211
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
212
|
+
[
|
|
213
|
+
'slides', 'replace-slide', 'p1', 's1', '/tmp/img.png',
|
|
214
|
+
'--notes=updated',
|
|
215
|
+
'--notes-file=/tmp/n.txt',
|
|
216
|
+
],
|
|
217
|
+
{ account: undefined },
|
|
218
|
+
);
|
|
9
219
|
});
|
|
10
220
|
});
|