Skip to main content

Convert markdown to Google Docs with formatting support

Project description

md-to-gdocs

Convert markdown files to Google Docs with formatting support. Both CLI tool and Python library.

Features

  • CLI and Library: Use as command-line tool or Python library
  • Rich Formatting: Headers, bold, italic, links, strikethrough, inline code
  • Lists: Bullet lists, numbered lists
  • Advanced Features: Tables, code blocks, blockquotes, horizontal rules
  • Flexible Operations: Create, append, insert at specific positions
  • Dry Run Mode: Preview changes before applying
  • JSON Output: Automation-friendly output format

Installation

Local Development

cd /path/to/markdown-to-google-doc
uv sync

From PyPI

pip install md-to-gdocs
# or with uv
uv pip install md-to-gdocs

Prerequisites

Google Authentication

You need a google_token.json file with Google OAuth2 credentials.

Token File Locations (checked in order):

  1. ./google_token.json (current directory - highest priority)
  2. ~/.claude/google_token.json (Claude directory)
  3. Custom path via --token flag

How to Generate Token:

Option 1: Using Claude Code

/google-auth  # Creates ~/.claude/google_token.json

Option 2: Manual Setup (for standalone use)

  1. Go to Google Cloud Console
  2. Create a project or select existing one
  3. Enable Google Docs API
  4. Create OAuth 2.0 credentials (Desktop app)
  5. Download credentials and save as google_token.json
  6. Place in current directory or ~/.claude/

Required Scope: https://www.googleapis.com/auth/documents

CLI Usage

Create New Document

# Basic usage (title derived from filename)
md-to-gdocs create README.md

# With custom title
md-to-gdocs create README.md --title "My Documentation"

# With custom token path
md-to-gdocs create README.md --token /path/to/google_token.json

# Dry run (preview without creating)
md-to-gdocs create README.md --dry-run

# JSON output (for scripting)
md-to-gdocs create README.md --output-json

Append to Existing Document

# Append markdown to end of document
md-to-gdocs append 1abc234def567 changelog.md

# With dry run
md-to-gdocs append 1abc234def567 changelog.md --dry-run

Insert at Specific Index

# Insert at character index 1234
md-to-gdocs update-index 1abc234def567 section.md 1234

# Use --dry-run to preview
md-to-gdocs update-index 1abc234def567 section.md 1234 --dry-run

Find Text (Get Character Indices)

# Find text and get indices (JSON output by default)
md-to-gdocs find 1abc234def567 "Section Header"

# Human-readable output
md-to-gdocs find 1abc234def567 "Section Header" --no-output-json

Python Library Usage

Create Document

from md_to_gdocs import create_document

markdown_content = """
# Main Header

This is **bold** and *italic* text.

- List item 1
- List item 2
"""

result = create_document(
    markdown_content=markdown_content,
    title="My Documentation"
)

if result.success:
    print(f"Created: {result.doc_url}")
    print(f"Doc ID: {result.doc_id}")
else:
    print(f"Error: {result.error}")

Append to Document

from md_to_gdocs import append_to_document

result = append_to_document(
    doc_id="1abc234def567",
    markdown_content="## New Section\n\nNew content here."
)

if result.success:
    print("Content appended successfully")

Insert at Index

from md_to_gdocs import update_at_index

result = update_at_index(
    doc_id="1abc234def567",
    markdown_content="## Inserted Section",
    start_index=1234
)

Find Text

from md_to_gdocs import find_text

locations = find_text(
    doc_id="1abc234def567",
    search_text="Section Header"
)

for loc in locations:
    print(f"Found at: {loc.index} - {loc.end_index}")
    print(f"Context: {loc.context}")

Dry Run Mode

result = create_document(
    markdown_content="# Test",
    title="Test Doc",
    dry_run=True
)

if result.dry_run:
    print(f"Would execute {len(result.requests)} API requests")
    print("Requests:", result.requests)

Supported Markdown Syntax

Headers

# H1 Header
## H2 Header
### H3 Header
#### H4 Header
##### H5 Header
###### H6 Header

Inline Formatting

**bold text**
*italic text*
***bold and italic***
~~strikethrough text~~
`inline code`
[link text](https://example.com)

Lists

# Bullet lists
- Bullet item 1
- Bullet item 2

* Also works with asterisks
* Another item

# Numbered lists
1. First item
2. Second item
3. Third item

Code Blocks

```python
def hello():
    print("Hello, world!")
```

```javascript
const greeting = "Hello, world!";
console.log(greeting);
```

Tables

| Header 1 | Header 2 | Header 3 |
|----------|----------|----------|
| Cell 1   | Cell 2   | Cell 3   |
| Cell 4   | Cell 5   | Cell 6   |

Blockquotes

> This is a blockquote
> It can span multiple lines

Horizontal Rules

---
***
___

Paragraphs

Regular text paragraphs are preserved. Blank lines create spacing.

Finding Document IDs

Google Doc IDs are in the URL:

https://docs.google.com/document/d/1abc234def567/edit
                                   ^^^^^^^^^^^^^^
                                   This is the doc_id

Workflow: Replace Section

To replace content between markers:

  1. Add HTML comments as markers in your Google Doc:

    <!-- START_SECTION -->
    Old content here
    <!-- END_SECTION -->
    
  2. Find the marker positions:

    md-to-gdocs find 1abc234def567 "<!-- START_SECTION -->"
    # Returns: index: 100, end_index: 125
    
    md-to-gdocs find 1abc234def567 "<!-- END_SECTION -->"
    # Returns: index: 500, end_index: 523
    
  3. Use the indices to insert new content (Phase 2 feature)

Error Handling

Authentication Errors

Error: Token file not found. Searched:
  - ./google_token.json
  - ~/.claude/google_token.json

Solution:

  • Place google_token.json in current directory or ~/.claude/
  • Or use --token flag to specify custom path
  • Generate token using /google-auth (Claude Code) or Google Cloud Console

Document Not Found

Error: Document 1abc234def567 not found

Solution: Check the document ID. Ensure you have access to the document.

Invalid Token

Error: Invalid credentials. Please run /google-auth skill to re-authenticate.

Solution: Token may be expired. Re-run /google-auth.

Development

Setup

# Clone and install dependencies
cd /Users/kprice/repos/misc/kj/markdown-to-google-doc
uv sync

# Install dev dependencies
uv sync --dev

Run Tests

# Run all tests with coverage
uv run pytest

# Run specific test file
uv run pytest tests/test_parsers.py

# Run with verbose output
uv run pytest -v

Code Quality

# Format code
uv run black src/ tests/

# Type checking
uv run mypy src/

Version Management

When making ANY code changes, ALWAYS update versions in BOTH locations:

  1. pyproject.toml (line 3): version = "X.Y.Z"
  2. src/md_to_gdocs/__init__.py (line 13): __version__ = "X.Y.Z"

Semantic Versioning Guidelines:

  • MAJOR (X.0.0): Breaking changes
  • MINOR (0.X.0): New features (backward compatible)
  • PATCH (0.0.X): Bug fixes (backward compatible)

After version bump:

uv sync  # Updates uv.lock

Project Structure

markdown-to-google-doc/
├── src/md_to_gdocs/
│   ├── __init__.py          # Public API exports
│   ├── cli.py               # Typer CLI commands
│   ├── core.py              # Core library functions
│   ├── auth.py              # Google OAuth handling
│   ├── api.py               # Google Docs API operations
│   ├── models.py            # Data classes
│   ├── exceptions.py        # Custom exceptions
│   └── parsers/
│       ├── __init__.py
│       └── custom.py        # Markdown parser
└── tests/
    ├── conftest.py          # Pytest fixtures
    ├── test_parsers.py      # Parser tests
    ├── test_api.py          # API tests
    ├── test_core.py         # Core tests
    └── test_cli.py          # CLI tests

Roadmap

Phase 1 (Completed ✅)

  • ✅ Markdown parsing (headers, bold, italic, lists, links)
  • ✅ Advanced features (tables, code blocks, blockquotes, horizontal rules, strikethrough)
  • ✅ CLI commands (create, append, update-index, find)
  • ✅ Python library API
  • ✅ Dry run mode
  • ✅ Comprehensive tests

Phase 2 (Next)

  • ⏳ Section management (replace, delete between markers)
  • ⏳ Index-based section operations
  • ⏳ Enhanced text search

Phase 3 (Future)

  • ⏳ CI/CD pipeline
  • ⏳ PyPI publication
  • ⏳ Alternative parser options (mistune)

Claude Code Integration

Want to use this as a Claude Code skill for automated documentation workflows?

See SKILL_CREATION.md for complete instructions on:

  • Converting this library into a reusable Claude skill
  • Usage patterns and workflow examples
  • Integration with /google-auth and other skills
  • Automated documentation publishing workflows

Contributing

This is currently a personal project. Contributions welcome after PyPI publication.

License

MIT License - see LICENSE file for details

Author

KJ Price

Acknowledgments

  • Parser design inspired by ai-book project
  • Google Docs API patterns from gdocs_editor.py
  • Built with: Typer, Rich, Google API Python Client

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

md_to_gdocs-0.2.1.tar.gz (32.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

md_to_gdocs-0.2.1-py3-none-any.whl (19.2 kB view details)

Uploaded Python 3

File details

Details for the file md_to_gdocs-0.2.1.tar.gz.

File metadata

  • Download URL: md_to_gdocs-0.2.1.tar.gz
  • Upload date:
  • Size: 32.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.28

File hashes

Hashes for md_to_gdocs-0.2.1.tar.gz
Algorithm Hash digest
SHA256 65175393aa5af08b6822297461074530b7f3fdbd0b0865d4a4f6dd7a85a9343b
MD5 a6ed05303651d5b304c021c1554531af
BLAKE2b-256 92b201dd4401a1944990e27d893b82e891e70927457cee6e45cc1004fe358c19

See more details on using hashes here.

File details

Details for the file md_to_gdocs-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for md_to_gdocs-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d9e8045650687c24e933db7a9cc897c0e25229302bdcfe5f659b23f40e20986c
MD5 14396f9b6f5685fca3bca6c013378937
BLAKE2b-256 ab090ec7679c1a7534626cf9335e4474ffc69b2ba3a555166449be1f911ec01c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page