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
@@ -0,0 +1,279 @@
1
+ """Check for bugbot CI check runs on a commit.
2
+
3
+ Usage:
4
+ python scripts/check_bugbot_ci.py --owner <O> --repo <R> --sha <SHA>
5
+ python scripts/check_bugbot_ci.py --owner <O> --repo <R> --sha <SHA> --check-active
6
+ python scripts/check_bugbot_ci.py --owner <O> --repo <R> --sha <SHA> --check-clean
7
+
8
+ Default mode (no flag):
9
+ 0 — bugbot check run found (printed to stdout as JSON)
10
+ 1 — no bugbot check run found
11
+ EXIT_CODE_GH_ERROR — gh CLI error
12
+
13
+ ``--check-active`` mode:
14
+ 0 — bugbot check run is queued or in_progress
15
+ 1 — bugbot check run is absent or no longer active
16
+
17
+ ``--check-clean`` mode (silent-pass detection):
18
+ 0 — bugbot check run is completed with success/neutral conclusion
19
+ 1 — bugbot check run is absent, still active, or completed with a
20
+ non-clean conclusion (failure, action_required, etc.)
21
+ EXIT_CODE_GH_ERROR — gh CLI error
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ import json
28
+ import subprocess
29
+ import sys
30
+ from pathlib import Path
31
+
32
+ _pr_converge_dir = Path(__file__).resolve().parent.parent
33
+ if str(_pr_converge_dir) not in sys.path:
34
+ sys.path.insert(0, str(_pr_converge_dir))
35
+
36
+ from config.constants import (
37
+ ALL_BUGBOT_CHECK_RUN_ACTIVE_STATUSES,
38
+ ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS,
39
+ BUGBOT_CHECK_RUN_COMPLETED_STATUS,
40
+ BUGBOT_CHECK_RUN_NAME_SUBSTRING,
41
+ CHECK_RUNS_PER_PAGE,
42
+ EXIT_CODE_GH_ERROR,
43
+ GH_CHECK_RUNS_PATH_TEMPLATE,
44
+ )
45
+
46
+
47
+ def _run_check_runs_api(
48
+ *, owner: str, repo: str, sha: str
49
+ ) -> subprocess.CompletedProcess[str]:
50
+ endpoint_path = GH_CHECK_RUNS_PATH_TEMPLATE.format(owner=owner, repo=repo, sha=sha)
51
+ jq_filter = ".check_runs[] | {name, status, conclusion}"
52
+ return subprocess.run(
53
+ [
54
+ "gh",
55
+ "api",
56
+ f"{endpoint_path}?per_page={CHECK_RUNS_PER_PAGE}",
57
+ "--jq",
58
+ jq_filter,
59
+ ],
60
+ capture_output=True,
61
+ text=True,
62
+ encoding="utf-8",
63
+ errors="replace",
64
+ check=False,
65
+ )
66
+
67
+
68
+ def check_bugbot_ci(*, owner: str, repo: str, sha: str) -> int:
69
+ """Check whether a bugbot CI check run exists on the given commit.
70
+
71
+ Args:
72
+ owner: GitHub repository owner.
73
+ repo: GitHub repository name.
74
+ sha: Commit SHA to check.
75
+
76
+ Returns:
77
+ 0 when a bugbot check run is found, 1 when absent,
78
+ EXIT_CODE_GH_ERROR on gh CLI error.
79
+ """
80
+ completed_process = _run_check_runs_api(owner=owner, repo=repo, sha=sha)
81
+ if completed_process.returncode != 0:
82
+ print(f"gh api error: {completed_process.stderr}", file=sys.stderr)
83
+ return EXIT_CODE_GH_ERROR
84
+ for each_line in completed_process.stdout.splitlines():
85
+ stripped_line = each_line.strip()
86
+ if not stripped_line:
87
+ continue
88
+ try:
89
+ check_entry: dict[str, object] = json.loads(stripped_line)
90
+ except json.JSONDecodeError:
91
+ continue
92
+ each_name: object = check_entry.get("name")
93
+ if not isinstance(each_name, str):
94
+ continue
95
+ if BUGBOT_CHECK_RUN_NAME_SUBSTRING.lower() in each_name.lower():
96
+ json.dump(check_entry, sys.stdout)
97
+ sys.stdout.write("\n")
98
+ return 0
99
+ return 1
100
+
101
+
102
+ def is_bugbot_run_active(*, owner: str, repo: str, sha: str) -> bool:
103
+ """Check whether bugbot has an active (queued/in-progress) check run.
104
+
105
+ Args:
106
+ owner: GitHub repository owner.
107
+ repo: GitHub repository name.
108
+ sha: Commit SHA to check.
109
+
110
+ Returns:
111
+ True when a bugbot check run with an active status exists.
112
+ """
113
+ completed_process = _run_check_runs_api(owner=owner, repo=repo, sha=sha)
114
+ if completed_process.returncode != 0:
115
+ return False
116
+ for each_line in completed_process.stdout.splitlines():
117
+ stripped_line = each_line.strip()
118
+ if not stripped_line:
119
+ continue
120
+ try:
121
+ check_entry: dict[str, object] = json.loads(stripped_line)
122
+ except json.JSONDecodeError:
123
+ continue
124
+ each_name: object = check_entry.get("name")
125
+ if not isinstance(each_name, str):
126
+ continue
127
+ if BUGBOT_CHECK_RUN_NAME_SUBSTRING.lower() not in each_name.lower():
128
+ continue
129
+ each_status: object = check_entry.get("status")
130
+ if (
131
+ isinstance(each_status, str)
132
+ and each_status in ALL_BUGBOT_CHECK_RUN_ACTIVE_STATUSES
133
+ ):
134
+ return True
135
+ return False
136
+
137
+
138
+ def _classify_bugbot_check_run(
139
+ completed_process: subprocess.CompletedProcess[str],
140
+ ) -> bool | None:
141
+ """Classify the bugbot check run state from a gh API process result.
142
+
143
+ Args:
144
+ completed_process: Result of calling ``_run_check_runs_api``.
145
+
146
+ Returns:
147
+ True when the captured stdout contains a bugbot check run with a
148
+ ``completed`` status and a conclusion in
149
+ ``ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS``. False when no such
150
+ check run is present (absent, still active, or completed with a
151
+ non-clean conclusion). None when ``completed_process.returncode``
152
+ is non-zero, signalling a gh CLI failure that the caller must
153
+ surface separately from "not clean".
154
+ """
155
+ if completed_process.returncode != 0:
156
+ return None
157
+ for each_line in completed_process.stdout.splitlines():
158
+ stripped_line = each_line.strip()
159
+ if not stripped_line:
160
+ continue
161
+ try:
162
+ check_entry: dict[str, object] = json.loads(stripped_line)
163
+ except json.JSONDecodeError:
164
+ continue
165
+ each_name: object = check_entry.get("name")
166
+ if not isinstance(each_name, str):
167
+ continue
168
+ if BUGBOT_CHECK_RUN_NAME_SUBSTRING.lower() not in each_name.lower():
169
+ continue
170
+ each_status: object = check_entry.get("status")
171
+ if each_status != BUGBOT_CHECK_RUN_COMPLETED_STATUS:
172
+ return False
173
+ each_conclusion: object = check_entry.get("conclusion")
174
+ return (
175
+ isinstance(each_conclusion, str)
176
+ and each_conclusion in ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS
177
+ )
178
+ return False
179
+
180
+
181
+ def is_bugbot_run_clean(*, owner: str, repo: str, sha: str) -> bool | None:
182
+ """Check whether bugbot has a completed check run with a clean conclusion.
183
+
184
+ A "silent pass" is bugbot's signal that it found no issues: the CI
185
+ check run completes with a ``success`` or ``neutral`` conclusion and
186
+ no review comment is posted. This function detects that signal so
187
+ callers can treat it as equivalent to an explicit clean review.
188
+
189
+ Args:
190
+ owner: GitHub repository owner.
191
+ repo: GitHub repository name.
192
+ sha: Commit SHA to check.
193
+
194
+ Returns:
195
+ True when a bugbot check run is completed with a conclusion in
196
+ ``ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS``. False when the
197
+ check run is absent, still active, or completed with a non-clean
198
+ conclusion. None when the gh CLI returns an error so the caller
199
+ can distinguish a transient API failure from a "not clean"
200
+ result.
201
+ """
202
+ completed_process = _run_check_runs_api(owner=owner, repo=repo, sha=sha)
203
+ return _classify_bugbot_check_run(completed_process)
204
+
205
+
206
+ def parse_arguments(all_argv: list[str]) -> argparse.Namespace:
207
+ """Parse command-line arguments.
208
+
209
+ Args:
210
+ all_argv: Command-line argument list.
211
+
212
+ Returns:
213
+ Parsed namespace with owner, repo, sha, and mode flags.
214
+ """
215
+ parser = argparse.ArgumentParser(description=__doc__)
216
+ parser.add_argument("--owner", required=True, help="GitHub repository owner")
217
+ parser.add_argument("--repo", required=True, help="GitHub repository name")
218
+ parser.add_argument("--sha", required=True, help="Commit SHA to check")
219
+ mode_group = parser.add_mutually_exclusive_group()
220
+ mode_group.add_argument(
221
+ "--check-active",
222
+ action="store_true",
223
+ default=False,
224
+ help="Check for active (queued/in-progress) check runs only",
225
+ )
226
+ mode_group.add_argument(
227
+ "--check-clean",
228
+ action="store_true",
229
+ default=False,
230
+ help=(
231
+ "Check for a completed bugbot check run with a "
232
+ "success/neutral conclusion (silent-pass detection)"
233
+ ),
234
+ )
235
+ return parser.parse_args(all_argv)
236
+
237
+
238
+ def main(all_arguments: list[str]) -> int:
239
+ """Entry point for check_bugbot_ci.
240
+
241
+ Args:
242
+ all_arguments: Command-line arguments.
243
+
244
+ Returns:
245
+ Exit code per the mode-specific contract documented in the
246
+ module docstring.
247
+ """
248
+ arguments = parse_arguments(all_arguments)
249
+ if arguments.check_clean:
250
+ completed_process = _run_check_runs_api(
251
+ owner=arguments.owner,
252
+ repo=arguments.repo,
253
+ sha=arguments.sha,
254
+ )
255
+ if completed_process.returncode != 0:
256
+ print(f"gh api error: {completed_process.stderr}", file=sys.stderr)
257
+ return EXIT_CODE_GH_ERROR
258
+ is_clean = _classify_bugbot_check_run(completed_process)
259
+ if is_clean is not True:
260
+ print("bugbot: not clean")
261
+ return 0 if is_clean is True else 1
262
+ if arguments.check_active:
263
+ is_active = is_bugbot_run_active(
264
+ owner=arguments.owner,
265
+ repo=arguments.repo,
266
+ sha=arguments.sha,
267
+ )
268
+ if not is_active:
269
+ print("bugbot: not found")
270
+ return 0 if is_active else 1
271
+ return check_bugbot_ci(
272
+ owner=arguments.owner,
273
+ repo=arguments.repo,
274
+ sha=arguments.sha,
275
+ )
276
+
277
+
278
+ if __name__ == "__main__":
279
+ raise SystemExit(main(sys.argv[1:]))