Skip to main content

Overview

Codaph captures activity from multiple coding agents:
  • Codex (OpenAI): SDK and exec adapters
  • Claude Code (Anthropic): History importer
  • Gemini CLI (Google): History importer
From src/lib/agent-providers.ts:4-112:
export type AgentProviderId = "codex" | "claude-code" | "gemini-cli";

const PROVIDER_LABELS: Record<AgentProviderId, string> = {
  codex: "Codex",
  "claude-code": "Claude Code",
  "gemini-cli": "Gemini CLI",
};

const PROVIDER_MARKERS: Record<AgentProviderId, string> = {
  codex: ".codex",
  "claude-code": ".claude",
  "gemini-cli": ".gemini",
};

Provider Detection

Codaph auto-detects providers by checking for marker directories:
async function detectAgentProvidersForRepo(repoRoot: string): Promise<AgentProviderId[]> {
  const detected: AgentProviderId[] = [];
  for (const provider of AGENT_PROVIDER_ORDER) {
    const markers = PROVIDER_REPO_MARKER_CANDIDATES[provider];
    for (const markerRel of markers) {
      const marker = join(repoRoot, markerRel);
      try {
        await stat(marker);
        detected.push(provider);
        break;
      } catch {
        // ignore missing marker
      }
    }
  }
  return detected;
}
Detection markers:
  • Codex: .codex/ directory
  • Claude Code: .claude/ directory OR CLAUDE.md OR CLAUDE.local.md
  • Gemini CLI: .gemini/ directory
Example:
# Your repo structure:
project/
├── .codex/
├── .claude/
└── src/

# Codaph detects:
codaph init
# Detected providers: Codex, Claude Code

Provider Configuration

During Init

codaph init
# Interactive multi-select:
# ✓ Codex (.codex)
# ✓ Claude Code (.claude)
# ✓ Gemini CLI (.gemini)

Explicit Selection

# Single provider
codaph init --providers codex

# Multiple providers
codaph init --providers codex,claude-code,gemini-cli

# All providers
codaph init --providers all

# Auto-detect (default)
codaph init --providers auto

Check Current Providers

codaph status
# Output:
# Agents: Codex, Claude Code
# Agent hooks: Codex

Codex Integration

From src/lib/adapter-codex-sdk.ts and src/lib/adapter-codex-exec.ts:

Live Capture (SDK)

import { CodexSdkAdapter } from "./lib/adapter-codex-sdk";
import { IngestPipeline } from "./lib/ingest-pipeline";

const pipeline = new IngestPipeline(mirror, { memoryEngine });
const adapter = new CodexSdkAdapter(pipeline);

const result = await adapter.runAndCapture({
  prompt: "add tests for auth",
  cwd: "/path/to/project",
  repoId: "owner/repo",
  model: "gpt-4.1",
});
Captured events:
  • prompt.submitted
  • tool_call (with name, arguments)
  • tool_result (with output)
  • Session metadata

Exec Wrapper

# Capture from codex exec JSON output
codaph run "add auth tests"
codaph exec "refactor api"

History Import

From src/codex-history-sync.ts:
# One-time backfill of Codex history
codaph push --providers codex

# Or during init
codaph init
# Providers: Codex (auto-detected)
# Run `codaph push` to backfill history
Scanned locations:
  • ~/.codex/db.sqlite3 (Codex SDK database)
  • .codex/ directories in project worktrees
Imported data:
  • Session metadata
  • Prompt/response pairs
  • Tool calls and results
  • File changes

Claude Code Integration

From src/claude-history-sync.ts:

History Import

codaph push --providers claude-code
Scanned locations:
  • ~/.claude/history/ (Claude Code session history)
  • .claude/ directories in project worktrees
Imported events:
  • Prompts and responses
  • Thinking/reasoning steps
  • File edits and tool calls

Agent Complete Hook

# Install during init
codaph init --agent-hooks claude-code

# Or manually
codaph sync setup
# Select Claude Code when prompted
Hook location: .claude/hooks/agent-complete Hook behavior:
  • Triggers after Claude Code agent run completes
  • Runs codaph hooks run agent-complete --provider claude-code
  • Syncs latest session to local mirror + Mubit
  • Respects sync lock and cooldown

Gemini CLI Integration

From src/gemini-history-sync.ts:

History Import

codaph push --providers gemini-cli
Scanned locations:
  • ~/.gemini/history/ (Gemini CLI session history)
  • .gemini/ directories in project worktrees
Imported events:
  • Prompts and responses
  • Code generation steps
  • File modifications

Agent Complete Hook

codaph init --agent-hooks gemini-cli
Hook location: .gemini/hooks/agent-complete

Multi-Provider Workflows

1
Mixed Team Setup
2
  • Project uses multiple agents:
  • 3
    cd /path/to/project
    ls -la
    # .codex/
    # .claude/
    # .gemini/
    
    4
  • Initialize with all providers:
  • 5
    codaph init --providers all
    # Or let auto-detect find them
    codaph init
    
    6
  • Backfill history from all agents:
  • 7
    codaph push
    # Scans Codex, Claude Code, and Gemini CLI history
    # Imports all sessions to local mirror + Mubit
    
    8
  • Browse mixed sessions:
  • 9
    codaph tui
    # Session list shows "Agent" column:
    # - Codex
    # - Claude
    # - Gemini
    

    Selective Import

    # Import only Codex history
    codaph push --providers codex
    
    # Import Claude Code and Gemini CLI
    codaph push --providers claude-code,gemini-cli
    
    # Status shows per-provider stats
    codaph status
    # Local push providers:
    # Codex e:42 f:10/15
    # Claude Code e:20 f:5/8
    

    Provider-Specific Automation

    # Install hooks for specific providers
    codaph sync setup
    # Select providers interactively
    
    # Or specify explicitly
    codaph init --agent-hooks codex,claude-code
    
    # Check which hooks are installed
    codaph status
    # Agent hooks: Codex, Claude Code
    

    History Sync Architecture

    From src/index.ts:1376-1495 (runSyncPushPhase):
    interface ProviderImportSummary {
      scannedFiles: number;
      matchedFiles: number;
      importedEvents: number;
      importedSessions: number;
      error?: string | null;
    }
    
    type ProviderImportSummaryMap = Partial<Record<AgentProviderId, ProviderImportSummary>>;
    
    Sync flow:
    1. For each selected provider:
      • Scan local history files
      • Match to current project
      • Parse events
      • Import via IngestPipeline
      • Deduplicate by eventId
      • Write to local mirror + Mubit
    2. Aggregate results:
    const summary = {
      scannedFiles: aggregate.reduce((sum, item) => sum + item.scannedFiles, 0),
      matchedFiles: aggregate.reduce((sum, item) => sum + item.matchedFiles, 0),
      importedEvents: aggregate.reduce((sum, item) => sum + item.importedEvents, 0),
      importedSessions: aggregate.reduce((sum, item) => sum + item.importedSessions, 0),
    };
    
    1. Persist state:
    // .codaph/local-push-state.json
    {
      "lastSuccessAt": "2026-03-03T10:00:00.000Z",
      "lastImportedEvents": 62,
      "providers": {
        "codex": {
          "scannedFiles": 15,
          "matchedFiles": 10,
          "importedEvents": 42,
          "importedSessions": 3,
          "lastSuccessAt": "2026-03-03T10:00:00.000Z",
          "lastError": null
        },
        "claude-code": {
          "scannedFiles": 8,
          "matchedFiles": 5,
          "importedEvents": 20,
          "importedSessions": 2,
          "lastSuccessAt": "2026-03-03T10:00:00.000Z",
          "lastError": null
        }
      }
    }
    

    Provider-Specific Event Sources

    From src/lib/core-types.ts:
    export type AgentSource =
      | "codex_sdk"           // Live Codex SDK capture
      | "codex_exec"          // codaph run/exec wrapper
      | "codex_history"       // Codex history import
      | "claude_code_history" // Claude Code history import
      | "gemini_cli_history"  // Gemini CLI history import
      | "unknown";
    
    Usage in events:
    const event: CapturedEventEnvelope = {
      eventId: "evt_abc123",
      source: "codex_sdk",  // or "claude_code_history", etc.
      repoId: "owner/repo",
      actorId: "alice",
      sessionId: "session_xyz",
      ts: "2026-03-03T10:00:00.000Z",
      eventType: "prompt.submitted",
      payload: { /* ... */ },
    };
    
    TUI display (from src/index.ts:4492-4506):
    function providerFromEventSource(source: string | null): AgentProviderId | null {
      if (source?.startsWith("codex_")) return "codex";
      if (source?.startsWith("claude_code_")) return "claude-code";
      if (source?.startsWith("gemini_cli_")) return "gemini-cli";
      return null;
    }
    
    function providerTag(provider: AgentProviderId | null): string {
      if (provider === "codex") return "codex";
      if (provider === "claude-code") return "claude";
      if (provider === "gemini-cli") return "gemini";
      return "unknown";
    }
    

    Best Practices

    Recommended: Let Codaph auto-detect providers during init.
    codaph init
    # Codaph scans for .codex/, .claude/, .gemini/
    # Shows multi-select with detected providers pre-checked
    
    Why?
    • Ensures you don’t miss providers in use
    • Detects new providers added later
    • Works with worktrees and monorepos
    Install hooks only for active agents:
    codaph sync setup
    # Select only providers you actively use
    # Example: Codex + Claude Code, skip Gemini CLI if not used
    
    Why?
    • Reduces unnecessary hook overhead
    • Avoids errors from missing agent binaries
    • Keeps automation focused
    After init, backfill history once:
    codaph init
    codaph push
    # Imports historical sessions from all providers
    
    Then rely on automation:
    codaph sync setup
    # Hooks keep future sessions in sync automatically
    
    Why?
    • codaph push scans full history (slow)
    • Daily codaph sync uses fast Mubit-first pull
    • Hooks capture new sessions immediately
    Check provider-specific sync status:
    codaph status
    # Local push providers:
    # Codex e:42 f:10/15
    # Claude Code e:20 f:5/8
    
    Investigate errors:
    codaph push --providers codex --json
    # Check for provider-specific errors
    

    Troubleshooting

    Issue: codaph init doesn’t find your agent.Fix: Ensure marker directory exists:
    # For Codex
    mkdir .codex
    
    # For Claude Code (any of these work)
    mkdir .claude
    # OR create CLAUDE.md
    touch CLAUDE.md
    
    # For Gemini CLI
    mkdir .gemini
    
    # Re-run init
    codaph init
    
    Issue: codaph push reports 0 matched files.Causes:
    1. History location not scanned
    2. No sessions for current project
    3. Project path mismatch
    Fix:
    # Check history exists
    ls ~/.codex/db.sqlite3      # Codex
    ls ~/.claude/history/       # Claude Code
    ls ~/.gemini/history/       # Gemini CLI
    
    # Try with worktrees
    codaph push --worktrees
    
    # Check project detection
    codaph status | grep repoId
    
    Issue: Hook installed but sync doesn’t run after agent completes.Fix:
    1. Check hook is executable:
    ls -la .codex/hooks/agent-complete.sh
    # Should be -rwxr-xr-x
    
    chmod +x .codex/hooks/agent-complete.sh
    
    1. Test hook manually:
    .codex/hooks/agent-complete.sh
    # Should trigger sync
    
    1. Check hook manager:
    codaph doctor
    # Look for hook manager warnings
    
    1. Reinstall hooks:
    codaph sync setup --force
    
    Issue: Sessions show wrong provider or “unknown”.Cause: Event source mismatch or missing provider info.Fix: Provider is inferred from event source field:
    // Check event source in timeline
    codaph timeline --session <id> --json
    // Look for "source": "codex_sdk" | "claude_code_history" | etc.
    
    If source is generic, provider detection falls back to session metadata.

    Extending Provider Support

    To add a new agent provider:
    1
  • Add provider ID to src/lib/agent-providers.ts:
  • 2
    export type AgentProviderId = "codex" | "claude-code" | "gemini-cli" | "cursor";
    
    const PROVIDER_MARKERS: Record<AgentProviderId, string> = {
      // ...
      cursor: ".cursor",
    };
    
    3
  • Create history sync (e.g., src/cursor-history-sync.ts):
  • 4
    export async function syncCursorHistory(options: {
      projectPath: string;
      pipeline: IngestPipeline;
      repoId: string;
      onProgress?: (progress: CursorHistorySyncProgress) => void;
    }): Promise<CursorHistorySyncSummary> {
      // Scan ~/.cursor/history/ or similar
      // Parse session files
      // Call pipeline.ingest() for each event
      // Return summary
    }
    
    5
  • Integrate in sync push (src/index.ts:runSyncPushPhase):
  • 6
    if (provider === "cursor") {
      providerSummary = await syncCursorHistory({
        projectPath: cwd,
        pipeline,
        repoId,
        actorId,
        onProgress: relayProgress,
      });
    }
    
    7
  • Add agent complete hook (src/sync-automation.ts):
  • 8
    export async function installCursorAgentCompleteHook(
      repoRoot: string,
      commands: string[],
    ) {
      // Write .cursor/hooks/agent-complete
      // Make executable
      // Return { ok: boolean, warning?: string }
    }
    

    Build docs developers (and LLMs) love