Skip to content

Developer guide

This page covers development workflows, the source layout, and testing without network access.

Development

uv sync
uv run python -m pytest -q                              # tests
uv run ruff check --config config/ruff.toml src tests  # lint

Source layout

All product code is under src/cheaphelp/_internal/:

Module Responsibility
config.py workspace layout + config.json
env.py .env secrets (parse/write, chmod 600)
github.py minimal GitHub REST client (httpx)
registry.py registered-repo store
gitutil.py shallow clones, commit, push, diff helpers
lock.py per-repo file locks so concurrent ticks don't collide
spend.py daily USD spend tracker for the budget guardrail
conventions.py reads CHEAPHELP.md/AGENTS.md/CONTRIBUTING.md into agent context
pr_state.py persists PR ↔ issue link state for the rework stage
opencode.py generate opencode.json, run agents headlessly (with --variant), parse decisions
templates/ bundled agent prompts (responder, planner, worker, reviewer, rework, fixer)
tasks.py task manifest + per-issue task state store
responder.py / planner.py / worker.py / reviewer.py / rework.py / fixer.py per-role turn logic
orchestrator.py one tick of the state machine (tick(), classify(), stage dispatch)
cleanup.py prune build clones for closed issues / unregistered repos
systemd.py user service + timer install (continuous-mode by default)
commands.py / cli.py CLI (init, repo, run, systemd, agents, config, doctor, status, clean, retry, logs)

Tests live in tests/, one file per src/cheaphelp/_internal/ module (plus test_cli_*.py per CLI subcommand and test_api.py); shared fixtures in tests/conftest.py.

Testing without tokens or opencode

Set CHEAPHELP_AGENT_MOCK=/path/to/decision.json and the agent layer returns that file's contents instead of invoking opencode — handy for exercising the orchestrator offline.