|
1 | | -AGENTS.md |
2 | | - |
3 | | -Agent Guidelines for OpenCode Token Meter |
4 | | -======================================= |
5 | | - |
6 | | -Purpose |
7 | | -------- |
8 | | -This document orients agents and contributors to the OpenCode Token Meter repository. |
9 | | -It summarizes build/run/test commands, coding style expectations, database safety rules, |
10 | | -and the exact deduplication behavior implemented in the agent. |
11 | | - |
12 | | -Workdir |
13 | | -------- |
14 | | -Repository root: the working directory where AGENTS.md lives (example): |
15 | | -`/Users/<you>/Desktop/OpenCode Token Meter` |
16 | | - |
17 | | -Quick commands |
18 | | --------------- |
19 | | -- Build (production): |
20 | | - - From repo root: `./build.sh` — builds both agent and menubar with PyInstaller |
21 | | -- Development build (agent): |
22 | | - - `cd App/agent` then: |
23 | | - - `python3 -m PyInstaller --onefile --name opencode-agent agent/__main__.py` |
24 | | -- Development build (menubar): |
25 | | - - `cd App/menubar` then: |
26 | | - - `python3 -m PyInstaller -y --clean opencode-menubar.spec` |
27 | | -- Clean build artifacts: |
28 | | - - `rm -rf build/ App/menubar/dist/ App/menubar/build/` |
29 | | - - `rm -rf App/agent/dist/ App/agent/build/` |
30 | | - - `rm -f opencode-agent.spec` |
31 | | - |
32 | | -Run (development) |
33 | | ------------------ |
34 | | -- Run agent (development): |
35 | | - - `cd App/agent` then `python3 -m agent` |
36 | | -- Run menubar (development): |
37 | | - - `cd App/menubar` then `python3 -m menubar` |
38 | | - |
39 | | -Syntax / quick checks |
40 | | ---------------------- |
41 | | -- Quick syntax check for a file: |
42 | | - - `python3 -m py_compile <path>` e.g. `python3 -m py_compile App/agent/agent/db.py` |
43 | | -- Run a small import test for menubar: |
44 | | - - `cd App/menubar && python3 -c "from menubar.app import OpenCodeTokenMeter, SettingsDialog; print('Import successful')"` |
45 | | - |
46 | | -Database paths and inspection |
47 | | ------------------------------ |
48 | | -- Default DB path used by code: `~/Library/Application Support/OpenCode Token Meter/index.db` |
49 | | -- Socket path: `~/Library/Application Support/OpenCode Token Meter/agent.sock` |
50 | | -- Inspect DB with sqlite3 CLI: |
51 | | - - `sqlite3 "~/Library/Application Support/OpenCode Token Meter/index.db" ".tables"` |
52 | | - - Run queries with: `sqlite3 "<path>" "SELECT ...;"` |
53 | | - |
54 | | -Testing |
55 | | -------- |
56 | | -- Currently there are no unit tests in the repository by default. Recommended patterns: |
57 | | - - Use pytest for unit tests. |
58 | | - - Example single-test run: |
59 | | - - `python -m pytest tests/test_db_dedup.py::test_deduped_counts -q` |
60 | | -- Suggested test files to add: |
61 | | - 1. `tests/test_db_dedup.py` — exercises dedup SQL on a temporary SQLite DB. |
62 | | - 2. `tests/test_settings_dialog.py` — basic tests for SettingsDialog behavior (preset vs custom). |
63 | | - 3. `tests/test_uds_client.py` — socket/client integration checks against a running agent (optional). |
64 | | - |
65 | | -Linting and formatting |
66 | | ----------------------- |
67 | | -- Recommended tools and commands: |
68 | | - - Black: `black .` (88 char line length by default) |
69 | | - - isort: `isort .` (use to keep imports grouped) |
70 | | - - flake8: `flake8 .` (project specific ignores can be added) |
71 | | - - mypy: `mypy .` (optional — gradually adopt for public APIs) |
72 | | - |
73 | | -Code style guidelines (for agents) |
74 | | ---------------------------------- |
75 | | -Follow these rules to keep changes consistent and predictable for automated agents. |
76 | | - |
77 | | -1) Imports |
78 | | - - Group imports: stdlib → third-party → local |
79 | | - - Use absolute imports inside packages (e.g. `from agent.config import DB_PATH`) |
80 | | - - Keep import statements at top of file except for guarded imports (platform/bundled checks) |
81 | | - |
82 | | -2) Formatting |
83 | | - - Use Black formatting (88 char width). Keep simple, machine-enforced style. |
84 | | - - Use isort for import ordering. |
85 | | - |
86 | | -3) Naming |
87 | | - - Files/modules: `snake_case.py` |
88 | | - - Classes: `PascalCase` |
89 | | - - Functions/Methods: `snake_case()` |
90 | | - - Constants: `UPPER_SNAKE_CASE` |
91 | | - |
92 | | -4) Type hints |
93 | | - - Prefer lightweight type hints on public APIs. Be pragmatic — gradual typing is fine. |
94 | | - - Annotate return types for functions that are part of the public package surface. |
95 | | - |
96 | | -5) SQL and DB safety |
97 | | - - Always use parameterized queries for user-supplied values: `?` placeholders in sqlite3. |
98 | | - - Avoid f-strings or string concatenation to build SQL with external inputs. |
99 | | - - Use `get_conn()` helper patterns and close connections (use context managers). |
100 | | - - Enable WAL mode: `conn.execute("PRAGMA journal_mode=WAL;")` near connection initialization. |
101 | | - - Keep long-running transactions off the UI thread (menubar). Do DB work in a worker thread or the agent. |
102 | | - |
103 | | -6) Error handling |
104 | | - - Catch specific exceptions where possible (e.g. `sqlite3.OperationalError`). |
105 | | - - Log errors to stderr: `print(f"Error: {e}", file=sys.stderr)`. |
106 | | - - Fail gracefully in the UI: return empty data structures rather than raising uncaught exceptions. |
107 | | - |
108 | | -7) Tests |
109 | | - - Write unit tests for DB logic (dedup, aggregates) and small integration tests for UDS client/server. |
110 | | - - Keep tests isolated: create temporary files/DBs and remove them after tests. |
111 | | - |
112 | | -8) Git / PR expectations |
113 | | - - Create small focused PRs with a single logical change. |
114 | | - - Commit messages: present-tense, short summary + optional body. Example: `docs: add AGENTS.md with build/lint/test commands` |
115 | | - - Run `python -m py_compile` and `black .` before pushing. |
116 | | - |
117 | | -Deduplication details (exact behavior) |
118 | | -------------------------------------- |
119 | | -Deduplication is implemented entirely at the database query level to avoid double counting messages |
120 | | -that OpenCode sometimes duplicates across sessions. |
121 | | - |
122 | | -- Implementation location: `App/agent/agent/db.py` |
123 | | - - Function: `_get_deduplicated_messages_subquery(where_clause="")` |
124 | | - - Other aggregates updated to use deduped subquery: `aggregate()`, `aggregate_range()`, |
125 | | - `aggregate_by_provider()`, `aggregate_by_model()`, `get_message_count*`, `get_request_count*`. |
126 | | - |
127 | | -- Dedup rule (precise): |
128 | | - - Group messages by the following stable fields: |
129 | | - - `ts, role, input, output, reasoning, cache_read, cache_write, provider_id, model_id` |
130 | | - - For each group keep the row whose `msg_id` is lexicographically smallest (SQL: `HAVING msg_id = MIN(msg_id)`). |
131 | | - - The aggregates and exports operate over this deduplicated derived table (subquery). |
132 | | - |
133 | | -Why this approach |
134 | | ------------------- |
135 | | -- OpenCode can copy the same message into multiple sessions — these duplicates are identical for all token/usage |
136 | | - relevant fields but differ in `msg_id` and sometimes `session_id`. |
137 | | -- Grouping by token-relevant fields and selecting a deterministic canonical `msg_id` prevents double counting |
138 | | - while preserving one representative record for exports. |
139 | | - |
140 | | -Performance & optional DB improvements |
141 | | -------------------------------------- |
142 | | -- Consider adding a composite index to speed the dedup query: |
143 | | - - `CREATE INDEX IF NOT EXISTS idx_dedup ON messages(ts, role, input, output, reasoning, cache_read, cache_write, provider_id, model_id);` |
144 | | -- Optionally create a view for debugging: |
145 | | - - `CREATE VIEW IF NOT EXISTS dedup_messages AS SELECT * FROM (<dedup subquery>);` |
146 | | - |
147 | | -Files of interest (where to look first) |
148 | | --------------------------------------- |
149 | | -- Agent DB + dedup: `App/agent/agent/db.py` |
150 | | -- Agent config: `App/agent/agent/config.py` |
151 | | -- Menubar app & SettingsDialog: `App/menubar/menubar/app.py` |
152 | | -- Menubar settings helpers: `App/menubar/menubar/settings.py` |
153 | | - |
154 | | -Recommended unit tests to add (starter outlines) |
155 | | ----------------------------------------------- |
156 | | -1) tests/test_db_dedup.py |
157 | | - - Create a temporary sqlite DB file and a `messages` table with the same schema. |
158 | | - - Insert multiple duplicate rows (same ts, role, inputs/outputs, provider/model, different msg_id and session_id). |
159 | | - - Run the aggregate function(s) or run the dedup subquery and assert duplicates collapse. |
160 | | - |
161 | | -2) tests/test_settings_dialog.py (UI-level, minimal) |
162 | | - - Instantiate `SettingsDialog` in a headless Qt test harness (or mock UI state). |
163 | | - - Assert selecting a preset model fills and disables provider/model inputs. |
164 | | - - Assert selecting `Custom model` clears and enables provider/model inputs. |
165 | | - |
166 | | -3) tests/test_agent_socket.py (integration) |
167 | | - - Start the agent in subprocess mode (dev binary), connect with `menubar/uds_client.AgentClient`, verify `is_online()`. |
168 | | - |
169 | | -How to run a single test file |
170 | | ------------------------------ |
171 | | -- With pytest installed: |
172 | | - - `python -m pytest tests/test_db_dedup.py -q` |
173 | | - - Or a single test function: `python -m pytest tests/test_db_dedup.py::test_deduped_counts -q` |
174 | | - |
175 | | -Cursor/Copilot hints |
176 | | --------------------- |
177 | | -- Check for workspace-specific AI/cursor rules in these locations (if present, include or adapt): |
178 | | - - `.cursor/`, `.cursorrules`, `.github/copilot-instructions.md` |
179 | | - |
180 | | -Agent checklist for changes |
181 | | ---------------------------- |
182 | | -When making code changes, follow this checklist: |
183 | | -1. Run `python -m py_compile` on modified files. |
184 | | -2. Run `black .` and `isort .`. |
185 | | -3. Add or update unit tests; run them locally with `pytest`. |
186 | | -4. Ensure SQL uses parameterized placeholders for external inputs. |
187 | | -5. Update AGENTS.md if workflows or build commands change. |
188 | | - |
189 | | -Suggested next steps (pick one) |
190 | | -1) Add `AGENTS.md` (done) and run the quick syntax checks: |
191 | | - - `python3 -m py_compile App/agent/agent/db.py` |
192 | | - - `python3 -m py_compile App/menubar/menubar/app.py` |
193 | | -2) Add unit tests for the dedup SQL (suggested path: `tests/test_db_dedup.py`). |
194 | | -3) Add the composite index and/or the `dedup_messages` view to speed debugging. |
195 | | - |
196 | | -Notes & caveats |
197 | | ---------------- |
198 | | -- The dedup approach uses `ts` (integer seconds). If messages have sub-second precision, consider |
199 | | - adjusting the grouping key. |
200 | | -- The canonicalization uses `MIN(msg_id)`. If `msg_id` ordering is non-deterministic in some environments, |
201 | | - consider switching to `MIN(rowid)` or a deterministic tiebreaker. |
202 | | - |
203 | | -Contact |
204 | | -------- |
205 | | -If anything in this document is unclear, open an issue or ask in the repository's primary communication channel. |
206 | | - |
207 | | -End of AGENTS.md |
| 1 | +# AGENTS.md — Developer Guidance |
| 2 | + |
| 3 | +This document codifies how to work with the **OpenCode Token Meter** codebase. It covers development, testing, and build requirements for both macOS and Windows. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 1) Execution & Development Workflow |
| 8 | + |
| 9 | +### Running in Development Mode |
| 10 | + |
| 11 | +To run the application directly from source: |
| 12 | + |
| 13 | +```bash |
| 14 | +# macOS/Windows |
| 15 | +python App/webview_ui/main_tray.py --debug |
| 16 | +``` |
| 17 | + |
| 18 | +*Note: The tray application will automatically start the background agent and stats worker as threads.* |
| 19 | + |
| 20 | +### Environment Requirements |
| 21 | + |
| 22 | +- **Python**: 3.9+ |
| 23 | +- **Conda Environment**: Recommended to use the `opencode` environment. |
| 24 | +- **Dependencies**: |
| 25 | + - `pip install pyinstaller pywebview pystray pillow pyperclip` |
| 26 | + - macOS specific: `pip install rumps pyobjc-framework-Cocoa` |
| 27 | + - Windows specific: `pip install win10toast` |
| 28 | + |
| 29 | +--- |
| 30 | + |
| 31 | +## 2) Component Architecture |
| 32 | + |
| 33 | +- **Agent**: Monitors `~/.local/share/opencode/storage/message/` (all platforms) for new JSON messages. |
| 34 | +- **Stats Worker**: Periodically aggregates database data for the tray display. |
| 35 | +- **Webview UI**: Frontend built with HTML/CSS/JS (Tailwind, Chart.js, Lato font), communicating with Python via a `JsApi` bridge. |
| 36 | +- **Database**: Local SQLite database stored at: |
| 37 | + - macOS: `~/Library/Application Support/OpenCode Token Meter/index.db` |
| 38 | + - Windows: `%APPDATA%\OpenCode Token Meter\index.db` |
| 39 | + |
| 40 | +--- |
| 41 | + |
| 42 | +## 3) Testing & Verification |
| 43 | + |
| 44 | +### Manual GUI Checklist |
| 45 | + |
| 46 | +Before releasing, verify the following: |
| 47 | + |
| 48 | +1. **Startup**: App launches into the system tray/menubar. |
| 49 | +2. **Webview**: Selecting "Show Dashboard" opens the UI window. |
| 50 | +3. **Data**: Counters (In/Out/Cost) update correctly after message activity. |
| 51 | +4. **Theme**: Verify dark mode aesthetics and high-weight (900) typography for headers/charts. |
| 52 | +5. **Export**: Verify CSV/JSON export functionality from the Details page. |
| 53 | + |
| 54 | +### Code Integrity |
| 55 | + |
| 56 | +- **Syntax Check**: `python -m py_compile <path>` |
| 57 | +- **Formatting**: Adhere to PEP 8. Headers and specific UI text use **Lato** with a font-weight of **900** for a premium look. |
| 58 | + |
| 59 | +--- |
| 60 | + |
| 61 | +## 4) Build & Packaging |
| 62 | + |
| 63 | +This repository uses a **unified spec file** (`OpenCodeTokenMeter.spec`) for all platforms. |
| 64 | + |
| 65 | +- **Build Commands**: |
| 66 | + - macOS: `./build.sh` (produces `.app` and `.dmg`) |
| 67 | + - Windows: `.\build_windows.bat` (produces `.exe`) |
| 68 | +- **Cleanup**: `rm -rf build/ dist/` |
| 69 | + |
| 70 | +--- |
| 71 | + |
| 72 | +## 5) Contributor Guidelines |
| 73 | + |
| 74 | +1. **SQL Safety**: Always use parameterized queries (`?`). Never use f-strings for SQL inputs. |
| 75 | +2. **Deduplication**: Message counting must use the deduplication logic in `App/agent/agent/db.py`. |
| 76 | +3. **Version Control**: Follow Semantic Versioning. Update `CHANGELOG.md` for every release. |
0 commit comments