gogcli-mcp-docs 2.0.11 → 2.0.12

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.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "Extended Google Docs for Claude via gogcli — auth + full Docs and comments support",
10
- "version": "2.0.11"
10
+ "version": "2.0.12"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "gogcli (Docs)",
16
16
  "source": "./",
17
17
  "description": "Extended Google Docs for Claude via gogcli — auth + full Docs and comments support",
18
- "version": "2.0.11",
18
+ "version": "2.0.12",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gogcli-mcp-docs",
3
3
  "displayName": "gogcli (Docs)",
4
- "version": "2.0.11",
4
+ "version": "2.0.12",
5
5
  "description": "Extended Google Docs for Claude via gogcli — auth + full Docs and comments support",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/index.js CHANGED
@@ -31292,7 +31292,7 @@ function registerDocsTools(server2) {
31292
31292
  var cellValueParam = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]);
31293
31293
 
31294
31294
  // ../gogcli-mcp/src/server.ts
31295
- var VERSION = true ? "2.0.11" : "0.0.0";
31295
+ var VERSION = true ? "2.0.12" : "0.0.0";
31296
31296
  function createServer(options) {
31297
31297
  return new McpServer({
31298
31298
  name: options?.name ?? "gogcli",
@@ -31399,6 +31399,8 @@ function registerExtraDocsTools(server2) {
31399
31399
  noStrikethrough: external_exports.boolean().optional().describe("Clear strikethrough"),
31400
31400
  alignment: external_exports.enum(["left", "center", "right", "justify", "start", "end", "justified"]).optional().describe("Paragraph alignment"),
31401
31401
  lineSpacing: external_exports.number().optional().describe("Line spacing percentage (e.g. 100 for single, 150 for 1.5x, 200 for double)"),
31402
+ headingLevel: external_exports.number().int().optional().describe("Set paragraph named style to HEADING_1..HEADING_6 (shortcut for namedStyle=HEADING_N)"),
31403
+ namedStyle: external_exports.enum(["NORMAL_TEXT", "TITLE", "SUBTITLE", "HEADING_1", "HEADING_2", "HEADING_3", "HEADING_4", "HEADING_5", "HEADING_6"]).optional().describe("Set paragraph named style explicitly"),
31402
31404
  account: accountParam
31403
31405
  }
31404
31406
  }, async (args) => {
@@ -31422,6 +31424,8 @@ function registerExtraDocsTools(server2) {
31422
31424
  if (a.noStrikethrough) argv.push("--no-strikethrough");
31423
31425
  if (a.alignment) argv.push(`--alignment=${a.alignment}`);
31424
31426
  if (a.lineSpacing !== void 0) argv.push(`--line-spacing=${a.lineSpacing}`);
31427
+ if (a.headingLevel !== void 0) argv.push(`--heading-level=${a.headingLevel}`);
31428
+ if (a.namedStyle) argv.push(`--named-style=${a.namedStyle}`);
31425
31429
  return runOrDiagnose(argv, { account: a.account });
31426
31430
  });
31427
31431
  server2.registerTool("gog_docs_export", {
@@ -31604,6 +31608,68 @@ function registerExtraDocsTools(server2) {
31604
31608
  }, async ({ docId, commentId, account }) => {
31605
31609
  return runOrDiagnose(["docs", "comments", "delete", docId, commentId], { account });
31606
31610
  });
31611
+ server2.registerTool("gog_docs_comments_reopen", {
31612
+ description: "Reopen a previously resolved comment (flip resolved \u2192 open).",
31613
+ annotations: { destructiveHint: true },
31614
+ inputSchema: {
31615
+ docId: external_exports.string().describe("Doc ID (from the URL)"),
31616
+ commentId: external_exports.string().describe("Comment ID to reopen"),
31617
+ account: accountParam
31618
+ }
31619
+ }, async ({ docId, commentId, account }) => {
31620
+ return runOrDiagnose(["docs", "comments", "reopen", docId, commentId], { account });
31621
+ });
31622
+ server2.registerTool("gog_docs_insert_page_break", {
31623
+ description: "Insert a Google Docs page break via InsertPageBreakRequest \u2014 the only path for multi-page deliverables (markdown has no page-break construct). Specify `index` for a precise character position, or `atEnd` for end-of-doc.",
31624
+ annotations: { destructiveHint: true },
31625
+ inputSchema: {
31626
+ docId: external_exports.string().describe("Doc ID (from the URL)"),
31627
+ index: external_exports.number().int().optional().describe("Character index to insert at (1 = beginning). Omit or use atEnd for end-of-doc."),
31628
+ atEnd: external_exports.boolean().optional().describe("Insert at end-of-doc/tab (mutually exclusive with index)"),
31629
+ tab: external_exports.string().optional().describe("Target a specific tab by title or ID"),
31630
+ account: accountParam
31631
+ }
31632
+ }, async ({ docId, index, atEnd, tab, account }) => {
31633
+ const args = ["docs", "insert-page-break", docId];
31634
+ if (index !== void 0) args.push(`--index=${index}`);
31635
+ if (atEnd) args.push("--at-end");
31636
+ if (tab) args.push(`--tab=${tab}`);
31637
+ return runOrDiagnose(args, { account });
31638
+ });
31639
+ server2.registerTool("gog_docs_page_layout", {
31640
+ description: "Toggle the page layout (pageless | pages) of an existing Google Doc. Sibling to the --pageless flag on docs create/write/update for docs that were already created (e.g. by Drive markdown conversion) without the desired layout.",
31641
+ annotations: { destructiveHint: true },
31642
+ inputSchema: {
31643
+ docId: external_exports.string().describe("Doc ID (from the URL)"),
31644
+ layout: external_exports.enum(["pageless", "pages"]).optional().describe("Page layout (default: pageless)"),
31645
+ account: accountParam
31646
+ }
31647
+ }, async ({ docId, layout, account }) => {
31648
+ const args = ["docs", "page-layout", docId];
31649
+ if (layout) args.push(`--layout=${layout}`);
31650
+ return runOrDiagnose(args, { account });
31651
+ });
31652
+ server2.registerTool("gog_docs_insert_table", {
31653
+ description: "Insert a native Google Docs table via InsertTableRequest, bypassing the markdown writer. Use this instead of writing a markdown table when you need precise dimensions or to avoid the markdown writer's table limitations. `valuesJson` is a JSON 2D string array whose dimensions must match rows x cols.",
31654
+ annotations: { destructiveHint: true },
31655
+ inputSchema: {
31656
+ docId: external_exports.string().describe("Doc ID (from the URL)"),
31657
+ rows: external_exports.number().int().min(1).describe("Number of rows (>=1)"),
31658
+ cols: external_exports.number().int().min(1).describe("Number of columns (>=1)"),
31659
+ index: external_exports.number().int().optional().describe("Character index to insert at (1 = beginning). Omit or use atEnd for end-of-doc."),
31660
+ atEnd: external_exports.boolean().optional().describe("Insert at end-of-doc/tab (mutually exclusive with index)"),
31661
+ valuesJson: external_exports.string().optional().describe("Cell values as a JSON 2D string array; dimensions must match rows x cols when supplied"),
31662
+ tab: external_exports.string().optional().describe("Target a specific tab by title or ID"),
31663
+ account: accountParam
31664
+ }
31665
+ }, async ({ docId, rows, cols, index, atEnd, valuesJson, tab, account }) => {
31666
+ const args = ["docs", "insert-table", docId, `--rows=${rows}`, `--cols=${cols}`];
31667
+ if (index !== void 0) args.push(`--index=${index}`);
31668
+ if (atEnd) args.push("--at-end");
31669
+ if (valuesJson !== void 0) args.push(`--values-json=${valuesJson}`);
31670
+ if (tab) args.push(`--tab=${tab}`);
31671
+ return runOrDiagnose(args, { account });
31672
+ });
31607
31673
  }
31608
31674
 
31609
31675
  // src/index.ts
package/manifest.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "manifest_version": "0.3",
4
4
  "name": "gogcli-mcp-docs",
5
5
  "display_name": "gogcli (Docs)",
6
- "version": "2.0.11",
6
+ "version": "2.0.12",
7
7
  "description": "Extended Google Docs for Claude via gogcli — auth + full Docs and comments support",
8
8
  "author": {
9
9
  "name": "Chris Hall",
@@ -169,11 +169,27 @@
169
169
  },
170
170
  {
171
171
  "name": "gog_docs_format",
172
- "description": "Apply text/paragraph formatting (bold, italic, font size, color, alignment, line spacing) — supports match, matchAll, matchCase"
172
+ "description": "Apply text/paragraph formatting (bold, italic, font size, color, alignment, line spacing, named style / heading level) — supports match, matchAll, matchCase"
173
173
  },
174
174
  {
175
175
  "name": "gog_docs_trash",
176
176
  "description": "Move an entire Google Doc to Drive trash (convenience wrapper around gog drive delete)"
177
+ },
178
+ {
179
+ "name": "gog_docs_comments_reopen",
180
+ "description": "Reopen a previously resolved comment."
181
+ },
182
+ {
183
+ "name": "gog_docs_insert_page_break",
184
+ "description": "Insert a Google Docs page break at a given index or at end-of-doc — the only path for multi-page deliverables."
185
+ },
186
+ {
187
+ "name": "gog_docs_page_layout",
188
+ "description": "Toggle page layout (pageless | pages) on an existing Google Doc."
189
+ },
190
+ {
191
+ "name": "gog_docs_insert_table",
192
+ "description": "Insert a native Google Docs table with given dimensions, optionally populated from a JSON 2D array."
177
193
  }
178
194
  ],
179
195
  "compatibility": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gogcli-mcp-docs",
3
- "version": "2.0.11",
3
+ "version": "2.0.12",
4
4
  "mcpName": "io.github.chrischall/gogcli-mcp-docs",
5
5
  "description": "Extended Google Docs MCP server via gogcli — all base tools plus full Docs support",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "source": "github",
8
8
  "subfolder": "packages/gogcli-mcp-docs"
9
9
  },
10
- "version": "2.0.11",
10
+ "version": "2.0.12",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "gogcli-mcp-docs",
15
- "version": "2.0.11",
15
+ "version": "2.0.12",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },
@@ -105,6 +105,8 @@ export function registerExtraDocsTools(server: McpServer): void {
105
105
  noStrikethrough: z.boolean().optional().describe('Clear strikethrough'),
106
106
  alignment: z.enum(['left', 'center', 'right', 'justify', 'start', 'end', 'justified']).optional().describe('Paragraph alignment'),
107
107
  lineSpacing: z.number().optional().describe('Line spacing percentage (e.g. 100 for single, 150 for 1.5x, 200 for double)'),
108
+ headingLevel: z.number().int().optional().describe('Set paragraph named style to HEADING_1..HEADING_6 (shortcut for namedStyle=HEADING_N)'),
109
+ namedStyle: z.enum(['NORMAL_TEXT', 'TITLE', 'SUBTITLE', 'HEADING_1', 'HEADING_2', 'HEADING_3', 'HEADING_4', 'HEADING_5', 'HEADING_6']).optional().describe('Set paragraph named style explicitly'),
108
110
  account: accountParam,
109
111
  },
110
112
  }, async (args) => {
@@ -124,6 +126,8 @@ export function registerExtraDocsTools(server: McpServer): void {
124
126
  strikethrough?: boolean; noStrikethrough?: boolean;
125
127
  alignment?: string;
126
128
  lineSpacing?: number;
129
+ headingLevel?: number;
130
+ namedStyle?: string;
127
131
  account?: string;
128
132
  };
129
133
  const argv = ['docs', 'format', a.docId];
@@ -145,6 +149,8 @@ export function registerExtraDocsTools(server: McpServer): void {
145
149
  if (a.noStrikethrough) argv.push('--no-strikethrough');
146
150
  if (a.alignment) argv.push(`--alignment=${a.alignment}`);
147
151
  if (a.lineSpacing !== undefined) argv.push(`--line-spacing=${a.lineSpacing}`);
152
+ if (a.headingLevel !== undefined) argv.push(`--heading-level=${a.headingLevel}`);
153
+ if (a.namedStyle) argv.push(`--named-style=${a.namedStyle}`);
148
154
  return runOrDiagnose(argv, { account: a.account });
149
155
  });
150
156
 
@@ -342,4 +348,70 @@ export function registerExtraDocsTools(server: McpServer): void {
342
348
  }, async ({ docId, commentId, account }) => {
343
349
  return runOrDiagnose(['docs', 'comments', 'delete', docId, commentId], { account });
344
350
  });
351
+
352
+ server.registerTool('gog_docs_comments_reopen', {
353
+ description: 'Reopen a previously resolved comment (flip resolved → open).',
354
+ annotations: { destructiveHint: true },
355
+ inputSchema: {
356
+ docId: z.string().describe('Doc ID (from the URL)'),
357
+ commentId: z.string().describe('Comment ID to reopen'),
358
+ account: accountParam,
359
+ },
360
+ }, async ({ docId, commentId, account }) => {
361
+ return runOrDiagnose(['docs', 'comments', 'reopen', docId, commentId], { account });
362
+ });
363
+
364
+ server.registerTool('gog_docs_insert_page_break', {
365
+ description: 'Insert a Google Docs page break via InsertPageBreakRequest — the only path for multi-page deliverables (markdown has no page-break construct). Specify `index` for a precise character position, or `atEnd` for end-of-doc.',
366
+ annotations: { destructiveHint: true },
367
+ inputSchema: {
368
+ docId: z.string().describe('Doc ID (from the URL)'),
369
+ index: z.number().int().optional().describe('Character index to insert at (1 = beginning). Omit or use atEnd for end-of-doc.'),
370
+ atEnd: z.boolean().optional().describe('Insert at end-of-doc/tab (mutually exclusive with index)'),
371
+ tab: z.string().optional().describe('Target a specific tab by title or ID'),
372
+ account: accountParam,
373
+ },
374
+ }, async ({ docId, index, atEnd, tab, account }) => {
375
+ const args = ['docs', 'insert-page-break', docId];
376
+ if (index !== undefined) args.push(`--index=${index}`);
377
+ if (atEnd) args.push('--at-end');
378
+ if (tab) args.push(`--tab=${tab}`);
379
+ return runOrDiagnose(args, { account });
380
+ });
381
+
382
+ server.registerTool('gog_docs_page_layout', {
383
+ description: 'Toggle the page layout (pageless | pages) of an existing Google Doc. Sibling to the --pageless flag on docs create/write/update for docs that were already created (e.g. by Drive markdown conversion) without the desired layout.',
384
+ annotations: { destructiveHint: true },
385
+ inputSchema: {
386
+ docId: z.string().describe('Doc ID (from the URL)'),
387
+ layout: z.enum(['pageless', 'pages']).optional().describe('Page layout (default: pageless)'),
388
+ account: accountParam,
389
+ },
390
+ }, async ({ docId, layout, account }) => {
391
+ const args = ['docs', 'page-layout', docId];
392
+ if (layout) args.push(`--layout=${layout}`);
393
+ return runOrDiagnose(args, { account });
394
+ });
395
+
396
+ server.registerTool('gog_docs_insert_table', {
397
+ description: 'Insert a native Google Docs table via InsertTableRequest, bypassing the markdown writer. Use this instead of writing a markdown table when you need precise dimensions or to avoid the markdown writer\'s table limitations. `valuesJson` is a JSON 2D string array whose dimensions must match rows x cols.',
398
+ annotations: { destructiveHint: true },
399
+ inputSchema: {
400
+ docId: z.string().describe('Doc ID (from the URL)'),
401
+ rows: z.number().int().min(1).describe('Number of rows (>=1)'),
402
+ cols: z.number().int().min(1).describe('Number of columns (>=1)'),
403
+ index: z.number().int().optional().describe('Character index to insert at (1 = beginning). Omit or use atEnd for end-of-doc.'),
404
+ atEnd: z.boolean().optional().describe('Insert at end-of-doc/tab (mutually exclusive with index)'),
405
+ valuesJson: z.string().optional().describe('Cell values as a JSON 2D string array; dimensions must match rows x cols when supplied'),
406
+ tab: z.string().optional().describe('Target a specific tab by title or ID'),
407
+ account: accountParam,
408
+ },
409
+ }, async ({ docId, rows, cols, index, atEnd, valuesJson, tab, account }) => {
410
+ const args = ['docs', 'insert-table', docId, `--rows=${rows}`, `--cols=${cols}`];
411
+ if (index !== undefined) args.push(`--index=${index}`);
412
+ if (atEnd) args.push('--at-end');
413
+ if (valuesJson !== undefined) args.push(`--values-json=${valuesJson}`);
414
+ if (tab) args.push(`--tab=${tab}`);
415
+ return runOrDiagnose(args, { account });
416
+ });
345
417
  }
@@ -665,4 +665,137 @@ describe('gog_docs_format', () => {
665
665
  await handlers.get('gog_docs_format')!({ docId: 'd1' });
666
666
  expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'format', 'd1'], { account: undefined });
667
667
  });
668
+
669
+ // gog 0.18.0
670
+ it('passes --heading-level and --named-style when provided', async () => {
671
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
672
+ const handlers = setupHandlers();
673
+ await handlers.get('gog_docs_format')!({
674
+ docId: 'd1', match: 'Intro', headingLevel: 1, namedStyle: 'HEADING_1',
675
+ });
676
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
677
+ ['docs', 'format', 'd1', '--match=Intro', '--heading-level=1', '--named-style=HEADING_1'],
678
+ { account: undefined },
679
+ );
680
+ });
681
+
682
+ it('passes --heading-level=0 when explicitly 0 (HEADING_0 reserved but argument should reach gog)', async () => {
683
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
684
+ const handlers = setupHandlers();
685
+ await handlers.get('gog_docs_format')!({ docId: 'd1', headingLevel: 0 });
686
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
687
+ ['docs', 'format', 'd1', '--heading-level=0'],
688
+ { account: undefined },
689
+ );
690
+ });
691
+ });
692
+
693
+ // --- gog 0.18.0 new tools ---
694
+
695
+ describe('gog_docs_insert_page_break', () => {
696
+ it('inserts at index when --index provided', async () => {
697
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
698
+ const handlers = setupHandlers();
699
+ await handlers.get('gog_docs_insert_page_break')!({ docId: 'd1', index: 42 });
700
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
701
+ ['docs', 'insert-page-break', 'd1', '--index=42'],
702
+ { account: undefined },
703
+ );
704
+ });
705
+
706
+ it('uses --at-end when atEnd is true', async () => {
707
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
708
+ const handlers = setupHandlers();
709
+ await handlers.get('gog_docs_insert_page_break')!({ docId: 'd1', atEnd: true, tab: 'Body' });
710
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
711
+ ['docs', 'insert-page-break', 'd1', '--at-end', '--tab=Body'],
712
+ { account: undefined },
713
+ );
714
+ });
715
+
716
+ it('passes --index=1 when explicitly 1', async () => {
717
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
718
+ const handlers = setupHandlers();
719
+ await handlers.get('gog_docs_insert_page_break')!({ docId: 'd1', index: 1 });
720
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
721
+ ['docs', 'insert-page-break', 'd1', '--index=1'],
722
+ { account: undefined },
723
+ );
724
+ });
725
+ });
726
+
727
+ describe('gog_docs_page_layout', () => {
728
+ it('passes --layout when provided', async () => {
729
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
730
+ const handlers = setupHandlers();
731
+ await handlers.get('gog_docs_page_layout')!({ docId: 'd1', layout: 'pages' });
732
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
733
+ ['docs', 'page-layout', 'd1', '--layout=pages'],
734
+ { account: undefined },
735
+ );
736
+ });
737
+
738
+ it('omits --layout when not provided (defaults to pageless on gog side)', async () => {
739
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
740
+ const handlers = setupHandlers();
741
+ await handlers.get('gog_docs_page_layout')!({ docId: 'd1' });
742
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'page-layout', 'd1'], { account: undefined });
743
+ });
744
+ });
745
+
746
+ describe('gog_docs_insert_table', () => {
747
+ it('passes required --rows and --cols', async () => {
748
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
749
+ const handlers = setupHandlers();
750
+ await handlers.get('gog_docs_insert_table')!({ docId: 'd1', rows: 3, cols: 2 });
751
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
752
+ ['docs', 'insert-table', 'd1', '--rows=3', '--cols=2'],
753
+ { account: undefined },
754
+ );
755
+ });
756
+
757
+ it('passes --values-json, --index, --tab when provided', async () => {
758
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
759
+ const handlers = setupHandlers();
760
+ const vj = '[["a","b"],["c","d"]]';
761
+ await handlers.get('gog_docs_insert_table')!({
762
+ docId: 'd1', rows: 2, cols: 2, index: 10, valuesJson: vj, tab: 'Body',
763
+ });
764
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
765
+ ['docs', 'insert-table', 'd1', '--rows=2', '--cols=2', '--index=10', `--values-json=${vj}`, '--tab=Body'],
766
+ { account: undefined },
767
+ );
768
+ });
769
+
770
+ it('uses --at-end when atEnd is true', async () => {
771
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
772
+ const handlers = setupHandlers();
773
+ await handlers.get('gog_docs_insert_table')!({ docId: 'd1', rows: 1, cols: 1, atEnd: true });
774
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
775
+ ['docs', 'insert-table', 'd1', '--rows=1', '--cols=1', '--at-end'],
776
+ { account: undefined },
777
+ );
778
+ });
779
+ });
780
+
781
+ describe('gog_docs_comments_reopen', () => {
782
+ it('calls runOrDiagnose with docId and commentId', async () => {
783
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
784
+ const handlers = setupHandlers();
785
+ await handlers.get('gog_docs_comments_reopen')!({ docId: 'd1', commentId: 'c1' });
786
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
787
+ ['docs', 'comments', 'reopen', 'd1', 'c1'],
788
+ { account: undefined },
789
+ );
790
+ });
791
+
792
+ it('forwards account', async () => {
793
+ vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
794
+ const handlers = setupHandlers();
795
+ await handlers.get('gog_docs_comments_reopen')!({ docId: 'd1', commentId: 'c1', account: 'a@b.com' });
796
+ expect(lib.runOrDiagnose).toHaveBeenCalledWith(
797
+ ['docs', 'comments', 'reopen', 'd1', 'c1'],
798
+ { account: 'a@b.com' },
799
+ );
800
+ });
668
801
  });