md-to-gdocs 0.2.3
Convert markdown to Google Docs with formatting support
License: MIT
Requires Python: >=3.11
Keywords: cli, converter, documentation, google-api, google-docs, markdown

# 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

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

### From PyPI

```bash
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**
```bash
/google-auth  # Creates ~/.claude/google_token.json
```

**Option 2: Manual Setup (for standalone use)**
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
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

```bash
# 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

```bash
# 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

```bash
# 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)

```bash
# 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

```python
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

```python
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

```python
from md_to_gdocs import update_at_index

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

### Find Text

```python
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

```python
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

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

### Inline Formatting

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

### Lists

```markdown
# 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

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

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

### Tables

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

### Blockquotes

```markdown
> This is a blockquote
> It can span multiple lines
```

### Horizontal Rules

```markdown
---
***
___
```

### 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:
   ```html
   <!-- START_SECTION -->
   Old content here
   <!-- END_SECTION -->
   ```

2. Find the marker positions:
   ```bash
   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

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

# Install dev dependencies
uv sync --dev
```

### Run Tests

```bash
# 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

```bash
# 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:**
```bash
uv sync  # Updates uv.lock
```

### Publishing to PyPI

**IMPORTANT: Always publish after fixing bugs or adding features!**

Complete workflow for bug fixes and features:

```bash
# 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
twine upload dist/*

# 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](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](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](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