Back to .md Directory

CLAUDE.md - Development Guide

Guides AI assistants on building and testing a Windows-only CLI tool that automates Classic Outlook via COM.

May 2, 2026
0 downloads
3 views
ai claude workflow automation safety
View source

What this file does

Guides AI assistants on building and testing a Windows-only CLI tool that automates Classic Outlook via COM.

When to use it

  • You need a development guide for a CLI tool that talks to Outlook via COM
  • You want to enforce a draft-first, safety-gated email workflow
  • You are setting up a Claude Code Skill for email automation
  • You need patterns for JSON output, error handling, and COM access in a Python project

Assumes this stack

Pythonpywin32pytestuvCOMClaude Code

CLAUDE.md - Development Guide

This document provides context for AI assistants (Claude) working on the outlookctl project.

Project Overview

outlookctl is a local CLI tool for automating Classic Outlook on Windows via COM automation. It includes a Claude Code Skill that enables AI-assisted email management.

Key Points

  • Local automation only - No external APIs, no OAuth, no cloud services
  • COM-based - Uses Windows COM (Outlook.Application) via pywin32
  • Classic Outlook required - New Outlook does not support COM
  • Safety-first design - Draft-first workflow, explicit send confirmation

Architecture

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│   Claude Code   │────▶│    outlookctl    │────▶│ Classic Outlook │
│     (Skill)     │     │   (Python CLI)   │     │     (COM)       │
└─────────────────┘     └──────────────────┘     └─────────────────┘

Module Responsibilities

ModulePurpose
cli.pyArgparse CLI entry point, command routing
models.pyDataclasses for JSON serialization
outlook_com.pyCOM automation wrapper, all Outlook interactions
safety.pySend confirmation gates, validation
audit.pyAudit logging for send operations

Running Commands

During Development

# From project directory
uv run python -m outlookctl.cli <command> [options]

# Examples
uv run python -m outlookctl.cli doctor
uv run python -m outlookctl.cli list --count 5
uv run python -m outlookctl.cli search --from "someone@example.com"

From Any Directory

uv run --project "C:/Users/GordonMickel/work/outlookctl" python -m outlookctl.cli <command>

Running Tests

uv run python -m pytest tests/ -v

Code Patterns

JSON Output

All commands output JSON via models.py dataclasses:

from outlookctl.models import ListResult, MessageSummary, FolderInfo

result = ListResult(
    folder=FolderInfo(name="Inbox"),
    items=[...],
)
print(json.dumps(result.to_dict(), indent=2))

Error Handling

Use ErrorResult for consistent error output:

from outlookctl.models import ErrorResult

def output_error(error: str, error_code: str = None, remediation: str = None):
    result = ErrorResult(error=error, error_code=error_code, remediation=remediation)
    print(json.dumps(result.to_dict(), indent=2))
    sys.exit(1)

COM Access Pattern

Always use get_outlook_app() which handles retries and error messages:

from outlookctl.outlook_com import get_outlook_app, OutlookNotAvailableError

try:
    outlook = get_outlook_app()
    # Use outlook object...
except OutlookNotAvailableError as e:
    output_error(str(e), "OUTLOOK_UNAVAILABLE")

Safety Gates

All send operations must go through safety validation:

from outlookctl.safety import validate_send_confirmation, SendConfirmationError

try:
    validate_send_confirmation(confirm_send="YES")
except SendConfirmationError as e:
    output_error(str(e), "CONFIRMATION_REQUIRED")

Skill System

Skill Location

  • Personal: ~/.claude/skills/outlook-automation/
  • Project: .claude/skills/outlook-automation/

SKILL.md Structure

---
name: outlook-automation
description: >
  Brief description for Claude to understand when to use this skill...
---

# Skill content with usage instructions...

Installing/Updating Skill

uv run python tools/install_skill.py --personal

Important Constraints

Never Auto-Send

The skill instructs Claude to always use draft-first workflow:

  1. Create draft with draft command
  2. Show preview to user
  3. Only send after explicit user confirmation

Metadata by Default

Body content should only be retrieved when explicitly requested:

  • list and search return metadata only by default
  • Use --include-body or --include-body-snippet when needed

Confirmation Required

The send command requires --confirm-send YES (exact string match).

Common Development Tasks

Adding a New Command

  1. Add command handler in cli.py:

    def cmd_newcommand(args: argparse.Namespace) -> None:
        # Implementation
    
  2. Add subparser in create_parser():

    new_parser = subparsers.add_parser("newcommand", help="...")
    new_parser.add_argument(...)
    new_parser.set_defaults(func=cmd_newcommand)
    
  3. Add result model in models.py if needed

  4. Update SKILL.md and reference docs

Adding COM Functionality

Add to outlook_com.py, following existing patterns:

  • Use get_outlook_app() for COM connection
  • Handle exceptions appropriately
  • Return typed data (dataclasses from models.py)

Updating the Skill

After any changes to CLI or behavior:

# Update skill files in skills/outlook-automation/
# Then reinstall:
uv run python tools/install_skill.py --personal

Testing

Unit Tests

uv run python -m pytest tests/ -v

Manual Testing

# Test doctor
uv run python -m outlookctl.cli doctor

# Test list
uv run python -m outlookctl.cli list --count 3

# Test search
uv run python -m outlookctl.cli search --unread-only --count 5

# Test draft (creates actual draft in Outlook)
uv run python -m outlookctl.cli draft --to "test@example.com" --subject "Test" --body-text "Test body"

Skill Testing

Start a new Claude Code session and test prompts:

  • "Summarize my latest 5 unread emails"
  • "Find emails from GitHub"
  • "Draft a reply to the most recent email"

Troubleshooting

"Access is denied" on outlookctl.exe

The globally installed executable has permission issues in Git Bash. Use:

uv run python -m outlookctl.cli <command>

COM Connection Fails

  1. Ensure Classic Outlook is running
  2. Check it's not New Outlook (toggle at top-right)
  3. Run doctor to diagnose

Tests Fail to Import

uv sync  # Ensure dependencies are installed

File Locations

FilePurpose
pyproject.tomlProject config, dependencies
src/outlookctl/Main Python package
skills/outlook-automation/SKILL.mdSkill definition
skills/outlook-automation/reference/Skill documentation
tools/install_skill.pySkill installer
tests/pytest tests
evals/Skill evaluation scenarios

Dependencies

  • pywin32 - Windows COM automation
  • pytest (dev) - Testing

Version History

  • 0.1.0 - Initial implementation
    • CLI with doctor, list, get, search, draft, send, attachments save
    • Claude Code Skill with safety guardrails
    • Draft-first workflow, audit logging

What's inside

13 sections covering architecture, commands, code patterns, skill system, testing, troubleshooting, and file locations

Change this for your project

  • Replace C:/Users/GordonMickel/work/outlookctl with your own project path
  • Replace gmickel/outlookctl with your own repository name
  • Replace outlookctl with your own package name throughout

Where it goes

Save as CLAUDE.md in your repository root. Claude Code reads it automatically at the start of every session.

Worth borrowing

  • Draft-first workflow with explicit send confirmation as a safety pattern
  • Using ErrorResult dataclass for consistent JSON error output across all commands
  • Separating COM automation into a single wrapper module with retry logic

Related Documents