claude-dev-env 1.38.0 → 1.39.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 (271) 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 +189 -0
  7. package/_shared/pr-loop/scripts/post_audit_thread.py +947 -0
  8. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -19
  9. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +923 -0
  10. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +127 -0
  11. package/_shared/pr-loop/state-schema.md +1 -1
  12. package/agents/clean-coder.md +2 -2
  13. package/bin/install.mjs +6 -7
  14. package/bin/install.test.mjs +8 -0
  15. package/commands/doc-gist.md +16 -0
  16. package/commands/plan.md +0 -2
  17. package/commands/review-plan.md +1 -1
  18. package/docs/CODE_RULES.md +122 -2
  19. package/hooks/blocking/bot_mention_comment_blocker.py +75 -0
  20. package/hooks/blocking/code_rules_enforcer.py +1236 -161
  21. package/hooks/blocking/convergence_gate_blocker.py +130 -0
  22. package/hooks/blocking/destructive_command_blocker.py +74 -0
  23. package/hooks/blocking/gh_body_arg_blocker.py +30 -0
  24. package/hooks/blocking/md_to_html_blocker.py +119 -0
  25. package/hooks/blocking/test_bot_mention_comment_blocker.py +131 -0
  26. package/hooks/blocking/test_code_rules_enforcer.py +21 -0
  27. package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +70 -0
  28. package/hooks/blocking/test_code_rules_enforcer_any_imports_and_cast.py +92 -0
  29. package/hooks/blocking/test_code_rules_enforcer_banned_import_alias.py +143 -0
  30. package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +152 -0
  31. package/hooks/blocking/test_code_rules_enforcer_bare_except.py +120 -0
  32. package/hooks/blocking/test_code_rules_enforcer_boundary_types.py +175 -0
  33. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
  34. package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +50 -0
  35. package/hooks/blocking/test_code_rules_enforcer_docstring_format.py +255 -0
  36. package/hooks/blocking/test_code_rules_enforcer_inline_tuple_string_magic.py +130 -0
  37. package/hooks/blocking/test_code_rules_enforcer_stub_implementations.py +141 -0
  38. package/hooks/blocking/test_code_rules_enforcer_test_branching.py +143 -0
  39. package/hooks/blocking/test_code_rules_enforcer_thin_wrapper_files.py +169 -0
  40. package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +99 -0
  41. package/hooks/blocking/test_code_rules_enforcer_typed_dict_pairs.py +141 -0
  42. package/hooks/blocking/test_code_rules_enforcer_unused_imports.py +158 -0
  43. package/hooks/blocking/test_convergence_gate_blocker.py +63 -0
  44. package/hooks/blocking/test_destructive_command_blocker.py +146 -0
  45. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +102 -0
  46. package/hooks/blocking/test_gh_body_arg_blocker.py +45 -0
  47. package/hooks/blocking/test_md_to_html_blocker.py +317 -0
  48. package/hooks/config/any_type_config.py +7 -0
  49. package/hooks/config/banned_identifiers_constants.py +11 -0
  50. package/hooks/config/blocking_check_limits.py +38 -0
  51. package/hooks/config/bot_mention_comment_blocker_constants.py +20 -0
  52. package/hooks/config/code_rules_enforcer_constants.py +53 -0
  53. package/hooks/config/convergence_branch_constants.py +9 -0
  54. package/hooks/config/doc_gist_auto_publish_constants.py +18 -0
  55. package/hooks/config/html_companion_constants.py +20 -0
  56. package/hooks/config/inline_tuple_string_magic_constants.py +22 -0
  57. package/hooks/config/test_banned_identifiers_constants.py +17 -0
  58. package/hooks/hooks.json +28 -20
  59. package/hooks/pyproject.toml +69 -0
  60. package/hooks/validators/mypy_integration.py +47 -1
  61. package/hooks/validators/run_all_validators.py +3 -3
  62. package/hooks/validators/test_mypy_integration.py +50 -1
  63. package/hooks/workflow/doc_gist_auto_publish.py +144 -0
  64. package/hooks/workflow/md_to_html_companion.py +365 -0
  65. package/hooks/workflow/test_doc_gist_auto_publish.py +117 -0
  66. package/hooks/workflow/test_md_to_html_companion.py +452 -0
  67. package/package.json +1 -1
  68. package/rules/gh-body-file.md +2 -0
  69. package/scripts/Install-SweepEmptyDirs.ps1 +111 -0
  70. package/scripts/check.ps1 +106 -0
  71. package/scripts/config/timing.py +11 -0
  72. package/scripts/sweep_empty_dirs.py +138 -0
  73. package/scripts/sync_to_cursor/rules.py +1 -1
  74. package/scripts/test_sweep_empty_dirs.py +183 -0
  75. package/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +323 -0
  76. package/skills/_shared/pr-loop/scripts/_cli_utils.py +22 -0
  77. package/skills/_shared/pr-loop/scripts/_path_resolver.py +165 -0
  78. package/skills/_shared/pr-loop/scripts/_xml_utils.py +20 -0
  79. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +182 -0
  80. package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +185 -0
  81. package/skills/_shared/pr-loop/scripts/config/__init__.py +0 -0
  82. package/skills/_shared/pr-loop/scripts/config/path_resolver_constants.py +78 -0
  83. package/skills/_shared/pr-loop/scripts/init_loop_state.py +135 -0
  84. package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +175 -0
  85. package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +182 -0
  86. package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +206 -0
  87. package/skills/bugteam/CONSTRAINTS.md +21 -22
  88. package/skills/bugteam/EXAMPLES.md +3 -3
  89. package/skills/bugteam/PROMPTS.md +227 -67
  90. package/skills/bugteam/SKILL.md +114 -455
  91. package/skills/bugteam/reference/README.md +1 -1
  92. package/skills/bugteam/reference/audit-and-teammates.md +112 -39
  93. package/skills/bugteam/reference/audit-contract.md +4 -22
  94. package/skills/bugteam/reference/copilot-gap-analysis.md +8 -5
  95. package/skills/bugteam/reference/design-rationale.md +2 -2
  96. package/skills/bugteam/reference/github-pr-reviews.md +50 -57
  97. package/skills/bugteam/reference/obstacles/audit-assign-ids.md +13 -0
  98. package/skills/bugteam/reference/obstacles/audit-capture-excerpts.md +13 -0
  99. package/skills/bugteam/reference/obstacles/audit-walk-categories.md +13 -0
  100. package/skills/bugteam/reference/obstacles/audit-write-xml.md +13 -0
  101. package/skills/bugteam/reference/obstacles/fix-append-summary.md +13 -0
  102. package/skills/bugteam/reference/obstacles/fix-apply-fixes.md +13 -0
  103. package/skills/bugteam/reference/obstacles/fix-git-add-commit.md +13 -0
  104. package/skills/bugteam/reference/obstacles/fix-git-push.md +13 -0
  105. package/skills/bugteam/reference/obstacles/fix-post-reply.md +13 -0
  106. package/skills/bugteam/reference/obstacles/fix-publish-summary.md +13 -0
  107. package/skills/bugteam/reference/obstacles/fix-py-compile.md +13 -0
  108. package/skills/bugteam/reference/obstacles/fix-read-files.md +13 -0
  109. package/skills/bugteam/reference/obstacles/fix-resolve-thread.md +13 -0
  110. package/skills/bugteam/reference/obstacles/fix-test-suite.md +13 -0
  111. package/skills/bugteam/reference/obstacles/fix-violation-count.md +13 -0
  112. package/skills/bugteam/reference/obstacles/fix-write-xml.md +13 -0
  113. package/skills/bugteam/reference/team-setup.md +106 -9
  114. package/skills/bugteam/reference/teardown-publish-permissions.md +39 -8
  115. package/skills/bugteam/scripts/README.md +60 -0
  116. package/skills/bugteam/scripts/_claude_permissions_common.py +358 -0
  117. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +976 -0
  118. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +375 -0
  119. package/skills/bugteam/scripts/bugteam_preflight.py +294 -0
  120. package/skills/bugteam/scripts/config/bugteam_code_rules_gate_constants.py +25 -0
  121. package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +26 -0
  122. package/skills/bugteam/scripts/config/bugteam_preflight_constants.py +35 -0
  123. package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +20 -0
  124. package/skills/bugteam/scripts/config/probe_code_rules_enforcer_check_constants.py +12 -0
  125. package/skills/bugteam/scripts/config/windows_safe_rmtree_constants.py +7 -0
  126. package/skills/bugteam/scripts/grant_project_claude_permissions.py +175 -0
  127. package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +107 -0
  128. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +220 -0
  129. package/skills/bugteam/scripts/test__claude_permissions_common.py +112 -0
  130. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +400 -0
  131. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +384 -0
  132. package/skills/bugteam/scripts/test_bugteam_preflight.py +268 -0
  133. package/skills/bugteam/scripts/test_claude_permissions_common.py +195 -0
  134. package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +55 -0
  135. package/skills/bugteam/scripts/test_probe_code_rules_enforcer_check.py +76 -0
  136. package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +55 -0
  137. package/skills/bugteam/scripts/test_windows_safe_rmtree.py +108 -0
  138. package/skills/bugteam/scripts/windows_safe_rmtree.py +100 -0
  139. package/skills/bugteam/test_skill_additions.py +1 -11
  140. package/skills/code/SKILL.md +176 -0
  141. package/skills/doc-gist/SKILL.md +99 -0
  142. package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +453 -0
  143. package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +515 -0
  144. package/skills/doc-gist/references/examples/03-code-review-pr.html +638 -0
  145. package/skills/doc-gist/references/examples/04-code-understanding.html +491 -0
  146. package/skills/doc-gist/references/examples/05-design-system.html +629 -0
  147. package/skills/doc-gist/references/examples/06-component-variants.html +605 -0
  148. package/skills/doc-gist/references/examples/07-prototype-animation.html +455 -0
  149. package/skills/doc-gist/references/examples/08-prototype-interaction.html +396 -0
  150. package/skills/doc-gist/references/examples/09-slide-deck.html +592 -0
  151. package/skills/doc-gist/references/examples/10-svg-illustrations.html +492 -0
  152. package/skills/doc-gist/references/examples/11-status-report.html +528 -0
  153. package/skills/doc-gist/references/examples/12-incident-report.html +596 -0
  154. package/skills/doc-gist/references/examples/13-flowchart-diagram.html +395 -0
  155. package/skills/doc-gist/references/examples/14-research-feature-explainer.html +381 -0
  156. package/skills/doc-gist/references/examples/15-research-concept-explainer.html +368 -0
  157. package/skills/doc-gist/references/examples/16-implementation-plan.html +702 -0
  158. package/skills/doc-gist/references/examples/17-pr-writeup.html +595 -0
  159. package/skills/doc-gist/references/examples/18-editor-triage-board.html +573 -0
  160. package/skills/doc-gist/references/examples/19-editor-feature-flags.html +663 -0
  161. package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +722 -0
  162. package/skills/doc-gist/references/examples/README.md +5 -0
  163. package/skills/doc-gist/scripts/config/__init__.py +0 -0
  164. package/skills/doc-gist/scripts/config/gist_upload_constants.py +16 -0
  165. package/skills/doc-gist/scripts/gist_upload.py +177 -0
  166. package/skills/doc-gist/scripts/test_gist_upload.py +51 -0
  167. package/skills/findbugs/SKILL.md +68 -2
  168. package/skills/monitor-open-prs/SKILL.md +13 -32
  169. package/skills/monitor-open-prs/test_skill_contract.py +0 -11
  170. package/skills/pr-consistency-audit/SKILL.md +112 -0
  171. package/skills/pr-consistency-audit/reference/detection-rules.md +96 -0
  172. package/skills/pr-consistency-audit/reference/illustrations.md +78 -0
  173. package/skills/pr-converge/SKILL.md +227 -23
  174. package/skills/pr-converge/config/__init__.py +0 -0
  175. package/skills/pr-converge/config/constants.py +62 -0
  176. package/skills/pr-converge/reference/convergence-gates.md +138 -44
  177. package/skills/pr-converge/reference/examples.md +43 -11
  178. package/skills/pr-converge/reference/fix-protocol.md +6 -5
  179. package/skills/pr-converge/reference/ground-rules.md +5 -3
  180. package/skills/pr-converge/reference/multi-pr-orchestration.md +44 -19
  181. package/skills/pr-converge/reference/obstacles/fix-post-replies.md +13 -0
  182. package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +13 -0
  183. package/skills/pr-converge/reference/obstacles/fix-push.md +13 -0
  184. package/skills/pr-converge/reference/obstacles/fix-read-filelines.md +13 -0
  185. package/skills/pr-converge/reference/obstacles/fix-reset-state.md +13 -0
  186. package/skills/pr-converge/reference/obstacles/fix-resolve-threads.md +13 -0
  187. package/skills/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +13 -0
  188. package/skills/pr-converge/reference/obstacles/fix-stage-commit.md +13 -0
  189. package/skills/pr-converge/reference/obstacles/fix-trigger-bugbot.md +13 -0
  190. package/skills/pr-converge/reference/obstacles/fix-write-test.md +13 -0
  191. package/skills/pr-converge/reference/per-tick.md +90 -31
  192. package/skills/pr-converge/reference/state-schema.md +22 -1
  193. package/skills/pr-converge/reference/stop-conditions.md +9 -7
  194. package/skills/pr-converge/scripts/README.md +34 -46
  195. package/skills/pr-converge/scripts/check_bugbot_ci.py +174 -0
  196. package/skills/pr-converge/scripts/check_convergence.py +497 -0
  197. package/skills/pr-converge/scripts/check_pending_reviews.py +154 -0
  198. package/skills/pr-converge/scripts/config/pr_converge_constants.py +118 -0
  199. package/skills/pr-converge/scripts/fetch_copilot_reviews.py +134 -0
  200. package/skills/pr-converge/scripts/post_fix_reply.py +168 -0
  201. package/skills/pr-converge/workflows/schedule-wakeup-loop.md +5 -12
  202. package/skills/qbug/SKILL.md +132 -27
  203. package/skills/session-log/SKILL.md +216 -114
  204. package/skills/session-tidy/SKILL.md +1 -1
  205. package/skills/skill-builder/SKILL.md +138 -56
  206. package/skills/skill-builder/references/delegation-map.md +72 -113
  207. package/skills/skill-builder/references/progressive-disclosure.md +122 -0
  208. package/skills/skill-builder/references/self-audit-checklist.md +92 -0
  209. package/skills/skill-builder/references/skill-types.md +228 -0
  210. package/skills/skill-builder/references/thariq-x-post-skills.json +33 -0
  211. package/skills/skill-builder/templates/gap-analysis.md +15 -8
  212. package/skills/skill-builder/workflows/improve-skill.md +86 -57
  213. package/skills/skill-builder/workflows/new-skill.md +80 -168
  214. package/skills/skill-builder/workflows/polish-skill.md +78 -54
  215. package/skills/structure-prompt/SKILL.md +50 -0
  216. package/skills/structure-prompt/reference/adversarial-tuning.md +62 -0
  217. package/skills/structure-prompt/reference/block-classification.md +27 -0
  218. package/skills/structure-prompt/reference/canonical-case.md +48 -0
  219. package/skills/structure-prompt/reference/citation-depth.md +70 -0
  220. package/skills/structure-prompt/reference/cleanup.md +33 -0
  221. package/skills/structure-prompt/reference/constraints.md +33 -0
  222. package/skills/structure-prompt/reference/directives.md +37 -0
  223. package/skills/structure-prompt/reference/examples.md +72 -0
  224. package/skills/structure-prompt/reference/instantiation.md +51 -0
  225. package/skills/structure-prompt/reference/output-contract.md +72 -0
  226. package/skills/structure-prompt/reference/per-category.md +23 -0
  227. package/skills/structure-prompt/reference/persona.md +38 -0
  228. package/skills/structure-prompt/reference/research.md +33 -0
  229. package/skills/structure-prompt/reference/structure.md +28 -0
  230. package/agents/code-standards-agent.md +0 -93
  231. package/agents/groq-coder.md +0 -113
  232. package/agents/plan-executor.md +0 -226
  233. package/agents/project-docs-analyzer.md +0 -53
  234. package/agents/project-structure-organizer-agent.md +0 -72
  235. package/agents/skill-to-agent-converter.md +0 -370
  236. package/agents/skill-writer-agent.md +0 -470
  237. package/agents/user-docs-writer.md +0 -67
  238. package/agents/workflow-visual-documenter.md +0 -82
  239. package/commands/readability-review.md +0 -20
  240. package/hooks/mypy.ini +0 -2
  241. package/hooks/notification/attention_needed_notify.py +0 -71
  242. package/hooks/notification/claude_notification_handler.py +0 -67
  243. package/hooks/notification/notification_utils.py +0 -267
  244. package/hooks/notification/subagent_complete_notify.py +0 -381
  245. package/hooks/notification/test_attention_needed_notify.py +0 -47
  246. package/hooks/notification/test_claude_notification_handler.py +0 -54
  247. package/hooks/notification/test_notification_utils.py +0 -91
  248. package/hooks/notification/test_subagent_complete_notify.py +0 -79
  249. package/scripts/config/groq_bugteam_config.py +0 -230
  250. package/scripts/config/test_groq_bugteam_config.py +0 -83
  251. package/scripts/config/test_spec_implementer_prompt.py +0 -32
  252. package/scripts/groq_bugteam.README.md +0 -131
  253. package/scripts/groq_bugteam.py +0 -647
  254. package/scripts/groq_bugteam_dotenv.py +0 -40
  255. package/scripts/groq_bugteam_spec.py +0 -226
  256. package/scripts/test_groq_bugteam.py +0 -529
  257. package/scripts/test_groq_bugteam_apply_fix_from_spec.py +0 -426
  258. package/scripts/test_groq_bugteam_dotenv.py +0 -66
  259. package/scripts/test_groq_bugteam_spec.py +0 -338
  260. package/skills/bugteam/SKILL_EVALS.md +0 -309
  261. package/skills/dream/SKILL.md +0 -118
  262. package/skills/ingest/SKILL.md +0 -40
  263. package/skills/npm-creator/SKILL.md +0 -187
  264. package/skills/readability-review/SKILL.md +0 -127
  265. package/skills/resume-review/SKILL.md +0 -261
  266. package/skills/rule-audit/SKILL.md +0 -307
  267. package/skills/rule-creator/SKILL.md +0 -150
  268. package/skills/searching-obsidian-vault/SKILL.md +0 -131
  269. package/skills/skill-writer/REFERENCE.md +0 -284
  270. package/skills/skill-writer/SKILL.md +0 -222
  271. 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"