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.
Files changed (87) hide show
  1. {overkill-0.5.4 → overkill-0.6.1}/.gitignore +3 -0
  2. {overkill-0.5.4 → overkill-0.6.1}/AGENTS.md +1 -0
  3. {overkill-0.5.4 → overkill-0.6.1}/PKG-INFO +6 -4
  4. {overkill-0.5.4 → overkill-0.6.1}/README.md +4 -2
  5. overkill-0.6.1/prompts/active/claude-fix-execute.prompt.md +14 -0
  6. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-fix-execute.prompt.md +2 -1
  7. {overkill-0.5.4 → overkill-0.6.1}/pyproject.toml +2 -2
  8. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/agents.py +2 -2
  9. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget/claude.py +52 -5
  10. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget_report.py +12 -3
  11. {overkill-0.5.4 → overkill-0.6.1}/uv.lock +1 -1
  12. overkill-0.5.4/prompts/active/claude-fix-execute.prompt.md +0 -13
  13. {overkill-0.5.4 → overkill-0.6.1}/.github/workflows/publish.yml +0 -0
  14. {overkill-0.5.4 → overkill-0.6.1}/.github/workflows/test.yml +0 -0
  15. {overkill-0.5.4 → overkill-0.6.1}/.overkillrc.example +0 -0
  16. {overkill-0.5.4 → overkill-0.6.1}/.python-version +0 -0
  17. {overkill-0.5.4 → overkill-0.6.1}/.refactorsuggestrc.example +0 -0
  18. {overkill-0.5.4 → overkill-0.6.1}/CLAUDE.md +0 -0
  19. {overkill-0.5.4 → overkill-0.6.1}/GEMINI.md +0 -0
  20. {overkill-0.5.4 → overkill-0.6.1}/LICENSE +0 -0
  21. {overkill-0.5.4 → overkill-0.6.1}/install.sh +0 -0
  22. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-fix.prompt.md +0 -0
  23. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-fix.prompt.md +0 -0
  24. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-full.prompt.md +0 -0
  25. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-layer.prompt.md +0 -0
  26. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-micro.prompt.md +0 -0
  27. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-refactor-module.prompt.md +0 -0
  28. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-review.prompt.md +0 -0
  29. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/claude-self-review.prompt.md +0 -0
  30. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-refactor-full.prompt.md +0 -0
  31. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-refactor-layer.prompt.md +0 -0
  32. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-refactor-micro.prompt.md +0 -0
  33. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-refactor-module.prompt.md +0 -0
  34. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/codex-review.prompt.md +0 -0
  35. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-refactor-full.prompt.md +0 -0
  36. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-refactor-layer.prompt.md +0 -0
  37. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-refactor-micro.prompt.md +0 -0
  38. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-refactor-module.prompt.md +0 -0
  39. {overkill-0.5.4 → overkill-0.6.1}/prompts/active/gemini-review.prompt.md +0 -0
  40. {overkill-0.5.4 → overkill-0.6.1}/prompts/reference/claude-code-review-plugin.md +0 -0
  41. {overkill-0.5.4 → overkill-0.6.1}/prompts/reference/claude-security-review.md +0 -0
  42. {overkill-0.5.4 → overkill-0.6.1}/prompts/reference/codex-review-original.md +0 -0
  43. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/__init__.py +0 -0
  44. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/__main__.py +0 -0
  45. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget/__init__.py +0 -0
  46. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget/codex.py +0 -0
  47. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/budget/gemini.py +0 -0
  48. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/classify.py +0 -0
  49. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/cli.py +0 -0
  50. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/data/__init__.py +0 -0
  51. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/git_ops.py +0 -0
  52. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/init.py +0 -0
  53. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/json_extract.py +0 -0
  54. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/loop_engine.py +0 -0
  55. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/models.py +0 -0
  56. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/refactor_suggest.py +0 -0
  57. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/reporting.py +0 -0
  58. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/resume.py +0 -0
  59. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/retry.py +0 -0
  60. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/review_loop.py +0 -0
  61. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/self_review.py +0 -0
  62. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/time_utils.py +0 -0
  63. {overkill-0.5.4 → overkill-0.6.1}/src/mr_overkill/two_step_fix.py +0 -0
  64. {overkill-0.5.4 → overkill-0.6.1}/tests/__init__.py +0 -0
  65. {overkill-0.5.4 → overkill-0.6.1}/tests/conftest.py +0 -0
  66. {overkill-0.5.4 → overkill-0.6.1}/tests/test_agents.py +0 -0
  67. {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_claude.py +0 -0
  68. {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_codex.py +0 -0
  69. {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_gemini.py +0 -0
  70. {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_policy.py +0 -0
  71. {overkill-0.5.4 → overkill-0.6.1}/tests/test_budget_report.py +0 -0
  72. {overkill-0.5.4 → overkill-0.6.1}/tests/test_classify.py +0 -0
  73. {overkill-0.5.4 → overkill-0.6.1}/tests/test_cli.py +0 -0
  74. {overkill-0.5.4 → overkill-0.6.1}/tests/test_git_ops.py +0 -0
  75. {overkill-0.5.4 → overkill-0.6.1}/tests/test_init.py +0 -0
  76. {overkill-0.5.4 → overkill-0.6.1}/tests/test_integration.py +0 -0
  77. {overkill-0.5.4 → overkill-0.6.1}/tests/test_json_extract.py +0 -0
  78. {overkill-0.5.4 → overkill-0.6.1}/tests/test_loop_engine.py +0 -0
  79. {overkill-0.5.4 → overkill-0.6.1}/tests/test_refactor_suggest.py +0 -0
  80. {overkill-0.5.4 → overkill-0.6.1}/tests/test_reporting.py +0 -0
  81. {overkill-0.5.4 → overkill-0.6.1}/tests/test_resume.py +0 -0
  82. {overkill-0.5.4 → overkill-0.6.1}/tests/test_retry.py +0 -0
  83. {overkill-0.5.4 → overkill-0.6.1}/tests/test_review_loop.py +0 -0
  84. {overkill-0.5.4 → overkill-0.6.1}/tests/test_self_review.py +0 -0
  85. {overkill-0.5.4 → overkill-0.6.1}/tests/test_time_utils.py +0 -0
  86. {overkill-0.5.4 → overkill-0.6.1}/tests/test_two_step_fix.py +0 -0
  87. {overkill-0.5.4 → overkill-0.6.1}/uninstall.sh +0 -0
@@ -8,6 +8,9 @@
8
8
  # Legacy directory (pre-migration)
9
9
  .review-loop/
10
10
 
11
+ # Environment
12
+ .env
13
+
11
14
  # Python
12
15
  __pycache__/
13
16
  *.py[cod]
@@ -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.5.4
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 both — that's the point):
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 # Codex CLI
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 both — that's the point):
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 # Codex CLI
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.5.4"
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", "plan", "-p", "-"],
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", "plan", "-p", "-"],
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
- # Rough 5-hour token limits per tier (empirical estimates).
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": 1_000_000,
40
- "max5": 5_000_000,
41
- "max20": 20_000_000,
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" * 24 # ════════════════════════
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
@@ -247,7 +247,7 @@ wheels = [
247
247
 
248
248
  [[package]]
249
249
  name = "overkill"
250
- version = "0.5.0"
250
+ version = "0.6.1"
251
251
  source = { editable = "." }
252
252
 
253
253
  [package.dev-dependencies]
@@ -1,13 +0,0 @@
1
- 좋아, 네 판단대로 고쳐줘.
2
-
3
- - 네가 동의한 finding만 고쳐
4
- - SKIP하겠다고 한 건 고치지 마
5
- - 최소한의 변경만 해. 기존 코드 스타일을 따라
6
- - 끝나면 아래 형식으로 요약해줘:
7
-
8
- ```
9
- ## Fix Summary
10
-
11
- - [FIXED] <finding title>: <변경 내용>
12
- - [SKIPPED] <finding title>: <SKIP 사유>
13
- ```
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