aeview 0.0.1__py3-none-any.whl
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.
- aeview/__init__.py +6 -0
- aeview/_data/DEDUPLICATION.md +26 -0
- aeview/_data/REVIEWER.md +51 -0
- aeview/_data/settings.json +8 -0
- aeview/activate.py +58 -0
- aeview/bundle.py +61 -0
- aeview/cli.py +897 -0
- aeview/config.py +126 -0
- aeview/dedup.py +119 -0
- aeview/doctor.py +86 -0
- aeview/fanout.py +133 -0
- aeview/harness/__init__.py +3 -0
- aeview/harness/base.py +165 -0
- aeview/harness/claude_code.py +271 -0
- aeview/harness/codex.py +350 -0
- aeview/harness/copilot.py +552 -0
- aeview/harness/eventlog.py +101 -0
- aeview/ignore.py +174 -0
- aeview/merge.py +251 -0
- aeview/process.py +120 -0
- aeview/prompt.py +63 -0
- aeview/report.py +87 -0
- aeview/resolve.py +262 -0
- aeview/runstore.py +337 -0
- aeview/schema.py +280 -0
- aeview/scope.py +462 -0
- aeview-0.0.1.dist-info/METADATA +397 -0
- aeview-0.0.1.dist-info/RECORD +31 -0
- aeview-0.0.1.dist-info/WHEEL +4 -0
- aeview-0.0.1.dist-info/entry_points.txt +3 -0
- aeview-0.0.1.dist-info/licenses/LICENSE +21 -0
aeview/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deduplication
|
|
3
|
+
description: Judges which findings from different reviews describe the same underlying issue. Precision over recall.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are given a flat list of findings collected from several independent code reviews.
|
|
7
|
+
Each finding has a stable `id`. Decide which findings describe **the same underlying
|
|
8
|
+
issue** — the same root cause at the same location — and group them.
|
|
9
|
+
|
|
10
|
+
## Rules
|
|
11
|
+
|
|
12
|
+
- **Precision over recall.** Only group findings you are confident are the same issue.
|
|
13
|
+
When in doubt, leave them separate. A wrongly-merged pair hides a real, distinct
|
|
14
|
+
problem; a wrongly-split pair only shows the user two views of one issue. The second
|
|
15
|
+
mistake is far cheaper.
|
|
16
|
+
- "Same issue" means same root cause and substantially the same location — not merely
|
|
17
|
+
the same file, the same category, or a similar severity.
|
|
18
|
+
- Do not rewrite, re-score, or summarize findings. You only group.
|
|
19
|
+
- A finding that matches nothing else forms a group of one (or is simply omitted from
|
|
20
|
+
the output — aeview treats any unlisted finding as its own group).
|
|
21
|
+
|
|
22
|
+
## Output
|
|
23
|
+
|
|
24
|
+
Return groups of duplicate ids. For each group, name one `survivor` (the clearest,
|
|
25
|
+
best-located, highest-severity statement of the issue) and list the other ids as
|
|
26
|
+
`duplicates`. Findings not mentioned are kept as-is.
|
aeview/_data/REVIEWER.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: default
|
|
3
|
+
description: Adversarial general-purpose code reviewer. The fallback reviewer used when no repo-local reviewer is selected.
|
|
4
|
+
harnesses:
|
|
5
|
+
- { harness: claude-code, model: claude-opus-4-8 }
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are an adversarial code reviewer. Your job is to find the problems in a change
|
|
9
|
+
before it ships — not to praise it. Approve only when you genuinely cannot break it.
|
|
10
|
+
|
|
11
|
+
## Stance
|
|
12
|
+
|
|
13
|
+
- Assume the change is wrong until the diff proves otherwise. Confidence is earned.
|
|
14
|
+
- Hunt for the failure that ships to production: the unhandled input, the race, the
|
|
15
|
+
off-by-one, the silently-swallowed error, the security hole, the missing test.
|
|
16
|
+
- Prefer a small number of high-signal findings over a long list of nitpicks. Every
|
|
17
|
+
finding must be something a maintainer would want to fix before merge.
|
|
18
|
+
|
|
19
|
+
## Attack surface
|
|
20
|
+
|
|
21
|
+
Work through, in order, and stop chasing anything the diff does not touch:
|
|
22
|
+
|
|
23
|
+
1. **Correctness** — logic errors, wrong conditions, boundary/empty/null cases,
|
|
24
|
+
incorrect assumptions about inputs or return values.
|
|
25
|
+
2. **Security** — injection, auth/authorization gaps, unsafe deserialization, secrets,
|
|
26
|
+
path traversal, unvalidated external input crossing a trust boundary.
|
|
27
|
+
3. **Regression** — behavior this change breaks for existing callers or data.
|
|
28
|
+
4. **Resource & concurrency** — leaks, unbounded growth, deadlocks, races, ordering.
|
|
29
|
+
5. **Test gaps** — new logic with no test, or a bug a reasonable test would have caught.
|
|
30
|
+
6. **Maintainability** — only when it rises to a real future-bug risk, not style.
|
|
31
|
+
|
|
32
|
+
## Calibration
|
|
33
|
+
|
|
34
|
+
- `critical`: data loss, security breach, crash on a common path, corruption.
|
|
35
|
+
- `high`: wrong result or broken behavior on a realistic path.
|
|
36
|
+
- `medium`: edge-case bug, missing test for risky logic, latent foot-gun.
|
|
37
|
+
- `low`: minor correctness or clarity issue worth fixing but not blocking.
|
|
38
|
+
|
|
39
|
+
Set `confidence` honestly: 1.0 means you can point at the exact line and explain the
|
|
40
|
+
failing input; lower it when you are inferring.
|
|
41
|
+
|
|
42
|
+
## Grounding
|
|
43
|
+
|
|
44
|
+
- Every finding cites a real file and line range from the change under review.
|
|
45
|
+
- `recommendation` is a concrete, specific fix — not "consider reviewing this".
|
|
46
|
+
- Do not invent code that is not in the diff. If you need surrounding context, read it.
|
|
47
|
+
|
|
48
|
+
## Verdict
|
|
49
|
+
|
|
50
|
+
- `needs-attention` if there is at least one finding a maintainer should act on.
|
|
51
|
+
- `approve` only when the change is correct, safe, and adequately tested.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fallbackReviewerHarnesses": [
|
|
3
|
+
{ "harness": "claude-code", "model": "claude-opus-4-8" }
|
|
4
|
+
],
|
|
5
|
+
"deduplicationHarness": { "harness": "claude-code", "model": "claude-opus-4-8" },
|
|
6
|
+
"retention": { "keepLast": 20, "ttlDays": 14 },
|
|
7
|
+
"reviewTimeoutSeconds": 1200
|
|
8
|
+
}
|
aeview/activate.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Auto mode: pick reviewers whose `auto-activate-paths` match the changed files.
|
|
2
|
+
|
|
3
|
+
A bare `aeview run` (no --reviewers) runs the `default` reviewer plus any reviewer whose
|
|
4
|
+
frontmatter `auto-activate-paths` globs match a file in the (already .aeviewignore-filtered)
|
|
5
|
+
diff. Matching is literal-glob via `PurePath.full_match`, NOT the gitignore engine `.aeviewignore`
|
|
6
|
+
uses: a single `*` does not cross a `/`, `**` crosses directory boundaries, there is no negation,
|
|
7
|
+
and a bare directory name matches nothing (write `dir/**`). Case-sensitive, so a reviewer
|
|
8
|
+
activates identically on macOS and Linux rather than following the platform default.
|
|
9
|
+
|
|
10
|
+
Each reviewer is anchored at its scope path (the parent of its `.aeview` dir): a changed file is
|
|
11
|
+
considered only if it lives under that path, and is matched relative to it. There is no clamp — a
|
|
12
|
+
home reviewer (`~/.aeview/reviewers/...`) anchored at `~` only reaches repos under home, and needs
|
|
13
|
+
`**/...` globs to match inside them. Discovery is upward-only (cwd->home), so a reviewer in a
|
|
14
|
+
subdirectory below cwd is never consulted.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from .ignore import changed_paths
|
|
22
|
+
from .resolve import (
|
|
23
|
+
REVIEWER_FILE,
|
|
24
|
+
ResolveError,
|
|
25
|
+
discover_reviewer_sources,
|
|
26
|
+
parse_reviewer,
|
|
27
|
+
reviewer_scope_path,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def select_auto_reviewers(cwd: Path, root: Path, diff: str) -> list[str]:
|
|
32
|
+
"""Reviewer names auto-activated by the diff, in nearest-first discovery order. `root` is the
|
|
33
|
+
repo top-level that makes the repo-root-relative diff paths absolute; `diff` is already
|
|
34
|
+
.aeviewignore-filtered, so an ignored file never triggers activation. `default` runs
|
|
35
|
+
unconditionally and is added by the caller, not here."""
|
|
36
|
+
abs_changed = [root / rel for rel in changed_paths(diff)]
|
|
37
|
+
activated: list[str] = []
|
|
38
|
+
for disc in discover_reviewer_sources(cwd):
|
|
39
|
+
try:
|
|
40
|
+
front, _ = parse_reviewer(disc.source / REVIEWER_FILE)
|
|
41
|
+
except ResolveError:
|
|
42
|
+
continue # unparseable frontmatter can't declare globs, so it never auto-activates
|
|
43
|
+
globs = front.auto_activate_paths
|
|
44
|
+
if globs and _any_match(abs_changed, reviewer_scope_path(disc.source), globs):
|
|
45
|
+
activated.append(disc.name)
|
|
46
|
+
return activated
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _any_match(abs_paths: list[Path], scope_path: Path, globs: list[str]) -> bool:
|
|
50
|
+
"""True if any changed file under `scope_path` full-matches any glob (relative to scope_path).
|
|
51
|
+
Literal-glob via PurePath.full_match: `*` stops at `/`, `**` crosses, case-sensitive."""
|
|
52
|
+
for abs_path in abs_paths:
|
|
53
|
+
if not abs_path.is_relative_to(scope_path):
|
|
54
|
+
continue # outside this reviewer's tree -> never its concern (no clamp the other way)
|
|
55
|
+
rel = abs_path.relative_to(scope_path)
|
|
56
|
+
if any(rel.full_match(glob, case_sensitive=True) for glob in globs):
|
|
57
|
+
return True
|
|
58
|
+
return False
|
aeview/bundle.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Adaptive bundling: measure the diff, then inline or self-collect.
|
|
2
|
+
|
|
3
|
+
- diff <= 256 KB -> inline: the frozen diff is embedded in the prompt, so every
|
|
4
|
+
review of the same change sees byte-identical input.
|
|
5
|
+
- diff > 256 KB -> self-collect: the prompt carries a stat summary + the path to
|
|
6
|
+
the frozen diff (which the harness reads selectively) and the
|
|
7
|
+
read-only git command to inspect the live tree — keeping the
|
|
8
|
+
prompt small on huge changes.
|
|
9
|
+
- patch scope -> always inline (there is no git to self-collect from).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
|
|
16
|
+
from .schema import ScopeSpec
|
|
17
|
+
from .scope import ResolvedScope
|
|
18
|
+
|
|
19
|
+
INLINE_MAX_BYTES = 256 * 1024
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(slots=True)
|
|
23
|
+
class Bundle:
|
|
24
|
+
mode: str # "inline" | "self-collect"
|
|
25
|
+
scope: ScopeSpec
|
|
26
|
+
diff: str
|
|
27
|
+
summary: str
|
|
28
|
+
inspect: list[str] = field(default_factory=list)
|
|
29
|
+
commits: str = ""
|
|
30
|
+
diff_bytes: int = 0
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def is_empty(self) -> bool:
|
|
34
|
+
return not self.diff.strip()
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def is_inline(self) -> bool:
|
|
38
|
+
return self.mode == "inline"
|
|
39
|
+
|
|
40
|
+
def manifest(self) -> dict:
|
|
41
|
+
return {
|
|
42
|
+
"mode": self.mode,
|
|
43
|
+
"scope": self.scope.model_dump(),
|
|
44
|
+
"diff_bytes": self.diff_bytes,
|
|
45
|
+
"summary": self.summary,
|
|
46
|
+
"inspect": self.inspect,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build_bundle(resolved: ResolvedScope) -> Bundle:
|
|
51
|
+
diff_bytes = len(resolved.diff.encode("utf-8"))
|
|
52
|
+
inline = resolved.inline_only or diff_bytes <= INLINE_MAX_BYTES
|
|
53
|
+
return Bundle(
|
|
54
|
+
mode="inline" if inline else "self-collect",
|
|
55
|
+
scope=resolved.spec,
|
|
56
|
+
diff=resolved.diff,
|
|
57
|
+
summary=resolved.summary,
|
|
58
|
+
inspect=resolved.inspect,
|
|
59
|
+
commits=resolved.commits,
|
|
60
|
+
diff_bytes=diff_bytes,
|
|
61
|
+
)
|