# Agent Guidelines for `copilot-powerline`
Welcome! This document provides architecture overviews, design constraints, and development guidelines for AI coding agents and contributors collaborating on `copilot-powerline`.
---
## 1. Project Overview
`copilot-powerline` is a fast, modular, and customizable status line tool for GitHub Copilot CLI written in Rust.
### Data Flow
1. **Stdin Input**: GitHub Copilot CLI passes a JSON payload via standard input on statusline refreshes (containing `context_window`, `ai_used`, `session_id`, `model`, etc.). Copilot CLI spawns the command in the session's current working directory (following `/cwd` and session switches), so PR/git lookups use the process's own cwd. The payload's `cwd` and `workspace.current_dir` (1.0.90+) carry the same value and are not parsed.
2. **Configuration**: The tool loads `~/.copilot/powerline.toml` (or a path provided via `--config`), falling back to built-in defaults.
3. **Database Query**: Queries the local SQLite database (`~/.copilot/session-store.db`) to calculate month-to-date spend across previous sessions. The query scans the whole usage table, so it only runs when `month_cost` is visible, and its result is cached per session for 60 s (`src/month_spend.rs`).
4. **PR Lookup (optional)**: If the `pr` segment is enabled, reads a disk-cached PR reference for the current branch; a stale or missing cache triggers a throttled, detached background refresh via `gh pr view`, plus `gh api graphql` for the unresolved review threads (see `--fetch-pr-cache` below) so the hot path never blocks on a network call. The `cycle_cost` segment does the same with `gh api /copilot_internal/user` (`--fetch-cycle-usage`, `src/cycle_usage.rs`).
5. **Cross-refresh State**: Every refresh is a fresh process, so anything compared across refreshes lives in small private files under the user cache dir (`src/state.rs`): the `--toggle` display mode override, per-session snapshots used to flag spend spikes, the cache hit trend and idle time (for the long-break reminder), and the cached month-to-date total of other sessions. Note that the payload's `current_usage` mirrors session totals, so per-step values must be derived from these snapshots.
6. **Payload Log (optional)**: When `COPILOT_POWERLINE_LOG` names a file, appends one line per refresh (time, hashed session id, totals, idle time) to diagnose stale displays (`src/payload_log.rs`). Never log paths or prompt content.
7. **Segment Assembly**: Picks `segments` or `compact_segments` based on the display mode, then formats each through the segment dispatch `match` in [src/main.rs](src/main.rs).
8. **Rendering**: The `renderer` applies the configured style (`minimal`, `powerline`, `capsule`, `plain`) and theme ANSI colors.
9. **Stdout Output**: Emits the single-line formatted status line to standard output.
---
## 2. Non-Obvious Locations
List the tree for the layout; one file per segment lives in `src/segments/`. Notes it does not show:
- `src/state.rs`: all cross-refresh state goes through it (private cache dir, atomic writes). `main` calls `prune_if_due` once per refresh to delete per-session files untouched for 30 days (about one scan a day, gated by a `last_prune` marker). A new kind of state file must be added to `PRUNABLE_KINDS`, or it is never pruned.
- `src/git.rs`: reads `.git/HEAD` directly, so the hot path never spawns `git`.
- `src/segments/spend_format.rs`: the nano AIU to AIC to USD conversion used by every cost segment, and the `alert_above_usd` check shared by `session_cost`, `month_cost` and `cycle_cost`, compared at the shown precision. `src/spend.rs` keeps its own `NANO_AIU_PER_USD` (a top-level module must not depend on a segment); a test in `spend_format.rs` pins it to the same rate. `src/segments/spend_total.rs` renders the `month_cost` and `cycle_cost` totals.
- `src/cycle_usage.rs` and the jq filter in `scripts/fetch-github-copilot-usage` both read `/copilot_internal/user` with the same rules (budget vs unlimited, cycle from `quota_reset_date`); change them together. Its cache is one global file, not per session, so it is not in `PRUNABLE_KINDS`.
- `.github/workflows/release.yml` is generated by dist from `dist-workspace.toml`; do not edit it by hand. `publish-crates.yml` is a hand-written dist custom publish job.
- In `ci.yml`, a pull request that only changes Markdown skips the Rust jobs (`scripts/docs-only-change` decides). `CI ok` is the only required check on `main`: a new required job must go in its `needs`.
- Each `scripts/<name>[.sh]` has a `tests/test-<name>.sh`, run by a step in the CI ShellCheck job; a new script needs both.
- `CODING_STANDARDS.md`: the judgement calls a reviewer applies to a diff.
- `tests/*_wiring.rs` run the built binary against a throwaway `HOME` (shared fixture in `tests/common/mod.rs`). Use them for what `main` wires up, which unit tests cannot see.
---
## 3. Non-Negotiable Engineering Rules
1. **Sub-15ms Execution**:
- This tool runs inside an interactive terminal status line loop.
- Do not introduce heavy dependencies, async runtimes (e.g. Tokio), or network requests during status line execution.
- The `pr` segment is the one feature that needs network access (`gh pr view`). It never runs that call inline: it only reads a disk-backed cache and, when the cache is stale, spawns a detached, throttled background process (this same binary re-invoked with `--fetch-pr-cache`) to refresh it. Follow the same pattern for any future segment that needs an external command or network call.
2. **Bundled SQLite**: [tests/repo_rules.rs](tests/repo_rules.rs) enforces the self-contained SQLite dependency.
3. **Defensive Stdin Parsing**:
- Copilot CLI payloads may change over time or be empty. Never assume a field is always present.
- All input deserialization in `src/input.rs` must use `Option<T>` or `#[serde(default)]` and fall back gracefully without panicking.
4. **Safe, Read-Only Database Access**:
- When querying `~/.copilot/session-store.db`, always open the connection with `OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_URI`.
- Never write to or lock the Copilot database.
- Always set a short timeout (`busy_timeout(500ms)`) to prevent blocking the status line if Copilot is writing to SQLite.
5. **No Machine-Specific Paths**: [scripts/check-machine-paths](scripts/check-machine-paths) enforces portable paths in tracked files.
---
## 4. How to Add a New Segment
When adding a new segment (e.g., `git`, `model`, `duration`):
1. **Create Segment Logic**:
- Add `src/segments/<segment_name>.rs` with:
- Pure formatting/calculation functions.
- A `render_<segment_name>_segment(...) -> Option<String>` function.
- Comprehensive unit tests covering edge cases.
2. **Update Module Exports**:
- Register the module in `src/segments/mod.rs`.
3. **Add Segment Configuration**:
- In `src/config.rs`, add the segment's configuration struct and include it in `Config`.
- Add sensible defaults so existing user configs continue working without schema errors.
4. **Wire into Renderer**:
- In `src/main.rs`, match the segment name in the rendering loop and append the formatted string to `rendered_segments`.
---
## 5. Development & Verification Workflow
Always follow test-first development for behavioural changes.
Run the checks defined in [the CI workflow](.github/workflows/ci.yml), the source of truth for formatting, linting (including compiler warnings), tests, packaging, and release builds.
Also run these local smoke checks, which CI does not cover:
```bash
# Test with a sample Copilot CLI stdin payload
echo '{"context_window":{"current_context_tokens":0,"displayed_context_limit":200000,"current_context_used_percentage":0},"ai_used":{"total_nano_aiu":0}}' | cargo run --
# Test style and theme overrides
echo '{"context_window":{"current_context_tokens":120000,"displayed_context_limit":200000,"current_context_used_percentage":60},"ai_used":{"total_nano_aiu":500000000000}}' | cargo run -- --style capsule --theme nord
```
Before pushing a PR branch, run `scripts/mutants-diff`; kill every MISSED mutant with a test, or exclude it in [.cargo/mutants.toml](.cargo/mutants.toml) with the reason.
On pull requests, CI's `Mutants` job runs the same script. It is advisory, not a required check: survivors are listed in the job summary and the `mutants.out` artifact.
---
## 6. Commits and Releases
- Commits follow [Conventional Commits](https://www.conventionalcommits.org/). Besides `feat`, `fix`, `docs`, `test` and `refactor`, this repo uses `perf`, `style`, `build`, `ci` and `chore`.
- Releases: run `scripts/release.sh` (see `RELEASING.md`). Never run `cargo publish` by hand: CI publishes to crates.io after the GitHub Release exists, and a crates.io version cannot be deleted or re-uploaded.
---
## Agent skills
### Issue tracker
Issues live in GitHub Issues on `xpepper/copilot-powerline`, managed with the `gh` CLI. See `docs/agents/issue-tracker.md`.
### Triage labels
The five default triage roles, each label named after its role (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`). See `docs/agents/triage-labels.md`.
### Domain docs
Single-context: one `GLOSSARY.md` and `docs/adr/` at the repo root, created lazily. See `docs/agents/domain.md`.