gogcli-mcp-docs 1.0.9 → 2.0.2

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,35 +1,22 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { registerExtraDocsTools } from '../../src/tools/docs-extra.js';
4
+ import * as lib from '../../../gogcli-mcp/src/lib.js';
3
5
 
4
- vi.mock('../../../gogcli-mcp/src/lib.js', async () => {
5
- const { z } = await import('zod');
6
- const mockRun = vi.fn<(args: string[], options?: { account?: string }) => Promise<string>>();
7
- const accountParam = z.string().optional().describe('Google account email');
8
- const toText = (output: string) => ({ content: [{ type: 'text' as const, text: output }] });
9
- const toError = (err: unknown) =>
10
- toText(err instanceof Error ? `Error: ${err.message}` : String(err));
11
-
6
+ vi.mock('../../../gogcli-mcp/src/lib.js', async (importOriginal) => {
7
+ const actual = await importOriginal<typeof lib>();
12
8
  return {
13
- createBaseServer: vi.fn(),
14
- run: mockRun,
15
- accountParam,
16
- toText,
17
- toError,
18
- runOrDiagnose: async (args: string[], options: { account?: string }) => {
19
- try {
20
- return toText(await mockRun(args, options));
21
- } catch (err) {
22
- return toError(err);
23
- }
24
- },
9
+ ...actual,
10
+ runOrDiagnose: vi.fn(),
25
11
  };
26
12
  });
27
13
 
28
- import { run } from '../../../gogcli-mcp/src/lib.js';
29
- import { registerExtraDocsTools } from '../../src/tools/docs-extra.js';
30
-
31
14
  type ToolHandler = (args: Record<string, unknown>) => Promise<{ content: Array<{ type: string; text: string }> }>;
32
15
 
16
+ function toText(text: string) {
17
+ return { content: [{ type: 'text', text }] };
18
+ }
19
+
33
20
  function setupHandlers(): Map<string, ToolHandler> {
34
21
  const server = new McpServer({ name: 'test', version: '0.0.0' });
35
22
  const handlers = new Map<string, ToolHandler>();
@@ -46,608 +33,503 @@ beforeEach(() => vi.clearAllMocks());
46
33
  // --- gog_docs_copy ---
47
34
 
48
35
  describe('gog_docs_copy', () => {
49
- it('calls run with correct args', async () => {
50
- vi.mocked(run).mockResolvedValue('{"docId":"newid"}');
36
+ it('calls runOrDiagnose with correct args', async () => {
37
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
51
38
  const handlers = setupHandlers();
52
- const result = await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'My Copy' });
53
- expect(run).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'My Copy'], { account: undefined });
54
- expect(result.content[0].text).toContain('newid');
39
+ await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'My Copy' });
40
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'My Copy'], { account: undefined });
55
41
  });
56
42
 
57
43
  it('includes --parent when provided', async () => {
58
- vi.mocked(run).mockResolvedValue('{}');
44
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
59
45
  const handlers = setupHandlers();
60
46
  await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'Copy', parent: 'folder123' });
61
- expect(run).toHaveBeenCalledWith(
47
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
62
48
  ['docs', 'copy', 'abc', 'Copy', '--parent=folder123'],
63
49
  { account: undefined },
64
50
  );
65
51
  });
66
52
 
67
53
  it('omits --parent when not provided', async () => {
68
- vi.mocked(run).mockResolvedValue('{}');
54
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
69
55
  const handlers = setupHandlers();
70
56
  await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'Copy' });
71
- expect(run).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'Copy'], { account: undefined });
57
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'Copy'], { account: undefined });
72
58
  });
73
59
 
74
60
  it('forwards account override', async () => {
75
- vi.mocked(run).mockResolvedValue('{}');
61
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
76
62
  const handlers = setupHandlers();
77
63
  await handlers.get('gog_docs_copy')!({ docId: 'abc', title: 'Copy', account: 'other@gmail.com' });
78
- expect(run).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'Copy'], { account: 'other@gmail.com' });
79
- });
80
-
81
- it('returns error text on failure', async () => {
82
- vi.mocked(run).mockRejectedValue(new Error('Copy failed'));
83
- const handlers = setupHandlers();
84
- const result = await handlers.get('gog_docs_copy')!({ docId: 'bad', title: 'x' });
85
- expect(result.content[0].text).toBe('Error: Copy failed');
64
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'copy', 'abc', 'Copy'], { account: 'other@gmail.com' });
86
65
  });
87
66
  });
88
67
 
89
68
  // --- gog_docs_delete ---
90
69
 
91
70
  describe('gog_docs_delete', () => {
92
- it('calls run with correct args', async () => {
93
- vi.mocked(run).mockResolvedValue('{}');
71
+ it('calls runOrDiagnose with correct args', async () => {
72
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
94
73
  const handlers = setupHandlers();
95
74
  await handlers.get('gog_docs_delete')!({ docId: 'abc', start: 5, end: 10 });
96
- expect(run).toHaveBeenCalledWith(
75
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
97
76
  ['docs', 'delete', '--start=5', '--end=10', 'abc'],
98
77
  { account: undefined },
99
78
  );
100
79
  });
101
80
 
102
81
  it('includes --tab-id when provided', async () => {
103
- vi.mocked(run).mockResolvedValue('{}');
82
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
104
83
  const handlers = setupHandlers();
105
84
  await handlers.get('gog_docs_delete')!({ docId: 'abc', start: 1, end: 5, tabId: 'tab1' });
106
- expect(run).toHaveBeenCalledWith(
85
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
107
86
  ['docs', 'delete', '--start=1', '--end=5', 'abc', '--tab-id=tab1'],
108
87
  { account: undefined },
109
88
  );
110
89
  });
111
90
 
112
91
  it('omits --tab-id when not provided', async () => {
113
- vi.mocked(run).mockResolvedValue('{}');
92
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
114
93
  const handlers = setupHandlers();
115
94
  await handlers.get('gog_docs_delete')!({ docId: 'abc', start: 1, end: 5 });
116
- expect(run).toHaveBeenCalledWith(
95
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
117
96
  ['docs', 'delete', '--start=1', '--end=5', 'abc'],
118
97
  { account: undefined },
119
98
  );
120
99
  });
121
-
122
- it('returns error text on failure', async () => {
123
- vi.mocked(run).mockRejectedValue(new Error('Delete failed'));
124
- const handlers = setupHandlers();
125
- const result = await handlers.get('gog_docs_delete')!({ docId: 'bad', start: 1, end: 5 });
126
- expect(result.content[0].text).toBe('Error: Delete failed');
127
- });
128
100
  });
129
101
 
130
102
  // --- gog_docs_edit ---
131
103
 
132
104
  describe('gog_docs_edit', () => {
133
- it('calls run with correct args', async () => {
134
- vi.mocked(run).mockResolvedValue('{}');
105
+ it('calls runOrDiagnose with correct args', async () => {
106
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
135
107
  const handlers = setupHandlers();
136
108
  await handlers.get('gog_docs_edit')!({ docId: 'abc', find: 'old', replace: 'new' });
137
- expect(run).toHaveBeenCalledWith(
109
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
138
110
  ['docs', 'edit', 'abc', 'old', 'new'],
139
111
  { account: undefined },
140
112
  );
141
113
  });
142
114
 
143
115
  it('includes --match-case when true', async () => {
144
- vi.mocked(run).mockResolvedValue('{}');
116
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
145
117
  const handlers = setupHandlers();
146
118
  await handlers.get('gog_docs_edit')!({ docId: 'abc', find: 'old', replace: 'new', matchCase: true });
147
- expect(run).toHaveBeenCalledWith(
119
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
148
120
  ['docs', 'edit', 'abc', 'old', 'new', '--match-case'],
149
121
  { account: undefined },
150
122
  );
151
123
  });
152
124
 
153
125
  it('omits --match-case when false', async () => {
154
- vi.mocked(run).mockResolvedValue('{}');
126
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
155
127
  const handlers = setupHandlers();
156
128
  await handlers.get('gog_docs_edit')!({ docId: 'abc', find: 'old', replace: 'new', matchCase: false });
157
- expect(run).toHaveBeenCalledWith(
129
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
158
130
  ['docs', 'edit', 'abc', 'old', 'new'],
159
131
  { account: undefined },
160
132
  );
161
133
  });
162
-
163
- it('returns error text on failure', async () => {
164
- vi.mocked(run).mockRejectedValue(new Error('Edit failed'));
165
- const handlers = setupHandlers();
166
- const result = await handlers.get('gog_docs_edit')!({ docId: 'bad', find: 'x', replace: 'y' });
167
- expect(result.content[0].text).toBe('Error: Edit failed');
168
- });
169
134
  });
170
135
 
171
136
  // --- gog_docs_export ---
172
137
 
173
138
  describe('gog_docs_export', () => {
174
- it('calls run with correct args', async () => {
175
- vi.mocked(run).mockResolvedValue('Exported to file.pdf');
139
+ it('calls runOrDiagnose with correct args', async () => {
140
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
176
141
  const handlers = setupHandlers();
177
- const result = await handlers.get('gog_docs_export')!({ docId: 'abc' });
178
- expect(run).toHaveBeenCalledWith(['docs', 'export', 'abc'], { account: undefined });
179
- expect(result.content[0].text).toContain('Exported');
142
+ await handlers.get('gog_docs_export')!({ docId: 'abc' });
143
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'export', 'abc'], { account: undefined });
180
144
  });
181
145
 
182
146
  it('includes --format when provided', async () => {
183
- vi.mocked(run).mockResolvedValue('{}');
147
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
184
148
  const handlers = setupHandlers();
185
149
  await handlers.get('gog_docs_export')!({ docId: 'abc', format: 'html' });
186
- expect(run).toHaveBeenCalledWith(
150
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
187
151
  ['docs', 'export', 'abc', '--format=html'],
188
152
  { account: undefined },
189
153
  );
190
154
  });
191
155
 
192
156
  it('includes --out when provided', async () => {
193
- vi.mocked(run).mockResolvedValue('{}');
157
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
194
158
  const handlers = setupHandlers();
195
159
  await handlers.get('gog_docs_export')!({ docId: 'abc', out: '/tmp/doc.pdf' });
196
- expect(run).toHaveBeenCalledWith(
160
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
197
161
  ['docs', 'export', 'abc', '--out=/tmp/doc.pdf'],
198
162
  { account: undefined },
199
163
  );
200
164
  });
201
165
 
202
166
  it('includes both --format and --out when provided', async () => {
203
- vi.mocked(run).mockResolvedValue('{}');
167
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
204
168
  const handlers = setupHandlers();
205
169
  await handlers.get('gog_docs_export')!({ docId: 'abc', format: 'txt', out: '/tmp/doc.txt' });
206
- expect(run).toHaveBeenCalledWith(
170
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
207
171
  ['docs', 'export', 'abc', '--format=txt', '--out=/tmp/doc.txt'],
208
172
  { account: undefined },
209
173
  );
210
174
  });
211
175
 
212
176
  it('omits optional flags when not provided', async () => {
213
- vi.mocked(run).mockResolvedValue('{}');
177
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
214
178
  const handlers = setupHandlers();
215
179
  await handlers.get('gog_docs_export')!({ docId: 'abc' });
216
- expect(run).toHaveBeenCalledWith(['docs', 'export', 'abc'], { account: undefined });
217
- });
218
-
219
- it('returns error text on failure', async () => {
220
- vi.mocked(run).mockRejectedValue(new Error('Export failed'));
221
- const handlers = setupHandlers();
222
- const result = await handlers.get('gog_docs_export')!({ docId: 'bad' });
223
- expect(result.content[0].text).toBe('Error: Export failed');
180
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'export', 'abc'], { account: undefined });
224
181
  });
225
182
  });
226
183
 
227
184
  // --- gog_docs_insert ---
228
185
 
229
186
  describe('gog_docs_insert', () => {
230
- it('calls run with content', async () => {
231
- vi.mocked(run).mockResolvedValue('{}');
187
+ it('calls runOrDiagnose with content', async () => {
188
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
232
189
  const handlers = setupHandlers();
233
190
  await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'Hello world' });
234
- expect(run).toHaveBeenCalledWith(
191
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
235
192
  ['docs', 'insert', 'abc', 'Hello world'],
236
193
  { account: undefined },
237
194
  );
238
195
  });
239
196
 
240
197
  it('includes --index when provided', async () => {
241
- vi.mocked(run).mockResolvedValue('{}');
198
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
242
199
  const handlers = setupHandlers();
243
200
  await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'text', index: 5 });
244
- expect(run).toHaveBeenCalledWith(
201
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
245
202
  ['docs', 'insert', 'abc', 'text', '--index=5'],
246
203
  { account: undefined },
247
204
  );
248
205
  });
249
206
 
250
207
  it('includes --index=0 when index is zero', async () => {
251
- vi.mocked(run).mockResolvedValue('{}');
208
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
252
209
  const handlers = setupHandlers();
253
210
  await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'text', index: 0 });
254
- expect(run).toHaveBeenCalledWith(
211
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
255
212
  ['docs', 'insert', 'abc', 'text', '--index=0'],
256
213
  { account: undefined },
257
214
  );
258
215
  });
259
216
 
260
217
  it('includes --file when provided', async () => {
261
- vi.mocked(run).mockResolvedValue('{}');
218
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
262
219
  const handlers = setupHandlers();
263
220
  await handlers.get('gog_docs_insert')!({ docId: 'abc', file: '/tmp/text.txt' });
264
- expect(run).toHaveBeenCalledWith(
221
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
265
222
  ['docs', 'insert', 'abc', '--file=/tmp/text.txt'],
266
223
  { account: undefined },
267
224
  );
268
225
  });
269
226
 
270
227
  it('includes --tab-id when provided', async () => {
271
- vi.mocked(run).mockResolvedValue('{}');
228
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
272
229
  const handlers = setupHandlers();
273
230
  await handlers.get('gog_docs_insert')!({ docId: 'abc', content: 'hi', tabId: 'tab1' });
274
- expect(run).toHaveBeenCalledWith(
231
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
275
232
  ['docs', 'insert', 'abc', 'hi', '--tab-id=tab1'],
276
233
  { account: undefined },
277
234
  );
278
235
  });
279
236
 
280
237
  it('omits optional flags when not provided', async () => {
281
- vi.mocked(run).mockResolvedValue('{}');
238
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
282
239
  const handlers = setupHandlers();
283
240
  await handlers.get('gog_docs_insert')!({ docId: 'abc' });
284
- expect(run).toHaveBeenCalledWith(['docs', 'insert', 'abc'], { account: undefined });
285
- });
286
-
287
- it('returns error text on failure', async () => {
288
- vi.mocked(run).mockRejectedValue(new Error('Insert failed'));
289
- const handlers = setupHandlers();
290
- const result = await handlers.get('gog_docs_insert')!({ docId: 'bad' });
291
- expect(result.content[0].text).toBe('Error: Insert failed');
241
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'insert', 'abc'], { account: undefined });
292
242
  });
293
243
  });
294
244
 
295
245
  // --- gog_docs_list_tabs ---
296
246
 
297
247
  describe('gog_docs_list_tabs', () => {
298
- it('calls run with correct args', async () => {
299
- vi.mocked(run).mockResolvedValue('[{"id":"t1","title":"Tab 1"}]');
248
+ it('calls runOrDiagnose with correct args', async () => {
249
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
300
250
  const handlers = setupHandlers();
301
- const result = await handlers.get('gog_docs_list_tabs')!({ docId: 'abc' });
302
- expect(run).toHaveBeenCalledWith(['docs', 'list-tabs', 'abc'], { account: undefined });
303
- expect(result.content[0].text).toContain('Tab 1');
251
+ await handlers.get('gog_docs_list_tabs')!({ docId: 'abc' });
252
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'list-tabs', 'abc'], { account: undefined });
304
253
  });
305
254
 
306
255
  it('forwards account override', async () => {
307
- vi.mocked(run).mockResolvedValue('[]');
256
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
308
257
  const handlers = setupHandlers();
309
258
  await handlers.get('gog_docs_list_tabs')!({ docId: 'abc', account: 'other@gmail.com' });
310
- expect(run).toHaveBeenCalledWith(['docs', 'list-tabs', 'abc'], { account: 'other@gmail.com' });
311
- });
312
-
313
- it('returns error text on failure', async () => {
314
- vi.mocked(run).mockRejectedValue(new Error('List tabs failed'));
315
- const handlers = setupHandlers();
316
- const result = await handlers.get('gog_docs_list_tabs')!({ docId: 'bad' });
317
- expect(result.content[0].text).toBe('Error: List tabs failed');
259
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'list-tabs', 'abc'], { account: 'other@gmail.com' });
318
260
  });
319
261
  });
320
262
 
321
263
  // --- gog_docs_sed ---
322
264
 
323
265
  describe('gog_docs_sed', () => {
324
- it('calls run with single expression', async () => {
325
- vi.mocked(run).mockResolvedValue('{}');
266
+ it('calls runOrDiagnose with single expression', async () => {
267
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
326
268
  const handlers = setupHandlers();
327
269
  await handlers.get('gog_docs_sed')!({ docId: 'abc', expression: 's/old/new/g' });
328
- expect(run).toHaveBeenCalledWith(
270
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
329
271
  ['docs', 'sed', 'abc', 's/old/new/g'],
330
272
  { account: undefined },
331
273
  );
332
274
  });
333
275
 
334
- it('calls run with multiple expressions', async () => {
335
- vi.mocked(run).mockResolvedValue('{}');
276
+ it('calls runOrDiagnose with multiple expressions', async () => {
277
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
336
278
  const handlers = setupHandlers();
337
279
  await handlers.get('gog_docs_sed')!({
338
280
  docId: 'abc',
339
281
  expressions: ['s/foo/bar/g', 's/baz/qux/g'],
340
282
  });
341
- expect(run).toHaveBeenCalledWith(
283
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
342
284
  ['docs', 'sed', 'abc', '--expressions=s/foo/bar/g', '--expressions=s/baz/qux/g'],
343
285
  { account: undefined },
344
286
  );
345
287
  });
346
288
 
347
289
  it('includes --file when provided', async () => {
348
- vi.mocked(run).mockResolvedValue('{}');
290
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
349
291
  const handlers = setupHandlers();
350
292
  await handlers.get('gog_docs_sed')!({ docId: 'abc', file: '/tmp/sed.txt' });
351
- expect(run).toHaveBeenCalledWith(
293
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
352
294
  ['docs', 'sed', 'abc', '--file=/tmp/sed.txt'],
353
295
  { account: undefined },
354
296
  );
355
297
  });
356
298
 
357
299
  it('includes --tab when provided', async () => {
358
- vi.mocked(run).mockResolvedValue('{}');
300
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
359
301
  const handlers = setupHandlers();
360
302
  await handlers.get('gog_docs_sed')!({ docId: 'abc', expression: 's/a/b/', tab: 'Notes' });
361
- expect(run).toHaveBeenCalledWith(
303
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
362
304
  ['docs', 'sed', 'abc', 's/a/b/', '--tab=Notes'],
363
305
  { account: undefined },
364
306
  );
365
307
  });
366
308
 
367
309
  it('omits optional flags when not provided', async () => {
368
- vi.mocked(run).mockResolvedValue('{}');
310
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
369
311
  const handlers = setupHandlers();
370
312
  await handlers.get('gog_docs_sed')!({ docId: 'abc' });
371
- expect(run).toHaveBeenCalledWith(['docs', 'sed', 'abc'], { account: undefined });
372
- });
373
-
374
- it('returns error text on failure', async () => {
375
- vi.mocked(run).mockRejectedValue(new Error('Sed failed'));
376
- const handlers = setupHandlers();
377
- const result = await handlers.get('gog_docs_sed')!({ docId: 'bad' });
378
- expect(result.content[0].text).toBe('Error: Sed failed');
313
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'sed', 'abc'], { account: undefined });
379
314
  });
380
315
  });
381
316
 
382
317
  // --- gog_docs_update ---
383
318
 
384
319
  describe('gog_docs_update', () => {
385
- it('calls run with --text flag', async () => {
386
- vi.mocked(run).mockResolvedValue('{}');
320
+ it('calls runOrDiagnose with --text flag', async () => {
321
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
387
322
  const handlers = setupHandlers();
388
323
  await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'Hello' });
389
- expect(run).toHaveBeenCalledWith(
324
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
390
325
  ['docs', 'update', 'abc', '--text=Hello'],
391
326
  { account: undefined },
392
327
  );
393
328
  });
394
329
 
395
330
  it('includes --file when provided', async () => {
396
- vi.mocked(run).mockResolvedValue('{}');
331
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
397
332
  const handlers = setupHandlers();
398
333
  await handlers.get('gog_docs_update')!({ docId: 'abc', file: '/tmp/content.txt' });
399
- expect(run).toHaveBeenCalledWith(
334
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
400
335
  ['docs', 'update', 'abc', '--file=/tmp/content.txt'],
401
336
  { account: undefined },
402
337
  );
403
338
  });
404
339
 
405
340
  it('includes --index when provided', async () => {
406
- vi.mocked(run).mockResolvedValue('{}');
341
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
407
342
  const handlers = setupHandlers();
408
343
  await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'hi', index: 10 });
409
- expect(run).toHaveBeenCalledWith(
344
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
410
345
  ['docs', 'update', 'abc', '--text=hi', '--index=10'],
411
346
  { account: undefined },
412
347
  );
413
348
  });
414
349
 
415
350
  it('includes --index=0 when index is zero', async () => {
416
- vi.mocked(run).mockResolvedValue('{}');
351
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
417
352
  const handlers = setupHandlers();
418
353
  await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'hi', index: 0 });
419
- expect(run).toHaveBeenCalledWith(
354
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
420
355
  ['docs', 'update', 'abc', '--text=hi', '--index=0'],
421
356
  { account: undefined },
422
357
  );
423
358
  });
424
359
 
425
360
  it('includes --tab-id when provided', async () => {
426
- vi.mocked(run).mockResolvedValue('{}');
361
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
427
362
  const handlers = setupHandlers();
428
363
  await handlers.get('gog_docs_update')!({ docId: 'abc', text: 'hi', tabId: 'tab1' });
429
- expect(run).toHaveBeenCalledWith(
364
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
430
365
  ['docs', 'update', 'abc', '--text=hi', '--tab-id=tab1'],
431
366
  { account: undefined },
432
367
  );
433
368
  });
434
369
 
435
370
  it('includes --pageless when true', async () => {
436
- vi.mocked(run).mockResolvedValue('{}');
371
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
437
372
  const handlers = setupHandlers();
438
373
  await handlers.get('gog_docs_update')!({ docId: 'abc', pageless: true });
439
- expect(run).toHaveBeenCalledWith(
374
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
440
375
  ['docs', 'update', 'abc', '--pageless'],
441
376
  { account: undefined },
442
377
  );
443
378
  });
444
379
 
445
380
  it('omits --pageless when false', async () => {
446
- vi.mocked(run).mockResolvedValue('{}');
381
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
447
382
  const handlers = setupHandlers();
448
383
  await handlers.get('gog_docs_update')!({ docId: 'abc', pageless: false });
449
- expect(run).toHaveBeenCalledWith(
384
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
450
385
  ['docs', 'update', 'abc'],
451
386
  { account: undefined },
452
387
  );
453
388
  });
454
389
 
455
390
  it('omits optional flags when not provided', async () => {
456
- vi.mocked(run).mockResolvedValue('{}');
391
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
457
392
  const handlers = setupHandlers();
458
393
  await handlers.get('gog_docs_update')!({ docId: 'abc' });
459
- expect(run).toHaveBeenCalledWith(['docs', 'update', 'abc'], { account: undefined });
460
- });
461
-
462
- it('returns error text on failure', async () => {
463
- vi.mocked(run).mockRejectedValue(new Error('Update failed'));
464
- const handlers = setupHandlers();
465
- const result = await handlers.get('gog_docs_update')!({ docId: 'bad' });
466
- expect(result.content[0].text).toBe('Error: Update failed');
394
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'update', 'abc'], { account: undefined });
467
395
  });
468
396
  });
469
397
 
470
398
  // --- Dedicated comment tools ---
471
399
 
472
400
  describe('gog_docs_comments_list', () => {
473
- it('calls run with correct args for open comments', async () => {
474
- vi.mocked(run).mockResolvedValue('[{"id":"c1","content":"Fix this"}]');
401
+ it('calls runOrDiagnose with correct args', async () => {
402
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
475
403
  const handlers = setupHandlers();
476
- const result = await handlers.get('gog_docs_comments_list')!({ docId: 'abc' });
477
- expect(run).toHaveBeenCalledWith(['docs', 'comments', 'list', 'abc'], { account: undefined });
478
- expect(result.content[0].text).toContain('Fix this');
404
+ await handlers.get('gog_docs_comments_list')!({ docId: 'abc' });
405
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'comments', 'list', 'abc'], { account: undefined });
479
406
  });
480
407
 
481
408
  it('includes --include-resolved when set', async () => {
482
- vi.mocked(run).mockResolvedValue('[]');
409
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
483
410
  const handlers = setupHandlers();
484
411
  await handlers.get('gog_docs_comments_list')!({ docId: 'abc', includeResolved: true });
485
- expect(run).toHaveBeenCalledWith(
412
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
486
413
  ['docs', 'comments', 'list', 'abc', '--include-resolved'],
487
414
  { account: undefined },
488
415
  );
489
416
  });
490
417
 
491
418
  it('omits --include-resolved when false', async () => {
492
- vi.mocked(run).mockResolvedValue('[]');
419
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
493
420
  const handlers = setupHandlers();
494
421
  await handlers.get('gog_docs_comments_list')!({ docId: 'abc', includeResolved: false });
495
- expect(run).toHaveBeenCalledWith(['docs', 'comments', 'list', 'abc'], { account: undefined });
422
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'comments', 'list', 'abc'], { account: undefined });
496
423
  });
497
424
 
498
425
  it('forwards account override', async () => {
499
- vi.mocked(run).mockResolvedValue('[]');
426
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
500
427
  const handlers = setupHandlers();
501
428
  await handlers.get('gog_docs_comments_list')!({ docId: 'abc', account: 'other@gmail.com' });
502
- expect(run).toHaveBeenCalledWith(
429
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
503
430
  ['docs', 'comments', 'list', 'abc'],
504
431
  { account: 'other@gmail.com' },
505
432
  );
506
433
  });
507
-
508
- it('returns error text on failure', async () => {
509
- vi.mocked(run).mockRejectedValue(new Error('List failed'));
510
- const handlers = setupHandlers();
511
- const result = await handlers.get('gog_docs_comments_list')!({ docId: 'bad' });
512
- expect(result.content[0].text).toContain('Error: List failed');
513
- });
514
434
  });
515
435
 
516
436
  describe('gog_docs_comments_get', () => {
517
- it('calls run with correct args', async () => {
518
- vi.mocked(run).mockResolvedValue('{"id":"c1","content":"Fix this","replies":[]}');
519
- const handlers = setupHandlers();
520
- const result = await handlers.get('gog_docs_comments_get')!({ docId: 'abc', commentId: 'c1' });
521
- expect(run).toHaveBeenCalledWith(['docs', 'comments', 'get', 'abc', 'c1'], { account: undefined });
522
- expect(result.content[0].text).toContain('Fix this');
523
- });
524
-
525
- it('returns error text on failure', async () => {
526
- vi.mocked(run).mockRejectedValue(new Error('Not found'));
437
+ it('calls runOrDiagnose with correct args', async () => {
438
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
527
439
  const handlers = setupHandlers();
528
- const result = await handlers.get('gog_docs_comments_get')!({ docId: 'abc', commentId: 'bad' });
529
- expect(result.content[0].text).toContain('Error: Not found');
440
+ await handlers.get('gog_docs_comments_get')!({ docId: 'abc', commentId: 'c1' });
441
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'comments', 'get', 'abc', 'c1'], { account: undefined });
530
442
  });
531
443
  });
532
444
 
533
445
  describe('gog_docs_comments_add', () => {
534
- it('calls run with content', async () => {
535
- vi.mocked(run).mockResolvedValue('{"id":"c2"}');
446
+ it('calls runOrDiagnose with content', async () => {
447
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
536
448
  const handlers = setupHandlers();
537
449
  await handlers.get('gog_docs_comments_add')!({ docId: 'abc', content: 'Please review' });
538
- expect(run).toHaveBeenCalledWith(
450
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
539
451
  ['docs', 'comments', 'add', 'abc', 'Please review'],
540
452
  { account: undefined },
541
453
  );
542
454
  });
543
455
 
544
456
  it('includes --quoted when provided', async () => {
545
- vi.mocked(run).mockResolvedValue('{"id":"c2"}');
457
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
546
458
  const handlers = setupHandlers();
547
459
  await handlers.get('gog_docs_comments_add')!({
548
460
  docId: 'abc', content: 'Typo here', quoted: 'teh',
549
461
  });
550
- expect(run).toHaveBeenCalledWith(
462
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
551
463
  ['docs', 'comments', 'add', 'abc', 'Typo here', '--quoted=teh'],
552
464
  { account: undefined },
553
465
  );
554
466
  });
555
467
 
556
468
  it('omits --quoted when not provided', async () => {
557
- vi.mocked(run).mockResolvedValue('{"id":"c2"}');
469
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
558
470
  const handlers = setupHandlers();
559
471
  await handlers.get('gog_docs_comments_add')!({ docId: 'abc', content: 'Nice' });
560
- expect(run).toHaveBeenCalledWith(
472
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
561
473
  ['docs', 'comments', 'add', 'abc', 'Nice'],
562
474
  { account: undefined },
563
475
  );
564
476
  });
565
-
566
- it('returns error text on failure', async () => {
567
- vi.mocked(run).mockRejectedValue(new Error('Add failed'));
568
- const handlers = setupHandlers();
569
- const result = await handlers.get('gog_docs_comments_add')!({ docId: 'bad', content: 'x' });
570
- expect(result.content[0].text).toContain('Error: Add failed');
571
- });
572
477
  });
573
478
 
574
479
  describe('gog_docs_comments_reply', () => {
575
- it('calls run with correct args', async () => {
576
- vi.mocked(run).mockResolvedValue('{"id":"r1"}');
480
+ it('calls runOrDiagnose with correct args', async () => {
481
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
577
482
  const handlers = setupHandlers();
578
483
  await handlers.get('gog_docs_comments_reply')!({ docId: 'abc', commentId: 'c1', content: 'Done' });
579
- expect(run).toHaveBeenCalledWith(
484
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
580
485
  ['docs', 'comments', 'reply', 'abc', 'c1', 'Done'],
581
486
  { account: undefined },
582
487
  );
583
488
  });
584
-
585
- it('returns error text on failure', async () => {
586
- vi.mocked(run).mockRejectedValue(new Error('Reply failed'));
587
- const handlers = setupHandlers();
588
- const result = await handlers.get('gog_docs_comments_reply')!({
589
- docId: 'abc', commentId: 'c1', content: 'x',
590
- });
591
- expect(result.content[0].text).toContain('Error: Reply failed');
592
- });
593
489
  });
594
490
 
595
491
  describe('gog_docs_comments_resolve', () => {
596
- it('calls run with correct args', async () => {
597
- vi.mocked(run).mockResolvedValue('{}');
492
+ it('calls runOrDiagnose with correct args', async () => {
493
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
598
494
  const handlers = setupHandlers();
599
495
  await handlers.get('gog_docs_comments_resolve')!({ docId: 'abc', commentId: 'c1' });
600
- expect(run).toHaveBeenCalledWith(
496
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
601
497
  ['docs', 'comments', 'resolve', 'abc', 'c1'],
602
498
  { account: undefined },
603
499
  );
604
500
  });
605
501
 
606
502
  it('includes --message when provided', async () => {
607
- vi.mocked(run).mockResolvedValue('{}');
503
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
608
504
  const handlers = setupHandlers();
609
505
  await handlers.get('gog_docs_comments_resolve')!({
610
506
  docId: 'abc', commentId: 'c1', message: 'Fixed in v2',
611
507
  });
612
- expect(run).toHaveBeenCalledWith(
508
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
613
509
  ['docs', 'comments', 'resolve', 'abc', 'c1', '--message=Fixed in v2'],
614
510
  { account: undefined },
615
511
  );
616
512
  });
617
513
 
618
514
  it('omits --message when not provided', async () => {
619
- vi.mocked(run).mockResolvedValue('{}');
515
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
620
516
  const handlers = setupHandlers();
621
517
  await handlers.get('gog_docs_comments_resolve')!({ docId: 'abc', commentId: 'c1' });
622
- expect(run).toHaveBeenCalledWith(
518
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
623
519
  ['docs', 'comments', 'resolve', 'abc', 'c1'],
624
520
  { account: undefined },
625
521
  );
626
522
  });
627
-
628
- it('returns error text on failure', async () => {
629
- vi.mocked(run).mockRejectedValue(new Error('Resolve failed'));
630
- const handlers = setupHandlers();
631
- const result = await handlers.get('gog_docs_comments_resolve')!({ docId: 'abc', commentId: 'c1' });
632
- expect(result.content[0].text).toContain('Error: Resolve failed');
633
- });
634
523
  });
635
524
 
636
525
  describe('gog_docs_comments_delete', () => {
637
- it('calls run with correct args', async () => {
638
- vi.mocked(run).mockResolvedValue('{}');
526
+ it('calls runOrDiagnose with correct args', async () => {
527
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
639
528
  const handlers = setupHandlers();
640
529
  await handlers.get('gog_docs_comments_delete')!({ docId: 'abc', commentId: 'c1' });
641
- expect(run).toHaveBeenCalledWith(
530
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
642
531
  ['docs', 'comments', 'delete', 'abc', 'c1'],
643
532
  { account: undefined },
644
533
  );
645
534
  });
646
-
647
- it('returns error text on failure', async () => {
648
- vi.mocked(run).mockRejectedValue(new Error('Delete failed'));
649
- const handlers = setupHandlers();
650
- const result = await handlers.get('gog_docs_comments_delete')!({ docId: 'abc', commentId: 'c1' });
651
- expect(result.content[0].text).toContain('Error: Delete failed');
652
- });
653
535
  });