devtrust-apr 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.
@@ -0,0 +1,45 @@
1
+ # OS / editor noise
2
+ .DS_Store
3
+ Thumbs.db
4
+ *.swp
5
+ *~
6
+ .idea/
7
+ .vscode/
8
+ *.iml
9
+
10
+ # Local environment
11
+ .env
12
+ .env.local
13
+ .env.*.local
14
+ *.local
15
+
16
+ # Build / dependency output
17
+ node_modules/
18
+ dist/
19
+ build/
20
+ target/
21
+ __pycache__/
22
+ *.pyc
23
+ .pytest_cache/
24
+ coverage/
25
+ .cache/
26
+
27
+ # Logs
28
+ *.log
29
+ logs/
30
+
31
+ # Secrets — NEVER commit these
32
+ *.pem
33
+ *.key
34
+ secrets/
35
+ *.secrets.json
36
+
37
+ # Docx working artifacts (keep .docx itself, drop temp files)
38
+ ~$*.docx
39
+ ~$*.xlsx
40
+ ~$*.pptx
41
+
42
+ # Claude session-specific (keep configs, drop runtime)
43
+ .claude/sessions/
44
+ .claude/cache/
45
+ .claude/.tmp/
@@ -0,0 +1,139 @@
1
+ # Agent-PR Reviewer — changelog
2
+
3
+ All notable changes to `apr` are documented here. Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [SemVer](https://semver.org/spec/v2.0.0.html).
4
+
5
+ ## [0.2.0] — 2026-05-09
6
+
7
+ ### Added
8
+ - **`ai-review:hallucinated-symbol` is now multi-language.** The deterministic AI-pattern checker that flags calls to names that don't exist now consumes Repo X-ray v0.4's JavaScript / TypeScript call edges in addition to Python. The rule fires the same way for all three languages: walk the artifact's edges, scoped to changed files, and flag callees whose first segment doesn't resolve to an in-repo symbol or a known global / package root.
9
+ - **Language-aware allowlist.** `_KNOWN_NAMES_PY` (Python builtins + stdlib + ~50 popular pip roots) and `_KNOWN_NAMES_JS` (ECMAScript globals, browser DOM globals, Node.js globals, ~70 popular npm package roots) are now disjoint. A `.ts` file calling `os.path.join` is not silenced by the Python allowlist — it's correctly flagged as a wrong-language hallucination signal.
10
+ - **In-repo JS/TS imports already pass.** Repox v0.4 sets `target_file` on every call edge whose callee was bound via `import { x } from './y'` to an in-repo file. The rule's existing `target_file is set -> skip` short-circuit handles those without changes.
11
+ - **External JS/TS imports.** `apr.repox_integration._binding_hint` now extracts a usable binding name from JS/TS module specifiers — `react-dom/client` → `client`, `@scope/pkg/sub` → `sub`, bare `'express'` → `'express'`. Renamed default imports (`import _ from 'lodash'`) are silenced by the existing two-character-name skip rather than misflagged.
12
+ - **8 new tests** in `tests/test_smoke.py`:
13
+ - `test_ai_rule_js_hallucinated_callee_flagged` — truly invented JS callee fires.
14
+ - `test_ai_rule_js_console_log_safe` — `console.log` doesn't fire.
15
+ - `test_ai_rule_js_browser_globals_safe` — `document.querySelector`, `fetch`, `localStorage.getItem` don't fire.
16
+ - `test_ai_rule_js_node_globals_safe` — `process.exit`, `Buffer.from` don't fire.
17
+ - `test_ai_rule_js_npm_root_safe` — `express()` doesn't fire when imported.
18
+ - `test_ai_rule_ts_in_repo_call_resolved` — call edge with `target_file` set is silent.
19
+ - `test_ai_rule_js_wrong_language_callee_flagged` — JS file calling `os.path.join` IS flagged (Python allowlist is not consulted for JS files).
20
+ - `test_binding_hint_for_js_specifiers` — direct unit test for the new `_binding_hint` helper.
21
+
22
+ ### Changed
23
+ - `apr.repox_integration` extracts the new `_binding_hint(target_module)` helper instead of inline `lstrip + split`. Behavior unchanged for Python imports; JS/TS specifiers now produce more accurate hints (subpath last-segment, scoped-package last-segment).
24
+ - The version-pin smoke test (`test_apr_version_*`) now uses a structural SemVer regex check instead of an exact-string equality, matching the pattern used in agtrace, tokencost, and whychanged. This prevents the test from going stale on every minor bump and keeps `scripts/release.py --check` clean.
25
+
26
+ ### Notes
27
+ - Schema unchanged (still `0.0.1` — additive: same `Finding` shape, same rule IDs, just broader coverage).
28
+ - Diff-comprehension rule is unchanged in v0.2.
29
+ - This release closes the only deferred v0.1 item: when we shipped repox v0.4 with JS/TS call edges, apr's hallucinated-symbol rule was still Python-only. v0.2.0 closes that gap and gives APR full multi-language coverage across all three Wave-1 supported languages (Python, JavaScript, TypeScript).
30
+ - Known limitation: renamed default imports like `import _ from 'lodash'` cannot be precisely tracked without extending the repox `Import` model to carry `local_names`. v0.2.0 mitigates by skipping ≤ 2-character first-segments; a future repox v0.5 + apr v0.3 will close this fully.
31
+
32
+ [0.2.0]: https://github.com/AbdullahBakir97/apr/compare/v0.1.1...v0.2.0
33
+
34
+ ---
35
+
36
+ ## [0.1.1] — 2026-05-08
37
+
38
+ ### Added
39
+ - **Real Anthropic backend for `ai-review:diff-comprehension`.** `AnthropicProvider.analyze_diff()` is now a working implementation:
40
+ - Calls the Messages API (non-streaming, single-turn) via the official `anthropic` SDK.
41
+ - Builds a JSON-shaped prompt (system + user) via the new `apr.prompts` module — diff is truncated at 60,000 chars by default to bound input cost on runaway diffs.
42
+ - Caps reply at 1024 tokens (configurable via the `AnthropicProvider` constructor).
43
+ - Parses the model's reply tolerantly: tries strict `json.loads` first, then extracts the first balanced `{...}` block when the model wraps JSON in prose.
44
+ - Filters invalid severity values silently rather than raising — no LLM tantrum can break review.
45
+ - New `apr.prompts` module — separates prompt construction + response parsing from the SDK so it can be unit-tested without the `anthropic` wheel installed.
46
+ - New env var `APR_ANTHROPIC_MODEL` lets operators override the default model (`claude-sonnet-4-6`) per deployment.
47
+ - 9 new tests: prompt construction, diff truncation, strict JSON parse, prose-wrapper recovery, garbage / invalid-severity rejection, end-to-end with a mocked SDK client, SDK-exception shielding, unparseable-reply handling, version assertion.
48
+
49
+ ### Changed
50
+ - `AnthropicProvider.__init__` accepts an optional `client` keyword for tests + future dependency injection.
51
+ - The SDK `import anthropic` happens lazily inside `_ensure_client`, so `apr` installs without the optional `[ai]` extra.
52
+ - All findings produced through this rule emerge with `rule_id="ai-review:diff-comprehension"` regardless of what the model claimed — vendor-internal IDs cannot leak into the report.
53
+
54
+ ### Notes
55
+ - Schema unchanged (still 0.0.1 — same Finding shape).
56
+ - Cost guidance: a typical 500-line PR diff produces ~3K input tokens + ~200 output tokens, i.e. < $0.005 per review at Sonnet pricing. Bound by `max_diff_chars` and `max_tokens`. Operators with high PR volume should monitor and tune.
57
+ - The rule is still gated behind `--enable-ai --ai-provider=anthropic` and requires `ANTHROPIC_API_KEY` in the environment. Default behavior (no flag) is unchanged: deterministic rules only, no API calls, no cost.
58
+
59
+ [0.1.1]: https://github.com/AbdullahBakir97/apr/compare/v0.1.0...v0.1.1
60
+
61
+ ---
62
+
63
+ ## [0.1.0] — 2026-05-08
64
+
65
+ ### Added
66
+ - **AI rule pack (`ai-review:*`).** New `src/apr/rules_ai.py` ships two rules, both opt-in via `--enable-ai`:
67
+ - `ai-review:hallucinated-symbol` (warning, ai-pattern) — **deterministic.** Walks Repo X-ray's `call_graph.edges` and flags function calls whose callee doesn't resolve to an in-repo symbol AND isn't a Python built-in / well-known stdlib root / file-local imported alias. Catches the classic AI-generation failure mode where a function call references a name that doesn't exist anywhere in the codebase.
68
+ - `ai-review:diff-comprehension` (delegates to `LLMProvider`) — pluggable LLM-backed checker for "does the PR description accurately describe the diff." v0.1.0 ships the `NullProvider` (returns no findings) and an `AnthropicProvider` stub. The streaming Anthropic implementation is v0.1.1.
69
+ - **Repox integration** (`src/apr/repox_integration.py`) — best-effort loader for `.repox/architecture.json`. Returns `None` for older artifacts (v0.0.x / v0.1.x) without a call graph; the AI rule pack then no-ops cleanly.
70
+ - **LLM provider interface** (`src/apr/llm.py`) — `LLMProvider` Protocol + `NullProvider` + `AnthropicProvider` stub + `build_provider()` factory. Vendor swaps are a matter of writing a new provider class; the engine and rules don't change.
71
+ - **CLI flags** — `--enable-ai/--no-enable-ai` (default off), `--ai-provider null|anthropic`, `--diff PATH` (unified-diff file for diff-comprehension).
72
+ - **Optional `[ai]` extra** in `pyproject.toml` — `pip install apr[ai]` brings in the `anthropic` SDK.
73
+ - **9 new tests** covering: hallucinated-symbol fires correctly, builtin/import allow-list silences false positives, only changed files are scanned, AI off by default, provider exception shielding, vendor rule_id namespace isolation, the AnthropicProvider stub raising NotImplementedError, repox integration returning None for missing/old artifacts.
74
+
75
+ ### Changed
76
+ - **`check_python_file` ordering fix.** Source-text rules (`todo-no-ticket`, `hardcoded-secret`) now run *before* AST parsing, so a syntax error doesn't silently suppress them. Regression tests added — was a real bug discovered when a BOM-encoded demo file produced only `syntax-error` and missed the embedded AKIA-shaped key.
77
+ - Promoted from `Development Status :: 3 - Alpha` to `Development Status :: 4 - Beta`.
78
+ - Engine `review()` gained kwargs: `enable_ai`, `llm_provider`, `diff`. Backward compatible — defaults preserve the v0.0.2 behavior.
79
+
80
+ ### Notes
81
+ - Schema unchanged (still 0.0.1 — same Finding shape, additive rule IDs).
82
+ - AI rules are off by default to keep PR review fast and deterministic for the common case. Operators opt in per repo by adding `--enable-ai` to their CI invocation.
83
+ - The hallucinated-symbol allow-list is conservative: stdlib roots + ~50 common third-party roots + apr/repox/sts itself. False positives are corrected by adding the missing root, not by relaxing the rule.
84
+
85
+ [0.1.0]: https://github.com/AbdullahBakir97/apr/compare/v0.0.2...v0.1.0
86
+
87
+ ---
88
+
89
+ ## [0.0.2] — 2026-05-08
90
+
91
+ ### Added
92
+ - **JS / TS rule pack** via tree-sitter (`src/apr/rules_js.py`):
93
+ - `console-log` (info, quality) — leftover `console.log` / `.debug` / `.info` calls. Skipped in obvious entry files (presence of `process.argv`, `.listen(`, or `import.meta.main`).
94
+ - `debugger-statement` (warning, quality) — `debugger;` left in code.
95
+ - `var-declaration` (info, style) — `var x = ...` instead of `let`/`const`.
96
+ - `todo-no-ticket` (info, todo) — TODO/FIXME/XXX/HACK without `#123` or `PROJ-123`. Mirror of the Python rule.
97
+ - **Additional Python rules** in `src/apr/rules.py`:
98
+ - `mutable-default-arg` (warning, quality) — `def f(x=[])` shares state across calls.
99
+ - `broad-except` (info, quality) — `except Exception:` is wider than most code needs.
100
+ - `assert-in-prod` (warning, security) — `assert` is stripped under `python -O`. Test files (`tests/`, `test_*.py`) are exempt.
101
+ - `hardcoded-secret` (critical, security) — high-precision regex pack catches AWS access keys (`AKIA*`), GitHub tokens (`ghp_*`, `ghs_*`), OpenAI keys (`sk-*`), Anthropic keys (`sk-ant-*`), Slack bot tokens (`xoxb-*`), and inline `password=`/`api_key=` literals.
102
+ - 13 new tests covering each rule end-to-end (some `pytest.importorskip` JS/TS rules when tree-sitter wheels aren't available on the platform).
103
+
104
+ ### Changed
105
+ - `apr.rules.check_file` now dispatches by extension: `.py` -> Python rules, `.js`/`.jsx`/`.mjs`/`.cjs`/`.ts`/`.tsx` -> tree-sitter rules, others -> empty.
106
+ - Tree-sitter (`tree-sitter>=0.23` + `tree-sitter-language-pack>=0.4,<1.0`) graduated to required deps.
107
+
108
+ ### Notes
109
+ - Schema unchanged (still **0.0.1**) — additive: same `Finding` shape, just more rule IDs.
110
+ - The hardcoded-secret rule is precision-tuned (high-confidence patterns only). False positives on `password = "x"` style literals are possible but accepted at info+ severity. False negatives on bespoke key formats are accepted; `apr` is meant to complement, not replace, dedicated secret scanners.
111
+
112
+ [0.0.2]: https://github.com/AbdullahBakir97/apr/compare/v0.0.1...v0.0.2
113
+
114
+ ---
115
+
116
+ ## [0.0.1] — 2026-05-08
117
+
118
+ ### Added
119
+ - Initial scaffold: `apr` Python package with two CLI commands (`review`, `version`).
120
+ - Pydantic v2 schema (versioned 0.0.1): `Finding`, `ReviewInputs`, `ReviewStats`, `ReviewReport`.
121
+ - `rules` module — deterministic rule pack for Python:
122
+ - `bare-except` (warning) — bare `except:` clause.
123
+ - `print-debug` (info) — leftover `print(...)`, skipped in `__main__` guard files.
124
+ - `todo-no-ticket` (info) — TODO/FIXME/XXX/HACK without `#123` / `PROJ-123` reference.
125
+ - `empty-function-body` (info, ai-pattern) — function body is only `pass`.
126
+ - `syntax-error` (error) — file does not parse.
127
+ - `rules.check_pr_metadata` — `pr-title-uninformative` (warning), `pr-description-too-short` (info).
128
+ - `engine` — orchestrator: PR-level checks first, then per-file rule packs; stably sorted findings; severity-bucket stats.
129
+ - `output` — JSON + Markdown writers emitting to `.apr/review.{json,md}`. The Markdown report includes a "Suggested fixes" block surfacing up to 10 suggestions with file:line.
130
+ - Workspace dep on `repox` (apr will use the architecture model for v0.0.2+ file-context rules).
131
+ - 16 smoke tests covering each rule, the engine, both writers, and the CLI.
132
+ - Apache-2.0 license, hatchling build, typer/rich/pydantic deps.
133
+
134
+ ### Notes
135
+ - Wave 2 lead bet of the DevTrust platform. Consolidates the patterns from three earlier GitHub Apps (`ai-quality-gate`, `pr-coach`, `commit-craft`) into one engine.
136
+ - v0.0.1 is **Python only by design**. JS/TS rules in v0.0.2.
137
+ - LLM-backed checks (`ai-review:*` rule IDs) land in v0.1+ once the deterministic rule output is stable enough to grade against.
138
+
139
+ [0.0.1]: https://github.com/AbdullahBakir97/apr/releases/tag/v0.0.1
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: devtrust-apr
3
+ Version: 0.2.0
4
+ Summary: Agent-PR Reviewer — deterministic + AI-pattern review for pull requests.
5
+ Project-URL: Homepage, https://github.com/AbdullahBakir97/DevTrust
6
+ Project-URL: Repository, https://github.com/AbdullahBakir97/DevTrust
7
+ Project-URL: Documentation, https://github.com/AbdullahBakir97/DevTrust/tree/main/src/products/03-agent-pr-reviewer/code#readme
8
+ Project-URL: Changelog, https://github.com/AbdullahBakir97/DevTrust/blob/main/src/products/03-agent-pr-reviewer/code/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/AbdullahBakir97/DevTrust/issues
10
+ Author: Abdullah Bakir
11
+ License-Expression: Apache-2.0
12
+ Keywords: ci,code-review,developer-tools,github-app,pr-review
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: Apache Software License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development :: Quality Assurance
24
+ Requires-Python: >=3.11
25
+ Requires-Dist: devtrust-repox
26
+ Requires-Dist: pydantic>=2.10.0
27
+ Requires-Dist: rich>=13.9.0
28
+ Requires-Dist: tree-sitter-language-pack<1.0,>=0.4.0
29
+ Requires-Dist: tree-sitter>=0.23.0
30
+ Requires-Dist: typer>=0.15.0
31
+ Provides-Extra: ai
32
+ Requires-Dist: anthropic>=0.40.0; extra == 'ai'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # Agent-PR Reviewer (`apr`)
36
+
37
+ > Deterministic + AI-pattern review for pull requests. Wave 2 lead bet of the DevTrust connected platform.
38
+
39
+ ## Status
40
+
41
+ **v0.2.0 beta** — deterministic Python + JS/TS rules, deterministic AI-pattern checker (`ai-review:hallucinated-symbol`) now multi-language via Repo X-ray v0.4 call edges, plus optional LLM-backed `ai-review:diff-comprehension` (Anthropic).
42
+
43
+ ## Why
44
+
45
+ Three of your existing GitHub Apps — `ai-quality-gate`, `pr-coach`, `commit-craft` — each solve part of "make PR review better" but ship as separate apps with separate auth, separate webhooks, separate UIs. `apr` consolidates them into one engine so:
46
+
47
+ - One install. One webhook. One sticky comment per PR.
48
+ - Deterministic rules first (testable, gradeable, cheap), LLM layer second.
49
+ - Reuses Repo X-ray's architecture model for file context.
50
+
51
+ ## Rules shipped through v0.0.2
52
+
53
+ ### Python (`.py`)
54
+
55
+ | Rule ID | Severity | Category | What it catches |
56
+ |---|---|---|---|
57
+ | `bare-except` | warning | quality | `except:` without an exception class |
58
+ | `print-debug` | info | quality | leftover `print(...)` calls (skipped in `__main__` guard files) |
59
+ | `todo-no-ticket` | info | todo | TODO/FIXME/XXX/HACK without `#123` / `PROJ-123` reference |
60
+ | `empty-function-body` | info | ai-pattern | function body is only `pass` |
61
+ | `syntax-error` | error | quality | file does not parse |
62
+ | `mutable-default-arg` | warning | quality | `def f(x=[])` shares state across calls |
63
+ | `broad-except` | info | quality | `except Exception:` is broader than most code needs |
64
+ | `assert-in-prod` | warning | security | `assert` is stripped under `python -O`. Test files exempt. |
65
+ | `hardcoded-secret` | critical | security | AWS / GitHub / OpenAI / Anthropic / Slack tokens, inline credential literals |
66
+
67
+ ### JavaScript / TypeScript (`.js .jsx .mjs .cjs .ts .tsx`) — v0.0.2+
68
+
69
+ | Rule ID | Severity | Category | What it catches |
70
+ |---|---|---|---|
71
+ | `console-log` | info | quality | leftover `console.log/debug/info`. Skipped in entry files (`process.argv`, `.listen(`, `import.meta.main`). |
72
+ | `debugger-statement` | warning | quality | `debugger;` left in code |
73
+ | `var-declaration` | info | style | `var` instead of `let`/`const` |
74
+ | `todo-no-ticket` | info | todo | mirror of the Python rule |
75
+
76
+ ### PR-level
77
+
78
+ | Rule ID | Severity | Category | What it catches |
79
+ |---|---|---|---|
80
+ | `pr-title-uninformative` | warning | commit | PR title too short or one of `wip`/`draft`/`tmp`/`test` |
81
+ | `pr-description-too-short` | info | commit | PR description under 30 characters |
82
+
83
+ ### AI-pattern (opt-in via `--enable-ai`) — v0.1.0+ / v0.2.0 multi-language
84
+
85
+ | Rule ID | Severity | Category | What it catches |
86
+ |---|---|---|---|
87
+ | `ai-review:hallucinated-symbol` | warning | ai-pattern | A function call whose name doesn't resolve to an in-repo symbol, an imported alias, or a known stdlib / global / popular package root. **Now spans Python + JS/TS as of v0.2.0.** Requires `.repox/architecture.json` (run `repox` first). |
88
+ | `ai-review:diff-comprehension` | warning/info | ai-pattern | LLM-backed check for "does the PR description accurately describe the diff?". Pass `--ai-provider anthropic` and set `ANTHROPIC_API_KEY`. |
89
+
90
+ ## CLI
91
+
92
+ ```bash
93
+ apr version
94
+ apr review --repo .
95
+ apr review --repo . --changed src/foo.py --changed src/bar.py
96
+ apr review --repo . --title "Fix nullable fields" --description "..."
97
+ ```
98
+
99
+ Output: `.apr/review.json` (schema-versioned, machine-readable) + `.apr/review.md` (human companion).
100
+
101
+ ## Roadmap
102
+
103
+ - ✅ **v0.0.2** — JS/TS rule pack (`console-log`, `debugger-statement`, `var-declaration`, `todo-no-ticket`).
104
+ - ✅ **v0.1.0** — AI rule pack: deterministic `ai-review:hallucinated-symbol` + LLM-pluggable `ai-review:diff-comprehension`.
105
+ - ✅ **v0.1.1** — real Anthropic backend for `ai-review:diff-comprehension`.
106
+ - ✅ **v0.2.0** — `ai-review:hallucinated-symbol` extended to JS/TS via repox v0.4 call edges. APR now covers all three Wave-1 languages.
107
+ - **v0.3.0** (next) — auto-suggest fixes via the GitHub Suggested Changes API; per-import `local_names` for renamed JS imports.
108
+
109
+ ## Status
110
+
111
+ Apache-2.0. See [CHANGELOG](CHANGELOG.md).
@@ -0,0 +1,77 @@
1
+ # Agent-PR Reviewer (`apr`)
2
+
3
+ > Deterministic + AI-pattern review for pull requests. Wave 2 lead bet of the DevTrust connected platform.
4
+
5
+ ## Status
6
+
7
+ **v0.2.0 beta** — deterministic Python + JS/TS rules, deterministic AI-pattern checker (`ai-review:hallucinated-symbol`) now multi-language via Repo X-ray v0.4 call edges, plus optional LLM-backed `ai-review:diff-comprehension` (Anthropic).
8
+
9
+ ## Why
10
+
11
+ Three of your existing GitHub Apps — `ai-quality-gate`, `pr-coach`, `commit-craft` — each solve part of "make PR review better" but ship as separate apps with separate auth, separate webhooks, separate UIs. `apr` consolidates them into one engine so:
12
+
13
+ - One install. One webhook. One sticky comment per PR.
14
+ - Deterministic rules first (testable, gradeable, cheap), LLM layer second.
15
+ - Reuses Repo X-ray's architecture model for file context.
16
+
17
+ ## Rules shipped through v0.0.2
18
+
19
+ ### Python (`.py`)
20
+
21
+ | Rule ID | Severity | Category | What it catches |
22
+ |---|---|---|---|
23
+ | `bare-except` | warning | quality | `except:` without an exception class |
24
+ | `print-debug` | info | quality | leftover `print(...)` calls (skipped in `__main__` guard files) |
25
+ | `todo-no-ticket` | info | todo | TODO/FIXME/XXX/HACK without `#123` / `PROJ-123` reference |
26
+ | `empty-function-body` | info | ai-pattern | function body is only `pass` |
27
+ | `syntax-error` | error | quality | file does not parse |
28
+ | `mutable-default-arg` | warning | quality | `def f(x=[])` shares state across calls |
29
+ | `broad-except` | info | quality | `except Exception:` is broader than most code needs |
30
+ | `assert-in-prod` | warning | security | `assert` is stripped under `python -O`. Test files exempt. |
31
+ | `hardcoded-secret` | critical | security | AWS / GitHub / OpenAI / Anthropic / Slack tokens, inline credential literals |
32
+
33
+ ### JavaScript / TypeScript (`.js .jsx .mjs .cjs .ts .tsx`) — v0.0.2+
34
+
35
+ | Rule ID | Severity | Category | What it catches |
36
+ |---|---|---|---|
37
+ | `console-log` | info | quality | leftover `console.log/debug/info`. Skipped in entry files (`process.argv`, `.listen(`, `import.meta.main`). |
38
+ | `debugger-statement` | warning | quality | `debugger;` left in code |
39
+ | `var-declaration` | info | style | `var` instead of `let`/`const` |
40
+ | `todo-no-ticket` | info | todo | mirror of the Python rule |
41
+
42
+ ### PR-level
43
+
44
+ | Rule ID | Severity | Category | What it catches |
45
+ |---|---|---|---|
46
+ | `pr-title-uninformative` | warning | commit | PR title too short or one of `wip`/`draft`/`tmp`/`test` |
47
+ | `pr-description-too-short` | info | commit | PR description under 30 characters |
48
+
49
+ ### AI-pattern (opt-in via `--enable-ai`) — v0.1.0+ / v0.2.0 multi-language
50
+
51
+ | Rule ID | Severity | Category | What it catches |
52
+ |---|---|---|---|
53
+ | `ai-review:hallucinated-symbol` | warning | ai-pattern | A function call whose name doesn't resolve to an in-repo symbol, an imported alias, or a known stdlib / global / popular package root. **Now spans Python + JS/TS as of v0.2.0.** Requires `.repox/architecture.json` (run `repox` first). |
54
+ | `ai-review:diff-comprehension` | warning/info | ai-pattern | LLM-backed check for "does the PR description accurately describe the diff?". Pass `--ai-provider anthropic` and set `ANTHROPIC_API_KEY`. |
55
+
56
+ ## CLI
57
+
58
+ ```bash
59
+ apr version
60
+ apr review --repo .
61
+ apr review --repo . --changed src/foo.py --changed src/bar.py
62
+ apr review --repo . --title "Fix nullable fields" --description "..."
63
+ ```
64
+
65
+ Output: `.apr/review.json` (schema-versioned, machine-readable) + `.apr/review.md` (human companion).
66
+
67
+ ## Roadmap
68
+
69
+ - ✅ **v0.0.2** — JS/TS rule pack (`console-log`, `debugger-statement`, `var-declaration`, `todo-no-ticket`).
70
+ - ✅ **v0.1.0** — AI rule pack: deterministic `ai-review:hallucinated-symbol` + LLM-pluggable `ai-review:diff-comprehension`.
71
+ - ✅ **v0.1.1** — real Anthropic backend for `ai-review:diff-comprehension`.
72
+ - ✅ **v0.2.0** — `ai-review:hallucinated-symbol` extended to JS/TS via repox v0.4 call edges. APR now covers all three Wave-1 languages.
73
+ - **v0.3.0** (next) — auto-suggest fixes via the GitHub Suggested Changes API; per-import `local_names` for renamed JS imports.
74
+
75
+ ## Status
76
+
77
+ Apache-2.0. See [CHANGELOG](CHANGELOG.md).
@@ -0,0 +1,63 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ # `apr` collides with the Apache Portable Runtime on PyPI; namespaced
7
+ # to avoid conflict. Module name stays `apr`.
8
+ name = "devtrust-apr"
9
+ version = "0.2.0"
10
+ description = "Agent-PR Reviewer — deterministic + AI-pattern review for pull requests."
11
+ readme = "README.md"
12
+ requires-python = ">=3.11"
13
+ license = "Apache-2.0"
14
+ authors = [{ name = "Abdullah Bakir" }]
15
+ keywords = ["ci", "code-review", "developer-tools", "github-app", "pr-review"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: Apache Software License",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Topic :: Software Development :: Quality Assurance",
28
+ ]
29
+
30
+ dependencies = [
31
+ "typer>=0.15.0",
32
+ "rich>=13.9.0",
33
+ "pydantic>=2.10.0",
34
+ # apr consumes Repo X-ray's architecture model for file context.
35
+ "devtrust-repox",
36
+ # JS / TS rule pack via tree-sitter (mirrors repox's pin).
37
+ "tree-sitter>=0.23.0",
38
+ "tree-sitter-language-pack>=0.4.0,<1.0",
39
+ ]
40
+
41
+ [project.optional-dependencies]
42
+ # Real LLM-backed rules (ai-review:diff-comprehension). The
43
+ # deterministic ai-review:hallucinated-symbol rule does NOT need
44
+ # this extra - it works from the repox artifact alone.
45
+ ai = [
46
+ "anthropic>=0.40.0",
47
+ ]
48
+
49
+ [project.scripts]
50
+ apr = "apr.cli:app"
51
+
52
+ [project.urls]
53
+ Homepage = "https://github.com/AbdullahBakir97/DevTrust"
54
+ Repository = "https://github.com/AbdullahBakir97/DevTrust"
55
+ Documentation = "https://github.com/AbdullahBakir97/DevTrust/tree/main/src/products/03-agent-pr-reviewer/code#readme"
56
+ Changelog = "https://github.com/AbdullahBakir97/DevTrust/blob/main/src/products/03-agent-pr-reviewer/code/CHANGELOG.md"
57
+ Issues = "https://github.com/AbdullahBakir97/DevTrust/issues"
58
+
59
+ [tool.hatch.build.targets.wheel]
60
+ packages = ["src/apr"]
61
+
62
+ [tool.uv.sources]
63
+ devtrust-repox = { workspace = true }
@@ -0,0 +1,14 @@
1
+ """Agent-PR Reviewer (`apr`) - the Wave 2 lead bet of the DevTrust platform.
2
+
3
+ Consolidates the patterns from three existing GitHub Apps into one
4
+ deterministic, fast, AI-pattern-aware PR reviewer:
5
+
6
+ - ai-quality-gate -> AI-likelihood + verbose-pattern detection
7
+ - pr-coach -> coaching feedback (description quality, TODOs)
8
+ - commit-craft -> commit-message review and normalization
9
+
10
+ v0.0.1 ships the deterministic rule layer; LLM-backed review is layered
11
+ on top in v0.1+ once the rule output is stable enough to grade against.
12
+ """
13
+
14
+ __version__ = "0.2.0"
@@ -0,0 +1,6 @@
1
+ """Enables `python -m apr ...`."""
2
+
3
+ from apr.cli import app
4
+
5
+ if __name__ == "__main__":
6
+ app()
@@ -0,0 +1,150 @@
1
+ """Agent-PR Reviewer command-line interface.
2
+
3
+ apr version
4
+ apr review [--repo PATH] [--changed FILE ...] [--title S] [--description S]
5
+ [--diff PATH] [--enable-ai] [--ai-provider null|anthropic]
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from pathlib import Path
12
+ from typing import Annotated
13
+
14
+ import typer
15
+ from rich.console import Console
16
+ from rich.table import Table
17
+
18
+ from apr import __version__
19
+ from apr.engine import review as review_engine
20
+ from apr.llm import LLMProvider, build_provider
21
+ from apr.output import write_json, write_markdown
22
+
23
+ app = typer.Typer(
24
+ name="apr",
25
+ help="Agent-PR Reviewer - deterministic + AI-pattern review for PRs.",
26
+ no_args_is_help=True,
27
+ add_completion=False,
28
+ )
29
+
30
+ console = Console()
31
+
32
+
33
+ @app.command()
34
+ def version() -> None:
35
+ """Print the installed Agent-PR Reviewer version."""
36
+ console.print(f"apr [bold]v{__version__}[/bold]")
37
+
38
+
39
+ @app.command()
40
+ def review(
41
+ repo: Annotated[
42
+ Path,
43
+ typer.Option("--repo", "-r", help="Repo to review. Defaults to current directory."),
44
+ ] = Path("."),
45
+ changed: Annotated[
46
+ list[str] | None,
47
+ typer.Option(
48
+ "--changed",
49
+ "-c",
50
+ help="Path(s) that changed. Repeat for multiple files.",
51
+ ),
52
+ ] = None,
53
+ title: Annotated[
54
+ str | None,
55
+ typer.Option("--title", "-t", help="The PR title (for metadata checks)."),
56
+ ] = None,
57
+ description: Annotated[
58
+ str | None,
59
+ typer.Option("--description", "-d", help="The PR description / body."),
60
+ ] = None,
61
+ diff_path: Annotated[
62
+ Path | None,
63
+ typer.Option(
64
+ "--diff",
65
+ help=(
66
+ "Path to a unified-diff file. Required for the "
67
+ "ai-review:diff-comprehension rule when --enable-ai."
68
+ ),
69
+ ),
70
+ ] = None,
71
+ enable_ai: Annotated[
72
+ bool,
73
+ typer.Option(
74
+ "--enable-ai/--no-enable-ai",
75
+ help=(
76
+ "Run the ai-review:* rule pack. Off by default. "
77
+ "ai-review:hallucinated-symbol needs a "
78
+ ".repox/architecture.json (run `repox build .` first)."
79
+ ),
80
+ ),
81
+ ] = False,
82
+ ai_provider: Annotated[
83
+ str,
84
+ typer.Option(
85
+ "--ai-provider",
86
+ help="LLM backend: 'null' (default, no calls) or 'anthropic'.",
87
+ ),
88
+ ] = "null",
89
+ quiet: Annotated[
90
+ bool,
91
+ typer.Option("--quiet", "-q", help="Suppress non-essential output."),
92
+ ] = False,
93
+ ) -> None:
94
+ """Run the review and emit `.apr/review.{json,md}`."""
95
+ if not repo.exists() or not repo.is_dir():
96
+ console.print(f"[red]Error:[/red] not a directory: {repo}")
97
+ raise typer.Exit(code=2)
98
+
99
+ repo = repo.resolve()
100
+ files = list(changed or [])
101
+
102
+ diff_text: str | None = None
103
+ if diff_path is not None:
104
+ try:
105
+ diff_text = diff_path.read_text(encoding="utf-8", errors="replace")
106
+ except OSError as exc:
107
+ console.print(f"[red]Error:[/red] cannot read diff: {exc}")
108
+ raise typer.Exit(code=2) from exc
109
+
110
+ provider: LLMProvider | None = None
111
+ if enable_ai:
112
+ # Anthropic API key from the conventional env var.
113
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
114
+ provider = build_provider(ai_provider, api_key)
115
+
116
+ if not quiet:
117
+ console.print(f"[bold]Reviewing[/bold] {repo}")
118
+ console.print(f"[dim]Changed files:[/dim] {len(files)}")
119
+ if enable_ai:
120
+ console.print(f"[dim]AI rules:[/dim] enabled (provider: {ai_provider})")
121
+
122
+ report = review_engine(
123
+ repo,
124
+ files,
125
+ pr_title=title,
126
+ pr_description=description,
127
+ enable_ai=enable_ai,
128
+ llm_provider=provider,
129
+ diff=diff_text,
130
+ )
131
+ json_path = write_json(report, repo)
132
+ md_path = write_markdown(report, repo)
133
+
134
+ if quiet:
135
+ return
136
+
137
+ s = report.stats
138
+ table = Table(title="\nReview summary", show_header=True, header_style="bold")
139
+ table.add_column("Severity", style="cyan")
140
+ table.add_column("Count", justify="right")
141
+ table.add_row("info", str(s.info))
142
+ table.add_row("warning", str(s.warning))
143
+ table.add_row("error", str(s.error))
144
+ table.add_row("critical", str(s.critical))
145
+ table.add_row("[bold]total[/bold]", f"[bold]{s.total}[/bold]")
146
+ console.print(table)
147
+ if s.blocking > 0:
148
+ console.print(f"[red]Blocking findings:[/red] {s.blocking} (error + critical)")
149
+ console.print(f"\n[green]✓[/green] wrote [bold]{json_path}[/bold]")
150
+ console.print(f"[green]✓[/green] wrote [bold]{md_path}[/bold]")