Skip to content

docs(github-plugin): document authentication strategy decision and gh CLI wrapper rationale #88

@finxo

Description

@finxo

Summary

This issue documents the architectural decision to use gh CLI as a wrapper for GitHub operations in the Titan GitHub plugin, rather than using direct API libraries like PyGithub. It captures the decision-making process, trade-offs, and rationale for future reference.

Background

The GitHub plugin currently implements all GitHub operations through the gh CLI wrapper approach. This decision was made after evaluating multiple authentication and API integration strategies. This issue serves as documentation for the chosen approach and provides context for potential future reconsiderations.

Current Implementation: gh CLI Wrapper

The plugin uses gh CLI as a subprocess wrapper for all GitHub operations, delegating authentication, token management, and API calls to the official GitHub CLI tool.

Advantages ✅

  1. Zero Python dependencies - Only uses subprocess and json from stdlib
  2. Authentication handled externally - gh auth login manages OAuth flow and token storage
  3. Independent updates - gh CLI updates independently of Titan
  4. Complete feature set - Access to all GitHub features without implementing them
  5. Better UX - Visual OAuth flow vs manual token management
  6. Secure by default - Tokens stored in system keyring, not in config files

Disadvantages ❌

  1. External dependency - Requires gh CLI to be installed
  2. Output parsing - Need to parse text/JSON from command output
  3. Less control - Dependent on gh CLI for error handling and behavior
  4. Subprocess overhead - Each operation spawns a new process

Alternative Considered: PyGithub / Direct API

A pure Python approach using libraries like PyGithub was evaluated as an alternative.

Advantages ✅

  1. Native Python API - More Pythonic interface
  2. No external binaries - Pure Python dependency
  3. Full control - Complete control over error handling and behavior
  4. Better performance - No subprocess overhead
  5. Type hints - Better IDE support and type checking

Disadvantages ❌

  1. New dependency - Adds PyGithub to requirements
  2. Token management - Titan must handle OAuth/PAT creation and storage
  3. Maintenance burden - Need to track GitHub API changes
  4. More complex setup - Users need to manually create and configure tokens
  5. Security concerns - Need to implement secure token storage

Comparison Matrix

Feature gh CLI PyGithub
Python Dependencies ✅ None ❌ PyGithub
External Dependencies ❌ gh CLI ✅ None
Authentication UX ✅ Visual OAuth ❌ Manual token
Token Storage ✅ System keyring ⚠️ Need to implement
API Coverage ✅ Complete ✅ Complete
Error Control ❌ Limited ✅ Full
Performance ⚠️ Subprocess ✅ Direct calls
Maintenance ✅ External ❌ Internal
Setup Complexity ✅ Simple ❌ Complex

Decision

Keep gh CLI approach for the following reasons:

  1. Superior UX - OAuth flow is significantly better than manual token creation
  2. Less code to maintain - Authentication, token refresh, and API changes handled externally
  3. More secure - Tokens never touch Titan's configuration or code
  4. Easier onboarding - gh auth login is intuitive and visual
  5. Proven approach - Many CLI tools use this pattern (GitHub's official recommendation)

Known Issues & Solutions

GITHUB_TOKEN Environment Variable Conflicts

Issue: Environment variable can override gh CLI auth
Solution: Enhanced error diagnostics in _check_auth() to detect and suggest fixes
Documentation: Added warnings about GITHUB_TOKEN conflicts

Multiple Auth Sources

Issue: gh CLI can have multiple accounts configured
Solution: Parse gh auth status to detect inactive accounts and suggest gh auth switch

When to Reconsider

This decision should be reconsidered if:

  • GitHub deprecates gh CLI (unlikely)
  • Need features not available in gh CLI (rare)
  • Subprocess overhead becomes a bottlennel (unlikely for CLI tool)
  • Need to support environments where binary installation is restricted

Potential Future Enhancement: Hybrid Approach

A hybrid system could be implemented as a fallback mechanism, trying gh CLI first and falling back to PyGithub if a token is available. However, this adds complexity and dual maintenance burden without significant benefit for the current use case.

Related Files

  • plugins/titan-plugin-github/titan_plugin_github/clients/github_client.py - Main client implementation
  • plugins/titan-plugin-github/README.md - Plugin documentation
  • .titan/config.toml - GitHub plugin configuration

Future Improvements

  • Add titan doctor command to verify gh CLI installation
  • Improve README with gh CLI installation instructions
  • Add troubleshooting guide for common authentication issues
  • Consider adding titan github login alias for gh auth login

Code Snippets

Current gh CLI Wrapper Implementation

class GitHubClient:
    def _run_gh_command(self, args: List[str]) -> str:
        result = subprocess.run(["gh"] + args, ...)
        return result.stdout.strip()

Alternative PyGithub Implementation (Not Used)

from github import Github

class GitHubClient:
    def __init__(self, token: str):
        self.g = Github(token)
    
    def get_pull_request(self, pr_number: int):
        repo = self.g.get_repo(f"{self.owner}/{self.name}")
        return repo.get_pull(pr_number)

Potential Hybrid Approach (Future Consideration)

class GitHubClient:
    def __init__(self, config, secrets):
        # Try gh CLI first (preferred)
        try:
            self._check_gh_cli()
            self.mode = "gh_cli"
        except GitHubCLINotFound:
            # Fallback to PyGithub if token available
            token = secrets.get("github_token")
            if token:
                self.g = Github(token)
                self.mode = "pygithub"
            else:
                raise GitHubAuthenticationError(...)

User Setup Process Comparison

Current (gh CLI):

# User setup:
1. brew install gh
2. gh auth login  # Visual OAuth flow
3. titan  # Works immediately

Alternative (PyGithub):

# User setup:
1. Go to github.com/settings/tokens
2. Create Personal Access Token with correct scopes
3. Copy token
4. titan configure github  # Paste token
5. titan  # Works

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions