gogcli-mcp-docs 2.8.0 → 2.18.1

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.
@@ -1,7 +1,8 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { registerExtraDocsTools } from '../../src/tools/docs-extra.js';
3
3
  import * as lib from '../../../gogcli-mcp/src/lib.js';
4
- import { setupHandlers as setupHandlersBase, toText } from '../../../gogcli-mcp/tests/helpers/test-harness.js';
4
+ import { createTestHarness } from '@chrischall/mcp-utils/test';
5
+ import { rawTextResult } from '@chrischall/mcp-utils';
5
6
 
6
7
  vi.mock('../../../gogcli-mcp/src/lib.js', async (importOriginal) => {
7
8
  const actual = await importOriginal<typeof lib>();
@@ -11,7 +12,7 @@ vi.mock('../../../gogcli-mcp/src/lib.js', async (importOriginal) => {
11
12
  };
12
13
  });
13
14
 
14
- const setupHandlers = () => setupHandlersBase(registerExtraDocsTools);
15
+ const setupHandlers = () => createTestHarness(registerExtraDocsTools);
15
16
 
16
17
  beforeEach(() => vi.clearAllMocks());
17
18
 
@@ -19,16 +20,16 @@ beforeEach(() => vi.clearAllMocks());
19
20
 
20
21
  describe('gog_docs_copy', () => {
21
22
  it('calls runOrDiagnose with correct args', async () => {
22
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
23
- const handlers = setupHandlers();
24
- await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'My Copy' });
23
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
24
+ const harness = await setupHandlers();
25
+ await harness.callTool('gog_docs_copy', { docId: 'abc', title: 'My Copy' });
25
26
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'My Copy'], { account: undefined });
26
27
  });
27
28
 
28
29
  it('includes --parent when provided', async () => {
29
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
30
- const handlers = setupHandlers();
31
- await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'Copy', parent: 'folder123' });
30
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
31
+ const harness = await setupHandlers();
32
+ await harness.callTool('gog_docs_copy', { docId: 'abc', title: 'Copy', parent: 'folder123' });
32
33
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
33
34
  ['docs', 'copy', 'abc', 'Copy', '--parent=folder123'],
34
35
  { account: undefined },
@@ -36,16 +37,16 @@ describe('gog_docs_copy', () => {
36
37
  });
37
38
 
38
39
  it('omits --parent when not provided', async () => {
39
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
40
- const handlers = setupHandlers();
41
- await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'Copy' });
40
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
41
+ const harness = await setupHandlers();
42
+ await harness.callTool('gog_docs_copy', { docId: 'abc', title: 'Copy' });
42
43
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'Copy'], { account: undefined });
43
44
  });
44
45
 
45
46
  it('forwards account override', async () => {
46
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
47
- const handlers = setupHandlers();
48
- await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'Copy', account: 'other@gmail.com' });
47
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
48
+ const harness = await setupHandlers();
49
+ await harness.callTool('gog_docs_copy', { docId: 'abc', title: 'Copy', account: 'other@gmail.com' });
49
50
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'Copy'], { account: 'other@gmail.com' });
50
51
  });
51
52
  });
@@ -54,9 +55,9 @@ describe('gog_docs_copy', () => {
54
55
 
55
56
  describe('gog_docs_delete', () => {
56
57
  it('calls runOrDiagnose with correct args', async () => {
57
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
58
- const handlers = setupHandlers();
59
- await handlers.get('gog_docs_delete')!({ docId: 'abc', start: 5, end: 10 });
58
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
59
+ const harness = await setupHandlers();
60
+ await harness.callTool('gog_docs_delete', { docId: 'abc', start: 5, end: 10 });
60
61
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
61
62
  ['docs', 'delete', '--start=5', '--end=10', 'abc'],
62
63
  { account: undefined },
@@ -65,9 +66,9 @@ describe('gog_docs_delete', () => {
65
66
 
66
67
  // gog 0.23.0
67
68
  it('anchors by text with --at/--occurrence/--match-case', async () => {
68
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
69
- const handlers = setupHandlers();
70
- await handlers.get('gog_docs_delete')!({ docId: 'abc', at: 'TODO', occurrence: 2, matchCase: true });
69
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
70
+ const harness = await setupHandlers();
71
+ await harness.callTool('gog_docs_delete', { docId: 'abc', at: 'TODO', occurrence: 2, matchCase: true });
71
72
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
72
73
  ['docs', 'delete', 'abc', '--at=TODO', '--occurrence=2', '--match-case'],
73
74
  { account: undefined },
@@ -75,9 +76,9 @@ describe('gog_docs_delete', () => {
75
76
  });
76
77
 
77
78
  it('includes --tab-id when provided', async () => {
78
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
79
- const handlers = setupHandlers();
80
- await handlers.get('gog_docs_delete')!({ docId: 'abc', start: 1, end: 5, tabId: 'tab1' });
79
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
80
+ const harness = await setupHandlers();
81
+ await harness.callTool('gog_docs_delete', { docId: 'abc', start: 1, end: 5, tabId: 'tab1' });
81
82
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
82
83
  ['docs', 'delete', '--start=1', '--end=5', 'abc', '--tab-id=tab1'],
83
84
  { account: undefined },
@@ -85,9 +86,9 @@ describe('gog_docs_delete', () => {
85
86
  });
86
87
 
87
88
  it('omits --tab-id when not provided', async () => {
88
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
89
- const handlers = setupHandlers();
90
- await handlers.get('gog_docs_delete')!({ docId: 'abc', start: 1, end: 5 });
89
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
90
+ const harness = await setupHandlers();
91
+ await harness.callTool('gog_docs_delete', { docId: 'abc', start: 1, end: 5 });
91
92
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
92
93
  ['docs', 'delete', '--start=1', '--end=5', 'abc'],
93
94
  { account: undefined },
@@ -99,9 +100,9 @@ describe('gog_docs_delete', () => {
99
100
 
100
101
  describe('gog_docs_edit', () => {
101
102
  it('calls runOrDiagnose with correct args', async () => {
102
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
103
- const handlers = setupHandlers();
104
- await handlers.get('gog_docs_edit')!({ docId: 'abc', find: 'old', replace: 'new' });
103
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
104
+ const harness = await setupHandlers();
105
+ await harness.callTool('gog_docs_edit', { docId: 'abc', find: 'old', replace: 'new' });
105
106
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
106
107
  ['docs', 'edit', 'abc', 'old', 'new'],
107
108
  { account: undefined },
@@ -109,9 +110,9 @@ describe('gog_docs_edit', () => {
109
110
  });
110
111
 
111
112
  it('includes --match-case when true', async () => {
112
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
113
- const handlers = setupHandlers();
114
- await handlers.get('gog_docs_edit')!({ docId: 'abc', find: 'old', replace: 'new', matchCase: true });
113
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
114
+ const harness = await setupHandlers();
115
+ await harness.callTool('gog_docs_edit', { docId: 'abc', find: 'old', replace: 'new', matchCase: true });
115
116
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
116
117
  ['docs', 'edit', 'abc', 'old', 'new', '--match-case'],
117
118
  { account: undefined },
@@ -119,9 +120,9 @@ describe('gog_docs_edit', () => {
119
120
  });
120
121
 
121
122
  it('omits --match-case when false', async () => {
122
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
123
- const handlers = setupHandlers();
124
- await handlers.get('gog_docs_edit')!({ docId: 'abc', find: 'old', replace: 'new', matchCase: false });
123
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
124
+ const harness = await setupHandlers();
125
+ await harness.callTool('gog_docs_edit', { docId: 'abc', find: 'old', replace: 'new', matchCase: false });
125
126
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
126
127
  ['docs', 'edit', 'abc', 'old', 'new'],
127
128
  { account: undefined },
@@ -133,16 +134,16 @@ describe('gog_docs_edit', () => {
133
134
 
134
135
  describe('gog_docs_export', () => {
135
136
  it('calls runOrDiagnose with correct args', async () => {
136
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
137
- const handlers = setupHandlers();
138
- await handlers.get('gog_docs_export')!({ docId: 'abc' });
137
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
138
+ const harness = await setupHandlers();
139
+ await harness.callTool('gog_docs_export', { docId: 'abc' });
139
140
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'export', 'abc'], { account: undefined });
140
141
  });
141
142
 
142
143
  it('includes --format when provided', async () => {
143
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
144
- const handlers = setupHandlers();
145
- await handlers.get('gog_docs_export')!({ docId: 'abc', format: 'html' });
144
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
145
+ const harness = await setupHandlers();
146
+ await harness.callTool('gog_docs_export', { docId: 'abc', format: 'html' });
146
147
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
147
148
  ['docs', 'export', 'abc', '--format=html'],
148
149
  { account: undefined },
@@ -150,9 +151,9 @@ describe('gog_docs_export', () => {
150
151
  });
151
152
 
152
153
  it('includes --out when provided', async () => {
153
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
154
- const handlers = setupHandlers();
155
- await handlers.get('gog_docs_export')!({ docId: 'abc', out: '/tmp/doc.pdf' });
154
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
155
+ const harness = await setupHandlers();
156
+ await harness.callTool('gog_docs_export', { docId: 'abc', out: '/tmp/doc.pdf' });
156
157
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
157
158
  ['docs', 'export', 'abc', '--out=/tmp/doc.pdf'],
158
159
  { account: undefined },
@@ -160,9 +161,9 @@ describe('gog_docs_export', () => {
160
161
  });
161
162
 
162
163
  it('includes both --format and --out when provided', async () => {
163
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
164
- const handlers = setupHandlers();
165
- await handlers.get('gog_docs_export')!({ docId: 'abc', format: 'txt', out: '/tmp/doc.txt' });
164
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
165
+ const harness = await setupHandlers();
166
+ await harness.callTool('gog_docs_export', { docId: 'abc', format: 'txt', out: '/tmp/doc.txt' });
166
167
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
167
168
  ['docs', 'export', 'abc', '--format=txt', '--out=/tmp/doc.txt'],
168
169
  { account: undefined },
@@ -170,20 +171,30 @@ describe('gog_docs_export', () => {
170
171
  });
171
172
 
172
173
  it('omits optional flags when not provided', async () => {
173
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
174
- const handlers = setupHandlers();
175
- await handlers.get('gog_docs_export')!({ docId: 'abc' });
174
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
175
+ const harness = await setupHandlers();
176
+ await harness.callTool('gog_docs_export', { docId: 'abc' });
176
177
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'export', 'abc'], { account: undefined });
177
178
  });
179
+
180
+ it('includes --overwrite when requested', async () => {
181
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
182
+ const harness = await setupHandlers();
183
+ await harness.callTool('gog_docs_export', { docId: 'abc', out: '/tmp/doc.pdf', overwrite: true });
184
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
185
+ ['docs', 'export', 'abc', '--out=/tmp/doc.pdf', '--overwrite'],
186
+ { account: undefined },
187
+ );
188
+ });
178
189
  });
179
190
 
180
191
  // --- gog_docs_insert ---
181
192
 
182
193
  describe('gog_docs_insert', () => {
183
194
  it('calls runOrDiagnose with content', async () => {
184
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
185
- const handlers = setupHandlers();
186
- await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'Hello world' });
195
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
196
+ const harness = await setupHandlers();
197
+ await harness.callTool('gog_docs_insert', { docId: 'abc', content: 'Hello world' });
187
198
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
188
199
  ['docs', 'insert', 'abc', 'Hello world'],
189
200
  { account: undefined },
@@ -192,9 +203,9 @@ describe('gog_docs_insert', () => {
192
203
 
193
204
  // gog 0.23.0
194
205
  it('anchors by text with --at/--occurrence/--match-case', async () => {
195
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
196
- const handlers = setupHandlers();
197
- await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'X', at: 'HERE', occurrence: 3, matchCase: true });
206
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
207
+ const harness = await setupHandlers();
208
+ await harness.callTool('gog_docs_insert', { docId: 'abc', content: 'X', at: 'HERE', occurrence: 3, matchCase: true });
198
209
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
199
210
  ['docs', 'insert', 'abc', 'X', '--at=HERE', '--occurrence=3', '--match-case'],
200
211
  { account: undefined },
@@ -202,9 +213,9 @@ describe('gog_docs_insert', () => {
202
213
  });
203
214
 
204
215
  it('includes --index when provided', async () => {
205
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
206
- const handlers = setupHandlers();
207
- await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'text', index: 5 });
216
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
217
+ const harness = await setupHandlers();
218
+ await harness.callTool('gog_docs_insert', { docId: 'abc', content: 'text', index: 5 });
208
219
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
209
220
  ['docs', 'insert', 'abc', 'text', '--index=5'],
210
221
  { account: undefined },
@@ -212,9 +223,9 @@ describe('gog_docs_insert', () => {
212
223
  });
213
224
 
214
225
  it('includes --index=0 when index is zero', async () => {
215
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
216
- const handlers = setupHandlers();
217
- await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'text', index: 0 });
226
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
227
+ const harness = await setupHandlers();
228
+ await harness.callTool('gog_docs_insert', { docId: 'abc', content: 'text', index: 0 });
218
229
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
219
230
  ['docs', 'insert', 'abc', 'text', '--index=0'],
220
231
  { account: undefined },
@@ -222,9 +233,9 @@ describe('gog_docs_insert', () => {
222
233
  });
223
234
 
224
235
  it('includes --file when provided', async () => {
225
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
226
- const handlers = setupHandlers();
227
- await handlers.get('gog_docs_insert')!({ docId: 'abc', file: '/tmp/text.txt' });
236
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
237
+ const harness = await setupHandlers();
238
+ await harness.callTool('gog_docs_insert', { docId: 'abc', file: '/tmp/text.txt' });
228
239
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
229
240
  ['docs', 'insert', 'abc', '--file=/tmp/text.txt'],
230
241
  { account: undefined },
@@ -232,9 +243,9 @@ describe('gog_docs_insert', () => {
232
243
  });
233
244
 
234
245
  it('includes --tab-id when provided', async () => {
235
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
236
- const handlers = setupHandlers();
237
- await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'hi', tabId: 'tab1' });
246
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
247
+ const harness = await setupHandlers();
248
+ await harness.callTool('gog_docs_insert', { docId: 'abc', content: 'hi', tabId: 'tab1' });
238
249
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
239
250
  ['docs', 'insert', 'abc', 'hi', '--tab-id=tab1'],
240
251
  { account: undefined },
@@ -242,9 +253,9 @@ describe('gog_docs_insert', () => {
242
253
  });
243
254
 
244
255
  it('omits optional flags when not provided', async () => {
245
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
246
- const handlers = setupHandlers();
247
- await handlers.get('gog_docs_insert')!({ docId: 'abc' });
256
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
257
+ const harness = await setupHandlers();
258
+ await harness.callTool('gog_docs_insert', { docId: 'abc' });
248
259
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'insert', 'abc'], { account: undefined });
249
260
  });
250
261
  });
@@ -253,16 +264,16 @@ describe('gog_docs_insert', () => {
253
264
 
254
265
  describe('gog_docs_list_tabs', () => {
255
266
  it('calls runOrDiagnose with correct args', async () => {
256
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
257
- const handlers = setupHandlers();
258
- await handlers.get('gog_docs_list_tabs')!({ docId: 'abc' });
267
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
268
+ const harness = await setupHandlers();
269
+ await harness.callTool('gog_docs_list_tabs', { docId: 'abc' });
259
270
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'list-tabs', 'abc'], { account: undefined });
260
271
  });
261
272
 
262
273
  it('forwards account override', async () => {
263
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
264
- const handlers = setupHandlers();
265
- await handlers.get('gog_docs_list_tabs')!({ docId: 'abc', account: 'other@gmail.com' });
274
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
275
+ const harness = await setupHandlers();
276
+ await harness.callTool('gog_docs_list_tabs', { docId: 'abc', account: 'other@gmail.com' });
266
277
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'list-tabs', 'abc'], { account: 'other@gmail.com' });
267
278
  });
268
279
  });
@@ -271,9 +282,9 @@ describe('gog_docs_list_tabs', () => {
271
282
 
272
283
  describe('gog_docs_sed', () => {
273
284
  it('calls runOrDiagnose with single expression', async () => {
274
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
275
- const handlers = setupHandlers();
276
- await handlers.get('gog_docs_sed')!({ docId: 'abc', expression: 's/old/new/g' });
285
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
286
+ const harness = await setupHandlers();
287
+ await harness.callTool('gog_docs_sed', { docId: 'abc', expression: 's/old/new/g' });
277
288
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
278
289
  ['docs', 'sed', 'abc', 's/old/new/g'],
279
290
  { account: undefined },
@@ -281,9 +292,9 @@ describe('gog_docs_sed', () => {
281
292
  });
282
293
 
283
294
  it('calls runOrDiagnose with multiple expressions', async () => {
284
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
285
- const handlers = setupHandlers();
286
- await handlers.get('gog_docs_sed')!({
295
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
296
+ const harness = await setupHandlers();
297
+ await harness.callTool('gog_docs_sed', {
287
298
  docId: 'abc',
288
299
  expressions: ['s/foo/bar/g', 's/baz/qux/g'],
289
300
  });
@@ -294,9 +305,9 @@ describe('gog_docs_sed', () => {
294
305
  });
295
306
 
296
307
  it('includes --file when provided', async () => {
297
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
298
- const handlers = setupHandlers();
299
- await handlers.get('gog_docs_sed')!({ docId: 'abc', file: '/tmp/sed.txt' });
308
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
309
+ const harness = await setupHandlers();
310
+ await harness.callTool('gog_docs_sed', { docId: 'abc', file: '/tmp/sed.txt' });
300
311
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
301
312
  ['docs', 'sed', 'abc', '--file=/tmp/sed.txt'],
302
313
  { account: undefined },
@@ -304,9 +315,9 @@ describe('gog_docs_sed', () => {
304
315
  });
305
316
 
306
317
  it('includes --tab when provided', async () => {
307
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
308
- const handlers = setupHandlers();
309
- await handlers.get('gog_docs_sed')!({ docId: 'abc', expression: 's/a/b/', tab: 'Notes' });
318
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
319
+ const harness = await setupHandlers();
320
+ await harness.callTool('gog_docs_sed', { docId: 'abc', expression: 's/a/b/', tab: 'Notes' });
310
321
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
311
322
  ['docs', 'sed', 'abc', 's/a/b/', '--tab=Notes'],
312
323
  { account: undefined },
@@ -314,9 +325,9 @@ describe('gog_docs_sed', () => {
314
325
  });
315
326
 
316
327
  it('omits optional flags when not provided', async () => {
317
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
318
- const handlers = setupHandlers();
319
- await handlers.get('gog_docs_sed')!({ docId: 'abc' });
328
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
329
+ const harness = await setupHandlers();
330
+ await harness.callTool('gog_docs_sed', { docId: 'abc' });
320
331
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'sed', 'abc'], { account: undefined });
321
332
  });
322
333
  });
@@ -325,9 +336,9 @@ describe('gog_docs_sed', () => {
325
336
 
326
337
  describe('gog_docs_update', () => {
327
338
  it('calls runOrDiagnose with --text flag', async () => {
328
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
329
- const handlers = setupHandlers();
330
- await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'Hello' });
339
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
340
+ const harness = await setupHandlers();
341
+ await harness.callTool('gog_docs_update', { docId: 'abc', text: 'Hello' });
331
342
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
332
343
  ['docs', 'update', 'abc', '--text=Hello'],
333
344
  { account: undefined },
@@ -336,9 +347,9 @@ describe('gog_docs_update', () => {
336
347
 
337
348
  // gog 0.23.0
338
349
  it('anchors by text with --at/--occurrence/--match-case', async () => {
339
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
340
- const handlers = setupHandlers();
341
- await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'New', at: 'OLD', occurrence: 1, matchCase: true });
350
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
351
+ const harness = await setupHandlers();
352
+ await harness.callTool('gog_docs_update', { docId: 'abc', text: 'New', at: 'OLD', occurrence: 1, matchCase: true });
342
353
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
343
354
  ['docs', 'update', 'abc', '--text=New', '--at=OLD', '--occurrence=1', '--match-case'],
344
355
  { account: undefined },
@@ -346,9 +357,9 @@ describe('gog_docs_update', () => {
346
357
  });
347
358
 
348
359
  it('includes --file when provided', async () => {
349
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
350
- const handlers = setupHandlers();
351
- await handlers.get('gog_docs_update')!({ docId: 'abc', file: '/tmp/content.txt' });
360
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
361
+ const harness = await setupHandlers();
362
+ await harness.callTool('gog_docs_update', { docId: 'abc', file: '/tmp/content.txt' });
352
363
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
353
364
  ['docs', 'update', 'abc', '--file=/tmp/content.txt'],
354
365
  { account: undefined },
@@ -356,9 +367,9 @@ describe('gog_docs_update', () => {
356
367
  });
357
368
 
358
369
  it('includes --index when provided', async () => {
359
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
360
- const handlers = setupHandlers();
361
- await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'hi', index: 10 });
370
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
371
+ const harness = await setupHandlers();
372
+ await harness.callTool('gog_docs_update', { docId: 'abc', text: 'hi', index: 10 });
362
373
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
363
374
  ['docs', 'update', 'abc', '--text=hi', '--index=10'],
364
375
  { account: undefined },
@@ -366,9 +377,9 @@ describe('gog_docs_update', () => {
366
377
  });
367
378
 
368
379
  it('includes --index=0 when index is zero', async () => {
369
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
370
- const handlers = setupHandlers();
371
- await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'hi', index: 0 });
380
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
381
+ const harness = await setupHandlers();
382
+ await harness.callTool('gog_docs_update', { docId: 'abc', text: 'hi', index: 0 });
372
383
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
373
384
  ['docs', 'update', 'abc', '--text=hi', '--index=0'],
374
385
  { account: undefined },
@@ -376,9 +387,9 @@ describe('gog_docs_update', () => {
376
387
  });
377
388
 
378
389
  it('includes --tab-id when provided', async () => {
379
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
380
- const handlers = setupHandlers();
381
- await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'hi', tabId: 'tab1' });
390
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
391
+ const harness = await setupHandlers();
392
+ await harness.callTool('gog_docs_update', { docId: 'abc', text: 'hi', tabId: 'tab1' });
382
393
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
383
394
  ['docs', 'update', 'abc', '--text=hi', '--tab-id=tab1'],
384
395
  { account: undefined },
@@ -386,9 +397,9 @@ describe('gog_docs_update', () => {
386
397
  });
387
398
 
388
399
  it('includes --pageless when true', async () => {
389
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
390
- const handlers = setupHandlers();
391
- await handlers.get('gog_docs_update')!({ docId: 'abc', pageless: true });
400
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
401
+ const harness = await setupHandlers();
402
+ await harness.callTool('gog_docs_update', { docId: 'abc', pageless: true });
392
403
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
393
404
  ['docs', 'update', 'abc', '--pageless'],
394
405
  { account: undefined },
@@ -396,9 +407,9 @@ describe('gog_docs_update', () => {
396
407
  });
397
408
 
398
409
  it('omits --pageless when false', async () => {
399
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
400
- const handlers = setupHandlers();
401
- await handlers.get('gog_docs_update')!({ docId: 'abc', pageless: false });
410
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
411
+ const harness = await setupHandlers();
412
+ await harness.callTool('gog_docs_update', { docId: 'abc', pageless: false });
402
413
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
403
414
  ['docs', 'update', 'abc'],
404
415
  { account: undefined },
@@ -406,16 +417,16 @@ describe('gog_docs_update', () => {
406
417
  });
407
418
 
408
419
  it('omits optional flags when not provided', async () => {
409
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
410
- const handlers = setupHandlers();
411
- await handlers.get('gog_docs_update')!({ docId: 'abc' });
420
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
421
+ const harness = await setupHandlers();
422
+ await harness.callTool('gog_docs_update', { docId: 'abc' });
412
423
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'update', 'abc'], { account: undefined });
413
424
  });
414
425
 
415
426
  it('includes --replace-range when provided', async () => {
416
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
417
- const handlers = setupHandlers();
418
- await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'new', replaceRange: '25:40' });
427
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
428
+ const harness = await setupHandlers();
429
+ await harness.callTool('gog_docs_update', { docId: 'abc', text: 'new', replaceRange: '25:40' });
419
430
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
420
431
  ['docs', 'update', 'abc', '--text=new', '--replace-range=25:40'],
421
432
  { account: undefined },
@@ -423,9 +434,9 @@ describe('gog_docs_update', () => {
423
434
  });
424
435
 
425
436
  it('includes --markdown when true', async () => {
426
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
427
- const handlers = setupHandlers();
428
- await handlers.get('gog_docs_update')!({ docId: 'abc', text: '# Heading', markdown: true });
437
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
438
+ const harness = await setupHandlers();
439
+ await harness.callTool('gog_docs_update', { docId: 'abc', text: '# Heading', markdown: true });
429
440
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
430
441
  ['docs', 'update', 'abc', '--text=# Heading', '--markdown'],
431
442
  { account: undefined },
@@ -433,9 +444,9 @@ describe('gog_docs_update', () => {
433
444
  });
434
445
 
435
446
  it('omits --markdown when false', async () => {
436
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
437
- const handlers = setupHandlers();
438
- await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'hi', markdown: false });
447
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
448
+ const harness = await setupHandlers();
449
+ await harness.callTool('gog_docs_update', { docId: 'abc', text: 'hi', markdown: false });
439
450
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
440
451
  ['docs', 'update', 'abc', '--text=hi'],
441
452
  { account: undefined },
@@ -447,16 +458,16 @@ describe('gog_docs_update', () => {
447
458
 
448
459
  describe('gog_docs_comments_list', () => {
449
460
  it('calls runOrDiagnose with correct args', async () => {
450
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
451
- const handlers = setupHandlers();
452
- await handlers.get('gog_docs_comments_list')!({ docId: 'abc' });
461
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
462
+ const harness = await setupHandlers();
463
+ await harness.callTool('gog_docs_comments_list', { docId: 'abc' });
453
464
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'comments', 'list', 'abc'], { account: undefined });
454
465
  });
455
466
 
456
467
  it('includes --include-resolved when set', async () => {
457
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
458
- const handlers = setupHandlers();
459
- await handlers.get('gog_docs_comments_list')!({ docId: 'abc', includeResolved: true });
468
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
469
+ const harness = await setupHandlers();
470
+ await harness.callTool('gog_docs_comments_list', { docId: 'abc', includeResolved: true });
460
471
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
461
472
  ['docs', 'comments', 'list', 'abc', '--include-resolved'],
462
473
  { account: undefined },
@@ -464,16 +475,16 @@ describe('gog_docs_comments_list', () => {
464
475
  });
465
476
 
466
477
  it('omits --include-resolved when false', async () => {
467
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
468
- const handlers = setupHandlers();
469
- await handlers.get('gog_docs_comments_list')!({ docId: 'abc', includeResolved: false });
478
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
479
+ const harness = await setupHandlers();
480
+ await harness.callTool('gog_docs_comments_list', { docId: 'abc', includeResolved: false });
470
481
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'comments', 'list', 'abc'], { account: undefined });
471
482
  });
472
483
 
473
484
  it('forwards account override', async () => {
474
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
475
- const handlers = setupHandlers();
476
- await handlers.get('gog_docs_comments_list')!({ docId: 'abc', account: 'other@gmail.com' });
485
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
486
+ const harness = await setupHandlers();
487
+ await harness.callTool('gog_docs_comments_list', { docId: 'abc', account: 'other@gmail.com' });
477
488
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
478
489
  ['docs', 'comments', 'list', 'abc'],
479
490
  { account: 'other@gmail.com' },
@@ -482,9 +493,9 @@ describe('gog_docs_comments_list', () => {
482
493
 
483
494
  // gog 0.22.0 adds --since; pagination flags were already supported by gog.
484
495
  it('includes --since and pagination flags when set', async () => {
485
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
486
- const handlers = setupHandlers();
487
- await handlers.get('gog_docs_comments_list')!({
496
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
497
+ const harness = await setupHandlers();
498
+ await harness.callTool('gog_docs_comments_list', {
488
499
  docId: 'abc', includeResolved: true, since: '2026-06-01T00:00:00Z', max: 10, page: 'tok', all: true,
489
500
  });
490
501
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -496,9 +507,9 @@ describe('gog_docs_comments_list', () => {
496
507
 
497
508
  describe('gog_docs_table_column_width', () => {
498
509
  it('sets a fixed column width', async () => {
499
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
500
- const handlers = setupHandlers();
501
- await handlers.get('gog_docs_table_column_width')!({
510
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
511
+ const harness = await setupHandlers();
512
+ await harness.callTool('gog_docs_table_column_width', {
502
513
  docId: 'd1', col: 2, width: 120, tableIndex: 1, tab: 'Body',
503
514
  });
504
515
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -508,9 +519,9 @@ describe('gog_docs_table_column_width', () => {
508
519
  });
509
520
 
510
521
  it('resets all columns to even distribution', async () => {
511
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
512
- const handlers = setupHandlers();
513
- await handlers.get('gog_docs_table_column_width')!({ docId: 'd1', evenlyDistributed: true });
522
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
523
+ const harness = await setupHandlers();
524
+ await harness.callTool('gog_docs_table_column_width', { docId: 'd1', evenlyDistributed: true });
514
525
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
515
526
  ['docs', 'table-column-width', 'd1', '--evenly-distributed'],
516
527
  { account: undefined },
@@ -518,9 +529,9 @@ describe('gog_docs_table_column_width', () => {
518
529
  });
519
530
 
520
531
  it('forwards account override', async () => {
521
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
522
- const handlers = setupHandlers();
523
- await handlers.get('gog_docs_table_column_width')!({ docId: 'd1', col: 1, width: 80, account: 'other@gmail.com' });
532
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
533
+ const harness = await setupHandlers();
534
+ await harness.callTool('gog_docs_table_column_width', { docId: 'd1', col: 1, width: 80, account: 'other@gmail.com' });
524
535
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
525
536
  ['docs', 'table-column-width', 'd1', '--col=1', '--width=80'],
526
537
  { account: 'other@gmail.com' },
@@ -531,16 +542,16 @@ describe('gog_docs_table_column_width', () => {
531
542
  // gog 0.23.0
532
543
  describe('gog_docs_find_range', () => {
533
544
  it('maps matched text to index ranges', async () => {
534
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
535
- const handlers = setupHandlers();
536
- await handlers.get('gog_docs_find_range')!({ docId: 'd1', text: 'hello' });
545
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
546
+ const harness = await setupHandlers();
547
+ await harness.callTool('gog_docs_find_range', { docId: 'd1', text: 'hello' });
537
548
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'find-range', 'd1', 'hello'], { account: undefined });
538
549
  });
539
550
 
540
551
  it('passes all match flags', async () => {
541
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
542
- const handlers = setupHandlers();
543
- await handlers.get('gog_docs_find_range')!({
552
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
553
+ const harness = await setupHandlers();
554
+ await harness.callTool('gog_docs_find_range', {
544
555
  docId: 'd1', text: 'hello', occurrence: 2, matchCase: true, normalizeWhitespace: true, all: true, failEmpty: true, tab: 'T',
545
556
  });
546
557
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -552,16 +563,16 @@ describe('gog_docs_find_range', () => {
552
563
 
553
564
  describe('gog_docs_comments_locate', () => {
554
565
  it('locates a comment by id', async () => {
555
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
556
- const handlers = setupHandlers();
557
- await handlers.get('gog_docs_comments_locate')!({ docId: 'd1', commentId: 'c1' });
566
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
567
+ const harness = await setupHandlers();
568
+ await harness.callTool('gog_docs_comments_locate', { docId: 'd1', commentId: 'c1' });
558
569
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'comments', 'locate', 'd1', 'c1'], { account: undefined });
559
570
  });
560
571
 
561
572
  it('passes match flags', async () => {
562
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
563
- const handlers = setupHandlers();
564
- await handlers.get('gog_docs_comments_locate')!({ docId: 'd1', commentId: 'c1', matchCase: true, normalizeWhitespace: true, tab: 'T' });
573
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
574
+ const harness = await setupHandlers();
575
+ await harness.callTool('gog_docs_comments_locate', { docId: 'd1', commentId: 'c1', matchCase: true, normalizeWhitespace: true, tab: 'T' });
565
576
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
566
577
  ['docs', 'comments', 'locate', 'd1', 'c1', '--match-case', '--normalize-whitespace', '--tab=T'],
567
578
  { account: undefined },
@@ -571,18 +582,18 @@ describe('gog_docs_comments_locate', () => {
571
582
 
572
583
  describe('gog_docs_comments_get', () => {
573
584
  it('calls runOrDiagnose with correct args', async () => {
574
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
575
- const handlers = setupHandlers();
576
- await handlers.get('gog_docs_comments_get')!({ docId: 'abc', commentId: 'c1' });
585
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
586
+ const harness = await setupHandlers();
587
+ await harness.callTool('gog_docs_comments_get', { docId: 'abc', commentId: 'c1' });
577
588
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'comments', 'get', 'abc', 'c1'], { account: undefined });
578
589
  });
579
590
  });
580
591
 
581
592
  describe('gog_docs_comments_add', () => {
582
593
  it('calls runOrDiagnose with content', async () => {
583
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
584
- const handlers = setupHandlers();
585
- await handlers.get('gog_docs_comments_add')!({ docId: 'abc', content: 'Please review' });
594
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
595
+ const harness = await setupHandlers();
596
+ await harness.callTool('gog_docs_comments_add', { docId: 'abc', content: 'Please review' });
586
597
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
587
598
  ['docs', 'comments', 'add', 'abc', 'Please review'],
588
599
  { account: undefined },
@@ -590,9 +601,9 @@ describe('gog_docs_comments_add', () => {
590
601
  });
591
602
 
592
603
  it('includes --quoted when provided', async () => {
593
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
594
- const handlers = setupHandlers();
595
- await handlers.get('gog_docs_comments_add')!({
604
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
605
+ const harness = await setupHandlers();
606
+ await harness.callTool('gog_docs_comments_add', {
596
607
  docId: 'abc', content: 'Typo here', quoted: 'teh',
597
608
  });
598
609
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -602,9 +613,9 @@ describe('gog_docs_comments_add', () => {
602
613
  });
603
614
 
604
615
  it('omits --quoted when not provided', async () => {
605
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
606
- const handlers = setupHandlers();
607
- await handlers.get('gog_docs_comments_add')!({ docId: 'abc', content: 'Nice' });
616
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
617
+ const harness = await setupHandlers();
618
+ await harness.callTool('gog_docs_comments_add', { docId: 'abc', content: 'Nice' });
608
619
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
609
620
  ['docs', 'comments', 'add', 'abc', 'Nice'],
610
621
  { account: undefined },
@@ -614,9 +625,9 @@ describe('gog_docs_comments_add', () => {
614
625
 
615
626
  describe('gog_docs_comments_reply', () => {
616
627
  it('calls runOrDiagnose with correct args', async () => {
617
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
618
- const handlers = setupHandlers();
619
- await handlers.get('gog_docs_comments_reply')!({ docId: 'abc', commentId: 'c1', content: 'Done' });
628
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
629
+ const harness = await setupHandlers();
630
+ await harness.callTool('gog_docs_comments_reply', { docId: 'abc', commentId: 'c1', content: 'Done' });
620
631
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
621
632
  ['docs', 'comments', 'reply', 'abc', 'c1', 'Done'],
622
633
  { account: undefined },
@@ -626,9 +637,9 @@ describe('gog_docs_comments_reply', () => {
626
637
 
627
638
  describe('gog_docs_comments_resolve', () => {
628
639
  it('calls runOrDiagnose with correct args', async () => {
629
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
630
- const handlers = setupHandlers();
631
- await handlers.get('gog_docs_comments_resolve')!({ docId: 'abc', commentId: 'c1' });
640
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
641
+ const harness = await setupHandlers();
642
+ await harness.callTool('gog_docs_comments_resolve', { docId: 'abc', commentId: 'c1' });
632
643
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
633
644
  ['docs', 'comments', 'resolve', 'abc', 'c1'],
634
645
  { account: undefined },
@@ -636,9 +647,9 @@ describe('gog_docs_comments_resolve', () => {
636
647
  });
637
648
 
638
649
  it('includes --message when provided', async () => {
639
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
640
- const handlers = setupHandlers();
641
- await handlers.get('gog_docs_comments_resolve')!({
650
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
651
+ const harness = await setupHandlers();
652
+ await harness.callTool('gog_docs_comments_resolve', {
642
653
  docId: 'abc', commentId: 'c1', message: 'Fixed in v2',
643
654
  });
644
655
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -648,9 +659,9 @@ describe('gog_docs_comments_resolve', () => {
648
659
  });
649
660
 
650
661
  it('omits --message when not provided', async () => {
651
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
652
- const handlers = setupHandlers();
653
- await handlers.get('gog_docs_comments_resolve')!({ docId: 'abc', commentId: 'c1' });
662
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
663
+ const harness = await setupHandlers();
664
+ await harness.callTool('gog_docs_comments_resolve', { docId: 'abc', commentId: 'c1' });
654
665
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
655
666
  ['docs', 'comments', 'resolve', 'abc', 'c1'],
656
667
  { account: undefined },
@@ -660,11 +671,11 @@ describe('gog_docs_comments_resolve', () => {
660
671
 
661
672
  describe('gog_docs_comments_delete', () => {
662
673
  it('calls runOrDiagnose with correct args', async () => {
663
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
664
- const handlers = setupHandlers();
665
- await handlers.get('gog_docs_comments_delete')!({ docId: 'abc', commentId: 'c1' });
674
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
675
+ const harness = await setupHandlers();
676
+ await harness.callTool('gog_docs_comments_delete', { docId: 'abc', commentId: 'c1' });
666
677
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
667
- ['docs', 'comments', 'delete', 'abc', 'c1'],
678
+ ['docs', 'comments', 'delete', 'abc', 'c1', '--force'],
668
679
  { account: undefined },
669
680
  );
670
681
  });
@@ -672,18 +683,18 @@ describe('gog_docs_comments_delete', () => {
672
683
 
673
684
  describe('gog_docs_trash', () => {
674
685
  it('routes to gog drive delete <docId>', async () => {
675
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
676
- const handlers = setupHandlers();
677
- await handlers.get('gog_docs_trash')!({ docId: 'd1' });
678
- expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'delete', 'd1'], { account: undefined });
686
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
687
+ const harness = await setupHandlers();
688
+ await harness.callTool('gog_docs_trash', { docId: 'd1' });
689
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['drive', 'delete', 'd1', '--force'], { account: undefined });
679
690
  });
680
691
  });
681
692
 
682
693
  describe('gog_docs_append', () => {
683
694
  it('uses gog docs write --append', async () => {
684
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
685
- const handlers = setupHandlers();
686
- await handlers.get('gog_docs_append')!({ docId: 'd1', text: 'Hello' });
695
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
696
+ const harness = await setupHandlers();
697
+ await harness.callTool('gog_docs_append', { docId: 'd1', text: 'Hello' });
687
698
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
688
699
  ['docs', 'write', 'd1', '--append', '--text=Hello'],
689
700
  { account: undefined },
@@ -691,9 +702,9 @@ describe('gog_docs_append', () => {
691
702
  });
692
703
 
693
704
  it('passes file, markdown, and tab flags', async () => {
694
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
695
- const handlers = setupHandlers();
696
- await handlers.get('gog_docs_append')!({
705
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
706
+ const harness = await setupHandlers();
707
+ await harness.callTool('gog_docs_append', {
697
708
  docId: 'd1', file: '/tmp/section.md', markdown: true, tab: 'Notes',
698
709
  });
699
710
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -727,35 +738,49 @@ describe('gog_docs_append', () => {
727
738
 
728
739
  describe('gog_docs_read', () => {
729
740
  it('defaults to plain text via gog docs cat', async () => {
730
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('hello'));
731
- const handlers = setupHandlers();
732
- await handlers.get('gog_docs_read')!({ docId: 'd1' });
741
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('hello'));
742
+ const harness = await setupHandlers();
743
+ await harness.callTool('gog_docs_read', { docId: 'd1' });
733
744
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'cat', 'd1'], { account: undefined });
734
745
  });
735
746
 
736
747
  it('routes json format to gog docs raw --pretty', async () => {
737
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
738
- const handlers = setupHandlers();
739
- await handlers.get('gog_docs_read')!({ docId: 'd1', format: 'json' });
748
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
749
+ const harness = await setupHandlers();
750
+ await harness.callTool('gog_docs_read', { docId: 'd1', format: 'json' });
740
751
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'raw', 'd1', '--pretty'], { account: undefined });
741
752
  });
742
753
 
743
754
  it('passes tab, allTabs, maxBytes in text mode', async () => {
744
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText(''));
745
- const handlers = setupHandlers();
746
- await handlers.get('gog_docs_read')!({ docId: 'd1', tab: 'Section A', allTabs: true, maxBytes: 0 });
755
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult(''));
756
+ const harness = await setupHandlers();
757
+ await harness.callTool('gog_docs_read', { docId: 'd1', tab: 'Section A', allTabs: true, maxBytes: 0 });
747
758
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
748
759
  ['docs', 'cat', 'd1', '--tab=Section A', '--all-tabs', '--max-bytes=0'],
749
760
  { account: undefined },
750
761
  );
751
762
  });
763
+
764
+ it('appends --chips in text mode', async () => {
765
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult(''));
766
+ const harness = await setupHandlers();
767
+ await harness.callTool('gog_docs_read', { docId: 'd1', chips: true });
768
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'cat', 'd1', '--chips'], { account: undefined });
769
+ });
770
+
771
+ it('ignores chips in json mode', async () => {
772
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
773
+ const harness = await setupHandlers();
774
+ await harness.callTool('gog_docs_read', { docId: 'd1', format: 'json', chips: true });
775
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'raw', 'd1', '--pretty'], { account: undefined });
776
+ });
752
777
  });
753
778
 
754
779
  describe('gog_docs_format', () => {
755
780
  it('passes all text/paragraph attribute flags', async () => {
756
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
757
- const handlers = setupHandlers();
758
- await handlers.get('gog_docs_format')!({
781
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
782
+ const harness = await setupHandlers();
783
+ await harness.callTool('gog_docs_format', {
759
784
  docId: 'd1',
760
785
  match: 'Title',
761
786
  matchAll: true,
@@ -795,9 +820,9 @@ describe('gog_docs_format', () => {
795
820
  });
796
821
 
797
822
  it('emits clear-style flags (noBold/noItalic/...) without the set variants', async () => {
798
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
799
- const handlers = setupHandlers();
800
- await handlers.get('gog_docs_format')!({
823
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
824
+ const harness = await setupHandlers();
825
+ await harness.callTool('gog_docs_format', {
801
826
  docId: 'd1',
802
827
  noBold: true,
803
828
  noItalic: true,
@@ -811,17 +836,17 @@ describe('gog_docs_format', () => {
811
836
  });
812
837
 
813
838
  it('omits all flags when not provided (whole-doc no-op call passes through)', async () => {
814
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
815
- const handlers = setupHandlers();
816
- await handlers.get('gog_docs_format')!({ docId: 'd1' });
839
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
840
+ const harness = await setupHandlers();
841
+ await harness.callTool('gog_docs_format', { docId: 'd1' });
817
842
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'format', 'd1'], { account: undefined });
818
843
  });
819
844
 
820
845
  // gog 0.18.0
821
846
  it('passes --heading-level and --named-style when provided', async () => {
822
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
823
- const handlers = setupHandlers();
824
- await handlers.get('gog_docs_format')!({
847
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
848
+ const harness = await setupHandlers();
849
+ await harness.callTool('gog_docs_format', {
825
850
  docId: 'd1', match: 'Intro', headingLevel: 1, namedStyle: 'HEADING_1',
826
851
  });
827
852
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -831,9 +856,9 @@ describe('gog_docs_format', () => {
831
856
  });
832
857
 
833
858
  it('passes --heading-level=0 when explicitly 0 (HEADING_0 reserved but argument should reach gog)', async () => {
834
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
835
- const handlers = setupHandlers();
836
- await handlers.get('gog_docs_format')!({ docId: 'd1', headingLevel: 0 });
859
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
860
+ const harness = await setupHandlers();
861
+ await harness.callTool('gog_docs_format', { docId: 'd1', headingLevel: 0 });
837
862
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
838
863
  ['docs', 'format', 'd1', '--heading-level=0'],
839
864
  { account: undefined },
@@ -842,9 +867,9 @@ describe('gog_docs_format', () => {
842
867
 
843
868
  // gog 0.22.0
844
869
  it('passes --code when code is true', async () => {
845
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
846
- const handlers = setupHandlers();
847
- await handlers.get('gog_docs_format')!({ docId: 'd1', match: 'snippet', code: true });
870
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
871
+ const harness = await setupHandlers();
872
+ await harness.callTool('gog_docs_format', { docId: 'd1', match: 'snippet', code: true });
848
873
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
849
874
  ['docs', 'format', 'd1', '--match=snippet', '--code'],
850
875
  { account: undefined },
@@ -853,9 +878,9 @@ describe('gog_docs_format', () => {
853
878
 
854
879
  // gog 0.23.0
855
880
  it('sets a hyperlink with --link', async () => {
856
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
857
- const handlers = setupHandlers();
858
- await handlers.get('gog_docs_format')!({ docId: 'd1', match: 'see docs', link: 'https://example.com' });
881
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
882
+ const harness = await setupHandlers();
883
+ await harness.callTool('gog_docs_format', { docId: 'd1', match: 'see docs', link: 'https://example.com' });
859
884
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
860
885
  ['docs', 'format', 'd1', '--match=see docs', '--link=https://example.com'],
861
886
  { account: undefined },
@@ -863,9 +888,9 @@ describe('gog_docs_format', () => {
863
888
  });
864
889
 
865
890
  it('clears a hyperlink with --no-link', async () => {
866
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
867
- const handlers = setupHandlers();
868
- await handlers.get('gog_docs_format')!({ docId: 'd1', match: 'linked', noLink: true });
891
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
892
+ const harness = await setupHandlers();
893
+ await harness.callTool('gog_docs_format', { docId: 'd1', match: 'linked', noLink: true });
869
894
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
870
895
  ['docs', 'format', 'd1', '--match=linked', '--no-link'],
871
896
  { account: undefined },
@@ -877,9 +902,9 @@ describe('gog_docs_format', () => {
877
902
 
878
903
  describe('gog_docs_insert_page_break', () => {
879
904
  it('inserts at index when --index provided', async () => {
880
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
881
- const handlers = setupHandlers();
882
- await handlers.get('gog_docs_insert_page_break')!({ docId: 'd1', index: 42 });
905
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
906
+ const harness = await setupHandlers();
907
+ await harness.callTool('gog_docs_insert_page_break', { docId: 'd1', index: 42 });
883
908
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
884
909
  ['docs', 'insert-page-break', 'd1', '--index=42'],
885
910
  { account: undefined },
@@ -888,9 +913,9 @@ describe('gog_docs_insert_page_break', () => {
888
913
 
889
914
  // gog 0.23.0
890
915
  it('anchors by text with --at/--occurrence/--match-case', async () => {
891
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
892
- const handlers = setupHandlers();
893
- await handlers.get('gog_docs_insert_page_break')!({ docId: 'd1', at: 'Chapter 2', occurrence: 1, matchCase: true });
916
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
917
+ const harness = await setupHandlers();
918
+ await harness.callTool('gog_docs_insert_page_break', { docId: 'd1', at: 'Chapter 2', occurrence: 1, matchCase: true });
894
919
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
895
920
  ['docs', 'insert-page-break', 'd1', '--at=Chapter 2', '--occurrence=1', '--match-case'],
896
921
  { account: undefined },
@@ -898,9 +923,9 @@ describe('gog_docs_insert_page_break', () => {
898
923
  });
899
924
 
900
925
  it('uses --at-end when atEnd is true', async () => {
901
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
902
- const handlers = setupHandlers();
903
- await handlers.get('gog_docs_insert_page_break')!({ docId: 'd1', atEnd: true, tab: 'Body' });
926
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
927
+ const harness = await setupHandlers();
928
+ await harness.callTool('gog_docs_insert_page_break', { docId: 'd1', atEnd: true, tab: 'Body' });
904
929
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
905
930
  ['docs', 'insert-page-break', 'd1', '--at-end', '--tab=Body'],
906
931
  { account: undefined },
@@ -908,9 +933,9 @@ describe('gog_docs_insert_page_break', () => {
908
933
  });
909
934
 
910
935
  it('passes --index=1 when explicitly 1', async () => {
911
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
912
- const handlers = setupHandlers();
913
- await handlers.get('gog_docs_insert_page_break')!({ docId: 'd1', index: 1 });
936
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
937
+ const harness = await setupHandlers();
938
+ await harness.callTool('gog_docs_insert_page_break', { docId: 'd1', index: 1 });
914
939
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
915
940
  ['docs', 'insert-page-break', 'd1', '--index=1'],
916
941
  { account: undefined },
@@ -920,9 +945,9 @@ describe('gog_docs_insert_page_break', () => {
920
945
 
921
946
  describe('gog_docs_page_layout', () => {
922
947
  it('passes --layout when provided', async () => {
923
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
924
- const handlers = setupHandlers();
925
- await handlers.get('gog_docs_page_layout')!({ docId: 'd1', layout: 'pages' });
948
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
949
+ const harness = await setupHandlers();
950
+ await harness.callTool('gog_docs_page_layout', { docId: 'd1', layout: 'pages' });
926
951
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
927
952
  ['docs', 'page-layout', 'd1', '--layout=pages'],
928
953
  { account: undefined },
@@ -930,16 +955,16 @@ describe('gog_docs_page_layout', () => {
930
955
  });
931
956
 
932
957
  it('omits --layout when not provided (defaults to pageless on gog side)', async () => {
933
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
934
- const handlers = setupHandlers();
935
- await handlers.get('gog_docs_page_layout')!({ docId: 'd1' });
958
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
959
+ const harness = await setupHandlers();
960
+ await harness.callTool('gog_docs_page_layout', { docId: 'd1' });
936
961
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'page-layout', 'd1'], { account: undefined });
937
962
  });
938
963
 
939
964
  it('passes --page-size, page dimensions and all margins', async () => {
940
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
941
- const handlers = setupHandlers();
942
- await handlers.get('gog_docs_page_layout')!({
965
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
966
+ const harness = await setupHandlers();
967
+ await harness.callTool('gog_docs_page_layout', {
943
968
  docId: 'd1', layout: 'pages', pageSize: 'A4', pageWidth: '8.5in', pageHeight: '11in',
944
969
  marginTop: '1in', marginBottom: '1in', marginLeft: '0.75in', marginRight: '0.75in',
945
970
  });
@@ -953,33 +978,82 @@ describe('gog_docs_page_layout', () => {
953
978
 
954
979
  describe('gog_docs_cell_update', () => {
955
980
  it('passes required --row and --col', async () => {
956
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
957
- const handlers = setupHandlers();
958
- await handlers.get('gog_docs_cell_update')!({ docId: 'd1', row: 2, col: 3, content: 'hi' });
981
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
982
+ const harness = await setupHandlers();
983
+ await harness.callTool('gog_docs_cell_update', { docId: 'd1', row: 2, col: 3, content: 'hi' });
959
984
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
960
985
  ['docs', 'cell-update', 'd1', '--row=2', '--col=3', '--content=hi'],
961
986
  { account: undefined },
962
987
  );
963
988
  });
964
989
 
965
- it('supports empty-string content, --content-file, --append, --format, --table-index and --tab', async () => {
966
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
967
- const handlers = setupHandlers();
968
- await handlers.get('gog_docs_cell_update')!({
969
- docId: 'd1', row: 1, col: 1, content: '', contentFile: '/tmp/c.md', append: true,
990
+ it('supports empty-string content plus --append, --format, --table-index and --tab', async () => {
991
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
992
+ const harness = await setupHandlers();
993
+ await harness.callTool('gog_docs_cell_update', {
994
+ docId: 'd1', row: 1, col: 1, content: '', append: true,
970
995
  format: 'plain', tableIndex: -1, tab: 'Tab2',
971
996
  });
972
997
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
973
- ['docs', 'cell-update', 'd1', '--row=1', '--col=1', '--content=', '--content-file=/tmp/c.md',
998
+ ['docs', 'cell-update', 'd1', '--row=1', '--col=1', '--content=',
974
999
  '--append', '--format=plain', '--table-index=-1', '--tab=Tab2'],
975
1000
  { account: undefined },
976
1001
  );
977
1002
  });
978
1003
 
1004
+ it('passes --content-file on its own', async () => {
1005
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1006
+ const harness = await setupHandlers();
1007
+ await harness.callTool('gog_docs_cell_update', {
1008
+ docId: 'd1', row: 1, col: 1, contentFile: '/tmp/c.md',
1009
+ });
1010
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1011
+ ['docs', 'cell-update', 'd1', '--row=1', '--col=1', '--content-file=/tmp/c.md'],
1012
+ { account: undefined },
1013
+ );
1014
+ });
1015
+
1016
+ // gog itself refuses --content together with --content-file, so the wrapper
1017
+ // rejects the combination up front rather than shipping a doomed argv.
1018
+ it('rejects content and contentFile together without invoking gog', async () => {
1019
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1020
+ const harness = await setupHandlers();
1021
+ const res = await harness.callTool('gog_docs_cell_update', {
1022
+ docId: 'd1', row: 1, col: 1, content: 'hi', contentFile: '/tmp/c.md',
1023
+ });
1024
+ expect(res.isError).toBe(true);
1025
+ expect(JSON.stringify(res.content)).toContain('not both');
1026
+ expect(lib.runOrDiagnose).not.toHaveBeenCalled();
1027
+ });
1028
+
1029
+ // An empty string clears the cell, so it must NOT be treated as "no content"
1030
+ // and must NOT collide with contentFile's truthiness guard.
1031
+ it('treats empty-string content as a conflict with contentFile', async () => {
1032
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1033
+ const harness = await setupHandlers();
1034
+ const res = await harness.callTool('gog_docs_cell_update', {
1035
+ docId: 'd1', row: 1, col: 1, content: '', contentFile: '/tmp/c.md',
1036
+ });
1037
+ expect(res.isError).toBe(true);
1038
+ expect(lib.runOrDiagnose).not.toHaveBeenCalled();
1039
+ });
1040
+
1041
+ it('routes a large content value to a --content-file payload arg', async () => {
1042
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1043
+ const harness = await setupHandlers();
1044
+ const big = 'c'.repeat(lib.PAYLOAD_INLINE_MAX + 1);
1045
+ await harness.callTool('gog_docs_cell_update', { docId: 'd1', row: 2, col: 3, content: big });
1046
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1047
+ ['docs', 'cell-update', 'd1', '--row=2', '--col=3',
1048
+ { kind: 'file', flag: 'content-file', contents: big, ext: 'md' }],
1049
+ { account: undefined },
1050
+ );
1051
+ });
1052
+
979
1053
  it('omits content when not provided and forwards account', async () => {
980
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
981
- const handlers = setupHandlers();
982
- await handlers.get('gog_docs_cell_update')!({ docId: 'd1', row: 1, col: 2, account: 'a@b.com' });
1054
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1055
+ const harness = await setupHandlers();
1056
+ await harness.callTool('gog_docs_cell_update', { docId: 'd1', row: 1, col: 2, account: 'a@b.com' });
983
1057
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
984
1058
  ['docs', 'cell-update', 'd1', '--row=1', '--col=2'],
985
1059
  { account: 'a@b.com' },
@@ -989,9 +1063,9 @@ describe('gog_docs_cell_update', () => {
989
1063
 
990
1064
  describe('gog_docs_cell_style', () => {
991
1065
  it('passes required --row and --col', async () => {
992
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
993
- const handlers = setupHandlers();
994
- await handlers.get('gog_docs_cell_style')!({ docId: 'd1', row: 0, col: 0 });
1066
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1067
+ const harness = await setupHandlers();
1068
+ await harness.callTool('gog_docs_cell_style', { docId: 'd1', row: 0, col: 0 });
995
1069
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
996
1070
  ['docs', 'cell-style', 'd1', '--row=0', '--col=0'],
997
1071
  { account: undefined },
@@ -999,9 +1073,9 @@ describe('gog_docs_cell_style', () => {
999
1073
  });
1000
1074
 
1001
1075
  it('passes spans, colors and text styling', async () => {
1002
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1003
- const handlers = setupHandlers();
1004
- await handlers.get('gog_docs_cell_style')!({
1076
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1077
+ const harness = await setupHandlers();
1078
+ await harness.callTool('gog_docs_cell_style', {
1005
1079
  docId: 'd1', row: 1, col: 2, rowSpan: 2, colSpan: 3, backgroundColor: '#eee',
1006
1080
  textColor: '#111', bold: true, italic: true, underline: true, tableIndex: 1, tab: 'T',
1007
1081
  });
@@ -1014,37 +1088,67 @@ describe('gog_docs_cell_style', () => {
1014
1088
  });
1015
1089
 
1016
1090
  it('omits text styling flags when false', async () => {
1017
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1018
- const handlers = setupHandlers();
1019
- await handlers.get('gog_docs_cell_style')!({ docId: 'd1', row: 0, col: 0, bold: false, italic: false, underline: false });
1091
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1092
+ const harness = await setupHandlers();
1093
+ await harness.callTool('gog_docs_cell_style', { docId: 'd1', row: 0, col: 0, bold: false, italic: false, underline: false });
1020
1094
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1021
1095
  ['docs', 'cell-style', 'd1', '--row=0', '--col=0'],
1022
1096
  { account: undefined },
1023
1097
  );
1024
1098
  });
1099
+
1100
+ it('passes border, padding and content-alignment flags', async () => {
1101
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1102
+ const harness = await setupHandlers();
1103
+ await harness.callTool('gog_docs_cell_style', {
1104
+ docId: 'd1', row: 0, col: 0,
1105
+ borderAll: '1pt,#000,DASH', borderTop: '2pt', borderBottom: '2pt', borderLeft: '1pt', borderRight: '1pt',
1106
+ paddingAll: '5', paddingTop: '6pt', paddingBottom: '6pt', paddingLeft: '4mm', paddingRight: '4mm',
1107
+ contentAlign: 'middle',
1108
+ });
1109
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1110
+ ['docs', 'cell-style', 'd1', '--row=0', '--col=0',
1111
+ '--border-all=1pt,#000,DASH', '--border-top=2pt', '--border-bottom=2pt', '--border-left=1pt', '--border-right=1pt',
1112
+ '--padding-all=5', '--padding-top=6pt', '--padding-bottom=6pt', '--padding-left=4mm', '--padding-right=4mm',
1113
+ '--content-align=middle'],
1114
+ { account: undefined },
1115
+ );
1116
+ });
1025
1117
  });
1026
1118
 
1027
1119
  describe('gog_docs_insert_image', () => {
1028
1120
  it('passes required --file and defaults', async () => {
1029
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1030
- const handlers = setupHandlers();
1031
- await handlers.get('gog_docs_insert_image')!({ docId: 'd1', file: '/tmp/pic.png' });
1121
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1122
+ const harness = await setupHandlers();
1123
+ await harness.callTool('gog_docs_insert_image', { docId: 'd1', file: '/tmp/pic.png' });
1032
1124
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1033
- ['docs', 'insert-image', 'd1', '--file=/tmp/pic.png'],
1125
+ ['docs', 'insert-image', 'd1', '--file=/tmp/pic.png', '--force'],
1034
1126
  { account: undefined },
1035
1127
  );
1036
1128
  });
1037
1129
 
1038
1130
  it('passes placement, sizing, naming and restriction handling', async () => {
1039
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1040
- const handlers = setupHandlers();
1041
- await handlers.get('gog_docs_insert_image')!({
1131
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1132
+ const harness = await setupHandlers();
1133
+ await harness.callTool('gog_docs_insert_image', {
1042
1134
  docId: 'd1', file: '/tmp/pic.png', at: '{{logo}}', width: 200, height: 100,
1043
1135
  name: 'logo.png', parent: 'folder1', onRestricted: 'link', tab: 'T',
1044
1136
  });
1045
1137
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1046
1138
  ['docs', 'insert-image', 'd1', '--file=/tmp/pic.png', '--at={{logo}}', '--width=200',
1047
- '--height=100', '--name=logo.png', '--parent=folder1', '--on-restricted=link', '--tab=T'],
1139
+ '--height=100', '--name=logo.png', '--parent=folder1', '--on-restricted=link', '--tab=T', '--force'],
1140
+ { account: undefined },
1141
+ );
1142
+ });
1143
+
1144
+ it('passes --before and --after anchors', async () => {
1145
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1146
+ const harness = await setupHandlers();
1147
+ await harness.callTool('gog_docs_insert_image', {
1148
+ docId: 'd1', url: 'https://x.test/i.png', before: 'Intro', after: 'Outro',
1149
+ });
1150
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1151
+ ['docs', 'insert-image', 'd1', '--url=https://x.test/i.png', '--before=Intro', '--after=Outro'],
1048
1152
  { account: undefined },
1049
1153
  );
1050
1154
  });
@@ -1052,9 +1156,9 @@ describe('gog_docs_insert_image', () => {
1052
1156
 
1053
1157
  describe('gog_docs_insert_person', () => {
1054
1158
  it('passes required --email', async () => {
1055
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1056
- const handlers = setupHandlers();
1057
- await handlers.get('gog_docs_insert_person')!({ docId: 'd1', email: 'a@b.com' });
1159
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1160
+ const harness = await setupHandlers();
1161
+ await harness.callTool('gog_docs_insert_person', { docId: 'd1', email: 'a@b.com' });
1058
1162
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1059
1163
  ['docs', 'insert-person', 'd1', '--email=a@b.com'],
1060
1164
  { account: undefined },
@@ -1063,9 +1167,9 @@ describe('gog_docs_insert_person', () => {
1063
1167
 
1064
1168
  // gog 0.23.0
1065
1169
  it('anchors by text with --at/--occurrence/--match-case', async () => {
1066
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1067
- const handlers = setupHandlers();
1068
- await handlers.get('gog_docs_insert_person')!({ docId: 'd1', email: 'a@b.com', at: '@alice', occurrence: 1, matchCase: true });
1170
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1171
+ const harness = await setupHandlers();
1172
+ await harness.callTool('gog_docs_insert_person', { docId: 'd1', email: 'a@b.com', at: '@alice', occurrence: 1, matchCase: true });
1069
1173
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1070
1174
  ['docs', 'insert-person', 'd1', '--email=a@b.com', '--at=@alice', '--occurrence=1', '--match-case'],
1071
1175
  { account: undefined },
@@ -1073,9 +1177,9 @@ describe('gog_docs_insert_person', () => {
1073
1177
  });
1074
1178
 
1075
1179
  it('passes --index, --at-end and --tab', async () => {
1076
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1077
- const handlers = setupHandlers();
1078
- await handlers.get('gog_docs_insert_person')!({ docId: 'd1', email: 'a@b.com', index: 0, atEnd: true, tab: 'T' });
1180
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1181
+ const harness = await setupHandlers();
1182
+ await harness.callTool('gog_docs_insert_person', { docId: 'd1', email: 'a@b.com', index: 0, atEnd: true, tab: 'T' });
1079
1183
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1080
1184
  ['docs', 'insert-person', 'd1', '--email=a@b.com', '--index=0', '--at-end', '--tab=T'],
1081
1185
  { account: undefined },
@@ -1085,16 +1189,16 @@ describe('gog_docs_insert_person', () => {
1085
1189
 
1086
1190
  describe('gog_docs_insert_date_chip', () => {
1087
1191
  it('omits all optional flags by default', async () => {
1088
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1089
- const handlers = setupHandlers();
1090
- await handlers.get('gog_docs_insert_date_chip')!({ docId: 'd1' });
1192
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1193
+ const harness = await setupHandlers();
1194
+ await harness.callTool('gog_docs_insert_date_chip', { docId: 'd1' });
1091
1195
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'insert-date-chip', 'd1'], { account: undefined });
1092
1196
  });
1093
1197
 
1094
1198
  it('passes --date, --format, --index and --at-end', async () => {
1095
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1096
- const handlers = setupHandlers();
1097
- await handlers.get('gog_docs_insert_date_chip')!({ docId: 'd1', date: '2026-06-01', format: 'iso', index: 5, atEnd: true });
1199
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1200
+ const harness = await setupHandlers();
1201
+ await harness.callTool('gog_docs_insert_date_chip', { docId: 'd1', date: '2026-06-01', format: 'iso', index: 5, atEnd: true });
1098
1202
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1099
1203
  ['docs', 'insert-date-chip', 'd1', '--date=2026-06-01', '--format=iso', '--index=5', '--at-end'],
1100
1204
  { account: undefined },
@@ -1104,9 +1208,9 @@ describe('gog_docs_insert_date_chip', () => {
1104
1208
 
1105
1209
  describe('gog_docs_insert_table', () => {
1106
1210
  it('passes required --rows and --cols', async () => {
1107
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1108
- const handlers = setupHandlers();
1109
- await handlers.get('gog_docs_insert_table')!({ docId: 'd1', rows: 3, cols: 2 });
1211
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1212
+ const harness = await setupHandlers();
1213
+ await harness.callTool('gog_docs_insert_table', { docId: 'd1', rows: 3, cols: 2 });
1110
1214
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1111
1215
  ['docs', 'insert-table', 'd1', '--rows=3', '--cols=2'],
1112
1216
  { account: undefined },
@@ -1114,10 +1218,10 @@ describe('gog_docs_insert_table', () => {
1114
1218
  });
1115
1219
 
1116
1220
  it('passes --values-json, --index, --tab when provided', async () => {
1117
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1118
- const handlers = setupHandlers();
1221
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1222
+ const harness = await setupHandlers();
1119
1223
  const vj = '[["a","b"],["c","d"]]';
1120
- await handlers.get('gog_docs_insert_table')!({
1224
+ await harness.callTool('gog_docs_insert_table', {
1121
1225
  docId: 'd1', rows: 2, cols: 2, index: 10, valuesJson: vj, tab: 'Body',
1122
1226
  });
1123
1227
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -1127,9 +1231,9 @@ describe('gog_docs_insert_table', () => {
1127
1231
  });
1128
1232
 
1129
1233
  it('uses --at-end when atEnd is true', async () => {
1130
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1131
- const handlers = setupHandlers();
1132
- await handlers.get('gog_docs_insert_table')!({ docId: 'd1', rows: 1, cols: 1, atEnd: true });
1234
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1235
+ const harness = await setupHandlers();
1236
+ await harness.callTool('gog_docs_insert_table', { docId: 'd1', rows: 1, cols: 1, atEnd: true });
1133
1237
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1134
1238
  ['docs', 'insert-table', 'd1', '--rows=1', '--cols=1', '--at-end'],
1135
1239
  { account: undefined },
@@ -1139,9 +1243,9 @@ describe('gog_docs_insert_table', () => {
1139
1243
 
1140
1244
  describe('gog_docs_comments_reopen', () => {
1141
1245
  it('calls runOrDiagnose with docId and commentId', async () => {
1142
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1143
- const handlers = setupHandlers();
1144
- await handlers.get('gog_docs_comments_reopen')!({ docId: 'd1', commentId: 'c1' });
1246
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1247
+ const harness = await setupHandlers();
1248
+ await harness.callTool('gog_docs_comments_reopen', { docId: 'd1', commentId: 'c1' });
1145
1249
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1146
1250
  ['docs', 'comments', 'reopen', 'd1', 'c1'],
1147
1251
  { account: undefined },
@@ -1149,9 +1253,9 @@ describe('gog_docs_comments_reopen', () => {
1149
1253
  });
1150
1254
 
1151
1255
  it('forwards account', async () => {
1152
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1153
- const handlers = setupHandlers();
1154
- await handlers.get('gog_docs_comments_reopen')!({ docId: 'd1', commentId: 'c1', account: 'a@b.com' });
1256
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1257
+ const harness = await setupHandlers();
1258
+ await harness.callTool('gog_docs_comments_reopen', { docId: 'd1', commentId: 'c1', account: 'a@b.com' });
1155
1259
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1156
1260
  ['docs', 'comments', 'reopen', 'd1', 'c1'],
1157
1261
  { account: 'a@b.com' },
@@ -1163,16 +1267,16 @@ describe('gog_docs_comments_reopen', () => {
1163
1267
 
1164
1268
  describe('gog_docs_add_tab', () => {
1165
1269
  it('calls runOrDiagnose with only docId when no flags', async () => {
1166
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1167
- const handlers = setupHandlers();
1168
- await handlers.get('gog_docs_add_tab')!({ docId: 'abc' });
1270
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1271
+ const harness = await setupHandlers();
1272
+ await harness.callTool('gog_docs_add_tab', { docId: 'abc' });
1169
1273
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'add-tab', 'abc'], { account: undefined });
1170
1274
  });
1171
1275
 
1172
1276
  it('includes all flags when provided', async () => {
1173
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1174
- const handlers = setupHandlers();
1175
- await handlers.get('gog_docs_add_tab')!({
1277
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1278
+ const harness = await setupHandlers();
1279
+ await harness.callTool('gog_docs_add_tab', {
1176
1280
  docId: 'abc',
1177
1281
  title: 'Notes',
1178
1282
  index: 2,
@@ -1187,9 +1291,9 @@ describe('gog_docs_add_tab', () => {
1187
1291
  });
1188
1292
 
1189
1293
  it('includes --index when zero', async () => {
1190
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1191
- const handlers = setupHandlers();
1192
- await handlers.get('gog_docs_add_tab')!({ docId: 'abc', index: 0 });
1294
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1295
+ const harness = await setupHandlers();
1296
+ await harness.callTool('gog_docs_add_tab', { docId: 'abc', index: 0 });
1193
1297
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1194
1298
  ['docs', 'add-tab', 'abc', '--index=0'],
1195
1299
  { account: undefined },
@@ -1201,9 +1305,9 @@ describe('gog_docs_add_tab', () => {
1201
1305
 
1202
1306
  describe('gog_docs_rename_tab', () => {
1203
1307
  it('calls runOrDiagnose with tab and title', async () => {
1204
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1205
- const handlers = setupHandlers();
1206
- await handlers.get('gog_docs_rename_tab')!({ docId: 'abc', tab: 'Old', title: 'New' });
1308
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1309
+ const harness = await setupHandlers();
1310
+ await harness.callTool('gog_docs_rename_tab', { docId: 'abc', tab: 'Old', title: 'New' });
1207
1311
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1208
1312
  ['docs', 'rename-tab', 'abc', '--tab=Old', '--title=New'],
1209
1313
  { account: undefined },
@@ -1211,9 +1315,9 @@ describe('gog_docs_rename_tab', () => {
1211
1315
  });
1212
1316
 
1213
1317
  it('forwards account override', async () => {
1214
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1215
- const handlers = setupHandlers();
1216
- await handlers.get('gog_docs_rename_tab')!({ docId: 'abc', tab: 't1', title: 'New', account: 'a@b.com' });
1318
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1319
+ const harness = await setupHandlers();
1320
+ await harness.callTool('gog_docs_rename_tab', { docId: 'abc', tab: 't1', title: 'New', account: 'a@b.com' });
1217
1321
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1218
1322
  ['docs', 'rename-tab', 'abc', '--tab=t1', '--title=New'],
1219
1323
  { account: 'a@b.com' },
@@ -1225,21 +1329,21 @@ describe('gog_docs_rename_tab', () => {
1225
1329
 
1226
1330
  describe('gog_docs_delete_tab', () => {
1227
1331
  it('calls runOrDiagnose with tab', async () => {
1228
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1229
- const handlers = setupHandlers();
1230
- await handlers.get('gog_docs_delete_tab')!({ docId: 'abc', tab: 'Old' });
1332
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1333
+ const harness = await setupHandlers();
1334
+ await harness.callTool('gog_docs_delete_tab', { docId: 'abc', tab: 'Old' });
1231
1335
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1232
- ['docs', 'delete-tab', 'abc', '--tab=Old'],
1336
+ ['docs', 'delete-tab', 'abc', '--tab=Old', '--force'],
1233
1337
  { account: undefined },
1234
1338
  );
1235
1339
  });
1236
1340
 
1237
1341
  it('forwards account override', async () => {
1238
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1239
- const handlers = setupHandlers();
1240
- await handlers.get('gog_docs_delete_tab')!({ docId: 'abc', tab: 't1', account: 'a@b.com' });
1342
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1343
+ const harness = await setupHandlers();
1344
+ await harness.callTool('gog_docs_delete_tab', { docId: 'abc', tab: 't1', account: 'a@b.com' });
1241
1345
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1242
- ['docs', 'delete-tab', 'abc', '--tab=t1'],
1346
+ ['docs', 'delete-tab', 'abc', '--tab=t1', '--force'],
1243
1347
  { account: 'a@b.com' },
1244
1348
  );
1245
1349
  });
@@ -1249,16 +1353,16 @@ describe('gog_docs_delete_tab', () => {
1249
1353
 
1250
1354
  describe('gog_docs_clear', () => {
1251
1355
  it('calls runOrDiagnose with docId', async () => {
1252
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1253
- const handlers = setupHandlers();
1254
- await handlers.get('gog_docs_clear')!({ docId: 'abc' });
1356
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1357
+ const harness = await setupHandlers();
1358
+ await harness.callTool('gog_docs_clear', { docId: 'abc' });
1255
1359
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'clear', 'abc'], { account: undefined });
1256
1360
  });
1257
1361
 
1258
1362
  it('forwards account override', async () => {
1259
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1260
- const handlers = setupHandlers();
1261
- await handlers.get('gog_docs_clear')!({ docId: 'abc', account: 'a@b.com' });
1363
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1364
+ const harness = await setupHandlers();
1365
+ await harness.callTool('gog_docs_clear', { docId: 'abc', account: 'a@b.com' });
1262
1366
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'clear', 'abc'], { account: 'a@b.com' });
1263
1367
  });
1264
1368
  });
@@ -1267,9 +1371,9 @@ describe('gog_docs_clear', () => {
1267
1371
 
1268
1372
  describe('gog_docs_table_row_insert', () => {
1269
1373
  it('appends a row with values to a selected table', async () => {
1270
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1271
- const handlers = setupHandlers();
1272
- await handlers.get('gog_docs_table_row_insert')!({
1374
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1375
+ const harness = await setupHandlers();
1376
+ await harness.callTool('gog_docs_table_row_insert', {
1273
1377
  docId: 'd1', table: '2', at: 'end', valuesJson: '["A","B"]', tab: 'T',
1274
1378
  });
1275
1379
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -1279,18 +1383,18 @@ describe('gog_docs_table_row_insert', () => {
1279
1383
  });
1280
1384
 
1281
1385
  it('works with no optional flags', async () => {
1282
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1283
- const handlers = setupHandlers();
1284
- await handlers.get('gog_docs_table_row_insert')!({ docId: 'd1' });
1386
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1387
+ const harness = await setupHandlers();
1388
+ await harness.callTool('gog_docs_table_row_insert', { docId: 'd1' });
1285
1389
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'table-row', 'insert', 'd1'], { account: undefined });
1286
1390
  });
1287
1391
  });
1288
1392
 
1289
1393
  describe('gog_docs_table_row_delete', () => {
1290
1394
  it('deletes a row by 1-based number', async () => {
1291
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1292
- const handlers = setupHandlers();
1293
- await handlers.get('gog_docs_table_row_delete')!({ docId: 'd1', row: -1, table: 'Budget' });
1395
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1396
+ const harness = await setupHandlers();
1397
+ await harness.callTool('gog_docs_table_row_delete', { docId: 'd1', row: -1, table: 'Budget' });
1294
1398
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1295
1399
  ['docs', 'table-row', 'delete', 'd1', '--row=-1', '--table=Budget'],
1296
1400
  { account: undefined },
@@ -1300,9 +1404,9 @@ describe('gog_docs_table_row_delete', () => {
1300
1404
 
1301
1405
  describe('gog_docs_table_column_insert', () => {
1302
1406
  it('inserts a column before a 1-based index', async () => {
1303
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1304
- const handlers = setupHandlers();
1305
- await handlers.get('gog_docs_table_column_insert')!({ docId: 'd1', at: '3', table: '*', tab: 'T' });
1407
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1408
+ const harness = await setupHandlers();
1409
+ await harness.callTool('gog_docs_table_column_insert', { docId: 'd1', at: '3', table: '*', tab: 'T' });
1306
1410
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1307
1411
  ['docs', 'table-column', 'insert', 'd1', '--at=3', '--table=*', '--tab=T'],
1308
1412
  { account: undefined },
@@ -1312,9 +1416,9 @@ describe('gog_docs_table_column_insert', () => {
1312
1416
 
1313
1417
  describe('gog_docs_table_column_delete', () => {
1314
1418
  it('deletes a column by 1-based number', async () => {
1315
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1316
- const handlers = setupHandlers();
1317
- await handlers.get('gog_docs_table_column_delete')!({ docId: 'd1', col: 2 });
1419
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1420
+ const harness = await setupHandlers();
1421
+ await harness.callTool('gog_docs_table_column_delete', { docId: 'd1', col: 2 });
1318
1422
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1319
1423
  ['docs', 'table-column', 'delete', 'd1', '--col=2'],
1320
1424
  { account: undefined },
@@ -1324,9 +1428,9 @@ describe('gog_docs_table_column_delete', () => {
1324
1428
 
1325
1429
  describe('gog_docs_table_merge', () => {
1326
1430
  it('merges a 1-based cell range', async () => {
1327
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1328
- const handlers = setupHandlers();
1329
- await handlers.get('gog_docs_table_merge')!({ docId: 'd1', range: '1,1:2,3', table: '1' });
1431
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1432
+ const harness = await setupHandlers();
1433
+ await harness.callTool('gog_docs_table_merge', { docId: 'd1', range: '1,1:2,3', table: '1' });
1330
1434
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1331
1435
  ['docs', 'table-merge', 'd1', '--range=1,1:2,3', '--table=1'],
1332
1436
  { account: undefined },
@@ -1336,9 +1440,9 @@ describe('gog_docs_table_merge', () => {
1336
1440
 
1337
1441
  describe('gog_docs_table_unmerge', () => {
1338
1442
  it('unmerges the region containing a cell', async () => {
1339
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1340
- const handlers = setupHandlers();
1341
- await handlers.get('gog_docs_table_unmerge')!({ docId: 'd1', cell: '1,1', tab: 'T' });
1443
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1444
+ const harness = await setupHandlers();
1445
+ await harness.callTool('gog_docs_table_unmerge', { docId: 'd1', cell: '1,1', tab: 'T' });
1342
1446
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1343
1447
  ['docs', 'table-unmerge', 'd1', '--cell=1,1', '--tab=T'],
1344
1448
  { account: undefined },
@@ -1348,9 +1452,9 @@ describe('gog_docs_table_unmerge', () => {
1348
1452
 
1349
1453
  describe('gog_docs_named_range_create', () => {
1350
1454
  it('creates a range around matched text', async () => {
1351
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1352
- const handlers = setupHandlers();
1353
- await handlers.get('gog_docs_named_range_create')!({
1455
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1456
+ const harness = await setupHandlers();
1457
+ await harness.callTool('gog_docs_named_range_create', {
1354
1458
  docId: 'd1', name: 'intro', at: 'Introduction', occurrence: 1, matchCase: true,
1355
1459
  });
1356
1460
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
@@ -1360,9 +1464,9 @@ describe('gog_docs_named_range_create', () => {
1360
1464
  });
1361
1465
 
1362
1466
  it('creates a range from explicit indices', async () => {
1363
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1364
- const handlers = setupHandlers();
1365
- await handlers.get('gog_docs_named_range_create')!({ docId: 'd1', name: 'intro', start: 5, end: 20, tab: 'T' });
1467
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1468
+ const harness = await setupHandlers();
1469
+ await harness.callTool('gog_docs_named_range_create', { docId: 'd1', name: 'intro', start: 5, end: 20, tab: 'T' });
1366
1470
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1367
1471
  ['docs', 'named-range', 'create', 'd1', '--name=intro', '--start=5', '--end=20', '--tab=T'],
1368
1472
  { account: undefined },
@@ -1372,9 +1476,9 @@ describe('gog_docs_named_range_create', () => {
1372
1476
 
1373
1477
  describe('gog_docs_named_range_list', () => {
1374
1478
  it('lists named ranges, optionally filtered by name', async () => {
1375
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1376
- const handlers = setupHandlers();
1377
- await handlers.get('gog_docs_named_range_list')!({ docId: 'd1', name: 'intro' });
1479
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1480
+ const harness = await setupHandlers();
1481
+ await harness.callTool('gog_docs_named_range_list', { docId: 'd1', name: 'intro' });
1378
1482
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1379
1483
  ['docs', 'named-range', 'list', 'd1', '--name=intro'],
1380
1484
  { account: undefined },
@@ -1384,9 +1488,9 @@ describe('gog_docs_named_range_list', () => {
1384
1488
 
1385
1489
  describe('gog_docs_named_range_delete', () => {
1386
1490
  it('deletes by name or id', async () => {
1387
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1388
- const handlers = setupHandlers();
1389
- await handlers.get('gog_docs_named_range_delete')!({ docId: 'd1', nameOrId: 'intro' });
1491
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1492
+ const harness = await setupHandlers();
1493
+ await harness.callTool('gog_docs_named_range_delete', { docId: 'd1', nameOrId: 'intro' });
1390
1494
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1391
1495
  ['docs', 'named-range', 'delete', 'd1', 'intro'],
1392
1496
  { account: undefined },
@@ -1396,9 +1500,9 @@ describe('gog_docs_named_range_delete', () => {
1396
1500
 
1397
1501
  describe('gog_docs_named_range_replace', () => {
1398
1502
  it('replaces range content with text', async () => {
1399
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1400
- const handlers = setupHandlers();
1401
- await handlers.get('gog_docs_named_range_replace')!({ docId: 'd1', nameOrId: 'intro', text: 'New intro', tab: 'T' });
1503
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1504
+ const harness = await setupHandlers();
1505
+ await harness.callTool('gog_docs_named_range_replace', { docId: 'd1', nameOrId: 'intro', text: 'New intro', tab: 'T' });
1402
1506
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1403
1507
  ['docs', 'named-range', 'replace', 'd1', 'intro', '--text=New intro', '--tab=T'],
1404
1508
  { account: undefined },
@@ -1406,9 +1510,9 @@ describe('gog_docs_named_range_replace', () => {
1406
1510
  });
1407
1511
 
1408
1512
  it('replaces range content from a file', async () => {
1409
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1410
- const handlers = setupHandlers();
1411
- await handlers.get('gog_docs_named_range_replace')!({ docId: 'd1', nameOrId: 'kix.abc', file: '/tmp/intro.txt' });
1513
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1514
+ const harness = await setupHandlers();
1515
+ await harness.callTool('gog_docs_named_range_replace', { docId: 'd1', nameOrId: 'kix.abc', file: '/tmp/intro.txt' });
1412
1516
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1413
1517
  ['docs', 'named-range', 'replace', 'd1', 'kix.abc', '--file=/tmp/intro.txt'],
1414
1518
  { account: undefined },
@@ -1418,36 +1522,36 @@ describe('gog_docs_named_range_replace', () => {
1418
1522
 
1419
1523
  describe('gog_docs_tables_list', () => {
1420
1524
  it('enumerates tables, tab-aware', async () => {
1421
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1422
- const handlers = setupHandlers();
1423
- await handlers.get('gog_docs_tables_list')!({ docId: 'd1', tab: 'T' });
1525
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1526
+ const harness = await setupHandlers();
1527
+ await harness.callTool('gog_docs_tables_list', { docId: 'd1', tab: 'T' });
1424
1528
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'tables', 'list', 'd1', '--tab=T'], { account: undefined });
1425
1529
  });
1426
1530
  });
1427
1531
 
1428
1532
  describe('gog_docs_images_list', () => {
1429
1533
  it('enumerates images', async () => {
1430
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1431
- const handlers = setupHandlers();
1432
- await handlers.get('gog_docs_images_list')!({ docId: 'd1' });
1534
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1535
+ const harness = await setupHandlers();
1536
+ await harness.callTool('gog_docs_images_list', { docId: 'd1' });
1433
1537
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'images', 'list', 'd1'], { account: undefined });
1434
1538
  });
1435
1539
  });
1436
1540
 
1437
1541
  describe('gog_docs_headings_list', () => {
1438
1542
  it('enumerates headings with a level filter', async () => {
1439
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1440
- const handlers = setupHandlers();
1441
- await handlers.get('gog_docs_headings_list')!({ docId: 'd1', level: 2 });
1543
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1544
+ const harness = await setupHandlers();
1545
+ await harness.callTool('gog_docs_headings_list', { docId: 'd1', level: 2 });
1442
1546
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'headings', 'list', 'd1', '--level=2'], { account: undefined });
1443
1547
  });
1444
1548
  });
1445
1549
 
1446
1550
  describe('gog_docs_paragraphs_list', () => {
1447
1551
  it('enumerates paragraphs with a style filter', async () => {
1448
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1449
- const handlers = setupHandlers();
1450
- await handlers.get('gog_docs_paragraphs_list')!({ docId: 'd1', style: 'HEADING_2', tab: 'T' });
1552
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1553
+ const harness = await setupHandlers();
1554
+ await harness.callTool('gog_docs_paragraphs_list', { docId: 'd1', style: 'HEADING_2', tab: 'T' });
1451
1555
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1452
1556
  ['docs', 'paragraphs', 'list', 'd1', '--style=HEADING_2', '--tab=T'],
1453
1557
  { account: undefined },
@@ -1457,9 +1561,9 @@ describe('gog_docs_paragraphs_list', () => {
1457
1561
 
1458
1562
  describe('gog_docs_insert_image url mode', () => {
1459
1563
  it('inserts a public HTTPS image by url without file', async () => {
1460
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1461
- const handlers = setupHandlers();
1462
- await handlers.get('gog_docs_insert_image')!({ docId: 'd1', url: 'https://x.test/i.png', at: '{{logo}}' });
1564
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1565
+ const harness = await setupHandlers();
1566
+ await harness.callTool('gog_docs_insert_image', { docId: 'd1', url: 'https://x.test/i.png', at: '{{logo}}' });
1463
1567
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1464
1568
  ['docs', 'insert-image', 'd1', '--url=https://x.test/i.png', '--at={{logo}}'],
1465
1569
  { account: undefined },
@@ -1469,16 +1573,16 @@ describe('gog_docs_insert_image url mode', () => {
1469
1573
 
1470
1574
  describe('gog_docs_read json tab targeting', () => {
1471
1575
  it('passes --tab through to docs raw in json mode', async () => {
1472
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1473
- const handlers = setupHandlers();
1474
- await handlers.get('gog_docs_read')!({ docId: 'd1', format: 'json', tab: 'T' });
1576
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1577
+ const harness = await setupHandlers();
1578
+ await harness.callTool('gog_docs_read', { docId: 'd1', format: 'json', tab: 'T' });
1475
1579
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'raw', 'd1', '--pretty', '--tab=T'], { account: undefined });
1476
1580
  });
1477
1581
 
1478
1582
  it('passes --all-tabs through to docs raw in json mode', async () => {
1479
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1480
- const handlers = setupHandlers();
1481
- await handlers.get('gog_docs_read')!({ docId: 'd1', format: 'json', allTabs: true });
1583
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1584
+ const harness = await setupHandlers();
1585
+ await harness.callTool('gog_docs_read', { docId: 'd1', format: 'json', allTabs: true });
1482
1586
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'raw', 'd1', '--pretty', '--all-tabs'], { account: undefined });
1483
1587
  });
1484
1588
  });
@@ -1487,9 +1591,9 @@ describe('gog_docs_read json tab targeting', () => {
1487
1591
 
1488
1592
  describe('batch lifecycle tools', () => {
1489
1593
  it('gog_batch_begin opens a batch for a doc', async () => {
1490
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1491
- const handlers = setupHandlers();
1492
- await handlers.get('gog_batch_begin')!({ docId: 'd1', name: 'big-edit' });
1594
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1595
+ const harness = await setupHandlers();
1596
+ await harness.callTool('gog_batch_begin', { docId: 'd1', name: 'big-edit' });
1493
1597
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1494
1598
  ['batch', 'begin', '--doc=d1', '--name=big-edit'],
1495
1599
  { account: undefined },
@@ -1497,9 +1601,9 @@ describe('batch lifecycle tools', () => {
1497
1601
  });
1498
1602
 
1499
1603
  it('gog_batch_begin opens a batch without a name', async () => {
1500
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1501
- const handlers = setupHandlers();
1502
- await handlers.get('gog_batch_begin')!({ docId: 'd1' });
1604
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1605
+ const harness = await setupHandlers();
1606
+ await harness.callTool('gog_batch_begin', { docId: 'd1' });
1503
1607
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1504
1608
  ['batch', 'begin', '--doc=d1'],
1505
1609
  { account: undefined },
@@ -1507,9 +1611,9 @@ describe('batch lifecycle tools', () => {
1507
1611
  });
1508
1612
 
1509
1613
  it('gog_batch_end submits with split/recovery modes', async () => {
1510
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1511
- const handlers = setupHandlers();
1512
- await handlers.get('gog_batch_end')!({ batchId: 'b1', autoSplit: true, continueOnError: true });
1614
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1615
+ const harness = await setupHandlers();
1616
+ await harness.callTool('gog_batch_end', { batchId: 'b1', autoSplit: true, continueOnError: true });
1513
1617
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1514
1618
  ['batch', 'end', 'b1', '--auto-split', '--continue-on-error'],
1515
1619
  { account: undefined },
@@ -1517,9 +1621,9 @@ describe('batch lifecycle tools', () => {
1517
1621
  });
1518
1622
 
1519
1623
  it('gog_batch_end submits atomically with no options', async () => {
1520
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1521
- const handlers = setupHandlers();
1522
- await handlers.get('gog_batch_end')!({ batchId: 'b1' });
1624
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1625
+ const harness = await setupHandlers();
1626
+ await harness.callTool('gog_batch_end', { batchId: 'b1' });
1523
1627
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1524
1628
  ['batch', 'end', 'b1'],
1525
1629
  { account: undefined },
@@ -1527,32 +1631,32 @@ describe('batch lifecycle tools', () => {
1527
1631
  });
1528
1632
 
1529
1633
  it('gog_batch_abort discards a batch', async () => {
1530
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1531
- const handlers = setupHandlers();
1532
- await handlers.get('gog_batch_abort')!({ batchId: 'b1' });
1634
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1635
+ const harness = await setupHandlers();
1636
+ await harness.callTool('gog_batch_abort', { batchId: 'b1' });
1533
1637
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'abort', 'b1'], { account: undefined });
1534
1638
  });
1535
1639
 
1536
1640
  it('gog_batch_list / gog_batch_show read batch state', async () => {
1537
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1538
- const handlers = setupHandlers();
1539
- await handlers.get('gog_batch_list')!({});
1641
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1642
+ const harness = await setupHandlers();
1643
+ await harness.callTool('gog_batch_list', {});
1540
1644
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'list'], { account: undefined });
1541
- await handlers.get('gog_batch_show')!({ batchId: 'b1' });
1645
+ await harness.callTool('gog_batch_show', { batchId: 'b1' });
1542
1646
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'show', 'b1'], { account: undefined });
1543
1647
  });
1544
1648
 
1545
1649
  it('gog_batch_prune deletes stale batches', async () => {
1546
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1547
- const handlers = setupHandlers();
1548
- await handlers.get('gog_batch_prune')!({ olderThan: '72h' });
1650
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1651
+ const harness = await setupHandlers();
1652
+ await harness.callTool('gog_batch_prune', { olderThan: '72h' });
1549
1653
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'prune', '--older-than=72h'], { account: undefined });
1550
1654
  });
1551
1655
 
1552
1656
  it('gog_batch_prune without a duration prunes with no filter', async () => {
1553
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1554
- const handlers = setupHandlers();
1555
- await handlers.get('gog_batch_prune')!({});
1657
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1658
+ const harness = await setupHandlers();
1659
+ await harness.callTool('gog_batch_prune', {});
1556
1660
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'prune'], { account: undefined });
1557
1661
  });
1558
1662
  });
@@ -1583,9 +1687,9 @@ describe('batch param on docs mutation tools (gog 0.25.0)', () => {
1583
1687
 
1584
1688
  for (const { tool, input, expected } of cases) {
1585
1689
  it(`${tool} appends to a batch via --batch`, async () => {
1586
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1587
- const handlers = setupHandlers();
1588
- await handlers.get(tool)!(input);
1690
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1691
+ const harness = await setupHandlers();
1692
+ await harness.callTool(tool, input);
1589
1693
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(expected, { account: undefined });
1590
1694
  });
1591
1695
  }
@@ -1616,9 +1720,9 @@ describe('tab/table selector branch coverage', () => {
1616
1720
  ];
1617
1721
  for (const { tool, input, expected } of cases) {
1618
1722
  it(`${tool} passes its tab/table selectors`, async () => {
1619
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1620
- const handlers = setupHandlers();
1621
- await handlers.get(tool)!(input);
1723
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1724
+ const harness = await setupHandlers();
1725
+ await harness.callTool(tool, input);
1622
1726
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(expected, { account: undefined });
1623
1727
  });
1624
1728
  }
@@ -1635,10 +1739,420 @@ describe('bare-call branch coverage (no optional flags)', () => {
1635
1739
  ];
1636
1740
  for (const { tool, input, expected } of cases) {
1637
1741
  it(`${tool} with no optional flags`, async () => {
1638
- vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
1639
- const handlers = setupHandlers();
1640
- await handlers.get(tool)!(input);
1742
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1743
+ const harness = await setupHandlers();
1744
+ await harness.callTool(tool, input);
1641
1745
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(expected, { account: undefined });
1642
1746
  });
1643
1747
  }
1644
1748
  });
1749
+
1750
+ // ===========================================================================
1751
+ // gog 0.30 docs authoring (PR2)
1752
+ // ===========================================================================
1753
+
1754
+ describe('segment targeting (gog 0.30)', () => {
1755
+ it('gog_docs_insert forwards --segment', async () => {
1756
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1757
+ const harness = await setupHandlers();
1758
+ await harness.callTool('gog_docs_insert', { docId: 'd1', content: 'hi', segment: 'kix.h1' });
1759
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1760
+ ['docs', 'insert', 'd1', 'hi', '--segment=kix.h1'],
1761
+ { account: undefined },
1762
+ );
1763
+ });
1764
+
1765
+ it('gog_docs_update forwards --segment', async () => {
1766
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1767
+ const harness = await setupHandlers();
1768
+ await harness.callTool('gog_docs_update', { docId: 'd1', text: 'hi', segment: 'kix.f1' });
1769
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1770
+ ['docs', 'update', 'd1', '--text=hi', '--segment=kix.f1'],
1771
+ { account: undefined },
1772
+ );
1773
+ });
1774
+
1775
+ it('gog_docs_delete forwards --segment', async () => {
1776
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1777
+ const harness = await setupHandlers();
1778
+ await harness.callTool('gog_docs_delete', { docId: 'd1', start: 1, end: 5, segment: 'kix.h1' });
1779
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1780
+ ['docs', 'delete', '--start=1', '--end=5', 'd1', '--segment=kix.h1'],
1781
+ { account: undefined },
1782
+ );
1783
+ });
1784
+
1785
+ it('gog_docs_find_range forwards --segment', async () => {
1786
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1787
+ const harness = await setupHandlers();
1788
+ await harness.callTool('gog_docs_find_range', { docId: 'd1', text: 'Heading', segment: 'kix.h1' });
1789
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1790
+ ['docs', 'find-range', 'd1', 'Heading', '--segment=kix.h1'],
1791
+ { account: undefined },
1792
+ );
1793
+ });
1794
+ });
1795
+
1796
+ describe('gog_docs_format paragraph list / indent / spacing / keep (gog 0.30)', () => {
1797
+ it('adds bullets, indentation, spacing, keep (true) and segment', async () => {
1798
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1799
+ const harness = await setupHandlers();
1800
+ await harness.callTool('gog_docs_format', {
1801
+ docId: 'd1', bullets: true, bulletPreset: 'BULLET_DISC_CIRCLE_SQUARE',
1802
+ indentStart: 18, indentEnd: 6, indentFirstLine: 36, spaceAbove: 4, spaceBelow: 8,
1803
+ keepLinesTogether: true, keepWithNext: true, segment: 'kix.h1',
1804
+ });
1805
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1806
+ ['docs', 'format', 'd1', '--bullets', '--bullet-preset=BULLET_DISC_CIRCLE_SQUARE',
1807
+ '--indent-start=18', '--indent-end=6', '--indent-first-line=36', '--space-above=4', '--space-below=8',
1808
+ '--keep-lines-together', '--keep-with-next', '--segment=kix.h1'],
1809
+ { account: undefined },
1810
+ );
1811
+ });
1812
+
1813
+ it('adds numbering, removal and negated keep (false)', async () => {
1814
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1815
+ const harness = await setupHandlers();
1816
+ await harness.callTool('gog_docs_format', {
1817
+ docId: 'd1', ordered: true, noBullets: true, keepLinesTogether: false, keepWithNext: false,
1818
+ });
1819
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1820
+ ['docs', 'format', 'd1', '--ordered', '--no-bullets', '--no-keep-lines-together', '--no-keep-with-next'],
1821
+ { account: undefined },
1822
+ );
1823
+ });
1824
+ });
1825
+
1826
+ describe('gog_docs_insert_footnote', () => {
1827
+ it('passes text and anchor flags', async () => {
1828
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1829
+ const harness = await setupHandlers();
1830
+ await harness.callTool('gog_docs_insert_footnote', {
1831
+ docId: 'd1', text: 'note', file: '/tmp/n.txt', index: 5, atEnd: true,
1832
+ at: 'word', occurrence: 2, matchCase: true, tab: 'T',
1833
+ });
1834
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1835
+ ['docs', 'insert-footnote', 'd1', '--text=note', '--file=/tmp/n.txt', '--index=5', '--at-end',
1836
+ '--at=word', '--occurrence=2', '--match-case', '--tab=T'],
1837
+ { account: undefined },
1838
+ );
1839
+ });
1840
+
1841
+ it('works with just docId', async () => {
1842
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1843
+ const harness = await setupHandlers();
1844
+ await harness.callTool('gog_docs_insert_footnote', { docId: 'd1' });
1845
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'insert-footnote', 'd1'], { account: undefined });
1846
+ });
1847
+ });
1848
+
1849
+ describe('gog_docs_insert_section_break', () => {
1850
+ it('passes type and anchor flags', async () => {
1851
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1852
+ const harness = await setupHandlers();
1853
+ await harness.callTool('gog_docs_insert_section_break', {
1854
+ docId: 'd1', type: 'continuous', index: 3, atEnd: true, at: 'x',
1855
+ occurrence: 1, matchCase: true, tab: 'T', batch: 'b1',
1856
+ });
1857
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1858
+ ['docs', 'insert-section-break', 'd1', '--type=continuous', '--index=3', '--at-end',
1859
+ '--at=x', '--occurrence=1', '--match-case', '--tab=T', '--batch=b1'],
1860
+ { account: undefined },
1861
+ );
1862
+ });
1863
+
1864
+ it('works with just docId', async () => {
1865
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1866
+ const harness = await setupHandlers();
1867
+ await harness.callTool('gog_docs_insert_section_break', { docId: 'd1' });
1868
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'insert-section-break', 'd1'], { account: undefined });
1869
+ });
1870
+ });
1871
+
1872
+ describe('gog_docs_insert_horizontal_rule', () => {
1873
+ it('passes anchor flags', async () => {
1874
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1875
+ const harness = await setupHandlers();
1876
+ await harness.callTool('gog_docs_insert_horizontal_rule', {
1877
+ docId: 'd1', index: 2, atEnd: true, at: 'x', occurrence: 1, matchCase: true, tab: 'T', batch: 'b1',
1878
+ });
1879
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1880
+ ['docs', 'insert-horizontal-rule', 'd1', '--index=2', '--at-end', '--at=x',
1881
+ '--occurrence=1', '--match-case', '--tab=T', '--batch=b1'],
1882
+ { account: undefined },
1883
+ );
1884
+ });
1885
+
1886
+ it('works with just docId', async () => {
1887
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1888
+ const harness = await setupHandlers();
1889
+ await harness.callTool('gog_docs_insert_horizontal_rule', { docId: 'd1' });
1890
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'insert-horizontal-rule', 'd1'], { account: undefined });
1891
+ });
1892
+ });
1893
+
1894
+ describe('gog_docs_section_columns', () => {
1895
+ it('passes count, separator and anchor flags', async () => {
1896
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1897
+ const harness = await setupHandlers();
1898
+ await harness.callTool('gog_docs_section_columns', {
1899
+ docId: 'd1', count: 2, separator: 'between', index: 4, atEnd: true,
1900
+ at: 'x', occurrence: 1, matchCase: true, tab: 'T', batch: 'b1',
1901
+ });
1902
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1903
+ ['docs', 'section-columns', 'd1', '--count=2', '--separator=between', '--index=4', '--at-end',
1904
+ '--at=x', '--occurrence=1', '--match-case', '--tab=T', '--batch=b1'],
1905
+ { account: undefined },
1906
+ );
1907
+ });
1908
+
1909
+ it('passes only the required --count', async () => {
1910
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1911
+ const harness = await setupHandlers();
1912
+ await harness.callTool('gog_docs_section_columns', { docId: 'd1', count: 1 });
1913
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'section-columns', 'd1', '--count=1'], { account: undefined });
1914
+ });
1915
+ });
1916
+
1917
+ describe('gog_docs header lifecycle', () => {
1918
+ it('header_list passes --tab', async () => {
1919
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1920
+ const harness = await setupHandlers();
1921
+ await harness.callTool('gog_docs_header_list', { docId: 'd1', tab: 'T' });
1922
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'header', 'list', 'd1', '--tab=T'], { account: undefined });
1923
+ });
1924
+
1925
+ it('header_list bare', async () => {
1926
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1927
+ const harness = await setupHandlers();
1928
+ await harness.callTool('gog_docs_header_list', { docId: 'd1' });
1929
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'header', 'list', 'd1'], { account: undefined });
1930
+ });
1931
+
1932
+ it('header_create passes text and anchor flags', async () => {
1933
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1934
+ const harness = await setupHandlers();
1935
+ await harness.callTool('gog_docs_header_create', {
1936
+ docId: 'd1', text: 'Title', file: '/tmp/h.txt', index: 1, atEnd: true,
1937
+ at: 'x', occurrence: 1, matchCase: true, tab: 'T',
1938
+ });
1939
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1940
+ ['docs', 'header', 'create', 'd1', '--text=Title', '--file=/tmp/h.txt', '--index=1', '--at-end',
1941
+ '--at=x', '--occurrence=1', '--match-case', '--tab=T'],
1942
+ { account: undefined },
1943
+ );
1944
+ });
1945
+
1946
+ it('header_create bare', async () => {
1947
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1948
+ const harness = await setupHandlers();
1949
+ await harness.callTool('gog_docs_header_create', { docId: 'd1' });
1950
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'header', 'create', 'd1'], { account: undefined });
1951
+ });
1952
+
1953
+ it('header_delete passes id and --tab', async () => {
1954
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1955
+ const harness = await setupHandlers();
1956
+ await harness.callTool('gog_docs_header_delete', { docId: 'd1', headerId: 'kix.h1', tab: 'T' });
1957
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'header', 'delete', 'd1', 'kix.h1', '--tab=T', '--force'], { account: undefined });
1958
+ });
1959
+
1960
+ it('header_delete bare', async () => {
1961
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1962
+ const harness = await setupHandlers();
1963
+ await harness.callTool('gog_docs_header_delete', { docId: 'd1', headerId: 'kix.h1' });
1964
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'header', 'delete', 'd1', 'kix.h1', '--force'], { account: undefined });
1965
+ });
1966
+ });
1967
+
1968
+ describe('gog_docs footer lifecycle', () => {
1969
+ it('footer_list passes --tab', async () => {
1970
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1971
+ const harness = await setupHandlers();
1972
+ await harness.callTool('gog_docs_footer_list', { docId: 'd1', tab: 'T' });
1973
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'footer', 'list', 'd1', '--tab=T'], { account: undefined });
1974
+ });
1975
+
1976
+ it('footer_list bare', async () => {
1977
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1978
+ const harness = await setupHandlers();
1979
+ await harness.callTool('gog_docs_footer_list', { docId: 'd1' });
1980
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'footer', 'list', 'd1'], { account: undefined });
1981
+ });
1982
+
1983
+ it('footer_create passes text and anchor flags', async () => {
1984
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1985
+ const harness = await setupHandlers();
1986
+ await harness.callTool('gog_docs_footer_create', {
1987
+ docId: 'd1', text: 'Foot', file: '/tmp/f.txt', index: 1, atEnd: true,
1988
+ at: 'x', occurrence: 1, matchCase: true, tab: 'T',
1989
+ });
1990
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
1991
+ ['docs', 'footer', 'create', 'd1', '--text=Foot', '--file=/tmp/f.txt', '--index=1', '--at-end',
1992
+ '--at=x', '--occurrence=1', '--match-case', '--tab=T'],
1993
+ { account: undefined },
1994
+ );
1995
+ });
1996
+
1997
+ it('footer_create bare', async () => {
1998
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
1999
+ const harness = await setupHandlers();
2000
+ await harness.callTool('gog_docs_footer_create', { docId: 'd1' });
2001
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'footer', 'create', 'd1'], { account: undefined });
2002
+ });
2003
+
2004
+ it('footer_delete passes id and --tab', async () => {
2005
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2006
+ const harness = await setupHandlers();
2007
+ await harness.callTool('gog_docs_footer_delete', { docId: 'd1', footerId: 'kix.f1', tab: 'T' });
2008
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'footer', 'delete', 'd1', 'kix.f1', '--tab=T', '--force'], { account: undefined });
2009
+ });
2010
+
2011
+ it('footer_delete bare', async () => {
2012
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2013
+ const harness = await setupHandlers();
2014
+ await harness.callTool('gog_docs_footer_delete', { docId: 'd1', footerId: 'kix.f1' });
2015
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'footer', 'delete', 'd1', 'kix.f1', '--force'], { account: undefined });
2016
+ });
2017
+ });
2018
+
2019
+ describe('gog_docs_replace_image', () => {
2020
+ it('passes url + object-id targeting', async () => {
2021
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2022
+ const harness = await setupHandlers();
2023
+ await harness.callTool('gog_docs_replace_image', {
2024
+ docId: 'd1', url: 'https://x.test/i.png', objectId: 'kix.img1', tab: 'T',
2025
+ });
2026
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2027
+ ['docs', 'replace-image', 'd1', '--url=https://x.test/i.png', '--object-id=kix.img1', '--tab=T'],
2028
+ { account: undefined },
2029
+ );
2030
+ });
2031
+
2032
+ it('passes file + alt-text + name + parent targeting', async () => {
2033
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2034
+ const harness = await setupHandlers();
2035
+ await harness.callTool('gog_docs_replace_image', {
2036
+ docId: 'd1', file: '/tmp/i.png', matchAlt: 'logo', name: 'new.png', parent: 'folder1',
2037
+ });
2038
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2039
+ ['docs', 'replace-image', 'd1', '--file=/tmp/i.png', '--match-alt=logo', '--name=new.png', '--parent=folder1', '--force'],
2040
+ { account: undefined },
2041
+ );
2042
+ });
2043
+ });
2044
+
2045
+ describe('gog_docs_table_row_pin_header', () => {
2046
+ it('passes --rows and table selector', async () => {
2047
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2048
+ const harness = await setupHandlers();
2049
+ await harness.callTool('gog_docs_table_row_pin_header', { docId: 'd1', rows: 2, table: '1', tab: 'T' });
2050
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2051
+ ['docs', 'table-row', 'pin-header', 'd1', '--rows=2', '--table=1', '--tab=T'],
2052
+ { account: undefined },
2053
+ );
2054
+ });
2055
+
2056
+ it('unpins with rows=0 and no selector', async () => {
2057
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2058
+ const harness = await setupHandlers();
2059
+ await harness.callTool('gog_docs_table_row_pin_header', { docId: 'd1', rows: 0 });
2060
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2061
+ ['docs', 'table-row', 'pin-header', 'd1', '--rows=0'],
2062
+ { account: undefined },
2063
+ );
2064
+ });
2065
+ });
2066
+
2067
+ describe('gog_docs_table_row_style', () => {
2068
+ it('passes row, min-height and --prevent-overflow (true)', async () => {
2069
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2070
+ const harness = await setupHandlers();
2071
+ await harness.callTool('gog_docs_table_row_style', {
2072
+ docId: 'd1', row: 1, minHeight: '20pt', preventOverflow: true, table: '1', tab: 'T',
2073
+ });
2074
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2075
+ ['docs', 'table-row', 'style', 'd1', '--row=1', '--min-height=20pt', '--prevent-overflow', '--table=1', '--tab=T'],
2076
+ { account: undefined },
2077
+ );
2078
+ });
2079
+
2080
+ it('uses --no-prevent-overflow (false) and styles all rows', async () => {
2081
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2082
+ const harness = await setupHandlers();
2083
+ await harness.callTool('gog_docs_table_row_style', { docId: 'd1', preventOverflow: false });
2084
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2085
+ ['docs', 'table-row', 'style', 'd1', '--no-prevent-overflow'],
2086
+ { account: undefined },
2087
+ );
2088
+ });
2089
+ });
2090
+
2091
+ describe('gog_docs_table_row_style overflow-unset branch', () => {
2092
+ it('omits the overflow flag when preventOverflow is unset', async () => {
2093
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2094
+ const harness = await setupHandlers();
2095
+ await harness.callTool('gog_docs_table_row_style', { docId: 'd1', row: 2, minHeight: '30pt' });
2096
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2097
+ ['docs', 'table-row', 'style', 'd1', '--row=2', '--min-height=30pt'],
2098
+ { account: undefined },
2099
+ );
2100
+ });
2101
+ });
2102
+
2103
+ // --- gog_docs_suggestions_list ---
2104
+
2105
+ describe('gog_docs_suggestions_list', () => {
2106
+ it('calls runOrDiagnose with correct args', async () => {
2107
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2108
+ const harness = await setupHandlers();
2109
+ await harness.callTool('gog_docs_suggestions_list', { docId: 'd1' });
2110
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'suggestions', 'list', 'd1'], { account: undefined });
2111
+ });
2112
+
2113
+ it('includes --tab when provided', async () => {
2114
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2115
+ const harness = await setupHandlers();
2116
+ await harness.callTool('gog_docs_suggestions_list', { docId: 'd1', tab: 'Notes' });
2117
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2118
+ ['docs', 'suggestions', 'list', 'd1', '--tab=Notes'],
2119
+ { account: undefined },
2120
+ );
2121
+ });
2122
+
2123
+ it('forwards account override', async () => {
2124
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2125
+ const harness = await setupHandlers();
2126
+ await harness.callTool('gog_docs_suggestions_list', { docId: 'd1', account: 'other@gmail.com' });
2127
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2128
+ ['docs', 'suggestions', 'list', 'd1'],
2129
+ { account: 'other@gmail.com' },
2130
+ );
2131
+ });
2132
+ });
2133
+
2134
+ // --- gog_docs_format spacingMode ---
2135
+
2136
+ describe('gog_docs_format spacingMode', () => {
2137
+ it('passes --spacing-mode alongside space-above/space-below', async () => {
2138
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2139
+ const harness = await setupHandlers();
2140
+ await harness.callTool('gog_docs_format', {
2141
+ docId: 'd1', spaceAbove: 6, spaceBelow: 12, spacingMode: 'COLLAPSE_LISTS',
2142
+ });
2143
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2144
+ ['docs', 'format', 'd1', '--space-above=6', '--space-below=12', '--spacing-mode=COLLAPSE_LISTS'],
2145
+ { account: undefined },
2146
+ );
2147
+ });
2148
+
2149
+ it('omits --spacing-mode when unset', async () => {
2150
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(rawTextResult('{}'));
2151
+ const harness = await setupHandlers();
2152
+ await harness.callTool('gog_docs_format', { docId: 'd1', spaceAbove: 6 });
2153
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
2154
+ ['docs', 'format', 'd1', '--space-above=6'],
2155
+ { account: undefined },
2156
+ );
2157
+ });
2158
+ });