gogcli-mcp-sheets 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 { registerExtraSheetsTools } from '../../src/tools/sheets-extra.js';
3
3
  import * as lib from '../../../gogcli-mcp/src/lib.js';
4
4
  import { createTestHarness } 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,14 +25,14 @@ describe('gog_sheets_add_tab', () => {
24
25
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
25
26
  const harness = await setupHandlers();
26
27
  await harness.callTool('gog_sheets_add_tab', { spreadsheetId: 'sid', tabName: 'NewTab' });
27
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'add-tab', 'sid', 'NewTab'], { account: undefined });
28
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'add-tab', pos('sid'), pos('NewTab')], { account: undefined });
28
29
  });
29
30
 
30
31
  it('forwards account', async () => {
31
32
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
32
33
  const harness = await setupHandlers();
33
34
  await harness.callTool('gog_sheets_add_tab', { spreadsheetId: 'sid', tabName: 'T', account: 'a@b.com' });
34
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'add-tab', 'sid', 'T'], { account: 'a@b.com' });
35
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'add-tab', pos('sid'), pos('T')], { account: 'a@b.com' });
35
36
  });
36
37
  });
37
38
 
@@ -41,7 +42,7 @@ describe('gog_sheets_delete_tab', () => {
41
42
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
42
43
  const harness = await setupHandlers();
43
44
  await harness.callTool('gog_sheets_delete_tab', { spreadsheetId: 'sid', tabName: 'OldTab' });
44
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'delete-tab', 'sid', 'OldTab', '--force'], { account: undefined });
45
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'delete-tab', pos('sid'), pos('OldTab'), '--force'], { account: undefined });
45
46
  });
46
47
  });
47
48
 
@@ -51,7 +52,7 @@ describe('gog_sheets_rename_tab', () => {
51
52
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
52
53
  const harness = await setupHandlers();
53
54
  await harness.callTool('gog_sheets_rename_tab', { spreadsheetId: 'sid', oldName: 'Old', newName: 'New' });
54
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'rename-tab', 'sid', 'Old', 'New'], { account: undefined });
55
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'rename-tab', pos('sid'), pos('Old'), pos('New')], { account: undefined });
55
56
  });
56
57
  });
57
58
 
@@ -61,14 +62,14 @@ describe('gog_sheets_copy', () => {
61
62
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
62
63
  const harness = await setupHandlers();
63
64
  await harness.callTool('gog_sheets_copy', { spreadsheetId: 'sid', title: 'Copy' });
64
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy', 'sid', 'Copy'], { account: undefined });
65
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy', pos('sid'), pos('Copy')], { account: undefined });
65
66
  });
66
67
 
67
68
  it('includes --parent when provided', async () => {
68
69
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
69
70
  const harness = await setupHandlers();
70
71
  await harness.callTool('gog_sheets_copy', { spreadsheetId: 'sid', title: 'Copy', parent: 'folderId' });
71
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy', 'sid', 'Copy', '--parent=folderId'], { account: undefined });
72
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy', pos('sid'), pos('Copy'), '--parent=folderId'], { account: undefined });
72
73
  });
73
74
  });
74
75
 
@@ -78,21 +79,21 @@ describe('gog_sheets_export', () => {
78
79
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
79
80
  const harness = await setupHandlers();
80
81
  await harness.callTool('gog_sheets_export', { spreadsheetId: 'sid' });
81
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', 'sid'], { account: undefined });
82
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', pos('sid')], { account: undefined });
82
83
  });
83
84
 
84
85
  it('includes --format and --out when provided', async () => {
85
86
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
86
87
  const harness = await setupHandlers();
87
88
  await harness.callTool('gog_sheets_export', { spreadsheetId: 'sid', format: 'pdf', out: '/tmp/out.pdf' });
88
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', 'sid', '--format=pdf', '--out=/tmp/out.pdf'], { account: undefined });
89
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', pos('sid'), '--format=pdf', '--out=/tmp/out.pdf'], { account: undefined });
89
90
  });
90
91
 
91
92
  it('includes --overwrite when requested', async () => {
92
93
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
93
94
  const harness = await setupHandlers();
94
95
  await harness.callTool('gog_sheets_export', { spreadsheetId: 'sid', out: '/tmp/out.csv', overwrite: true });
95
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', 'sid', '--out=/tmp/out.csv', '--overwrite'], { account: undefined });
96
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'export', pos('sid'), '--out=/tmp/out.csv', '--overwrite'], { account: undefined });
96
97
  });
97
98
  });
98
99
 
@@ -102,21 +103,21 @@ describe('gog_sheets_freeze', () => {
102
103
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
103
104
  const harness = await setupHandlers();
104
105
  await harness.callTool('gog_sheets_freeze', { spreadsheetId: 'sid' });
105
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', 'sid'], { account: undefined });
106
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', pos('sid')], { account: undefined });
106
107
  });
107
108
 
108
109
  it('includes --rows, --cols, --sheet when provided', async () => {
109
110
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
110
111
  const harness = await setupHandlers();
111
112
  await harness.callTool('gog_sheets_freeze', { spreadsheetId: 'sid', rows: 1, cols: 2, sheet: 'Data' });
112
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', 'sid', '--rows=1', '--cols=2', '--sheet=Data'], { account: undefined });
113
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', pos('sid'), '--rows=1', '--cols=2', '--sheet=Data'], { account: undefined });
113
114
  });
114
115
 
115
116
  it('includes --rows=0 when rows is 0', async () => {
116
117
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
117
118
  const harness = await setupHandlers();
118
119
  await harness.callTool('gog_sheets_freeze', { spreadsheetId: 'sid', rows: 0 });
119
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', 'sid', '--rows=0'], { account: undefined });
120
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'freeze', pos('sid'), '--rows=0'], { account: undefined });
120
121
  });
121
122
  });
122
123
 
@@ -129,7 +130,7 @@ describe('gog_sheets_insert', () => {
129
130
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
130
131
  const harness = await setupHandlers();
131
132
  await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'Sheet1', dimension: 'ROWS', start: 5 });
132
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'Sheet1', 'ROWS', '6'], { account: undefined });
133
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', pos('sid'), pos('Sheet1'), pos('ROWS'), pos('6')], { account: undefined });
133
134
  });
134
135
 
135
136
  it('bug: start=1, after:false inserts before 0-based index 1 (new row 2), not at the top', async () => {
@@ -138,7 +139,7 @@ describe('gog_sheets_insert', () => {
138
139
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
139
140
  const harness = await setupHandlers();
140
141
  await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 1, count: 1 });
141
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '2', '--count=1'], { account: undefined });
142
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', pos('sid'), pos('S1'), pos('ROWS'), pos('2'), '--count=1'], { account: undefined });
142
143
  });
143
144
 
144
145
  it('shifts start by +1 when after:true so the new dimension lands AFTER start', async () => {
@@ -147,7 +148,7 @@ describe('gog_sheets_insert', () => {
147
148
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
148
149
  const harness = await setupHandlers();
149
150
  await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'COLUMNS', start: 0, count: 3, after: true });
150
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'COLUMNS', '1', '--count=3', '--after'], { account: undefined });
151
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', pos('sid'), pos('S1'), pos('COLUMNS'), pos('1'), '--count=3', '--after'], { account: undefined });
151
152
  });
152
153
 
153
154
  it('issue #42 acceptance: start=28, after:true leaves column 28 untouched (insertion lands at 29)', async () => {
@@ -155,14 +156,14 @@ describe('gog_sheets_insert', () => {
155
156
  const harness = await setupHandlers();
156
157
  await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'COLUMNS', start: 28, after: true, count: 1 });
157
158
  // CLI is 1-based; with --after CLI uses startIndex = c.Start, so passing 29 → API startIndex=29.
158
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'COLUMNS', '29', '--count=1', '--after'], { account: undefined });
159
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', pos('sid'), pos('S1'), pos('COLUMNS'), pos('29'), '--count=1', '--after'], { account: undefined });
159
160
  });
160
161
 
161
162
  it('includes --count=0 when count is 0', async () => {
162
163
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
163
164
  const harness = await setupHandlers();
164
165
  await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 0, count: 0 });
165
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '1', '--count=0'], { account: undefined });
166
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', pos('sid'), pos('S1'), pos('ROWS'), pos('1'), '--count=0'], { account: undefined });
166
167
  });
167
168
 
168
169
  it('omits --after and sends start+1 so start=0 inserts at the very top (after:false)', async () => {
@@ -172,21 +173,21 @@ describe('gog_sheets_insert', () => {
172
173
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
173
174
  const harness = await setupHandlers();
174
175
  await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 0, after: false });
175
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '1'], { account: undefined });
176
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', pos('sid'), pos('S1'), pos('ROWS'), pos('1')], { account: undefined });
176
177
  });
177
178
 
178
179
  it('passes --inherit-from-before=true when set', async () => {
179
180
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
180
181
  const harness = await setupHandlers();
181
182
  await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 5, after: true, inheritFromBefore: true });
182
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '6', '--after', '--inherit-from-before=true'], { account: undefined });
183
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', pos('sid'), pos('S1'), pos('ROWS'), pos('6'), '--after', '--inherit-from-before=true'], { account: undefined });
183
184
  });
184
185
 
185
186
  it('passes --inherit-from-before=false to inherit from the following neighbor', async () => {
186
187
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
187
188
  const harness = await setupHandlers();
188
189
  await harness.callTool('gog_sheets_insert', { spreadsheetId: 'sid', sheet: 'S1', dimension: 'ROWS', start: 5, after: true, inheritFromBefore: false });
189
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', 'sid', 'S1', 'ROWS', '6', '--after', '--inherit-from-before=false'], { account: undefined });
190
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'insert', pos('sid'), pos('S1'), pos('ROWS'), pos('6'), '--after', '--inherit-from-before=false'], { account: undefined });
190
191
  });
191
192
  });
192
193
 
@@ -195,21 +196,21 @@ describe('gog_sheets_copy_paste', () => {
195
196
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
196
197
  const harness = await setupHandlers();
197
198
  await harness.callTool('gog_sheets_copy_paste', { spreadsheetId: 'sid', source: 'Sheet1!A2:H71', dest: 'Sheet1!A2:H120' });
198
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'Sheet1!A2:H71', 'Sheet1!A2:H120'], { account: undefined });
199
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', pos('sid'), pos('Sheet1!A2:H71'), pos('Sheet1!A2:H120')], { account: undefined });
199
200
  });
200
201
 
201
202
  it('appends --type and --transpose when provided', async () => {
202
203
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
203
204
  const harness = await setupHandlers();
204
205
  await harness.callTool('gog_sheets_copy_paste', { spreadsheetId: 'sid', source: 'A1:B2', dest: 'D1:E2', type: 'FORMULA', transpose: true });
205
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'A1:B2', 'D1:E2', '--type=FORMULA', '--transpose'], { account: undefined });
206
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', pos('sid'), pos('A1:B2'), pos('D1:E2'), '--type=FORMULA', '--transpose'], { account: undefined });
206
207
  });
207
208
 
208
209
  it('forwards account', async () => {
209
210
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
210
211
  const harness = await setupHandlers();
211
212
  await harness.callTool('gog_sheets_copy_paste', { spreadsheetId: 'sid', source: 'A1', dest: 'B1', account: 'a@b.com' });
212
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', 'sid', 'A1', 'B1'], { account: 'a@b.com' });
213
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'copy-paste', pos('sid'), pos('A1'), pos('B1')], { account: 'a@b.com' });
213
214
  });
214
215
  });
215
216
 
@@ -219,14 +220,14 @@ describe('gog_sheets_merge', () => {
219
220
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
220
221
  const harness = await setupHandlers();
221
222
  await harness.callTool('gog_sheets_merge', { spreadsheetId: 'sid', range: 'A1:C3' });
222
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'merge', 'sid', 'A1:C3'], { account: undefined });
223
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'merge', pos('sid'), pos('A1:C3')], { account: undefined });
223
224
  });
224
225
 
225
226
  it('includes --type when provided', async () => {
226
227
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
227
228
  const harness = await setupHandlers();
228
229
  await harness.callTool('gog_sheets_merge', { spreadsheetId: 'sid', range: 'A1:C3', type: 'MERGE_COLUMNS' });
229
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'merge', 'sid', 'A1:C3', '--type=MERGE_COLUMNS'], { account: undefined });
230
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'merge', pos('sid'), pos('A1:C3'), '--type=MERGE_COLUMNS'], { account: undefined });
230
231
  });
231
232
  });
232
233
 
@@ -236,7 +237,7 @@ describe('gog_sheets_unmerge', () => {
236
237
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
237
238
  const harness = await setupHandlers();
238
239
  await harness.callTool('gog_sheets_unmerge', { spreadsheetId: 'sid', range: 'A1:C3' });
239
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'unmerge', 'sid', 'A1:C3'], { account: undefined });
240
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'unmerge', pos('sid'), pos('A1:C3')], { account: undefined });
240
241
  });
241
242
  });
242
243
 
@@ -248,7 +249,7 @@ describe('gog_sheets_format', () => {
248
249
  const fj = '{"textFormat":{"bold":true}}';
249
250
  await harness.callTool('gog_sheets_format', { spreadsheetId: 'sid', range: 'A1:B2', formatJson: fj, formatFields: 'textFormat.bold' });
250
251
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
251
- ['sheets', 'format', 'sid', 'A1:B2', `--format-json=${fj}`, '--format-fields=textFormat.bold'],
252
+ ['sheets', 'format', pos('sid'), pos('A1:B2'), `--format-json=${fj}`, '--format-fields=textFormat.bold'],
252
253
  { account: undefined },
253
254
  );
254
255
  });
@@ -258,7 +259,7 @@ describe('gog_sheets_format', () => {
258
259
  const harness = await setupHandlers();
259
260
  await harness.callTool('gog_sheets_format', { spreadsheetId: 'sid', range: 'A1', formatJson: '{}' });
260
261
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
261
- ['sheets', 'format', 'sid', 'A1', '--format-json={}'],
262
+ ['sheets', 'format', pos('sid'), pos('A1'), '--format-json={}'],
262
263
  { account: undefined },
263
264
  );
264
265
  });
@@ -277,8 +278,8 @@ describe('gog_sheets_format', () => {
277
278
  const argv = call[0];
278
279
  expect(argv[0]).toBe('sheets');
279
280
  expect(argv[1]).toBe('format');
280
- expect(argv[2]).toBe('sid');
281
- expect(argv[3]).toBe('A1:C3');
281
+ expect(argv[2]).toEqual(pos('sid'));
282
+ expect(argv[3]).toEqual(pos('A1:C3'));
282
283
  const fmtArg = (argv[4] as string).replace(/^--format-json=/, '');
283
284
  const parsed = JSON.parse(fmtArg);
284
285
  expect(parsed.textFormat.bold).toBe(true);
@@ -366,7 +367,7 @@ describe('gog_sheets_list_tabs', () => {
366
367
  const harness = await setupHandlers();
367
368
  await harness.callTool('gog_sheets_list_tabs', { spreadsheetId: 'sid' });
368
369
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
369
- ['sheets', 'metadata', 'sid', '--select=sheets.properties.sheetId,sheets.properties.title,sheets.properties.index,sheets.properties.gridProperties'],
370
+ ['sheets', 'metadata', pos('sid'), '--select=sheets.properties.sheetId,sheets.properties.title,sheets.properties.index,sheets.properties.gridProperties'],
370
371
  { account: undefined },
371
372
  );
372
373
  });
@@ -378,14 +379,14 @@ describe('gog_sheets_number_format', () => {
378
379
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
379
380
  const harness = await setupHandlers();
380
381
  await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A10' });
381
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'number-format', 'sid', 'A1:A10'], { account: undefined });
382
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'number-format', pos('sid'), pos('A1:A10')], { account: undefined });
382
383
  });
383
384
 
384
385
  it('includes --type and --pattern when provided', async () => {
385
386
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
386
387
  const harness = await setupHandlers();
387
388
  await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1', type: 'CURRENCY', pattern: '#,##0.00' });
388
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'number-format', 'sid', 'A1', '--type=CURRENCY', '--pattern=#,##0.00'], { account: undefined });
389
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'number-format', pos('sid'), pos('A1'), '--type=CURRENCY', '--pattern=#,##0.00'], { account: undefined });
389
390
  });
390
391
 
391
392
  // Issue #43: warn when DATE/DATE_TIME format applied to small integers (silent 1899/1900 render).
@@ -401,12 +402,12 @@ describe('gog_sheets_number_format', () => {
401
402
  const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'Sheet1!A1:A3', type: 'DATE' });
402
403
  // Peek call goes out first.
403
404
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
404
- ['sheets', 'get', 'sid', 'Sheet1!A1:A3', '--render=UNFORMATTED_VALUE'],
405
+ ['sheets', 'get', pos('sid'), pos('Sheet1!A1:A3'), '--render=UNFORMATTED_VALUE'],
405
406
  { account: undefined },
406
407
  );
407
408
  // The format call still happens (warning is non-blocking).
408
409
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
409
- ['sheets', 'number-format', 'sid', 'Sheet1!A1:A3', '--type=DATE'],
410
+ ['sheets', 'number-format', pos('sid'), pos('Sheet1!A1:A3'), '--type=DATE'],
410
411
  { account: undefined },
411
412
  );
412
413
  const text = result.content[0].text;
@@ -434,7 +435,7 @@ describe('gog_sheets_number_format', () => {
434
435
  // Only the format call should fire — no peek.
435
436
  expect(lib.runOrDiagnose).toHaveBeenCalledTimes(1);
436
437
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
437
- ['sheets', 'number-format', 'sid', 'Sheet1!A1:A3', '--type=DATE'],
438
+ ['sheets', 'number-format', pos('sid'), pos('Sheet1!A1:A3'), '--type=DATE'],
438
439
  { account: undefined },
439
440
  );
440
441
  expect(result.content[0].text).not.toMatch(/warning/i);
@@ -459,7 +460,7 @@ describe('gog_sheets_number_format', () => {
459
460
  await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'CURRENCY' });
460
461
  expect(lib.runOrDiagnose).toHaveBeenCalledTimes(1);
461
462
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
462
- ['sheets', 'number-format', 'sid', 'A1:A3', '--type=CURRENCY'],
463
+ ['sheets', 'number-format', pos('sid'), pos('A1:A3'), '--type=CURRENCY'],
463
464
  { account: undefined },
464
465
  );
465
466
  });
@@ -573,7 +574,7 @@ describe('gog_sheets_number_format', () => {
573
574
  const result = await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1:A3', type: 'DATE' });
574
575
  // Format call should still happen.
575
576
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
576
- ['sheets', 'number-format', 'sid', 'A1:A3', '--type=DATE'],
577
+ ['sheets', 'number-format', pos('sid'), pos('A1:A3'), '--type=DATE'],
577
578
  { account: undefined },
578
579
  );
579
580
  // No warning emitted when peek didn't yield clear evidence.
@@ -590,11 +591,11 @@ describe('gog_sheets_number_format', () => {
590
591
  });
591
592
  await harness.callTool('gog_sheets_number_format', { spreadsheetId: 'sid', range: 'A1', type: 'DATE', account: 'a@b.com' });
592
593
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
593
- ['sheets', 'get', 'sid', 'A1', '--render=UNFORMATTED_VALUE'],
594
+ ['sheets', 'get', pos('sid'), pos('A1'), '--render=UNFORMATTED_VALUE'],
594
595
  { account: 'a@b.com' },
595
596
  );
596
597
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
597
- ['sheets', 'number-format', 'sid', 'A1', '--type=DATE'],
598
+ ['sheets', 'number-format', pos('sid'), pos('A1'), '--type=DATE'],
598
599
  { account: 'a@b.com' },
599
600
  );
600
601
  });
@@ -607,21 +608,21 @@ describe('gog_sheets_read_format', () => {
607
608
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
608
609
  const harness = await setupHandlers();
609
610
  await harness.callTool('gog_sheets_read_format', { spreadsheetId: 'sid', range: 'A1:B2' });
610
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', 'sid', 'A1:B2'], { account: undefined });
611
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', pos('sid'), pos('A1:B2')], { account: undefined });
611
612
  });
612
613
 
613
614
  it('includes --effective when true', async () => {
614
615
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
615
616
  const harness = await setupHandlers();
616
617
  await harness.callTool('gog_sheets_read_format', { spreadsheetId: 'sid', range: 'A1', effective: true });
617
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', 'sid', 'A1', '--effective'], { account: undefined });
618
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', pos('sid'), pos('A1'), '--effective'], { account: undefined });
618
619
  });
619
620
 
620
621
  it('omits --effective when false', async () => {
621
622
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
622
623
  const harness = await setupHandlers();
623
624
  await harness.callTool('gog_sheets_read_format', { spreadsheetId: 'sid', range: 'A1', effective: false });
624
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', 'sid', 'A1'], { account: undefined });
625
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'read-format', pos('sid'), pos('A1')], { account: undefined });
625
626
  });
626
627
  });
627
628
 
@@ -631,21 +632,21 @@ describe('gog_sheets_resize_columns', () => {
631
632
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
632
633
  const harness = await setupHandlers();
633
634
  await harness.callTool('gog_sheets_resize_columns', { spreadsheetId: 'sid', columns: 'A:C' });
634
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', 'sid', 'A:C'], { account: undefined });
635
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', pos('sid'), pos('A:C')], { account: undefined });
635
636
  });
636
637
 
637
638
  it('includes --width and --auto when provided', async () => {
638
639
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
639
640
  const harness = await setupHandlers();
640
641
  await harness.callTool('gog_sheets_resize_columns', { spreadsheetId: 'sid', columns: 'A:C', width: 200, auto: true });
641
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', 'sid', 'A:C', '--width=200', '--auto'], { account: undefined });
642
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', pos('sid'), pos('A:C'), '--width=200', '--auto'], { account: undefined });
642
643
  });
643
644
 
644
645
  it('includes --width=0 when width is 0', async () => {
645
646
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
646
647
  const harness = await setupHandlers();
647
648
  await harness.callTool('gog_sheets_resize_columns', { spreadsheetId: 'sid', columns: 'A:A', width: 0 });
648
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', 'sid', 'A:A', '--width=0'], { account: undefined });
649
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-columns', pos('sid'), pos('A:A'), '--width=0'], { account: undefined });
649
650
  });
650
651
  });
651
652
 
@@ -655,21 +656,21 @@ describe('gog_sheets_resize_rows', () => {
655
656
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
656
657
  const harness = await setupHandlers();
657
658
  await harness.callTool('gog_sheets_resize_rows', { spreadsheetId: 'sid', rows: '1:10' });
658
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', 'sid', '1:10'], { account: undefined });
659
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', pos('sid'), pos('1:10')], { account: undefined });
659
660
  });
660
661
 
661
662
  it('includes --height and --auto when provided', async () => {
662
663
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
663
664
  const harness = await setupHandlers();
664
665
  await harness.callTool('gog_sheets_resize_rows', { spreadsheetId: 'sid', rows: '1:5', height: 40, auto: true });
665
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', 'sid', '1:5', '--height=40', '--auto'], { account: undefined });
666
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', pos('sid'), pos('1:5'), '--height=40', '--auto'], { account: undefined });
666
667
  });
667
668
 
668
669
  it('includes --height=0 when height is 0', async () => {
669
670
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
670
671
  const harness = await setupHandlers();
671
672
  await harness.callTool('gog_sheets_resize_rows', { spreadsheetId: 'sid', rows: '1:1', height: 0 });
672
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', 'sid', '1:1', '--height=0'], { account: undefined });
673
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'resize-rows', pos('sid'), pos('1:1'), '--height=0'], { account: undefined });
673
674
  });
674
675
  });
675
676
 
@@ -679,7 +680,7 @@ describe('gog_sheets_notes', () => {
679
680
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
680
681
  const harness = await setupHandlers();
681
682
  await harness.callTool('gog_sheets_notes', { spreadsheetId: 'sid', range: 'A1:B5' });
682
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'notes', 'sid', 'A1:B5'], { account: undefined });
683
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'notes', pos('sid'), pos('A1:B5')], { account: undefined });
683
684
  });
684
685
  });
685
686
 
@@ -689,14 +690,14 @@ describe('gog_sheets_update_note', () => {
689
690
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
690
691
  const harness = await setupHandlers();
691
692
  await harness.callTool('gog_sheets_update_note', { spreadsheetId: 'sid', range: 'A1', note: 'Hello' });
692
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'update-note', 'sid', 'A1', '--note=Hello'], { account: undefined });
693
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'update-note', pos('sid'), pos('A1'), '--note=Hello'], { account: undefined });
693
694
  });
694
695
 
695
696
  it('passes empty string to clear note', async () => {
696
697
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
697
698
  const harness = await setupHandlers();
698
699
  await harness.callTool('gog_sheets_update_note', { spreadsheetId: 'sid', range: 'A1', note: '' });
699
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'update-note', 'sid', 'A1', '--note='], { account: undefined });
700
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'update-note', pos('sid'), pos('A1'), '--note='], { account: undefined });
700
701
  });
701
702
 
702
703
  it('spills a large note to a --note-file payload arg', async () => {
@@ -705,7 +706,7 @@ describe('gog_sheets_update_note', () => {
705
706
  const big = 'n'.repeat(lib.PAYLOAD_INLINE_MAX + 1);
706
707
  await harness.callTool('gog_sheets_update_note', { spreadsheetId: 'sid', range: 'A1', note: big });
707
708
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
708
- ['sheets', 'update-note', 'sid', 'A1',
709
+ ['sheets', 'update-note', pos('sid'), pos('A1'),
709
710
  { kind: 'file', flag: 'note-file', contents: big, ext: undefined }],
710
711
  { account: undefined },
711
712
  );
@@ -718,7 +719,7 @@ describe('gog_sheets_links', () => {
718
719
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
719
720
  const harness = await setupHandlers();
720
721
  await harness.callTool('gog_sheets_links', { spreadsheetId: 'sid', range: 'A1:Z100' });
721
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'links', 'sid', 'A1:Z100'], { account: undefined });
722
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'links', pos('sid'), pos('A1:Z100')], { account: undefined });
722
723
  });
723
724
  });
724
725
 
@@ -729,7 +730,7 @@ describe('gog_sheets_links_set', () => {
729
730
  const harness = await setupHandlers();
730
731
  await harness.callTool('gog_sheets_links_set', { spreadsheetId: 'sid', cell: 'A1', url: 'https://x.com', text: 'Link' });
731
732
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
732
- ['sheets', 'links', 'set', 'sid', 'A1', 'https://x.com', 'Link'],
733
+ ['sheets', 'links', 'set', pos('sid'), pos('A1'), pos('https://x.com'), pos('Link')],
733
734
  { account: undefined },
734
735
  );
735
736
  });
@@ -739,7 +740,7 @@ describe('gog_sheets_links_set', () => {
739
740
  const harness = await setupHandlers();
740
741
  await harness.callTool('gog_sheets_links_set', { spreadsheetId: 'sid', cell: 'B2', runsJson: '[{"text":"A","uri":"https://a"}]' });
741
742
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
742
- ['sheets', 'links', 'set', 'sid', 'B2', '--runs-json=[{"text":"A","uri":"https://a"}]'],
743
+ ['sheets', 'links', 'set', pos('sid'), pos('B2'), '--runs-json=[{"text":"A","uri":"https://a"}]'],
743
744
  { account: undefined },
744
745
  );
745
746
  });
@@ -749,7 +750,7 @@ describe('gog_sheets_links_set', () => {
749
750
  const harness = await setupHandlers();
750
751
  await harness.callTool('gog_sheets_links_set', { spreadsheetId: 'sid', cellsJson: '[{"cell":"A1","url":"https://a"}]' });
751
752
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
752
- ['sheets', 'links', 'set', 'sid', '--cells-json=[{"cell":"A1","url":"https://a"}]'],
753
+ ['sheets', 'links', 'set', pos('sid'), '--cells-json=[{"cell":"A1","url":"https://a"}]'],
753
754
  { account: undefined },
754
755
  );
755
756
  });
@@ -764,7 +765,7 @@ describe('gog_sheets_links_set', () => {
764
765
  const harness = await setupHandlers();
765
766
  await harness.callTool('gog_sheets_links_set', { spreadsheetId: 'sid', cellsJson: '@/tmp/cells.json' });
766
767
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
767
- ['sheets', 'links', 'set', 'sid', '--cells-json=@/tmp/cells.json'],
768
+ ['sheets', 'links', 'set', pos('sid'), '--cells-json=@/tmp/cells.json'],
768
769
  { account: undefined },
769
770
  );
770
771
  });
@@ -788,7 +789,7 @@ describe('gog_sheets_named_ranges_list', () => {
788
789
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
789
790
  const harness = await setupHandlers();
790
791
  await harness.callTool('gog_sheets_named_ranges_list', { spreadsheetId: 'sid' });
791
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'list', 'sid'], { account: undefined });
792
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'list', pos('sid')], { account: undefined });
792
793
  });
793
794
  });
794
795
 
@@ -798,7 +799,7 @@ describe('gog_sheets_named_ranges_get', () => {
798
799
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
799
800
  const harness = await setupHandlers();
800
801
  await harness.callTool('gog_sheets_named_ranges_get', { spreadsheetId: 'sid', nameOrId: 'MyRange' });
801
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'get', 'sid', 'MyRange'], { account: undefined });
802
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'get', pos('sid'), pos('MyRange')], { account: undefined });
802
803
  });
803
804
  });
804
805
 
@@ -808,7 +809,7 @@ describe('gog_sheets_named_ranges_add', () => {
808
809
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
809
810
  const harness = await setupHandlers();
810
811
  await harness.callTool('gog_sheets_named_ranges_add', { spreadsheetId: 'sid', name: 'Totals', range: 'Sheet1!A1:B10' });
811
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'add', 'sid', 'Totals', 'Sheet1!A1:B10'], { account: undefined });
812
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'add', pos('sid'), pos('Totals'), pos('Sheet1!A1:B10')], { account: undefined });
812
813
  });
813
814
  });
814
815
 
@@ -818,14 +819,14 @@ describe('gog_sheets_named_ranges_update', () => {
818
819
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
819
820
  const harness = await setupHandlers();
820
821
  await harness.callTool('gog_sheets_named_ranges_update', { spreadsheetId: 'sid', nameOrId: 'MyRange' });
821
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'update', 'sid', 'MyRange'], { account: undefined });
822
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'update', pos('sid'), pos('MyRange')], { account: undefined });
822
823
  });
823
824
 
824
825
  it('includes --name and --range when provided', async () => {
825
826
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
826
827
  const harness = await setupHandlers();
827
828
  await harness.callTool('gog_sheets_named_ranges_update', { spreadsheetId: 'sid', nameOrId: 'MyRange', name: 'NewName', range: 'Sheet1!C1:D5' });
828
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'update', 'sid', 'MyRange', '--name=NewName', '--range=Sheet1!C1:D5'], { account: undefined });
829
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'update', pos('sid'), pos('MyRange'), '--name=NewName', '--range=Sheet1!C1:D5'], { account: undefined });
829
830
  });
830
831
  });
831
832
 
@@ -835,7 +836,7 @@ describe('gog_sheets_named_ranges_delete', () => {
835
836
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
836
837
  const harness = await setupHandlers();
837
838
  await harness.callTool('gog_sheets_named_ranges_delete', { spreadsheetId: 'sid', nameOrId: 'OldRange' });
838
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'delete', 'sid', 'OldRange'], { account: undefined });
839
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'named-ranges', 'delete', pos('sid'), pos('OldRange')], { account: undefined });
839
840
  });
840
841
  });
841
842
 
@@ -847,7 +848,7 @@ describe('gog_sheets_batch_update', () => {
847
848
  const data = '[{"range":"Sheet1!A1:B1","values":[["a","b"]]}]';
848
849
  await harness.callTool('gog_sheets_batch_update', { spreadsheetId: 'sid', dataJson: data });
849
850
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
850
- ['sheets', 'batch-update', 'sid', `--data-json=${data}`],
851
+ ['sheets', 'batch-update', pos('sid'), `--data-json=${data}`],
851
852
  { account: undefined },
852
853
  );
853
854
  });
@@ -866,7 +867,7 @@ describe('gog_sheets_batch_update', () => {
866
867
  });
867
868
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
868
869
  [
869
- 'sheets', 'batch-update', 'sid',
870
+ 'sheets', 'batch-update', pos('sid'),
870
871
  '--data-json=[]',
871
872
  '--input=RAW',
872
873
  '--include-values-in-response',
@@ -884,7 +885,7 @@ describe('gog_sheets_batch_update', () => {
884
885
  spreadsheetId: 'sid', dataJson: '[]', includeValuesInResponse: false,
885
886
  });
886
887
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
887
- ['sheets', 'batch-update', 'sid', '--data-json=[]'],
888
+ ['sheets', 'batch-update', pos('sid'), '--data-json=[]'],
888
889
  { account: undefined },
889
890
  );
890
891
  });
@@ -897,7 +898,7 @@ describe('gog_sheets_reorder_tab', () => {
897
898
  const harness = await setupHandlers();
898
899
  await harness.callTool('gog_sheets_reorder_tab', { spreadsheetId: 'sid', tab: 'Data', to: 2 });
899
900
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
900
- ['sheets', 'reorder-tab', 'sid', '--tab=Data', '--to=2'],
901
+ ['sheets', 'reorder-tab', pos('sid'), '--tab=Data', '--to=2'],
901
902
  { account: undefined },
902
903
  );
903
904
  });
@@ -907,7 +908,7 @@ describe('gog_sheets_reorder_tab', () => {
907
908
  const harness = await setupHandlers();
908
909
  await harness.callTool('gog_sheets_reorder_tab', { spreadsheetId: 'sid', tab: '123', to: 0 });
909
910
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
910
- ['sheets', 'reorder-tab', 'sid', '--tab=123', '--to=0'],
911
+ ['sheets', 'reorder-tab', pos('sid'), '--tab=123', '--to=0'],
911
912
  { account: undefined },
912
913
  );
913
914
  });
@@ -917,7 +918,7 @@ describe('gog_sheets_reorder_tab', () => {
917
918
  const harness = await setupHandlers();
918
919
  await harness.callTool('gog_sheets_reorder_tab', { spreadsheetId: 'sid', tab: 'Data', to: 1, account: 'a@b.com' });
919
920
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
920
- ['sheets', 'reorder-tab', 'sid', '--tab=Data', '--to=1'],
921
+ ['sheets', 'reorder-tab', pos('sid'), '--tab=Data', '--to=1'],
921
922
  { account: 'a@b.com' },
922
923
  );
923
924
  });
@@ -929,14 +930,14 @@ describe('gog_sheets_chart_list', () => {
929
930
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
930
931
  const harness = await setupHandlers();
931
932
  await harness.callTool('gog_sheets_chart_list', { spreadsheetId: 'sid' });
932
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: undefined });
933
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', pos('sid')], { account: undefined });
933
934
  });
934
935
 
935
936
  it('forwards account', async () => {
936
937
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
937
938
  const harness = await setupHandlers();
938
939
  await harness.callTool('gog_sheets_chart_list', { spreadsheetId: 'sid', account: 'a@b.com' });
939
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', 'sid'], { account: 'a@b.com' });
940
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'list', pos('sid')], { account: 'a@b.com' });
940
941
  });
941
942
  });
942
943
 
@@ -946,7 +947,7 @@ describe('gog_sheets_chart_get', () => {
946
947
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
947
948
  const harness = await setupHandlers();
948
949
  await harness.callTool('gog_sheets_chart_get', { spreadsheetId: 'sid', chartId: '12345' });
949
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'get', 'sid', '12345'], { account: undefined });
950
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'get', pos('sid'), pos('12345')], { account: undefined });
950
951
  });
951
952
  });
952
953
 
@@ -958,7 +959,7 @@ describe('gog_sheets_chart_create', () => {
958
959
  const spec = '{"basicChart":{"chartType":"COLUMN"}}';
959
960
  await harness.callTool('gog_sheets_chart_create', { spreadsheetId: 'sid', specJson: spec });
960
961
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
961
- ['sheets', 'chart', 'create', 'sid', `--spec-json=${spec}`],
962
+ ['sheets', 'chart', 'create', pos('sid'), `--spec-json=${spec}`],
962
963
  { account: undefined },
963
964
  );
964
965
  });
@@ -970,7 +971,7 @@ describe('gog_sheets_chart_create', () => {
970
971
  spreadsheetId: 'sid', specJson: '{}', sheet: 'Data', anchor: 'E10', width: 800, height: 400,
971
972
  });
972
973
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
973
- ['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--sheet=Data', '--anchor=E10', '--width=800', '--height=400'],
974
+ ['sheets', 'chart', 'create', pos('sid'), '--spec-json={}', '--sheet=Data', '--anchor=E10', '--width=800', '--height=400'],
974
975
  { account: undefined },
975
976
  );
976
977
  });
@@ -980,7 +981,7 @@ describe('gog_sheets_chart_create', () => {
980
981
  const harness = await setupHandlers();
981
982
  await harness.callTool('gog_sheets_chart_create', { spreadsheetId: 'sid', specJson: '{}', width: 0, height: 0 });
982
983
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
983
- ['sheets', 'chart', 'create', 'sid', '--spec-json={}', '--width=0', '--height=0'],
984
+ ['sheets', 'chart', 'create', pos('sid'), '--spec-json={}', '--width=0', '--height=0'],
984
985
  { account: undefined },
985
986
  );
986
987
  });
@@ -993,7 +994,7 @@ describe('gog_sheets_chart_update', () => {
993
994
  const harness = await setupHandlers();
994
995
  await harness.callTool('gog_sheets_chart_update', { spreadsheetId: 'sid', chartId: '99', specJson: '{}' });
995
996
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
996
- ['sheets', 'chart', 'update', 'sid', '99', '--spec-json={}'],
997
+ ['sheets', 'chart', 'update', pos('sid'), pos('99'), '--spec-json={}'],
997
998
  { account: undefined },
998
999
  );
999
1000
  });
@@ -1005,7 +1006,7 @@ describe('gog_sheets_chart_delete', () => {
1005
1006
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1006
1007
  const harness = await setupHandlers();
1007
1008
  await harness.callTool('gog_sheets_chart_delete', { spreadsheetId: 'sid', chartId: '7' });
1008
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'delete', 'sid', '7', '--force'], { account: undefined });
1009
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'chart', 'delete', pos('sid'), pos('7'), '--force'], { account: undefined });
1009
1010
  });
1010
1011
  });
1011
1012
 
@@ -1015,7 +1016,7 @@ describe('gog_sheets_table_list', () => {
1015
1016
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1016
1017
  const harness = await setupHandlers();
1017
1018
  await harness.callTool('gog_sheets_table_list', { spreadsheetId: 'sid' });
1018
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'list', 'sid'], { account: undefined });
1019
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'list', pos('sid')], { account: undefined });
1019
1020
  });
1020
1021
  });
1021
1022
 
@@ -1025,7 +1026,7 @@ describe('gog_sheets_table_get', () => {
1025
1026
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1026
1027
  const harness = await setupHandlers();
1027
1028
  await harness.callTool('gog_sheets_table_get', { spreadsheetId: 'sid', tableId: 't1' });
1028
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'get', 'sid', 't1'], { account: undefined });
1029
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'get', pos('sid'), pos('t1')], { account: undefined });
1029
1030
  });
1030
1031
  });
1031
1032
 
@@ -1037,7 +1038,7 @@ describe('gog_sheets_table_create', () => {
1037
1038
  const cols = '[{"columnName":"Name","columnType":"TEXT"}]';
1038
1039
  await harness.callTool('gog_sheets_table_create', { spreadsheetId: 'sid', range: 'Sheet1!A1:B10', name: 'People', columnsJson: cols });
1039
1040
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1040
- ['sheets', 'table', 'create', 'sid', 'Sheet1!A1:B10', '--name=People', `--columns-json=${cols}`],
1041
+ ['sheets', 'table', 'create', pos('sid'), pos('Sheet1!A1:B10'), '--name=People', `--columns-json=${cols}`],
1041
1042
  { account: undefined },
1042
1043
  );
1043
1044
  });
@@ -1050,7 +1051,7 @@ describe('gog_sheets_table_append', () => {
1050
1051
  const harness = await setupHandlers();
1051
1052
  await harness.callTool('gog_sheets_table_append', { spreadsheetId: 'sid', tableId: 't1', valuesJson: '[["a",1]]' });
1052
1053
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1053
- ['sheets', 'table', 'append', 'sid', 't1', '--values-json=[["a",1]]'],
1054
+ ['sheets', 'table', 'append', pos('sid'), pos('t1'), '--values-json=[["a",1]]'],
1054
1055
  { account: undefined },
1055
1056
  );
1056
1057
  });
@@ -1060,7 +1061,7 @@ describe('gog_sheets_table_append', () => {
1060
1061
  const harness = await setupHandlers();
1061
1062
  await harness.callTool('gog_sheets_table_append', { spreadsheetId: 'sid', tableId: 't1', valuesJson: '[]', input: 'RAW' });
1062
1063
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1063
- ['sheets', 'table', 'append', 'sid', 't1', '--values-json=[]', '--input=RAW'],
1064
+ ['sheets', 'table', 'append', pos('sid'), pos('t1'), '--values-json=[]', '--input=RAW'],
1064
1065
  { account: undefined },
1065
1066
  );
1066
1067
  });
@@ -1072,7 +1073,7 @@ describe('gog_sheets_table_clear', () => {
1072
1073
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1073
1074
  const harness = await setupHandlers();
1074
1075
  await harness.callTool('gog_sheets_table_clear', { spreadsheetId: 'sid', tableId: 't1' });
1075
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'clear', 'sid', 't1', '--force'], { account: undefined });
1076
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'clear', pos('sid'), pos('t1'), '--force'], { account: undefined });
1076
1077
  });
1077
1078
  });
1078
1079
 
@@ -1082,7 +1083,7 @@ describe('gog_sheets_table_delete', () => {
1082
1083
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1083
1084
  const harness = await setupHandlers();
1084
1085
  await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1', keep_data: false });
1085
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'delete', 'sid', 't1', '--discard-data', '--force'], { account: undefined });
1086
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'table', 'delete', pos('sid'), pos('t1'), '--discard-data', '--force'], { account: undefined });
1086
1087
  expect(lib.run).not.toHaveBeenCalled();
1087
1088
  });
1088
1089
 
@@ -1094,10 +1095,10 @@ describe('gog_sheets_table_delete', () => {
1094
1095
  .mockResolvedValueOnce('{"updatedRows":2}'); // update restore
1095
1096
  const harness = await setupHandlers();
1096
1097
  const res = await harness.callTool('gog_sheets_table_delete', { spreadsheetId: 'sid', tableId: 't1', account: 'a@b.com' });
1097
- expect(lib.run).toHaveBeenNthCalledWith(1, ['sheets', 'table', 'get', 'sid', 't1'], { account: 'a@b.com' });
1098
- expect(lib.run).toHaveBeenNthCalledWith(2, ['sheets', 'get', 'sid', 'Sheet1!A1:C2', '--render=FORMULA'], { account: 'a@b.com' });
1099
- expect(lib.run).toHaveBeenNthCalledWith(3, ['sheets', 'table', 'delete', 'sid', 't1', '--discard-data', '--force'], { account: 'a@b.com' });
1100
- expect(lib.run).toHaveBeenNthCalledWith(4, ['sheets', 'update', 'sid', 'Sheet1!A1:C2', '--values-json=[["Name","City","N"],["A","NYC","=1+1"]]'], { account: 'a@b.com' });
1098
+ expect(lib.run).toHaveBeenNthCalledWith(1, ['sheets', 'table', 'get', pos('sid'), pos('t1')], { account: 'a@b.com' });
1099
+ expect(lib.run).toHaveBeenNthCalledWith(2, ['sheets', 'get', pos('sid'), pos('Sheet1!A1:C2'), '--render=FORMULA'], { account: 'a@b.com' });
1100
+ expect(lib.run).toHaveBeenNthCalledWith(3, ['sheets', 'table', 'delete', pos('sid'), pos('t1'), '--discard-data', '--force'], { account: 'a@b.com' });
1101
+ expect(lib.run).toHaveBeenNthCalledWith(4, ['sheets', 'update', pos('sid'), pos('Sheet1!A1:C2'), '--values-json=[["Name","City","N"],["A","NYC","=1+1"]]'], { account: 'a@b.com' });
1101
1102
  expect(res.content[0].text).toContain('2 row');
1102
1103
  expect(res.content[0].text).toContain('Sheet1!A1:C2');
1103
1104
  });
@@ -1163,14 +1164,14 @@ describe('gog_sheets_banding_list', () => {
1163
1164
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1164
1165
  const harness = await setupHandlers();
1165
1166
  await harness.callTool('gog_sheets_banding_list', { spreadsheetId: 'sid' });
1166
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid'], { account: undefined });
1167
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', pos('sid')], { account: undefined });
1167
1168
  });
1168
1169
 
1169
1170
  it('includes --sheet when provided', async () => {
1170
1171
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1171
1172
  const harness = await setupHandlers();
1172
1173
  await harness.callTool('gog_sheets_banding_list', { spreadsheetId: 'sid', sheet: 'Data' });
1173
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', 'sid', '--sheet=Data'], { account: undefined });
1174
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'list', pos('sid'), '--sheet=Data'], { account: undefined });
1174
1175
  });
1175
1176
  });
1176
1177
 
@@ -1180,7 +1181,7 @@ describe('gog_sheets_banding_set', () => {
1180
1181
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1181
1182
  const harness = await setupHandlers();
1182
1183
  await harness.callTool('gog_sheets_banding_set', { spreadsheetId: 'sid', range: 'Sheet1!A1:D20' });
1183
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'set', 'sid', 'Sheet1!A1:D20'], { account: undefined });
1184
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'set', pos('sid'), pos('Sheet1!A1:D20')], { account: undefined });
1184
1185
  });
1185
1186
 
1186
1187
  it('includes --row-properties-json and --column-properties-json when provided', async () => {
@@ -1192,7 +1193,7 @@ describe('gog_sheets_banding_set', () => {
1192
1193
  columnPropertiesJson: '{"secondBandColor":{}}',
1193
1194
  });
1194
1195
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1195
- ['sheets', 'banding', 'set', 'sid', 'A1:D20', '--row-properties-json={"firstBandColor":{}}', '--column-properties-json={"secondBandColor":{}}'],
1196
+ ['sheets', 'banding', 'set', pos('sid'), pos('A1:D20'), '--row-properties-json={"firstBandColor":{}}', '--column-properties-json={"secondBandColor":{}}'],
1196
1197
  { account: undefined },
1197
1198
  );
1198
1199
  });
@@ -1204,28 +1205,28 @@ describe('gog_sheets_banding_clear', () => {
1204
1205
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1205
1206
  const harness = await setupHandlers();
1206
1207
  await harness.callTool('gog_sheets_banding_clear', { spreadsheetId: 'sid' });
1207
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--force'], { account: undefined });
1208
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', pos('sid'), '--force'], { account: undefined });
1208
1209
  });
1209
1210
 
1210
1211
  it('includes --id when provided (including 0)', async () => {
1211
1212
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1212
1213
  const harness = await setupHandlers();
1213
1214
  await harness.callTool('gog_sheets_banding_clear', { spreadsheetId: 'sid', id: 0 });
1214
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=0', '--force'], { account: undefined });
1215
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', pos('sid'), '--id=0', '--force'], { account: undefined });
1215
1216
  });
1216
1217
 
1217
1218
  it('includes --all and --sheet when clearing a whole sheet', async () => {
1218
1219
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1219
1220
  const harness = await setupHandlers();
1220
1221
  await harness.callTool('gog_sheets_banding_clear', { spreadsheetId: 'sid', all: true, sheet: 'Data' });
1221
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--all', '--sheet=Data', '--force'], { account: undefined });
1222
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', pos('sid'), '--all', '--sheet=Data', '--force'], { account: undefined });
1222
1223
  });
1223
1224
 
1224
1225
  it('omits --all when false', async () => {
1225
1226
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1226
1227
  const harness = await setupHandlers();
1227
1228
  await harness.callTool('gog_sheets_banding_clear', { spreadsheetId: 'sid', all: false, id: 5 });
1228
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', 'sid', '--id=5', '--force'], { account: undefined });
1229
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'banding', 'clear', pos('sid'), '--id=5', '--force'], { account: undefined });
1229
1230
  });
1230
1231
  });
1231
1232
 
@@ -1235,14 +1236,14 @@ describe('gog_sheets_conditional_format_list', () => {
1235
1236
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1236
1237
  const harness = await setupHandlers();
1237
1238
  await harness.callTool('gog_sheets_conditional_format_list', { spreadsheetId: 'sid' });
1238
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid'], { account: undefined });
1239
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', pos('sid')], { account: undefined });
1239
1240
  });
1240
1241
 
1241
1242
  it('includes --sheet when provided', async () => {
1242
1243
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1243
1244
  const harness = await setupHandlers();
1244
1245
  await harness.callTool('gog_sheets_conditional_format_list', { spreadsheetId: 'sid', sheet: 'Data' });
1245
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', 'sid', '--sheet=Data'], { account: undefined });
1246
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'list', pos('sid'), '--sheet=Data'], { account: undefined });
1246
1247
  });
1247
1248
  });
1248
1249
 
@@ -1255,7 +1256,7 @@ describe('gog_sheets_conditional_format_add', () => {
1255
1256
  spreadsheetId: 'sid', range: 'A1:A100', type: 'not-blank', formatJson: '{"backgroundColor":{}}',
1256
1257
  });
1257
1258
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1258
- ['sheets', 'conditional-format', 'add', 'sid', 'A1:A100', '--type=not-blank', '--format-json={"backgroundColor":{}}'],
1259
+ ['sheets', 'conditional-format', 'add', pos('sid'), pos('A1:A100'), '--type=not-blank', '--format-json={"backgroundColor":{}}'],
1259
1260
  { account: undefined },
1260
1261
  );
1261
1262
  });
@@ -1267,7 +1268,7 @@ describe('gog_sheets_conditional_format_add', () => {
1267
1268
  spreadsheetId: 'sid', range: 'B1:B50', type: 'number-gt', expr: '100', formatJson: '{}', formatFields: 'backgroundColor,textFormat.bold',
1268
1269
  });
1269
1270
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1270
- ['sheets', 'conditional-format', 'add', 'sid', 'B1:B50', '--type=number-gt', '--format-json={}', '--expr=100', '--format-fields=backgroundColor,textFormat.bold'],
1271
+ ['sheets', 'conditional-format', 'add', pos('sid'), pos('B1:B50'), '--type=number-gt', '--format-json={}', '--expr=100', '--format-fields=backgroundColor,textFormat.bold'],
1271
1272
  { account: undefined },
1272
1273
  );
1273
1274
  });
@@ -1279,7 +1280,7 @@ describe('gog_sheets_conditional_format_add', () => {
1279
1280
  spreadsheetId: 'sid', range: 'A1', type: 'text-eq', expr: '', formatJson: '{}',
1280
1281
  });
1281
1282
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1282
- ['sheets', 'conditional-format', 'add', 'sid', 'A1', '--type=text-eq', '--format-json={}', '--expr='],
1283
+ ['sheets', 'conditional-format', 'add', pos('sid'), pos('A1'), '--type=text-eq', '--format-json={}', '--expr='],
1283
1284
  { account: undefined },
1284
1285
  );
1285
1286
  });
@@ -1291,7 +1292,7 @@ describe('gog_sheets_conditional_format_add', () => {
1291
1292
  spreadsheetId: 'sid', range: 'Sheet1!A1:A100', gradientRuleJson: '{"minpoint":{},"maxpoint":{}}',
1292
1293
  });
1293
1294
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1294
- ['sheets', 'conditional-format', 'add', 'sid', 'Sheet1!A1:A100', '--gradient-rule-json={"minpoint":{},"maxpoint":{}}'],
1295
+ ['sheets', 'conditional-format', 'add', pos('sid'), pos('Sheet1!A1:A100'), '--gradient-rule-json={"minpoint":{},"maxpoint":{}}'],
1295
1296
  { account: undefined },
1296
1297
  );
1297
1298
  });
@@ -1304,7 +1305,7 @@ describe('gog_sheets_filter_set', () => {
1304
1305
  const harness = await setupHandlers();
1305
1306
  await harness.callTool('gog_sheets_filter_set', { spreadsheetId: 'sid', range: 'Sheet1!A1:C10' });
1306
1307
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1307
- ['sheets', 'filter', 'set', 'sid', 'Sheet1!A1:C10'],
1308
+ ['sheets', 'filter', 'set', pos('sid'), pos('Sheet1!A1:C10')],
1308
1309
  { account: undefined },
1309
1310
  );
1310
1311
  });
@@ -1314,7 +1315,7 @@ describe('gog_sheets_filter_set', () => {
1314
1315
  const harness = await setupHandlers();
1315
1316
  await harness.callTool('gog_sheets_filter_set', { spreadsheetId: 'sid', range: 'Sheet1!A1:C10', replace: true });
1316
1317
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1317
- ['sheets', 'filter', 'set', 'sid', 'Sheet1!A1:C10', '--force'],
1318
+ ['sheets', 'filter', 'set', pos('sid'), pos('Sheet1!A1:C10'), '--force'],
1318
1319
  { account: undefined },
1319
1320
  );
1320
1321
  });
@@ -1324,7 +1325,7 @@ describe('gog_sheets_filter_set', () => {
1324
1325
  const harness = await setupHandlers();
1325
1326
  await harness.callTool('gog_sheets_filter_set', { spreadsheetId: 'sid', range: 'Named', account: 'x@y.com' });
1326
1327
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1327
- ['sheets', 'filter', 'set', 'sid', 'Named'],
1328
+ ['sheets', 'filter', 'set', pos('sid'), pos('Named')],
1328
1329
  { account: 'x@y.com' },
1329
1330
  );
1330
1331
  });
@@ -1336,28 +1337,28 @@ describe('gog_sheets_conditional_format_clear', () => {
1336
1337
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1337
1338
  const harness = await setupHandlers();
1338
1339
  await harness.callTool('gog_sheets_conditional_format_clear', { spreadsheetId: 'sid', sheet: 'Data' });
1339
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--force'], { account: undefined });
1340
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', pos('sid'), '--sheet=Data', '--force'], { account: undefined });
1340
1341
  });
1341
1342
 
1342
1343
  it('includes --index when provided (including 0)', async () => {
1343
1344
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1344
1345
  const harness = await setupHandlers();
1345
1346
  await harness.callTool('gog_sheets_conditional_format_clear', { spreadsheetId: 'sid', sheet: 'Data', index: 0 });
1346
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=0', '--force'], { account: undefined });
1347
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', pos('sid'), '--sheet=Data', '--index=0', '--force'], { account: undefined });
1347
1348
  });
1348
1349
 
1349
1350
  it('includes --all when true', async () => {
1350
1351
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1351
1352
  const harness = await setupHandlers();
1352
1353
  await harness.callTool('gog_sheets_conditional_format_clear', { spreadsheetId: 'sid', sheet: 'Data', all: true });
1353
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--all', '--force'], { account: undefined });
1354
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', pos('sid'), '--sheet=Data', '--all', '--force'], { account: undefined });
1354
1355
  });
1355
1356
 
1356
1357
  it('omits --all when false', async () => {
1357
1358
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1358
1359
  const harness = await setupHandlers();
1359
1360
  await harness.callTool('gog_sheets_conditional_format_clear', { spreadsheetId: 'sid', sheet: 'Data', all: false, index: 2 });
1360
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', 'sid', '--sheet=Data', '--index=2', '--force'], { account: undefined });
1361
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'conditional-format', 'clear', pos('sid'), '--sheet=Data', '--index=2', '--force'], { account: undefined });
1361
1362
  });
1362
1363
  });
1363
1364
 
@@ -1367,14 +1368,14 @@ describe('gog_sheets_snapshot', () => {
1367
1368
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1368
1369
  const harness = await setupHandlers();
1369
1370
  await harness.callTool('gog_sheets_snapshot', { spreadsheetId: 'sid', name: 'Backup A' });
1370
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'copy', 'sid', 'Backup A'], { account: undefined });
1371
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'copy', pos('sid'), pos('Backup A')], { account: undefined });
1371
1372
  });
1372
1373
 
1373
1374
  it('includes --parent and forwards account', async () => {
1374
1375
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1375
1376
  const harness = await setupHandlers();
1376
1377
  await harness.callTool('gog_sheets_snapshot', { spreadsheetId: 'sid', name: 'Backup A', parent: 'folder1', account: 'a@b.com' });
1377
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'copy', 'sid', 'Backup A', '--parent=folder1'], { account: 'a@b.com' });
1378
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'copy', pos('sid'), pos('Backup A'), '--parent=folder1'], { account: 'a@b.com' });
1378
1379
  });
1379
1380
  });
1380
1381
 
@@ -1385,7 +1386,7 @@ describe('gog_sheets_validation_get', () => {
1385
1386
  const harness = await setupHandlers();
1386
1387
  await harness.callTool('gog_sheets_validation_get', { spreadsheetId: 'sid', range: 'Sheet1!A1:A10' });
1387
1388
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1388
- ['sheets', 'validation', 'get', 'sid', 'Sheet1!A1:A10'],
1389
+ ['sheets', 'validation', 'get', pos('sid'), pos('Sheet1!A1:A10')],
1389
1390
  { account: undefined },
1390
1391
  );
1391
1392
  });
@@ -1399,7 +1400,7 @@ describe('gog_sheets_validation_set', () => {
1399
1400
  spreadsheetId: 'sid', range: 'A1:A10', type: 'ONE_OF_LIST', values: ['Red', 'Green'],
1400
1401
  });
1401
1402
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1402
- ['sheets', 'validation', 'set', 'sid', 'A1:A10', '--type=ONE_OF_LIST', '--value=Red', '--value=Green'],
1403
+ ['sheets', 'validation', 'set', pos('sid'), pos('A1:A10'), '--type=ONE_OF_LIST', '--value=Red', '--value=Green'],
1403
1404
  { account: undefined },
1404
1405
  );
1405
1406
  });
@@ -1412,7 +1413,7 @@ describe('gog_sheets_validation_set', () => {
1412
1413
  strict: true, inputMessage: 'Pick 1-10', showCustomUi: true, filteredRowsIncluded: true,
1413
1414
  });
1414
1415
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1415
- ['sheets', 'validation', 'set', 'sid', 'A1', '--type=NUMBER_BETWEEN', '--value=1', '--value=10',
1416
+ ['sheets', 'validation', 'set', pos('sid'), pos('A1'), '--type=NUMBER_BETWEEN', '--value=1', '--value=10',
1416
1417
  '--strict', '--input-message=Pick 1-10', '--show-custom-ui', '--filtered-rows-included'],
1417
1418
  { account: undefined },
1418
1419
  );
@@ -1425,7 +1426,7 @@ describe('gog_sheets_validation_clear', () => {
1425
1426
  const harness = await setupHandlers();
1426
1427
  await harness.callTool('gog_sheets_validation_clear', { spreadsheetId: 'sid', range: 'A1:A10', filteredRowsIncluded: true });
1427
1428
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1428
- ['sheets', 'validation', 'clear', 'sid', 'A1:A10', '--filtered-rows-included'],
1429
+ ['sheets', 'validation', 'clear', pos('sid'), pos('A1:A10'), '--filtered-rows-included'],
1429
1430
  { account: undefined },
1430
1431
  );
1431
1432
  });
@@ -1439,7 +1440,7 @@ describe('gog_sheets_delete_dimension', () => {
1439
1440
  spreadsheetId: 'sid', rangeOrSheet: 'Sheet1', dimension: 'ROWS', start: 5, end: 7,
1440
1441
  });
1441
1442
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1442
- ['sheets', 'delete-dimension', 'sid', 'Sheet1', '--dimension=ROWS', '--start=5', '--end=7', '--force'],
1443
+ ['sheets', 'delete-dimension', pos('sid'), pos('Sheet1'), '--dimension=ROWS', '--start=5', '--end=7', '--force'],
1443
1444
  { account: undefined },
1444
1445
  );
1445
1446
  });
@@ -1451,7 +1452,7 @@ describe('gog_sheets_delete_dimension', () => {
1451
1452
  spreadsheetId: 'sid', rangeOrSheet: 'Sheet1!C:D', dimension: 'COLUMNS', account: 'a@b.com',
1452
1453
  });
1453
1454
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1454
- ['sheets', 'delete-dimension', 'sid', 'Sheet1!C:D', '--dimension=COLUMNS', '--force'],
1455
+ ['sheets', 'delete-dimension', pos('sid'), pos('Sheet1!C:D'), '--dimension=COLUMNS', '--force'],
1455
1456
  { account: 'a@b.com' },
1456
1457
  );
1457
1458
  });
@@ -1464,7 +1465,7 @@ describe('validation tools bare calls', () => {
1464
1465
  const harness = await setupHandlers();
1465
1466
  await harness.callTool('gog_sheets_validation_set', { spreadsheetId: 'sid', range: 'A1', type: 'BOOLEAN' });
1466
1467
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1467
- ['sheets', 'validation', 'set', 'sid', 'A1', '--type=BOOLEAN'],
1468
+ ['sheets', 'validation', 'set', pos('sid'), pos('A1'), '--type=BOOLEAN'],
1468
1469
  { account: undefined },
1469
1470
  );
1470
1471
  });
@@ -1474,7 +1475,7 @@ describe('validation tools bare calls', () => {
1474
1475
  const harness = await setupHandlers();
1475
1476
  await harness.callTool('gog_sheets_validation_clear', { spreadsheetId: 'sid', range: 'A1' });
1476
1477
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1477
- ['sheets', 'validation', 'clear', 'sid', 'A1'],
1478
+ ['sheets', 'validation', 'clear', pos('sid'), pos('A1')],
1478
1479
  { account: undefined },
1479
1480
  );
1480
1481
  });
@@ -1517,14 +1518,14 @@ describe('gog_sheets_datasource_list', () => {
1517
1518
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1518
1519
  const harness = await setupHandlers();
1519
1520
  await harness.callTool('gog_sheets_datasource_list', { spreadsheetId: 'sid' });
1520
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'datasource', 'list', 'sid'], { account: undefined });
1521
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'datasource', 'list', pos('sid')], { account: undefined });
1521
1522
  });
1522
1523
 
1523
1524
  it('forwards account', async () => {
1524
1525
  vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1525
1526
  const harness = await setupHandlers();
1526
1527
  await harness.callTool('gog_sheets_datasource_list', { spreadsheetId: 'sid', account: 'me@x.com' });
1527
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'datasource', 'list', 'sid'], { account: 'me@x.com' });
1528
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['sheets', 'datasource', 'list', pos('sid')], { account: 'me@x.com' });
1528
1529
  });
1529
1530
  });
1530
1531
 
@@ -1534,7 +1535,7 @@ describe('gog_sheets_datasource_describe', () => {
1534
1535
  const harness = await setupHandlers();
1535
1536
  await harness.callTool('gog_sheets_datasource_describe', { spreadsheetId: 'sid', dataSourceId: 'ds1' });
1536
1537
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1537
- ['sheets', 'datasource', 'describe', 'sid', 'ds1'],
1538
+ ['sheets', 'datasource', 'describe', pos('sid'), pos('ds1')],
1538
1539
  { account: undefined },
1539
1540
  );
1540
1541
  });
@@ -1546,7 +1547,7 @@ describe('gog_sheets_datasource_table_list', () => {
1546
1547
  const harness = await setupHandlers();
1547
1548
  await harness.callTool('gog_sheets_datasource_table_list', { spreadsheetId: 'sid' });
1548
1549
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1549
- ['sheets', 'datasource', 'table', 'list', 'sid'],
1550
+ ['sheets', 'datasource', 'table', 'list', pos('sid')],
1550
1551
  { account: undefined },
1551
1552
  );
1552
1553
  });
@@ -1556,7 +1557,7 @@ describe('gog_sheets_datasource_table_list', () => {
1556
1557
  const harness = await setupHandlers();
1557
1558
  await harness.callTool('gog_sheets_datasource_table_list', { spreadsheetId: 'sid', dataSourceId: 'ds1' });
1558
1559
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1559
- ['sheets', 'datasource', 'table', 'list', 'sid', '--data-source-id=ds1'],
1560
+ ['sheets', 'datasource', 'table', 'list', pos('sid'), '--data-source-id=ds1'],
1560
1561
  { account: undefined },
1561
1562
  );
1562
1563
  });
@@ -1568,7 +1569,7 @@ describe('gog_sheets_datasource_table_describe', () => {
1568
1569
  const harness = await setupHandlers();
1569
1570
  await harness.callTool('gog_sheets_datasource_table_describe', { spreadsheetId: 'sid', anchor: 'Extracts!B3' });
1570
1571
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1571
- ['sheets', 'datasource', 'table', 'describe', 'sid', 'Extracts!B3'],
1572
+ ['sheets', 'datasource', 'table', 'describe', pos('sid'), pos('Extracts!B3')],
1572
1573
  { account: undefined },
1573
1574
  );
1574
1575
  });
@@ -1580,7 +1581,7 @@ describe('gog_sheets_datasource_table_read', () => {
1580
1581
  const harness = await setupHandlers();
1581
1582
  await harness.callTool('gog_sheets_datasource_table_read', { spreadsheetId: 'sid', anchor: 'Extracts!B3' });
1582
1583
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1583
- ['sheets', 'datasource', 'table', 'read', 'sid', 'Extracts!B3'],
1584
+ ['sheets', 'datasource', 'table', 'read', pos('sid'), pos('Extracts!B3')],
1584
1585
  { account: undefined },
1585
1586
  );
1586
1587
  });
@@ -1592,7 +1593,7 @@ describe('gog_sheets_datasource_table_read', () => {
1592
1593
  spreadsheetId: 'sid', anchor: 'Extracts!B3', maxRows: 250, render: 'UNFORMATTED_VALUE',
1593
1594
  });
1594
1595
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1595
- ['sheets', 'datasource', 'table', 'read', 'sid', 'Extracts!B3', '--max-rows=250', '--render=UNFORMATTED_VALUE'],
1596
+ ['sheets', 'datasource', 'table', 'read', pos('sid'), pos('Extracts!B3'), '--max-rows=250', '--render=UNFORMATTED_VALUE'],
1596
1597
  { account: undefined },
1597
1598
  );
1598
1599
  });
@@ -1615,7 +1616,7 @@ describe('gog_sheets_datasource_add', () => {
1615
1616
  spreadsheetId: 'sid', billingProject: 'my-proj', query: 'SELECT 1',
1616
1617
  });
1617
1618
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1618
- ['sheets', 'datasource', 'add', 'sid', '--billing-project=my-proj', '--query=SELECT 1'],
1619
+ ['sheets', 'datasource', 'add', pos('sid'), '--billing-project=my-proj', '--query=SELECT 1'],
1619
1620
  { account: undefined },
1620
1621
  );
1621
1622
  });
@@ -1627,7 +1628,7 @@ describe('gog_sheets_datasource_add', () => {
1627
1628
  spreadsheetId: 'sid', billingProject: 'billing', dataset: 'ds', table: 'tbl', tableProject: 'owner',
1628
1629
  });
1629
1630
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1630
- ['sheets', 'datasource', 'add', 'sid', '--billing-project=billing', '--table-project=owner', '--dataset=ds', '--table=tbl'],
1631
+ ['sheets', 'datasource', 'add', pos('sid'), '--billing-project=billing', '--table-project=owner', '--dataset=ds', '--table=tbl'],
1631
1632
  { account: undefined },
1632
1633
  );
1633
1634
  });
@@ -1661,7 +1662,7 @@ describe('gog_sheets_datasource_update', () => {
1661
1662
  spreadsheetId: 'sid', dataSourceId: 'ds1', query: 'SELECT 2',
1662
1663
  });
1663
1664
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1664
- ['sheets', 'datasource', 'update', 'sid', 'ds1', '--query=SELECT 2'],
1665
+ ['sheets', 'datasource', 'update', pos('sid'), pos('ds1'), '--query=SELECT 2'],
1665
1666
  { account: undefined },
1666
1667
  );
1667
1668
  });
@@ -1673,7 +1674,7 @@ describe('gog_sheets_datasource_update', () => {
1673
1674
  spreadsheetId: 'sid', dataSourceId: 'ds1', billingProject: 'new-proj', dataset: 'ds2', table: 't2',
1674
1675
  });
1675
1676
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1676
- ['sheets', 'datasource', 'update', 'sid', 'ds1', '--billing-project=new-proj', '--dataset=ds2', '--table=t2'],
1677
+ ['sheets', 'datasource', 'update', pos('sid'), pos('ds1'), '--billing-project=new-proj', '--dataset=ds2', '--table=t2'],
1677
1678
  { account: undefined },
1678
1679
  );
1679
1680
  });
@@ -1685,7 +1686,7 @@ describe('gog_sheets_datasource_update', () => {
1685
1686
  spreadsheetId: 'sid', dataSourceId: 'ds1', tableProject: 'other-owner',
1686
1687
  });
1687
1688
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1688
- ['sheets', 'datasource', 'update', 'sid', 'ds1', '--table-project=other-owner'],
1689
+ ['sheets', 'datasource', 'update', pos('sid'), pos('ds1'), '--table-project=other-owner'],
1689
1690
  { account: undefined },
1690
1691
  );
1691
1692
  });
@@ -1715,7 +1716,7 @@ describe('gog_sheets_datasource_refresh', () => {
1715
1716
  const harness = await setupHandlers();
1716
1717
  await harness.callTool('gog_sheets_datasource_refresh', { spreadsheetId: 'sid', dataSourceId: 'ds1' });
1717
1718
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1718
- ['sheets', 'datasource', 'refresh', 'sid', 'ds1'],
1719
+ ['sheets', 'datasource', 'refresh', pos('sid'), pos('ds1')],
1719
1720
  { account: undefined },
1720
1721
  );
1721
1722
  });
@@ -1727,7 +1728,7 @@ describe('gog_sheets_datasource_refresh', () => {
1727
1728
  spreadsheetId: 'sid', dataSourceId: 'ds1', forceRefresh: true,
1728
1729
  });
1729
1730
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1730
- ['sheets', 'datasource', 'refresh', 'sid', 'ds1', '--force-refresh'],
1731
+ ['sheets', 'datasource', 'refresh', pos('sid'), pos('ds1'), '--force-refresh'],
1731
1732
  { account: undefined },
1732
1733
  );
1733
1734
  });
@@ -1768,7 +1769,7 @@ describe('gog_sheets_datasource_delete', () => {
1768
1769
  const harness = await setupHandlers();
1769
1770
  await harness.callTool('gog_sheets_datasource_delete', { spreadsheetId: 'sid', dataSourceId: 'ds1' });
1770
1771
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1771
- ['sheets', 'datasource', 'delete', 'sid', 'ds1', '--force'],
1772
+ ['sheets', 'datasource', 'delete', pos('sid'), pos('ds1'), '--force'],
1772
1773
  { account: undefined },
1773
1774
  );
1774
1775
  });
@@ -1780,8 +1781,45 @@ describe('gog_sheets_datasource_delete', () => {
1780
1781
  spreadsheetId: 'sid', dataSourceId: 'ds1', account: 'me@x.com',
1781
1782
  });
1782
1783
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1783
- ['sheets', 'datasource', 'delete', 'sid', 'ds1', '--force'],
1784
+ ['sheets', 'datasource', 'delete', pos('sid'), pos('ds1'), '--force'],
1784
1785
  { account: 'me@x.com' },
1785
1786
  );
1786
1787
  });
1787
1788
  });
1789
+
1790
+ // SEC-3/SEC-4: every model-supplied server path must resolve inside an
1791
+ // operator-configured root (GOG_FILE_ROOTS). The suite runs with '/' so the
1792
+ // arg-shape tests can use any path; these narrow it.
1793
+ async function withFileRoots<T>(roots: string, fn: () => Promise<T>): Promise<T> {
1794
+ const prev = process.env.GOG_FILE_ROOTS;
1795
+ process.env.GOG_FILE_ROOTS = roots;
1796
+ try {
1797
+ return await fn();
1798
+ } finally {
1799
+ process.env.GOG_FILE_ROOTS = prev;
1800
+ }
1801
+ }
1802
+
1803
+ describe('server paths are confined to GOG_FILE_ROOTS', () => {
1804
+ it.each([
1805
+ ['gog_sheets_export', {"spreadsheetId": "s1", "out": "/Users/me/.zshrc"}, 'out'],
1806
+ ['gog_sheets_links_set', {"spreadsheetId": "s1", "cellsJson": "@/etc/passwd"}, 'cellsJson'],
1807
+ ['gog_sheets_batch_update', {"spreadsheetId": "s1", "dataJson": "@/etc/passwd"}, 'dataJson'],
1808
+ ] as Array<[string, Record<string, unknown>, string]>)('%s refuses %j outside the roots', async (tool, args, param) => {
1809
+ const h = await setupHandlers();
1810
+ const result = await withFileRoots('/srv/gog-files', () => h.callTool(tool, args));
1811
+ expect(result.isError).toBe(true);
1812
+ expect(result.content[0].text).toMatch(new RegExp(`${param} ".*" is outside the directories`));
1813
+ expect(lib.runOrDiagnose).not.toHaveBeenCalled();
1814
+ });
1815
+
1816
+ // Clients auto-approve readOnlyHint tools; these write (and can overwrite)
1817
+ // files on the gog host, so they must not claim to be read-only.
1818
+ it.each(["gog_sheets_export"])('%s is not advertised as read-only and is marked destructive', async (name) => {
1819
+ const h = await setupHandlers();
1820
+ const { tools } = await h.client.listTools();
1821
+ const tool = tools.find((t) => t.name === name)!;
1822
+ expect(tool.annotations?.readOnlyHint).not.toBe(true);
1823
+ expect(tool.annotations?.destructiveHint).toBe(true);
1824
+ });
1825
+ });