overkill 0.5.4__tar.gz → 0.6.1__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.
- {overkill-0.5.4 → overkill-0.6.1}/.gitignore +3 -0
- {overkill-0.5.4 → overkill-0.6.1}/AGENTS.md +1 -0
- {overkill-0.5.4 → overkill-0.6.1}/PKG-INFO +6 -4
- {overkill-0.5.4 → overkill-0.6.1}/README.md +4 -2
- overkill-0.6.1/prompts/active/claude-fix-execute.prompt.md +14 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-fix-execute.prompt.md +2 -1
- {overkill-0.5.4 → overkill-0.6.1}/pyproject.toml +2 -2
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/agents.py +2 -2
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget/claude.py +52 -5
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget_report.py +12 -3
- {overkill-0.5.4 → overkill-0.6.1}/uv.lock +1 -1
- overkill-0.5.4/prompts/active/claude-fix-execute.prompt.md +0 -13
- {overkill-0.5.4 → overkill-0.6.1}/.github/workflows/publish.yml +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/.github/workflows/test.yml +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/.overkillrc.example +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/.python-version +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/.refactorsuggestrc.example +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/CLAUDE.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/GEMINI.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/LICENSE +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/install.sh +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-fix.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-fix.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-full.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-layer.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-micro.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-module.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-review.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-self-review.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-refactor-full.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-refactor-layer.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-refactor-micro.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-refactor-module.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-review.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-refactor-full.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-refactor-layer.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-refactor-micro.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-refactor-module.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-review.prompt.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/reference/claude-code-review-plugin.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/reference/claude-security-review.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/prompts/reference/codex-review-original.md +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/__init__.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/__main__.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget/__init__.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget/codex.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget/gemini.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/classify.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/cli.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/data/__init__.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/git_ops.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/init.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/json_extract.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/loop_engine.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/models.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/refactor_suggest.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/reporting.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/resume.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/retry.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/review_loop.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/self_review.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/time_utils.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/two_step_fix.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/__init__.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/conftest.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_agents.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_claude.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_codex.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_gemini.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_policy.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_report.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_classify.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_cli.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_git_ops.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_init.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_integration.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_json_extract.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_loop_engine.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_refactor_suggest.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_reporting.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_resume.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_retry.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_review_loop.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_self_review.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_time_utils.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/tests/test_two_step_fix.py +0 -0
- {overkill-0.5.4 → overkill-0.6.1}/uninstall.sh +0 -0
|
@@ -49,6 +49,7 @@ git log HEAD..origin/<target-branch> --oneline
|
|
|
49
49
|
2. **Release branch → main**
|
|
50
50
|
- Create `release/x.y.z` from `develop`
|
|
51
51
|
- Bump version in `pyproject.toml` (`[project] version`)
|
|
52
|
+
- Run `uv lock` to sync `uv.lock` with the new version
|
|
52
53
|
- Commit: `chore: bump version to x.y.z`
|
|
53
54
|
- Push and open PR targeting `main`
|
|
54
55
|
- Run review loop — if the review produces fix commits, merge them into `develop` first (via PR) before merging the release PR into `main`
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: overkill
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: AI-powered code review loop — automates Codex review + Claude fix cycles
|
|
3
|
+
Version: 0.6.1
|
|
4
|
+
Summary: AI-powered code review loop — automates Codex/Gemini review + Claude fix cycles
|
|
5
5
|
Project-URL: Repository, https://github.com/modocai/mr-overkill
|
|
6
6
|
Project-URL: Issues, https://github.com/modocai/mr-overkill/issues
|
|
7
7
|
Author: ModocAI
|
|
@@ -73,10 +73,11 @@ git clone --depth 1 https://github.com/modocai/mr-overkill.git /tmp/overkill \
|
|
|
73
73
|
|
|
74
74
|
You need these to participate in the madness:
|
|
75
75
|
|
|
76
|
-
**Accounts** (yes, you need
|
|
76
|
+
**Accounts** (yes, you need all three — that's the point):
|
|
77
77
|
|
|
78
78
|
- [OpenAI](https://platform.openai.com/) account (paid plan) — because free tier is for weak code
|
|
79
79
|
- [Anthropic](https://console.anthropic.com/) account (Pro/Max plan or API credits) — because Claude needs to think *deeply* about your variable names
|
|
80
|
+
- [Google AI](https://aistudio.google.com/) account (API key) — because Gemini wants in on the overkill too
|
|
80
81
|
|
|
81
82
|
**Runtime**:
|
|
82
83
|
|
|
@@ -87,8 +88,9 @@ You need these to participate in the madness:
|
|
|
87
88
|
**CLI Tools**:
|
|
88
89
|
|
|
89
90
|
```bash
|
|
90
|
-
npm install -g @openai/codex
|
|
91
|
+
npm install -g @openai/codex # Codex CLI
|
|
91
92
|
npm install -g @anthropic-ai/claude-code # Claude Code CLI
|
|
93
|
+
npm install -g @google/gemini-cli # Gemini CLI
|
|
92
94
|
```
|
|
93
95
|
|
|
94
96
|
- [jq](https://jqlang.github.io/jq/) — JSON processor
|
|
@@ -50,10 +50,11 @@ git clone --depth 1 https://github.com/modocai/mr-overkill.git /tmp/overkill \
|
|
|
50
50
|
|
|
51
51
|
You need these to participate in the madness:
|
|
52
52
|
|
|
53
|
-
**Accounts** (yes, you need
|
|
53
|
+
**Accounts** (yes, you need all three — that's the point):
|
|
54
54
|
|
|
55
55
|
- [OpenAI](https://platform.openai.com/) account (paid plan) — because free tier is for weak code
|
|
56
56
|
- [Anthropic](https://console.anthropic.com/) account (Pro/Max plan or API credits) — because Claude needs to think *deeply* about your variable names
|
|
57
|
+
- [Google AI](https://aistudio.google.com/) account (API key) — because Gemini wants in on the overkill too
|
|
57
58
|
|
|
58
59
|
**Runtime**:
|
|
59
60
|
|
|
@@ -64,8 +65,9 @@ You need these to participate in the madness:
|
|
|
64
65
|
**CLI Tools**:
|
|
65
66
|
|
|
66
67
|
```bash
|
|
67
|
-
npm install -g @openai/codex
|
|
68
|
+
npm install -g @openai/codex # Codex CLI
|
|
68
69
|
npm install -g @anthropic-ai/claude-code # Claude Code CLI
|
|
70
|
+
npm install -g @google/gemini-cli # Gemini CLI
|
|
69
71
|
```
|
|
70
72
|
|
|
71
73
|
- [jq](https://jqlang.github.io/jq/) — JSON processor
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
좋아, 네 판단대로 고쳐줘.
|
|
2
|
+
|
|
3
|
+
- 네가 동의한 finding만 고쳐
|
|
4
|
+
- SKIP하겠다고 한 finding 자체는 고치지 마. 단, opinion에서 같은 false positive 재발을 방지하는 작은 조치를 제안했으면 (주석 추가, @deprecated 표시, dead code 제거 등) 그건 해줘.
|
|
5
|
+
- 최소한의 변경만 해. 기존 코드 스타일을 따라
|
|
6
|
+
- 끝나면 아래 형식으로 요약해줘:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
## Fix Summary
|
|
10
|
+
|
|
11
|
+
- [FIXED] <finding title>: <변경 내용>
|
|
12
|
+
- [SKIPPED] <finding title>: <SKIP 사유>
|
|
13
|
+
- [SKIPPED] <finding title>: <SKIP 사유> → 예방 조치: <조치 내용>
|
|
14
|
+
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
좋아, 네 판단대로 리팩토링해줘.
|
|
2
2
|
|
|
3
3
|
- 네가 동의한 finding만 고쳐
|
|
4
|
-
- SKIP하겠다고 한
|
|
4
|
+
- SKIP하겠다고 한 finding 자체는 고치지 마. 단, opinion에서 같은 false positive 재발을 방지하는 작은 조치를 제안했으면 (주석 추가, @deprecated 표시, dead code 제거 등) 그건 해줘.
|
|
5
5
|
- refactoring_plan의 steps 순서대로 진행해
|
|
6
6
|
- 각 step이 끝날 때마다 코드가 정상 동작하는 상태를 유지해
|
|
7
7
|
- 최소한의 변경만 해. 기존 코드 스타일을 따라
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
- [FIXED] <finding title>: <변경 내용>
|
|
14
14
|
- [SKIPPED] <finding title>: <SKIP 사유>
|
|
15
|
+
- [SKIPPED] <finding title>: <SKIP 사유> → 예방 조치: <조치 내용>
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
## Safety Guards
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "overkill"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "AI-powered code review loop — automates Codex review + Claude fix cycles"
|
|
3
|
+
version = "0.6.1"
|
|
4
|
+
description = "AI-powered code review loop — automates Codex/Gemini review + Claude fix cycles"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
7
7
|
requires-python = ">=3.11"
|
|
@@ -385,7 +385,7 @@ class GeminiReviewAgent(ReviewAgent):
|
|
|
385
385
|
return retry_gemini_cmd(
|
|
386
386
|
output_path,
|
|
387
387
|
"Gemini review",
|
|
388
|
-
["gemini", "--sandbox", "--approval-mode", "
|
|
388
|
+
["gemini", "--sandbox", "--approval-mode", "yolo", "-p", "-"],
|
|
389
389
|
stdin=prompt_text,
|
|
390
390
|
max_wait=config.retry_max_wait,
|
|
391
391
|
initial_wait=config.retry_initial_wait,
|
|
@@ -441,7 +441,7 @@ class GeminiRefactorReviewAgent(ReviewAgent):
|
|
|
441
441
|
return retry_gemini_cmd(
|
|
442
442
|
output_path,
|
|
443
443
|
"Gemini analysis",
|
|
444
|
-
["gemini", "--sandbox", "--approval-mode", "
|
|
444
|
+
["gemini", "--sandbox", "--approval-mode", "yolo", "-p", "-"],
|
|
445
445
|
stdin=prompt_text,
|
|
446
446
|
max_wait=config.retry_max_wait,
|
|
447
447
|
initial_wait=config.retry_initial_wait,
|
|
@@ -34,11 +34,14 @@ def _sum_usage(usage: dict[str, int]) -> float:
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
#
|
|
37
|
+
# Approximate 5-hour token limits per tier.
|
|
38
|
+
# Derived from comparing local weighted-token sums against OAuth utilization
|
|
39
|
+
# percentages (e.g. 5.4M weighted tokens ≈ 60% → ~9-10M limit for max5).
|
|
40
|
+
# These are best-effort estimates; the authoritative value comes from OAuth.
|
|
38
41
|
_TIER_LIMITS: dict[str, int] = {
|
|
39
|
-
"pro":
|
|
40
|
-
"max5":
|
|
41
|
-
"max20":
|
|
42
|
+
"pro": 2_000_000,
|
|
43
|
+
"max5": 10_000_000,
|
|
44
|
+
"max20": 40_000_000,
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
_TIER_MAP: dict[str, str] = {
|
|
@@ -129,11 +132,13 @@ def check_oauth() -> BudgetStatus | None:
|
|
|
129
132
|
timeout=5,
|
|
130
133
|
)
|
|
131
134
|
if result.returncode != 0 or not result.stdout.strip():
|
|
135
|
+
logger.debug("OAuth: keychain access failed (rc=%d)", result.returncode)
|
|
132
136
|
return None
|
|
133
137
|
|
|
134
138
|
creds = json.loads(result.stdout.strip())
|
|
135
139
|
token = creds.get("claudeAiOauth", {}).get("accessToken")
|
|
136
140
|
if not token:
|
|
141
|
+
logger.debug("OAuth: no access token in credentials")
|
|
137
142
|
return None
|
|
138
143
|
|
|
139
144
|
resp = subprocess.run(
|
|
@@ -153,12 +158,25 @@ def check_oauth() -> BudgetStatus | None:
|
|
|
153
158
|
timeout=15,
|
|
154
159
|
)
|
|
155
160
|
if resp.returncode != 0 or not resp.stdout.strip():
|
|
161
|
+
logger.debug("OAuth: API request failed (rc=%d)", resp.returncode)
|
|
156
162
|
return None
|
|
157
163
|
|
|
158
164
|
data = json.loads(resp.stdout)
|
|
165
|
+
|
|
166
|
+
# Check for API error response (e.g. rate_limit_error)
|
|
167
|
+
if "error" in data:
|
|
168
|
+
err = data["error"]
|
|
169
|
+
logger.warning(
|
|
170
|
+
"OAuth: API error — %s: %s",
|
|
171
|
+
err.get("type", "unknown"),
|
|
172
|
+
err.get("message", ""),
|
|
173
|
+
)
|
|
174
|
+
return None
|
|
175
|
+
|
|
159
176
|
five_hour = data.get("five_hour", {})
|
|
160
177
|
utilization = five_hour.get("utilization")
|
|
161
178
|
if utilization is None:
|
|
179
|
+
logger.debug("OAuth: no utilization in response")
|
|
162
180
|
return None
|
|
163
181
|
|
|
164
182
|
tier = detect_tier()
|
|
@@ -179,7 +197,8 @@ def check_oauth() -> BudgetStatus | None:
|
|
|
179
197
|
json.JSONDecodeError,
|
|
180
198
|
KeyError,
|
|
181
199
|
OSError,
|
|
182
|
-
):
|
|
200
|
+
) as exc:
|
|
201
|
+
logger.debug("OAuth: %s", exc)
|
|
183
202
|
return None
|
|
184
203
|
|
|
185
204
|
|
|
@@ -243,6 +262,16 @@ def check_local(projects_dir: Path | None = None) -> BudgetStatus:
|
|
|
243
262
|
|
|
244
263
|
pct = (int(total_tokens) * 100 // limit) if total_tokens > 0 and limit > 0 else 0
|
|
245
264
|
|
|
265
|
+
logger.info(
|
|
266
|
+
"Local estimate: %d%% (%d weighted tokens / %d limit, "
|
|
267
|
+
"tier=%s, %d deduplicated messages)",
|
|
268
|
+
pct,
|
|
269
|
+
int(total_tokens),
|
|
270
|
+
limit,
|
|
271
|
+
tier,
|
|
272
|
+
len(seen_ids),
|
|
273
|
+
)
|
|
274
|
+
|
|
246
275
|
return BudgetStatus(
|
|
247
276
|
five_hour_used_pct=pct,
|
|
248
277
|
seven_day_used_pct=None,
|
|
@@ -311,6 +340,24 @@ def check_token_budget() -> BudgetStatus:
|
|
|
311
340
|
result = check_oauth()
|
|
312
341
|
if result is not None:
|
|
313
342
|
_save_cache(result)
|
|
343
|
+
# Run local estimate for calibration only when debug logging is active;
|
|
344
|
+
# check_local() does a full rglob scan which is too expensive for the
|
|
345
|
+
# normal OAuth fast path.
|
|
346
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
347
|
+
try:
|
|
348
|
+
local = check_local()
|
|
349
|
+
if local.five_hour_used_pct is not None:
|
|
350
|
+
logger.debug(
|
|
351
|
+
"Budget calibration: oauth=%d%% local=%d%% "
|
|
352
|
+
"(diff=%+dpp, %d weighted tokens, tier=%s)",
|
|
353
|
+
result.five_hour_used_pct or 0,
|
|
354
|
+
local.five_hour_used_pct,
|
|
355
|
+
(local.five_hour_used_pct) - (result.five_hour_used_pct or 0),
|
|
356
|
+
local.tokens_used,
|
|
357
|
+
local.tier,
|
|
358
|
+
)
|
|
359
|
+
except Exception:
|
|
360
|
+
logger.debug("Calibration check_local() failed", exc_info=True)
|
|
314
361
|
return result
|
|
315
362
|
|
|
316
363
|
cached = _load_cache()
|
|
@@ -11,7 +11,7 @@ from mr_overkill.budget.claude import check_token_budget as claude_check
|
|
|
11
11
|
from mr_overkill.budget.codex import check_token_budget as codex_check
|
|
12
12
|
from mr_overkill.models import BudgetScope, BudgetStatus
|
|
13
13
|
|
|
14
|
-
_HEADER = "\u2550" *
|
|
14
|
+
_HEADER = "\u2550" * 33 # ═════════════════════════════════
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def _fmt_pct(pct: int | None) -> str:
|
|
@@ -58,10 +58,17 @@ def _print_codex(status: BudgetStatus) -> None:
|
|
|
58
58
|
print()
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
def _print_gemini() -> None:
|
|
62
|
+
print("Gemini Budget")
|
|
63
|
+
print(_HEADER)
|
|
64
|
+
print(" No local budget data (API-key billing).")
|
|
65
|
+
print()
|
|
66
|
+
|
|
67
|
+
|
|
61
68
|
def _print_scope_table(claude_st: BudgetStatus, codex_st: BudgetStatus) -> None:
|
|
62
69
|
print("Scope Thresholds")
|
|
63
70
|
print(_HEADER)
|
|
64
|
-
print(" Claude Codex")
|
|
71
|
+
print(" Claude Codex Gemini")
|
|
65
72
|
scopes = (
|
|
66
73
|
BudgetScope.MICRO, BudgetScope.MODULE,
|
|
67
74
|
BudgetScope.LAYER, BudgetScope.FULL,
|
|
@@ -72,7 +79,7 @@ def _print_scope_table(claude_st: BudgetStatus, codex_st: BudgetStatus) -> None:
|
|
|
72
79
|
x_label = "GO" if budget_sufficient(scope, codex_st) else "NOGO"
|
|
73
80
|
else:
|
|
74
81
|
c_label = x_label = "—"
|
|
75
|
-
print(f" {scope.value:<8} {c_label:<7} {x_label}")
|
|
82
|
+
print(f" {scope.value:<8} {c_label:<7} {x_label:<7} —")
|
|
76
83
|
|
|
77
84
|
|
|
78
85
|
def print_budget_report(*, json_mode: bool = False) -> int:
|
|
@@ -88,11 +95,13 @@ def print_budget_report(*, json_mode: bool = False) -> int:
|
|
|
88
95
|
payload = {
|
|
89
96
|
"claude": asdict(claude_st),
|
|
90
97
|
"codex": asdict(codex_st),
|
|
98
|
+
"gemini": {"mode": "api-key", "note": "no local budget data"},
|
|
91
99
|
}
|
|
92
100
|
print(json.dumps(payload, indent=2))
|
|
93
101
|
return 0
|
|
94
102
|
|
|
95
103
|
_print_claude(claude_st)
|
|
96
104
|
_print_codex(codex_st)
|
|
105
|
+
_print_gemini()
|
|
97
106
|
_print_scope_table(claude_st, codex_st)
|
|
98
107
|
return 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|