Skip to content

waf/gdoc-control

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gdoc-control — Google Docs CLI

A command-line tool for searching, reading, creating, and safely editing Google Docs. Designed to be invoked programmatically (e.g. by an AI agent), with structured JSON output and optimistic concurrency control to avoid clobbering concurrent edits.

Features

  • Search Google Drive for Docs by full-text query
  • Read a document as plain text or raw API JSON
  • Create a document from Markdown
  • Update a document safely:
    • Append text at the end
    • Find-and-replace text across the whole document
    • Replace the content under a named heading
  • All updates use WriteControl.RequiredRevisionId — if someone else edits the document between your read and write, the API rejects the write rather than silently overwriting it

Prerequisites

  • .NET 10 SDK
  • A Google Cloud project with the Google Docs API and Google Drive API enabled
  • An OAuth 2.0 Desktop app credential (client ID + secret)

Google Cloud setup

  1. Go to Google Cloud Console and select or create a project.
  2. Enable the APIs:
    • APIs & Services → Library → search Google Docs API → Enable
    • APIs & Services → Library → search Google Drive API → Enable
  3. Create credentials:
    • APIs & Services → Credentials → Create Credentials → OAuth client ID
    • Application type: Desktop app
    • Download or note the Client ID and Client Secret
  4. Set environment variables:
    GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
    GOOGLE_CLIENT_SECRET=your-client-secret
    

Build

cd GDocControl
dotnet build

To publish a self-contained executable:

dotnet publish -c Release -r win-x64 --self-contained
# Binary: bin/Release/net10.0/win-x64/publish/gdoc-control.exe

Authentication

On first run, the tool opens a browser tab asking you to sign in to Google and grant access to Docs and Drive. After you authorize, the OAuth tokens are saved locally and reused automatically on subsequent runs — no browser prompt needed again unless the token is revoked or expires.

Token storage location:

  • Windows: %APPDATA%\GoogleApis\gdoc-cli\
  • Linux/macOS: ~/.local/share/google-filedatastore/gdoc-cli/

To force re-authentication, delete the token files from that directory.


Usage

Search for documents

gdoc-control search "project proposal"
gdoc-control search "meeting notes" --limit 20
{
  "files": [
    {
      "id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
      "name": "Q3 Project Proposal",
      "modifiedTime": "2024-11-01T14:32:00.000Z",
      "url": "https://docs.google.com/document/d/1BxiM.../edit"
    }
  ]
}

Read a document

gdoc-control get 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms

Body is returned as extracted plain text. Headings are prefixed with #/##/etc. for readability.

{
  "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
  "title": "Q3 Project Proposal",
  "revisionId": "AOV_f48abc123...",
  "body": "# Q3 Project Proposal\nIntroduction text.\n\n## Goals\nGoal one.\n\n## Timeline\nSee below."
}

Add --structured to get the raw Google Docs API body JSON instead (useful for debugging or inspecting element indices):

gdoc-control get <id> --structured

Create a document from Markdown

gdoc-control create "Meeting Notes" --content "# Meeting Notes

## Attendees
- Alice
- Bob

## Action Items
- Follow up by Friday"

Or pipe from a file:

gdoc-control create "Design Doc" --stdin < design.md
{
  "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
  "title": "Meeting Notes",
  "url": "https://docs.google.com/document/d/1BxiM.../edit"
}

Append text

gdoc-control update append <id> --text "New paragraph added at the end."

Find and replace text

gdoc-control update replace-text <id> --search "Q3" --replacement "Q4"

Replaces all occurrences. Match is case-sensitive.

Replace a section

gdoc-control update replace-section <id> --heading "Timeline" --content "Phase 1: March
Phase 2: April"

Or pipe content from stdin:

gdoc-control update replace-section <id> --heading "Action Items" --stdin < action-items.txt

The heading text must match exactly (case-sensitive) as it appears in the document — without the # prefix. The heading paragraph itself is kept; only the content beneath it is replaced, up to the next heading of the same or higher level.


Concurrency control

All update commands (append, replace-text, replace-section) use the Google Docs API's WriteControl.RequiredRevisionId mechanism:

  1. The command reads the document to get the current revisionId.
  2. It submits the edit with that revisionId in WriteControl.RequiredRevisionId.
  3. If anyone else edited the document in the meantime, Google rejects the write with HTTP 412.

On a 412 error, the tool outputs:

{ "error": "Concurrent edit conflict — re-read the document and retry", "status": 412 }

Simply re-run the command. The second attempt will read the updated document and apply the edit on top of the latest revision.


Error handling

Errors are written to stderr as JSON with exit code 1:

{ "error": "The caller does not have permission", "status": 403 }
{ "error": "Requested entity was not found.", "status": 404 }
{ "error": "Heading not found: \"Timeline\"" }
{ "error": "GOOGLE_CLIENT_ID environment variable is not set." }

Project structure

GDocControl/
├── Program.cs       — CLI commands (System.CommandLine 2.0.3)
├── GoogleAuth.cs    — OAuth Installed App flow
├── DocsClient.cs    — Google Docs / Drive API operations
├── Models.cs        — JSON output record types
└── GDocControl.csproj

Dependencies: Google.Apis.Docs.v1, Google.Apis.Drive.v3, Markdig, System.CommandLine

About

Google Docs CLI - structured editing (concurrent editing safe) and doc creation from markdown. Built in .NET but does not require it to run.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages