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,5 @@
1
+ The example files in this directory are verbatim copies of Thariq Shihipar's [html-effectiveness](https://thariqs.github.io/html-effectiveness/) prototypes, used with permission as reference material for the doc-gist skill's artifact design gallery.
2
+
3
+ Each file demonstrates a distinct HTML artifact shape — annotated PR diff, design system swatches, incident timeline, slide deck, prompt tuner — and serves as learning material for fresh per-request HTML design. The gallery teaches what shapes work; the agent adapts per request rather than copying.
4
+
5
+ See [SKILL.md](../../SKILL.md) for the full gallery-to-artifact-type mapping.
File without changes
@@ -0,0 +1,16 @@
1
+ """Constants for the gist_upload utility.
2
+
3
+ GIST_HOST_PREFIX: anchor for parsing gh's emitted gist URL into user + id.
4
+ PREVIEW_URL_TEMPLATE: the htmlpreview.github.io shape that renders a raw gist file.
5
+ GIST_DEFAULT_FILENAME: filename used when input is stdin (no filename to inherit).
6
+ MINIMUM_GIST_URL_PARTS: gh's gist URL is /<user>/<id>; need at least these many segments.
7
+ """
8
+
9
+ GIST_HOST_PREFIX = "https://gist.github.com/"
10
+ PREVIEW_URL_TEMPLATE = (
11
+ "https://htmlpreview.github.io/?https://gist.githubusercontent.com/"
12
+ "{user}/{gist_id}/raw/{filename}"
13
+ )
14
+ GIST_DEFAULT_FILENAME = "doc.html"
15
+ MINIMUM_GIST_URL_PARTS = 2
16
+ UPLOAD_TIMEOUT_SECONDS = 45
@@ -0,0 +1,177 @@
1
+ """Upload an HTML file as a secret GitHub gist and emit the htmlpreview URL.
2
+
3
+ Single-purpose transport. Takes HTML on stdin or via `--input <path>`, runs
4
+ `gh gist create` (secret by default), parses the gist URL from gh's output,
5
+ composes the htmlpreview.github.io preview URL, optionally opens it in the
6
+ default browser, and prints both URLs.
7
+
8
+ Designed to compose with anything that produces HTML — pipe in the output of
9
+ a script, the contents of a hand-authored file, or have the auto-publish
10
+ hook invoke it on a file Claude just wrote. The script does not opine on
11
+ HTML shape, structure, or styling.
12
+
13
+ Usage:
14
+ gist_upload.py --input <path>
15
+ gist_upload.py --input - --filename writeup.html < some.html
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import os
22
+ import subprocess
23
+ import sys
24
+ import tempfile
25
+ import webbrowser
26
+ from pathlib import Path
27
+ from urllib.parse import quote
28
+
29
+ _script_directory = str(Path(__file__).resolve().parent)
30
+ if _script_directory not in sys.path:
31
+ sys.path.insert(0, _script_directory)
32
+
33
+ from config.gist_upload_constants import ( # noqa: E402
34
+ GIST_DEFAULT_FILENAME,
35
+ GIST_HOST_PREFIX,
36
+ MINIMUM_GIST_URL_PARTS,
37
+ PREVIEW_URL_TEMPLATE,
38
+ UPLOAD_TIMEOUT_SECONDS,
39
+ )
40
+
41
+
42
+ def _read_html(input_argument: str) -> str:
43
+ """Read HTML content from stdin (when input is '-') or a file path.
44
+
45
+ Reads the full stdin stream when input_argument is '-'. Callers piping
46
+ content through an external process should ensure bounded input.
47
+ """
48
+ if input_argument == "-":
49
+ return sys.stdin.read()
50
+ try:
51
+ return Path(input_argument).expanduser().resolve().read_text(encoding="utf-8")
52
+ except (OSError, UnicodeError) as error:
53
+ raise SystemExit(f"Cannot read {input_argument}: {error}")
54
+
55
+
56
+ def _resolve_filename(input_argument: str, override: str | None) -> str:
57
+ """Pick the filename gh will use for the gist file."""
58
+ default_filename = GIST_DEFAULT_FILENAME
59
+ if override:
60
+ return Path(override).name
61
+ if input_argument != "-":
62
+ return Path(input_argument).name
63
+ return default_filename
64
+
65
+
66
+ def _write_to_temp(html_text: str, filename: str, parent_directory: Path) -> Path:
67
+ """Write HTML to a temp file inside parent_directory with the given filename."""
68
+ target_path = parent_directory / filename
69
+ target_path.write_text(html_text, encoding="utf-8")
70
+ return target_path
71
+
72
+
73
+ def _create_secret_gist(html_path: Path, description: str) -> str:
74
+ """Run `gh gist create` against html_path and return the gist URL."""
75
+ try:
76
+ completed = subprocess.run(
77
+ ["gh", "gist", "create", str(html_path), "--desc", description],
78
+ check=False,
79
+ capture_output=True,
80
+ text=True,
81
+ encoding="utf-8",
82
+ errors="replace",
83
+ timeout=UPLOAD_TIMEOUT_SECONDS,
84
+ )
85
+ except FileNotFoundError:
86
+ raise SystemExit(
87
+ "gh CLI not found. Install GitHub CLI (https://cli.github.com) and run `gh auth login`."
88
+ )
89
+ except subprocess.TimeoutExpired:
90
+ raise SystemExit(
91
+ f"gh gist create timed out after {UPLOAD_TIMEOUT_SECONDS}s. Check network and retry."
92
+ )
93
+ if completed.returncode != 0:
94
+ message_text = completed.stderr.strip() or completed.stdout.strip()
95
+ raise SystemExit(
96
+ f"gh gist create failed:\n{message_text}\nRun `gh auth login` and retry."
97
+ )
98
+ output_lines = completed.stdout.strip().splitlines()
99
+ if not output_lines:
100
+ raise SystemExit("gh gist create produced no output.")
101
+ last_line = output_lines[-1].strip()
102
+ if not last_line.startswith(GIST_HOST_PREFIX):
103
+ raise SystemExit(f"Unexpected gh gist create output: {last_line!r}")
104
+ return last_line
105
+
106
+
107
+ def _compose_preview_url(gist_url: str, filename: str) -> str:
108
+ """Build the htmlpreview.github.io URL from the gist URL and the gist filename."""
109
+ minimum_parts = MINIMUM_GIST_URL_PARTS
110
+ preview_template = PREVIEW_URL_TEMPLATE
111
+ path_after_host = gist_url[len(GIST_HOST_PREFIX) :]
112
+ parts = path_after_host.split("/")
113
+ if len(parts) < minimum_parts:
114
+ raise SystemExit(f"Cannot parse gist URL: {gist_url!r}")
115
+ return preview_template.format(user=parts[0], gist_id=parts[1], filename=quote(filename, safe=""))
116
+
117
+
118
+ def _open_in_browser(url: str) -> None:
119
+ """Open the URL in the default browser, deferring to OS-native behavior on Windows."""
120
+ if sys.platform.startswith("win"):
121
+ os.startfile(url)
122
+ return
123
+ webbrowser.open(url)
124
+
125
+
126
+ def main() -> int:
127
+ """Entry point — read HTML, upload as secret gist, print URLs, open preview.
128
+
129
+ Returns:
130
+ Process exit code (0 on success).
131
+ """
132
+ parser = argparse.ArgumentParser(
133
+ description="Upload HTML as a secret gist; print gist + htmlpreview URLs."
134
+ )
135
+ parser.add_argument(
136
+ "--input",
137
+ required=True,
138
+ help="Path to an .html file, or '-' for stdin.",
139
+ )
140
+ parser.add_argument(
141
+ "--filename",
142
+ default=None,
143
+ help="Filename inside the gist. Defaults to the input filename, or 'doc.html' for stdin.",
144
+ )
145
+ parser.add_argument(
146
+ "--description",
147
+ default="HTML artifact",
148
+ help="Gist description.",
149
+ )
150
+ parser.add_argument(
151
+ "--no-open",
152
+ action="store_true",
153
+ help="Skip opening the preview URL in the default browser.",
154
+ )
155
+ arguments = parser.parse_args()
156
+
157
+ html_text = _read_html(arguments.input)
158
+ filename = _resolve_filename(arguments.input, arguments.filename)
159
+
160
+ with tempfile.TemporaryDirectory(prefix="gist-upload-") as temp_directory:
161
+ upload_path = _write_to_temp(html_text, filename, Path(temp_directory))
162
+ gist_url = _create_secret_gist(upload_path, arguments.description)
163
+ preview_url = _compose_preview_url(gist_url, filename)
164
+
165
+ print(f"Gist: {gist_url}", file=sys.stderr)
166
+ print(f"Preview: {preview_url}", file=sys.stderr)
167
+
168
+ if not arguments.no_open:
169
+ _open_in_browser(preview_url)
170
+ print("Opened preview in default browser.", file=sys.stderr)
171
+
172
+ print(preview_url)
173
+ return 0
174
+
175
+
176
+ if __name__ == "__main__":
177
+ sys.exit(main())
@@ -0,0 +1,51 @@
1
+ """Tests for gist_upload.py pure functions — URL composition and filename resolution."""
2
+
3
+ from pathlib import Path
4
+ import sys
5
+
6
+ _script_directory = str(Path(__file__).resolve().parent)
7
+ if _script_directory not in sys.path:
8
+ sys.path.insert(0, _script_directory)
9
+
10
+ from gist_upload import _compose_preview_url, _resolve_filename
11
+
12
+
13
+ class TestResolveFilename:
14
+ def test_uses_override_when_provided(self):
15
+ assert (
16
+ _resolve_filename("report.html", "custom-name.html") == "custom-name.html"
17
+ )
18
+
19
+ def test_strips_directory_from_override(self):
20
+ assert (
21
+ _resolve_filename("report.html", "/some/path/override.html")
22
+ == "override.html"
23
+ )
24
+
25
+ def test_uses_input_filename_when_no_override(self):
26
+ assert _resolve_filename("report.html", None) == "report.html"
27
+
28
+ def test_uses_default_for_stdin_without_override(self):
29
+ assert _resolve_filename("-", None) == "doc.html"
30
+
31
+ def test_override_takes_precedence_over_stdin(self):
32
+ assert _resolve_filename("-", "writeup.html") == "writeup.html"
33
+
34
+
35
+ class TestComposePreviewUrl:
36
+ def test_standard_gist_url(self):
37
+ result = _compose_preview_url(
38
+ "https://gist.github.com/user123/abc123def456",
39
+ "report.html",
40
+ )
41
+ assert result == (
42
+ "https://htmlpreview.github.io/"
43
+ "?https://gist.githubusercontent.com/user123/abc123def456/raw/report.html"
44
+ )
45
+
46
+ def test_filename_with_spaces_is_encoded(self):
47
+ result = _compose_preview_url(
48
+ "https://gist.github.com/user123/abc123def456",
49
+ "my report.html",
50
+ )
51
+ assert "my%20report.html" in result
@@ -18,6 +18,16 @@ User types `/findbugs` or asks for a bug audit on the current branch's PR. Typic
18
18
 
19
19
  If the current branch has no associated PR and no diff against the default branch, say so and stop. Do not invent scope.
20
20
 
21
+ ## Refusals
22
+
23
+ First match wins; respond with the quoted line exactly and stop:
24
+
25
+ - **Disabled via environment.** When `CLAUDE_REVIEWS_DISABLED` contains the
26
+ token `bugteam` (comma-separated, case-insensitive, whitespace-tolerant):
27
+ `/findbugs is disabled via CLAUDE_REVIEWS_DISABLED.` `/findbugs` is a PR
28
+ bug-audit skill in the same family as `/bugteam` and `/qbug`, so the
29
+ shared `bugteam` token disables all three.
30
+
21
31
  ## The Process
22
32
 
23
33
  ### Step 1: Resolve PR scope
@@ -112,7 +122,91 @@ The XML prompt skeleton:
112
122
  </output_format>
113
123
  ```
114
124
 
115
- ### Step 4: Surface findings, then clean up
125
+ ### Step 4: Post the audit review
126
+
127
+ Every `/findbugs` invocation posts one audit review back to the PR. The
128
+ unresolved-thread gate counts review threads, so a findbugs pass that
129
+ returns findings without posting them as inline comments is invisible
130
+ to the gate. Findbugs remains read-only on code — the review post is
131
+ the only side effect.
132
+
133
+ **Self-PR auto-toggle.** GitHub rejects both `APPROVE` and
134
+ `REQUEST_CHANGES` reviews with HTTP 422 when the authenticated identity
135
+ matches the PR author ("Cannot approve/request changes on your own pull
136
+ request"). `post_audit_thread.py` detects this case via `gh api user` +
137
+ `gh api repos/<o>/<r>/pulls/<n>` and auto-resolves an alternate gh
138
+ account's token for the reviews POST — the active `gh auth` account is
139
+ not mutated; only the bearer token sent on the request changes. After
140
+ the POST the active account is still whoever it was before, so no
141
+ "swap back" step is needed.
142
+
143
+ Configuration:
144
+
145
+ - `GH_TOKEN` / `GITHUB_TOKEN` env vars take precedence over the toggle.
146
+ Set them when you need to pin a specific reviewer identity by token
147
+ rather than by account login.
148
+ - `BUGTEAM_REVIEWER_ACCOUNT` env var names which authenticated alternate
149
+ to prefer when a toggle is needed (for example,
150
+ `BUGTEAM_REVIEWER_ACCOUNT=jl-cmd`). The env var name is shared across
151
+ every skill that invokes `post_audit_thread.py`. When unset, the
152
+ script falls back to the first alternate account `gh auth status`
153
+ reports.
154
+ - The named alternate must be logged in (`gh auth login -h github.com -u
155
+ <login>`) before the audit skill runs. The script exits 1 with a
156
+ pointing-at-`gh auth login` message when self-PR is detected and no
157
+ usable alternate is authenticated.
158
+
159
+ After the agent (and Haiku secondary) return and the merge is complete,
160
+ serialize the merged findings to a JSON file and call
161
+ `post_audit_thread.py`:
162
+
163
+ ```
164
+ python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/post_audit_thread.py" \
165
+ --skill findbugs \
166
+ --owner <owner> \
167
+ --repo <repo> \
168
+ --pr-number <N> \
169
+ --commit <head_sha> \
170
+ --state <CLEAN|DIRTY> \
171
+ --findings-json <path>
172
+ ```
173
+
174
+ The findings JSON root is a list of objects shaped
175
+ `{path, line, side, severity, description, fix_summary}`. Before
176
+ serializing, partition the merged findings into anchored (line appears
177
+ in the captured diff) and unanchored (line is not in the diff) buckets.
178
+ Only anchored findings are serialized to the JSON — the GitHub reviews
179
+ API rejects the entire POST if any inline comment in `comments[]`
180
+ targets a line not in the diff at `--commit`, so a single unanchored
181
+ entry breaks the whole review. Unanchored findings are surfaced via
182
+ Step 5's user-facing output rather than as inline anchored comments.
183
+ For each anchored finding, map `file` → `path`; split the finding's
184
+ `failure_mode` at the literal `Fix:` heading so the failure narrative
185
+ becomes `description` and the suffix beginning at `Fix:` (including
186
+ the trailing `Validation:` clause) becomes `fix_summary` (the
187
+ `failure_mode` field carries the full audit-to-fix handoff per
188
+ [`agents/code-quality-agent.md`](../../agents/code-quality-agent.md)).
189
+ When a finding's `failure_mode` omits the `Fix:` heading, write the
190
+ full text to BOTH `description` and `fix_summary`. Set `side="RIGHT"`
191
+ for every entry. Zero anchored findings → `--state CLEAN` with the
192
+ findings file holding an empty array (`[]`); one or more anchored
193
+ findings → `--state DIRTY` with the full list. CLEAN posts an APPROVE review (the
194
+ request event; GitHub stores it as `state=APPROVED`) with a "no
195
+ findings" summary; DIRTY posts a REQUEST_CHANGES review with one inline
196
+ anchored comment per finding (each becomes its own resolvable thread on
197
+ the PR).
198
+
199
+ Capture `<head_sha>` once at the start of Step 4 via `git rev-parse
200
+ HEAD` in the worktree the diff was scoped against.
201
+
202
+ Exit codes: `0` on success (emits the new review's `html_url` to
203
+ stdout, surface alongside the totals in Step 5); `1` on user input
204
+ error; `2` on retry exhaustion (1s / 4s / 16s backoff across four
205
+ attempts). On exit 2, tell the user the review post failed and that
206
+ the unresolved-thread gate will not see this audit pass; do not retry
207
+ silently.
208
+
209
+ ### Step 5: Surface findings, then clean up
116
210
 
117
211
  When the agent returns, report concisely:
118
212
 
@@ -148,7 +242,7 @@ Want me to run /fixbugs for the P0/P1 findings?
148
242
 
149
243
  ## Constraints
150
244
 
151
- - **Read-only.** The skill never edits code, never pushes, never commits.
245
+ - **Read-only on code.** The skill never edits files, never pushes, never commits. One audit review per invocation gets posted back to the PR (Step 4) — that is the only side effect, and it is required so the unresolved-thread gate sees the audit pass.
152
246
  - **Foreground spawn.** The user is waiting for the result on this turn.
153
247
  - **PR-scoped, not session-scoped.** The audit covers the entire PR diff regardless of which files were edited in this conversation.
154
248
  - **Clean-room prompt.** The agent's prompt is self-contained — no references to chat history, no anchoring hints, no expected outcomes.
@@ -2,24 +2,22 @@
2
2
  name: monitor-open-prs
3
3
  description: >-
4
4
  Discover every open pull request across the jl-cmd/* and JonEcho/*
5
- owner scopes, spawn /bugteam on each in parallel with the Groq-backed
6
- FIX implementer (BUGTEAM_FIX_IMPLEMENTER=groq-coder) and the bugbot
7
- re-trigger flag (--bugbot-retrigger), wrap the session in `bws run`
8
- to inject GROQ_API_KEY, and poll Cursor's bugbot replies after
9
- convergence so any post-Groq findings loop back through /bugteam.
10
- Triggers: '/monitor-open-prs', 'sweep the open PRs', 'groq-bugteam the backlog'.
5
+ owner scopes, spawn /bugteam on each in parallel with the bugbot
6
+ re-trigger flag (--bugbot-retrigger), and poll Cursor's bugbot replies
7
+ after convergence so any post-bugteam findings loop back through
8
+ /bugteam. Triggers: '/monitor-open-prs', 'sweep the open PRs',
9
+ 'audit the open PR backlog'.
11
10
  ---
12
11
 
13
12
  # Monitor Open PRs
14
13
 
15
- **Core principle:** One sweep covers every open PR across both owner scopes. Claude discovers PRs live via `scripts/discover_open_prs.py` which shells out to `gh search prs --owner <owner> --state open --json ...`, dispatches `/bugteam` per PR with `BUGTEAM_FIX_IMPLEMENTER=groq-coder` and `--bugbot-retrigger`, then polls Cursor's bugbot replies until each PR is quiet for a full backoff cycle.
14
+ **Core principle:** One sweep covers every open PR across both owner scopes. Claude discovers PRs live via `scripts/discover_open_prs.py` which shells out to `gh search prs --owner <owner> --state open --json ...`, dispatches `/bugteam --bugbot-retrigger` per PR, then polls Cursor's bugbot replies until each PR is quiet for a full backoff cycle.
16
15
 
17
16
  ## Contents
18
17
 
19
18
  - When this skill applies — refusal cases and trigger conditions
20
19
  - Discovery — `scripts/discover_open_prs.py` queries via `gh search prs` across both owner scopes
21
- - Wrapping`bws run` for GROQ_API_KEY injection
22
- - Dispatch — `/bugteam --bugbot-retrigger <pr_numbers...>` with groq-coder + retrigger
20
+ - Dispatch`/bugteam --bugbot-retrigger <pr_numbers...>`
23
21
  - Post-convergence polling — bugbot replies and re-invocation
24
22
  - `scripts/discover_open_prs.py` — the discovery helper
25
23
 
@@ -29,37 +27,23 @@ description: >-
29
27
 
30
28
  Refusals — first match wins; respond with the quoted line exactly and stop:
31
29
 
32
- - **bws not on PATH.** `bws not installed. /monitor-open-prs injects GROQ_API_KEY via Bitwarden Secrets Manager.`
30
+ - **Disabled via environment.** When `CLAUDE_REVIEWS_DISABLED` (comma-separated, case-insensitive, whitespace-tolerant) contains the token `bugteam`: `/monitor-open-prs is a /bugteam dispatcher and /bugteam is disabled via CLAUDE_REVIEWS_DISABLED.`
33
31
  - **GitHub API not accessible.** `get_me failed. /monitor-open-prs needs active GitHub MCP credentials.`
34
32
  - **Dirty tree on the caller's repo.** `Uncommitted changes detected. Stash, commit, or revert before /monitor-open-prs.`
35
- - **Required subagents missing.** Confirm `code-quality-agent`, `clean-coder`, and `groq-coder` exist. Else: `Required subagent type <name> not installed.`
33
+ - **Required subagents missing.** Confirm `code-quality-agent` and `clean-coder` exist. Else: `Required subagent type <name> not installed.`
36
34
 
37
35
  ## Discovery
38
36
 
39
37
  Call `scripts/discover_open_prs.discover_open_prs(all_owners=["jl-cmd", "JonEcho"])` to merge the live open-PR list across both scopes. The helper shells out to `gh search prs --owner <owner> --state open --json number,repository,url,headRefName,baseRefName` for each owner scope and flattens the result to a uniform dict shape with keys `number`, `owner`, `repo`, `head_ref`, `base_ref`, `url`. Empty scopes contribute empty lists; an entirely empty sweep returns `[]` and exits cleanly.
40
38
 
41
- ## Secret Wrapping
42
-
43
- Every `/bugteam` dispatch runs inside `bws run` so `GROQ_API_KEY` is injected from Bitwarden Secrets Manager without touching the filesystem. The project and secret UUIDs are fixed for this skill:
44
-
45
- ```bash
46
- bws run \
47
- --project-id c69cedc5-aea1-4aa8-b350-b4300145d978 \
48
- -- \
49
- env BUGTEAM_FIX_IMPLEMENTER=groq-coder \
50
- /bugteam --bugbot-retrigger <pr_numbers...>
51
- ```
52
-
53
- The `bws run` subshell resolves the project's secrets and exports them for the wrapped command. The `GROQ_API_KEY` secret's UUID inside that project is `b7e99a7f-2ecc-42b3-99a5-b434010622f9`. GitHub auth is not sourced through `bws` — existing MCP `get_me` credentials carry the session.
54
-
55
39
  ## Dispatch
56
40
 
57
41
  For each discovered PR:
58
42
 
59
43
  1. Resolve the PR's repo checkout (existing worktree or fresh `git clone`).
60
- 2. From that checkout, invoke `/bugteam --bugbot-retrigger <pr_number>` under the `bws run` wrapper from §Secret Wrapping.
61
- 3. The `BUGTEAM_FIX_IMPLEMENTER=groq-coder` env var routes the FIX role to the `groq-coder` subagent. The `--bugbot-retrigger` flag tells bugteam to post `bugbot run` as an issue comment after every successful FIX push so Cursor's bugbot re-evaluates the new commit.
62
- 4. Bugteam runs its own 10-loop audit/fix cycle per PR; this skill waits for each bugteam invocation to return before dispatching the next (or fanning out — see below).
44
+ 2. From that checkout, invoke `/bugteam --bugbot-retrigger <pr_number>`. When `CLAUDE_REVIEWS_DISABLED` (comma-separated, case-insensitive, whitespace-tolerant) contains the token `bugbot`, omit `--bugbot-retrigger` from the dispatched command so the bugbot leg sits out the run.
45
+ 3. The `--bugbot-retrigger` flag tells bugteam to post `bugbot run` as an issue comment after every successful FIX push so Cursor's bugbot re-evaluates the new commit.
46
+ 4. Bugteam runs its own 20-loop audit/fix cycle per PR; this skill waits for each bugteam invocation to return before dispatching the next (or fanning out — see below).
63
47
 
64
48
  **Fan-out (optional):** when the discovered list has more than one PR, the skill may spawn `/bugteam` dispatches in parallel by issuing multiple `Agent` calls in a single assistant message. Each dispatch operates in its own per-PR worktree (bugteam Step 1.1). Serialize when the caller sets an explicit `--serial` flag.
65
49
 
@@ -70,7 +54,7 @@ After a `/bugteam` invocation returns (converged, cap reached, stuck, or error),
70
54
  1. Baseline: capture `since_timestamp` as the PR's last commit timestamp.
71
55
  2. Every 60 seconds, call `pull_request_read(method="get", pullNumber=<pr_number>, owner=<owner>, repo=<repo>)` and filter the response's `comments` array for entries whose `.author.login` matches `"bugbot"` or `"cursor"` and `.createdAt` is after `<since_timestamp>`.
72
56
  3. Back off: 60s → 120s → 240s → 480s → 960s. If five successive polls return empty, exit polling for this PR.
73
- 4. If bugbot posts a new finding in any poll, re-invoke `/bugteam <pr_number>` via the same `bws run` wrapper with the bugbot finding text seeded into the invocation's `bugs_to_fix` preamble. Reset the backoff.
57
+ 4. If bugbot posts a new finding in any poll, re-invoke `/bugteam <pr_number>` with the bugbot finding text seeded into the invocation's `bugs_to_fix` preamble. Reset the backoff.
74
58
 
75
59
  ### Polling Cost and Cadence
76
60
 
@@ -86,16 +70,14 @@ PRs discovered: <N>
86
70
  jl-cmd/*: <count>
87
71
  JonEcho/*: <count>
88
72
  PRs converged clean: <count>
89
- PRs hit 10-loop cap: <count>
73
+ PRs hit 20-loop cap: <count>
90
74
  PRs stuck: <count>
91
75
  PRs errored: <count>
92
76
  Bugbot re-triggers fired: <count>
93
- Total Groq tokens consumed: <approx from /bugteam outcome summaries>
94
77
  ```
95
78
 
96
79
  ## Non-Negotiable Guardrails
97
80
 
98
- - Never source secrets outside `bws run` — no `.env` files, no shell history, no logs.
99
81
  - Never pass `--no-verify` or `--no-gpg-sign` to git in any dispatched bugteam run.
100
82
  - Never open a PR from this skill; only comment on existing ones.
101
83
  - Never merge or close PRs; the skill is read + audit + patch only.
@@ -16,12 +16,6 @@ def test_skill_has_frontmatter_name():
16
16
  assert "name: monitor-open-prs" in skill_text
17
17
 
18
18
 
19
- def test_skill_invokes_bugteam_with_groq_implementer():
20
- skill_text = _read_skill_text()
21
- assert "BUGTEAM_FIX_IMPLEMENTER" in skill_text
22
- assert "groq-coder" in skill_text
23
-
24
-
25
19
  def test_skill_references_bugbot_retrigger_flag():
26
20
  skill_text = _read_skill_text()
27
21
  assert "--bugbot-retrigger" in skill_text
@@ -31,8 +25,3 @@ def test_skill_enumerates_both_owner_scopes():
31
25
  skill_text = _read_skill_text()
32
26
  assert "jl-cmd" in skill_text
33
27
  assert "JonEcho" in skill_text
34
-
35
-
36
- def test_skill_documents_bws_wrapping():
37
- skill_text = _read_skill_text()
38
- assert "bws run" in skill_text
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: pr-consistency-audit
3
+ description: >-
4
+ Audits a PR for cross-file inconsistencies. Finds wrong argument names, missing required args, references to files or scripts that do not exist, stale feature remnants, docstring-vs-implementation mismatches, placeholder text, cross-file contradictions, parameter naming convention violations, and cross-platform bugs. Use when the user says "audit this PR", "find inconsistencies", "cross-reference docs against scripts", "check for stale references", "PR consistency audit".
5
+ ---
6
+
7
+ # PR Consistency Audit
8
+
9
+ Read every changed file in a pull request. Find every inconsistency across files. Leave nothing unchecked. Leave nothing assumed. Zero context needed beyond the diff.
10
+
11
+ ## Gotchas
12
+
13
+ - The highest-signal rule is canonical-source cross-reference. Do it first. Do it slowly. Copilot found 20 of its 43 findings with this rule alone. Missing it means missing half the bugs.
14
+ - Parameter naming conventions are per-tool, not per-project. `issue_read` uses `issue_number` (snake_case). `add_issue_comment` uses `issueNumber` (camelCase). A doc that mixes them is wrong even if snake_case works for `issue_read`.
15
+ - When a finding appears in many files with the same wrong pattern, flag every instance individually. Do not say "this is wrong in 20 files" and move on. List every file with its line.
16
+ - Template files and obstacle files are often skipped because they feel "generated" or "low priority." They are not. Copilot found 6 issues in template files that human auditors never opened.
17
+ - A manifest written to a temp file is not optional. Hold all script signatures, all MCP tool names, all constants, and all concept names in a JSON file. Read from it during detection. Memory fails. Files do not.
18
+
19
+ ## When this skill applies
20
+
21
+ Trigger when the user provides a PR diff, a list of changed files, or asks to audit cross-file consistency. The agent needs no prior knowledge of the repo, the scripts, or the project conventions.
22
+
23
+ ## Process
24
+
25
+ ### Step 1: Build the manifest
26
+
27
+ Read every file in the diff. Top to bottom. Build `<tmp>/audit-manifest-<timestamp>.json`. Track:
28
+
29
+ - Python scripts → argparse arguments (`add_argument("--name", ...)`), which are `required=True`
30
+ - MCP tool call patterns in docs → tool name, every parameter used, file and line
31
+ - Shell commands (`gh`, `git`, `python`, `bash`) → full command, file, line
32
+ - File paths referenced in docs → whether they resolve to real files
33
+ - Named concepts in prose → "inline_lag", "COPILOT_WAIT", "bugfind", phase names, state values. File and line each.
34
+ - Constants, thresholds, timeouts → value, concept it measures, file, line
35
+ - Functions with docstring claims → function name, claim made, file, line
36
+
37
+ ### Step 2: Find canonical sources
38
+
39
+ Identify the files that define the authoritative form of each concept. Signs a file is canonical:
40
+
41
+ - Has structured schemas, payload definitions, API contracts (files like `gh-payloads.md`, `mcp_tool_signatures.json`, `config.py`)
42
+ - Name includes "reference", "spec", "schema", "payload", "contract", "canonical"
43
+ - Other files cite it as the source of truth
44
+ - It defines the implementation that docstrings describe (the `.py` file, not the `.md` that talks about it)
45
+
46
+ For each canonical source, note what concept it is canonical for. When a rule needs to decide what is "correct," the canonical source wins.
47
+
48
+ ### Step 3: Run detection rules
49
+
50
+ Read [`reference/detection-rules.md`](reference/detection-rules.md). Run all ten rules against every file. Write findings immediately to `<tmp>/inconsistency-audit-<timestamp>.csv`:
51
+
52
+ ```
53
+ file_path | line_number | rule_id | severity | what_is_wrong | what_it_should_be | evidence_path | evidence_detail
54
+ ```
55
+
56
+ ### Step 4: Produce summary
57
+
58
+ Print to stdout:
59
+
60
+ ```
61
+ ====== DIFF INCONSISTENCY AUDIT ======
62
+ Files audited: <N>
63
+ Canonical sources: <list>
64
+ Total findings: <N>
65
+
66
+ By rule:
67
+ Rule 1 — canonical_source_cross_reference: X (P0: A, P1: B, P2: C)
68
+ ...
69
+
70
+ By severity:
71
+ P0 (runtime failure): X
72
+ P1 (confusing or wrong): Y
73
+ P2 (cleanup): Z
74
+
75
+ Top findings:
76
+ 1. file:line — [P0] — what is wrong — what it should be
77
+ 2. ...
78
+
79
+ Full report: <tmp>/inconsistency-audit-<timestamp>.csv
80
+ Manifest: <tmp>/audit-manifest-<timestamp>.json
81
+ ====== END AUDIT ======
82
+ ```
83
+
84
+ ## Constraints
85
+
86
+ - Read every file completely. No skimming.
87
+ - Write findings immediately. Do not batch in memory.
88
+ - Every finding cites file:line of problem AND file:line of evidence.
89
+ - When two files contradict, flag both. Do not guess which is correct.
90
+ - If unresolvable, mark "unresolvable — no canonical source found."
91
+ - Run every rule. The highest-signal findings come from rules you think will be empty.
92
+
93
+ ## Severity
94
+
95
+ | Severity | Meaning |
96
+ |----------|---------|
97
+ | P0 | Runtime failure — the command or tool call will error |
98
+ | P1 | Confusing or wrong — will mislead but not crash |
99
+ | P2 | Cleanup — stale references, docs out of sync |
100
+
101
+ ## File index
102
+
103
+ | File | Purpose |
104
+ |------|---------|
105
+ | `SKILL.md` | Hub — process, constraints, gotchas |
106
+ | `reference/detection-rules.md` | All 10 detection rules with procedures |
107
+ | `reference/illustrations.md` | Concrete findings with why-they-matter explanations |
108
+
109
+ ## Folder map
110
+
111
+ - `SKILL.md` — hub.
112
+ - `reference/` — detection rules and illustrations.