This file provides guidance to AI agents when working with code in this repository.
uv sync # install dependencies
uv run convx --help # run CLI
uv run pytest # run all tests
uv run pytest tests/test_integration_sync.py::test_name # run a single testconvx (convx-ai) exports AI session files into a Git repository as Markdown transcripts + hidden JSON blobs.
Data flow:
- Adapter (
src/convx_ai/adapters/) — discovers and parses source files intoNormalizedSession/NormalizedMessagemodels. Adapters: Codex (~/.codex/sessions), Claude (~/.claude/projects), Cursor (workspaceStorage). - Engine (
engine.py) —sync_sessions()orchestrates idempotency: loads.convx/index.json, fingerprints source files (SHA-256), skips unchanged sessions, calls the adapter to parse changed ones, then writes artifacts and updates the index. - Render (
render.py) — convertsNormalizedSessionto Markdown transcript or JSON string. - CLI (
cli.py) — two main commands built with Typer:sync: runs inside a project repo, filters sessions by current foldercwdrecursively by default (--no-recursivedisables subfolders), writes flat under.ai/history/<user>/<source>/backup: writes to a dedicated repo with full path nestinghistory/<user>/<source>/<system>/<relative-cwd>/
Idempotency index: .convx/index.json in the output repo. Keyed by session_key (<source_system>:<session_id>). A session is re-exported only when the source SHA-256 changes or output files are missing.
Adding a new source system adapter: implement discover_files(input_path), peek_session(source_path, source_system), and parse_session(...) → NormalizedSession, then register in adapters/__init__.py.
NormalizedMessage.kind distinguishes rendering roles: "user" | "assistant" | "system" | "tool". In the Codex adapter, role="user" messages are classified as kind="system" when the text wasn't typed by the human (injected context vs. actual user input).
Feature: Replace entire lines containing sensitive keywords with [SANITIZED] to exclude work/client content from exported history.
Configuration: Create .convx/config.toml in the output repo:
[sanitize]
# Lines containing any of these terms will be replaced with [SANITIZED]
keywords = ["work", "sensitive topic ", "some rant"]How it works:
- Loaded automatically on every
syncorbackuprun - Applied after secret redaction (API keys, tokens, passwords)
- Matching is case-insensitive (e.g.,
"PONY"matchespony,Pony) - Each entire line is replaced; no partial leaks
- File can be committed to share team defaults, or ignored for per-user setup
Integration: sanitize.py exports:
load_sanitize_keywords(repo_path: Path) -> list[str]— reads.convx/config.toml([sanitize].keywords)sanitize_lines(text: str, keywords: list[str]) -> str— replaces matching lines
Called in engine.py after every redact_secrets() call (markdown, JSON, child sessions).
Feature: Re-export all sessions, ignoring cached fingerprints, to reprocess previously exported data with new redaction/sanitization rules.
Usage:
convx sync --overwrite # Sync with force-overwrite
convx backup --output-path ~/my-history --overwriteHow it works:
- Bypasses fingerprint check in
engine.py:140 - All sessions are re-exported and re-sanitized
- Useful after adding new keywords to
.convx/config.tomlto clean old exports - Index is still updated normally; subsequent runs without
--overwriteskip unchanged sessions