|
| 1 | +# AGENTS.md — Thermal Coding Standards & Architecture Guide |
| 2 | + |
| 3 | +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. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 1. Project Purpose & Architecture |
| 8 | + |
| 9 | +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. |
| 10 | + |
| 11 | +### Directory Structure |
| 12 | + |
| 13 | +``` |
| 14 | +thermal/ |
| 15 | +├── cmd/thermal/ # Main CLI entrypoint (main.go, upgrade.go for self-update mechanism) |
| 16 | +├── internal/loaders/ # Tool-specific ingestion engines and incremental delta-cache |
| 17 | +├── internal/thermal/ # Heatmap algorithms, streak calculation, and time parsing |
| 18 | +├── internal/render/ # Terminal formatting, color palettes, dashboard & leaderboard UI |
| 19 | +├── internal/version/ # SemVer constants and ldflag injection targets |
| 20 | +├── build.sh # Local multi-target build and UPX compression script |
| 21 | +└── .goreleaser.yml # Automated GitHub Actions release configuration |
| 22 | +``` |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## 2. Core Coding & Architectural Standards |
| 27 | + |
| 28 | +### A. Read-Only Data Ingestion (`internal/loaders/`) |
| 29 | +1. **Never Modify User Data**: All SQLite connections MUST open in strictly read-only mode using URI parameters: |
| 30 | + ```go |
| 31 | + db, err := sql.Open("sqlite", "file:"+dbPath+"?mode=ro") |
| 32 | + ``` |
| 33 | +2. **Memory-Mapped I/O**: Immediately after opening any SQLite database, execute `PRAGMA mmap_size` to enable memory mapping for instant scanning of multi-gigabyte files: |
| 34 | + ```go |
| 35 | + db.Exec("PRAGMA mmap_size=268435456") // 256MB mmap window |
| 36 | + ``` |
| 37 | +3. **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 or `time_test.go` utilities. Never assume a single fixed format. |
| 38 | +4. **Incremental Delta Caching (`cache.go`)**: For large databases (`Devin`, `OpenCode`, `MiMoCode`, `Codex`) and multi-file scanners (`command-code`, `Agy`), use the `LoadOrScanWithCache` mechanism (`~/.cache/thermal/<tool>.json`). Store exact file modification times (`mod_time`), sizes (`size`), or max seen transaction IDs so subsequent invocations take under `10ms`. |
| 39 | +5. **Concurrency Safety**: Multi-file, directory, and JSONL log scanners (`command-code`, `Codex` rollout logs, `Agy` overview logs) must use bounded worker pools (`sync.WaitGroup` or semaphore channels capped at ~16 workers) with thread-safe aggregation (`sync.Mutex`). Never spawn unbounded goroutines over thousands of files. |
| 40 | + |
| 41 | +### B. Tool-Specific Loader Quirks |
| 42 | +* **Devin (`devin.go`)**: Queries the SQLite DB joining `message_nodes` against `sessions`. Always check `metadata.metrics` for true input/output/cache token counts (`input_tokens`, `output_tokens`, `cache_creation_tokens`, `cache_read_tokens`). Check `prompt_history` (`updated_at` fallback to `created_at`) for accurate streak calculations across sessions without messages. |
| 43 | +* **OpenCode / MiMoCode (`opencode.go`, `mimocode.go`)**: Read pre-aggregated `session_summary` or `sessions` tables containing token counts, costs, and diff lines. |
| 44 | +* **Codex (`codex.go`)**: Reads `state_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. |
| 45 | +* **codewhale (`codewhale.go`)**: Reads `~/.codewhale/sessions/*.json` files for `metadata.total_tokens` and `metadata.cost.session_cost_usd`. |
| 46 | +* **command-code (`commandcode.go`)**: Scans `~/.commandcode/sessions/*/transcript.jsonl` for message activity and `.meta.json` sidecars for model distributions. |
| 47 | +* **Agy (`agy.go`)**: Scans `~/.gemini/antigravity-cli/brain/*/overview.txt` (`Step <id>:` step count activity) and `.system_generated/logs/transcript.jsonl` (`MODEL` step entries) for model distribution. |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## 3. Terminal Rendering & CLI (`internal/render/`) |
| 52 | + |
| 53 | +1. **Leaderboard Categories**: |
| 54 | + * **Token Warriors**: Tools reporting token usage (`Devin`, `OpenCode`, `MiMoCode`, `Codex`, `codewhale`). |
| 55 | + * **Activity Hunters**: Tools reporting actions/messages/steps instead of tokens (`command-code`, `Agy`). |
| 56 | +2. **Compact Number Formatting**: Always format large numbers concisely via `CompactNumber()`: `57.2B tok`, `44.0M tok`, `22.7K tok`, `304 step`. |
| 57 | +3. **Color & Verbosity Flags**: |
| 58 | + * `--no-color`: Strips all ANSI escape sequences. Always check `colorEnabled` before emitting color codes. |
| 59 | + * `--verbose`: Outputs non-fatal loader diagnostic warnings (`database locked`, `missing directory`) exclusively to `os.Stderr`. Never pollute `os.Stdout` or JSON output (`--json`) with warnings. |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +## 4. Build, Versioning & Release Guidelines |
| 64 | + |
| 65 | +### A. `.gitignore` Path Rules |
| 66 | +* **IMPORTANT**: Never put bare binary names like `thermal` directly in `.gitignore`. Because `cmd/thermal/` and `internal/thermal/` share the name `thermal`, a bare `thermal` rule will silently ignore source files inside those directories (`e.g., cmd/thermal/upgrade.go`)! |
| 67 | +* **Rule**: Always use root-anchored paths for build artifacts: `/thermal`, `/dist/`, `*.exe`. |
| 68 | + |
| 69 | +### B. GoReleaser Configuration (`.goreleaser.yml`) |
| 70 | +1. **Template Variables**: GoReleaser v2 strictly enforces `{{ .ShortCommit }}`. Never use `.Short_commit` (causes fatal build errors). |
| 71 | +2. **UPX Compression**: |
| 72 | + ```yaml |
| 73 | + upx: |
| 74 | + - enabled: true |
| 75 | + compress: "9" |
| 76 | + goos: |
| 77 | + - linux |
| 78 | + - windows |
| 79 | + ``` |
| 80 | + * **Rule**: Enable UPX compression **ONLY** for `linux` and `windows` binaries. |
| 81 | + * **Do NOT compress `darwin` (macOS) binaries with UPX**: Stripped `darwin/amd64` and `darwin/arm64` binaries must remain uncompressed so macOS Gatekeeper, code signing, and binary format verifiers do not reject the executable. |
| 82 | + |
| 83 | +### C. Automated Release Pipeline (`release-please` + `goreleaser`) |
| 84 | +1. **Conventional Commits**: All commit messages MUST follow Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`). |
| 85 | + * `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 updating `CHANGELOG.md` and `.release-please-manifest.json`. |
| 86 | +2. **Release Execution Flow**: |
| 87 | + * When the automated `release-please` PR (`chore(master): release x.y.z`) is merged into `main`, `release-please` tags the commit (`vx.y.z`) and creates the GitHub Release entry. |
| 88 | + * The `.github/workflows/release.yml` workflow triggers on `push: tags: ["v*"]`. It installs `upx` (`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` / `.zip` assets directly to the GitHub Release. |
| 89 | + |
| 90 | +--- |
| 91 | + |
| 92 | +## 5. Testing & Verification Checklist |
| 93 | + |
| 94 | +Before committing or submitting a pull request, run the verification suite: |
| 95 | + |
| 96 | +```bash |
| 97 | +# 1. Run all unit tests with race detection and verbose output |
| 98 | +go test -v ./... |
| 99 | +
|
| 100 | +# 2. Verify compilation of the CLI binary |
| 101 | +go build -o /tmp/thermal-test ./cmd/thermal |
| 102 | +
|
| 103 | +# 3. Test alias resolution and heatmap output locally |
| 104 | +/tmp/thermal-test --tool auto |
| 105 | +/tmp/thermal-test --tool devin |
| 106 | +/tmp/thermal-test --tool agy --verbose |
| 107 | +``` |
| 108 | + |
| 109 | +When 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. |
0 commit comments