# Gdoc Control

> Command Line Tool for retrieving, creating, and editing Google Docs.

- Skill: `waf/gdoc-control` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add waf/gdoc-control`
- Raw SKILL.md: https://api.skillmd.com/api/skills/waf/gdoc-control/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: waf (https://skillmd.com/u/waf)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/waf/gdoc-control

---


# gdoc-control — Google Docs CLI Skill

A command-line tool for reading and writing Google Docs. All output is JSON on stdout. Errors are JSON on stderr with a non-zero exit code.

Invocation: `gdoc-control <command> [args]`

---

## Commands

### search — find documents

```
gdoc-control search <query> [--limit <n>]
```

- `query` — full-text search string (searches document content and title)
- `--limit` — max results, default 10

**Output:**
```json
{
  "files": [
    {
      "id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
      "name": "Q3 Status Report",
      "modifiedTime": "2024-11-01T14:32:00.000Z",
      "url": "https://docs.google.com/document/d/1BxiM.../edit"
    }
  ]
}
```

---

### get — read a document

```
gdoc-control get <document-id>
gdoc-control get <document-id> --structured
```

- Default output: `body` is extracted plain text with headings prefixed `#`, `##`, etc.
- `--structured`: `body` is the raw Google Docs API body JSON (includes character indices — use this when you need precise locations for editing, not for normal reading).

**Output (default):**
```json
{
  "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
  "title": "Q3 Status Report",
  "revisionId": "AOV_f48abc123...",
  "body": "# Q3 Status Report\nSome intro text.\n\n## Status\nAll good.\n\n## Next Steps\nDo things."
}
```

**Important:** The `revisionId` is used by the update commands for optimistic concurrency. You do not need to pass it manually — the update commands fetch it automatically. It is shown in `get` output for informational purposes.

---

### create — create a document from markdown

```
gdoc-control create <title> --content <markdown>
gdoc-control create <title> --stdin         # read markdown from stdin
```

Markdown is converted to HTML via Markdig (advanced extensions enabled), then uploaded to Google Drive as a Google Doc.

**Output:**
```json
{
  "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
  "title": "My New Doc",
  "url": "https://docs.google.com/document/d/1BxiM.../edit"
}
```

---

### update append — add text at the end

```
gdoc-control update append <document-id> --text <text>
```

Appends a newline followed by `text` at the very end of the document body.

**Output:**
```json
{
  "documentId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms",
  "revisionId": "AOV_f48def456..."
}
```

---

### update replace-text — find and replace all occurrences

```
gdoc-control update replace-text <document-id> --search <text> --replacement <text>
```

- Replaces **all** occurrences of `--search` with `--replacement` across the entire document.
- Match is **case-sensitive**.

**Output:** same shape as append.

---

### update replace-section — replace content under a heading

```
gdoc-control update replace-section <document-id> --heading <heading> --content <text>
gdoc-control update replace-section <document-id> --heading <heading> --stdin
```

- Finds the first heading paragraph whose text **exactly matches** `--heading` (case-sensitive, trimmed).
- Deletes all content between that heading and the next heading at the same or higher level (or end of document).
- Inserts `--content` in its place.
- The heading paragraph itself is preserved.

**Output:** same shape as append.

---

## Error response

All errors are written to **stderr** with exit code 1:

```json
{ "error": "Concurrent edit conflict — re-read the document and retry", "status": 412 }
{ "error": "Heading not found: \"Status\"" }
{ "error": "The caller does not have permission", "status": 403 }
```

---

## Workflow guidance

### Reading a document
Use `get` to read content. The plain-text `body` field uses markdown-style heading markers (`#`, `##`, etc.) so you can identify sections by name for use with `replace-section`.

### Editing safely
All update commands internally call `get` first to fetch the current `revisionId`, then submit the edit with `WriteControl.RequiredRevisionId`. If someone else edits the document between the read and write, the API returns 412 and the write is rejected — nothing is silently overwritten.

**On a 412 error:** retry the entire operation (re-read, then re-apply the edit).

### Finding a document ID
- From `search` output: use the `id` field.
- From a Google Docs URL: `https://docs.google.com/document/d/<ID>/edit` — the `<ID>` segment.

### Using replace-section correctly
Run `get` first. The heading text to pass to `--heading` is the heading line from `body` **without** the `#` prefix. For example, if `body` contains `## Next Steps`, pass `--heading "Next Steps"`.

### Multi-line content in --text / --content
Use `--stdin` and pipe text in, or use shell `$'...'` syntax for embedded newlines. Example:
```
printf "Line one\nLine two\n" | gdoc-control update append <id> --stdin
```
(Note: `append` does not have `--stdin`; use `replace-section --stdin` or `create --stdin` for multi-line content. For append, embed `\n` directly in the `--text` value.)

