claude-dev-env 1.38.1 → 1.40.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 (282) hide show
  1. package/CLAUDE.md +10 -36
  2. package/_shared/pr-loop/audit-reply-template.md +147 -0
  3. package/_shared/pr-loop/fix-protocol.md +25 -4
  4. package/_shared/pr-loop/gh-payloads.md +37 -50
  5. package/_shared/pr-loop/scripts/code_rules_gate.py +0 -60
  6. package/_shared/pr-loop/scripts/config/post_audit_thread_constants.py +199 -0
  7. package/_shared/pr-loop/scripts/config/reviews_disabled_constants.py +8 -0
  8. package/_shared/pr-loop/scripts/post_audit_thread.py +1242 -0
  9. package/_shared/pr-loop/scripts/preflight.py +129 -2
  10. package/_shared/pr-loop/scripts/reviews_disabled.py +59 -0
  11. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -19
  12. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +1116 -0
  13. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +127 -0
  14. package/_shared/pr-loop/scripts/tests/test_preflight.py +41 -0
  15. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +36 -0
  16. package/_shared/pr-loop/state-schema.md +1 -1
  17. package/agents/clean-coder.md +2 -2
  18. package/agents/pr-description-writer.md +150 -52
  19. package/bin/install.mjs +6 -7
  20. package/bin/install.test.mjs +8 -0
  21. package/commands/doc-gist.md +16 -0
  22. package/commands/plan.md +0 -2
  23. package/commands/review-plan.md +1 -1
  24. package/docs/CODE_RULES.md +122 -2
  25. package/docs/PR_DESCRIPTION_GUIDE.md +127 -64
  26. package/hooks/blocking/bot_mention_comment_blocker.py +75 -0
  27. package/hooks/blocking/code_rules_enforcer.py +1143 -129
  28. package/hooks/blocking/convergence_gate_blocker.py +130 -0
  29. package/hooks/blocking/destructive_command_blocker.py +74 -0
  30. package/hooks/blocking/gh_body_arg_blocker.py +30 -0
  31. package/hooks/blocking/md_to_html_blocker.py +119 -0
  32. package/hooks/blocking/pr_description_enforcer.py +57 -22
  33. package/hooks/blocking/test_bot_mention_comment_blocker.py +131 -0
  34. package/hooks/blocking/test_code_rules_enforcer.py +21 -0
  35. package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +70 -0
  36. package/hooks/blocking/test_code_rules_enforcer_any_imports_and_cast.py +92 -0
  37. package/hooks/blocking/test_code_rules_enforcer_banned_import_alias.py +143 -0
  38. package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +152 -0
  39. package/hooks/blocking/test_code_rules_enforcer_bare_except.py +120 -0
  40. package/hooks/blocking/test_code_rules_enforcer_boundary_types.py +175 -0
  41. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
  42. package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +50 -0
  43. package/hooks/blocking/test_code_rules_enforcer_docstring_format.py +255 -0
  44. package/hooks/blocking/test_code_rules_enforcer_inline_tuple_string_magic.py +130 -0
  45. package/hooks/blocking/test_code_rules_enforcer_stub_implementations.py +141 -0
  46. package/hooks/blocking/test_code_rules_enforcer_test_branching.py +143 -0
  47. package/hooks/blocking/test_code_rules_enforcer_thin_wrapper_files.py +169 -0
  48. package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +99 -0
  49. package/hooks/blocking/test_code_rules_enforcer_typed_dict_pairs.py +141 -0
  50. package/hooks/blocking/test_convergence_gate_blocker.py +63 -0
  51. package/hooks/blocking/test_destructive_command_blocker.py +146 -0
  52. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +102 -0
  53. package/hooks/blocking/test_gh_body_arg_blocker.py +45 -0
  54. package/hooks/blocking/test_md_to_html_blocker.py +317 -0
  55. package/hooks/blocking/test_pr_description_enforcer.py +69 -8
  56. package/hooks/config/any_type_config.py +7 -0
  57. package/hooks/config/banned_identifiers_constants.py +11 -0
  58. package/hooks/config/blocking_check_limits.py +38 -0
  59. package/hooks/config/bot_mention_comment_blocker_constants.py +20 -0
  60. package/hooks/config/code_rules_enforcer_constants.py +53 -0
  61. package/hooks/config/convergence_branch_constants.py +9 -0
  62. package/hooks/config/doc_gist_auto_publish_constants.py +18 -0
  63. package/hooks/config/html_companion_constants.py +20 -0
  64. package/hooks/config/inline_tuple_string_magic_constants.py +22 -0
  65. package/hooks/config/pr_description_enforcer_constants.py +14 -0
  66. package/hooks/config/test_banned_identifiers_constants.py +17 -0
  67. package/hooks/hooks.json +28 -20
  68. package/hooks/pyproject.toml +69 -0
  69. package/hooks/validators/mypy_integration.py +47 -1
  70. package/hooks/validators/run_all_validators.py +3 -3
  71. package/hooks/validators/test_mypy_integration.py +50 -1
  72. package/hooks/workflow/doc_gist_auto_publish.py +144 -0
  73. package/hooks/workflow/md_to_html_companion.py +365 -0
  74. package/hooks/workflow/test_doc_gist_auto_publish.py +117 -0
  75. package/hooks/workflow/test_md_to_html_companion.py +452 -0
  76. package/package.json +1 -1
  77. package/rules/gh-body-file.md +2 -0
  78. package/scripts/Install-SweepEmptyDirs.ps1 +111 -0
  79. package/scripts/check.ps1 +106 -0
  80. package/scripts/config/timing.py +11 -0
  81. package/scripts/sweep_empty_dirs.py +138 -0
  82. package/scripts/sync_to_cursor/rules.py +1 -1
  83. package/scripts/test_sweep_empty_dirs.py +183 -0
  84. package/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +323 -0
  85. package/skills/_shared/pr-loop/scripts/_cli_utils.py +22 -0
  86. package/skills/_shared/pr-loop/scripts/_path_resolver.py +165 -0
  87. package/skills/_shared/pr-loop/scripts/_xml_utils.py +20 -0
  88. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +182 -0
  89. package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +185 -0
  90. package/skills/_shared/pr-loop/scripts/config/__init__.py +0 -0
  91. package/skills/_shared/pr-loop/scripts/config/path_resolver_constants.py +78 -0
  92. package/skills/_shared/pr-loop/scripts/init_loop_state.py +135 -0
  93. package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +175 -0
  94. package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +182 -0
  95. package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +206 -0
  96. package/skills/bugteam/CONSTRAINTS.md +21 -22
  97. package/skills/bugteam/EXAMPLES.md +3 -3
  98. package/skills/bugteam/PROMPTS.md +227 -67
  99. package/skills/bugteam/SKILL.md +132 -455
  100. package/skills/bugteam/reference/README.md +1 -1
  101. package/skills/bugteam/reference/audit-and-teammates.md +112 -39
  102. package/skills/bugteam/reference/audit-contract.md +4 -22
  103. package/skills/bugteam/reference/copilot-gap-analysis.md +8 -5
  104. package/skills/bugteam/reference/design-rationale.md +2 -2
  105. package/skills/bugteam/reference/github-pr-reviews.md +50 -57
  106. package/skills/bugteam/reference/obstacles/audit-assign-ids.md +13 -0
  107. package/skills/bugteam/reference/obstacles/audit-capture-excerpts.md +13 -0
  108. package/skills/bugteam/reference/obstacles/audit-walk-categories.md +13 -0
  109. package/skills/bugteam/reference/obstacles/audit-write-xml.md +13 -0
  110. package/skills/bugteam/reference/obstacles/fix-append-summary.md +13 -0
  111. package/skills/bugteam/reference/obstacles/fix-apply-fixes.md +13 -0
  112. package/skills/bugteam/reference/obstacles/fix-git-add-commit.md +13 -0
  113. package/skills/bugteam/reference/obstacles/fix-git-push.md +13 -0
  114. package/skills/bugteam/reference/obstacles/fix-post-reply.md +13 -0
  115. package/skills/bugteam/reference/obstacles/fix-publish-summary.md +13 -0
  116. package/skills/bugteam/reference/obstacles/fix-py-compile.md +13 -0
  117. package/skills/bugteam/reference/obstacles/fix-read-files.md +13 -0
  118. package/skills/bugteam/reference/obstacles/fix-resolve-thread.md +13 -0
  119. package/skills/bugteam/reference/obstacles/fix-test-suite.md +13 -0
  120. package/skills/bugteam/reference/obstacles/fix-violation-count.md +13 -0
  121. package/skills/bugteam/reference/obstacles/fix-write-xml.md +13 -0
  122. package/skills/bugteam/reference/team-setup.md +111 -9
  123. package/skills/bugteam/reference/teardown-publish-permissions.md +39 -8
  124. package/skills/bugteam/scripts/README.md +60 -0
  125. package/skills/bugteam/scripts/_claude_permissions_common.py +358 -0
  126. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +976 -0
  127. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +375 -0
  128. package/skills/bugteam/scripts/bugteam_preflight.py +328 -0
  129. package/skills/bugteam/scripts/config/bugteam_code_rules_gate_constants.py +25 -0
  130. package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +26 -0
  131. package/skills/bugteam/scripts/config/bugteam_preflight_constants.py +35 -0
  132. package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +20 -0
  133. package/skills/bugteam/scripts/config/probe_code_rules_enforcer_check_constants.py +12 -0
  134. package/skills/bugteam/scripts/config/windows_safe_rmtree_constants.py +7 -0
  135. package/skills/bugteam/scripts/grant_project_claude_permissions.py +175 -0
  136. package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +107 -0
  137. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +220 -0
  138. package/skills/bugteam/scripts/test__claude_permissions_common.py +112 -0
  139. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +400 -0
  140. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +384 -0
  141. package/skills/bugteam/scripts/test_bugteam_preflight.py +309 -0
  142. package/skills/bugteam/scripts/test_claude_permissions_common.py +195 -0
  143. package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +55 -0
  144. package/skills/bugteam/scripts/test_probe_code_rules_enforcer_check.py +76 -0
  145. package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +55 -0
  146. package/skills/bugteam/scripts/test_windows_safe_rmtree.py +108 -0
  147. package/skills/bugteam/scripts/windows_safe_rmtree.py +100 -0
  148. package/skills/bugteam/test_skill_additions.py +1 -11
  149. package/skills/code/SKILL.md +176 -0
  150. package/skills/copilot-review/SKILL.md +16 -0
  151. package/skills/doc-gist/SKILL.md +99 -0
  152. package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +453 -0
  153. package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +515 -0
  154. package/skills/doc-gist/references/examples/03-code-review-pr.html +638 -0
  155. package/skills/doc-gist/references/examples/04-code-understanding.html +491 -0
  156. package/skills/doc-gist/references/examples/05-design-system.html +629 -0
  157. package/skills/doc-gist/references/examples/06-component-variants.html +605 -0
  158. package/skills/doc-gist/references/examples/07-prototype-animation.html +455 -0
  159. package/skills/doc-gist/references/examples/08-prototype-interaction.html +396 -0
  160. package/skills/doc-gist/references/examples/09-slide-deck.html +592 -0
  161. package/skills/doc-gist/references/examples/10-svg-illustrations.html +492 -0
  162. package/skills/doc-gist/references/examples/11-status-report.html +528 -0
  163. package/skills/doc-gist/references/examples/12-incident-report.html +596 -0
  164. package/skills/doc-gist/references/examples/13-flowchart-diagram.html +395 -0
  165. package/skills/doc-gist/references/examples/14-research-feature-explainer.html +381 -0
  166. package/skills/doc-gist/references/examples/15-research-concept-explainer.html +368 -0
  167. package/skills/doc-gist/references/examples/16-implementation-plan.html +702 -0
  168. package/skills/doc-gist/references/examples/17-pr-writeup.html +595 -0
  169. package/skills/doc-gist/references/examples/18-editor-triage-board.html +573 -0
  170. package/skills/doc-gist/references/examples/19-editor-feature-flags.html +663 -0
  171. package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +722 -0
  172. package/skills/doc-gist/references/examples/README.md +5 -0
  173. package/skills/doc-gist/scripts/config/__init__.py +0 -0
  174. package/skills/doc-gist/scripts/config/gist_upload_constants.py +16 -0
  175. package/skills/doc-gist/scripts/gist_upload.py +177 -0
  176. package/skills/doc-gist/scripts/test_gist_upload.py +51 -0
  177. package/skills/findbugs/SKILL.md +96 -2
  178. package/skills/monitor-open-prs/SKILL.md +14 -32
  179. package/skills/monitor-open-prs/test_skill_contract.py +0 -11
  180. package/skills/pr-consistency-audit/SKILL.md +112 -0
  181. package/skills/pr-consistency-audit/reference/detection-rules.md +96 -0
  182. package/skills/pr-consistency-audit/reference/illustrations.md +78 -0
  183. package/skills/pr-converge/SKILL.md +229 -23
  184. package/skills/pr-converge/config/__init__.py +0 -0
  185. package/skills/pr-converge/config/constants.py +63 -0
  186. package/skills/pr-converge/reference/convergence-gates.md +138 -44
  187. package/skills/pr-converge/reference/examples.md +43 -11
  188. package/skills/pr-converge/reference/fix-protocol.md +6 -5
  189. package/skills/pr-converge/reference/ground-rules.md +5 -3
  190. package/skills/pr-converge/reference/multi-pr-orchestration.md +44 -19
  191. package/skills/pr-converge/reference/obstacles/fix-post-replies.md +13 -0
  192. package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +13 -0
  193. package/skills/pr-converge/reference/obstacles/fix-push.md +13 -0
  194. package/skills/pr-converge/reference/obstacles/fix-read-filelines.md +13 -0
  195. package/skills/pr-converge/reference/obstacles/fix-reset-state.md +13 -0
  196. package/skills/pr-converge/reference/obstacles/fix-resolve-threads.md +13 -0
  197. package/skills/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +13 -0
  198. package/skills/pr-converge/reference/obstacles/fix-stage-commit.md +13 -0
  199. package/skills/pr-converge/reference/obstacles/fix-trigger-bugbot.md +13 -0
  200. package/skills/pr-converge/reference/obstacles/fix-write-test.md +13 -0
  201. package/skills/pr-converge/reference/per-tick.md +107 -31
  202. package/skills/pr-converge/reference/state-schema.md +22 -1
  203. package/skills/pr-converge/reference/stop-conditions.md +9 -7
  204. package/skills/pr-converge/scripts/README.md +34 -46
  205. package/skills/pr-converge/scripts/check_bugbot_ci.py +279 -0
  206. package/skills/pr-converge/scripts/check_convergence.py +497 -0
  207. package/skills/pr-converge/scripts/check_pending_reviews.py +154 -0
  208. package/skills/pr-converge/scripts/config/pr_converge_constants.py +118 -0
  209. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +134 -0
  210. package/skills/pr-converge/scripts/post_fix_reply.py +168 -0
  211. package/skills/pr-converge/scripts/test_check_bugbot_ci.py +312 -0
  212. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +5 -12
  213. package/skills/qbug/SKILL.md +157 -27
  214. package/skills/session-log/SKILL.md +216 -114
  215. package/skills/session-tidy/SKILL.md +1 -1
  216. package/skills/skill-builder/SKILL.md +138 -56
  217. package/skills/skill-builder/references/delegation-map.md +72 -113
  218. package/skills/skill-builder/references/progressive-disclosure.md +122 -0
  219. package/skills/skill-builder/references/self-audit-checklist.md +92 -0
  220. package/skills/skill-builder/references/skill-types.md +228 -0
  221. package/skills/skill-builder/references/thariq-x-post-skills.json +33 -0
  222. package/skills/skill-builder/templates/gap-analysis.md +15 -8
  223. package/skills/skill-builder/workflows/improve-skill.md +86 -57
  224. package/skills/skill-builder/workflows/new-skill.md +80 -168
  225. package/skills/skill-builder/workflows/polish-skill.md +78 -54
  226. package/skills/structure-prompt/SKILL.md +50 -0
  227. package/skills/structure-prompt/reference/adversarial-tuning.md +62 -0
  228. package/skills/structure-prompt/reference/block-classification.md +27 -0
  229. package/skills/structure-prompt/reference/canonical-case.md +48 -0
  230. package/skills/structure-prompt/reference/citation-depth.md +70 -0
  231. package/skills/structure-prompt/reference/cleanup.md +33 -0
  232. package/skills/structure-prompt/reference/constraints.md +33 -0
  233. package/skills/structure-prompt/reference/directives.md +37 -0
  234. package/skills/structure-prompt/reference/examples.md +72 -0
  235. package/skills/structure-prompt/reference/instantiation.md +51 -0
  236. package/skills/structure-prompt/reference/output-contract.md +72 -0
  237. package/skills/structure-prompt/reference/per-category.md +23 -0
  238. package/skills/structure-prompt/reference/persona.md +38 -0
  239. package/skills/structure-prompt/reference/research.md +33 -0
  240. package/skills/structure-prompt/reference/structure.md +28 -0
  241. package/agents/code-standards-agent.md +0 -93
  242. package/agents/groq-coder.md +0 -113
  243. package/agents/plan-executor.md +0 -226
  244. package/agents/project-docs-analyzer.md +0 -53
  245. package/agents/project-structure-organizer-agent.md +0 -72
  246. package/agents/skill-to-agent-converter.md +0 -370
  247. package/agents/skill-writer-agent.md +0 -470
  248. package/agents/user-docs-writer.md +0 -67
  249. package/agents/workflow-visual-documenter.md +0 -82
  250. package/commands/readability-review.md +0 -20
  251. package/hooks/mypy.ini +0 -2
  252. package/hooks/notification/attention_needed_notify.py +0 -71
  253. package/hooks/notification/claude_notification_handler.py +0 -67
  254. package/hooks/notification/notification_utils.py +0 -267
  255. package/hooks/notification/subagent_complete_notify.py +0 -381
  256. package/hooks/notification/test_attention_needed_notify.py +0 -47
  257. package/hooks/notification/test_claude_notification_handler.py +0 -54
  258. package/hooks/notification/test_notification_utils.py +0 -91
  259. package/hooks/notification/test_subagent_complete_notify.py +0 -79
  260. package/scripts/config/groq_bugteam_config.py +0 -230
  261. package/scripts/config/test_groq_bugteam_config.py +0 -83
  262. package/scripts/config/test_spec_implementer_prompt.py +0 -32
  263. package/scripts/groq_bugteam.README.md +0 -131
  264. package/scripts/groq_bugteam.py +0 -647
  265. package/scripts/groq_bugteam_dotenv.py +0 -40
  266. package/scripts/groq_bugteam_spec.py +0 -226
  267. package/scripts/test_groq_bugteam.py +0 -529
  268. package/scripts/test_groq_bugteam_apply_fix_from_spec.py +0 -426
  269. package/scripts/test_groq_bugteam_dotenv.py +0 -66
  270. package/scripts/test_groq_bugteam_spec.py +0 -338
  271. package/skills/bugteam/SKILL_EVALS.md +0 -309
  272. package/skills/dream/SKILL.md +0 -118
  273. package/skills/ingest/SKILL.md +0 -40
  274. package/skills/npm-creator/SKILL.md +0 -187
  275. package/skills/readability-review/SKILL.md +0 -127
  276. package/skills/resume-review/SKILL.md +0 -261
  277. package/skills/rule-audit/SKILL.md +0 -307
  278. package/skills/rule-creator/SKILL.md +0 -150
  279. package/skills/searching-obsidian-vault/SKILL.md +0 -131
  280. package/skills/skill-writer/REFERENCE.md +0 -284
  281. package/skills/skill-writer/SKILL.md +0 -222
  282. package/skills/tdd-team/SKILL.md +0 -128
@@ -1,426 +0,0 @@
1
- """Tests for groq_bugteam.apply_fix_from_spec().
2
-
3
- Covers the Claude-authored fix-spec pipeline: replacement_code splicing,
4
- intended_change derivation, acceptance-criterion self-check, out-of-range
5
- guard, and trailing-newline preservation. All Groq HTTP calls are
6
- monkeypatched; no network activity.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import importlib.util
12
- import io
13
- import json
14
- import pathlib
15
- import sys
16
-
17
- import pytest
18
-
19
-
20
- def _load_groq_bugteam_module():
21
- scripts_directory = pathlib.Path(__file__).parent
22
- sys.path.insert(0, str(scripts_directory))
23
- modules_to_remove = [
24
- each_module_name
25
- for each_module_name in list(sys.modules)
26
- if each_module_name == "groq_bugteam"
27
- or each_module_name.startswith("groq_bugteam.")
28
- ]
29
- for each_module_name in modules_to_remove:
30
- del sys.modules[each_module_name]
31
- module_path = scripts_directory / "groq_bugteam.py"
32
- module_spec = importlib.util.spec_from_file_location("groq_bugteam", module_path)
33
- loaded_module = importlib.util.module_from_spec(module_spec)
34
- sys.modules["groq_bugteam"] = loaded_module
35
- module_spec.loader.exec_module(loaded_module)
36
- return loaded_module
37
-
38
-
39
- groq_bugteam = _load_groq_bugteam_module()
40
-
41
-
42
- FAKE_API_KEY = "gsk_test_placeholder_value"
43
-
44
-
45
- def _stub_groq_response(monkeypatch, response_object: dict) -> None:
46
- """Force call_groq_with_fallback() to return a synthetic JSON payload."""
47
-
48
- def fake_call(api_key, messages, temperature, max_completion_tokens):
49
- return groq_bugteam.GroqCallResult(
50
- content=json.dumps(response_object),
51
- model="fake-model",
52
- )
53
-
54
- monkeypatch.setenv("GROQ_API_KEY", FAKE_API_KEY)
55
- monkeypatch.setattr(groq_bugteam, "call_groq_with_fallback", fake_call)
56
-
57
-
58
- class TestApplyFixFromSpecReplacementCode:
59
- def test_applies_replacement_code_byte_for_byte_outside_edit(self, monkeypatch):
60
- original_file = "line_one\nline_two\nline_three\n"
61
- spec_list = [
62
- {
63
- "finding_index": 0,
64
- "severity": "P1",
65
- "category": "J",
66
- "file": "sample.py",
67
- "target_line_start": 2,
68
- "target_line_end": 2,
69
- "intended_change": "replace line_two",
70
- "replacement_code": "line_two_fixed",
71
- "acceptance_criteria": ["line_two_fixed appears on line 2"],
72
- }
73
- ]
74
- patched_file = "line_one\nline_two_fixed\nline_three\n"
75
- fake_response = {
76
- "updated_content": patched_file,
77
- "applied_finding_indexes": [0],
78
- "skipped": [],
79
- "acceptance_checks": [
80
- {
81
- "finding_index": 0,
82
- "criterion": "line_two_fixed appears on line 2",
83
- "met": True,
84
- }
85
- ],
86
- }
87
- _stub_groq_response(monkeypatch, fake_response)
88
-
89
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
90
-
91
- assert outcome["updated_content"] == patched_file
92
- assert outcome["applied_finding_indexes"] == [0]
93
- assert outcome["skipped"] == []
94
-
95
-
96
- class TestApplyFixFromSpecDerivedEdit:
97
- def test_derives_minimal_edit_when_replacement_absent(self, monkeypatch):
98
- original_file = "value = 1\nreturn value\n"
99
- spec_list = [
100
- {
101
- "finding_index": 3,
102
- "severity": "P2",
103
- "category": "E",
104
- "file": "sample.py",
105
- "target_line_start": 1,
106
- "target_line_end": 1,
107
- "intended_change": "rename value to total_count",
108
- "acceptance_criteria": [
109
- "variable named total_count exists on line 1",
110
- "the literal token value does not appear on line 1",
111
- ],
112
- }
113
- ]
114
- patched_file = "total_count = 1\nreturn value\n"
115
- fake_response = {
116
- "updated_content": patched_file,
117
- "applied_finding_indexes": [3],
118
- "skipped": [],
119
- "acceptance_checks": [
120
- {
121
- "finding_index": 3,
122
- "criterion": "variable named total_count exists on line 1",
123
- "met": True,
124
- },
125
- {
126
- "finding_index": 3,
127
- "criterion": "the literal token value does not appear on line 1",
128
- "met": True,
129
- },
130
- ],
131
- }
132
- _stub_groq_response(monkeypatch, fake_response)
133
-
134
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
135
-
136
- assert outcome["updated_content"] == patched_file
137
- assert outcome["applied_finding_indexes"] == [3]
138
-
139
-
140
- class TestApplyFixFromSpecAcceptanceFailure:
141
- def test_moves_finding_to_skipped_when_any_criterion_unmet(self, monkeypatch):
142
- original_file = "alpha\nbeta\n"
143
- spec_list = [
144
- {
145
- "finding_index": 7,
146
- "severity": "P1",
147
- "category": "H",
148
- "file": "sample.py",
149
- "target_line_start": 2,
150
- "target_line_end": 2,
151
- "intended_change": "replace beta with gamma",
152
- "replacement_code": "gamma",
153
- "acceptance_criteria": [
154
- "gamma appears on line 2",
155
- "delta appears on line 2",
156
- ],
157
- }
158
- ]
159
- patched_file = "alpha\ngamma\n"
160
- fake_response = {
161
- "updated_content": patched_file,
162
- "applied_finding_indexes": [7],
163
- "skipped": [],
164
- "acceptance_checks": [
165
- {
166
- "finding_index": 7,
167
- "criterion": "gamma appears on line 2",
168
- "met": True,
169
- },
170
- {
171
- "finding_index": 7,
172
- "criterion": "delta appears on line 2",
173
- "met": False,
174
- },
175
- ],
176
- }
177
- _stub_groq_response(monkeypatch, fake_response)
178
-
179
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
180
-
181
- assert 7 not in outcome["applied_finding_indexes"]
182
- skipped_indexes = [each["finding_index"] for each in outcome["skipped"]]
183
- assert 7 in skipped_indexes
184
- reason_text = next(
185
- each["reason"] for each in outcome["skipped"] if each["finding_index"] == 7
186
- )
187
- assert "delta appears on line 2" in reason_text
188
-
189
-
190
- class TestApplyFixFromSpecOutOfRange:
191
- def test_skips_when_target_lines_out_of_range(self, monkeypatch):
192
- original_file = "only_line\n"
193
- spec_list = [
194
- {
195
- "finding_index": 2,
196
- "severity": "P2",
197
- "category": "E",
198
- "file": "sample.py",
199
- "target_line_start": 50,
200
- "target_line_end": 51,
201
- "intended_change": "fix beyond file end",
202
- "replacement_code": "noop",
203
- "acceptance_criteria": ["noop replaces line 50"],
204
- }
205
- ]
206
- fake_response = {
207
- "updated_content": original_file,
208
- "applied_finding_indexes": [],
209
- "skipped": [
210
- {
211
- "finding_index": 2,
212
- "reason": "target_line_start out of range",
213
- }
214
- ],
215
- "acceptance_checks": [],
216
- }
217
- _stub_groq_response(monkeypatch, fake_response)
218
-
219
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
220
-
221
- assert outcome["updated_content"] == original_file
222
- assert outcome["applied_finding_indexes"] == []
223
- assert outcome["skipped"][0]["finding_index"] == 2
224
-
225
-
226
- class TestApplyFixFromSpecTrailingNewline:
227
- def test_preserves_trailing_newline_when_original_had_one(self, monkeypatch):
228
- original_file = "alpha\nbeta\n"
229
- spec_list = [
230
- {
231
- "finding_index": 0,
232
- "severity": "P1",
233
- "category": "J",
234
- "file": "sample.py",
235
- "target_line_start": 1,
236
- "target_line_end": 1,
237
- "intended_change": "rename alpha to alpha_fixed",
238
- "replacement_code": "alpha_fixed",
239
- "acceptance_criteria": ["alpha_fixed appears on line 1"],
240
- }
241
- ]
242
- fake_response = {
243
- "updated_content": "alpha_fixed\nbeta",
244
- "applied_finding_indexes": [0],
245
- "skipped": [],
246
- "acceptance_checks": [
247
- {
248
- "finding_index": 0,
249
- "criterion": "alpha_fixed appears on line 1",
250
- "met": True,
251
- }
252
- ],
253
- }
254
- _stub_groq_response(monkeypatch, fake_response)
255
-
256
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
257
-
258
- assert outcome["updated_content"].endswith("\n")
259
-
260
- def test_preserves_absence_of_trailing_newline(self, monkeypatch):
261
- original_file = "alpha\nbeta"
262
- spec_list = [
263
- {
264
- "finding_index": 0,
265
- "severity": "P1",
266
- "category": "J",
267
- "file": "sample.py",
268
- "target_line_start": 1,
269
- "target_line_end": 1,
270
- "intended_change": "rename alpha to alpha_fixed",
271
- "replacement_code": "alpha_fixed",
272
- "acceptance_criteria": ["alpha_fixed appears on line 1"],
273
- }
274
- ]
275
- fake_response = {
276
- "updated_content": "alpha_fixed\nbeta\n",
277
- "applied_finding_indexes": [0],
278
- "skipped": [],
279
- "acceptance_checks": [
280
- {
281
- "finding_index": 0,
282
- "criterion": "alpha_fixed appears on line 1",
283
- "met": True,
284
- }
285
- ],
286
- }
287
- _stub_groq_response(monkeypatch, fake_response)
288
-
289
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
290
-
291
- assert not outcome["updated_content"].endswith("\n")
292
-
293
-
294
- class TestApplyFixFromSpecUntrustedResponseShape:
295
- def test_skipped_entry_missing_finding_index_does_not_crash(self, monkeypatch):
296
- original_file = "alpha\nbeta\n"
297
- spec_list = [
298
- {
299
- "finding_index": 4,
300
- "severity": "P1",
301
- "category": "J",
302
- "file": "sample.py",
303
- "target_line_start": 1,
304
- "target_line_end": 1,
305
- "intended_change": "rename alpha",
306
- "replacement_code": "alpha_fixed",
307
- "acceptance_criteria": ["alpha_fixed appears on line 1"],
308
- }
309
- ]
310
- patched_file = "alpha_fixed\nbeta\n"
311
- fake_response = {
312
- "updated_content": patched_file,
313
- "applied_finding_indexes": [4],
314
- "skipped": [{"reason": "malformed entry without finding_index"}],
315
- "acceptance_checks": [
316
- {
317
- "finding_index": 4,
318
- "criterion": "alpha_fixed appears on line 1",
319
- "met": True,
320
- }
321
- ],
322
- }
323
- _stub_groq_response(monkeypatch, fake_response)
324
-
325
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
326
-
327
- assert outcome["updated_content"] == patched_file
328
- assert outcome["applied_finding_indexes"] == [4]
329
-
330
- def test_null_updated_content_falls_back_to_current_content(self, monkeypatch):
331
- original_file = "alpha\nbeta\n"
332
- spec_list = [
333
- {
334
- "finding_index": 0,
335
- "severity": "P2",
336
- "category": "E",
337
- "file": "sample.py",
338
- "target_line_start": 1,
339
- "target_line_end": 1,
340
- "intended_change": "no-op fallback",
341
- "replacement_code": "alpha",
342
- "acceptance_criteria": ["alpha remains on line 1"],
343
- }
344
- ]
345
- fake_response = {
346
- "updated_content": None,
347
- "applied_finding_indexes": [],
348
- "skipped": [
349
- {
350
- "finding_index": 0,
351
- "reason": "Groq returned null updated_content",
352
- }
353
- ],
354
- "acceptance_checks": [],
355
- }
356
- _stub_groq_response(monkeypatch, fake_response)
357
-
358
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
359
-
360
- assert outcome["updated_content"] == original_file
361
-
362
- def test_null_collection_fields_coerce_to_empty_lists(self, monkeypatch):
363
- original_file = "alpha\n"
364
- spec_list = [
365
- {
366
- "finding_index": 1,
367
- "severity": "P2",
368
- "category": "E",
369
- "file": "sample.py",
370
- "target_line_start": 1,
371
- "target_line_end": 1,
372
- "intended_change": "no-op",
373
- "replacement_code": "alpha",
374
- "acceptance_criteria": ["alpha remains"],
375
- }
376
- ]
377
- fake_response = {
378
- "updated_content": original_file,
379
- "applied_finding_indexes": None,
380
- "skipped": None,
381
- "acceptance_checks": None,
382
- }
383
- _stub_groq_response(monkeypatch, fake_response)
384
-
385
- outcome = groq_bugteam.apply_fix_from_spec(spec_list, original_file)
386
-
387
- assert outcome["applied_finding_indexes"] == []
388
- assert outcome["skipped"] == []
389
- assert outcome["acceptance_checks"] == []
390
-
391
-
392
- class TestRunSpecModeMainErrorContract:
393
- def test_missing_api_key_emits_json_error_and_exits_nonzero(
394
- self, monkeypatch, capsys
395
- ):
396
- monkeypatch.delenv("GROQ_API_KEY", raising=False)
397
- monkeypatch.setattr(
398
- "groq_bugteam_dotenv.load_claude_dev_env_dotenv_file",
399
- lambda: None,
400
- )
401
- spec_payload = {
402
- "spec": [
403
- {
404
- "finding_index": 0,
405
- "severity": "P1",
406
- "category": "J",
407
- "file": "sample.py",
408
- "target_line_start": 1,
409
- "target_line_end": 1,
410
- "intended_change": "noop",
411
- "replacement_code": "noop",
412
- "acceptance_criteria": ["noop"],
413
- }
414
- ],
415
- "current_content": "noop\n",
416
- }
417
- monkeypatch.setattr("sys.stdin", io.StringIO(json.dumps(spec_payload)))
418
-
419
- with pytest.raises(SystemExit) as exit_info:
420
- groq_bugteam.run_spec_mode_main()
421
-
422
- captured = capsys.readouterr()
423
- emitted_outcome = json.loads(captured.out)
424
- assert "error" in emitted_outcome
425
- assert "GROQ_API_KEY" in emitted_outcome["error"]
426
- assert exit_info.value.code != 0
@@ -1,66 +0,0 @@
1
- """Tests for groq_bugteam_dotenv local .env loading."""
2
-
3
- from __future__ import annotations
4
-
5
- import os
6
- import pathlib
7
- import sys
8
-
9
- import pytest
10
-
11
- _SCRIPTS_DIRECTORY = pathlib.Path(__file__).parent.resolve()
12
- sys.path.insert(0, str(_SCRIPTS_DIRECTORY))
13
-
14
- from groq_bugteam_dotenv import ( # noqa: E402
15
- claude_dev_env_dotenv_path,
16
- load_claude_dev_env_dotenv_file,
17
- )
18
-
19
-
20
- class TestLoadClaudeDevEnvDotenvFile:
21
- def test_sets_groq_key_from_file(self, monkeypatch, tmp_path):
22
- monkeypatch.delenv("GROQ_API_KEY", raising=False)
23
- env_file = tmp_path / ".env"
24
- env_file.write_text("GROQ_API_KEY=from_file_value\n", encoding="utf-8")
25
- load_claude_dev_env_dotenv_file(env_file)
26
- assert os.environ["GROQ_API_KEY"] == "from_file_value"
27
-
28
- def test_does_not_override_existing_key(self, monkeypatch, tmp_path):
29
- monkeypatch.setenv("GROQ_API_KEY", "preset_value")
30
- env_file = tmp_path / ".env"
31
- env_file.write_text("GROQ_API_KEY=from_file_value\n", encoding="utf-8")
32
- load_claude_dev_env_dotenv_file(env_file)
33
- assert os.environ["GROQ_API_KEY"] == "preset_value"
34
-
35
- def test_skips_comments_and_blank_lines(self, monkeypatch, tmp_path):
36
- monkeypatch.delenv("GROQ_API_KEY", raising=False)
37
- env_file = tmp_path / ".env"
38
- env_file.write_text("\n# comment\nGROQ_API_KEY=x\n", encoding="utf-8")
39
- load_claude_dev_env_dotenv_file(env_file)
40
- assert os.environ["GROQ_API_KEY"] == "x"
41
-
42
- def test_strips_export_prefix(self, monkeypatch, tmp_path):
43
- monkeypatch.delenv("GROQ_API_KEY", raising=False)
44
- env_file = tmp_path / ".env"
45
- env_file.write_text("export GROQ_API_KEY=exported\n", encoding="utf-8")
46
- load_claude_dev_env_dotenv_file(env_file)
47
- assert os.environ["GROQ_API_KEY"] == "exported"
48
-
49
- def test_strips_double_quotes(self, monkeypatch, tmp_path):
50
- monkeypatch.delenv("GROQ_API_KEY", raising=False)
51
- env_file = tmp_path / ".env"
52
- env_file.write_text('GROQ_API_KEY="quoted"\n', encoding="utf-8")
53
- load_claude_dev_env_dotenv_file(env_file)
54
- assert os.environ["GROQ_API_KEY"] == "quoted"
55
-
56
- def test_missing_file_is_no_op(self, monkeypatch, tmp_path):
57
- monkeypatch.delenv("GROQ_API_KEY", raising=False)
58
- missing_file = tmp_path / "does_not_exist.env"
59
- load_claude_dev_env_dotenv_file(missing_file)
60
- assert "GROQ_API_KEY" not in os.environ
61
-
62
-
63
- def test_claude_dev_env_dotenv_path_ends_with_env_filename():
64
- resolved = claude_dev_env_dotenv_path()
65
- assert resolved.name == ".env"
66
- assert resolved.parent.name == "claude-dev-env"