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

Publishing to PyPI

IMPORTANT: Always publish after fixing bugs or adding features!

Complete workflow for bug fixes and features:

# 1. Make changes and test
uv run pytest

# 2. Commit the changes
git add <files>
git commit -m "Fix: description" # or "Feature: description"

# 3. Update version in BOTH files
# pyproject.toml (line 3) and src/md_to_gdocs/__init__.py (line 13)
# Bug fix: 0.2.1 → 0.2.2 (PATCH)
# Feature: 0.2.1 → 0.3.0 (MINOR)

# 4. Build and publish
uv sync
uv run pytest  # Verify again
uv build
uv publish

# 5. Commit version bump and push
git add pyproject.toml src/md_to_gdocs/__init__.py uv.lock
git commit -m "Bump version to X.Y.Z"
git tag vX.Y.Z
git push origin main vX.Y.Z

Why publish immediately?

  • ✅ Users get bug fixes right away
  • ✅ Keeps repo and PyPI in sync
  • ✅ Catches issues early

See DEPLOYMENT.md for detailed publishing instructions and troubleshooting.

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.2.tar.gz (35.5 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.2-py3-none-any.whl (20.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: md_to_gdocs-0.2.2.tar.gz
  • Upload date:
  • Size: 35.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for md_to_gdocs-0.2.2.tar.gz
Algorithm Hash digest
SHA256 aae626320597b4d4ae5843bbd3f19b8f5e2304ff945fa5ac4ce93707aac4b6b5
MD5 5cc9c364a2b050e1b1ff9c9b6bb76df8
BLAKE2b-256 b587389b5c148da89d7fa5404f7e82d606fa321835834e25fdf72b061f41bfc7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: md_to_gdocs-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 20.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.2

File hashes

Hashes for md_to_gdocs-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a99b68bff35e70b578109ef51d5e048b1f4b343667a59427f9062d5278df4d1a
MD5 e95b13c5ec4ce7bf50b437193d198a12
BLAKE2b-256 3d4827ffce4908e8c23c5b739f375f6d2efbf0e34e6758bd0e0611fb0e01b5ff

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