4take 0.2.0__tar.gz → 0.2.2__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.
- {4take-0.2.0 → 4take-0.2.2}/AGENTS.md +3 -1
- {4take-0.2.0 → 4take-0.2.2}/CHANGELOG.md +47 -0
- {4take-0.2.0 → 4take-0.2.2}/CLAUDE.md +2 -1
- {4take-0.2.0 → 4take-0.2.2}/PKG-INFO +76 -56
- {4take-0.2.0 → 4take-0.2.2}/README.md +74 -54
- {4take-0.2.0 → 4take-0.2.2}/SECURITY.md +1 -1
- {4take-0.2.0 → 4take-0.2.2}/SKILL.md +1 -1
- {4take-0.2.0 → 4take-0.2.2}/evals/README.md +0 -1
- {4take-0.2.0 → 4take-0.2.2}/llms.txt +2 -2
- {4take-0.2.0 → 4take-0.2.2}/pyproject.toml +1 -1
- 4take-0.2.2/src/fourtake/__init__.py +1 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/cli.py +1 -1
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/findings.py +18 -2
- {4take-0.2.0 → 4take-0.2.2}/tests/test_findings.py +57 -0
- 4take-0.2.0/evals/backfill_duplicates.py +0 -74
- 4take-0.2.0/src/fourtake/__init__.py +0 -1
- {4take-0.2.0 → 4take-0.2.2}/.gitignore +0 -0
- {4take-0.2.0 → 4take-0.2.2}/.pre-commit-config.yaml +0 -0
- {4take-0.2.0 → 4take-0.2.2}/CLA.md +0 -0
- {4take-0.2.0 → 4take-0.2.2}/CONTRIBUTING.md +0 -0
- {4take-0.2.0 → 4take-0.2.2}/LICENSE +0 -0
- {4take-0.2.0 → 4take-0.2.2}/POST_MORTEMS.md +0 -0
- {4take-0.2.0 → 4take-0.2.2}/ROADMAP.md +0 -0
- {4take-0.2.0 → 4take-0.2.2}/docs/adr/0001-use-exponential-backoff-with-10-retries.md +0 -0
- {4take-0.2.0 → 4take-0.2.2}/docs/adr/0002-cap-retries-at-four-and-honour-retry-after.md +0 -0
- {4take-0.2.0 → 4take-0.2.2}/docs/adr/0003-retry-a-rate-limited-provider-eight-times.md +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/async_gather_swallow.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/auth_jwt_decode.js +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/auth_timing_compare.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/clean_lru.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/clean_slugify.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/cmdinjection_backup.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/cmdinjection_git.js +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/divzero_average_latency.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/leak_db_connection.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/leak_file_handle.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/leak_subprocess.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/none_env_parse.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/none_json_response.js +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/none_lookup_chain.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/offbyone_pagination.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/offbyone_retry_window.js +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/offbyone_ring_buffer.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/overflow_backoff.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/pathtraversal_download.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/pathtraversal_zip.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/race_async_cache.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/race_counter_increment.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/race_lazy_singleton.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/sqli_order_by.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/sqli_user_search.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/toctou_config_write.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/toctou_upload_quota.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus/unbounded_memory_tail.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/cache_key_version/context/profiles/events.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/cache_key_version/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/config_key_mismatch/context/app/settings.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/config_key_mismatch/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/ctx_clean_locked_by_caller/context/cache/lru.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/ctx_clean_locked_by_caller/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/ctx_clean_validated_upstream/context/billing/coupons.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/ctx_clean_validated_upstream/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/duplicate_normalizer/context/accounts/identity.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/duplicate_normalizer/context/accounts/login.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/duplicate_normalizer/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/event_renamed/context/src/notifications/subscribers.js +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/event_renamed/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/lock_not_taken/context/limits/counter.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/lock_not_taken/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/missing_await/context/web/sessions.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/missing_await/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/nested_commit/context/orders/lines.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/nested_commit/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/none_return_breaks_caller/context/app/api.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/none_return_breaks_caller/context/app/mailer.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/none_return_breaks_caller/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/shell_helper_injection/context/deploy/git_tools.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/shell_helper_injection/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/swapped_positional_args/context/src/billing.js +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/swapped_positional_args/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/tuple_as_bool/context/shop/payments.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/tuple_as_bool/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/units_seconds_to_ms/context/jobs/watchdog.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/corpus_ctx/units_seconds_to_ms/review.diff +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/labels.json +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/labels_ctx.json +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/record.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/evals/score.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/autocontext.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/cache.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/client.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/config.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/context.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/discovery.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/engine.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/filelock.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/input_kind.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/mcp_entry.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/mcp_server.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/output.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/preflight.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/prompts.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/rate_limiter.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/safety.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/telemetry.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/src/fourtake/tokenizer.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/conftest.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_autocontext.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_cache.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_cli.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_client.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_config.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_context.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_council_scheduling.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_discovery.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_docs.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_engine.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_evals.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_input_kind.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_integration.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_mcp_server.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_output.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_output_contract.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_preflight.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_prompts.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_rate_limiter.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_safety.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_telemetry.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_tokenizer.py +0 -0
- {4take-0.2.0 → 4take-0.2.2}/tests/test_zero_cost_enforcement.py +0 -0
|
@@ -118,11 +118,13 @@ usable review; read `agreement` against `summary.distinct_models`.
|
|
|
118
118
|
|
|
119
119
|
```json
|
|
120
120
|
{
|
|
121
|
-
"version": "0.2.
|
|
121
|
+
"version": "0.2.2",
|
|
122
122
|
"timestamp": "2026-08-16T14:20:00Z",
|
|
123
|
+
"config": {"models_requested": 12, "min_responses": 4, "timeout_seconds": 600, "strict": false},
|
|
123
124
|
"summary": {
|
|
124
125
|
"task_hash": "25d297f773c3",
|
|
125
126
|
"responses_received": 4,
|
|
127
|
+
"distinct_models": 4,
|
|
126
128
|
"responses_failed": 0,
|
|
127
129
|
"total_latency_ms": 3200,
|
|
128
130
|
"context": {"files": ["src/mailer.py", "src/users.py (lines 40-90)"], "tokens": 812, "dropped": []}
|
|
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.2] - 2026-10-26
|
|
11
|
+
|
|
12
|
+
One fix to what the review shows; how findings are grouped and counted is unchanged.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **The README leads with what the council found, not with what an agent did with it.** On the
|
|
16
|
+
weak-primary test's 50 PRs the council's findings caught 37 bugs and Gemini 3.8 Flash alone 24
|
|
17
|
+
(15 against 2 discordant, exact McNemar p ≈ 0.002, not pre-registered; `docs/benchmark` gives the
|
|
18
|
+
strict figures). The model given those findings caught 26, which is now the reason for
|
|
19
|
+
`report_for_user` rather than the section's headline. The MCP section no longer opens with the
|
|
20
|
+
20-bug Opus test, which stays in the ROADMAP.
|
|
21
|
+
- The README's JSON example shows `served_model`, `served_provider`, `finish_reason`, `truncated`,
|
|
22
|
+
`duplicate_of`, `config` and `summary.distinct_models`, which the CLI has emitted all along and
|
|
23
|
+
the agent guides tell readers to rely on.
|
|
24
|
+
- `uv.lock` pins the patched PyJWT, certifi, cryptography, mcp and urllib3. It is used by CI and
|
|
25
|
+
`uv sync`; installs from PyPI resolve their own versions and already had the fixed ones.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **A consensus entry no longer takes a bare symbol as its title.** Cohere's north-mini-code
|
|
29
|
+
sometimes writes the symbol where the title belongs (`[CRITICAL] INJECTION find_user (def
|
|
30
|
+
find_user)`, 11 of its 75 recorded findings). Among equally severe findings the first in order set
|
|
31
|
+
the title, so in a live review on 26 October all five entries read "find_user", in the CLI and in
|
|
32
|
+
`report_for_user` alike. An entry now takes a title that names the defect when one of its findings
|
|
33
|
+
has one, and otherwise the start of the issue, cut at a word. Grouping and agreement are
|
|
34
|
+
unchanged: `evals/score.py` prints the same report before and after.
|
|
35
|
+
|
|
36
|
+
## [0.2.1] - 2026-10-26
|
|
37
|
+
|
|
38
|
+
Documentation and packaging only; the review behaves exactly as in 0.2.0.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- **The benchmark comparison says when each side was recorded, and what the models may have
|
|
42
|
+
seen.** The commercial reviewers' comments on Greptile's 50 PRs date from late July 2025;
|
|
43
|
+
4take's runs from October 2026. The PRs and their bug descriptions have been public since 2025,
|
|
44
|
+
so a model trained later may have seen them. Both favour 4take, and the README, `docs/benchmark`
|
|
45
|
+
and the 0.2.0 release notes now say so.
|
|
46
|
+
- **Claims the measurements do not support are gone.** The agent diagram no longer says the
|
|
47
|
+
council "dismisses hallucinations" and yields "final clean code", `CLAUDE.md` no longer says it
|
|
48
|
+
eliminates single-model hallucinations, and "universal", "seamlessly" and "beautiful" are out of
|
|
49
|
+
the README, `llms.txt`, the CLI help and the PyPI summary.
|
|
50
|
+
- **Two README badges carried other projects' logos** (FastAPI's for FastMCP, OpenAI's for
|
|
51
|
+
OpenRouter). They are replaced by the PyPI version.
|
|
52
|
+
|
|
53
|
+
### Removed
|
|
54
|
+
- `evals/backfill_duplicates.py`, a one-off migration of recordings made before 0.2.0. It stays in
|
|
55
|
+
the git history.
|
|
56
|
+
|
|
10
57
|
## [0.2.0] - 2026-10-26
|
|
11
58
|
|
|
12
59
|
### Added
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## What is this?
|
|
4
4
|
4take is a CLI and MCP server that queries 4 independent LLMs in parallel to review code.
|
|
5
|
-
You pipe code into it, and it returns
|
|
5
|
+
You pipe code into it, and it returns four models' reviews with their findings grouped by how
|
|
6
|
+
many raised each one.
|
|
6
7
|
|
|
7
8
|
It reviews four axes, not just defects: **correctness**, **security**, **structure**
|
|
8
9
|
(responsibility in the wrong layer, coupling that spreads one behavioural change across
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: 4take
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Code review by a council of four free LLMs on OpenRouter, with findings ranked by how many reviewers raised them
|
|
5
5
|
Project-URL: Homepage, https://github.com/aleksbuss/4take
|
|
6
6
|
Project-URL: Repository, https://github.com/aleksbuss/4take
|
|
7
7
|
Project-URL: Issues, https://github.com/aleksbuss/4take/issues
|
|
@@ -40,16 +40,15 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
[](https://www.python.org/)
|
|
42
42
|
[](https://github.com/aleksbuss/4take/blob/main/LICENSE)
|
|
43
|
-
[](https://openrouter.ai/)
|
|
43
|
+
[](https://pypi.org/project/4take/)
|
|
45
44
|
[](https://github.com/aleksbuss/4take/actions)
|
|
46
45
|
[](https://github.com/astral-sh/ruff)
|
|
47
46
|
|
|
48
47
|
**Multi-Model AI Council for Code Review.**
|
|
49
|
-
*
|
|
48
|
+
*Four free models from four vendors review your code in one command, and their findings are ranked by how many raised each one.*
|
|
50
49
|
|
|
51
50
|
On Greptile's own 50-PR bug benchmark, a council of free models found 37 of 50 bugs:
|
|
52
|
-
level with Greptile (41), ahead of Copilot and CodeRabbit. [How it was measured](#-against-the-paid-reviewers-greptiles-benchmark)
|
|
51
|
+
level with Greptile (41), ahead of Copilot and CodeRabbit, whose reviews date from July 2025. [How it was measured](#-against-the-paid-reviewers-greptiles-benchmark)
|
|
53
52
|
|
|
54
53
|
[⚡ 30-Second Quickstart](#-30-second-quickstart) •
|
|
55
54
|
[🤖 AI Agent & MCP Integration](#-ai-coding-agents--mcp-integration) •
|
|
@@ -114,33 +113,33 @@ git diff --cached | 4take
|
|
|
114
113
|
|
|
115
114
|
## 🤖 AI Coding Agents & MCP Integration
|
|
116
115
|
|
|
117
|
-
`4take`
|
|
116
|
+
`4take` works with AI coding agents (Claude Code, Cursor, Antigravity IDE, Windsurf, Cline, Roo Code, Codex) as an MCP server or a CLI: the agent sends its code to the council and gets the findings back ranked by agreement, to show the user.
|
|
118
117
|
|
|
119
|
-
|
|
118
|
+
An agent tends to keep the findings most reviewers agree on and drop the rest, which loses much of what the council catches ([measured](#-read-the-council-yourself)). So the MCP payload opens with every finding and its agreement, for the agent to show you.
|
|
120
119
|
|
|
121
120
|
```text
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
121
|
+
┌──────────────────────┐
|
|
122
|
+
│ AI coding agent │
|
|
123
|
+
└──────────┬───────────┘
|
|
124
|
+
│ 1. sends its code or diff
|
|
125
|
+
▼
|
|
126
|
+
┌──────────────────────┐
|
|
127
|
+
│ 4take council │
|
|
128
|
+
└──────────┬───────────┘
|
|
129
|
+
┌────────────┬───────┴────┬────────────┐
|
|
130
|
+
▼ ▼ ▼ ▼
|
|
131
|
+
reviewer 1 reviewer 2 reviewer 3 reviewer 4
|
|
132
|
+
(vendor A) (vendor B) (vendor C) (vendor D)
|
|
133
|
+
└────────────┴───────┬────┴────────────┘
|
|
134
|
+
│ 2. findings grouped and ranked
|
|
135
|
+
▼ by how many raised each one
|
|
136
|
+
┌──────────────────────┐
|
|
137
|
+
│ report_for_user │
|
|
138
|
+
│ e.g. "3/4 HIGH ..." │
|
|
139
|
+
└──────────┬───────────┘
|
|
140
|
+
│ 3. the agent shows every line and
|
|
141
|
+
▼ checks each against the code
|
|
142
|
+
user
|
|
144
143
|
```
|
|
145
144
|
|
|
146
145
|
### Option A: FastMCP Server (`4take-mcp`)
|
|
@@ -224,11 +223,13 @@ cat modified_file.py | 4take --format json --accept-risk --quiet
|
|
|
224
223
|
|
|
225
224
|
```json
|
|
226
225
|
{
|
|
227
|
-
"version": "0.2.
|
|
226
|
+
"version": "0.2.2",
|
|
228
227
|
"timestamp": "2026-08-16T14:20:00Z",
|
|
228
|
+
"config": {"models_requested": 12, "min_responses": 4, "timeout_seconds": 600, "strict": false},
|
|
229
229
|
"summary": {
|
|
230
230
|
"task_hash": "25d297f773c3",
|
|
231
231
|
"responses_received": 4,
|
|
232
|
+
"distinct_models": 4,
|
|
232
233
|
"responses_failed": 0,
|
|
233
234
|
"total_latency_ms": 3200,
|
|
234
235
|
"context": {"files": ["src/mailer.py", "src/users.py (lines 40-90)"], "tokens": 812, "dropped": []}
|
|
@@ -251,12 +252,22 @@ cat modified_file.py | 4take --format json --accept-risk --quiet
|
|
|
251
252
|
"responses": [
|
|
252
253
|
{
|
|
253
254
|
"model": "openrouter/nvidia/nemotron-3-super-120b-a12b:free",
|
|
255
|
+
"served_model": "nvidia/nemotron-3-super-120b-a12b:free",
|
|
256
|
+
"served_provider": "NVIDIA",
|
|
254
257
|
"content": "### Code Review\n1. Found SQL Injection on line 8...",
|
|
258
|
+
"finish_reason": "stop",
|
|
259
|
+
"truncated": false,
|
|
260
|
+
"duplicate_of": null,
|
|
255
261
|
"latency_ms": 1150
|
|
256
262
|
},
|
|
257
263
|
{
|
|
258
264
|
"model": "openrouter/poolside/laguna-s-2.1:free",
|
|
265
|
+
"served_model": "poolside/laguna-s-2.1:free",
|
|
266
|
+
"served_provider": "Poolside",
|
|
259
267
|
"content": "### Security & Performance\n1. Use parameterized query for user_id...",
|
|
268
|
+
"finish_reason": "stop",
|
|
269
|
+
"truncated": false,
|
|
270
|
+
"duplicate_of": null,
|
|
260
271
|
"latency_ms": 1420
|
|
261
272
|
}
|
|
262
273
|
],
|
|
@@ -299,7 +310,7 @@ council took about 8½ minutes, and 6 ran to the 600 s limit. On the 28 short sn
|
|
|
299
310
|
corpus the median was about 50 s, and 27 of 28 councils seated four.
|
|
300
311
|
|
|
301
312
|
### Ready-to-Use Agent Configuration Files:
|
|
302
|
-
- **
|
|
313
|
+
- **Instructions for any agent**: [`AGENTS.md`](https://github.com/aleksbuss/4take/blob/main/AGENTS.md)
|
|
303
314
|
- **Claude Code**: [`CLAUDE.md`](https://github.com/aleksbuss/4take/blob/main/CLAUDE.md)
|
|
304
315
|
- **Antigravity Skill**: [`SKILL.md`](https://github.com/aleksbuss/4take/blob/main/SKILL.md)
|
|
305
316
|
- **Machine Summary**: [`llms.txt`](https://github.com/aleksbuss/4take/blob/main/llms.txt)
|
|
@@ -308,15 +319,15 @@ corpus the median was about 50 s, and 27 of 28 councils seated four.
|
|
|
308
319
|
|
|
309
320
|
## ✨ Features
|
|
310
321
|
|
|
311
|
-
- 🚀 **
|
|
322
|
+
- 🚀 **One Command**: Pipe a file or a diff into `4take`; four free models review it in parallel.
|
|
312
323
|
- 🧠 **Auto-Discovery Ranked on Evidence**: Fetches and filters the free models on OpenRouter, then ranks them by the success rate and median latency they have actually delivered on your machine. The name-based heuristic (parameter count, context window) survives only as the cold-start prior for models you have not run yet.
|
|
313
324
|
- 🔎 **Four Review Axes**: Correctness, security, structure and simplicity — not just defects. Every structural finding has to name its cost, so the breadth does not become a flood of taste.
|
|
314
325
|
- 🤝 **Consensus Grouping**: Findings several models raised independently are grouped and ranked by how many raised them, so the cross-referencing is done for you rather than left in four parallel reviews.
|
|
315
|
-
- 🔄 **Fault-Tolerant Dynamic Rotation**:
|
|
326
|
+
- 🔄 **Fault-Tolerant Dynamic Rotation**: A rate-limited model (HTTP 429) is retried with exponential backoff; one that keeps failing gives its seat to the next model in a pre-ranked pool of up to 12.
|
|
316
327
|
- 🆓 **Free Models Only**: Requests go only to OpenRouter's free (`:free`) models, and a paid model is rejected before anything is sent. Free has limits: roughly 4 to 8 reviews a day on an account that has never been topped up, roughly 80 to 160 once it has ([details](#-openrouter-free-tier--rate-limits)).
|
|
317
328
|
- 🛡️ **Secret Redaction**: Automatically scrubs API keys, passwords, and authorization tokens before sending payloads over the wire. The code itself still goes to third-party free endpoints ([privacy](#%EF%B8%8F-privacy--security)).
|
|
318
329
|
- 🚦 **Cross-Process Rate Limiter**: Shared file-locked token bucket ensures concurrent terminal sessions or background agents never trigger rate limit bans.
|
|
319
|
-
- 🎨 **
|
|
330
|
+
- 🎨 **Terminal and JSON Output**: Markdown rendered in the terminal, or `--format json` for scripts and CI.
|
|
320
331
|
|
|
321
332
|
---
|
|
322
333
|
|
|
@@ -324,9 +335,9 @@ corpus the median was about 50 s, and 27 of 28 councils seated four.
|
|
|
324
335
|
|
|
325
336
|
Greptile published a benchmark of 50 pull requests from five open-source projects (Cal.com,
|
|
326
337
|
Discourse, Grafana, Keycloak, Sentry), each with one labelled bug, together with the comments five
|
|
327
|
-
commercial AI reviewers left on them
|
|
328
|
-
models (commit 2f07956, 8 October 2026). Its reviews were
|
|
329
|
-
analysis written down before the run.
|
|
338
|
+
commercial AI reviewers left on them in late July 2025. Fifteen months later 4take reviewed the
|
|
339
|
+
same 50 diffs using only OpenRouter's free models (commit 2f07956, 8 October 2026). Its reviews were
|
|
340
|
+
graded blind, against a rubric and an analysis written down before the run.
|
|
330
341
|
|
|
331
342
|
| reviewer | benchmark standard, 50 PRs | strict, 42 verified labels |
|
|
332
343
|
| :--- | ---: | ---: |
|
|
@@ -355,6 +366,10 @@ analysis written down before the run.
|
|
|
355
366
|
4take. A strong signal, not proof. The pool has moved on since: two of the four most-seated
|
|
356
367
|
models of that run (apodex-1.1-mini, ling-3.0-flash-sante) have left the free tier, so the same
|
|
357
368
|
councils cannot be seated again.
|
|
369
|
+
- **Fifteen months apart, on public data.** The commercial reviews were written in July 2025 and
|
|
370
|
+
4take's in October 2026, and models improved in between: those tools would probably score higher
|
|
371
|
+
today. The PRs and the benchmark's bug descriptions have been public since 2025, so a model trained
|
|
372
|
+
later may have seen them, and nothing here rules that out. Both effects favour 4take.
|
|
358
373
|
- **Where it is weakest.** Large Java diffs: Keycloak 5 of 10. On two of them, and on one Sentry
|
|
359
374
|
diff, only one free vendor reasoned through to an answer. A council took a median of six
|
|
360
375
|
minutes on these diffs and up to the 600 s timeout.
|
|
@@ -365,30 +380,30 @@ diffs are other projects' code, so the recordings and the graded packets are not
|
|
|
365
380
|
|
|
366
381
|
---
|
|
367
382
|
|
|
368
|
-
## 🧪
|
|
383
|
+
## 🧪 Read the Council Yourself
|
|
369
384
|
|
|
370
|
-
**
|
|
371
|
-
a finding all four raised held a real, labelled defect 96% of the time; one raised by two or three
|
|
372
|
-
held one 60% of the time; one raised by a single reviewer 41%. A single reviewer's finding is a
|
|
373
|
-
pointer to check, not a verdict, and four in ten of them are real.
|
|
374
|
-
|
|
375
|
-
**Read the council's output; don't only hand it to another model.** A
|
|
385
|
+
**Four free models found more than a commercial model alone.** A
|
|
376
386
|
[pre-registered test](https://github.com/aleksbuss/4take/blob/main/docs/benchmark/README.md#does-the-council-help-a-weaker-model)
|
|
377
|
-
on the same 50 PRs gave Gemini 3.8 Flash each diff alone, and
|
|
378
|
-
notes
|
|
387
|
+
on the same 50 PRs, in October 2026, gave Gemini 3.8 Flash (thinking high) each diff alone, and
|
|
388
|
+
again with the council's findings as notes; three runs each, graded blind:
|
|
379
389
|
|
|
380
|
-
| |
|
|
390
|
+
| | bugs found, of 50 |
|
|
381
391
|
| :--- | ---: |
|
|
392
|
+
| **the council's findings, read directly** | **37** |
|
|
382
393
|
| Gemini 3.8 Flash alone | 24 |
|
|
383
|
-
| Gemini 3.8 Flash
|
|
384
|
-
|
|
394
|
+
| Gemini 3.8 Flash given the council's findings | 26 |
|
|
395
|
+
|
|
396
|
+
The council caught 15 bugs the model missed, and the model 2 that the council missed (exact McNemar
|
|
397
|
+
p ≈ 0.002). This comparison was not pre-registered, and it credits the council's best reviewer per
|
|
398
|
+
PR against the model's majority of three runs. Both sides were recorded in the same month on the
|
|
399
|
+
same diffs, so the time gap of the commercial comparison does not arise here, and any exposure of
|
|
400
|
+
the PRs in training data applies to both.
|
|
385
401
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
council). 4take does not make a model a better reviewer by being pasted into its context.
|
|
402
|
+
**An agent that reads the findings keeps only part of them.** Given the council's findings as
|
|
403
|
+
notes, the model caught 26, not 37; against the model alone that is no difference shown (p = 0.69).
|
|
404
|
+
In an exploratory breakdown it took up 3 of the 4 bugs only the council caught that three or four
|
|
405
|
+
reviewers shared, and none of the 11 that one or two raised: it kept the consensus and dropped the
|
|
406
|
+
long tail.
|
|
392
407
|
|
|
393
408
|
**So 4take puts every finding in front of you.** Over MCP the user sees only what the agent
|
|
394
409
|
relays, so the payload opens with `report_for_user`: one line per finding with its agreement
|
|
@@ -396,6 +411,11 @@ relays, so the payload opens with `report_for_user`: one line per finding with i
|
|
|
396
411
|
act on agreement 3 or more, and to check agreement 1 or 2 against the code instead of dropping it.
|
|
397
412
|
The CLI prints the same ranking to your terminal.
|
|
398
413
|
|
|
414
|
+
**Agreement tells you where to look first.** On the eval corpus, over three recordings with four
|
|
415
|
+
reviewers seated, a finding all four raised held a real, labelled defect 96% of the time; one
|
|
416
|
+
raised by two or three held one 60% of the time; one raised by a single reviewer 41%. A single
|
|
417
|
+
reviewer's finding is a pointer to check, not a verdict, and four in ten of them are real.
|
|
418
|
+
|
|
399
419
|
---
|
|
400
420
|
|
|
401
421
|
## 🔎 What the Council Reviews
|
|
@@ -4,16 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.python.org/)
|
|
6
6
|
[](https://github.com/aleksbuss/4take/blob/main/LICENSE)
|
|
7
|
-
[](https://openrouter.ai/)
|
|
7
|
+
[](https://pypi.org/project/4take/)
|
|
9
8
|
[](https://github.com/aleksbuss/4take/actions)
|
|
10
9
|
[](https://github.com/astral-sh/ruff)
|
|
11
10
|
|
|
12
11
|
**Multi-Model AI Council for Code Review.**
|
|
13
|
-
*
|
|
12
|
+
*Four free models from four vendors review your code in one command, and their findings are ranked by how many raised each one.*
|
|
14
13
|
|
|
15
14
|
On Greptile's own 50-PR bug benchmark, a council of free models found 37 of 50 bugs:
|
|
16
|
-
level with Greptile (41), ahead of Copilot and CodeRabbit. [How it was measured](#-against-the-paid-reviewers-greptiles-benchmark)
|
|
15
|
+
level with Greptile (41), ahead of Copilot and CodeRabbit, whose reviews date from July 2025. [How it was measured](#-against-the-paid-reviewers-greptiles-benchmark)
|
|
17
16
|
|
|
18
17
|
[⚡ 30-Second Quickstart](#-30-second-quickstart) •
|
|
19
18
|
[🤖 AI Agent & MCP Integration](#-ai-coding-agents--mcp-integration) •
|
|
@@ -78,33 +77,33 @@ git diff --cached | 4take
|
|
|
78
77
|
|
|
79
78
|
## 🤖 AI Coding Agents & MCP Integration
|
|
80
79
|
|
|
81
|
-
`4take`
|
|
80
|
+
`4take` works with AI coding agents (Claude Code, Cursor, Antigravity IDE, Windsurf, Cline, Roo Code, Codex) as an MCP server or a CLI: the agent sends its code to the council and gets the findings back ranked by agreement, to show the user.
|
|
82
81
|
|
|
83
|
-
|
|
82
|
+
An agent tends to keep the findings most reviewers agree on and drop the rest, which loses much of what the council catches ([measured](#-read-the-council-yourself)). So the MCP payload opens with every finding and its agreement, for the agent to show you.
|
|
84
83
|
|
|
85
84
|
```text
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
85
|
+
┌──────────────────────┐
|
|
86
|
+
│ AI coding agent │
|
|
87
|
+
└──────────┬───────────┘
|
|
88
|
+
│ 1. sends its code or diff
|
|
89
|
+
▼
|
|
90
|
+
┌──────────────────────┐
|
|
91
|
+
│ 4take council │
|
|
92
|
+
└──────────┬───────────┘
|
|
93
|
+
┌────────────┬───────┴────┬────────────┐
|
|
94
|
+
▼ ▼ ▼ ▼
|
|
95
|
+
reviewer 1 reviewer 2 reviewer 3 reviewer 4
|
|
96
|
+
(vendor A) (vendor B) (vendor C) (vendor D)
|
|
97
|
+
└────────────┴───────┬────┴────────────┘
|
|
98
|
+
│ 2. findings grouped and ranked
|
|
99
|
+
▼ by how many raised each one
|
|
100
|
+
┌──────────────────────┐
|
|
101
|
+
│ report_for_user │
|
|
102
|
+
│ e.g. "3/4 HIGH ..." │
|
|
103
|
+
└──────────┬───────────┘
|
|
104
|
+
│ 3. the agent shows every line and
|
|
105
|
+
▼ checks each against the code
|
|
106
|
+
user
|
|
108
107
|
```
|
|
109
108
|
|
|
110
109
|
### Option A: FastMCP Server (`4take-mcp`)
|
|
@@ -188,11 +187,13 @@ cat modified_file.py | 4take --format json --accept-risk --quiet
|
|
|
188
187
|
|
|
189
188
|
```json
|
|
190
189
|
{
|
|
191
|
-
"version": "0.2.
|
|
190
|
+
"version": "0.2.2",
|
|
192
191
|
"timestamp": "2026-08-16T14:20:00Z",
|
|
192
|
+
"config": {"models_requested": 12, "min_responses": 4, "timeout_seconds": 600, "strict": false},
|
|
193
193
|
"summary": {
|
|
194
194
|
"task_hash": "25d297f773c3",
|
|
195
195
|
"responses_received": 4,
|
|
196
|
+
"distinct_models": 4,
|
|
196
197
|
"responses_failed": 0,
|
|
197
198
|
"total_latency_ms": 3200,
|
|
198
199
|
"context": {"files": ["src/mailer.py", "src/users.py (lines 40-90)"], "tokens": 812, "dropped": []}
|
|
@@ -215,12 +216,22 @@ cat modified_file.py | 4take --format json --accept-risk --quiet
|
|
|
215
216
|
"responses": [
|
|
216
217
|
{
|
|
217
218
|
"model": "openrouter/nvidia/nemotron-3-super-120b-a12b:free",
|
|
219
|
+
"served_model": "nvidia/nemotron-3-super-120b-a12b:free",
|
|
220
|
+
"served_provider": "NVIDIA",
|
|
218
221
|
"content": "### Code Review\n1. Found SQL Injection on line 8...",
|
|
222
|
+
"finish_reason": "stop",
|
|
223
|
+
"truncated": false,
|
|
224
|
+
"duplicate_of": null,
|
|
219
225
|
"latency_ms": 1150
|
|
220
226
|
},
|
|
221
227
|
{
|
|
222
228
|
"model": "openrouter/poolside/laguna-s-2.1:free",
|
|
229
|
+
"served_model": "poolside/laguna-s-2.1:free",
|
|
230
|
+
"served_provider": "Poolside",
|
|
223
231
|
"content": "### Security & Performance\n1. Use parameterized query for user_id...",
|
|
232
|
+
"finish_reason": "stop",
|
|
233
|
+
"truncated": false,
|
|
234
|
+
"duplicate_of": null,
|
|
224
235
|
"latency_ms": 1420
|
|
225
236
|
}
|
|
226
237
|
],
|
|
@@ -263,7 +274,7 @@ council took about 8½ minutes, and 6 ran to the 600 s limit. On the 28 short sn
|
|
|
263
274
|
corpus the median was about 50 s, and 27 of 28 councils seated four.
|
|
264
275
|
|
|
265
276
|
### Ready-to-Use Agent Configuration Files:
|
|
266
|
-
- **
|
|
277
|
+
- **Instructions for any agent**: [`AGENTS.md`](https://github.com/aleksbuss/4take/blob/main/AGENTS.md)
|
|
267
278
|
- **Claude Code**: [`CLAUDE.md`](https://github.com/aleksbuss/4take/blob/main/CLAUDE.md)
|
|
268
279
|
- **Antigravity Skill**: [`SKILL.md`](https://github.com/aleksbuss/4take/blob/main/SKILL.md)
|
|
269
280
|
- **Machine Summary**: [`llms.txt`](https://github.com/aleksbuss/4take/blob/main/llms.txt)
|
|
@@ -272,15 +283,15 @@ corpus the median was about 50 s, and 27 of 28 councils seated four.
|
|
|
272
283
|
|
|
273
284
|
## ✨ Features
|
|
274
285
|
|
|
275
|
-
- 🚀 **
|
|
286
|
+
- 🚀 **One Command**: Pipe a file or a diff into `4take`; four free models review it in parallel.
|
|
276
287
|
- 🧠 **Auto-Discovery Ranked on Evidence**: Fetches and filters the free models on OpenRouter, then ranks them by the success rate and median latency they have actually delivered on your machine. The name-based heuristic (parameter count, context window) survives only as the cold-start prior for models you have not run yet.
|
|
277
288
|
- 🔎 **Four Review Axes**: Correctness, security, structure and simplicity — not just defects. Every structural finding has to name its cost, so the breadth does not become a flood of taste.
|
|
278
289
|
- 🤝 **Consensus Grouping**: Findings several models raised independently are grouped and ranked by how many raised them, so the cross-referencing is done for you rather than left in four parallel reviews.
|
|
279
|
-
- 🔄 **Fault-Tolerant Dynamic Rotation**:
|
|
290
|
+
- 🔄 **Fault-Tolerant Dynamic Rotation**: A rate-limited model (HTTP 429) is retried with exponential backoff; one that keeps failing gives its seat to the next model in a pre-ranked pool of up to 12.
|
|
280
291
|
- 🆓 **Free Models Only**: Requests go only to OpenRouter's free (`:free`) models, and a paid model is rejected before anything is sent. Free has limits: roughly 4 to 8 reviews a day on an account that has never been topped up, roughly 80 to 160 once it has ([details](#-openrouter-free-tier--rate-limits)).
|
|
281
292
|
- 🛡️ **Secret Redaction**: Automatically scrubs API keys, passwords, and authorization tokens before sending payloads over the wire. The code itself still goes to third-party free endpoints ([privacy](#%EF%B8%8F-privacy--security)).
|
|
282
293
|
- 🚦 **Cross-Process Rate Limiter**: Shared file-locked token bucket ensures concurrent terminal sessions or background agents never trigger rate limit bans.
|
|
283
|
-
- 🎨 **
|
|
294
|
+
- 🎨 **Terminal and JSON Output**: Markdown rendered in the terminal, or `--format json` for scripts and CI.
|
|
284
295
|
|
|
285
296
|
---
|
|
286
297
|
|
|
@@ -288,9 +299,9 @@ corpus the median was about 50 s, and 27 of 28 councils seated four.
|
|
|
288
299
|
|
|
289
300
|
Greptile published a benchmark of 50 pull requests from five open-source projects (Cal.com,
|
|
290
301
|
Discourse, Grafana, Keycloak, Sentry), each with one labelled bug, together with the comments five
|
|
291
|
-
commercial AI reviewers left on them
|
|
292
|
-
models (commit 2f07956, 8 October 2026). Its reviews were
|
|
293
|
-
analysis written down before the run.
|
|
302
|
+
commercial AI reviewers left on them in late July 2025. Fifteen months later 4take reviewed the
|
|
303
|
+
same 50 diffs using only OpenRouter's free models (commit 2f07956, 8 October 2026). Its reviews were
|
|
304
|
+
graded blind, against a rubric and an analysis written down before the run.
|
|
294
305
|
|
|
295
306
|
| reviewer | benchmark standard, 50 PRs | strict, 42 verified labels |
|
|
296
307
|
| :--- | ---: | ---: |
|
|
@@ -319,6 +330,10 @@ analysis written down before the run.
|
|
|
319
330
|
4take. A strong signal, not proof. The pool has moved on since: two of the four most-seated
|
|
320
331
|
models of that run (apodex-1.1-mini, ling-3.0-flash-sante) have left the free tier, so the same
|
|
321
332
|
councils cannot be seated again.
|
|
333
|
+
- **Fifteen months apart, on public data.** The commercial reviews were written in July 2025 and
|
|
334
|
+
4take's in October 2026, and models improved in between: those tools would probably score higher
|
|
335
|
+
today. The PRs and the benchmark's bug descriptions have been public since 2025, so a model trained
|
|
336
|
+
later may have seen them, and nothing here rules that out. Both effects favour 4take.
|
|
322
337
|
- **Where it is weakest.** Large Java diffs: Keycloak 5 of 10. On two of them, and on one Sentry
|
|
323
338
|
diff, only one free vendor reasoned through to an answer. A council took a median of six
|
|
324
339
|
minutes on these diffs and up to the 600 s timeout.
|
|
@@ -329,30 +344,30 @@ diffs are other projects' code, so the recordings and the graded packets are not
|
|
|
329
344
|
|
|
330
345
|
---
|
|
331
346
|
|
|
332
|
-
## 🧪
|
|
347
|
+
## 🧪 Read the Council Yourself
|
|
333
348
|
|
|
334
|
-
**
|
|
335
|
-
a finding all four raised held a real, labelled defect 96% of the time; one raised by two or three
|
|
336
|
-
held one 60% of the time; one raised by a single reviewer 41%. A single reviewer's finding is a
|
|
337
|
-
pointer to check, not a verdict, and four in ten of them are real.
|
|
338
|
-
|
|
339
|
-
**Read the council's output; don't only hand it to another model.** A
|
|
349
|
+
**Four free models found more than a commercial model alone.** A
|
|
340
350
|
[pre-registered test](https://github.com/aleksbuss/4take/blob/main/docs/benchmark/README.md#does-the-council-help-a-weaker-model)
|
|
341
|
-
on the same 50 PRs gave Gemini 3.8 Flash each diff alone, and
|
|
342
|
-
notes
|
|
351
|
+
on the same 50 PRs, in October 2026, gave Gemini 3.8 Flash (thinking high) each diff alone, and
|
|
352
|
+
again with the council's findings as notes; three runs each, graded blind:
|
|
343
353
|
|
|
344
|
-
| |
|
|
354
|
+
| | bugs found, of 50 |
|
|
345
355
|
| :--- | ---: |
|
|
356
|
+
| **the council's findings, read directly** | **37** |
|
|
346
357
|
| Gemini 3.8 Flash alone | 24 |
|
|
347
|
-
| Gemini 3.8 Flash
|
|
348
|
-
|
|
358
|
+
| Gemini 3.8 Flash given the council's findings | 26 |
|
|
359
|
+
|
|
360
|
+
The council caught 15 bugs the model missed, and the model 2 that the council missed (exact McNemar
|
|
361
|
+
p ≈ 0.002). This comparison was not pre-registered, and it credits the council's best reviewer per
|
|
362
|
+
PR against the model's majority of three runs. Both sides were recorded in the same month on the
|
|
363
|
+
same diffs, so the time gap of the commercial comparison does not arise here, and any exposure of
|
|
364
|
+
the PRs in training data applies to both.
|
|
349
365
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
council). 4take does not make a model a better reviewer by being pasted into its context.
|
|
366
|
+
**An agent that reads the findings keeps only part of them.** Given the council's findings as
|
|
367
|
+
notes, the model caught 26, not 37; against the model alone that is no difference shown (p = 0.69).
|
|
368
|
+
In an exploratory breakdown it took up 3 of the 4 bugs only the council caught that three or four
|
|
369
|
+
reviewers shared, and none of the 11 that one or two raised: it kept the consensus and dropped the
|
|
370
|
+
long tail.
|
|
356
371
|
|
|
357
372
|
**So 4take puts every finding in front of you.** Over MCP the user sees only what the agent
|
|
358
373
|
relays, so the payload opens with `report_for_user`: one line per finding with its agreement
|
|
@@ -360,6 +375,11 @@ relays, so the payload opens with `report_for_user`: one line per finding with i
|
|
|
360
375
|
act on agreement 3 or more, and to check agreement 1 or 2 against the code instead of dropping it.
|
|
361
376
|
The CLI prints the same ranking to your terminal.
|
|
362
377
|
|
|
378
|
+
**Agreement tells you where to look first.** On the eval corpus, over three recordings with four
|
|
379
|
+
reviewers seated, a finding all four raised held a real, labelled defect 96% of the time; one
|
|
380
|
+
raised by two or three held one 60% of the time; one raised by a single reviewer 41%. A single
|
|
381
|
+
reviewer's finding is a pointer to check, not a verdict, and four in ten of them are real.
|
|
382
|
+
|
|
363
383
|
---
|
|
364
384
|
|
|
365
385
|
## 🔎 What the Council Reviews
|
|
@@ -8,7 +8,7 @@ Only the latest release receives security fixes. Today that is 0.2.x.
|
|
|
8
8
|
|
|
9
9
|
Please do not open a public issue for a vulnerability.
|
|
10
10
|
|
|
11
|
-
- Use GitHub's **Report a vulnerability** button
|
|
11
|
+
- Use GitHub's **Report a vulnerability** button on the Security tab, or
|
|
12
12
|
- write to **takelicensing@gmail.com** with the subject `4take security`.
|
|
13
13
|
|
|
14
14
|
Include the output of `4take --version`, the steps to reproduce, and what an attacker gains. A
|
|
@@ -7,7 +7,7 @@ description: Get multiple independent AI opinions on your code using the 4take C
|
|
|
7
7
|
## When to use
|
|
8
8
|
- After writing a new feature or complex algorithm
|
|
9
9
|
- Before committing critical security, database, or async logic
|
|
10
|
-
- When you want
|
|
10
|
+
- When you want several independent AI reviews to catch what one model misses
|
|
11
11
|
- When code works and you want to know whether its structure will survive the next change
|
|
12
12
|
|
|
13
13
|
Reviews four axes: correctness, security, structure and simplicity. Takes code, a diff or
|
|
@@ -19,4 +19,3 @@ carries.
|
|
|
19
19
|
| `recordings_ctx/`, `runs_ctx/` | the cross-file corpus recorded without context, with it, and with `--auto-context` |
|
|
20
20
|
| `record.py` | re-records against live models; the only step that uses the network |
|
|
21
21
|
| `score.py` | scores recordings offline |
|
|
22
|
-
| `backfill_duplicates.py` | one-off migration that restored `duplicate_of` in older recordings |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# 4take —
|
|
1
|
+
# 4take — LLM council for code review (OpenRouter free models)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> CLI and MCP server that sends code to 4 free LLMs from different vendors via OpenRouter for review, with findings grouped by how many reviewers raised them. Source-available under the Business Source License 1.1: free for individuals, solo developers and volunteer projects; organizations need a commercial license.
|
|
4
4
|
|
|
5
5
|
## Quickstart
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ name = "4take"
|
|
|
10
10
|
# Single source of truth: `src/fourtake/__init__.py`. Two hand-maintained version strings
|
|
11
11
|
# drift, and the one that ships on PyPI is not the one `4take --version` prints.
|
|
12
12
|
dynamic = ["version"]
|
|
13
|
-
description = "
|
|
13
|
+
description = "Code review by a council of four free LLMs on OpenRouter, with findings ranked by how many reviewers raised them"
|
|
14
14
|
readme = "README.md"
|
|
15
15
|
license = {text = "BUSL-1.1"}
|
|
16
16
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.2"
|