@aarushpandey/gitagent 1.0.0

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 (36) hide show
  1. package/CONTRIBUTING.md +104 -0
  2. package/LICENSE +21 -0
  3. package/README.md +570 -0
  4. package/TESTING.md +290 -0
  5. package/action.yml +113 -0
  6. package/examples/README.md +124 -0
  7. package/examples/sample-audit-trail-issue-4.md +112 -0
  8. package/examples/sample-review-tqec-pr894-v1-raw-flawed.md +71 -0
  9. package/examples/sample-review-tqec-pr894-v2-raw.md +48 -0
  10. package/examples/sample-review-tqec-pr894-v3-curated.md +118 -0
  11. package/examples/verify-marker-precedence/README.md +97 -0
  12. package/examples/verify-marker-precedence/conftest.py +15 -0
  13. package/examples/verify-marker-precedence/pyproject.toml +8 -0
  14. package/examples/verify-marker-precedence/test_marker_precedence.py +56 -0
  15. package/examples/verify-marker-precedence/verify_precedence.py +67 -0
  16. package/examples/workflows/issue-fix.yml +32 -0
  17. package/examples/workflows/pr-review.yml +34 -0
  18. package/package.json +75 -0
  19. package/scripts/verify.js +478 -0
  20. package/src/agents/agentLoop.js +176 -0
  21. package/src/agents/engineeringAgent.js +51 -0
  22. package/src/agents/reviewCopilot.js +79 -0
  23. package/src/agents/tools.js +486 -0
  24. package/src/cli/output.js +137 -0
  25. package/src/config.js +22 -0
  26. package/src/mapper/fileRelevance.js +113 -0
  27. package/src/mapper/repoMap.js +105 -0
  28. package/src/orchestrator.js +336 -0
  29. package/src/pipeline.js +985 -0
  30. package/src/prompts/engineering.js +189 -0
  31. package/src/prompts/review.js +149 -0
  32. package/src/utils/cost.js +47 -0
  33. package/src/utils/diffLines.js +67 -0
  34. package/src/utils/githubUrl.js +8 -0
  35. package/src/web/public/index.html +128 -0
  36. package/src/web/server.js +51 -0
@@ -0,0 +1,71 @@
1
+ # ⚠️ FLAWED OUTPUT — kept as a teaching artifact
2
+
3
+ This file is the **raw output of an earlier version of the review copilot
4
+ prompt**. Multiple findings turned out to be factually wrong when verified
5
+ against pytest and pytest-timeout source.
6
+
7
+ **Do not use this as a model of the tool's current behavior.** It exists in
8
+ the repo to document:
9
+ - what hallucination from a code-review LLM actually looks like in practice;
10
+ - why we then hardened the system prompt (see `src/prompts/review.js` rule set
11
+ starting "Anti-hallucination rules");
12
+ - why the maintainer-facing workflow includes a manual curation step.
13
+
14
+ ## What was wrong with this v1 review (verified against source)
15
+
16
+ | Original claim | Reality |
17
+ |---|---|
18
+ | "Silent no-op if `pytest-timeout` is not installed" | `tqec/pyproject.toml` pins `pytest-timeout>=2.4.0`. The plugin is always installed. The reviewer didn't have `pyproject.toml` in context — fixed in v2 by also fetching dependency manifests. |
19
+ | "If `pytest-timeout` iterates in reverse … slow tests could silently receive the wrong timeout" | Per pytest source, `get_closest_marker` returns the first marker yielded by `iter_markers`. For function-level markers in `own_markers`, that's the first one added. The behavior is deterministic, not version-dependent. |
20
+ | "Tests with an explicit `@pytest.mark.timeout(N)` will have their timeout overridden by the conftest" | **Backwards.** Because the conftest *appends* (`add_marker(append=True)`) and `get_closest_marker` returns the *first* matching marker, an existing decorator wins. The conftest's marker is silently ignored when an explicit one exists. |
21
+ | "If CI passes `--timeout=X` on the command line, that takes precedence over marker-based timeouts" | **Wrong direction.** Per [pytest-timeout docs](https://pypi.org/project/pytest-timeout/) the documented precedence (low → high) is: config → env var → CLI `--timeout` → marker. Markers outrank CLI. |
22
+
23
+ The improved-prompt output is in `sample-review-tqec-pr894-v2-raw.md`. The
24
+ human-curated final reviewer notes are in `sample-review-tqec-pr894-v3-curated.md`.
25
+
26
+ ---
27
+
28
+ # Original v1 raw output (DO NOT TRUST)
29
+
30
+ ## 1. Bug Risk
31
+
32
+ **Marker ordering / override risk (`tests/conftest.py:9-10`)**
33
+
34
+ `item.add_marker(pytest.mark.timeout(SLOW_TEST_TIMEOUT))` appends the marker. When `pytest-timeout` resolves the effective timeout for a test item it looks for the *first* matching marker (highest priority). If a specific test already carries its own `@pytest.mark.timeout(N)` annotation, the conftest-added marker will be appended *after* that one, which is actually correct behavior for most pytest-timeout versions (first marker wins). However, if `pytest-timeout` iterates in reverse or any future version changes precedence, slow tests could silently receive the wrong timeout. There is no guard against double-application.
35
+
36
+ **No `append` kwarg specified (`tests/conftest.py:10`)**
37
+
38
+ `add_marker` defaults to `append=True` in modern pytest, but making this explicit (`item.add_marker(..., append=True)`) documents intent and prevents a surprise if the default ever changes.
39
+
40
+ **Silent no-op if `pytest-timeout` is not installed (`tests/conftest.py:10`)**
41
+
42
+ The hook happily adds a `timeout` marker regardless of whether `pytest-timeout` is installed. If the plugin is absent, the marker is ignored without warning. There is no `pytest.importorskip` or requirements check to surface this misconfiguration.
43
+
44
+ **Drift from issue intent**: The issue says the *default* timeout is 30 s. That default appears to come from a `pyproject.toml`/`pytest.ini` `timeout =` setting (not visible in this diff). The conftest only increases timeouts for slow tests; it does not verify or document what the baseline is. If that baseline setting is ever removed, slow tests would get 300 s but fast tests would get *no* timeout at all — silently.
45
+
46
+ ## 2. Edge Cases
47
+
48
+ - **Test already marked with `@pytest.mark.timeout`**: A developer who decorates a slow test with a custom timeout (e.g., 600 s for a particularly heavy test) will now have *two* timeout markers. Behavior depends entirely on `pytest-timeout` internals (which marker takes precedence). The conftest should check for an existing `timeout` marker before adding one.
49
+ - **Nested/parametrized slow tests**: `get_closest_marker("slow")` works correctly for parametrized items and inherited class-level markers, so this is low risk, but worth confirming if the test suite uses class-based tests.
50
+ - **`SLOW_TEST_TIMEOUT = 300` hardcoded**: 300 s (5 min) may be too short for some future slow tests and too long for others. Consider making this configurable via a `pytest.ini` option or environment variable.
51
+ - **`--timeout` CLI override interaction**: If CI passes `--timeout=X` on the command line, that takes precedence over marker-based timeouts in some `pytest-timeout` versions, making the conftest logic a no-op in those environments without warning.
52
+
53
+ ## 3. Test Coverage
54
+
55
+ There is no test verifying that the hook correctly applies the marker. Conftest hooks are notoriously easy to accidentally break (e.g., a rename of `get_closest_marker` in a future pytest version). A meta-test (e.g., using `pytester`/`testdir` fixtures) that:
56
+
57
+ 1. Defines a dummy slow test,
58
+ 2. Runs collection via the conftest hook,
59
+ 3. Asserts the `timeout` marker is present with value `300`,
60
+
61
+ would lock in the intended behavior. The absence of such a test means a silent regression is possible.
62
+
63
+ ## 4. Scope Creep
64
+
65
+ None detected. The diff is minimal and directly addresses the stated issue. Only `tests/conftest.py` is touched.
66
+
67
+ ## 5. Verdict
68
+
69
+ **NEEDS_DISCUSSION**
70
+
71
+ The implementation is directionally correct but has two issues worth resolving before merge: (1) there is no guard against double-application when a slow test already carries an explicit `timeout` marker, which can produce surprising behavior depending on `pytest-timeout` version; and (2) the interaction with the global `timeout` baseline (presumably set elsewhere) is undocumented and fragile — removing that baseline setting would silently disable timeouts for all non-slow tests. These are not outright blockers but represent enough ambiguity in intended behavior that a brief discussion (or at minimum inline comments documenting the assumptions) is warranted before approval.
@@ -0,0 +1,48 @@
1
+ # Raw output — improved-prompt v2
2
+
3
+ This is the unedited output of the review copilot **after** the prompt was
4
+ hardened with explicit anti-hallucination rules and the pipeline was changed
5
+ to also fetch dependency manifests (`pyproject.toml`, `package.json`, …)
6
+ into review context.
7
+
8
+ Compare against `sample-review-tqec-pr894-v1-raw-flawed.md` to see what the
9
+ prompt-level fixes change in practice. Notice this version:
10
+
11
+ - Does **not** claim `pytest-timeout` might be missing (it now sees `pyproject.toml` and could verify).
12
+ - Does **not** assert pytest-timeout precedence as fact — it explicitly hedges *"I am not certain of the precedence rules…please confirm"*.
13
+ - Recommends a concrete fix that sidesteps the precedence ambiguity entirely (`if not item.get_closest_marker("timeout")`).
14
+
15
+ The human-curated final version (with citations to pytest source that *answer*
16
+ the questions this review hedges on) is `sample-review-tqec-pr894-v3-curated.md`.
17
+
18
+ ---
19
+
20
+ ## 1. Bug Risk
21
+
22
+ **Marker append order — potential timeout override (speculation, requires library confirmation):**
23
+
24
+ `tests/conftest.py:9-10` — The code calls `item.add_marker(pytest.mark.timeout(SLOW_TEST_TIMEOUT))` to append a `timeout` marker to items that already carry a `slow` marker. `pytest-timeout` resolves which `timeout` marker to honor when multiple are present on a single test item. I am not certain of the precedence rules for `pytest-timeout` when multiple `timeout` markers exist; please confirm against its docs or source. If `pytest-timeout` uses the *first* marker it finds rather than the *last*, and tests already have an explicit `@pytest.mark.timeout(...)` decorator, the appended marker may lose. Conversely, if a slow test has an explicit `@pytest.mark.timeout(60)`, this code will also append the 300-second marker, which may override the intentional shorter timeout. The safer call, if the library supports it, would be `item.add_marker(..., append=False)` (prepend) or — better — to check whether a `timeout` marker already exists before adding one.
25
+
26
+ **No drift from the issue's stated intent.** The PR does what the issue asks: uses `conftest.py` to set a higher timeout for `slow`-marked tests.
27
+
28
+ ## 2. Edge Cases
29
+
30
+ **Tests with an explicit, shorter `timeout` marker:** If any slow test already has `@pytest.mark.timeout(N)` for some `N < 300`, this hook unconditionally appends a second `timeout` marker (`conftest.py:10`). The effective timeout will depend on `pytest-timeout`'s multi-marker behavior (unknown from this context). A guard like `if not item.get_closest_marker("timeout"):` would make the intent explicit and avoid surprises.
31
+
32
+ **`config` parameter omission:** `pytest_collection_modifyitems` accepts an optional `config` parameter (`def pytest_collection_modifyitems(config, items)`). Omitting it is valid in pytest, so this is not a bug — just noting it is intentional or irrelevant here.
33
+
34
+ **`SLOW_TEST_TIMEOUT = 300` with no documentation of rationale:** 5 minutes is a reasonable heuristic, but there is no comment explaining why 300 was chosen or whether it was validated against actual slow-test durations. This is a nit, not a blocker.
35
+
36
+ ## 3. Test Coverage
37
+
38
+ There are no tests for the `conftest.py` hook itself. For a conftest hook this is standard practice — testing pytest hooks in isolation is unusual and generally not expected. The correctness of the hook can be validated by running `pytest -m slow` and observing that the timeout is applied. No gap to flag here beyond the speculative multi-marker concern above.
39
+
40
+ ## 4. Scope Creep
41
+
42
+ No scope creep. The diff is a single new file (`tests/conftest.py`) that directly implements what the issue requested. No unrelated changes are bundled.
43
+
44
+ ## 5. Verdict
45
+
46
+ **NEEDS_DISCUSSION**
47
+
48
+ The implementation is minimal and correctly structured, but there is one unresolved question that warrants discussion before merging: `pytest-timeout`'s behavior when multiple `timeout` markers exist on the same test item is not confirmed from the available context. If it honors the *last*-added marker (append wins), the code works as intended for the common case but will silently override any intentional explicit-timeout annotation on a slow test. If it honors the *first* marker, the appended marker is a no-op for tests that already carry a `timeout`. Adding a guard — `if not item.get_closest_marker("timeout"): item.add_marker(...)` — would make the behavior unambiguous regardless of library internals and is a low-cost change. The team should confirm the intended interaction with tests that carry both `slow` and an explicit `timeout` marker before merging.
@@ -0,0 +1,118 @@
1
+ # Curated review — `tqec/tqec` PR #894
2
+
3
+ This is the human-curated review of [PR #894](https://github.com/tqec/tqec/pull/894),
4
+ distilled from the v2 raw output (`sample-review-tqec-pr894-v2-raw.md`)
5
+ after verifying every behavioural claim against pytest and pytest-timeout
6
+ source.
7
+
8
+ The agent's raw v2 output had to hedge on pytest-timeout's multi-marker
9
+ precedence because the library docs don't address it. Looking at pytest source
10
+ resolves the ambiguity — and the resolution is **less alarming** than the v1
11
+ raw output suggested. This version answers the question rather than
12
+ speculating about it.
13
+
14
+ ---
15
+
16
+ ## Verdict: NEEDS_DISCUSSION
17
+
18
+ Not blocking. One concrete behavioural footgun is worth a brief discussion or
19
+ a one-line guard before merging.
20
+
21
+ ## What works
22
+
23
+ - **The diff is minimal and faithful to the issue.** A single new file
24
+ `tests/conftest.py`, no unrelated changes, behaviour scoped to tests
25
+ carrying the `slow` marker.
26
+ - **The dependency is real.** `pyproject.toml` pins
27
+ `pytest-timeout>=2.4.0` under the `test` extra, and the `slow` marker is
28
+ registered in `[tool.pytest.ini_options]`. No "what if it's not installed"
29
+ concern applies here.
30
+
31
+ ## One subtle behavioural footgun
32
+
33
+ When a slow test **also** carries an explicit `@pytest.mark.timeout(N)`
34
+ decorator, the conftest's appended marker is silently ignored — the original
35
+ decorator wins. Whether this is desirable depends on author intent:
36
+
37
+ - A developer who writes `@pytest.mark.timeout(900)` on a particularly heavy
38
+ slow test (because they know 300 s is too short) **gets the behaviour they
39
+ expected**: their 900 is preserved.
40
+ - A developer who writes `@pytest.mark.timeout(60)` on a slow test (perhaps
41
+ for debugging, perhaps by accident) **does not get the conftest's 300 s
42
+ bump** — they get 60 s and the slow test fails inside their explicit cap.
43
+
44
+ ### Why this is the actual behaviour, with citations and a runtime check
45
+
46
+ The claim that the explicit decorator wins is verified two ways:
47
+
48
+ 1. **From source.** The chain of citations below explains why.
49
+ 2. **At runtime.** [`verify-marker-precedence/`](verify-marker-precedence/)
50
+ in this repo is a self-contained 4-file pytest project that uses the
51
+ exact `conftest.py` from PR #894, defines the three relevant test
52
+ configurations (slow + explicit-timeout(1), slow only, no markers), and
53
+ prints what `get_closest_marker("timeout")` resolves to for each test
54
+ without executing them. Reproducible in 30 seconds with `pip install
55
+ pytest pytest-timeout && python verify_precedence.py`. Recorded output
56
+ shows the test with both markers resolves to the explicit `(1,)`.
57
+
58
+
59
+
60
+ `add_marker(marker, append: bool = True) -> None` ([pytest source][1]) — the
61
+ default is `append=True`, so the conftest's marker lands at the **end** of
62
+ the item's `own_markers` list.
63
+
64
+ `pytest-timeout`'s `_get_item_settings` calls
65
+ `item.get_closest_marker("timeout")` ([pytest-timeout source][2]). pytest's
66
+ `get_closest_marker` returns `next(self.iter_markers(name=name), default)`
67
+ ([pytest source][3]). For markers on the same node, `iter_markers` yields them
68
+ in the order they were added to `own_markers`, so the explicit decorator —
69
+ which was applied at collection time, before this hook runs — comes first
70
+ and wins.
71
+
72
+ [1]: https://github.com/pytest-dev/pytest/blob/main/src/_pytest/nodes.py
73
+ [2]: https://github.com/pytest-dev/pytest-timeout/blob/master/pytest_timeout.py
74
+ [3]: https://github.com/pytest-dev/pytest/blob/main/src/_pytest/nodes.py
75
+
76
+ ### Suggested fix
77
+
78
+ Make the intent explicit by guarding on existing timeout markers:
79
+
80
+ ```python
81
+ def pytest_collection_modifyitems(items):
82
+ """Increase timeouts for tests marked as slow, unless the test has its
83
+ own explicit timeout marker (which we leave alone)."""
84
+ for item in items:
85
+ if item.get_closest_marker("slow") and not item.get_closest_marker("timeout"):
86
+ item.add_marker(pytest.mark.timeout(SLOW_TEST_TIMEOUT))
87
+ ```
88
+
89
+ This is mechanically equivalent to today's behaviour (because the existing
90
+ decorator would win anyway) but makes the precedence visible at the call site,
91
+ removes any reader confusion about which marker takes effect, and avoids any
92
+ dependency on a future pytest-timeout change to multi-marker resolution.
93
+
94
+ ## Coupling worth a comment
95
+
96
+ The diff increases timeouts only for `slow` tests. The 30 s baseline for
97
+ non-slow tests comes from `[tool.pytest.ini_options] timeout = 30` in
98
+ `pyproject.toml`, which lives outside this diff. If anyone ever removes that
99
+ line, fast tests silently lose all timeouts. A two-line comment in
100
+ `conftest.py` pointing at the pyproject setting would make the coupling
101
+ discoverable for future maintainers — not a blocker, just a courtesy to the
102
+ next person debugging a runaway test.
103
+
104
+ ---
105
+
106
+ ## What was cut from the raw v2 output, and why
107
+
108
+ | Cut | Reason |
109
+ |---|---|
110
+ | "Marker ordering / first-vs-last" hedge | Resolved with the citations above. The behaviour is deterministic. |
111
+ | "Make `append=True` explicit" | Style nit; default is documented. |
112
+ | "`config` parameter omission" | The raw output already noted this is not a bug. Removed to reduce noise. |
113
+ | "`SLOW_TEST_TIMEOUT = 300` lacks rationale comment" | True but inconsequential. A heuristic doesn't need defence. |
114
+ | "Missing meta-test" | The raw v2 already softened this from v1's "valid nit" to "testing pytest hooks in isolation is unusual" — fully cut here for the same reason. |
115
+
116
+ What's left is **two findings**: one concrete actionable suggestion (the guard)
117
+ and one documentation nudge (the comment about the pyproject baseline). That's
118
+ the maintainer-facing version.
@@ -0,0 +1,97 @@
1
+ # Marker-precedence empirical verification
2
+
3
+ Self-contained pytest project that runtime-verifies the central behavioral
4
+ claim in [`../sample-review-tqec-pr894-v3-curated.md`](../sample-review-tqec-pr894-v3-curated.md):
5
+
6
+ > When a slow test also carries an explicit `@pytest.mark.timeout(N)`
7
+ > decorator, the conftest's appended marker is silently ignored — the
8
+ > original decorator wins.
9
+
10
+ The curated review derives this from reading pytest and pytest-timeout
11
+ source code. This directory makes it observable at runtime.
12
+
13
+ ## Files
14
+
15
+ | File | Role |
16
+ |---|---|
17
+ | `conftest.py` | Verbatim copy (modulo formatting) of `tests/conftest.py` from [`tqec/tqec` PR #894](https://github.com/tqec/tqec/pull/894). |
18
+ | `pyproject.toml` | Registers the `slow` marker so pytest doesn't warn about an unknown marker. Deliberately omits the `timeout = 30` baseline that lives in tqec's pyproject — we only want to test the marker-vs-marker interaction. |
19
+ | `test_marker_precedence.py` | Three test cases probing the three relevant configurations: `slow + explicit-timeout(1)`, `slow only`, `no markers`. |
20
+ | `verify_precedence.py` | Plugin-based inspector that runs `pytest --collect-only` with a hook that prints the resolved timeout marker for each item. **No tests are actually executed** — sidesteps platform-dependent pytest-timeout behavior. |
21
+
22
+ ## How to reproduce
23
+
24
+ ```bash
25
+ pip install pytest pytest-timeout # tested with 8.3.5 / 2.4.0
26
+ cd examples/verify-marker-precedence
27
+ python verify_precedence.py
28
+ ```
29
+
30
+ ## Recorded output
31
+
32
+ Run on `pytest 8.3.5 + pytest-timeout 2.4.0` (Python 3.8.0, Windows):
33
+
34
+ ```
35
+ test_marker_precedence.py::test_explicit_short_timeout_should_win
36
+ test_marker_precedence.py::test_only_slow_marker_uses_conftest_timeout
37
+ test_marker_precedence.py::test_no_markers_unaffected
38
+
39
+ ================================================================
40
+ Marker precedence inspection (no tests executed)
41
+ ================================================================
42
+
43
+ test_marker_precedence.py::test_explicit_short_timeout_should_win
44
+ all timeout markers found: [(1,), (300,)]
45
+ effective (get_closest_marker): (1,)
46
+
47
+ test_marker_precedence.py::test_only_slow_marker_uses_conftest_timeout
48
+ all timeout markers found: [(300,)]
49
+ effective (get_closest_marker): (300,)
50
+
51
+ test_marker_precedence.py::test_no_markers_unaffected
52
+ all timeout markers found: []
53
+ effective (get_closest_marker): None
54
+
55
+
56
+ 3 tests collected in 0.00s
57
+ ```
58
+
59
+ ## Reading the result
60
+
61
+ The first test has **both** timeout markers attached to its item — `(1,)` was
62
+ applied during decoration (module load), `(300,)` was appended by the
63
+ conftest's `pytest_collection_modifyitems` hook after collection. Both are
64
+ visible via `iter_markers`.
65
+
66
+ `get_closest_marker("timeout")` returns the **first** one (`(1,)`), because
67
+ within the same node's `own_markers` list, that's the iteration order. The
68
+ explicit decorator wins; the conftest's marker is shadowed.
69
+
70
+ This is the runtime-observable consequence of the citation chain in
71
+ v3-curated.md:
72
+
73
+ 1. `add_marker(marker, append=True)` (default) appends to `own_markers`.
74
+ ([pytest source][1])
75
+ 2. `iter_markers(name=...)` yields markers from `own_markers` in order.
76
+ ([pytest source][1])
77
+ 3. `get_closest_marker(...)` returns `next(iter_markers(...), default)`.
78
+ ([pytest source][1])
79
+ 4. pytest-timeout reads exactly that. ([pytest-timeout source][2])
80
+
81
+ [1]: https://github.com/pytest-dev/pytest/blob/main/src/_pytest/nodes.py
82
+ [2]: https://github.com/pytest-dev/pytest-timeout/blob/master/pytest_timeout.py
83
+
84
+ ## Why this matters for the v3-curated review
85
+
86
+ The recommended fix in the curated review —
87
+
88
+ ```python
89
+ if item.get_closest_marker("slow") and not item.get_closest_marker("timeout"):
90
+ item.add_marker(pytest.mark.timeout(SLOW_TEST_TIMEOUT))
91
+ ```
92
+
93
+ — is **mechanically equivalent** to today's behaviour for tests with an
94
+ explicit timeout, because the explicit one already wins. It's a clarity
95
+ nudge, not a bug fix. The recorded output above is what makes that claim
96
+ checkable: every reviewer can re-run this script in 30 seconds and see
97
+ which marker wins.
@@ -0,0 +1,15 @@
1
+ # Mirror of tests/conftest.py from tqec/tqec PR #894 — kept here verbatim
2
+ # (modulo formatting) so the empirical test in this directory exercises the
3
+ # exact behavior under review.
4
+ #
5
+ # Source: https://github.com/tqec/tqec/pull/894/files
6
+ import pytest
7
+
8
+ SLOW_TEST_TIMEOUT = 300
9
+
10
+
11
+ def pytest_collection_modifyitems(items):
12
+ """Increase timeouts for tests marked as slow."""
13
+ for item in items:
14
+ if item.get_closest_marker("slow"):
15
+ item.add_marker(pytest.mark.timeout(SLOW_TEST_TIMEOUT))
@@ -0,0 +1,8 @@
1
+ [tool.pytest.ini_options]
2
+ markers = [
3
+ "slow: mark a test that takes a long time to run.",
4
+ ]
5
+ # We DO NOT set `timeout = 30` here, unlike tqec's pyproject.toml — the only
6
+ # thing this minimal repo is testing is whether the conftest's appended marker
7
+ # overrides an explicit @pytest.mark.timeout decorator. Adding a baseline would
8
+ # add a second variable.
@@ -0,0 +1,56 @@
1
+ """
2
+ Empirical verification of the marker-precedence claim made in
3
+ `examples/sample-review-tqec-pr894-v3-curated.md`.
4
+
5
+ The curated review claims:
6
+
7
+ When a slow test also carries an explicit @pytest.mark.timeout(N)
8
+ decorator, the conftest's appended marker is silently ignored — the
9
+ original decorator wins.
10
+
11
+ This file proves that runtime, not just from source-reading.
12
+
13
+ Three tests:
14
+
15
+ 1. test_explicit_short_timeout_should_win
16
+ Has @pytest.mark.slow AND @pytest.mark.timeout(1). Sleeps 2 seconds.
17
+ - If the explicit 1s decorator wins → this test FAILS with a Timeout.
18
+ - If the conftest's appended 300s wins → this test PASSES.
19
+
20
+ 2. test_only_slow_marker_uses_conftest_timeout
21
+ Has only @pytest.mark.slow, sleeps 2 seconds. Should always pass:
22
+ conftest's 300s applies, 2 < 300.
23
+
24
+ 3. test_no_markers_unaffected
25
+ Bare test. No timeout marker. No conftest hook applies. Passes trivially.
26
+
27
+ Expected outcome on POSIX (Linux/macOS): 1 failed (timeouted), 2 passed.
28
+ The failed test is the proof that the explicit decorator survives the hook.
29
+
30
+ Expected outcome on Windows: pytest-timeout's default `--timeout-method=thread`
31
+ hard-terminates the process via os._exit(1) on the first timeout, so tests 2
32
+ and 3 may never report. For a deterministic transcript on every platform, run
33
+ `python verify_precedence.py` instead — it inspects the resolved markers via
34
+ --collect-only and never executes the test bodies.
35
+ """
36
+ import time
37
+
38
+ import pytest
39
+
40
+
41
+ @pytest.mark.slow
42
+ @pytest.mark.timeout(1)
43
+ def test_explicit_short_timeout_should_win() -> None:
44
+ """Sleep 2s. Times out at 1s if the explicit decorator wins."""
45
+ time.sleep(2)
46
+
47
+
48
+ @pytest.mark.slow
49
+ def test_only_slow_marker_uses_conftest_timeout() -> None:
50
+ """No explicit timeout — conftest's 300s applies, 2s sleep passes."""
51
+ time.sleep(2)
52
+
53
+
54
+ def test_no_markers_unaffected() -> None:
55
+ """Bare test, conftest hook does nothing for it."""
56
+ assert True
@@ -0,0 +1,67 @@
1
+ """
2
+ Inspect — without executing — which @pytest.mark.timeout marker each test
3
+ resolves to under the conftest.py from tqec/tqec PR #894.
4
+
5
+ This sidesteps the platform-dependent run-time behavior of pytest-timeout
6
+ (thread-method on Windows hard-terminates the process, masking the summary).
7
+ By using --collect-only and inspecting the marker structure directly, we get
8
+ a deterministic transcript that any reviewer can reproduce.
9
+
10
+ Run it with:
11
+
12
+ python verify_precedence.py
13
+
14
+ Expected output (verified on pytest 8.3.5 + pytest-timeout 2.4.0):
15
+
16
+ test_marker_precedence.py::test_explicit_short_timeout_should_win
17
+ all timeout markers found: [(1,), (300,)]
18
+ effective (get_closest_marker): (1,)
19
+ test_marker_precedence.py::test_only_slow_marker_uses_conftest_timeout
20
+ all timeout markers found: [(300,)]
21
+ effective (get_closest_marker): (300,)
22
+ test_marker_precedence.py::test_no_markers_unaffected
23
+ all timeout markers found: []
24
+ effective (get_closest_marker): None
25
+
26
+ Reading: when both an explicit @pytest.mark.timeout(1) decorator AND the
27
+ conftest's appended @pytest.mark.timeout(300) are present, get_closest_marker
28
+ returns the (1,) marker — the explicit decorator wins, the appended marker
29
+ is silently shadowed. This empirically confirms the citation chain in
30
+ sample-review-tqec-pr894-v3-curated.md.
31
+ """
32
+
33
+ import sys
34
+
35
+ import pytest
36
+
37
+
38
+ class MarkerInspector:
39
+ """Pytest plugin that prints the resolved timeout marker for each item
40
+ after collection, then lets the run end (via --collect-only)."""
41
+
42
+ def pytest_collection_finish(self, session): # noqa: D401
43
+ print()
44
+ print("=" * 64)
45
+ print("Marker precedence inspection (no tests executed)")
46
+ print("=" * 64)
47
+ for item in session.items:
48
+ timeout_markers = [m.args for m in item.iter_markers(name="timeout")]
49
+ closest = item.get_closest_marker("timeout")
50
+ effective = closest.args if closest else None
51
+ print(f"\n{item.nodeid}")
52
+ print(f" all timeout markers found: {timeout_markers}")
53
+ print(f" effective (get_closest_marker): {effective}")
54
+ print()
55
+
56
+
57
+ if __name__ == "__main__":
58
+ sys.exit(
59
+ pytest.main(
60
+ [
61
+ "--collect-only",
62
+ "-q",
63
+ "test_marker_precedence.py",
64
+ ],
65
+ plugins=[MarkerInspector()],
66
+ )
67
+ )
@@ -0,0 +1,32 @@
1
+ # Auto-fix issues with github-agent: label an issue `agent-fix` and the agent
2
+ # clones the repo, writes a patch, self-reviews it, and opens a PR.
3
+ #
4
+ # Copy this into your repo at .github/workflows/issue-fix.yml and add an
5
+ # ANTHROPIC_API_KEY secret. The built-in GITHUB_TOKEN handles the PR.
6
+ #
7
+ # Safety: the agent only runs when a maintainer applies the `agent-fix` label,
8
+ # so random issue authors can't trigger paid runs. The pipeline's own PR gate
9
+ # still refuses to open a PR on a failed self-review.
10
+ name: Issue fix (github-agent)
11
+
12
+ on:
13
+ issues:
14
+ types: [labeled]
15
+
16
+ permissions:
17
+ contents: write
18
+ issues: write
19
+ pull-requests: write
20
+
21
+ jobs:
22
+ fix:
23
+ if: github.event.label.name == 'agent-fix'
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - name: Engineer a fix and open a PR
27
+ uses: Hadar01/github-agents@v1
28
+ with:
29
+ command: issue
30
+ anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
31
+ comment: 'true' # link the PR back on the issue
32
+ max-cost: '2.50'
@@ -0,0 +1,34 @@
1
+ # Auto-review every pull request with github-agent.
2
+ #
3
+ # Copy this file into your repo at .github/workflows/pr-review.yml and add an
4
+ # ANTHROPIC_API_KEY secret (Settings → Secrets and variables → Actions).
5
+ #
6
+ # Why pull_request_target instead of pull_request:
7
+ # github-agent reviews a PR by fetching its diff through the GitHub API and
8
+ # sending it to Claude — it never checks out or executes the PR's code. That
9
+ # makes pull_request_target safe here, and it is what lets reviews on PRs from
10
+ # forks read the ANTHROPIC_API_KEY secret (plain pull_request cannot).
11
+ name: PR review (github-agent)
12
+
13
+ on:
14
+ pull_request_target:
15
+ types: [opened, synchronize, reopened]
16
+
17
+ permissions:
18
+ contents: read
19
+ pull-requests: write
20
+
21
+ jobs:
22
+ review:
23
+ runs-on: ubuntu-latest
24
+ steps:
25
+ - name: Review the pull request
26
+ uses: Hadar01/github-agents@v1
27
+ with:
28
+ command: review
29
+ anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
30
+ post: 'true'
31
+ # Advisory by default: posts findings, never blocks merge.
32
+ # Flip to 'true' to make REQUEST_CHANGES fail the check (a merge gate).
33
+ fail-on-request-changes: 'false'
34
+ max-cost: '1.50'
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@aarushpandey/gitagent",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Autonomous GitHub engineering pipeline: takes an issue, ships a PR, and reviews its own diff before opening it.",
8
+ "main": "src/pipeline.js",
9
+ "bin": {
10
+ "github-agent": "src/pipeline.js"
11
+ },
12
+ "scripts": {
13
+ "test": "jest",
14
+ "issue": "node src/pipeline.js issue",
15
+ "review": "node src/pipeline.js review",
16
+ "triage": "node src/pipeline.js triage",
17
+ "verify": "node scripts/verify.js",
18
+ "smoke": "node -e \"const p = require('./src/pipeline.js'); if (typeof p.buildAuditTrail !== 'function') process.exit(1); console.log('ok');\"",
19
+ "prepublishOnly": "npm test"
20
+ },
21
+ "keywords": [
22
+ "ai",
23
+ "agent",
24
+ "claude",
25
+ "anthropic",
26
+ "github",
27
+ "automation",
28
+ "code-review",
29
+ "pull-request",
30
+ "engineering-agent",
31
+ "autonomous"
32
+ ],
33
+ "author": "Hadar01",
34
+ "license": "MIT",
35
+ "homepage": "https://github.com/Hadar01/github-agents#readme",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/Hadar01/github-agents.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/Hadar01/github-agents/issues"
42
+ },
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
46
+ "files": [
47
+ "src/",
48
+ "scripts/",
49
+ "examples/",
50
+ "action.yml",
51
+ "README.md",
52
+ "LICENSE",
53
+ "CONTRIBUTING.md",
54
+ "TESTING.md"
55
+ ],
56
+ "dependencies": {
57
+ "@anthropic-ai/sdk": "^0.90.0",
58
+ "@octokit/rest": "^22.0.1",
59
+ "dotenv": "^17.4.2",
60
+ "express": "^5.2.1",
61
+ "simple-git": "^3.36.0"
62
+ },
63
+ "devDependencies": {
64
+ "jest": "^30.3.0"
65
+ },
66
+ "jest": {
67
+ "testPathIgnorePatterns": [
68
+ "/node_modules/",
69
+ "/repos/"
70
+ ],
71
+ "modulePathIgnorePatterns": [
72
+ "/repos/"
73
+ ]
74
+ }
75
+ }