argus-code 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. argus_code-0.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +68 -0
  2. argus_code-0.2.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. argus_code-0.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
  4. argus_code-0.2.0/.github/pull_request_template.md +36 -0
  5. argus_code-0.2.0/.gitignore +26 -0
  6. argus_code-0.2.0/.npmrc +0 -0
  7. argus_code-0.2.0/ARCHITECTURE.md +307 -0
  8. argus_code-0.2.0/CONTRIBUTING.md +156 -0
  9. argus_code-0.2.0/LICENSE +21 -0
  10. argus_code-0.2.0/NOTICE +22 -0
  11. argus_code-0.2.0/PKG-INFO +247 -0
  12. argus_code-0.2.0/PRD.md +380 -0
  13. argus_code-0.2.0/README.md +195 -0
  14. argus_code-0.2.0/SECURITY.md +101 -0
  15. argus_code-0.2.0/TESTING.md +355 -0
  16. argus_code-0.2.0/dashboard/.astro/content-assets.mjs +1 -0
  17. argus_code-0.2.0/dashboard/.astro/content-modules.mjs +1 -0
  18. argus_code-0.2.0/dashboard/.astro/content.d.ts +155 -0
  19. argus_code-0.2.0/dashboard/.astro/types.d.ts +2 -0
  20. argus_code-0.2.0/dashboard/astro.config.mjs +9 -0
  21. argus_code-0.2.0/dashboard/package-lock.json +4787 -0
  22. argus_code-0.2.0/dashboard/package.json +18 -0
  23. argus_code-0.2.0/dashboard/public/styles/global.css +307 -0
  24. argus_code-0.2.0/dashboard/src/layouts/Default.astro +65 -0
  25. argus_code-0.2.0/dashboard/src/pages/index.astro +131 -0
  26. argus_code-0.2.0/dashboard/src/pages/models.astro +131 -0
  27. argus_code-0.2.0/dashboard/src/pages/prompts.astro +407 -0
  28. argus_code-0.2.0/dashboard/src/pages/session.astro +237 -0
  29. argus_code-0.2.0/dashboard/src/pages/sessions/index.astro +87 -0
  30. argus_code-0.2.0/dashboard/src/pages/settings.astro +272 -0
  31. argus_code-0.2.0/dashboard/src/pages/tools.astro +212 -0
  32. argus_code-0.2.0/dashboard/src/pages/trends.astro +55 -0
  33. argus_code-0.2.0/dashboard/src/scripts/alerts.ts +85 -0
  34. argus_code-0.2.0/dashboard/src/scripts/api.ts +152 -0
  35. argus_code-0.2.0/dashboard/src/scripts/charts.ts +200 -0
  36. argus_code-0.2.0/dashboard/src/scripts/format.ts +70 -0
  37. argus_code-0.2.0/dashboard/src/styles/global.css +307 -0
  38. argus_code-0.2.0/dashboard/tsconfig.json +4 -0
  39. argus_code-0.2.0/dashboard-dist/_astro/charts.BIevw6Es.js +1 -0
  40. argus_code-0.2.0/dashboard-dist/_astro/format.DxC1NGYT.js +1 -0
  41. argus_code-0.2.0/dashboard-dist/_astro/index.astro_astro_type_script_index_0_lang.CgwSARdD.js +24 -0
  42. argus_code-0.2.0/dashboard-dist/_astro/index.astro_astro_type_script_index_0_lang.W18SJsr7.js +11 -0
  43. argus_code-0.2.0/dashboard-dist/_astro/installCanvasRenderer.D_tC6TXz.js +18 -0
  44. argus_code-0.2.0/dashboard-dist/_astro/models.astro_astro_type_script_index_0_lang.BHTHXYHC.js +13 -0
  45. argus_code-0.2.0/dashboard-dist/_astro/prompts.astro_astro_type_script_index_0_lang.DfNgiDv9.js +17 -0
  46. argus_code-0.2.0/dashboard-dist/_astro/session.astro_astro_type_script_index_0_lang.Dj_bfrIa.js +86 -0
  47. argus_code-0.2.0/dashboard-dist/_astro/settings.astro_astro_type_script_index_0_lang.d_a-uvdi.js +24 -0
  48. argus_code-0.2.0/dashboard-dist/_astro/tools.astro_astro_type_script_index_0_lang.Dzzau3Yt.js +12 -0
  49. argus_code-0.2.0/dashboard-dist/_astro/trends.astro_astro_type_script_index_0_lang.BLLeGRNa.js +5 -0
  50. argus_code-0.2.0/dashboard-dist/index.html +2 -0
  51. argus_code-0.2.0/dashboard-dist/models/index.html +1 -0
  52. argus_code-0.2.0/dashboard-dist/prompts/index.html +18 -0
  53. argus_code-0.2.0/dashboard-dist/session/index.html +2 -0
  54. argus_code-0.2.0/dashboard-dist/sessions/index.html +1 -0
  55. argus_code-0.2.0/dashboard-dist/settings/index.html +8 -0
  56. argus_code-0.2.0/dashboard-dist/styles/global.css +307 -0
  57. argus_code-0.2.0/dashboard-dist/tools/index.html +1 -0
  58. argus_code-0.2.0/dashboard-dist/trends/index.html +1 -0
  59. argus_code-0.2.0/pricing/2026-05-02.json +24 -0
  60. argus_code-0.2.0/pyproject.toml +68 -0
  61. argus_code-0.2.0/python/argus/__init__.py +3 -0
  62. argus_code-0.2.0/python/argus/adapters/__init__.py +7 -0
  63. argus_code-0.2.0/python/argus/adapters/base.py +108 -0
  64. argus_code-0.2.0/python/argus/adapters/claude_code/__init__.py +5 -0
  65. argus_code-0.2.0/python/argus/adapters/claude_code/adapter.py +63 -0
  66. argus_code-0.2.0/python/argus/adapters/claude_code/discover.py +72 -0
  67. argus_code-0.2.0/python/argus/adapters/claude_code/extract_tool_calls.py +86 -0
  68. argus_code-0.2.0/python/argus/adapters/claude_code/extract_transcript.py +111 -0
  69. argus_code-0.2.0/python/argus/adapters/claude_code/extract_turns.py +69 -0
  70. argus_code-0.2.0/python/argus/adapters/claude_code/history_jsonl.py +138 -0
  71. argus_code-0.2.0/python/argus/adapters/claude_code/ingest_file.py +137 -0
  72. argus_code-0.2.0/python/argus/adapters/claude_code/model.py +11 -0
  73. argus_code-0.2.0/python/argus/adapters/claude_code/schemas.py +77 -0
  74. argus_code-0.2.0/python/argus/adapters/registry.py +30 -0
  75. argus_code-0.2.0/python/argus/cli.py +384 -0
  76. argus_code-0.2.0/python/argus/collector/__init__.py +0 -0
  77. argus_code-0.2.0/python/argus/collector/aggregate.py +102 -0
  78. argus_code-0.2.0/python/argus/collector/first_run.py +189 -0
  79. argus_code-0.2.0/python/argus/collector/pipeline.py +140 -0
  80. argus_code-0.2.0/python/argus/collector/rollup_subagents.py +27 -0
  81. argus_code-0.2.0/python/argus/collector/scheduler.py +89 -0
  82. argus_code-0.2.0/python/argus/collector/search_backfill.py +109 -0
  83. argus_code-0.2.0/python/argus/collector/watcher.py +178 -0
  84. argus_code-0.2.0/python/argus/detectors/__init__.py +6 -0
  85. argus_code-0.2.0/python/argus/detectors/base.py +34 -0
  86. argus_code-0.2.0/python/argus/detectors/registry.py +20 -0
  87. argus_code-0.2.0/python/argus/detectors/tool_error_rate_spike.py +138 -0
  88. argus_code-0.2.0/python/argus/pricing/__init__.py +0 -0
  89. argus_code-0.2.0/python/argus/pricing/compute.py +46 -0
  90. argus_code-0.2.0/python/argus/pricing/load.py +45 -0
  91. argus_code-0.2.0/python/argus/pricing/refresh.py +91 -0
  92. argus_code-0.2.0/python/argus/pricing/types.py +21 -0
  93. argus_code-0.2.0/python/argus/scaffold/__init__.py +0 -0
  94. argus_code-0.2.0/python/argus/scaffold/scaffolder.py +45 -0
  95. argus_code-0.2.0/python/argus/scaffold/snapshot.py +73 -0
  96. argus_code-0.2.0/python/argus/scaffold/storage.py +60 -0
  97. argus_code-0.2.0/python/argus/schema/__init__.py +0 -0
  98. argus_code-0.2.0/python/argus/schema/types.py +157 -0
  99. argus_code-0.2.0/python/argus/server/__init__.py +0 -0
  100. argus_code-0.2.0/python/argus/server/api.py +661 -0
  101. argus_code-0.2.0/python/argus/server/app.py +97 -0
  102. argus_code-0.2.0/python/argus/store/__init__.py +0 -0
  103. argus_code-0.2.0/python/argus/store/db.py +103 -0
  104. argus_code-0.2.0/python/argus/store/migrations/__init__.py +0 -0
  105. argus_code-0.2.0/python/argus/store/migrations/inline.py +180 -0
  106. argus_code-0.2.0/python/argus/store/repository.py +778 -0
  107. argus_code-0.2.0/templates/default/.claude/agents/code-reviewer.md +27 -0
  108. argus_code-0.2.0/templates/default/.claude/agents/security-auditor.md +28 -0
  109. argus_code-0.2.0/templates/default/.claude/commands/commit.md +38 -0
  110. argus_code-0.2.0/templates/default/.claude/commands/deploy.md +13 -0
  111. argus_code-0.2.0/templates/default/.claude/commands/fix-issue.md +15 -0
  112. argus_code-0.2.0/templates/default/.claude/commands/pr.md +38 -0
  113. argus_code-0.2.0/templates/default/.claude/commands/review.md +14 -0
  114. argus_code-0.2.0/templates/default/.claude/rules/api-conventions.md +27 -0
  115. argus_code-0.2.0/templates/default/.claude/rules/code-style.md +25 -0
  116. argus_code-0.2.0/templates/default/.claude/rules/testing.md +19 -0
  117. argus_code-0.2.0/templates/default/.claude/settings.json +28 -0
  118. argus_code-0.2.0/templates/default/.claude/skills/example/SKILL.md +11 -0
  119. argus_code-0.2.0/templates/default/CLAUDE.md +57 -0
  120. argus_code-0.2.0/tests/__init__.py +0 -0
  121. argus_code-0.2.0/tests/adapters/__init__.py +0 -0
  122. argus_code-0.2.0/tests/adapters/claude_code/__init__.py +0 -0
  123. argus_code-0.2.0/tests/adapters/claude_code/test_adapter.py +10 -0
  124. argus_code-0.2.0/tests/adapters/claude_code/test_discover.py +31 -0
  125. argus_code-0.2.0/tests/adapters/claude_code/test_extract_tool_calls.py +103 -0
  126. argus_code-0.2.0/tests/adapters/claude_code/test_extract_transcript.py +152 -0
  127. argus_code-0.2.0/tests/adapters/claude_code/test_extract_turns.py +54 -0
  128. argus_code-0.2.0/tests/adapters/claude_code/test_history_jsonl.py +156 -0
  129. argus_code-0.2.0/tests/adapters/claude_code/test_ingest_file.py +88 -0
  130. argus_code-0.2.0/tests/adapters/claude_code/test_integration.py +37 -0
  131. argus_code-0.2.0/tests/adapters/claude_code/test_model.py +17 -0
  132. argus_code-0.2.0/tests/adapters/claude_code/test_schemas.py +102 -0
  133. argus_code-0.2.0/tests/adapters/test_registry.py +79 -0
  134. argus_code-0.2.0/tests/collector/__init__.py +0 -0
  135. argus_code-0.2.0/tests/collector/test_aggregate.py +79 -0
  136. argus_code-0.2.0/tests/collector/test_first_run.py +77 -0
  137. argus_code-0.2.0/tests/collector/test_pipeline.py +223 -0
  138. argus_code-0.2.0/tests/collector/test_rollup_subagents.py +39 -0
  139. argus_code-0.2.0/tests/collector/test_scheduler.py +141 -0
  140. argus_code-0.2.0/tests/collector/test_watcher.py +67 -0
  141. argus_code-0.2.0/tests/conftest.py +188 -0
  142. argus_code-0.2.0/tests/detectors/__init__.py +0 -0
  143. argus_code-0.2.0/tests/detectors/test_registry.py +54 -0
  144. argus_code-0.2.0/tests/detectors/test_tool_error_rate_spike.py +143 -0
  145. argus_code-0.2.0/tests/pricing/__init__.py +0 -0
  146. argus_code-0.2.0/tests/pricing/test_compute.py +84 -0
  147. argus_code-0.2.0/tests/pricing/test_load.py +16 -0
  148. argus_code-0.2.0/tests/pricing/test_refresh.py +54 -0
  149. argus_code-0.2.0/tests/scaffold/__init__.py +0 -0
  150. argus_code-0.2.0/tests/scaffold/test_bundled_template.py +40 -0
  151. argus_code-0.2.0/tests/scaffold/test_cli_claude.py +112 -0
  152. argus_code-0.2.0/tests/scaffold/test_scaffolder.py +76 -0
  153. argus_code-0.2.0/tests/scaffold/test_snapshot.py +67 -0
  154. argus_code-0.2.0/tests/scaffold/test_storage.py +57 -0
  155. argus_code-0.2.0/tests/schema/__init__.py +0 -0
  156. argus_code-0.2.0/tests/schema/test_types.py +74 -0
  157. argus_code-0.2.0/tests/server/__init__.py +0 -0
  158. argus_code-0.2.0/tests/server/test_api.py +233 -0
  159. argus_code-0.2.0/tests/server/test_api_search.py +313 -0
  160. argus_code-0.2.0/tests/server/test_server.py +125 -0
  161. argus_code-0.2.0/tests/server/test_week_of.py +65 -0
  162. argus_code-0.2.0/tests/store/__init__.py +0 -0
  163. argus_code-0.2.0/tests/store/test_db.py +149 -0
  164. argus_code-0.2.0/tests/store/test_repository.py +521 -0
  165. argus_code-0.2.0/tests/test_e2e.py +77 -0
  166. argus_code-0.2.0/uv.lock +983 -0
@@ -0,0 +1,68 @@
1
+ name: Bug report
2
+ description: Something doesn't work the way it should
3
+ title: "[Bug] "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for taking the time to file a bug. The more specific you can
10
+ be about what you ran and what you saw, the faster this gets fixed.
11
+
12
+ **For security issues**, please use [SECURITY.md](https://github.com/KrishBhimani/argus-code/blob/main/SECURITY.md) instead of a public issue.
13
+
14
+ - type: textarea
15
+ id: description
16
+ attributes:
17
+ label: What happened?
18
+ description: A clear description of the bug.
19
+ validations:
20
+ required: true
21
+
22
+ - type: textarea
23
+ id: expected
24
+ attributes:
25
+ label: What did you expect?
26
+ description: What did you think would happen?
27
+ validations:
28
+ required: true
29
+
30
+ - type: textarea
31
+ id: steps
32
+ attributes:
33
+ label: How to reproduce
34
+ description: |
35
+ Step-by-step. A repro repo, JSONL fixture, or exact command beats a
36
+ prose description. If the bug is about parsing a session, include
37
+ the smallest JSONL line that triggers it (with sensitive content
38
+ scrubbed).
39
+ validations:
40
+ required: true
41
+
42
+ - type: textarea
43
+ id: environment
44
+ attributes:
45
+ label: Environment
46
+ description: |
47
+ Please include:
48
+ - OS (Windows / macOS / Linux + version)
49
+ - Python version (`python --version`)
50
+ - Argus version (`argus --version`)
51
+ - How you installed it (pipx, uv tool, git clone, etc.)
52
+ placeholder: |
53
+ OS: macOS 14.5
54
+ Python: 3.12.4
55
+ Argus: 0.2.0
56
+ Install: pipx install argus-code
57
+ render: markdown
58
+ validations:
59
+ required: true
60
+
61
+ - type: textarea
62
+ id: logs
63
+ attributes:
64
+ label: Logs / output (optional)
65
+ description: Any relevant error messages, stack traces, or terminal output.
66
+ render: shell
67
+ validations:
68
+ required: false
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/KrishBhimani/argus-code/security/advisories/new
5
+ about: Please report security issues privately via GitHub Security Advisories — see SECURITY.md for details.
@@ -0,0 +1,46 @@
1
+ name: Feature request
2
+ description: Suggest a new feature or change in behavior
3
+ title: "[Feature] "
4
+ labels: ["enhancement"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Argus prioritizes "things you cannot get from `ccusage` or other
10
+ existing tools" — feature ideas that lean on the local-only data
11
+ in `~/.claude/` are especially welcome.
12
+
13
+ - type: textarea
14
+ id: problem
15
+ attributes:
16
+ label: What problem are you trying to solve?
17
+ description: Lead with the problem, not the solution. What can't you do today?
18
+ validations:
19
+ required: true
20
+
21
+ - type: textarea
22
+ id: proposal
23
+ attributes:
24
+ label: Proposed solution
25
+ description: |
26
+ How you imagine it working — what would you click on, what would you
27
+ see? Screenshots of mockups, references to other tools, or rough
28
+ sketches all welcome.
29
+ validations:
30
+ required: true
31
+
32
+ - type: textarea
33
+ id: alternatives
34
+ attributes:
35
+ label: Alternatives considered
36
+ description: Any other ways to solve this you ruled out, and why?
37
+ validations:
38
+ required: false
39
+
40
+ - type: textarea
41
+ id: context
42
+ attributes:
43
+ label: Additional context
44
+ description: Related issues, links, screenshots — anything else useful.
45
+ validations:
46
+ required: false
@@ -0,0 +1,36 @@
1
+ ## Summary
2
+
3
+ <!-- What does this PR do, and why? Skip the "what" if the diff makes it
4
+ obvious; the "why" is rarely obvious from the diff alone. -->
5
+
6
+ ## Type of change
7
+
8
+ - [ ] Bug fix (`fix:`)
9
+ - [ ] New feature (`feat:`)
10
+ - [ ] Docs / README / comments (`docs:`)
11
+ - [ ] Refactor / cleanup (`refactor:` / `chore:`)
12
+ - [ ] CI / build (`ci:` / `build:`)
13
+
14
+ ## Related issue
15
+
16
+ <!-- e.g. Closes #42, Fixes #17, or "n/a" for unsolicited changes. -->
17
+
18
+ ## Test plan
19
+
20
+ <!-- What did you actually run to convince yourself this works?
21
+ - New tests added / existing tests updated?
22
+ - Manual smoke test? On which OS?
23
+ - Edge cases checked? -->
24
+
25
+ ## Screenshots (UI changes only)
26
+
27
+ <!-- Delete this section if it's not a UI change. -->
28
+
29
+ ## Checklist
30
+
31
+ - [ ] Tests added or updated for the change
32
+ - [ ] `npm test` and `npm run build` pass locally
33
+ - [ ] Considered cross-platform behavior (especially if touching paths or fs)
34
+ - [ ] Updated `README.md` / `SECURITY.md` if user-facing behavior or the
35
+ security model changed
36
+ - [ ] No `console.log` or debug-only code left in
@@ -0,0 +1,26 @@
1
+ node_modules/
2
+ dist/
3
+ *.log
4
+ .env
5
+ .env.local
6
+ .argus/
7
+ .DS_Store
8
+ .vitest-cache/
9
+ coverage/
10
+
11
+ # Personal planning notes — kept local, not published
12
+ docs/
13
+
14
+ # Python build / virtualenv / cache artifacts
15
+ __pycache__/
16
+ *.py[cod]
17
+ *$py.class
18
+ .venv/
19
+ venv/
20
+ .pytest_cache/
21
+ .ruff_cache/
22
+ .mypy_cache/
23
+ .coverage
24
+ htmlcov/
25
+ *.egg-info/
26
+ build/
File without changes
@@ -0,0 +1,307 @@
1
+ # Argus architecture
2
+
3
+ A guide to how the pieces fit together, aimed at anyone about to change
4
+ the code. Read this before adding a feature so you know where things
5
+ live and why. README.md is the user-facing intro; this is the
6
+ contributor's map.
7
+
8
+ ## The 30-second mental model
9
+
10
+ Claude Code writes a `.jsonl` file every time you use it, one file per
11
+ session, at `~/.claude/projects/<project>/<session-id>.jsonl`. Each
12
+ line is one event: a user message, an assistant reply, a tool call, or
13
+ a tool result.
14
+
15
+ Argus is two things:
16
+
17
+ 1. A **watcher** that reads those files as they grow, validates each
18
+ line, and stores it in a local SQLite database at
19
+ `~/.argus/argus.db`. The database is also an **archive** —
20
+ rows survive even after Claude Code's own cleanup deletes the
21
+ source `.jsonl`.
22
+ 2. A **local web server** that queries SQLite and serves a static
23
+ dashboard at `http://localhost:4242`.
24
+
25
+ No network calls (except optional `argus pricing refresh`). No
26
+ telemetry. No LLM calls. Everything stays on your machine.
27
+
28
+ The backend is Python (≥3.11, FastAPI + uvicorn + pydantic + watchdog
29
+ + stdlib sqlite3 + typer). The dashboard is Astro + ECharts, statically
30
+ built. Both ship together in the `argus-code` wheel on PyPI.
31
+
32
+ ## The data layer
33
+
34
+ SQLite in WAL mode. Two tables you'll touch most often:
35
+
36
+ ### `sessions`
37
+ One row per session. **Pre-summed totals** for fast listing:
38
+
39
+ - `id` (e.g. `claude_code:abc123…`), `agent`, `project_path`
40
+ - `started_at`, `ended_at`, `duration_sec`
41
+ - `total_*_tokens` (fresh_input, output, cache_read, cache_write)
42
+ - `total_cost_usd`, `primary_model`, `turn_count`
43
+
44
+ Use for: the Sessions table, "Top sessions in window" lists, anything
45
+ that needs a whole-session view.
46
+
47
+ ### `turns`
48
+ One row per back-and-forth with Claude — **the source of truth** for
49
+ everything else.
50
+
51
+ - `id`, `session_id`, `sequence`, `timestamp`
52
+ - `model`, `model_raw`
53
+ - Per-turn token counts (fresh_input, output, cache_read,
54
+ cache_write_5m, cache_write_1h)
55
+ - `tool_calls_count`, `cost_usd`, `metadata`
56
+
57
+ Use for: anything that needs **per-day** granularity — Overview
58
+ heatmap, Trends line chart, "Last N days" totals.
59
+
60
+ > ⚠️ **Critical rule for windowed views.** Query `turns` by their
61
+ > `timestamp`, not `sessions` by `started_at`. A session that started
62
+ > 3 weeks ago but had turns this week must still count toward "last 7
63
+ > days". The canonical helper is
64
+ > `Repository.aggregateTurnsByDay(cutoffIso)`.
65
+
66
+ ### Other tables
67
+ - `tool_calls` — one row per tool invocation; powers the Tools page
68
+ - `prompts`, `transcript_segments` (+ FTS5 siblings) — opt-in full-text
69
+ search indexes
70
+ - `file_offsets` — per-file ingest cursor so we re-read only new bytes
71
+ - `parse_errors` — surfaced on Settings → Parse errors
72
+ - `alerts` — detector findings shown on the Overview "What needs
73
+ attention" card; written **only** by the alert scheduler (see below)
74
+ - `app_meta` — schema version, search-indexing flag
75
+
76
+ ## The write path (ingest)
77
+
78
+ ```
79
+ ~/.claude/projects/<proj>/<session-id>.jsonl
80
+
81
+ │ watchdog Observer emits add/change events
82
+
83
+ python/argus/adapters/claude_code/ parse one line at a time
84
+
85
+ │ pydantic models validate each line
86
+
87
+ python/argus/collector/pipeline.py convert to sessions/turns/tool_calls
88
+
89
+
90
+ python/argus/store/repository.py upsert into SQLite (idempotent — re-ingest is safe)
91
+
92
+
93
+ ~/.argus/argus.db
94
+ ```
95
+
96
+ Two ingest paths share the same `ingest_file` function:
97
+
98
+ 1. **First run** (`python/argus/collector/first_run.py`) — at startup,
99
+ walks every file. Recent files first (foreground), older files in a
100
+ background thread. Dashboard becomes useful within a few seconds.
101
+ 2. **Watcher** (`python/argus/collector/watcher.py`) — keeps running
102
+ after first-run, picks up new lines as Claude Code writes them. A
103
+ per-path 100ms debouncer coalesces fs-event bursts before handing
104
+ the path to a single ingest worker thread (SQLite WAL = one writer
105
+ at a time).
106
+
107
+ ## The read path (server + dashboard)
108
+
109
+ ```
110
+ browser → http://localhost:4242
111
+
112
+
113
+ python/argus/server/app.py FastAPI app + uvicorn, binds 127.0.0.1 by default
114
+
115
+
116
+ python/argus/server/api.py /api/overview, /api/sessions, /api/tools, …
117
+
118
+
119
+ python/argus/store/repository.py parameterized SQL queries
120
+
121
+
122
+ dashboard-dist/ static HTML+JS bundled from dashboard/src/ (Astro + ECharts)
123
+ ```
124
+
125
+ The dashboard is **statically built**. No server-side rendering, no
126
+ Node in the browser. Pages just `fetch('/api/...')` and render with
127
+ ECharts.
128
+
129
+ ## Detection (alerts)
130
+
131
+ Beyond passively charting history, Argus runs **detectors** that flag
132
+ behavior worth your attention. The flow reuses ingest's separation of
133
+ concerns — readers read, one writer writes:
134
+
135
+ ```
136
+ python/argus/detectors/registry.py @register + available_detectors()
137
+ │ each detector is pure: detect(repo, now_iso) reads, returns Findings
138
+
139
+ python/argus/collector/scheduler.py the ONLY writer of alerts; runs every
140
+ │ detector on a fixed cadence in a thread
141
+
142
+ ~/.argus/argus.db → alerts table upsert by (detector, dedup_key);
143
+ │ idempotent, with a resolved_at lifecycle
144
+
145
+ python/argus/server/api.py GET /api/alerts, GET /api/alerts/unseen,
146
+ │ POST /api/alerts/{id}/seen
147
+
148
+ dashboard "What needs attention" card (hidden when empty) + a browser
149
+ Notification on unseen critical findings
150
+ ```
151
+
152
+ Rules that matter:
153
+
154
+ - **Detectors are pure.** `detect(repo, now_iso)` reads the DB and returns
155
+ `Finding` objects — it never writes. The scheduler owns every write. That
156
+ boundary is what makes detectors trivially unit-testable.
157
+ - **Idempotent upserts.** `upsert_alert` keys on `(detector, dedup_key)` with
158
+ `ON CONFLICT … DO UPDATE … RETURNING id`, so re-running a detector updates
159
+ the existing row instead of duplicating.
160
+ - **`resolved_at` lifecycle.** Each tick the scheduler resolves alerts whose
161
+ `dedup_key` is no longer in the detector's current findings
162
+ (`resolve_stale_alerts`). If the issue recurs, the row un-resolves and its
163
+ `seen_at` clears — so a tool that recovers then re-spikes notifies again
164
+ rather than staying silent.
165
+ - **Self-registration.** Detectors `@register` in their module, imported for
166
+ side-effect in `python/argus/detectors/__init__.py` — same pattern as
167
+ adapters. Adding one is a new file + the import line; no scheduler edits.
168
+
169
+ The v1 detector (`tool_error_rate_spike`) compares each tool's last-7-day
170
+ error rate against its preceding 4-week baseline and fires when the rate
171
+ multiplies past a threshold — or breaks from a zero baseline. Cost was
172
+ deliberately *not* the v1 signal: it's meaningless for Pro/Max users who
173
+ don't pay per token.
174
+
175
+ ## Scaffolding (`argus claude`)
176
+
177
+ Separate from ingest/serve: a file-copy tool that stamps a `.claude/` setup
178
+ (and a root `CLAUDE.md`) into a project — proactive config *before* the agent
179
+ runs, not just observation after. No DB, no daemon, no schema.
180
+
181
+ ```
182
+ python/argus/scaffold/storage.py resolve template names across two tiers
183
+ python/argus/scaffold/scaffolder.py init: copy a template dir → a project
184
+ python/argus/scaffold/snapshot.py create: snapshot a project's .claude/
185
+
186
+
187
+ templates/<name>/ bundled (read-only, shipped in the wheel)
188
+ ~/.argus/templates/<name>/ user templates (read-write)
189
+ ```
190
+
191
+ - **Two tiers, user-first.** `resolve_template` checks `~/.argus/templates/`
192
+ then the bundled `templates/`. The bundled `default` ships in the wheel via
193
+ `force-include`, exactly like `pricing/` and `dashboard-dist/`.
194
+ - **Pure file copy.** No templating or substitution in v1. `init` copies each
195
+ template file to its matching path in the project (`CLAUDE.md` → root,
196
+ everything else → `.claude/`).
197
+ - **`CLAUDE.md` is never overwritten**, even with `--force` — it's the user's
198
+ project brain. `create` is scoped to `.claude/` and excludes session/cache
199
+ dirs from the snapshot.
200
+
201
+ ## Conventions that matter
202
+
203
+ A few patterns to absorb before adding code:
204
+
205
+ - **Cutoff-string aggregation.** Windowed queries take an ISO
206
+ timestamp string and use `WHERE timestamp >= ?`. Pass `''` for
207
+ "all time" (empty string sorts below every real ISO string).
208
+ Examples: `aggregateTurnsByDay`, `toolCallsTotal`, `mcpToolCalls`.
209
+
210
+ - **Top-level vs sub-agent ids.** Sub-agent rollup sessions contain
211
+ `/` in their `id`. SQL excludes them with
212
+ `session_id NOT LIKE '%/%'`; JS excludes them with the
213
+ `isTopLevel(id)` helper. Sub-agent token usage is rolled into the
214
+ parent's totals during ingest, so excluding them at read time
215
+ prevents double-counting.
216
+
217
+ - **Migrations are append-only.** Never edit a published migration in
218
+ `python/argus/store/migrations/inline.py`. Add a new `MIGRATION_N+1`
219
+ and bump the schema version check in `db.py`. Production users have
220
+ data that depends on the exact SQL that already ran.
221
+
222
+ - **Idempotent ingest.** `upsert_turn`, `upsert_session`, etc. all use
223
+ `ON CONFLICT(id) DO UPDATE`. Re-ingesting a file from offset 0 is
224
+ safe — useful for backfilling derived data after a schema change.
225
+
226
+ - **HTML-escape every user-controlled string.** Anything from the
227
+ JSONL that ends up in `innerHTML` goes through `escapeHtml()` from
228
+ `dashboard/src/scripts/format.ts`. The only HTML allowed through
229
+ raw is `<mark>` from FTS5 snippets, via `safeSnippet()`.
230
+
231
+ - **CSRF Origin check.** All non-GET `/api/*` routes verify the
232
+ `Origin` header is loopback. Implemented as FastAPI middleware in
233
+ `python/argus/server/app.py`. Defends against random tabs in your
234
+ browser hitting argus while it's running.
235
+
236
+ - **Path safety.** `discover_session_files` calls `Path.resolve(strict=True)`
237
+ on each candidate and rejects anything that doesn't canonicalise
238
+ under `~/.claude/`. Defends against a hostile symlink planted in
239
+ `projects/` pointing at e.g. `/etc/passwd`. On Windows the
240
+ containment check lowercases both sides so case differences in the
241
+ canonical path don't silently reject every candidate.
242
+
243
+ - **Adapter registry.** Adapter classes self-register via the
244
+ `@register` decorator (`python/argus/adapters/registry.py`).
245
+ `available_adapters()` returns every registered class whose
246
+ `is_present()` is True. Adding a new adapter (Codex, OpenClaw,
247
+ Hermes, …) is one new folder + one `@register` — zero edits to CLI,
248
+ watcher, pipeline, or server.
249
+
250
+ ## Where things live
251
+
252
+ ```
253
+ python/argus/
254
+ cli.py argus start / search / pricing / claude / wipe (typer)
255
+ adapters/
256
+ base.py Adapter protocol + ParseError
257
+ registry.py @register + available_adapters()
258
+ claude_code/ JSONL parsers, discovery, history.jsonl
259
+ collector/ pipeline, watcher, first-run, backfills, alert scheduler
260
+ detectors/ alert detectors (pure reads) + @register registry
261
+ scaffold/ argus claude: template storage / init / snapshot
262
+ pricing/ LiteLLM-derived price table + cost compute
263
+ store/ SQLite repository + migrations
264
+ server/ FastAPI app (app.py) + routes (api.py)
265
+ schema/ pydantic data models
266
+
267
+ dashboard/
268
+ src/
269
+ layouts/Default.astro nav, footer, font, styles
270
+ pages/ one .astro per route
271
+ scripts/ api client, charts, formatters
272
+ styles/global.css single theme stylesheet
273
+ dashboard-dist/ built dashboard, shipped in the wheel as data
274
+
275
+ pricing/ bundled LiteLLM price tables (shipped in wheel as data)
276
+ templates/ bundled .claude/ scaffolding templates (shipped in wheel as data)
277
+ tests/ pytest suite, mirrors python/argus/ layout
278
+ ~/.argus/argus.db SQLite DB (created on first run)
279
+ ~/.argus/templates/ user-saved scaffolding templates (argus claude template create)
280
+ ~/.claude/ source data we read from
281
+ ```
282
+
283
+ ## What's deliberately NOT here
284
+
285
+ - **No embeddings, no vector DB.** Search is SQLite FTS5 — inverted
286
+ index, BM25 ranking, sub-millisecond, fully offline.
287
+ - **No external APIs** except the optional `argus pricing refresh`
288
+ (single HTTP GET to LiteLLM's GitHub).
289
+ - **No message queues or external workers.** Concurrency is at most three
290
+ in-process threads: the file watcher, the HTTP server, and a lightweight
291
+ detector scheduler. No brokers, no cron, no separate processes.
292
+ - **No auth.** Loopback binding is the security model — see
293
+ [SECURITY.md](./SECURITY.md).
294
+
295
+ ## Common changes and where to start
296
+
297
+ | Want to… | Touch these |
298
+ |---|---|
299
+ | Add a new dashboard page | `dashboard/src/pages/<name>.astro`, add nav link in `dashboard/src/layouts/Default.astro` |
300
+ | Add a new API endpoint | `python/argus/server/api.py`, add a `repo.<method_name>()` in `python/argus/store/repository.py` |
301
+ | Add a new SQL table or column | New `MIGRATION_N+1` in `python/argus/store/migrations/inline.py`, bump schema check in `db.py`, add `repo` method, add pydantic model in `python/argus/schema/types.py` |
302
+ | Parse a new JSONL field | `python/argus/adapters/claude_code/schemas.py` (pydantic), wire into `pipeline.py` |
303
+ | Tweak cost computation | `python/argus/pricing/compute.py`, then re-ingest to recompute via a backfill |
304
+ | Add a new chart | `dashboard/src/scripts/charts.ts` (theme is shared) |
305
+ | Add a new adapter (Codex, OpenClaw, Hermes, …) | New folder `python/argus/adapters/<agent>/` + `@register class` in `adapter.py`. No edits to CLI / watcher / pipeline / server. |
306
+ | Add an alert detector | New file in `python/argus/detectors/` with a `@register` class whose pure `detect()` returns `Finding`s, plus the side-effect import in `detectors/__init__.py`. The scheduler picks it up automatically. |
307
+ | Change the bundled scaffold template | Edit files under `templates/default/`; they're force-included into the wheel. New top-level dirs need a `force-include` entry in `pyproject.toml`. |
@@ -0,0 +1,156 @@
1
+ # Contributing to Argus
2
+
3
+ Thanks for being here. Argus is a one-person project at this point, so
4
+ the bar for contributions is "does it make the tool better for people
5
+ running it?". Bug fixes, docs improvements, and small features are all
6
+ welcome — please skim this file before opening a big PR so we're not
7
+ working at cross-purposes.
8
+
9
+ If you're new to the codebase, **read [ARCHITECTURE.md](./ARCHITECTURE.md)
10
+ first** — it's a 5-minute tour of how the pieces fit together.
11
+
12
+ ## Quick start
13
+
14
+ ```sh
15
+ git clone https://github.com/KrishBhimani/argus-code.git
16
+ cd argus-code
17
+ uv sync # creates .venv and installs deps + dev tools
18
+ uv run pytest # ~215 tests, ~20s
19
+ uv run argus start # fast iteration loop — runs directly from source
20
+ ```
21
+
22
+ `uv run argus start` runs the Python source directly. No build step.
23
+ Change a file, restart, see the result.
24
+
25
+ If you don't have `uv` yet: <https://docs.astral.sh/uv/getting-started/installation/>.
26
+
27
+ ## Running the built form
28
+
29
+ CI builds and tests on every push. To smoke-test the built wheel
30
+ locally exactly as a user would:
31
+
32
+ ```sh
33
+ cd dashboard && npm ci && npm run build && cd ..
34
+ cp -r dashboard/dist dashboard-dist # bundle the dashboard
35
+ uv build # wheel + sdist in dist/
36
+ uv tool install ./dist/argus_cli-*.whl # install globally
37
+ argus start
38
+ ```
39
+
40
+ ## Tests
41
+
42
+ ```sh
43
+ uv run pytest # one-shot
44
+ uv run pytest -k <expr> # filter by test name
45
+ uv run pytest tests/store/ # by directory
46
+ uv run pytest --cov=argus # with coverage
47
+ ```
48
+
49
+ Tests live in a sibling `tests/` tree mirroring `python/argus/`
50
+ (e.g., `python/argus/store/repository.py` → `tests/store/test_repository.py`).
51
+ New features need new tests. Bug fixes need a regression test that
52
+ fails before the fix and passes after. See [TESTING.md](./TESTING.md)
53
+ for the full catalog.
54
+
55
+ ## CI
56
+
57
+ `.github/workflows/test.yml` runs on every push and PR:
58
+
59
+ - **Test matrix:** Ubuntu, macOS, Windows × Python 3.11, 3.12, 3.13.
60
+ - **Dashboard build** (release-only): runs `npm ci && npm run build`
61
+ before `uv build` to bundle the static dashboard into the wheel.
62
+
63
+ The Windows runner is non-negotiable — path normalisation, WAL `-shm`
64
+ cleanup, and short-name path quirks are all platform-sensitive.
65
+
66
+ If CI fails on a platform you don't have, mention it in the PR and we'll
67
+ help debug rather than asking you to set up a Windows VM.
68
+
69
+ ## Code style
70
+
71
+ - Type-annotated Python (`from __future__ import annotations`,
72
+ `int | None` over `Optional[int]`).
73
+ - Match the existing style. There's no separate lint config — read the
74
+ neighboring files and mimic.
75
+ - Run `uv run pytest` before pushing. CI is a backstop, not a replacement.
76
+ - Conventional commit prefixes (`feat:`, `fix:`, `docs:`, `chore:`,
77
+ `ci:`) for clarity. Not strictly enforced, but helpful.
78
+
79
+ ## Areas to be careful
80
+
81
+ A few corners of the codebase have hidden complexity. If you're
82
+ touching them, take a beat.
83
+
84
+ ### Path handling on Windows
85
+
86
+ The Windows filesystem is case-insensitive but Python's string
87
+ equality isn't. `discover_session_files()` lowercases both sides of
88
+ the canonical-root containment check before comparing, so case
89
+ differences in resolved paths don't silently reject every candidate.
90
+ If your change touches path comparison logic, test on Windows CI
91
+ before assuming it works.
92
+
93
+ ### Pricing tables (`pricing/*.json`)
94
+
95
+ These are generated from LiteLLM via `argus pricing refresh`. Don't
96
+ hand-edit them. If you need to change pricing logic, look at
97
+ `python/argus/pricing/compute.py`.
98
+
99
+ ### Schema migrations (`python/argus/store/migrations/inline.py`)
100
+
101
+ Append-only. Never edit a published migration. Add a new `MIGRATION_N+1`
102
+ and bump the `schema_version` check in `db.py`. SQLite in production
103
+ has data that depends on the exact SQL that already ran.
104
+
105
+ ### Windowed aggregations
106
+
107
+ For any "last N days" / windowed view, query the **`turns`** table by
108
+ `timestamp`, NOT the **`sessions`** table by `started_at`. Session
109
+ totals are pre-summed across the session's lifetime — using them for
110
+ windowed views silently drops activity in sessions that started before
111
+ the window and dumps multi-day spend onto the session's start date in
112
+ heatmaps. The canonical helper is `Repository.aggregate_turns_by_day()`.
113
+ See [ARCHITECTURE.md](./ARCHITECTURE.md) for the data-model rationale.
114
+
115
+ ### Adapters
116
+
117
+ The `Adapter` protocol in `python/argus/adapters/base.py` is what the
118
+ pipeline, watcher, and server depend on. Per-adapter packages
119
+ self-register via `@register` in their `adapter.py`. If you're adding
120
+ a new adapter (Codex, OpenClaw, Hermes, …), do not edit shared code
121
+ to special-case it — express the per-agent behavior through the
122
+ optional extension points (`extra_watch_paths`, `ingest_extra`,
123
+ `sub_session_files_for`, `should_skip`, `normalize_model_name`).
124
+
125
+ ### Detectors and the alert scheduler
126
+
127
+ Detectors (`python/argus/detectors/`) must stay **pure**: `detect()` reads
128
+ the DB and returns `Finding`s, and that's it. The scheduler
129
+ (`python/argus/collector/scheduler.py`) is the **only** thing that writes to
130
+ the `alerts` table — don't blur that boundary by writing from a detector.
131
+ Alerts are upserted on `(detector, dedup_key)` and reconciled each tick via
132
+ `resolved_at`, so the key you choose for `dedup_key` *is* the identity of the
133
+ thing being alerted on. See ARCHITECTURE.md → "Detection (alerts)".
134
+
135
+ ### Security model
136
+
137
+ `SECURITY.md` lists the invariants this codebase deliberately
138
+ maintains: `127.0.0.1` binding by default, CSRF Origin check, HTML
139
+ escaping, symlink rejection, parameterized SQL, no postinstall scripts.
140
+ If your change weakens any of those, call it out explicitly in the PR
141
+ description so review can focus there.
142
+
143
+ ## Reporting bugs / requesting features
144
+
145
+ Use the issue templates:
146
+
147
+ - **Bug** — include OS, Python version, argus version, repro steps.
148
+ - **Feature** — describe the problem first, the solution second.
149
+
150
+ For **security issues**, do not open a public issue — see
151
+ [SECURITY.md](./SECURITY.md) for the private reporting path.
152
+
153
+ ## License
154
+
155
+ By contributing, you agree your code ships under the MIT license (same
156
+ as the rest of the repo — see [LICENSE](./LICENSE)).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Krish Bhimani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.