gogcli-mcp-slides 4.2.4 → 4.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.
@@ -3,6 +3,7 @@ import { registerExtraSlidesTools } from '../../src/tools/slides-extra.js';
3
3
  import * as lib from '../../../gogcli-mcp/src/lib.js';
4
4
  import { createTestHarness, type TestHarness } from '@chrischall/mcp-utils/test';
5
5
  import { rawTextResult } from '@chrischall/mcp-utils';
6
+ import { pos } from '../../../gogcli-mcp/src/argv.js';
6
7
 
7
8
  vi.mock('../../../gogcli-mcp/src/lib.js', async (importOriginal) => {
8
9
  const actual = await importOriginal<typeof lib>();
@@ -24,7 +25,7 @@ describe('gog_slides_create_from_markdown', () => {
24
25
  it('calls runOrDiagnose with title only', async () => {
25
26
  await harness.callTool('gog_slides_create_from_markdown', { title: 'Deck' });
26
27
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
27
- ['slides', 'create-from-markdown', 'Deck'],
28
+ ['slides', 'create-from-markdown', pos('Deck')],
28
29
  { account: undefined },
29
30
  );
30
31
  });
@@ -39,7 +40,7 @@ describe('gog_slides_create_from_markdown', () => {
39
40
  });
40
41
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
41
42
  [
42
- 'slides', 'create-from-markdown', 'Deck',
43
+ 'slides', 'create-from-markdown', pos('Deck'),
43
44
  '--content=# Slide 1',
44
45
  '--content-file=/tmp/deck.md',
45
46
  '--parent=folder1',
@@ -52,7 +53,7 @@ describe('gog_slides_create_from_markdown', () => {
52
53
  it('omits --debug when false', async () => {
53
54
  await harness.callTool('gog_slides_create_from_markdown', { title: 'Deck', debug: false });
54
55
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
55
- ['slides', 'create-from-markdown', 'Deck'],
56
+ ['slides', 'create-from-markdown', pos('Deck')],
56
57
  { account: undefined },
57
58
  );
58
59
  });
@@ -62,7 +63,7 @@ describe('gog_slides_create_from_template', () => {
62
63
  it('calls runOrDiagnose with templateId and title only', async () => {
63
64
  await harness.callTool('gog_slides_create_from_template', { templateId: 'tpl1', title: 'Deck' });
64
65
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
65
- ['slides', 'create-from-template', 'tpl1', 'Deck'],
66
+ ['slides', 'create-from-template', pos('tpl1'), pos('Deck')],
66
67
  { account: undefined },
67
68
  );
68
69
  });
@@ -74,7 +75,7 @@ describe('gog_slides_create_from_template', () => {
74
75
  replacements: { name: 'Alice' },
75
76
  });
76
77
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
77
- ['slides', 'create-from-template', 'tpl1', 'Deck', '--replace=name=Alice'],
78
+ ['slides', 'create-from-template', pos('tpl1'), pos('Deck'), '--replace=name=Alice'],
78
79
  { account: undefined },
79
80
  );
80
81
  });
@@ -100,7 +101,7 @@ describe('gog_slides_create_from_template', () => {
100
101
  });
101
102
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
102
103
  [
103
- 'slides', 'create-from-template', 'tpl1', 'Deck',
104
+ 'slides', 'create-from-template', pos('tpl1'), pos('Deck'),
104
105
  '--replacements=/tmp/r.json',
105
106
  '--parent=folder1',
106
107
  '--exact',
@@ -116,7 +117,7 @@ describe('gog_slides_create_from_template', () => {
116
117
  exact: false,
117
118
  });
118
119
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
119
- ['slides', 'create-from-template', 'tpl1', 'Deck'],
120
+ ['slides', 'create-from-template', pos('tpl1'), pos('Deck')],
120
121
  { account: undefined },
121
122
  );
122
123
  });
@@ -131,7 +132,7 @@ describe('slides payload routing', () => {
131
132
  it('routes a large create-from-markdown content to --content-file', async () => {
132
133
  await harness.callTool('gog_slides_create_from_markdown', { title: 'Deck', content: big });
133
134
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
134
- ['slides', 'create-from-markdown', 'Deck',
135
+ ['slides', 'create-from-markdown', pos('Deck'),
135
136
  { kind: 'file', flag: 'content-file', contents: big, ext: 'md' }],
136
137
  { account: undefined },
137
138
  );
@@ -140,7 +141,7 @@ describe('slides payload routing', () => {
140
141
  it('routes large add-slide notes to --notes-file', async () => {
141
142
  await harness.callTool('gog_slides_add_slide', { presentationId: 'p1', image: '/tmp/i.png', notes: big });
142
143
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
143
- ['slides', 'add-slide', 'p1', '/tmp/i.png',
144
+ ['slides', 'add-slide', pos('p1'), pos('/tmp/i.png'),
144
145
  { kind: 'file', flag: 'notes-file', contents: big, ext: undefined }],
145
146
  { account: undefined },
146
147
  );
@@ -149,7 +150,7 @@ describe('slides payload routing', () => {
149
150
  it('routes large update-notes notes to --notes-file', async () => {
150
151
  await harness.callTool('gog_slides_update_notes', { presentationId: 'p1', slideId: 's1', notes: big });
151
152
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
152
- ['slides', 'update-notes', 'p1', 's1',
153
+ ['slides', 'update-notes', pos('p1'), pos('s1'),
153
154
  { kind: 'file', flag: 'notes-file', contents: big, ext: undefined }],
154
155
  { account: undefined },
155
156
  );
@@ -160,7 +161,7 @@ describe('slides payload routing', () => {
160
161
  presentationId: 'p1', slideId: 's1', image: '/tmp/i.png', notes: big,
161
162
  });
162
163
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
163
- ['slides', 'replace-slide', 'p1', 's1', '/tmp/i.png',
164
+ ['slides', 'replace-slide', pos('p1'), pos('s1'), pos('/tmp/i.png'),
164
165
  { kind: 'file', flag: 'notes-file', contents: big, ext: undefined }],
165
166
  { account: undefined },
166
167
  );
@@ -173,7 +174,7 @@ describe('slides payload routing', () => {
173
174
  presentationId: 'p1', slideId: 's1', notes: big, notesFile: '/tmp/mine.txt',
174
175
  });
175
176
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
176
- ['slides', 'update-notes', 'p1', 's1',
177
+ ['slides', 'update-notes', pos('p1'), pos('s1'),
177
178
  { kind: 'file', flag: 'notes-file', contents: big, ext: undefined },
178
179
  '--notes-file=/tmp/mine.txt'],
179
180
  { account: undefined },
@@ -194,7 +195,7 @@ describe('gog_slides_add_slide', () => {
194
195
  it('calls runOrDiagnose with presentationId and image', async () => {
195
196
  await harness.callTool('gog_slides_add_slide', { presentationId: 'p1', image: '/tmp/img.png' });
196
197
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
197
- ['slides', 'add-slide', 'p1', '/tmp/img.png'],
198
+ ['slides', 'add-slide', pos('p1'), pos('/tmp/img.png')],
198
199
  { account: undefined },
199
200
  );
200
201
  });
@@ -209,7 +210,7 @@ describe('gog_slides_add_slide', () => {
209
210
  });
210
211
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
211
212
  [
212
- 'slides', 'add-slide', 'p1', '/tmp/img.png',
213
+ 'slides', 'add-slide', pos('p1'), pos('/tmp/img.png'),
213
214
  '--notes=Speaker note',
214
215
  '--notes-file=/tmp/notes.txt',
215
216
  '--before=slide5',
@@ -223,7 +224,7 @@ describe('gog_slides_delete_slide', () => {
223
224
  it('calls runOrDiagnose with presentationId and slideId', async () => {
224
225
  await harness.callTool('gog_slides_delete_slide', { presentationId: 'p1', slideId: 's1' });
225
226
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
226
- ['slides', 'delete-slide', 'p1', 's1', '--force'],
227
+ ['slides', 'delete-slide', pos('p1'), pos('s1'), '--force'],
227
228
  { account: undefined },
228
229
  );
229
230
  });
@@ -233,7 +234,7 @@ describe('gog_slides_update_notes', () => {
233
234
  it('calls runOrDiagnose with presentationId and slideId', async () => {
234
235
  await harness.callTool('gog_slides_update_notes', { presentationId: 'p1', slideId: 's1' });
235
236
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
236
- ['slides', 'update-notes', 'p1', 's1'],
237
+ ['slides', 'update-notes', pos('p1'), pos('s1')],
237
238
  { account: undefined },
238
239
  );
239
240
  });
@@ -247,7 +248,7 @@ describe('gog_slides_update_notes', () => {
247
248
  });
248
249
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
249
250
  [
250
- 'slides', 'update-notes', 'p1', 's1',
251
+ 'slides', 'update-notes', pos('p1'), pos('s1'),
251
252
  '--notes=speak clearly',
252
253
  '--notes-file=/tmp/n.txt',
253
254
  ],
@@ -264,7 +265,7 @@ describe('gog_slides_replace_slide', () => {
264
265
  image: '/tmp/img.png',
265
266
  });
266
267
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
267
- ['slides', 'replace-slide', 'p1', 's1', '/tmp/img.png'],
268
+ ['slides', 'replace-slide', pos('p1'), pos('s1'), pos('/tmp/img.png')],
268
269
  { account: undefined },
269
270
  );
270
271
  });
@@ -279,7 +280,7 @@ describe('gog_slides_replace_slide', () => {
279
280
  });
280
281
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
281
282
  [
282
- 'slides', 'replace-slide', 'p1', 's1', '/tmp/img.png',
283
+ 'slides', 'replace-slide', pos('p1'), pos('s1'), pos('/tmp/img.png'),
283
284
  '--notes=updated',
284
285
  '--notes-file=/tmp/n.txt',
285
286
  ],
@@ -295,7 +296,7 @@ describe('gog_slides_insert_image', () => {
295
296
  presentationId: 'p1', slideId: 's1', image: '/tmp/i.png', width: 200,
296
297
  });
297
298
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
298
- ['slides', 'insert-image', 'p1', 's1', '/tmp/i.png', '--width=200'],
299
+ ['slides', 'insert-image', pos('p1'), pos('s1'), pos('/tmp/i.png'), '--width=200'],
299
300
  { account: undefined },
300
301
  );
301
302
  });
@@ -305,7 +306,7 @@ describe('gog_slides_insert_image', () => {
305
306
  presentationId: 'p1', slideId: 's1', image: '/tmp/i.png', width: 200, height: 100, x: 50, y: 60, unit: 'PT', account: 'a@b.com',
306
307
  });
307
308
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
308
- ['slides', 'insert-image', 'p1', 's1', '/tmp/i.png', '--width=200', '--height=100', '--x=50', '--y=60', '--unit=PT'],
309
+ ['slides', 'insert-image', pos('p1'), pos('s1'), pos('/tmp/i.png'), '--width=200', '--height=100', '--x=50', '--y=60', '--unit=PT'],
309
310
  { account: 'a@b.com' },
310
311
  );
311
312
  });
@@ -321,7 +322,7 @@ describe('gog_slides_insert_image url mode', () => {
321
322
  presentationId: 'p1', slideId: 's1', url: 'https://x.test/i.png', width: 200, height: 100,
322
323
  });
323
324
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
324
- ['slides', 'insert-image', 'p1', 's1', '--width=200', '--url=https://x.test/i.png', '--height=100'],
325
+ ['slides', 'insert-image', pos('p1'), pos('s1'), '--width=200', '--url=https://x.test/i.png', '--height=100'],
325
326
  { account: undefined },
326
327
  );
327
328
  });
@@ -333,7 +334,7 @@ describe('gog_slides_replace_slide url mode', () => {
333
334
  presentationId: 'p1', slideId: 's1', url: 'https://x.test/i.png', notes: 'n',
334
335
  });
335
336
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
336
- ['slides', 'replace-slide', 'p1', 's1', '--url=https://x.test/i.png', '--notes=n'],
337
+ ['slides', 'replace-slide', pos('p1'), pos('s1'), '--url=https://x.test/i.png', '--notes=n'],
337
338
  { account: undefined },
338
339
  );
339
340
  });
@@ -342,11 +343,11 @@ describe('gog_slides_replace_slide url mode', () => {
342
343
  describe('gog_slides_raw', () => {
343
344
  it('passes --pretty', async () => {
344
345
  await harness.callTool('gog_slides_raw', { presentationId: 'p1', pretty: true });
345
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'raw', 'p1', '--pretty'], { account: undefined, lossless: true });
346
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'raw', pos('p1'), '--pretty'], { account: undefined, lossless: true });
346
347
  });
347
348
  it('bare', async () => {
348
349
  await harness.callTool('gog_slides_raw', { presentationId: 'p1' });
349
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'raw', 'p1'], { account: undefined, lossless: true });
350
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'raw', pos('p1')], { account: undefined, lossless: true });
350
351
  });
351
352
  });
352
353
 
@@ -356,13 +357,13 @@ describe('gog_slides_locate', () => {
356
357
  presentationId: 'p1', text: 'hi', page: 'sl1', occurrence: 2, all: true, matchCase: true, failEmpty: true,
357
358
  });
358
359
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
359
- ['slides', 'locate', 'p1', 'hi', '--page=sl1', '--occurrence=2', '--all', '--match-case', '--fail-empty'],
360
+ ['slides', 'locate', pos('p1'), pos('hi'), '--page=sl1', '--occurrence=2', '--all', '--match-case', '--fail-empty'],
360
361
  { account: undefined },
361
362
  );
362
363
  });
363
364
  it('bare', async () => {
364
365
  await harness.callTool('gog_slides_locate', { presentationId: 'p1', text: 'hi' });
365
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'locate', 'p1', 'hi'], { account: undefined });
366
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'locate', pos('p1'), pos('hi')], { account: undefined });
366
367
  });
367
368
  });
368
369
 
@@ -372,13 +373,13 @@ describe('gog_slides_thumbnail', () => {
372
373
  presentationId: 'p1', slideId: 's1', format: 'png', size: 'large', out: '/tmp/t.png', overwrite: true,
373
374
  });
374
375
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
375
- ['slides', 'thumbnail', 'p1', 's1', '--format=png', '--size=large', '--out=/tmp/t.png', '--overwrite'],
376
+ ['slides', 'thumbnail', pos('p1'), pos('s1'), '--format=png', '--size=large', '--out=/tmp/t.png', '--overwrite'],
376
377
  { account: undefined },
377
378
  );
378
379
  });
379
380
  it('bare', async () => {
380
381
  await harness.callTool('gog_slides_thumbnail', { presentationId: 'p1', slideId: 's1' });
381
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'thumbnail', 'p1', 's1'], { account: undefined });
382
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'thumbnail', pos('p1'), pos('s1')], { account: undefined });
382
383
  });
383
384
  });
384
385
 
@@ -386,17 +387,17 @@ describe('gog_slides_new_slide', () => {
386
387
  it('passes layout and index', async () => {
387
388
  await harness.callTool('gog_slides_new_slide', { presentationId: 'p1', layout: 'TITLE_AND_BODY', index: 2 });
388
389
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
389
- ['slides', 'new-slide', 'p1', '--layout=TITLE_AND_BODY', '--index=2'],
390
+ ['slides', 'new-slide', pos('p1'), '--layout=TITLE_AND_BODY', '--index=2'],
390
391
  { account: undefined },
391
392
  );
392
393
  });
393
394
  it('passes layoutId', async () => {
394
395
  await harness.callTool('gog_slides_new_slide', { presentationId: 'p1', layoutId: 'LAY1' });
395
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'new-slide', 'p1', '--layout-id=LAY1'], { account: undefined });
396
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'new-slide', pos('p1'), '--layout-id=LAY1'], { account: undefined });
396
397
  });
397
398
  it('bare', async () => {
398
399
  await harness.callTool('gog_slides_new_slide', { presentationId: 'p1' });
399
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'new-slide', 'p1'], { account: undefined });
400
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'new-slide', pos('p1')], { account: undefined });
400
401
  });
401
402
  });
402
403
 
@@ -404,11 +405,11 @@ describe('gog_slides_duplicate_slide', () => {
404
405
  it('passes --to-index', async () => {
405
406
  await harness.callTool('gog_slides_duplicate_slide', { presentationId: 'p1', slideId: 's1', toIndex: 3 });
406
407
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
407
- ['slides', 'duplicate-slide', 'p1', 's1', '--to-index=3'], { account: undefined });
408
+ ['slides', 'duplicate-slide', pos('p1'), pos('s1'), '--to-index=3'], { account: undefined });
408
409
  });
409
410
  it('bare', async () => {
410
411
  await harness.callTool('gog_slides_duplicate_slide', { presentationId: 'p1', slideId: 's1' });
411
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'duplicate-slide', 'p1', 's1'], { account: undefined });
412
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'duplicate-slide', pos('p1'), pos('s1')], { account: undefined });
412
413
  });
413
414
  });
414
415
 
@@ -416,7 +417,7 @@ describe('gog_slides_move_slide', () => {
416
417
  it('passes required --to-index', async () => {
417
418
  await harness.callTool('gog_slides_move_slide', { presentationId: 'p1', slideId: 's1', toIndex: 0 });
418
419
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
419
- ['slides', 'move-slide', 'p1', 's1', '--to-index=0'], { account: undefined });
420
+ ['slides', 'move-slide', pos('p1'), pos('s1'), '--to-index=0'], { account: undefined });
420
421
  });
421
422
  });
422
423
 
@@ -426,13 +427,13 @@ describe('gog_slides_insert_text', () => {
426
427
  presentationId: 'p1', objectId: 'o1', text: 'hi', row: 0, col: 1, insertionIndex: 2, replace: true,
427
428
  });
428
429
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
429
- ['slides', 'insert-text', 'p1', 'o1', 'hi', '--row=0', '--col=1', '--insertion-index=2', '--replace'],
430
+ ['slides', 'insert-text', pos('p1'), pos('o1'), pos('hi'), '--row=0', '--col=1', '--insertion-index=2', '--replace'],
430
431
  { account: undefined },
431
432
  );
432
433
  });
433
434
  it('bare', async () => {
434
435
  await harness.callTool('gog_slides_insert_text', { presentationId: 'p1', objectId: 'o1', text: 'hi' });
435
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'insert-text', 'p1', 'o1', 'hi'], { account: undefined });
436
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'insert-text', pos('p1'), pos('o1'), pos('hi')], { account: undefined });
436
437
  });
437
438
  });
438
439
 
@@ -443,14 +444,14 @@ describe('gog_slides_style_text', () => {
443
444
  bold: true, noBold: true, italic: true, noItalic: true, underline: true, noUnderline: true,
444
445
  });
445
446
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
446
- ['slides', 'style-text', 'p1', 'o1', '--range=0:5', '--font=Arial', '--size=18', '--text-color=#ff0000',
447
+ ['slides', 'style-text', pos('p1'), pos('o1'), '--range=0:5', '--font=Arial', '--size=18', '--text-color=#ff0000',
447
448
  '--bold', '--no-bold', '--italic', '--no-italic', '--underline', '--no-underline'],
448
449
  { account: undefined },
449
450
  );
450
451
  });
451
452
  it('minimal (range only)', async () => {
452
453
  await harness.callTool('gog_slides_style_text', { presentationId: 'p1', objectId: 'o1', range: '0:5' });
453
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'style-text', 'p1', 'o1', '--range=0:5'], { account: undefined });
454
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'style-text', pos('p1'), pos('o1'), '--range=0:5'], { account: undefined });
454
455
  });
455
456
  });
456
457
 
@@ -458,12 +459,12 @@ describe('gog_slides_link', () => {
458
459
  it('applies a url', async () => {
459
460
  await harness.callTool('gog_slides_link', { presentationId: 'p1', objectId: 'o1', range: '0:5', url: 'https://x.test' });
460
461
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
461
- ['slides', 'link', 'p1', 'o1', '--range=0:5', '--url=https://x.test'], { account: undefined });
462
+ ['slides', 'link', pos('p1'), pos('o1'), '--range=0:5', '--url=https://x.test'], { account: undefined });
462
463
  });
463
464
  it('clears the link', async () => {
464
465
  await harness.callTool('gog_slides_link', { presentationId: 'p1', objectId: 'o1', range: '0:5', clear: true });
465
466
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
466
- ['slides', 'link', 'p1', 'o1', '--range=0:5', '--clear'], { account: undefined });
467
+ ['slides', 'link', pos('p1'), pos('o1'), '--range=0:5', '--clear'], { account: undefined });
467
468
  });
468
469
  });
469
470
 
@@ -471,12 +472,12 @@ describe('gog_slides_bullets', () => {
471
472
  it('turns bullets on with a preset', async () => {
472
473
  await harness.callTool('gog_slides_bullets', { presentationId: 'p1', objectId: 'o1', range: '0:5', on: true, preset: 'BULLET_DISC_CIRCLE_SQUARE' });
473
474
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
474
- ['slides', 'bullets', 'p1', 'o1', '--range=0:5', '--on', '--preset=BULLET_DISC_CIRCLE_SQUARE'], { account: undefined });
475
+ ['slides', 'bullets', pos('p1'), pos('o1'), '--range=0:5', '--on', '--preset=BULLET_DISC_CIRCLE_SQUARE'], { account: undefined });
475
476
  });
476
477
  it('turns bullets off', async () => {
477
478
  await harness.callTool('gog_slides_bullets', { presentationId: 'p1', objectId: 'o1', range: '0:5', off: true });
478
479
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
479
- ['slides', 'bullets', 'p1', 'o1', '--range=0:5', '--off'], { account: undefined });
480
+ ['slides', 'bullets', pos('p1'), pos('o1'), '--range=0:5', '--off'], { account: undefined });
480
481
  });
481
482
  });
482
483
 
@@ -484,17 +485,17 @@ describe('gog_slides_replace_text', () => {
484
485
  it('scopes to a single object', async () => {
485
486
  await harness.callTool('gog_slides_replace_text', { presentationId: 'p1', find: 'a', replacement: 'b', object: 'o1', matchCase: true });
486
487
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
487
- ['slides', 'replace-text', 'p1', 'a', 'b', '--object=o1', '--match-case'], { account: undefined });
488
+ ['slides', 'replace-text', pos('p1'), pos('a'), pos('b'), '--object=o1', '--match-case'], { account: undefined });
488
489
  });
489
490
  it('scopes to specific pages (repeatable)', async () => {
490
491
  await harness.callTool('gog_slides_replace_text', { presentationId: 'p1', find: 'a', replacement: 'b', pages: ['s1', 's2'] });
491
492
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
492
- ['slides', 'replace-text', 'p1', 'a', 'b', '--page=s1', '--page=s2'], { account: undefined });
493
+ ['slides', 'replace-text', pos('p1'), pos('a'), pos('b'), '--page=s1', '--page=s2'], { account: undefined });
493
494
  });
494
495
  it('scopes to the whole presentation', async () => {
495
496
  await harness.callTool('gog_slides_replace_text', { presentationId: 'p1', find: 'a', replacement: 'b', all: true });
496
497
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
497
- ['slides', 'replace-text', 'p1', 'a', 'b', '--all'], { account: undefined });
498
+ ['slides', 'replace-text', pos('p1'), pos('a'), pos('b'), '--all'], { account: undefined });
498
499
  });
499
500
  });
500
501
 
@@ -504,13 +505,13 @@ describe('gog_slides_element_create_shape', () => {
504
505
  presentationId: 'p1', slideId: 's1', type: 'RECTANGLE', x: 10, y: 20, width: 100, height: 50, unit: 'PT', objectId: 'shape1',
505
506
  });
506
507
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
507
- ['slides', 'element', 'create-shape', 'p1', 's1', '--type=RECTANGLE', '--x=10', '--y=20', '--width=100', '--height=50', '--unit=PT', '--object-id=shape1'],
508
+ ['slides', 'element', 'create-shape', pos('p1'), pos('s1'), '--type=RECTANGLE', '--x=10', '--y=20', '--width=100', '--height=50', '--unit=PT', '--object-id=shape1'],
508
509
  { account: undefined },
509
510
  );
510
511
  });
511
512
  it('bare', async () => {
512
513
  await harness.callTool('gog_slides_element_create_shape', { presentationId: 'p1', slideId: 's1' });
513
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'create-shape', 'p1', 's1'], { account: undefined });
514
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'create-shape', pos('p1'), pos('s1')], { account: undefined });
514
515
  });
515
516
  });
516
517
 
@@ -520,13 +521,13 @@ describe('gog_slides_element_create_line', () => {
520
521
  presentationId: 'p1', slideId: 's1', category: 'BENT', x: 10, y: 20, width: 100, height: 50, unit: 'EMU', objectId: 'line1',
521
522
  });
522
523
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
523
- ['slides', 'element', 'create-line', 'p1', 's1', '--category=BENT', '--x=10', '--y=20', '--width=100', '--height=50', '--unit=EMU', '--object-id=line1'],
524
+ ['slides', 'element', 'create-line', pos('p1'), pos('s1'), '--category=BENT', '--x=10', '--y=20', '--width=100', '--height=50', '--unit=EMU', '--object-id=line1'],
524
525
  { account: undefined },
525
526
  );
526
527
  });
527
528
  it('bare', async () => {
528
529
  await harness.callTool('gog_slides_element_create_line', { presentationId: 'p1', slideId: 's1' });
529
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'create-line', 'p1', 's1'], { account: undefined });
530
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'create-line', pos('p1'), pos('s1')], { account: undefined });
530
531
  });
531
532
  });
532
533
 
@@ -537,14 +538,14 @@ describe('gog_slides_element_style', () => {
537
538
  outlineColor: '#000', outlineWeight: 2, outlineDash: 'DASH', outlineTransparent: true,
538
539
  });
539
540
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
540
- ['slides', 'element', 'style', 'p1', 'o1', '--kind=shape', '--fill-color=#fff', '--fill-transparent',
541
+ ['slides', 'element', 'style', pos('p1'), pos('o1'), '--kind=shape', '--fill-color=#fff', '--fill-transparent',
541
542
  '--outline-color=#000', '--outline-weight=2', '--outline-dash=DASH', '--outline-transparent'],
542
543
  { account: undefined },
543
544
  );
544
545
  });
545
546
  it('bare', async () => {
546
547
  await harness.callTool('gog_slides_element_style', { presentationId: 'p1', objectId: 'o1' });
547
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'style', 'p1', 'o1'], { account: undefined });
548
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'style', pos('p1'), pos('o1')], { account: undefined });
548
549
  });
549
550
  });
550
551
 
@@ -555,14 +556,14 @@ describe('gog_slides_element_transform', () => {
555
556
  shearX: 0.1, shearY: 0.2, rotate: 45, applyMode: 'ABSOLUTE', unit: 'PT',
556
557
  });
557
558
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
558
- ['slides', 'element', 'transform', 'p1', 'o1', '--translate-x=10', '--translate-y=20', '--scale-x=1.5', '--scale-y=2',
559
+ ['slides', 'element', 'transform', pos('p1'), pos('o1'), '--translate-x=10', '--translate-y=20', '--scale-x=1.5', '--scale-y=2',
559
560
  '--shear-x=0.1', '--shear-y=0.2', '--rotate=45', '--apply-mode=ABSOLUTE', '--unit=PT'],
560
561
  { account: undefined },
561
562
  );
562
563
  });
563
564
  it('bare', async () => {
564
565
  await harness.callTool('gog_slides_element_transform', { presentationId: 'p1', objectId: 'o1' });
565
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'transform', 'p1', 'o1'], { account: undefined });
566
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'transform', pos('p1'), pos('o1')], { account: undefined });
566
567
  });
567
568
  });
568
569
 
@@ -570,7 +571,7 @@ describe('gog_slides_element_z_order', () => {
570
571
  it('passes the operation', async () => {
571
572
  await harness.callTool('gog_slides_element_z_order', { presentationId: 'p1', objectId: 'o1', operation: 'BRING_TO_FRONT' });
572
573
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
573
- ['slides', 'element', 'z-order', 'p1', 'o1', '--operation=BRING_TO_FRONT'], { account: undefined });
574
+ ['slides', 'element', 'z-order', pos('p1'), pos('o1'), '--operation=BRING_TO_FRONT'], { account: undefined });
574
575
  });
575
576
  });
576
577
 
@@ -578,12 +579,12 @@ describe('gog_slides_element_group', () => {
578
579
  it('groups elements with a group id', async () => {
579
580
  await harness.callTool('gog_slides_element_group', { presentationId: 'p1', objectIds: ['o1', 'o2', 'o3'], groupId: 'g1' });
580
581
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
581
- ['slides', 'element', 'group', 'p1', 'o1', 'o2', 'o3', '--group-id=g1'], { account: undefined });
582
+ ['slides', 'element', 'group', pos('p1'), pos('o1'), pos('o2'), pos('o3'), '--group-id=g1'], { account: undefined });
582
583
  });
583
584
  it('groups elements without a group id', async () => {
584
585
  await harness.callTool('gog_slides_element_group', { presentationId: 'p1', objectIds: ['o1', 'o2'] });
585
586
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
586
- ['slides', 'element', 'group', 'p1', 'o1', 'o2'], { account: undefined });
587
+ ['slides', 'element', 'group', pos('p1'), pos('o1'), pos('o2')], { account: undefined });
587
588
  });
588
589
  });
589
590
 
@@ -591,7 +592,7 @@ describe('gog_slides_element_ungroup', () => {
591
592
  it('ungroups groups', async () => {
592
593
  await harness.callTool('gog_slides_element_ungroup', { presentationId: 'p1', groupIds: ['g1', 'g2'] });
593
594
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
594
- ['slides', 'element', 'ungroup', 'p1', 'g1', 'g2'], { account: undefined });
595
+ ['slides', 'element', 'ungroup', pos('p1'), pos('g1'), pos('g2')], { account: undefined });
595
596
  });
596
597
  });
597
598
 
@@ -599,23 +600,23 @@ describe('gog_slides_element_alt_text', () => {
599
600
  it('sets title and description', async () => {
600
601
  await harness.callTool('gog_slides_element_alt_text', { presentationId: 'p1', objectId: 'o1', title: 'T', description: 'D' });
601
602
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
602
- ['slides', 'element', 'alt-text', 'p1', 'o1', '--title=T', '--description=D'], { account: undefined });
603
+ ['slides', 'element', 'alt-text', pos('p1'), pos('o1'), '--title=T', '--description=D'], { account: undefined });
603
604
  });
604
605
  it('clears both fields with empty strings', async () => {
605
606
  await harness.callTool('gog_slides_element_alt_text', { presentationId: 'p1', objectId: 'o1', title: '', description: '' });
606
607
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
607
- ['slides', 'element', 'alt-text', 'p1', 'o1', '--title=', '--description='], { account: undefined });
608
+ ['slides', 'element', 'alt-text', pos('p1'), pos('o1'), '--title=', '--description='], { account: undefined });
608
609
  });
609
610
  it('bare leaves both untouched', async () => {
610
611
  await harness.callTool('gog_slides_element_alt_text', { presentationId: 'p1', objectId: 'o1' });
611
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'alt-text', 'p1', 'o1'], { account: undefined });
612
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'alt-text', pos('p1'), pos('o1')], { account: undefined });
612
613
  });
613
614
  });
614
615
 
615
616
  describe('gog_slides_element_delete', () => {
616
617
  it('deletes an element', async () => {
617
618
  await harness.callTool('gog_slides_element_delete', { presentationId: 'p1', objectId: 'o1' });
618
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'delete', 'p1', 'o1', '--force'], { account: undefined });
619
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['slides', 'element', 'delete', pos('p1'), pos('o1'), '--force'], { account: undefined });
619
620
  });
620
621
  });
621
622
 
@@ -627,12 +628,12 @@ describe('gog_slides_table_create', () => {
627
628
  it('passes rows, cols and object id', async () => {
628
629
  await harness.callTool('gog_slides_table_create', { presentationId: 'p1', slideId: 's1', rows: 3, cols: 4, objectId: 'tbl1' });
629
630
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
630
- ['slides', 'table', 'create', 'p1', 's1', '--rows=3', '--cols=4', '--object-id=tbl1'], { account: undefined });
631
+ ['slides', 'table', 'create', pos('p1'), pos('s1'), '--rows=3', '--cols=4', '--object-id=tbl1'], { account: undefined });
631
632
  });
632
633
  it('bare', async () => {
633
634
  await harness.callTool('gog_slides_table_create', { presentationId: 'p1', slideId: 's1', rows: 2, cols: 2 });
634
635
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
635
- ['slides', 'table', 'create', 'p1', 's1', '--rows=2', '--cols=2'], { account: undefined });
636
+ ['slides', 'table', 'create', pos('p1'), pos('s1'), '--rows=2', '--cols=2'], { account: undefined });
636
637
  });
637
638
  });
638
639
 
@@ -644,7 +645,7 @@ describe('gog_slides_table_cell_style', () => {
644
645
  bold: true, noBold: true, italic: true, noItalic: true, underline: true, noUnderline: true,
645
646
  });
646
647
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
647
- ['slides', 'table', 'cell', 'style', 'p1', 't1', '--row=0', '--col=1', '--range=0:3', '--fill-color=#eee', '--fill-transparent',
648
+ ['slides', 'table', 'cell', 'style', pos('p1'), pos('t1'), '--row=0', '--col=1', '--range=0:3', '--fill-color=#eee', '--fill-transparent',
648
649
  '--content-align=MIDDLE', '--font=Arial', '--size=12', '--text-color=#111',
649
650
  '--bold', '--no-bold', '--italic', '--no-italic', '--underline', '--no-underline'],
650
651
  { account: undefined },
@@ -653,7 +654,7 @@ describe('gog_slides_table_cell_style', () => {
653
654
  it('minimal (row/col only)', async () => {
654
655
  await harness.callTool('gog_slides_table_cell_style', { presentationId: 'p1', tableObjectId: 't1', row: 0, col: 0 });
655
656
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
656
- ['slides', 'table', 'cell', 'style', 'p1', 't1', '--row=0', '--col=0'], { account: undefined });
657
+ ['slides', 'table', 'cell', 'style', pos('p1'), pos('t1'), '--row=0', '--col=0'], { account: undefined });
657
658
  });
658
659
  });
659
660
 
@@ -664,7 +665,7 @@ describe('gog_slides_table_border_style', () => {
664
665
  position: 'OUTER', borderColor: '#000', weight: 2, dash: 'DASH', transparent: true,
665
666
  });
666
667
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
667
- ['slides', 'table', 'border', 'style', 'p1', 't1', '--row=0', '--col=0', '--row-span=2', '--col-span=2',
668
+ ['slides', 'table', 'border', 'style', pos('p1'), pos('t1'), '--row=0', '--col=0', '--row-span=2', '--col-span=2',
668
669
  '--position=OUTER', '--border-color=#000', '--weight=2', '--dash=DASH', '--transparent'],
669
670
  { account: undefined },
670
671
  );
@@ -672,7 +673,7 @@ describe('gog_slides_table_border_style', () => {
672
673
  it('minimal (row/col only)', async () => {
673
674
  await harness.callTool('gog_slides_table_border_style', { presentationId: 'p1', tableObjectId: 't1', row: 1, col: 1 });
674
675
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
675
- ['slides', 'table', 'border', 'style', 'p1', 't1', '--row=1', '--col=1'], { account: undefined });
676
+ ['slides', 'table', 'border', 'style', pos('p1'), pos('t1'), '--row=1', '--col=1'], { account: undefined });
676
677
  });
677
678
  });
678
679
 
@@ -680,12 +681,12 @@ describe('gog_slides_table_column_insert', () => {
680
681
  it('passes count and --right', async () => {
681
682
  await harness.callTool('gog_slides_table_column_insert', { presentationId: 'p1', tableObjectId: 't1', col: 1, count: 2, right: true });
682
683
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
683
- ['slides', 'table', 'column', 'insert', 'p1', 't1', '--col=1', '--count=2', '--right'], { account: undefined });
684
+ ['slides', 'table', 'column', 'insert', pos('p1'), pos('t1'), '--col=1', '--count=2', '--right'], { account: undefined });
684
685
  });
685
686
  it('bare', async () => {
686
687
  await harness.callTool('gog_slides_table_column_insert', { presentationId: 'p1', tableObjectId: 't1', col: 0 });
687
688
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
688
- ['slides', 'table', 'column', 'insert', 'p1', 't1', '--col=0'], { account: undefined });
689
+ ['slides', 'table', 'column', 'insert', pos('p1'), pos('t1'), '--col=0'], { account: undefined });
689
690
  });
690
691
  });
691
692
 
@@ -693,7 +694,7 @@ describe('gog_slides_table_column_delete', () => {
693
694
  it('deletes by column index', async () => {
694
695
  await harness.callTool('gog_slides_table_column_delete', { presentationId: 'p1', tableObjectId: 't1', col: 2 });
695
696
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
696
- ['slides', 'table', 'column', 'delete', 'p1', 't1', '--col=2', '--force'], { account: undefined });
697
+ ['slides', 'table', 'column', 'delete', pos('p1'), pos('t1'), '--col=2', '--force'], { account: undefined });
697
698
  });
698
699
  });
699
700
 
@@ -701,7 +702,7 @@ describe('gog_slides_table_column_size', () => {
701
702
  it('sets column width', async () => {
702
703
  await harness.callTool('gog_slides_table_column_size', { presentationId: 'p1', tableObjectId: 't1', col: 1, width: 120 });
703
704
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
704
- ['slides', 'table', 'column', 'size', 'p1', 't1', '--col=1', '--width=120'], { account: undefined });
705
+ ['slides', 'table', 'column', 'size', pos('p1'), pos('t1'), '--col=1', '--width=120'], { account: undefined });
705
706
  });
706
707
  });
707
708
 
@@ -709,12 +710,12 @@ describe('gog_slides_table_row_insert', () => {
709
710
  it('passes count and --below', async () => {
710
711
  await harness.callTool('gog_slides_table_row_insert', { presentationId: 'p1', tableObjectId: 't1', row: 0, count: 1, below: true });
711
712
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
712
- ['slides', 'table', 'row', 'insert', 'p1', 't1', '--row=0', '--count=1', '--below'], { account: undefined });
713
+ ['slides', 'table', 'row', 'insert', pos('p1'), pos('t1'), '--row=0', '--count=1', '--below'], { account: undefined });
713
714
  });
714
715
  it('bare', async () => {
715
716
  await harness.callTool('gog_slides_table_row_insert', { presentationId: 'p1', tableObjectId: 't1', row: 1 });
716
717
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
717
- ['slides', 'table', 'row', 'insert', 'p1', 't1', '--row=1'], { account: undefined });
718
+ ['slides', 'table', 'row', 'insert', pos('p1'), pos('t1'), '--row=1'], { account: undefined });
718
719
  });
719
720
  });
720
721
 
@@ -722,7 +723,7 @@ describe('gog_slides_table_row_delete', () => {
722
723
  it('deletes by row index', async () => {
723
724
  await harness.callTool('gog_slides_table_row_delete', { presentationId: 'p1', tableObjectId: 't1', row: 2 });
724
725
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
725
- ['slides', 'table', 'row', 'delete', 'p1', 't1', '--row=2', '--force'], { account: undefined });
726
+ ['slides', 'table', 'row', 'delete', pos('p1'), pos('t1'), '--row=2', '--force'], { account: undefined });
726
727
  });
727
728
  });
728
729
 
@@ -730,7 +731,7 @@ describe('gog_slides_table_row_size', () => {
730
731
  it('sets row min height', async () => {
731
732
  await harness.callTool('gog_slides_table_row_size', { presentationId: 'p1', tableObjectId: 't1', row: 0, height: 40 });
732
733
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
733
- ['slides', 'table', 'row', 'size', 'p1', 't1', '--row=0', '--height=40'], { account: undefined });
734
+ ['slides', 'table', 'row', 'size', pos('p1'), pos('t1'), '--row=0', '--height=40'], { account: undefined });
734
735
  });
735
736
  });
736
737
 
@@ -738,12 +739,12 @@ describe('gog_slides_table_merge', () => {
738
739
  it('passes span', async () => {
739
740
  await harness.callTool('gog_slides_table_merge', { presentationId: 'p1', tableObjectId: 't1', row: 0, col: 0, rowSpan: 2, colSpan: 3 });
740
741
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
741
- ['slides', 'table', 'merge', 'p1', 't1', '--row=0', '--col=0', '--row-span=2', '--col-span=3'], { account: undefined });
742
+ ['slides', 'table', 'merge', pos('p1'), pos('t1'), '--row=0', '--col=0', '--row-span=2', '--col-span=3'], { account: undefined });
742
743
  });
743
744
  it('minimal', async () => {
744
745
  await harness.callTool('gog_slides_table_merge', { presentationId: 'p1', tableObjectId: 't1', row: 0, col: 0 });
745
746
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
746
- ['slides', 'table', 'merge', 'p1', 't1', '--row=0', '--col=0'], { account: undefined });
747
+ ['slides', 'table', 'merge', pos('p1'), pos('t1'), '--row=0', '--col=0'], { account: undefined });
747
748
  });
748
749
  });
749
750
 
@@ -751,12 +752,12 @@ describe('gog_slides_table_unmerge', () => {
751
752
  it('passes span', async () => {
752
753
  await harness.callTool('gog_slides_table_unmerge', { presentationId: 'p1', tableObjectId: 't1', row: 0, col: 0, rowSpan: 2, colSpan: 2 });
753
754
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
754
- ['slides', 'table', 'unmerge', 'p1', 't1', '--row=0', '--col=0', '--row-span=2', '--col-span=2'], { account: undefined });
755
+ ['slides', 'table', 'unmerge', pos('p1'), pos('t1'), '--row=0', '--col=0', '--row-span=2', '--col-span=2'], { account: undefined });
755
756
  });
756
757
  it('minimal', async () => {
757
758
  await harness.callTool('gog_slides_table_unmerge', { presentationId: 'p1', tableObjectId: 't1', row: 1, col: 1 });
758
759
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
759
- ['slides', 'table', 'unmerge', 'p1', 't1', '--row=1', '--col=1'], { account: undefined });
760
+ ['slides', 'table', 'unmerge', pos('p1'), pos('t1'), '--row=1', '--col=1'], { account: undefined });
760
761
  });
761
762
  });
762
763
 
@@ -764,7 +765,7 @@ describe('gog_slides_skip_slide', () => {
764
765
  it('calls runOrDiagnose with presentationId and slideId', async () => {
765
766
  await harness.callTool('gog_slides_skip_slide', { presentationId: 'p1', slideId: 's1' });
766
767
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
767
- ['slides', 'skip-slide', 'p1', 's1'],
768
+ ['slides', 'skip-slide', pos('p1'), pos('s1')],
768
769
  { account: undefined },
769
770
  );
770
771
  });
@@ -772,7 +773,7 @@ describe('gog_slides_skip_slide', () => {
772
773
  it('passes the account through', async () => {
773
774
  await harness.callTool('gog_slides_skip_slide', { presentationId: 'p1', slideId: 's1', account: 'me@x.com' });
774
775
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
775
- ['slides', 'skip-slide', 'p1', 's1'],
776
+ ['slides', 'skip-slide', pos('p1'), pos('s1')],
776
777
  { account: 'me@x.com' },
777
778
  );
778
779
  });
@@ -782,8 +783,51 @@ describe('gog_slides_unskip_slide', () => {
782
783
  it('calls runOrDiagnose with presentationId and slideId', async () => {
783
784
  await harness.callTool('gog_slides_unskip_slide', { presentationId: 'p1', slideId: 's1' });
784
785
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
785
- ['slides', 'unskip-slide', 'p1', 's1'],
786
- { account: undefined },
787
- );
786
+ ['slides', 'unskip-slide', pos('p1'), pos('s1')],
787
+ { account: undefined },
788
+ );
789
+ });
790
+ });
791
+
792
+ // SEC-3/SEC-4: every model-supplied server path must resolve inside an
793
+ // operator-configured root (GOG_FILE_ROOTS). The suite runs with '/' so the
794
+ // arg-shape tests can use any path; these narrow it.
795
+ async function withFileRoots<T>(roots: string, fn: () => Promise<T>): Promise<T> {
796
+ const prev = process.env.GOG_FILE_ROOTS;
797
+ process.env.GOG_FILE_ROOTS = roots;
798
+ try {
799
+ return await fn();
800
+ } finally {
801
+ process.env.GOG_FILE_ROOTS = prev;
802
+ }
803
+ }
804
+
805
+ describe('server paths are confined to GOG_FILE_ROOTS', () => {
806
+ it.each([
807
+ ['gog_slides_create_from_markdown', {"title": "t", "contentFile": "/etc/passwd"}, 'contentFile'],
808
+ ['gog_slides_create_from_template', {"templateId": "t", "title": "x", "replacementsFile": "/etc/passwd"}, 'replacementsFile'],
809
+ ['gog_slides_add_slide', {"presentationId": "p", "image": "/etc/passwd"}, 'image'],
810
+ ['gog_slides_add_slide', {"presentationId": "p", "image": "/srv/gog-files/a.png", "notesFile": "/etc/passwd"}, 'notesFile'],
811
+ ['gog_slides_insert_image', {"presentationId": "p", "slideId": "s", "image": "/etc/passwd", "width": 10}, 'image'],
812
+ ['gog_slides_update_notes', {"presentationId": "p", "slideId": "s", "notesFile": "/etc/passwd"}, 'notesFile'],
813
+ ['gog_slides_replace_slide', {"presentationId": "p", "slideId": "s", "image": "/etc/passwd"}, 'image'],
814
+ ['gog_slides_replace_slide', {"presentationId": "p", "slideId": "s", "notesFile": "/etc/passwd"}, 'notesFile'],
815
+ ['gog_slides_thumbnail', {"presentationId": "p", "slideId": "s", "out": "/Users/me/.zshrc"}, 'out'],
816
+ ] as Array<[string, Record<string, unknown>, string]>)('%s refuses %j outside the roots', async (tool, args, param) => {
817
+ const h = harness;
818
+ const result = await withFileRoots('/srv/gog-files', () => h.callTool(tool, args));
819
+ expect(result.isError).toBe(true);
820
+ expect(result.content[0].text).toMatch(new RegExp(`${param} ".*" is outside the directories`));
821
+ expect(lib.runOrDiagnose).not.toHaveBeenCalled();
822
+ });
823
+
824
+ // Clients auto-approve readOnlyHint tools; these write (and can overwrite)
825
+ // files on the gog host, so they must not claim to be read-only.
826
+ it.each(["gog_slides_thumbnail"])('%s is not advertised as read-only and is marked destructive', async (name) => {
827
+ const h = harness;
828
+ const { tools } = await h.client.listTools();
829
+ const tool = tools.find((t) => t.name === name)!;
830
+ expect(tool.annotations?.readOnlyHint).not.toBe(true);
831
+ expect(tool.annotations?.destructiveHint).toBe(true);
788
832
  });
789
833
  });