This document defines the architectural conventions, coding standards, loader requirements, build quirks, and historical lessons for Thermal (github.com/jadmadi/thermal). Any AI coding assistant (Claude Code, Gemini CLI, Devin, OpenCode, Cursor, etc.) working on this repository MUST read and adhere to these standards.
Thermal (thermal) is a high-performance, zero-allocation terminal contribution heatmap and leaderboard tool for AI coding assistants. It scans local databases and session transcripts across multiple AI tools, calculates activity and token metrics, and renders beautiful terminal heatmaps.
thermal/
├── cmd/thermal/ # Main CLI entrypoint (main.go, upgrade.go for self-update mechanism)
├── internal/loaders/ # Tool-specific ingestion engines and incremental delta-cache
├── internal/thermal/ # Heatmap algorithms, streak calculation, and time parsing
├── internal/render/ # Terminal formatting, color palettes, dashboard & leaderboard UI
├── internal/version/ # SemVer constants and ldflag injection targets
├── build.sh # Local multi-target build and UPX compression script
└── .goreleaser.yml # Automated GitHub Actions release configuration
- Never Modify User Data: All SQLite connections MUST open in strictly read-only mode using URI parameters:
db, err := sql.Open("sqlite", "file:"+dbPath+"?mode=ro")
- Memory-Mapped I/O: Immediately after opening any SQLite database, execute
PRAGMA mmap_sizeto enable memory mapping for instant scanning of multi-gigabyte files:db.Exec("PRAGMA mmap_size=268435456") // 256MB mmap window
- Robust Time Parsing (
parseSessionTime): AI tool timestamps vary wildly across tools and versions (RFC3339 strings, Unix seconds, milliseconds, microseconds, SQLite strings). Always use robust multi-format parsing ortime_test.goutilities. Never assume a single fixed format. - Incremental Delta Caching (
cache.go): For large databases (Devin,OpenCode,MiMoCode,Codex) and multi-file scanners (command-code,Agy), use theLoadOrScanWithCachemechanism (~/.cache/thermal/<tool>.json). Store exact file modification times (mod_time), sizes (size), or max seen transaction IDs so subsequent invocations take under10ms. - Concurrency Safety: Multi-file, directory, and JSONL log scanners (
command-code,Codexrollout logs,Agyoverview logs) must use bounded worker pools (sync.WaitGroupor semaphore channels capped at ~16 workers) with thread-safe aggregation (sync.Mutex). Never spawn unbounded goroutines over thousands of files.
- Devin (
devin.go): Queries the SQLite DB joiningmessage_nodesagainstsessions. Always checkmetadata.metricsfor true input/output/cache token counts (input_tokens,output_tokens,cache_creation_tokens,cache_read_tokens). Checkprompt_history(updated_atfallback tocreated_at) for accurate streak calculations across sessions without messages. - OpenCode / MiMoCode (
opencode.go,mimocode.go): Read pre-aggregatedsession_summaryorsessionstables containing token counts, costs, and diff lines. - Codex (
codex.go): Readsstate_5.sqlite(threads.tokens_used, reasoning effort, source/model breakdown) as the primary source. Supplements with rollout JSONL logs (~/.codex/sessions/**/*.jsonl) for granular token breakdowns when available. - codewhale (
codewhale.go): Reads~/.codewhale/sessions/*.jsonfiles formetadata.total_tokensandmetadata.cost.session_cost_usd. - command-code (
commandcode.go): Scans~/.commandcode/sessions/*/transcript.jsonlfor message activity and.meta.jsonsidecars for model distributions. - Agy (
agy.go): Scans~/.gemini/antigravity-cli/brain/*/overview.txt(Step <id>:step count activity) and.system_generated/logs/transcript.jsonl(MODELstep entries) for model distribution.
- Leaderboard Categories:
- Token Warriors: Tools reporting token usage (
Devin,OpenCode,MiMoCode,Codex,codewhale). - Activity Hunters: Tools reporting actions/messages/steps instead of tokens (
command-code,Agy).
- Token Warriors: Tools reporting token usage (
- Compact Number Formatting: Always format large numbers concisely via
CompactNumber():57.2B tok,44.0M tok,22.7K tok,304 step. - Color & Verbosity Flags:
--no-color: Strips all ANSI escape sequences. Always checkcolorEnabledbefore emitting color codes.--verbose: Outputs non-fatal loader diagnostic warnings (database locked,missing directory) exclusively toos.Stderr. Never polluteos.Stdoutor JSON output (--json) with warnings.
- IMPORTANT: Never put bare binary names like
thermaldirectly in.gitignore. Becausecmd/thermal/andinternal/thermal/share the namethermal, a barethermalrule will silently ignore source files inside those directories (e.g., cmd/thermal/upgrade.go)! - Rule: Always use root-anchored paths for build artifacts:
/thermal,/dist/,*.exe.
- Template Variables: GoReleaser v2 strictly enforces
{{ .ShortCommit }}. Never use.Short_commit(causes fatal build errors). - UPX Compression:
upx: - enabled: true compress: "9" goos: - linux - windows
- Rule: Enable UPX compression ONLY for
linuxandwindowsbinaries. - Do NOT compress
darwin(macOS) binaries with UPX: Strippeddarwin/amd64anddarwin/arm64binaries must remain uncompressed so macOS Gatekeeper, code signing, and binary format verifiers do not reject the executable.
- Rule: Enable UPX compression ONLY for
- Conventional Commits: All commit messages MUST follow Conventional Commits (
feat:,fix:,docs:,chore:,refactor:,test:).release-please(googleapis/release-please-action@v5) parses commits since the last release tag (vx.y.z) to calculate SemVer bumps and automatically generate PRs updatingCHANGELOG.mdand.release-please-manifest.json.
- Release Execution Flow:
- When the automated
release-pleasePR (chore(master): release x.y.z) is merged intomain,release-pleasetags the commit (vx.y.z) and creates the GitHub Release entry. - The
.github/workflows/release.ymlworkflow triggers onpush: tags: ["v*"]. It installsupx(sudo apt-get install -y upx) and runs GoReleaser (goreleaser/goreleaser-action@v6) to cross-compile across 5 OS/Arch targets, apply UPX compression to Linux/Windows binaries, and upload.tar.gz/.zipassets directly to the GitHub Release.
- When the automated
Before committing or submitting a pull request, run the verification suite:
# 1. Run all unit tests with race detection and verbose output
go test -v ./...
# 2. Verify compilation of the CLI binary
go build -o /tmp/thermal-test ./cmd/thermal
# 3. Test alias resolution and heatmap output locally
/tmp/thermal-test --tool auto
/tmp/thermal-test --tool devin
/tmp/thermal-test --tool agy --verboseWhen modifying loaders, ensure unit tests inside internal/loaders/*_test.go cover edge cases (missing databases, corrupted files, zero-token sessions, timestamp variations, and schema differences) using mock temporary files or SQLite in-memory databases.