Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

gdocs-comments

Text-anchored comments on Google Docs from the command line, and as a Claude Code skill.

Comments that attach to a specific sentence and appear in the margin — not document-level notes floating in the sidebar. Anchoring is by exact text, so there is no index arithmetic to get wrong.

python3 scripts/gdoc_comments.py --doc DOC_ID \
  add "This claim needs a citation." \
  --anchor "the exact sentence from the document"

Why this is not straightforward

The obvious approach does not work. The Drive API's comments.create accepts an anchor field, and Google's own documentation explains why that is a trap:

Developers can define their own format for the anchor specification, but the anchor is saved and returned when retrieving the comment; however Google Workspace editor apps treat these comments as un-anchored comments.

Seven anchor formats were tried against it — 0-based offsets, 1-based, integer and string ty, segment ids, classic kix, tab-scoped, line anchors. All were accepted without error. None bound to text. Every one returned an empty quotedFileContent, which is Drive's own signal that anchoring failed. Do not spend an afternoon there.

The mechanism that does work is InsertCommentRequest in the Docs API's documents.batchUpdate, which takes an ordinary range of start and end indices. It is available only through the Google Workspace Developer Preview Program.

Requirements

  • Python 3.9+
  • gws, the Google Workspace CLI, authenticated (gws auth login)
  • Enrollment in the Google Workspace Developer Preview Program — see below

Joining the Google Workspace Developer Preview Program

Enrollment is free and takes a couple of days. It attaches to both a human Google account and a Google Cloud project, and preview API access is granted through the project.

  1. Have a Google Cloud project with the Google Docs API and Google Drive API enabled. If you already authenticate with gws, run gws auth status and note the project_id. That is the project to enroll, because it is the one behind your OAuth client.

  2. Read the Program Terms — you confirm agreement on the form.

  3. Submit the application form with your Google Workspace account and your Cloud project number. The project number is the numeric one, not the project id — find it on the Cloud console dashboard.

  4. Accept the Google Group invitation. Google adds your address to a program group; make sure the account can be added to Groups, or the enrollment stalls here.

  5. Wait for the confirmation email. Usually a couple of days, up to a week. Your project is registered during this step, and that registration is what unlocks the preview methods.

Two things worth knowing before you apply. Service accounts cannot be enrolled — the program requires a human Google Workspace account. And if you later authenticate against a different Cloud project, preview access does not follow you; the enrollment is tied to the project you registered.

Preview features typically stay in preview for three to six months before general availability, at which point the discovery patch below becomes unnecessary.

Install

As a plain CLI:

git clone https://github.com/julianfleck/gdocs-comments.git
python3 gdocs-comments/scripts/gdoc_comments.py --help

As a Claude Code skill:

git clone https://github.com/julianfleck/gdocs-comments.git ~/.claude/skills/gdocs-comments

Claude then picks it up automatically when a task involves commenting on a Google Doc.

Usage

DOC_ID is the id in the document URL: docs.google.com/document/d/<DOC_ID>/edit

T=scripts/gdoc_comments.py

# add a comment anchored to exact text
python3 $T --doc DOC_ID add "Needs a source." --anchor "the exact sentence"

# disambiguate repeated text
python3 $T --doc DOC_ID add "Second instance." --anchor "the phrase" --occurrence 2

# raw Docs indices, when the text is awkward to quote
python3 $T --doc DOC_ID add "Note." --range 2925,3291

# assign to someone
python3 $T --doc DOC_ID add "Can you check?" --anchor "..." --assignee colleague@example.com

# read
python3 $T --doc DOC_ID list
python3 $T --doc DOC_ID list --json
python3 $T --doc DOC_ID list --since 2026-08-03T01:50 --unresolved   # what changed since

# diff the body against a saved snapshot
python3 $T --doc DOC_ID snapshot before.txt
python3 $T --doc DOC_ID diff before.txt [--context 3] [--update]

# reply, resolve, reopen
python3 $T --doc DOC_ID reply COMMENT_ID "Good catch."
python3 $T --doc DOC_ID reply COMMENT_ID "Fixed." --resolve
python3 $T --doc DOC_ID reply COMMENT_ID "Reopening." --reopen

# edit a post
python3 $T --doc DOC_ID update COMMENT_ID POST_ID "Corrected wording."

# delete a reply, or a whole thread
python3 $T --doc DOC_ID delete COMMENT_ID --post-id POST_ID
python3 $T --doc DOC_ID delete COMMENT_ID

Reviewing a whole document

bulk places many comments in one API call and is the right shape for a review pass:

[
  {"anchor": "first exact sentence",  "text": "Unsupported at this scale."},
  {"anchor": "second exact sentence", "text": "Contradicts the previous section."},
  {"anchor": "a repeated phrase", "text": "Third instance only.", "occurrence": 3}
]
python3 $T --doc DOC_ID bulk review.json

Anchors that no longer match are reported and skipped rather than failing the batch, so a stale quote costs one comment instead of the run.

How it works

gws validates request bodies against a cached API discovery document. The public Docs discovery document lists 41 request types and none of them are comment-related, so gws rejects insertComment before it ever reaches Google.

The script patches that cache — ~/.config/gws/cache/docs_v1.json — additively, adding the five comment request schemas and wiring them into Request. It runs on every invocation and is idempotent, so if gws refreshes the cache the patch is silently reapplied. Your original is backed up once to docs_v1.json.pre-comments.bak.

Writes go through the Docs API. Reads go through the Drive API, which returns quotedFileContent — the anchored text, and the reliable test of whether anchoring worked.

Notes

Take a snapshot before any batch edit and diff afterwards. Rebuilding a document by delete-and-reinsert is easy to get subtly wrong, and the diff catches it in one line. Docs soft line breaks (\x0b) are normalised, so a wrapped heading does not read as a spurious change.

Anchor to a whole sentence or paragraph rather than a fragment. Short anchors match in unintended places and the first occurrence wins.

Anchor before editing. Rewriting the body invalidates quotes captured earlier, so either place comments against the current text or re-read the document first.

Deleting a thread is not reversible through this tool. Resolving is, via --reopen, so prefer --resolve for anything that might matter later.

The Docs API can accept, reject and delete existing suggestions, but cannot create them. There is no programmatic route to suggestion mode.

Troubleshooting

PERMISSION_DENIED, or an unknown-field error on insertComment — the account or its Cloud project is not enrolled in the Developer Preview, or gws is authenticated against a different project than the one registered. Check with gws auth status.

Comments appear but highlight nothing — that is the Drive API fallback behaviour, which means the Docs path did not run. Confirm the discovery patch applied: python3 scripts/gdoc_comments.py --doc x ensure-schema.

gws discovery cache not found — run any gws docs command once so the cache is created, then retry.

License

MIT

About

Text-anchored comments on Google Docs from the CLI, and as a Claude Code skill. Uses the Workspace Developer Preview.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages