Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Read this skill when you need to read or write Google Docs. Reading works without opening a browser tab.
.claude/skills/kunanonj-aside-google-docs/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 12% | 0% |
| case-07 | ✗→✓ | ▲ Improved | -14% | 0% |
| case-09 | ✗→✓ | ▲ Improved | -5% | 0% |
| case-10 | ✗→✓ | ▲ Improved | -6% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -6% | 0% |
Use the googleDocs global in the REPL tool. Cookie-only reads work without opening a tab; editing operations open a Docs tab automatically.
IMPORTANT: User may use multiple Google accounts. ALWAYS CALL console.log(await googleAccounts.list()) to identify the correct account UID when constructing URLs with /u/{uid}/.
js// Parse URL to get docId and uid const { docId, uid } = googleDocs.parseUrl('https://docs.google.com/document/u/1/d/abc123/edit'); // → { docId: 'abc123', uid: 1 } // Read document (cookie-only, no tab needed) const html = await googleDocs.getDocumentHTML('https://docs.google.com/document/u/1/d/.../edit'); const text = await googleDocs.getDocumentText('https://docs.google.com/document/u/1/d/.../edit'); // Or pass { docId, uid } directly const html2 = await googleDocs.getDocumentHTML({ docId: 'abc123', uid: 1 }); // Edit document (opens tab) await googleDocs.connect(url); const title = await googleDocs.getTitle(); const liveText = await googleDocs.getLiveText(); await googleDocs.insertText('Hello world'); await googleDocs.dispose(); // Apply surgical diffs (the killer feature) await googleDocs.connect(url); await googleDocs.applyDiffs([ { type: 'replace', startIndex: 0, endIndex: 5, text: 'Hello' }, { type: 'insert', startIndex: 10, text: ' world' }, { type: 'delete', startIndex: 20, endIndex: 25 }, ]); await googleDocs.dispose(); // Apply diffs as suggestions await googleDocs.connect(url); await googleDocs.applyDiffsAsSuggestions(diffs, 'Fixed grammar issues'); await googleDocs.dispose();
googleDocs.parseUrl(url): { docId: string; uid?: number }Extract docId and uid from a Google Docs URL. Useful for multi-account scenarios where you need to pass { docId, uid } to read methods.
googleDocs.getDocumentHTML(target): Promise<string>Fetch the full document as HTML. target is a URL string or { docId: string; uid?: number }. Include /u/{uid}/ in the URL (or pass uid) for multi-account.
googleDocs.getDocumentText(target): Promise<string>Fetch the full document as plain text. Same target signature as getDocumentHTML.
Call connect(url) before using these, and dispose() when done.
googleDocs.connect(url): Promise<void>Open a Docs tab and wait for the editor to load.
googleDocs.dispose(): Promise<void>Close the Docs tab.
googleDocs.getTitle(): Promise<string>Read the document title from the live editor.
googleDocs.getLiveText(): Promise<string>Read the full document text from the live editor. Note: temporarily selects all text to read it.
googleDocs.getSelectedContent(): Promise<{ text: string; html: string }>Read the current selection as text and HTML.
googleDocs.insertText(text): Promise<void>Insert text at the current cursor position.
googleDocs.selectAll(): Promise<void>Select all document text.
googleDocs.selectTextRange(startIndex, endIndex): Promise<void>Select a range of text by character indices.
googleDocs.insertHtmlContent(html): Promise<void>Paste rich HTML content at the cursor position.
googleDocs.pasteFromMarkdown(markdown): Promise<void>Use Docs' "Paste from Markdown" context menu item.
googleDocs.applyDiffs(diffs): Promise<void>Apply surgical text modifications using select-and-replace. Diffs are sorted end-to-start so later edits don't shift earlier indices.
tsinterface TextDiff { type: 'replace' | 'delete' | 'insert'; startIndex: number; endIndex?: number; // required for replace and delete text?: string; // required for replace and insert }
googleDocs.applyDiffsAsSuggestions(diffs, explanation?): Promise<void>Apply diffs as Google Docs suggestions. Switches to suggestion mode, applies diffs, then switches back. Optionally adds an explanation comment.
googleDocs.addComment(text): Promise<void>Add a comment on the current selection.
googleDocs.deleteSelection(): Promise<void>Delete the currently selected text.
connect() → edit → dispose().applyDiffs() and applyDiffsAsSuggestions() are the primary editing mechanisms — they're precise, index-based, and handle multiple edits atomically.Cmd+ACmd+FCmd+Shift+HCmd+Alt+MCmd+Alt+1 through Cmd+Alt+6Cmd+B, Cmd+I, Cmd+UCmd+Z, Cmd+Y| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 5,016 | 5,141 | +2% | 1 | 1 | 0% | 1,158 | 1,677 | +45% | 0 | 0 | — |
case-02 | fail→fail | 8,636 | 5,193 | -40% | 1 | 1 | 0% | 1,914 | 1,787 | -7% | 0 | 0 | — |
case-03 | fail→fail | 3,799 | 6,703 | +76% | 1 | 1 | 0% | 758 | 1,789 | +136% | 0 | 0 | — |
case-08 | fail→pass | 7,060 | 1,609 | -77% | 1 | 1 | 0% | 1,546 | 1,734 | +12% | 0 | 0 | — |
case-04 | pass→pass | 5,173 | 7,539 | +46% | 1 | 1 | 0% | 1,378 | 2,779 | +102% | 0 | 0 | — |
case-05 | pass→pass | 9,929 | 11,258 | +13% | 1 | 1 | 0% | 2,431 | 4,042 | +66% | 0 | 0 | — |
case-06 | pass→pass | 8,701 | 6,312 | -27% | 1 | 1 | 0% | 2,083 | 2,981 | +43% | 0 | 0 | — |
case-07 | fail→pass | 10,643 | 5,309 | -50% | 1 | 1 | 0% | 2,359 | 2,036 | -14% | 0 | 0 | — |
case-09 | fail→pass | 8,805 | 1,816 | -79% | 1 | 1 | 0% | 1,881 | 1,782 | -5% | 0 | 0 | — |
case-10 | fail→pass | 8,437 | 2,359 | -72% | 1 | 1 | 0% | 1,959 | 1,849 | -6% | 0 | 0 | — |
case-11 | fail→pass | 25,668 | 2,680 | -90% | 1 | 1 | 0% | 2,128 | 2,003 | -6% | 0 | 0 | — |
case-12 | fail→pass | 9,123 | 3,346 | -63% | 1 | 1 | 0% | 2,043 | 2,160 | +6% | 0 | 0 | — |
case-13 | pass→pass | 10,137 | 5,435 | -46% | 1 | 1 | 0% | 2,150 | 2,460 | +14% | 0 | 0 | — |
case-14 | pass→pass | 6,639 | 1,408 | -79% | 1 | 1 | 0% | 1,236 | 1,616 | +31% | 0 | 0 | — |
case-15 | fail→pass | 10,817 | 2,193 | -80% | 1 | 1 | 0% | 2,256 | 1,795 | -20% | 0 | 0 | — |
case-16 | fail→pass | 10,516 | 1,663 | -84% | 1 | 1 | 0% | 2,138 | 1,661 | -22% | 0 | 0 | — |
case-17 | pass→pass | 9,391 | 1,364 | -85% | 1 | 1 | 0% | 1,871 | 1,627 | -13% | 0 | 0 | — |
case-18 | fail→pass | 6,838 | 1,660 | -76% | 1 | 1 | 0% | 1,427 | 1,752 | +23% | 0 | 0 | — |
case-19 | fail→pass | 12,029 | 2,557 | -79% | 1 | 1 | 0% | 2,394 | 1,861 | -22% | 0 | 0 | — |
case-20 | fail→pass | 11,249 | 2,274 | -80% | 1 | 1 | 0% | 2,417 | 1,843 | -24% | 0 | 0 | — |
case-21 | fail→pass | 7,894 | 1,453 | -82% | 1 | 1 | 0% | 1,617 | 1,615 | -0% | 0 | 0 | — |
case-22 | pass→pass | 10,625 | 3,985 | -62% | 1 | 1 | 0% | 2,035 | 2,059 | +1% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 19 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +55 percentage points is the difference between those two pass rates over the 19 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.