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.
- package/CLAUDE.md +10 -36
- package/_shared/pr-loop/audit-reply-template.md +147 -0
- package/_shared/pr-loop/fix-protocol.md +25 -4
- package/_shared/pr-loop/gh-payloads.md +37 -50
- package/_shared/pr-loop/scripts/code_rules_gate.py +0 -60
- package/_shared/pr-loop/scripts/config/post_audit_thread_constants.py +199 -0
- package/_shared/pr-loop/scripts/config/reviews_disabled_constants.py +8 -0
- package/_shared/pr-loop/scripts/post_audit_thread.py +1242 -0
- package/_shared/pr-loop/scripts/preflight.py +129 -2
- package/_shared/pr-loop/scripts/reviews_disabled.py +59 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -19
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +1116 -0
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +127 -0
- package/_shared/pr-loop/scripts/tests/test_preflight.py +41 -0
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +36 -0
- package/_shared/pr-loop/state-schema.md +1 -1
- package/agents/clean-coder.md +2 -2
- package/agents/pr-description-writer.md +150 -52
- package/bin/install.mjs +6 -7
- package/bin/install.test.mjs +8 -0
- package/commands/doc-gist.md +16 -0
- package/commands/plan.md +0 -2
- package/commands/review-plan.md +1 -1
- package/docs/CODE_RULES.md +122 -2
- package/docs/PR_DESCRIPTION_GUIDE.md +127 -64
- package/hooks/blocking/bot_mention_comment_blocker.py +75 -0
- package/hooks/blocking/code_rules_enforcer.py +1143 -129
- package/hooks/blocking/convergence_gate_blocker.py +130 -0
- package/hooks/blocking/destructive_command_blocker.py +74 -0
- package/hooks/blocking/gh_body_arg_blocker.py +30 -0
- package/hooks/blocking/md_to_html_blocker.py +119 -0
- package/hooks/blocking/pr_description_enforcer.py +57 -22
- package/hooks/blocking/test_bot_mention_comment_blocker.py +131 -0
- package/hooks/blocking/test_code_rules_enforcer.py +21 -0
- package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +70 -0
- package/hooks/blocking/test_code_rules_enforcer_any_imports_and_cast.py +92 -0
- package/hooks/blocking/test_code_rules_enforcer_banned_import_alias.py +143 -0
- package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +152 -0
- package/hooks/blocking/test_code_rules_enforcer_bare_except.py +120 -0
- package/hooks/blocking/test_code_rules_enforcer_boundary_types.py +175 -0
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
- package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +50 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_format.py +255 -0
- package/hooks/blocking/test_code_rules_enforcer_inline_tuple_string_magic.py +130 -0
- package/hooks/blocking/test_code_rules_enforcer_stub_implementations.py +141 -0
- package/hooks/blocking/test_code_rules_enforcer_test_branching.py +143 -0
- package/hooks/blocking/test_code_rules_enforcer_thin_wrapper_files.py +169 -0
- package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +99 -0
- package/hooks/blocking/test_code_rules_enforcer_typed_dict_pairs.py +141 -0
- package/hooks/blocking/test_convergence_gate_blocker.py +63 -0
- package/hooks/blocking/test_destructive_command_blocker.py +146 -0
- package/hooks/blocking/test_destructive_command_blocker_no_verify.py +102 -0
- package/hooks/blocking/test_gh_body_arg_blocker.py +45 -0
- package/hooks/blocking/test_md_to_html_blocker.py +317 -0
- package/hooks/blocking/test_pr_description_enforcer.py +69 -8
- package/hooks/config/any_type_config.py +7 -0
- package/hooks/config/banned_identifiers_constants.py +11 -0
- package/hooks/config/blocking_check_limits.py +38 -0
- package/hooks/config/bot_mention_comment_blocker_constants.py +20 -0
- package/hooks/config/code_rules_enforcer_constants.py +53 -0
- package/hooks/config/convergence_branch_constants.py +9 -0
- package/hooks/config/doc_gist_auto_publish_constants.py +18 -0
- package/hooks/config/html_companion_constants.py +20 -0
- package/hooks/config/inline_tuple_string_magic_constants.py +22 -0
- package/hooks/config/pr_description_enforcer_constants.py +14 -0
- package/hooks/config/test_banned_identifiers_constants.py +17 -0
- package/hooks/hooks.json +28 -20
- package/hooks/pyproject.toml +69 -0
- package/hooks/validators/mypy_integration.py +47 -1
- package/hooks/validators/run_all_validators.py +3 -3
- package/hooks/validators/test_mypy_integration.py +50 -1
- package/hooks/workflow/doc_gist_auto_publish.py +144 -0
- package/hooks/workflow/md_to_html_companion.py +365 -0
- package/hooks/workflow/test_doc_gist_auto_publish.py +117 -0
- package/hooks/workflow/test_md_to_html_companion.py +452 -0
- package/package.json +1 -1
- package/rules/gh-body-file.md +2 -0
- package/scripts/Install-SweepEmptyDirs.ps1 +111 -0
- package/scripts/check.ps1 +106 -0
- package/scripts/config/timing.py +11 -0
- package/scripts/sweep_empty_dirs.py +138 -0
- package/scripts/sync_to_cursor/rules.py +1 -1
- package/scripts/test_sweep_empty_dirs.py +183 -0
- package/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +323 -0
- package/skills/_shared/pr-loop/scripts/_cli_utils.py +22 -0
- package/skills/_shared/pr-loop/scripts/_path_resolver.py +165 -0
- package/skills/_shared/pr-loop/scripts/_xml_utils.py +20 -0
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +182 -0
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +185 -0
- package/skills/_shared/pr-loop/scripts/config/__init__.py +0 -0
- package/skills/_shared/pr-loop/scripts/config/path_resolver_constants.py +78 -0
- package/skills/_shared/pr-loop/scripts/init_loop_state.py +135 -0
- package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +175 -0
- package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +182 -0
- package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +206 -0
- package/skills/bugteam/CONSTRAINTS.md +21 -22
- package/skills/bugteam/EXAMPLES.md +3 -3
- package/skills/bugteam/PROMPTS.md +227 -67
- package/skills/bugteam/SKILL.md +132 -455
- package/skills/bugteam/reference/README.md +1 -1
- package/skills/bugteam/reference/audit-and-teammates.md +112 -39
- package/skills/bugteam/reference/audit-contract.md +4 -22
- package/skills/bugteam/reference/copilot-gap-analysis.md +8 -5
- package/skills/bugteam/reference/design-rationale.md +2 -2
- package/skills/bugteam/reference/github-pr-reviews.md +50 -57
- package/skills/bugteam/reference/obstacles/audit-assign-ids.md +13 -0
- package/skills/bugteam/reference/obstacles/audit-capture-excerpts.md +13 -0
- package/skills/bugteam/reference/obstacles/audit-walk-categories.md +13 -0
- package/skills/bugteam/reference/obstacles/audit-write-xml.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-append-summary.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-apply-fixes.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-git-add-commit.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-git-push.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-post-reply.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-publish-summary.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-py-compile.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-read-files.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-resolve-thread.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-test-suite.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-violation-count.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-write-xml.md +13 -0
- package/skills/bugteam/reference/team-setup.md +111 -9
- package/skills/bugteam/reference/teardown-publish-permissions.md +39 -8
- package/skills/bugteam/scripts/README.md +60 -0
- package/skills/bugteam/scripts/_claude_permissions_common.py +358 -0
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +976 -0
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +375 -0
- package/skills/bugteam/scripts/bugteam_preflight.py +328 -0
- package/skills/bugteam/scripts/config/bugteam_code_rules_gate_constants.py +25 -0
- package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +26 -0
- package/skills/bugteam/scripts/config/bugteam_preflight_constants.py +35 -0
- package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +20 -0
- package/skills/bugteam/scripts/config/probe_code_rules_enforcer_check_constants.py +12 -0
- package/skills/bugteam/scripts/config/windows_safe_rmtree_constants.py +7 -0
- package/skills/bugteam/scripts/grant_project_claude_permissions.py +175 -0
- package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +107 -0
- package/skills/bugteam/scripts/revoke_project_claude_permissions.py +220 -0
- package/skills/bugteam/scripts/test__claude_permissions_common.py +112 -0
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +400 -0
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +384 -0
- package/skills/bugteam/scripts/test_bugteam_preflight.py +309 -0
- package/skills/bugteam/scripts/test_claude_permissions_common.py +195 -0
- package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +55 -0
- package/skills/bugteam/scripts/test_probe_code_rules_enforcer_check.py +76 -0
- package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +55 -0
- package/skills/bugteam/scripts/test_windows_safe_rmtree.py +108 -0
- package/skills/bugteam/scripts/windows_safe_rmtree.py +100 -0
- package/skills/bugteam/test_skill_additions.py +1 -11
- package/skills/code/SKILL.md +176 -0
- package/skills/copilot-review/SKILL.md +16 -0
- package/skills/doc-gist/SKILL.md +99 -0
- package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +453 -0
- package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +515 -0
- package/skills/doc-gist/references/examples/03-code-review-pr.html +638 -0
- package/skills/doc-gist/references/examples/04-code-understanding.html +491 -0
- package/skills/doc-gist/references/examples/05-design-system.html +629 -0
- package/skills/doc-gist/references/examples/06-component-variants.html +605 -0
- package/skills/doc-gist/references/examples/07-prototype-animation.html +455 -0
- package/skills/doc-gist/references/examples/08-prototype-interaction.html +396 -0
- package/skills/doc-gist/references/examples/09-slide-deck.html +592 -0
- package/skills/doc-gist/references/examples/10-svg-illustrations.html +492 -0
- package/skills/doc-gist/references/examples/11-status-report.html +528 -0
- package/skills/doc-gist/references/examples/12-incident-report.html +596 -0
- package/skills/doc-gist/references/examples/13-flowchart-diagram.html +395 -0
- package/skills/doc-gist/references/examples/14-research-feature-explainer.html +381 -0
- package/skills/doc-gist/references/examples/15-research-concept-explainer.html +368 -0
- package/skills/doc-gist/references/examples/16-implementation-plan.html +702 -0
- package/skills/doc-gist/references/examples/17-pr-writeup.html +595 -0
- package/skills/doc-gist/references/examples/18-editor-triage-board.html +573 -0
- package/skills/doc-gist/references/examples/19-editor-feature-flags.html +663 -0
- package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +722 -0
- package/skills/doc-gist/references/examples/README.md +5 -0
- package/skills/doc-gist/scripts/config/__init__.py +0 -0
- package/skills/doc-gist/scripts/config/gist_upload_constants.py +16 -0
- package/skills/doc-gist/scripts/gist_upload.py +177 -0
- package/skills/doc-gist/scripts/test_gist_upload.py +51 -0
- package/skills/findbugs/SKILL.md +96 -2
- package/skills/monitor-open-prs/SKILL.md +14 -32
- package/skills/monitor-open-prs/test_skill_contract.py +0 -11
- package/skills/pr-consistency-audit/SKILL.md +112 -0
- package/skills/pr-consistency-audit/reference/detection-rules.md +96 -0
- package/skills/pr-consistency-audit/reference/illustrations.md +78 -0
- package/skills/pr-converge/SKILL.md +229 -23
- package/skills/pr-converge/config/__init__.py +0 -0
- package/skills/pr-converge/config/constants.py +63 -0
- package/skills/pr-converge/reference/convergence-gates.md +138 -44
- package/skills/pr-converge/reference/examples.md +43 -11
- package/skills/pr-converge/reference/fix-protocol.md +6 -5
- package/skills/pr-converge/reference/ground-rules.md +5 -3
- package/skills/pr-converge/reference/multi-pr-orchestration.md +44 -19
- package/skills/pr-converge/reference/obstacles/fix-post-replies.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-push.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-read-filelines.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-reset-state.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-resolve-threads.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-stage-commit.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-trigger-bugbot.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-write-test.md +13 -0
- package/skills/pr-converge/reference/per-tick.md +107 -31
- package/skills/pr-converge/reference/state-schema.md +22 -1
- package/skills/pr-converge/reference/stop-conditions.md +9 -7
- package/skills/pr-converge/scripts/README.md +34 -46
- package/skills/pr-converge/scripts/check_bugbot_ci.py +279 -0
- package/skills/pr-converge/scripts/check_convergence.py +497 -0
- package/skills/pr-converge/scripts/check_pending_reviews.py +154 -0
- package/skills/pr-converge/scripts/config/pr_converge_constants.py +118 -0
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +134 -0
- package/skills/pr-converge/scripts/post_fix_reply.py +168 -0
- package/skills/pr-converge/scripts/test_check_bugbot_ci.py +312 -0
- package/skills/pr-converge/workflows/schedule-wakeup-loop.md +5 -12
- package/skills/qbug/SKILL.md +157 -27
- package/skills/session-log/SKILL.md +216 -114
- package/skills/session-tidy/SKILL.md +1 -1
- package/skills/skill-builder/SKILL.md +138 -56
- package/skills/skill-builder/references/delegation-map.md +72 -113
- package/skills/skill-builder/references/progressive-disclosure.md +122 -0
- package/skills/skill-builder/references/self-audit-checklist.md +92 -0
- package/skills/skill-builder/references/skill-types.md +228 -0
- package/skills/skill-builder/references/thariq-x-post-skills.json +33 -0
- package/skills/skill-builder/templates/gap-analysis.md +15 -8
- package/skills/skill-builder/workflows/improve-skill.md +86 -57
- package/skills/skill-builder/workflows/new-skill.md +80 -168
- package/skills/skill-builder/workflows/polish-skill.md +78 -54
- package/skills/structure-prompt/SKILL.md +50 -0
- package/skills/structure-prompt/reference/adversarial-tuning.md +62 -0
- package/skills/structure-prompt/reference/block-classification.md +27 -0
- package/skills/structure-prompt/reference/canonical-case.md +48 -0
- package/skills/structure-prompt/reference/citation-depth.md +70 -0
- package/skills/structure-prompt/reference/cleanup.md +33 -0
- package/skills/structure-prompt/reference/constraints.md +33 -0
- package/skills/structure-prompt/reference/directives.md +37 -0
- package/skills/structure-prompt/reference/examples.md +72 -0
- package/skills/structure-prompt/reference/instantiation.md +51 -0
- package/skills/structure-prompt/reference/output-contract.md +72 -0
- package/skills/structure-prompt/reference/per-category.md +23 -0
- package/skills/structure-prompt/reference/persona.md +38 -0
- package/skills/structure-prompt/reference/research.md +33 -0
- package/skills/structure-prompt/reference/structure.md +28 -0
- package/agents/code-standards-agent.md +0 -93
- package/agents/groq-coder.md +0 -113
- package/agents/plan-executor.md +0 -226
- package/agents/project-docs-analyzer.md +0 -53
- package/agents/project-structure-organizer-agent.md +0 -72
- package/agents/skill-to-agent-converter.md +0 -370
- package/agents/skill-writer-agent.md +0 -470
- package/agents/user-docs-writer.md +0 -67
- package/agents/workflow-visual-documenter.md +0 -82
- package/commands/readability-review.md +0 -20
- package/hooks/mypy.ini +0 -2
- package/hooks/notification/attention_needed_notify.py +0 -71
- package/hooks/notification/claude_notification_handler.py +0 -67
- package/hooks/notification/notification_utils.py +0 -267
- package/hooks/notification/subagent_complete_notify.py +0 -381
- package/hooks/notification/test_attention_needed_notify.py +0 -47
- package/hooks/notification/test_claude_notification_handler.py +0 -54
- package/hooks/notification/test_notification_utils.py +0 -91
- package/hooks/notification/test_subagent_complete_notify.py +0 -79
- package/scripts/config/groq_bugteam_config.py +0 -230
- package/scripts/config/test_groq_bugteam_config.py +0 -83
- package/scripts/config/test_spec_implementer_prompt.py +0 -32
- package/scripts/groq_bugteam.README.md +0 -131
- package/scripts/groq_bugteam.py +0 -647
- package/scripts/groq_bugteam_dotenv.py +0 -40
- package/scripts/groq_bugteam_spec.py +0 -226
- package/scripts/test_groq_bugteam.py +0 -529
- package/scripts/test_groq_bugteam_apply_fix_from_spec.py +0 -426
- package/scripts/test_groq_bugteam_dotenv.py +0 -66
- package/scripts/test_groq_bugteam_spec.py +0 -338
- package/skills/bugteam/SKILL_EVALS.md +0 -309
- package/skills/dream/SKILL.md +0 -118
- package/skills/ingest/SKILL.md +0 -40
- package/skills/npm-creator/SKILL.md +0 -187
- package/skills/readability-review/SKILL.md +0 -127
- package/skills/resume-review/SKILL.md +0 -261
- package/skills/rule-audit/SKILL.md +0 -307
- package/skills/rule-creator/SKILL.md +0 -150
- package/skills/searching-obsidian-vault/SKILL.md +0 -131
- package/skills/skill-writer/REFERENCE.md +0 -284
- package/skills/skill-writer/SKILL.md +0 -222
- 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
|
package/skills/findbugs/SKILL.md
CHANGED
|
@@ -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:
|
|
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
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
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
|
-
-
|
|
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
|
-
- **
|
|
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
|
|
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
|
|
61
|
-
3. The
|
|
62
|
-
4. Bugteam runs its own
|
|
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>`
|
|
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
|
|
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.
|