gogcli-mcp-docs 2.7.1 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +98 -10
- package/manifest.json +25 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/tools/docs-extra.ts +104 -8
- package/tests/tools/docs-extra.test.ts +160 -0
|
@@ -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.
|
|
10
|
+
"version": "2.8.0"
|
|
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.
|
|
18
|
+
"version": "2.8.0",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
package/dist/index.js
CHANGED
|
@@ -31277,12 +31277,14 @@ function registerDocsTools(server2) {
|
|
|
31277
31277
|
text: external_exports.string().describe("Text content to write"),
|
|
31278
31278
|
append: external_exports.boolean().optional().describe("Append to existing content instead of replacing (default: false)"),
|
|
31279
31279
|
checkOrphans: external_exports.boolean().optional().describe("Block the write (exit code 11) if it would orphan an open comment \u2014 i.e. remove the text the comment is anchored to. Recommended for replacement writes on commented docs."),
|
|
31280
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin in gogcli-mcp-docs) instead of applying it \u2014 nothing changes in the doc until the batch is submitted."),
|
|
31280
31281
|
account: accountParam
|
|
31281
31282
|
}
|
|
31282
|
-
}, async ({ docId, text, append, checkOrphans, account }) => {
|
|
31283
|
+
}, async ({ docId, text, append, checkOrphans, batch, account }) => {
|
|
31283
31284
|
const args = ["docs", "write", docId, `--text=${text}`];
|
|
31284
31285
|
if (append) args.push("--append");
|
|
31285
31286
|
if (checkOrphans) args.push("--check-orphans");
|
|
31287
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
31286
31288
|
return runOrDiagnose(args, { account });
|
|
31287
31289
|
});
|
|
31288
31290
|
server2.registerTool("gog_docs_find_replace", {
|
|
@@ -31320,7 +31322,7 @@ var failIfNotEmptyParam = external_exports.boolean().optional().describe(
|
|
|
31320
31322
|
);
|
|
31321
31323
|
|
|
31322
31324
|
// ../gogcli-mcp/src/server.ts
|
|
31323
|
-
var VERSION = true ? "2.
|
|
31325
|
+
var VERSION = true ? "2.8.0" : "0.0.0";
|
|
31324
31326
|
function createServer(options) {
|
|
31325
31327
|
return new McpServer({
|
|
31326
31328
|
name: options?.name ?? "gogcli",
|
|
@@ -31354,9 +31356,10 @@ function registerExtraDocsTools(server2) {
|
|
|
31354
31356
|
occurrence: external_exports.number().int().optional().describe("Use the Nth `at` match (1-based; required when `at` is ambiguous)"),
|
|
31355
31357
|
matchCase: external_exports.boolean().optional().describe("Case-sensitive `at` matching"),
|
|
31356
31358
|
tabId: external_exports.string().optional().describe("Tab ID to delete content from (for multi-tab docs)"),
|
|
31359
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
31357
31360
|
account: accountParam
|
|
31358
31361
|
}
|
|
31359
|
-
}, async ({ docId, start, end, at, occurrence, matchCase, tabId, account }) => {
|
|
31362
|
+
}, async ({ docId, start, end, at, occurrence, matchCase, tabId, batch, account }) => {
|
|
31360
31363
|
const args = ["docs", "delete"];
|
|
31361
31364
|
if (start !== void 0) args.push(`--start=${start}`);
|
|
31362
31365
|
if (end !== void 0) args.push(`--end=${end}`);
|
|
@@ -31365,6 +31368,7 @@ function registerExtraDocsTools(server2) {
|
|
|
31365
31368
|
if (occurrence !== void 0) args.push(`--occurrence=${occurrence}`);
|
|
31366
31369
|
if (matchCase) args.push("--match-case");
|
|
31367
31370
|
if (tabId) args.push(`--tab-id=${tabId}`);
|
|
31371
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
31368
31372
|
return runOrDiagnose(args, { account });
|
|
31369
31373
|
});
|
|
31370
31374
|
server2.registerTool("gog_docs_trash", {
|
|
@@ -31444,6 +31448,7 @@ function registerExtraDocsTools(server2) {
|
|
|
31444
31448
|
lineSpacing: external_exports.number().optional().describe("Line spacing percentage (e.g. 100 for single, 150 for 1.5x, 200 for double)"),
|
|
31445
31449
|
headingLevel: external_exports.number().int().optional().describe("Set paragraph named style to HEADING_1..HEADING_6 (shortcut for namedStyle=HEADING_N)"),
|
|
31446
31450
|
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"),
|
|
31451
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
31447
31452
|
account: accountParam
|
|
31448
31453
|
}
|
|
31449
31454
|
}, async (args) => {
|
|
@@ -31472,6 +31477,7 @@ function registerExtraDocsTools(server2) {
|
|
|
31472
31477
|
if (a.lineSpacing !== void 0) argv.push(`--line-spacing=${a.lineSpacing}`);
|
|
31473
31478
|
if (a.headingLevel !== void 0) argv.push(`--heading-level=${a.headingLevel}`);
|
|
31474
31479
|
if (a.namedStyle) argv.push(`--named-style=${a.namedStyle}`);
|
|
31480
|
+
if (a.batch) argv.push(`--batch=${a.batch}`);
|
|
31475
31481
|
return runOrDiagnose(argv, { account: a.account });
|
|
31476
31482
|
});
|
|
31477
31483
|
server2.registerTool("gog_docs_export", {
|
|
@@ -31501,9 +31507,10 @@ function registerExtraDocsTools(server2) {
|
|
|
31501
31507
|
occurrence: external_exports.number().int().optional().describe("Use the Nth `at` match (1-based; required when `at` is ambiguous)"),
|
|
31502
31508
|
matchCase: external_exports.boolean().optional().describe("Case-sensitive `at` matching"),
|
|
31503
31509
|
tabId: external_exports.string().optional().describe("Tab ID to insert into (for multi-tab docs)"),
|
|
31510
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
31504
31511
|
account: accountParam
|
|
31505
31512
|
}
|
|
31506
|
-
}, async ({ docId, content, index, file: file2, at, occurrence, matchCase, tabId, account }) => {
|
|
31513
|
+
}, async ({ docId, content, index, file: file2, at, occurrence, matchCase, tabId, batch, account }) => {
|
|
31507
31514
|
const args = ["docs", "insert", docId];
|
|
31508
31515
|
if (content) args.push(content);
|
|
31509
31516
|
if (index !== void 0) args.push(`--index=${index}`);
|
|
@@ -31512,6 +31519,7 @@ function registerExtraDocsTools(server2) {
|
|
|
31512
31519
|
if (occurrence !== void 0) args.push(`--occurrence=${occurrence}`);
|
|
31513
31520
|
if (matchCase) args.push("--match-case");
|
|
31514
31521
|
if (tabId) args.push(`--tab-id=${tabId}`);
|
|
31522
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
31515
31523
|
return runOrDiagnose(args, { account });
|
|
31516
31524
|
});
|
|
31517
31525
|
server2.registerTool("gog_docs_append", {
|
|
@@ -31581,9 +31589,10 @@ function registerExtraDocsTools(server2) {
|
|
|
31581
31589
|
matchCase: external_exports.boolean().optional().describe("Case-sensitive `at` matching"),
|
|
31582
31590
|
tabId: external_exports.string().optional().describe("Tab ID for multi-tab docs"),
|
|
31583
31591
|
pageless: external_exports.boolean().optional().describe("Set document to pageless format"),
|
|
31592
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
31584
31593
|
account: accountParam
|
|
31585
31594
|
}
|
|
31586
|
-
}, async ({ docId, text, file: file2, index, replaceRange, markdown, at, occurrence, matchCase, tabId, pageless, account }) => {
|
|
31595
|
+
}, async ({ docId, text, file: file2, index, replaceRange, markdown, at, occurrence, matchCase, tabId, pageless, batch, account }) => {
|
|
31587
31596
|
const args = ["docs", "update", docId];
|
|
31588
31597
|
if (text) args.push(`--text=${text}`);
|
|
31589
31598
|
if (file2) args.push(`--file=${file2}`);
|
|
@@ -31595,6 +31604,7 @@ function registerExtraDocsTools(server2) {
|
|
|
31595
31604
|
if (matchCase) args.push("--match-case");
|
|
31596
31605
|
if (tabId) args.push(`--tab-id=${tabId}`);
|
|
31597
31606
|
if (pageless) args.push("--pageless");
|
|
31607
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
31598
31608
|
return runOrDiagnose(args, { account });
|
|
31599
31609
|
});
|
|
31600
31610
|
server2.registerTool("gog_docs_comments_list", {
|
|
@@ -31727,6 +31737,74 @@ function registerExtraDocsTools(server2) {
|
|
|
31727
31737
|
if (tab) args.push(`--tab=${tab}`);
|
|
31728
31738
|
return runOrDiagnose(args, { account });
|
|
31729
31739
|
});
|
|
31740
|
+
server2.registerTool("gog_batch_begin", {
|
|
31741
|
+
description: "Open a persisted, revision-locked request batch for a Google Doc. Subsequent docs mutation tools called with batch=<batchId> append their requests locally instead of applying them; gog_batch_end submits everything atomically. Returns the batchId.",
|
|
31742
|
+
inputSchema: {
|
|
31743
|
+
docId: external_exports.string().describe("Google Doc ID the batch is locked to"),
|
|
31744
|
+
name: external_exports.string().optional().describe("Optional batch label"),
|
|
31745
|
+
account: accountParam
|
|
31746
|
+
}
|
|
31747
|
+
}, async ({ docId, name, account }) => {
|
|
31748
|
+
const args = ["batch", "begin", `--doc=${docId}`];
|
|
31749
|
+
if (name) args.push(`--name=${name}`);
|
|
31750
|
+
return runOrDiagnose(args, { account });
|
|
31751
|
+
});
|
|
31752
|
+
server2.registerTool("gog_batch_end", {
|
|
31753
|
+
description: "Submit a persisted batch: applies every composed request to the doc in one atomic batchUpdate against the locked revision. Atomic by default \u2014 set autoSplit to submit >500-request batches as ordered chunks (non-atomic), or continueOnError to retry individually after an atomic validation failure, retaining failures in the batch.",
|
|
31754
|
+
annotations: { destructiveHint: true },
|
|
31755
|
+
inputSchema: {
|
|
31756
|
+
batchId: external_exports.string().describe("Batch ID (from gog_batch_begin)"),
|
|
31757
|
+
autoSplit: external_exports.boolean().optional().describe("Submit batches over 500 requests as ordered chunks (non-atomic)"),
|
|
31758
|
+
continueOnError: external_exports.boolean().optional().describe("After an atomic validation failure, submit requests individually and retain failures"),
|
|
31759
|
+
account: accountParam
|
|
31760
|
+
}
|
|
31761
|
+
}, async ({ batchId, autoSplit, continueOnError, account }) => {
|
|
31762
|
+
const args = ["batch", "end", batchId];
|
|
31763
|
+
if (autoSplit) args.push("--auto-split");
|
|
31764
|
+
if (continueOnError) args.push("--continue-on-error");
|
|
31765
|
+
return runOrDiagnose(args, { account });
|
|
31766
|
+
});
|
|
31767
|
+
server2.registerTool("gog_batch_abort", {
|
|
31768
|
+
description: "Discard a persisted batch and its composed requests without applying anything to the doc.",
|
|
31769
|
+
annotations: { destructiveHint: true },
|
|
31770
|
+
inputSchema: {
|
|
31771
|
+
batchId: external_exports.string().describe("Batch ID to discard"),
|
|
31772
|
+
account: accountParam
|
|
31773
|
+
}
|
|
31774
|
+
}, async ({ batchId, account }) => {
|
|
31775
|
+
return runOrDiagnose(["batch", "abort", batchId], { account });
|
|
31776
|
+
});
|
|
31777
|
+
server2.registerTool("gog_batch_list", {
|
|
31778
|
+
description: "List persisted Docs request batches (id, doc, label, request count, status).",
|
|
31779
|
+
annotations: { readOnlyHint: true },
|
|
31780
|
+
inputSchema: {
|
|
31781
|
+
account: accountParam
|
|
31782
|
+
}
|
|
31783
|
+
}, async ({ account }) => {
|
|
31784
|
+
return runOrDiagnose(["batch", "list"], { account });
|
|
31785
|
+
});
|
|
31786
|
+
server2.registerTool("gog_batch_show", {
|
|
31787
|
+
description: "Show one persisted batch: its doc, locked revision, and the composed requests awaiting submission.",
|
|
31788
|
+
annotations: { readOnlyHint: true },
|
|
31789
|
+
inputSchema: {
|
|
31790
|
+
batchId: external_exports.string().describe("Batch ID"),
|
|
31791
|
+
account: accountParam
|
|
31792
|
+
}
|
|
31793
|
+
}, async ({ batchId, account }) => {
|
|
31794
|
+
return runOrDiagnose(["batch", "show", batchId], { account });
|
|
31795
|
+
});
|
|
31796
|
+
server2.registerTool("gog_batch_prune", {
|
|
31797
|
+
description: "Delete stale persisted batches (not updated within olderThan).",
|
|
31798
|
+
annotations: { destructiveHint: true },
|
|
31799
|
+
inputSchema: {
|
|
31800
|
+
olderThan: external_exports.string().optional().describe("Delete batches not updated within this duration (e.g. 72h, 7d)"),
|
|
31801
|
+
account: accountParam
|
|
31802
|
+
}
|
|
31803
|
+
}, async ({ olderThan, account }) => {
|
|
31804
|
+
const args = ["batch", "prune"];
|
|
31805
|
+
if (olderThan) args.push(`--older-than=${olderThan}`);
|
|
31806
|
+
return runOrDiagnose(args, { account });
|
|
31807
|
+
});
|
|
31730
31808
|
server2.registerTool("gog_docs_table_column_width", {
|
|
31731
31809
|
description: "Set a fixed width (in points) for a table column, or reset columns to Docs-managed even distribution. Target the table by 1-based index in document order (negative counts from the end) and the column by 1-based number. Pass evenlyDistributed without col to reset every column in the table.",
|
|
31732
31810
|
annotations: { destructiveHint: true },
|
|
@@ -31737,15 +31815,17 @@ function registerExtraDocsTools(server2) {
|
|
|
31737
31815
|
evenlyDistributed: external_exports.boolean().optional().describe("Reset the selected column (or all columns when col is omitted) to Docs-managed equal width."),
|
|
31738
31816
|
tableIndex: external_exports.number().int().optional().describe("1-based table index in document order; negative counts from the end (default: 1)"),
|
|
31739
31817
|
tab: external_exports.string().optional().describe("Target tab title or ID"),
|
|
31818
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
31740
31819
|
account: accountParam
|
|
31741
31820
|
}
|
|
31742
|
-
}, async ({ docId, col, width, evenlyDistributed, tableIndex, tab, account }) => {
|
|
31821
|
+
}, async ({ docId, col, width, evenlyDistributed, tableIndex, tab, batch, account }) => {
|
|
31743
31822
|
const args = ["docs", "table-column-width", docId];
|
|
31744
31823
|
if (col !== void 0) args.push(`--col=${col}`);
|
|
31745
31824
|
if (width !== void 0) args.push(`--width=${width}`);
|
|
31746
31825
|
if (evenlyDistributed) args.push("--evenly-distributed");
|
|
31747
31826
|
if (tableIndex !== void 0) args.push(`--table-index=${tableIndex}`);
|
|
31748
31827
|
if (tab) args.push(`--tab=${tab}`);
|
|
31828
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
31749
31829
|
return runOrDiagnose(args, { account });
|
|
31750
31830
|
});
|
|
31751
31831
|
const tableSelectorParam = external_exports.string().optional().describe("Table selector: 1-based index (negative from the end), exact first-cell text, * for the only table, or text:VALUE for numeric/syntax-looking first-cell text (default: 1)");
|
|
@@ -31983,9 +32063,10 @@ function registerExtraDocsTools(server2) {
|
|
|
31983
32063
|
occurrence: external_exports.number().int().optional().describe("Use the Nth `at` match (1-based; required when `at` is ambiguous)"),
|
|
31984
32064
|
matchCase: external_exports.boolean().optional().describe("Case-sensitive `at` matching"),
|
|
31985
32065
|
tab: external_exports.string().optional().describe("Target a specific tab by title or ID"),
|
|
32066
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
31986
32067
|
account: accountParam
|
|
31987
32068
|
}
|
|
31988
|
-
}, async ({ docId, index, atEnd, at, occurrence, matchCase, tab, account }) => {
|
|
32069
|
+
}, async ({ docId, index, atEnd, at, occurrence, matchCase, tab, batch, account }) => {
|
|
31989
32070
|
const args = ["docs", "insert-page-break", docId];
|
|
31990
32071
|
if (index !== void 0) args.push(`--index=${index}`);
|
|
31991
32072
|
if (atEnd) args.push("--at-end");
|
|
@@ -31993,6 +32074,7 @@ function registerExtraDocsTools(server2) {
|
|
|
31993
32074
|
if (occurrence !== void 0) args.push(`--occurrence=${occurrence}`);
|
|
31994
32075
|
if (matchCase) args.push("--match-case");
|
|
31995
32076
|
if (tab) args.push(`--tab=${tab}`);
|
|
32077
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
31996
32078
|
return runOrDiagnose(args, { account });
|
|
31997
32079
|
});
|
|
31998
32080
|
server2.registerTool("gog_docs_page_layout", {
|
|
@@ -32084,9 +32166,10 @@ function registerExtraDocsTools(server2) {
|
|
|
32084
32166
|
underline: external_exports.boolean().optional().describe("Set cell text underline"),
|
|
32085
32167
|
tableIndex: external_exports.number().int().optional().describe("0-based table index in document order (default: 0)"),
|
|
32086
32168
|
tab: external_exports.string().optional().describe("Target a specific tab by title or ID"),
|
|
32169
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
32087
32170
|
account: accountParam
|
|
32088
32171
|
}
|
|
32089
|
-
}, async ({ docId, row, col, rowSpan, colSpan, backgroundColor, textColor, bold, italic, underline, tableIndex, tab, account }) => {
|
|
32172
|
+
}, async ({ docId, row, col, rowSpan, colSpan, backgroundColor, textColor, bold, italic, underline, tableIndex, tab, batch, account }) => {
|
|
32090
32173
|
const args = ["docs", "cell-style", docId, `--row=${row}`, `--col=${col}`];
|
|
32091
32174
|
if (rowSpan !== void 0) args.push(`--row-span=${rowSpan}`);
|
|
32092
32175
|
if (colSpan !== void 0) args.push(`--col-span=${colSpan}`);
|
|
@@ -32097,6 +32180,7 @@ function registerExtraDocsTools(server2) {
|
|
|
32097
32180
|
if (underline) args.push("--underline");
|
|
32098
32181
|
if (tableIndex !== void 0) args.push(`--table-index=${tableIndex}`);
|
|
32099
32182
|
if (tab) args.push(`--tab=${tab}`);
|
|
32183
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
32100
32184
|
return runOrDiagnose(args, { account });
|
|
32101
32185
|
});
|
|
32102
32186
|
server2.registerTool("gog_docs_insert_image", {
|
|
@@ -32140,9 +32224,10 @@ function registerExtraDocsTools(server2) {
|
|
|
32140
32224
|
occurrence: external_exports.number().int().optional().describe("Use the Nth `at` match (1-based; required when `at` is ambiguous)"),
|
|
32141
32225
|
matchCase: external_exports.boolean().optional().describe("Case-sensitive `at` matching"),
|
|
32142
32226
|
tab: external_exports.string().optional().describe("Target a specific tab by title or ID"),
|
|
32227
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
32143
32228
|
account: accountParam
|
|
32144
32229
|
}
|
|
32145
|
-
}, async ({ docId, email: email3, index, atEnd, at, occurrence, matchCase, tab, account }) => {
|
|
32230
|
+
}, async ({ docId, email: email3, index, atEnd, at, occurrence, matchCase, tab, batch, account }) => {
|
|
32146
32231
|
const args = ["docs", "insert-person", docId, `--email=${email3}`];
|
|
32147
32232
|
if (index !== void 0) args.push(`--index=${index}`);
|
|
32148
32233
|
if (atEnd) args.push("--at-end");
|
|
@@ -32150,6 +32235,7 @@ function registerExtraDocsTools(server2) {
|
|
|
32150
32235
|
if (occurrence !== void 0) args.push(`--occurrence=${occurrence}`);
|
|
32151
32236
|
if (matchCase) args.push("--match-case");
|
|
32152
32237
|
if (tab) args.push(`--tab=${tab}`);
|
|
32238
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
32153
32239
|
return runOrDiagnose(args, { account });
|
|
32154
32240
|
});
|
|
32155
32241
|
server2.registerTool("gog_docs_insert_date_chip", {
|
|
@@ -32162,15 +32248,17 @@ function registerExtraDocsTools(server2) {
|
|
|
32162
32248
|
index: external_exports.number().int().optional().describe("Character index to insert at. Omit or use atEnd for end-of-doc."),
|
|
32163
32249
|
atEnd: external_exports.boolean().optional().describe("Insert at end-of-doc/tab (mutually exclusive with index)"),
|
|
32164
32250
|
tab: external_exports.string().optional().describe("Target a specific tab by title or ID"),
|
|
32251
|
+
batch: external_exports.string().optional().describe("Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it \u2014 nothing changes in the doc until gog_batch_end submits the batch."),
|
|
32165
32252
|
account: accountParam
|
|
32166
32253
|
}
|
|
32167
|
-
}, async ({ docId, date: date5, format, index, atEnd, tab, account }) => {
|
|
32254
|
+
}, async ({ docId, date: date5, format, index, atEnd, tab, batch, account }) => {
|
|
32168
32255
|
const args = ["docs", "insert-date-chip", docId];
|
|
32169
32256
|
if (date5) args.push(`--date=${date5}`);
|
|
32170
32257
|
if (format) args.push(`--format=${format}`);
|
|
32171
32258
|
if (index !== void 0) args.push(`--index=${index}`);
|
|
32172
32259
|
if (atEnd) args.push("--at-end");
|
|
32173
32260
|
if (tab) args.push(`--tab=${tab}`);
|
|
32261
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
32174
32262
|
return runOrDiagnose(args, { account });
|
|
32175
32263
|
});
|
|
32176
32264
|
server2.registerTool("gog_docs_add_tab", {
|
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.
|
|
6
|
+
"version": "2.8.0",
|
|
7
7
|
"description": "Extended Google Docs for Claude via gogcli — auth + full Docs and comments support",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Chris Hall",
|
|
@@ -203,6 +203,30 @@
|
|
|
203
203
|
"name": "gog_docs_find_range",
|
|
204
204
|
"description": "Map literal text to its Docs API UTF-16 index range(s) for index-based edits."
|
|
205
205
|
},
|
|
206
|
+
{
|
|
207
|
+
"name": "gog_batch_begin",
|
|
208
|
+
"description": "Open a persisted, revision-locked request batch for a Doc; compose mutations with batch=<id>."
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"name": "gog_batch_end",
|
|
212
|
+
"description": "Submit a batch atomically (or with explicit split / partial-recovery modes)."
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"name": "gog_batch_abort",
|
|
216
|
+
"description": "Discard a batch without applying anything."
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"name": "gog_batch_list",
|
|
220
|
+
"description": "List persisted request batches."
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"name": "gog_batch_show",
|
|
224
|
+
"description": "Show a batch's doc, locked revision, and composed requests."
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"name": "gog_batch_prune",
|
|
228
|
+
"description": "Delete stale batches not updated within a duration."
|
|
229
|
+
},
|
|
206
230
|
{
|
|
207
231
|
"name": "gog_docs_table_column_width",
|
|
208
232
|
"description": "Set a fixed table column width in points, or reset columns to Docs-managed even distribution."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gogcli-mcp-docs",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
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.
|
|
10
|
+
"version": "2.8.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "gogcli-mcp-docs",
|
|
15
|
-
"version": "2.
|
|
15
|
+
"version": "2.8.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/tools/docs-extra.ts
CHANGED
|
@@ -28,9 +28,10 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
28
28
|
occurrence: z.number().int().optional().describe('Use the Nth `at` match (1-based; required when `at` is ambiguous)'),
|
|
29
29
|
matchCase: z.boolean().optional().describe('Case-sensitive `at` matching'),
|
|
30
30
|
tabId: z.string().optional().describe('Tab ID to delete content from (for multi-tab docs)'),
|
|
31
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
31
32
|
account: accountParam,
|
|
32
33
|
},
|
|
33
|
-
}, async ({ docId, start, end, at, occurrence, matchCase, tabId, account }) => {
|
|
34
|
+
}, async ({ docId, start, end, at, occurrence, matchCase, tabId, batch, account }) => {
|
|
34
35
|
const args = ['docs', 'delete'];
|
|
35
36
|
if (start !== undefined) args.push(`--start=${start}`);
|
|
36
37
|
if (end !== undefined) args.push(`--end=${end}`);
|
|
@@ -39,6 +40,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
39
40
|
if (occurrence !== undefined) args.push(`--occurrence=${occurrence}`);
|
|
40
41
|
if (matchCase) args.push('--match-case');
|
|
41
42
|
if (tabId) args.push(`--tab-id=${tabId}`);
|
|
43
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
42
44
|
return runOrDiagnose(args, { account });
|
|
43
45
|
});
|
|
44
46
|
|
|
@@ -122,6 +124,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
122
124
|
lineSpacing: z.number().optional().describe('Line spacing percentage (e.g. 100 for single, 150 for 1.5x, 200 for double)'),
|
|
123
125
|
headingLevel: z.number().int().optional().describe('Set paragraph named style to HEADING_1..HEADING_6 (shortcut for namedStyle=HEADING_N)'),
|
|
124
126
|
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'),
|
|
127
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
125
128
|
account: accountParam,
|
|
126
129
|
},
|
|
127
130
|
}, async (args) => {
|
|
@@ -142,6 +145,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
142
145
|
code?: boolean;
|
|
143
146
|
link?: string; noLink?: boolean;
|
|
144
147
|
alignment?: string;
|
|
148
|
+
batch?: string;
|
|
145
149
|
lineSpacing?: number;
|
|
146
150
|
headingLevel?: number;
|
|
147
151
|
namedStyle?: string;
|
|
@@ -171,6 +175,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
171
175
|
if (a.lineSpacing !== undefined) argv.push(`--line-spacing=${a.lineSpacing}`);
|
|
172
176
|
if (a.headingLevel !== undefined) argv.push(`--heading-level=${a.headingLevel}`);
|
|
173
177
|
if (a.namedStyle) argv.push(`--named-style=${a.namedStyle}`);
|
|
178
|
+
if (a.batch) argv.push(`--batch=${a.batch}`);
|
|
174
179
|
return runOrDiagnose(argv, { account: a.account });
|
|
175
180
|
});
|
|
176
181
|
|
|
@@ -202,9 +207,10 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
202
207
|
occurrence: z.number().int().optional().describe('Use the Nth `at` match (1-based; required when `at` is ambiguous)'),
|
|
203
208
|
matchCase: z.boolean().optional().describe('Case-sensitive `at` matching'),
|
|
204
209
|
tabId: z.string().optional().describe('Tab ID to insert into (for multi-tab docs)'),
|
|
210
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
205
211
|
account: accountParam,
|
|
206
212
|
},
|
|
207
|
-
}, async ({ docId, content, index, file, at, occurrence, matchCase, tabId, account }) => {
|
|
213
|
+
}, async ({ docId, content, index, file, at, occurrence, matchCase, tabId, batch, account }) => {
|
|
208
214
|
const args = ['docs', 'insert', docId];
|
|
209
215
|
if (content) args.push(content);
|
|
210
216
|
if (index !== undefined) args.push(`--index=${index}`);
|
|
@@ -213,6 +219,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
213
219
|
if (occurrence !== undefined) args.push(`--occurrence=${occurrence}`);
|
|
214
220
|
if (matchCase) args.push('--match-case');
|
|
215
221
|
if (tabId) args.push(`--tab-id=${tabId}`);
|
|
222
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
216
223
|
return runOrDiagnose(args, { account });
|
|
217
224
|
});
|
|
218
225
|
|
|
@@ -286,9 +293,10 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
286
293
|
matchCase: z.boolean().optional().describe('Case-sensitive `at` matching'),
|
|
287
294
|
tabId: z.string().optional().describe('Tab ID for multi-tab docs'),
|
|
288
295
|
pageless: z.boolean().optional().describe('Set document to pageless format'),
|
|
296
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
289
297
|
account: accountParam,
|
|
290
298
|
},
|
|
291
|
-
}, async ({ docId, text, file, index, replaceRange, markdown, at, occurrence, matchCase, tabId, pageless, account }) => {
|
|
299
|
+
}, async ({ docId, text, file, index, replaceRange, markdown, at, occurrence, matchCase, tabId, pageless, batch, account }) => {
|
|
292
300
|
const args = ['docs', 'update', docId];
|
|
293
301
|
if (text) args.push(`--text=${text}`);
|
|
294
302
|
if (file) args.push(`--file=${file}`);
|
|
@@ -300,6 +308,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
300
308
|
if (matchCase) args.push('--match-case');
|
|
301
309
|
if (tabId) args.push(`--tab-id=${tabId}`);
|
|
302
310
|
if (pageless) args.push('--pageless');
|
|
311
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
303
312
|
return runOrDiagnose(args, { account });
|
|
304
313
|
});
|
|
305
314
|
|
|
@@ -445,6 +454,83 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
445
454
|
return runOrDiagnose(args, { account });
|
|
446
455
|
});
|
|
447
456
|
|
|
457
|
+
// Persisted, revision-locked Docs request batches (gog 0.25): begin a batch,
|
|
458
|
+
// run docs mutation tools with batch=<id> to compose requests locally, then
|
|
459
|
+
// end to submit them atomically against the locked revision.
|
|
460
|
+
server.registerTool('gog_batch_begin', {
|
|
461
|
+
description: 'Open a persisted, revision-locked request batch for a Google Doc. Subsequent docs mutation tools called with batch=<batchId> append their requests locally instead of applying them; gog_batch_end submits everything atomically. Returns the batchId.',
|
|
462
|
+
inputSchema: {
|
|
463
|
+
docId: z.string().describe('Google Doc ID the batch is locked to'),
|
|
464
|
+
name: z.string().optional().describe('Optional batch label'),
|
|
465
|
+
account: accountParam,
|
|
466
|
+
},
|
|
467
|
+
}, async ({ docId, name, account }) => {
|
|
468
|
+
const args = ['batch', 'begin', `--doc=${docId}`];
|
|
469
|
+
if (name) args.push(`--name=${name}`);
|
|
470
|
+
return runOrDiagnose(args, { account });
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
server.registerTool('gog_batch_end', {
|
|
474
|
+
description: 'Submit a persisted batch: applies every composed request to the doc in one atomic batchUpdate against the locked revision. Atomic by default — set autoSplit to submit >500-request batches as ordered chunks (non-atomic), or continueOnError to retry individually after an atomic validation failure, retaining failures in the batch.',
|
|
475
|
+
annotations: { destructiveHint: true },
|
|
476
|
+
inputSchema: {
|
|
477
|
+
batchId: z.string().describe('Batch ID (from gog_batch_begin)'),
|
|
478
|
+
autoSplit: z.boolean().optional().describe('Submit batches over 500 requests as ordered chunks (non-atomic)'),
|
|
479
|
+
continueOnError: z.boolean().optional().describe('After an atomic validation failure, submit requests individually and retain failures'),
|
|
480
|
+
account: accountParam,
|
|
481
|
+
},
|
|
482
|
+
}, async ({ batchId, autoSplit, continueOnError, account }) => {
|
|
483
|
+
const args = ['batch', 'end', batchId];
|
|
484
|
+
if (autoSplit) args.push('--auto-split');
|
|
485
|
+
if (continueOnError) args.push('--continue-on-error');
|
|
486
|
+
return runOrDiagnose(args, { account });
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
server.registerTool('gog_batch_abort', {
|
|
490
|
+
description: 'Discard a persisted batch and its composed requests without applying anything to the doc.',
|
|
491
|
+
annotations: { destructiveHint: true },
|
|
492
|
+
inputSchema: {
|
|
493
|
+
batchId: z.string().describe('Batch ID to discard'),
|
|
494
|
+
account: accountParam,
|
|
495
|
+
},
|
|
496
|
+
}, async ({ batchId, account }) => {
|
|
497
|
+
return runOrDiagnose(['batch', 'abort', batchId], { account });
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
server.registerTool('gog_batch_list', {
|
|
501
|
+
description: 'List persisted Docs request batches (id, doc, label, request count, status).',
|
|
502
|
+
annotations: { readOnlyHint: true },
|
|
503
|
+
inputSchema: {
|
|
504
|
+
account: accountParam,
|
|
505
|
+
},
|
|
506
|
+
}, async ({ account }) => {
|
|
507
|
+
return runOrDiagnose(['batch', 'list'], { account });
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
server.registerTool('gog_batch_show', {
|
|
511
|
+
description: 'Show one persisted batch: its doc, locked revision, and the composed requests awaiting submission.',
|
|
512
|
+
annotations: { readOnlyHint: true },
|
|
513
|
+
inputSchema: {
|
|
514
|
+
batchId: z.string().describe('Batch ID'),
|
|
515
|
+
account: accountParam,
|
|
516
|
+
},
|
|
517
|
+
}, async ({ batchId, account }) => {
|
|
518
|
+
return runOrDiagnose(['batch', 'show', batchId], { account });
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
server.registerTool('gog_batch_prune', {
|
|
522
|
+
description: 'Delete stale persisted batches (not updated within olderThan).',
|
|
523
|
+
annotations: { destructiveHint: true },
|
|
524
|
+
inputSchema: {
|
|
525
|
+
olderThan: z.string().optional().describe('Delete batches not updated within this duration (e.g. 72h, 7d)'),
|
|
526
|
+
account: accountParam,
|
|
527
|
+
},
|
|
528
|
+
}, async ({ olderThan, account }) => {
|
|
529
|
+
const args = ['batch', 'prune'];
|
|
530
|
+
if (olderThan) args.push(`--older-than=${olderThan}`);
|
|
531
|
+
return runOrDiagnose(args, { account });
|
|
532
|
+
});
|
|
533
|
+
|
|
448
534
|
server.registerTool('gog_docs_table_column_width', {
|
|
449
535
|
description: 'Set a fixed width (in points) for a table column, or reset columns to Docs-managed even distribution. Target the table by 1-based index in document order (negative counts from the end) and the column by 1-based number. Pass evenlyDistributed without col to reset every column in the table.',
|
|
450
536
|
annotations: { destructiveHint: true },
|
|
@@ -455,15 +541,17 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
455
541
|
evenlyDistributed: z.boolean().optional().describe('Reset the selected column (or all columns when col is omitted) to Docs-managed equal width.'),
|
|
456
542
|
tableIndex: z.number().int().optional().describe('1-based table index in document order; negative counts from the end (default: 1)'),
|
|
457
543
|
tab: z.string().optional().describe('Target tab title or ID'),
|
|
544
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
458
545
|
account: accountParam,
|
|
459
546
|
},
|
|
460
|
-
}, async ({ docId, col, width, evenlyDistributed, tableIndex, tab, account }) => {
|
|
547
|
+
}, async ({ docId, col, width, evenlyDistributed, tableIndex, tab, batch, account }) => {
|
|
461
548
|
const args = ['docs', 'table-column-width', docId];
|
|
462
549
|
if (col !== undefined) args.push(`--col=${col}`);
|
|
463
550
|
if (width !== undefined) args.push(`--width=${width}`);
|
|
464
551
|
if (evenlyDistributed) args.push('--evenly-distributed');
|
|
465
552
|
if (tableIndex !== undefined) args.push(`--table-index=${tableIndex}`);
|
|
466
553
|
if (tab) args.push(`--tab=${tab}`);
|
|
554
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
467
555
|
return runOrDiagnose(args, { account });
|
|
468
556
|
});
|
|
469
557
|
|
|
@@ -719,9 +807,10 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
719
807
|
occurrence: z.number().int().optional().describe('Use the Nth `at` match (1-based; required when `at` is ambiguous)'),
|
|
720
808
|
matchCase: z.boolean().optional().describe('Case-sensitive `at` matching'),
|
|
721
809
|
tab: z.string().optional().describe('Target a specific tab by title or ID'),
|
|
810
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
722
811
|
account: accountParam,
|
|
723
812
|
},
|
|
724
|
-
}, async ({ docId, index, atEnd, at, occurrence, matchCase, tab, account }) => {
|
|
813
|
+
}, async ({ docId, index, atEnd, at, occurrence, matchCase, tab, batch, account }) => {
|
|
725
814
|
const args = ['docs', 'insert-page-break', docId];
|
|
726
815
|
if (index !== undefined) args.push(`--index=${index}`);
|
|
727
816
|
if (atEnd) args.push('--at-end');
|
|
@@ -729,6 +818,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
729
818
|
if (occurrence !== undefined) args.push(`--occurrence=${occurrence}`);
|
|
730
819
|
if (matchCase) args.push('--match-case');
|
|
731
820
|
if (tab) args.push(`--tab=${tab}`);
|
|
821
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
732
822
|
return runOrDiagnose(args, { account });
|
|
733
823
|
});
|
|
734
824
|
|
|
@@ -824,9 +914,10 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
824
914
|
underline: z.boolean().optional().describe('Set cell text underline'),
|
|
825
915
|
tableIndex: z.number().int().optional().describe('0-based table index in document order (default: 0)'),
|
|
826
916
|
tab: z.string().optional().describe('Target a specific tab by title or ID'),
|
|
917
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
827
918
|
account: accountParam,
|
|
828
919
|
},
|
|
829
|
-
}, async ({ docId, row, col, rowSpan, colSpan, backgroundColor, textColor, bold, italic, underline, tableIndex, tab, account }) => {
|
|
920
|
+
}, async ({ docId, row, col, rowSpan, colSpan, backgroundColor, textColor, bold, italic, underline, tableIndex, tab, batch, account }) => {
|
|
830
921
|
const args = ['docs', 'cell-style', docId, `--row=${row}`, `--col=${col}`];
|
|
831
922
|
if (rowSpan !== undefined) args.push(`--row-span=${rowSpan}`);
|
|
832
923
|
if (colSpan !== undefined) args.push(`--col-span=${colSpan}`);
|
|
@@ -837,6 +928,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
837
928
|
if (underline) args.push('--underline');
|
|
838
929
|
if (tableIndex !== undefined) args.push(`--table-index=${tableIndex}`);
|
|
839
930
|
if (tab) args.push(`--tab=${tab}`);
|
|
931
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
840
932
|
return runOrDiagnose(args, { account });
|
|
841
933
|
});
|
|
842
934
|
|
|
@@ -882,9 +974,10 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
882
974
|
occurrence: z.number().int().optional().describe('Use the Nth `at` match (1-based; required when `at` is ambiguous)'),
|
|
883
975
|
matchCase: z.boolean().optional().describe('Case-sensitive `at` matching'),
|
|
884
976
|
tab: z.string().optional().describe('Target a specific tab by title or ID'),
|
|
977
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
885
978
|
account: accountParam,
|
|
886
979
|
},
|
|
887
|
-
}, async ({ docId, email, index, atEnd, at, occurrence, matchCase, tab, account }) => {
|
|
980
|
+
}, async ({ docId, email, index, atEnd, at, occurrence, matchCase, tab, batch, account }) => {
|
|
888
981
|
const args = ['docs', 'insert-person', docId, `--email=${email}`];
|
|
889
982
|
if (index !== undefined) args.push(`--index=${index}`);
|
|
890
983
|
if (atEnd) args.push('--at-end');
|
|
@@ -892,6 +985,7 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
892
985
|
if (occurrence !== undefined) args.push(`--occurrence=${occurrence}`);
|
|
893
986
|
if (matchCase) args.push('--match-case');
|
|
894
987
|
if (tab) args.push(`--tab=${tab}`);
|
|
988
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
895
989
|
return runOrDiagnose(args, { account });
|
|
896
990
|
});
|
|
897
991
|
|
|
@@ -905,15 +999,17 @@ export function registerExtraDocsTools(server: McpServer): void {
|
|
|
905
999
|
index: z.number().int().optional().describe('Character index to insert at. Omit or use atEnd for end-of-doc.'),
|
|
906
1000
|
atEnd: z.boolean().optional().describe('Insert at end-of-doc/tab (mutually exclusive with index)'),
|
|
907
1001
|
tab: z.string().optional().describe('Target a specific tab by title or ID'),
|
|
1002
|
+
batch: z.string().optional().describe('Append this mutation to a persisted batch (from gog_batch_begin) instead of applying it — nothing changes in the doc until gog_batch_end submits the batch.'),
|
|
908
1003
|
account: accountParam,
|
|
909
1004
|
},
|
|
910
|
-
}, async ({ docId, date, format, index, atEnd, tab, account }) => {
|
|
1005
|
+
}, async ({ docId, date, format, index, atEnd, tab, batch, account }) => {
|
|
911
1006
|
const args = ['docs', 'insert-date-chip', docId];
|
|
912
1007
|
if (date) args.push(`--date=${date}`);
|
|
913
1008
|
if (format) args.push(`--format=${format}`);
|
|
914
1009
|
if (index !== undefined) args.push(`--index=${index}`);
|
|
915
1010
|
if (atEnd) args.push('--at-end');
|
|
916
1011
|
if (tab) args.push(`--tab=${tab}`);
|
|
1012
|
+
if (batch) args.push(`--batch=${batch}`);
|
|
917
1013
|
return runOrDiagnose(args, { account });
|
|
918
1014
|
});
|
|
919
1015
|
|
|
@@ -1482,3 +1482,163 @@ describe('gog_docs_read json tab targeting', () => {
|
|
|
1482
1482
|
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['docs', 'raw', 'd1', '--pretty', '--all-tabs'], { account: undefined });
|
|
1483
1483
|
});
|
|
1484
1484
|
});
|
|
1485
|
+
|
|
1486
|
+
// --- gog 0.25.0: persisted Docs request batches ---
|
|
1487
|
+
|
|
1488
|
+
describe('batch lifecycle tools', () => {
|
|
1489
|
+
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' });
|
|
1493
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1494
|
+
['batch', 'begin', '--doc=d1', '--name=big-edit'],
|
|
1495
|
+
{ account: undefined },
|
|
1496
|
+
);
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1499
|
+
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' });
|
|
1503
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1504
|
+
['batch', 'begin', '--doc=d1'],
|
|
1505
|
+
{ account: undefined },
|
|
1506
|
+
);
|
|
1507
|
+
});
|
|
1508
|
+
|
|
1509
|
+
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 });
|
|
1513
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1514
|
+
['batch', 'end', 'b1', '--auto-split', '--continue-on-error'],
|
|
1515
|
+
{ account: undefined },
|
|
1516
|
+
);
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
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' });
|
|
1523
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(
|
|
1524
|
+
['batch', 'end', 'b1'],
|
|
1525
|
+
{ account: undefined },
|
|
1526
|
+
);
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
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' });
|
|
1533
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'abort', 'b1'], { account: undefined });
|
|
1534
|
+
});
|
|
1535
|
+
|
|
1536
|
+
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')!({});
|
|
1540
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'list'], { account: undefined });
|
|
1541
|
+
await handlers.get('gog_batch_show')!({ batchId: 'b1' });
|
|
1542
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'show', 'b1'], { account: undefined });
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
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' });
|
|
1549
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'prune', '--older-than=72h'], { account: undefined });
|
|
1550
|
+
});
|
|
1551
|
+
|
|
1552
|
+
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')!({});
|
|
1556
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(['batch', 'prune'], { account: undefined });
|
|
1557
|
+
});
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1560
|
+
describe('batch param on docs mutation tools (gog 0.25.0)', () => {
|
|
1561
|
+
// Each wrapped docs command that gained --batch appends to a persisted
|
|
1562
|
+
// batch instead of submitting when batch is supplied.
|
|
1563
|
+
const cases: Array<{ tool: string; input: Record<string, unknown>; expected: string[] }> = [
|
|
1564
|
+
{ tool: 'gog_docs_insert', input: { docId: 'd1', content: 'X', batch: 'b1' },
|
|
1565
|
+
expected: ['docs', 'insert', 'd1', 'X', '--batch=b1'] },
|
|
1566
|
+
{ tool: 'gog_docs_delete', input: { docId: 'd1', start: 1, end: 5, batch: 'b1' },
|
|
1567
|
+
expected: ['docs', 'delete', '--start=1', '--end=5', 'd1', '--batch=b1'] },
|
|
1568
|
+
{ tool: 'gog_docs_update', input: { docId: 'd1', text: 'T', batch: 'b1' },
|
|
1569
|
+
expected: ['docs', 'update', 'd1', '--text=T', '--batch=b1'] },
|
|
1570
|
+
{ tool: 'gog_docs_format', input: { docId: 'd1', match: 'm', bold: true, batch: 'b1' },
|
|
1571
|
+
expected: ['docs', 'format', 'd1', '--match=m', '--bold', '--batch=b1'] },
|
|
1572
|
+
{ tool: 'gog_docs_insert_page_break', input: { docId: 'd1', atEnd: true, batch: 'b1' },
|
|
1573
|
+
expected: ['docs', 'insert-page-break', 'd1', '--at-end', '--batch=b1'] },
|
|
1574
|
+
{ tool: 'gog_docs_insert_person', input: { docId: 'd1', email: 'a@b.com', batch: 'b1' },
|
|
1575
|
+
expected: ['docs', 'insert-person', 'd1', '--email=a@b.com', '--batch=b1'] },
|
|
1576
|
+
{ tool: 'gog_docs_insert_date_chip', input: { docId: 'd1', batch: 'b1' },
|
|
1577
|
+
expected: ['docs', 'insert-date-chip', 'd1', '--batch=b1'] },
|
|
1578
|
+
{ tool: 'gog_docs_table_column_width', input: { docId: 'd1', col: 1, width: 80, batch: 'b1' },
|
|
1579
|
+
expected: ['docs', 'table-column-width', 'd1', '--col=1', '--width=80', '--batch=b1'] },
|
|
1580
|
+
{ tool: 'gog_docs_cell_style', input: { docId: 'd1', row: 0, col: 0, bold: true, batch: 'b1' },
|
|
1581
|
+
expected: ['docs', 'cell-style', 'd1', '--row=0', '--col=0', '--bold', '--batch=b1'] },
|
|
1582
|
+
];
|
|
1583
|
+
|
|
1584
|
+
for (const { tool, input, expected } of cases) {
|
|
1585
|
+
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);
|
|
1589
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(expected, { account: undefined });
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
});
|
|
1593
|
+
|
|
1594
|
+
// Truthy --tab/--table branches the original tests missed (exposed by the
|
|
1595
|
+
// vitest 4.1.8 V8 remapping change — these flags were untested on these tools).
|
|
1596
|
+
describe('tab/table selector branch coverage', () => {
|
|
1597
|
+
const cases: Array<{ tool: string; input: Record<string, unknown>; expected: string[] }> = [
|
|
1598
|
+
{ tool: 'gog_docs_table_row_delete', input: { docId: 'd1', row: 1, tab: 'T' },
|
|
1599
|
+
expected: ['docs', 'table-row', 'delete', 'd1', '--row=1', '--tab=T'] },
|
|
1600
|
+
{ tool: 'gog_docs_table_column_delete', input: { docId: 'd1', col: 1, table: '2', tab: 'T' },
|
|
1601
|
+
expected: ['docs', 'table-column', 'delete', 'd1', '--col=1', '--table=2', '--tab=T'] },
|
|
1602
|
+
{ tool: 'gog_docs_table_merge', input: { docId: 'd1', range: '1,1:2,2', tab: 'T' },
|
|
1603
|
+
expected: ['docs', 'table-merge', 'd1', '--range=1,1:2,2', '--tab=T'] },
|
|
1604
|
+
{ tool: 'gog_docs_table_unmerge', input: { docId: 'd1', cell: '1,1', table: 'Budget' },
|
|
1605
|
+
expected: ['docs', 'table-unmerge', 'd1', '--cell=1,1', '--table=Budget'] },
|
|
1606
|
+
{ tool: 'gog_docs_named_range_list', input: { docId: 'd1', tab: 'T' },
|
|
1607
|
+
expected: ['docs', 'named-range', 'list', 'd1', '--tab=T'] },
|
|
1608
|
+
{ tool: 'gog_docs_named_range_delete', input: { docId: 'd1', nameOrId: 'nr', tab: 'T' },
|
|
1609
|
+
expected: ['docs', 'named-range', 'delete', 'd1', 'nr', '--tab=T'] },
|
|
1610
|
+
{ tool: 'gog_docs_images_list', input: { docId: 'd1', tab: 'T' },
|
|
1611
|
+
expected: ['docs', 'images', 'list', 'd1', '--tab=T'] },
|
|
1612
|
+
{ tool: 'gog_docs_headings_list', input: { docId: 'd1', tab: 'T' },
|
|
1613
|
+
expected: ['docs', 'headings', 'list', 'd1', '--tab=T'] },
|
|
1614
|
+
{ tool: 'gog_docs_insert_date_chip', input: { docId: 'd1', tab: 'T' },
|
|
1615
|
+
expected: ['docs', 'insert-date-chip', 'd1', '--tab=T'] },
|
|
1616
|
+
];
|
|
1617
|
+
for (const { tool, input, expected } of cases) {
|
|
1618
|
+
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);
|
|
1622
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(expected, { account: undefined });
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1627
|
+
describe('bare-call branch coverage (no optional flags)', () => {
|
|
1628
|
+
const cases: Array<{ tool: string; input: Record<string, unknown>; expected: string[] }> = [
|
|
1629
|
+
{ tool: 'gog_docs_table_column_insert', input: { docId: 'd1' },
|
|
1630
|
+
expected: ['docs', 'table-column', 'insert', 'd1'] },
|
|
1631
|
+
{ tool: 'gog_docs_tables_list', input: { docId: 'd1' },
|
|
1632
|
+
expected: ['docs', 'tables', 'list', 'd1'] },
|
|
1633
|
+
{ tool: 'gog_docs_paragraphs_list', input: { docId: 'd1' },
|
|
1634
|
+
expected: ['docs', 'paragraphs', 'list', 'd1'] },
|
|
1635
|
+
];
|
|
1636
|
+
for (const { tool, input, expected } of cases) {
|
|
1637
|
+
it(`${tool} with no optional flags`, async () => {
|
|
1638
|
+
vi.mocked(lib.runOrDiagnose).mockResolvedValue(toText('{}'));
|
|
1639
|
+
const handlers = setupHandlers();
|
|
1640
|
+
await handlers.get(tool)!(input);
|
|
1641
|
+
expect(lib.runOrDiagnose).toHaveBeenCalledWith(expected, { account: undefined });
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
});
|