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.
- argus_code-0.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +68 -0
- argus_code-0.2.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- argus_code-0.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
- argus_code-0.2.0/.github/pull_request_template.md +36 -0
- argus_code-0.2.0/.gitignore +26 -0
- argus_code-0.2.0/.npmrc +0 -0
- argus_code-0.2.0/ARCHITECTURE.md +307 -0
- argus_code-0.2.0/CONTRIBUTING.md +156 -0
- argus_code-0.2.0/LICENSE +21 -0
- argus_code-0.2.0/NOTICE +22 -0
- argus_code-0.2.0/PKG-INFO +247 -0
- argus_code-0.2.0/PRD.md +380 -0
- argus_code-0.2.0/README.md +195 -0
- argus_code-0.2.0/SECURITY.md +101 -0
- argus_code-0.2.0/TESTING.md +355 -0
- argus_code-0.2.0/dashboard/.astro/content-assets.mjs +1 -0
- argus_code-0.2.0/dashboard/.astro/content-modules.mjs +1 -0
- argus_code-0.2.0/dashboard/.astro/content.d.ts +155 -0
- argus_code-0.2.0/dashboard/.astro/types.d.ts +2 -0
- argus_code-0.2.0/dashboard/astro.config.mjs +9 -0
- argus_code-0.2.0/dashboard/package-lock.json +4787 -0
- argus_code-0.2.0/dashboard/package.json +18 -0
- argus_code-0.2.0/dashboard/public/styles/global.css +307 -0
- argus_code-0.2.0/dashboard/src/layouts/Default.astro +65 -0
- argus_code-0.2.0/dashboard/src/pages/index.astro +131 -0
- argus_code-0.2.0/dashboard/src/pages/models.astro +131 -0
- argus_code-0.2.0/dashboard/src/pages/prompts.astro +407 -0
- argus_code-0.2.0/dashboard/src/pages/session.astro +237 -0
- argus_code-0.2.0/dashboard/src/pages/sessions/index.astro +87 -0
- argus_code-0.2.0/dashboard/src/pages/settings.astro +272 -0
- argus_code-0.2.0/dashboard/src/pages/tools.astro +212 -0
- argus_code-0.2.0/dashboard/src/pages/trends.astro +55 -0
- argus_code-0.2.0/dashboard/src/scripts/alerts.ts +85 -0
- argus_code-0.2.0/dashboard/src/scripts/api.ts +152 -0
- argus_code-0.2.0/dashboard/src/scripts/charts.ts +200 -0
- argus_code-0.2.0/dashboard/src/scripts/format.ts +70 -0
- argus_code-0.2.0/dashboard/src/styles/global.css +307 -0
- argus_code-0.2.0/dashboard/tsconfig.json +4 -0
- argus_code-0.2.0/dashboard-dist/_astro/charts.BIevw6Es.js +1 -0
- argus_code-0.2.0/dashboard-dist/_astro/format.DxC1NGYT.js +1 -0
- argus_code-0.2.0/dashboard-dist/_astro/index.astro_astro_type_script_index_0_lang.CgwSARdD.js +24 -0
- argus_code-0.2.0/dashboard-dist/_astro/index.astro_astro_type_script_index_0_lang.W18SJsr7.js +11 -0
- argus_code-0.2.0/dashboard-dist/_astro/installCanvasRenderer.D_tC6TXz.js +18 -0
- argus_code-0.2.0/dashboard-dist/_astro/models.astro_astro_type_script_index_0_lang.BHTHXYHC.js +13 -0
- argus_code-0.2.0/dashboard-dist/_astro/prompts.astro_astro_type_script_index_0_lang.DfNgiDv9.js +17 -0
- argus_code-0.2.0/dashboard-dist/_astro/session.astro_astro_type_script_index_0_lang.Dj_bfrIa.js +86 -0
- argus_code-0.2.0/dashboard-dist/_astro/settings.astro_astro_type_script_index_0_lang.d_a-uvdi.js +24 -0
- argus_code-0.2.0/dashboard-dist/_astro/tools.astro_astro_type_script_index_0_lang.Dzzau3Yt.js +12 -0
- argus_code-0.2.0/dashboard-dist/_astro/trends.astro_astro_type_script_index_0_lang.BLLeGRNa.js +5 -0
- argus_code-0.2.0/dashboard-dist/index.html +2 -0
- argus_code-0.2.0/dashboard-dist/models/index.html +1 -0
- argus_code-0.2.0/dashboard-dist/prompts/index.html +18 -0
- argus_code-0.2.0/dashboard-dist/session/index.html +2 -0
- argus_code-0.2.0/dashboard-dist/sessions/index.html +1 -0
- argus_code-0.2.0/dashboard-dist/settings/index.html +8 -0
- argus_code-0.2.0/dashboard-dist/styles/global.css +307 -0
- argus_code-0.2.0/dashboard-dist/tools/index.html +1 -0
- argus_code-0.2.0/dashboard-dist/trends/index.html +1 -0
- argus_code-0.2.0/pricing/2026-05-02.json +24 -0
- argus_code-0.2.0/pyproject.toml +68 -0
- argus_code-0.2.0/python/argus/__init__.py +3 -0
- argus_code-0.2.0/python/argus/adapters/__init__.py +7 -0
- argus_code-0.2.0/python/argus/adapters/base.py +108 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/__init__.py +5 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/adapter.py +63 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/discover.py +72 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/extract_tool_calls.py +86 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/extract_transcript.py +111 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/extract_turns.py +69 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/history_jsonl.py +138 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/ingest_file.py +137 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/model.py +11 -0
- argus_code-0.2.0/python/argus/adapters/claude_code/schemas.py +77 -0
- argus_code-0.2.0/python/argus/adapters/registry.py +30 -0
- argus_code-0.2.0/python/argus/cli.py +384 -0
- argus_code-0.2.0/python/argus/collector/__init__.py +0 -0
- argus_code-0.2.0/python/argus/collector/aggregate.py +102 -0
- argus_code-0.2.0/python/argus/collector/first_run.py +189 -0
- argus_code-0.2.0/python/argus/collector/pipeline.py +140 -0
- argus_code-0.2.0/python/argus/collector/rollup_subagents.py +27 -0
- argus_code-0.2.0/python/argus/collector/scheduler.py +89 -0
- argus_code-0.2.0/python/argus/collector/search_backfill.py +109 -0
- argus_code-0.2.0/python/argus/collector/watcher.py +178 -0
- argus_code-0.2.0/python/argus/detectors/__init__.py +6 -0
- argus_code-0.2.0/python/argus/detectors/base.py +34 -0
- argus_code-0.2.0/python/argus/detectors/registry.py +20 -0
- argus_code-0.2.0/python/argus/detectors/tool_error_rate_spike.py +138 -0
- argus_code-0.2.0/python/argus/pricing/__init__.py +0 -0
- argus_code-0.2.0/python/argus/pricing/compute.py +46 -0
- argus_code-0.2.0/python/argus/pricing/load.py +45 -0
- argus_code-0.2.0/python/argus/pricing/refresh.py +91 -0
- argus_code-0.2.0/python/argus/pricing/types.py +21 -0
- argus_code-0.2.0/python/argus/scaffold/__init__.py +0 -0
- argus_code-0.2.0/python/argus/scaffold/scaffolder.py +45 -0
- argus_code-0.2.0/python/argus/scaffold/snapshot.py +73 -0
- argus_code-0.2.0/python/argus/scaffold/storage.py +60 -0
- argus_code-0.2.0/python/argus/schema/__init__.py +0 -0
- argus_code-0.2.0/python/argus/schema/types.py +157 -0
- argus_code-0.2.0/python/argus/server/__init__.py +0 -0
- argus_code-0.2.0/python/argus/server/api.py +661 -0
- argus_code-0.2.0/python/argus/server/app.py +97 -0
- argus_code-0.2.0/python/argus/store/__init__.py +0 -0
- argus_code-0.2.0/python/argus/store/db.py +103 -0
- argus_code-0.2.0/python/argus/store/migrations/__init__.py +0 -0
- argus_code-0.2.0/python/argus/store/migrations/inline.py +180 -0
- argus_code-0.2.0/python/argus/store/repository.py +778 -0
- argus_code-0.2.0/templates/default/.claude/agents/code-reviewer.md +27 -0
- argus_code-0.2.0/templates/default/.claude/agents/security-auditor.md +28 -0
- argus_code-0.2.0/templates/default/.claude/commands/commit.md +38 -0
- argus_code-0.2.0/templates/default/.claude/commands/deploy.md +13 -0
- argus_code-0.2.0/templates/default/.claude/commands/fix-issue.md +15 -0
- argus_code-0.2.0/templates/default/.claude/commands/pr.md +38 -0
- argus_code-0.2.0/templates/default/.claude/commands/review.md +14 -0
- argus_code-0.2.0/templates/default/.claude/rules/api-conventions.md +27 -0
- argus_code-0.2.0/templates/default/.claude/rules/code-style.md +25 -0
- argus_code-0.2.0/templates/default/.claude/rules/testing.md +19 -0
- argus_code-0.2.0/templates/default/.claude/settings.json +28 -0
- argus_code-0.2.0/templates/default/.claude/skills/example/SKILL.md +11 -0
- argus_code-0.2.0/templates/default/CLAUDE.md +57 -0
- argus_code-0.2.0/tests/__init__.py +0 -0
- argus_code-0.2.0/tests/adapters/__init__.py +0 -0
- argus_code-0.2.0/tests/adapters/claude_code/__init__.py +0 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_adapter.py +10 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_discover.py +31 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_extract_tool_calls.py +103 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_extract_transcript.py +152 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_extract_turns.py +54 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_history_jsonl.py +156 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_ingest_file.py +88 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_integration.py +37 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_model.py +17 -0
- argus_code-0.2.0/tests/adapters/claude_code/test_schemas.py +102 -0
- argus_code-0.2.0/tests/adapters/test_registry.py +79 -0
- argus_code-0.2.0/tests/collector/__init__.py +0 -0
- argus_code-0.2.0/tests/collector/test_aggregate.py +79 -0
- argus_code-0.2.0/tests/collector/test_first_run.py +77 -0
- argus_code-0.2.0/tests/collector/test_pipeline.py +223 -0
- argus_code-0.2.0/tests/collector/test_rollup_subagents.py +39 -0
- argus_code-0.2.0/tests/collector/test_scheduler.py +141 -0
- argus_code-0.2.0/tests/collector/test_watcher.py +67 -0
- argus_code-0.2.0/tests/conftest.py +188 -0
- argus_code-0.2.0/tests/detectors/__init__.py +0 -0
- argus_code-0.2.0/tests/detectors/test_registry.py +54 -0
- argus_code-0.2.0/tests/detectors/test_tool_error_rate_spike.py +143 -0
- argus_code-0.2.0/tests/pricing/__init__.py +0 -0
- argus_code-0.2.0/tests/pricing/test_compute.py +84 -0
- argus_code-0.2.0/tests/pricing/test_load.py +16 -0
- argus_code-0.2.0/tests/pricing/test_refresh.py +54 -0
- argus_code-0.2.0/tests/scaffold/__init__.py +0 -0
- argus_code-0.2.0/tests/scaffold/test_bundled_template.py +40 -0
- argus_code-0.2.0/tests/scaffold/test_cli_claude.py +112 -0
- argus_code-0.2.0/tests/scaffold/test_scaffolder.py +76 -0
- argus_code-0.2.0/tests/scaffold/test_snapshot.py +67 -0
- argus_code-0.2.0/tests/scaffold/test_storage.py +57 -0
- argus_code-0.2.0/tests/schema/__init__.py +0 -0
- argus_code-0.2.0/tests/schema/test_types.py +74 -0
- argus_code-0.2.0/tests/server/__init__.py +0 -0
- argus_code-0.2.0/tests/server/test_api.py +233 -0
- argus_code-0.2.0/tests/server/test_api_search.py +313 -0
- argus_code-0.2.0/tests/server/test_server.py +125 -0
- argus_code-0.2.0/tests/server/test_week_of.py +65 -0
- argus_code-0.2.0/tests/store/__init__.py +0 -0
- argus_code-0.2.0/tests/store/test_db.py +149 -0
- argus_code-0.2.0/tests/store/test_repository.py +521 -0
- argus_code-0.2.0/tests/test_e2e.py +77 -0
- 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,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/
|
argus_code-0.2.0/.npmrc
ADDED
|
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)).
|
argus_code-0.2.0/LICENSE
ADDED
|
@@ -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.
|