claude-dev-env 1.38.0 → 1.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +189 -0
- package/_shared/pr-loop/scripts/post_audit_thread.py +947 -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 +923 -0
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +127 -0
- package/_shared/pr-loop/state-schema.md +1 -1
- package/agents/clean-coder.md +2 -2
- 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/hooks/blocking/bot_mention_comment_blocker.py +75 -0
- package/hooks/blocking/code_rules_enforcer.py +1236 -161
- 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/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_code_rules_enforcer_unused_imports.py +158 -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/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/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 +114 -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 +106 -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 +294 -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 +268 -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/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 +68 -2
- package/skills/monitor-open-prs/SKILL.md +13 -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 +227 -23
- package/skills/pr-converge/config/__init__.py +0 -0
- package/skills/pr-converge/config/constants.py +62 -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 +90 -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 +174 -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/workflows/schedule-wakeup-loop.md +5 -12
- package/skills/qbug/SKILL.md +132 -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,118 @@
|
|
|
1
|
+
"""Configuration constants for the pr-converge skill scripts.
|
|
2
|
+
|
|
3
|
+
Path templates accept ``str.format(**kwargs)`` substitution; bugbot strings
|
|
4
|
+
match the literal phrasing the Cursor Bugbot reviewer emits.
|
|
5
|
+
|
|
6
|
+
All runtime and API constants are imported from ``config.constants``.
|
|
7
|
+
Only script-specific constants (CLI args, markdown patterns, reflow
|
|
8
|
+
settings) live here.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from config.constants import ( # noqa: F401
|
|
15
|
+
ALL_BUGBOT_CHECK_RUN_ACTIVE_STATUSES,
|
|
16
|
+
ALL_CLAUDE_DIRTY_REVIEW_STATES,
|
|
17
|
+
ALL_COPILOT_DIRTY_REVIEW_STATES,
|
|
18
|
+
ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS,
|
|
19
|
+
BUGBOT_CHECK_RUN_NAME_SUBSTRING,
|
|
20
|
+
BUGBOT_DIRTY_BODY_REGEX,
|
|
21
|
+
BUGBOT_RUN_TRIGGER_PHRASE,
|
|
22
|
+
BUGBOT_RUN_TRIGGER_WAIT_SECONDS,
|
|
23
|
+
CHECK_RUNS_PER_PAGE,
|
|
24
|
+
ALL_CLAUDE_CLEAN_REVIEW_STATES,
|
|
25
|
+
CLAUDE_LOGIN_FILTER_SUBSTRING,
|
|
26
|
+
CLAUDE_REVIEWER_LOGIN,
|
|
27
|
+
CLAUDE_REVIEWER_REQUEST_ID,
|
|
28
|
+
CLAUDE_SOFT_DIRTY_REVIEW_STATE,
|
|
29
|
+
ALL_COPILOT_CLEAN_REVIEW_STATES,
|
|
30
|
+
COPILOT_LOGIN_FILTER_SUBSTRING,
|
|
31
|
+
COPILOT_REVIEWER_LOGIN,
|
|
32
|
+
COPILOT_REVIEWER_REQUEST_ID,
|
|
33
|
+
COPILOT_SOFT_DIRTY_REVIEW_STATE,
|
|
34
|
+
CURSOR_BOT_LOGIN,
|
|
35
|
+
CURSOR_LOGIN_FILTER_SUBSTRING,
|
|
36
|
+
EXIT_CODE_GH_ERROR,
|
|
37
|
+
EXIT_CODE_NOT_FOUND,
|
|
38
|
+
GH_CHECK_RUNS_PATH_TEMPLATE,
|
|
39
|
+
GH_INLINE_COMMENT_CREATE_PATH_TEMPLATE,
|
|
40
|
+
GH_INLINE_COMMENTS_PATH_TEMPLATE,
|
|
41
|
+
GH_ISSUE_COMMENT_CREATE_PATH_TEMPLATE,
|
|
42
|
+
GH_PR_OBJECT_PATH_TEMPLATE,
|
|
43
|
+
GH_REQUESTED_REVIEWERS_FIELD_TEMPLATE,
|
|
44
|
+
GH_REVIEW_COMMENTS_PATH_TEMPLATE,
|
|
45
|
+
GH_REVIEWS_PATH_TEMPLATE,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
BUGBOT_RUN_TEMPFILE_SUFFIX: str = ".md"
|
|
49
|
+
|
|
50
|
+
BUGBOT_RUN_TEMPFILE_PREFIX: str = "pr-converge-bugbot-run-"
|
|
51
|
+
|
|
52
|
+
PR_CONTEXT_FIELDS: str = "number,url,headRefOid,baseRefName,headRefName,isDraft"
|
|
53
|
+
|
|
54
|
+
PR_DETACHED_HEAD_ARGS_ERROR: str = "--owner and --repo require --number; all three must be provided together for detached-HEAD PR resolution"
|
|
55
|
+
|
|
56
|
+
PR_NUMBER_ARG_FLAG: str = "--number"
|
|
57
|
+
|
|
58
|
+
PR_NUMBER_ARG_HELP: str = "PR number"
|
|
59
|
+
|
|
60
|
+
PR_OWNER_ARG_FLAG: str = "--owner"
|
|
61
|
+
|
|
62
|
+
PR_OWNER_ARG_HELP: str = "GitHub repository owner"
|
|
63
|
+
|
|
64
|
+
PR_REPO_ARG_FLAG: str = "--repo"
|
|
65
|
+
|
|
66
|
+
PR_REPO_ARG_HELP: str = "GitHub repository name"
|
|
67
|
+
|
|
68
|
+
GH_REPO_FLAG: str = "--repo"
|
|
69
|
+
|
|
70
|
+
MERGEABILITY_FIELDS: str = "mergeable,mergeStateStatus,headRefOid"
|
|
71
|
+
|
|
72
|
+
GH_FIELD_BODY_AT_PREFIX: str = "body=@"
|
|
73
|
+
|
|
74
|
+
GH_REPO_ARG_TEMPLATE: str = "{owner}/{repo}"
|
|
75
|
+
|
|
76
|
+
SKILL_REFLOW_MAXIMUM_WIDTH: int = 80
|
|
77
|
+
|
|
78
|
+
PR_CONVERGE_SKILL_PATH: Path = Path(__file__).resolve().parent.parent.parent / "SKILL.md"
|
|
79
|
+
|
|
80
|
+
MARKDOWN_CODE_FENCE_MARKER: str = "```"
|
|
81
|
+
|
|
82
|
+
YAML_FRONT_MATTER_DELIMITER: str = "---"
|
|
83
|
+
|
|
84
|
+
YAML_DESCRIPTION_PREFIX: str = "description: >-"
|
|
85
|
+
|
|
86
|
+
EXAMPLE_OPEN_TAG: str = "<example>"
|
|
87
|
+
|
|
88
|
+
EXAMPLE_CLOSE_TAG: str = "</example>"
|
|
89
|
+
|
|
90
|
+
BASH_FENCE_LANGUAGE: str = "bash"
|
|
91
|
+
|
|
92
|
+
BASH_LINE_CONTINUATION_SUFFIX: str = " \\"
|
|
93
|
+
|
|
94
|
+
BASH_CONTINUATION_INDENT: str = " "
|
|
95
|
+
|
|
96
|
+
REFLOW_FRONT_MATTER_ERROR: str = "expected YAML front matter starting with ---"
|
|
97
|
+
|
|
98
|
+
ORDERED_MARKDOWN_LIST_PATTERN: re.Pattern[str] = re.compile(
|
|
99
|
+
r"^(?P<leading_whitespace>\s*)(?P<marker>\d+\.\s)(?P<body>.*)$"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
BULLET_MARKDOWN_LIST_PATTERN: re.Pattern[str] = re.compile(
|
|
103
|
+
r"^(?P<leading_whitespace>\s*)(?P<marker>[-*]\s)(?P<body>.*)$"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
UNFINISHED_MARKDOWN_LINK_TARGET_PATTERN: re.Pattern[str] = re.compile(r"\]\([^)]*$")
|
|
107
|
+
|
|
108
|
+
MARKDOWN_HEADING_PATTERN: re.Pattern[str] = re.compile(r"^#{1,6}\s+.+$")
|
|
109
|
+
|
|
110
|
+
MARKDOWN_REFERENCE_DEFINITION_PATTERN: re.Pattern[str] = re.compile(r"^\[[^\]]+\]:\s+\S+")
|
|
111
|
+
|
|
112
|
+
BASH_LINE_CONTINUATION_MARKER_WIDTH: int = 2
|
|
113
|
+
|
|
114
|
+
CODE_FENCE_MARKER_LENGTH: int = 3
|
|
115
|
+
|
|
116
|
+
BASH_MINIMUM_SEGMENT_WIDTH: int = 1
|
|
117
|
+
|
|
118
|
+
LONG_ROW_PREVIEW_LIMIT: int = 20
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Fetch GitHub Copilot reviews for a pull request, newest first.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
python scripts/fetch_copilot_reviews.py --owner <O> --repo <R> --pr-number <N>
|
|
5
|
+
|
|
6
|
+
Output: JSON array of Copilot reviews to stdout, each with id, state, body,
|
|
7
|
+
commit_id, submitted_at, and html_url.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
_pr_converge_dir = Path(__file__).resolve().parent.parent
|
|
19
|
+
if str(_pr_converge_dir) not in sys.path:
|
|
20
|
+
sys.path.insert(0, str(_pr_converge_dir))
|
|
21
|
+
|
|
22
|
+
from config.constants import (
|
|
23
|
+
COPILOT_LOGIN_FILTER_SUBSTRING,
|
|
24
|
+
GH_REVIEWS_PATH_TEMPLATE,
|
|
25
|
+
REVIEWS_PER_PAGE,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def fetch_copilot_reviews(
|
|
30
|
+
*, owner: str, repo: str, number: int
|
|
31
|
+
) -> list[dict[str, object]]:
|
|
32
|
+
"""Return Copilot reviews for a PR, newest first.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
owner: GitHub repository owner.
|
|
36
|
+
repo: GitHub repository name.
|
|
37
|
+
number: Pull request number.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
List of Copilot review entries sorted by submitted_at descending.
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
SystemExit: When the gh CLI call fails.
|
|
44
|
+
"""
|
|
45
|
+
endpoint_path = GH_REVIEWS_PATH_TEMPLATE.format(
|
|
46
|
+
owner=owner, repo=repo, number=number
|
|
47
|
+
)
|
|
48
|
+
completed_process = subprocess.run(
|
|
49
|
+
[
|
|
50
|
+
"gh", "api",
|
|
51
|
+
f"{endpoint_path}?per_page={REVIEWS_PER_PAGE}",
|
|
52
|
+
"--paginate", "--slurp",
|
|
53
|
+
],
|
|
54
|
+
capture_output=True,
|
|
55
|
+
text=True,
|
|
56
|
+
encoding="utf-8",
|
|
57
|
+
errors="replace",
|
|
58
|
+
check=False,
|
|
59
|
+
)
|
|
60
|
+
if completed_process.returncode != 0:
|
|
61
|
+
print(f"gh api error: {completed_process.stderr}", file=sys.stderr)
|
|
62
|
+
raise SystemExit(1)
|
|
63
|
+
raw_output: object = json.loads(completed_process.stdout)
|
|
64
|
+
if not isinstance(raw_output, list):
|
|
65
|
+
return []
|
|
66
|
+
all_pages: list[list[dict[str, object]]] = [
|
|
67
|
+
each_page for each_page in raw_output if isinstance(each_page, list)
|
|
68
|
+
]
|
|
69
|
+
all_flat: list[dict[str, object]] = [
|
|
70
|
+
each_item for each_page in all_pages for each_item in each_page
|
|
71
|
+
]
|
|
72
|
+
copilot_reviews: list[dict[str, object]] = []
|
|
73
|
+
for each_review in all_flat:
|
|
74
|
+
user_object: object = each_review.get("user")
|
|
75
|
+
if not isinstance(user_object, dict):
|
|
76
|
+
continue
|
|
77
|
+
raw_login: object = user_object.get("login")
|
|
78
|
+
if not isinstance(raw_login, str):
|
|
79
|
+
continue
|
|
80
|
+
if COPILOT_LOGIN_FILTER_SUBSTRING not in raw_login.lower():
|
|
81
|
+
continue
|
|
82
|
+
copilot_reviews.append({
|
|
83
|
+
"id": each_review.get("id"),
|
|
84
|
+
"state": each_review.get("state"),
|
|
85
|
+
"body": each_review.get("body"),
|
|
86
|
+
"commit_id": each_review.get("commit_id"),
|
|
87
|
+
"submitted_at": each_review.get("submitted_at"),
|
|
88
|
+
"html_url": each_review.get("html_url"),
|
|
89
|
+
})
|
|
90
|
+
copilot_reviews.sort(
|
|
91
|
+
key=lambda each_review: str(each_review.get("submitted_at", "")),
|
|
92
|
+
reverse=True,
|
|
93
|
+
)
|
|
94
|
+
return copilot_reviews
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def parse_arguments(all_argv: list[str]) -> argparse.Namespace:
|
|
98
|
+
"""Parse command-line arguments.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
all_argv: Command-line argument list.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Parsed namespace with owner, repo, and number.
|
|
105
|
+
"""
|
|
106
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
107
|
+
parser.add_argument("--owner", required=True, help="GitHub repository owner")
|
|
108
|
+
parser.add_argument("--repo", required=True, help="GitHub repository name")
|
|
109
|
+
parser.add_argument("--pr-number", required=True, type=int, help="Pull request number")
|
|
110
|
+
return parser.parse_args(all_argv)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def main(all_arguments: list[str]) -> int:
|
|
114
|
+
"""Entry point for fetch_copilot_reviews.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
all_arguments: Command-line arguments.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
0 on success, 1 on error.
|
|
121
|
+
"""
|
|
122
|
+
arguments = parse_arguments(all_arguments)
|
|
123
|
+
all_reviews = fetch_copilot_reviews(
|
|
124
|
+
owner=arguments.owner,
|
|
125
|
+
repo=arguments.repo,
|
|
126
|
+
number=getattr(arguments, "pr_number"),
|
|
127
|
+
)
|
|
128
|
+
json.dump(all_reviews, sys.stdout)
|
|
129
|
+
sys.stdout.write("\n")
|
|
130
|
+
return 0
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Post a fix reply to a pull request comment thread or as a general PR comment.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
# Reply to an inline comment thread
|
|
5
|
+
python scripts/post_fix_reply.py --owner <O> --repo <R> --pr-number <N> \
|
|
6
|
+
--body "Fixed in <sha>" --in-reply-to <COMMENT_ID>
|
|
7
|
+
|
|
8
|
+
python scripts/post_fix_reply.py --owner <O> --repo <R> --pr-number <N> \
|
|
9
|
+
--body "Your reply text"
|
|
10
|
+
|
|
11
|
+
Exit codes:
|
|
12
|
+
0 — reply posted successfully
|
|
13
|
+
2 — gh CLI error
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
_pr_converge_dir = Path(__file__).resolve().parent.parent
|
|
24
|
+
if str(_pr_converge_dir) not in sys.path:
|
|
25
|
+
sys.path.insert(0, str(_pr_converge_dir))
|
|
26
|
+
|
|
27
|
+
from config.constants import (
|
|
28
|
+
EXIT_CODE_GH_ERROR,
|
|
29
|
+
GH_INLINE_COMMENT_REPLY_PATH_TEMPLATE,
|
|
30
|
+
GH_ISSUE_COMMENT_CREATE_PATH_TEMPLATE,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def post_inline_reply(
|
|
35
|
+
*,
|
|
36
|
+
owner: str,
|
|
37
|
+
repo: str,
|
|
38
|
+
number: int,
|
|
39
|
+
body: str,
|
|
40
|
+
in_reply_to: int,
|
|
41
|
+
) -> int:
|
|
42
|
+
"""Post a reply to an inline pull request comment thread.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
owner: GitHub repository owner.
|
|
46
|
+
repo: GitHub repository name.
|
|
47
|
+
number: Pull request number.
|
|
48
|
+
body: Reply body text.
|
|
49
|
+
in_reply_to: The comment ID to reply to.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
0 on success, EXIT_CODE_GH_ERROR on failure.
|
|
53
|
+
"""
|
|
54
|
+
endpoint_path = GH_INLINE_COMMENT_REPLY_PATH_TEMPLATE.format(
|
|
55
|
+
owner=owner, repo=repo, number=number, comment_id=in_reply_to
|
|
56
|
+
)
|
|
57
|
+
completed_process = subprocess.run(
|
|
58
|
+
[
|
|
59
|
+
"gh",
|
|
60
|
+
"api",
|
|
61
|
+
endpoint_path,
|
|
62
|
+
"-f",
|
|
63
|
+
f"body={body}",
|
|
64
|
+
],
|
|
65
|
+
capture_output=True,
|
|
66
|
+
text=True,
|
|
67
|
+
encoding="utf-8",
|
|
68
|
+
errors="replace",
|
|
69
|
+
check=False,
|
|
70
|
+
)
|
|
71
|
+
if completed_process.returncode != 0:
|
|
72
|
+
print(f"gh api error: {completed_process.stderr}", file=sys.stderr)
|
|
73
|
+
return EXIT_CODE_GH_ERROR
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def post_pr_comment(
|
|
78
|
+
*,
|
|
79
|
+
owner: str,
|
|
80
|
+
repo: str,
|
|
81
|
+
number: int,
|
|
82
|
+
body: str,
|
|
83
|
+
) -> int:
|
|
84
|
+
"""Post a general PR comment.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
owner: GitHub repository owner.
|
|
88
|
+
repo: GitHub repository name.
|
|
89
|
+
number: Pull request number (issue_number for PR comments).
|
|
90
|
+
body: Comment body text.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
0 on success, EXIT_CODE_GH_ERROR on failure.
|
|
94
|
+
"""
|
|
95
|
+
endpoint_path = GH_ISSUE_COMMENT_CREATE_PATH_TEMPLATE.format(
|
|
96
|
+
owner=owner, repo=repo, number=number
|
|
97
|
+
)
|
|
98
|
+
completed_process = subprocess.run(
|
|
99
|
+
[
|
|
100
|
+
"gh",
|
|
101
|
+
"api",
|
|
102
|
+
endpoint_path,
|
|
103
|
+
"-f",
|
|
104
|
+
f"body={body}",
|
|
105
|
+
],
|
|
106
|
+
capture_output=True,
|
|
107
|
+
text=True,
|
|
108
|
+
encoding="utf-8",
|
|
109
|
+
errors="replace",
|
|
110
|
+
check=False,
|
|
111
|
+
)
|
|
112
|
+
if completed_process.returncode != 0:
|
|
113
|
+
print(f"gh api error: {completed_process.stderr}", file=sys.stderr)
|
|
114
|
+
return EXIT_CODE_GH_ERROR
|
|
115
|
+
return 0
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def parse_arguments(all_argv: list[str]) -> argparse.Namespace:
|
|
119
|
+
"""Parse command-line arguments.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
all_argv: Command-line argument list.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Parsed namespace with owner, repo, number, body, and in_reply_to.
|
|
126
|
+
"""
|
|
127
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
128
|
+
parser.add_argument("--owner", required=True, help="GitHub repository owner")
|
|
129
|
+
parser.add_argument("--repo", required=True, help="GitHub repository name")
|
|
130
|
+
parser.add_argument("--pr-number", required=True, type=int, help="Pull request number")
|
|
131
|
+
parser.add_argument("--body", required=True, help="Reply body text")
|
|
132
|
+
parser.add_argument(
|
|
133
|
+
"--in-reply-to",
|
|
134
|
+
type=int,
|
|
135
|
+
default=None,
|
|
136
|
+
help="Comment ID to reply to (omit for general PR comment)",
|
|
137
|
+
)
|
|
138
|
+
return parser.parse_args(all_argv)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def main(all_arguments: list[str]) -> int:
|
|
142
|
+
"""Entry point for post_fix_reply.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
all_arguments: Command-line arguments.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
0 on success, EXIT_CODE_GH_ERROR on failure.
|
|
149
|
+
"""
|
|
150
|
+
arguments = parse_arguments(all_arguments)
|
|
151
|
+
if arguments.in_reply_to is not None:
|
|
152
|
+
return post_inline_reply(
|
|
153
|
+
owner=arguments.owner,
|
|
154
|
+
repo=arguments.repo,
|
|
155
|
+
number=getattr(arguments, "pr_number"),
|
|
156
|
+
body=arguments.body,
|
|
157
|
+
in_reply_to=arguments.in_reply_to,
|
|
158
|
+
)
|
|
159
|
+
return post_pr_comment(
|
|
160
|
+
owner=arguments.owner,
|
|
161
|
+
repo=arguments.repo,
|
|
162
|
+
number=getattr(arguments, "pr_number"),
|
|
163
|
+
body=arguments.body,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -4,21 +4,14 @@ Load this document for converge **loop pacing**. The pre-flight in `SKILL.md`
|
|
|
4
4
|
guarantees `ScheduleWakeup` is available before any tick runs. Shared bugbot
|
|
5
5
|
/ bugteam / Fix protocol steps stay in the main `SKILL.md`.
|
|
6
6
|
|
|
7
|
-
## Session behavior
|
|
8
|
-
|
|
9
|
-
Call `ScheduleWakeup` from this same session so the next tick fires back into **this** transcript with the prior tick's state line and PR context still addressable.
|
|
10
|
-
|
|
11
7
|
## Calling ScheduleWakeup
|
|
12
8
|
|
|
13
|
-
At end of tick
|
|
14
|
-
|
|
9
|
+
At end of every tick — across all phases (BUGBOT, BUGTEAM, COPILOT_WAIT)
|
|
10
|
+
without distinction — call `ScheduleWakeup` unless convergence or another
|
|
11
|
+
stop condition already omitted pacing:
|
|
15
12
|
|
|
16
|
-
- `delaySeconds:
|
|
17
|
-
|
|
18
|
-
mandatory re-trigger, or by BUGTEAM's same-tick re-trigger). Bugbot
|
|
19
|
-
finishes a review in 1–4 minutes, so 270s stays under the 5-minute
|
|
20
|
-
prompt-cache TTL with margin past bugbot's typical upper bound. The
|
|
21
|
-
exception is the BUGBOT inline-lag branch (see below).
|
|
13
|
+
- `delaySeconds: 360` — default wakeup interval. Keeps the loop advancing
|
|
14
|
+
through all phases. Exception: BUGBOT inline-lag branch (see below).
|
|
22
15
|
- `reason`: one short sentence on what is being awaited, including the
|
|
23
16
|
current `phase` and `bugbot_clean_at` SHA when set.
|
|
24
17
|
- `prompt: "/pr-converge"` — re-enters this skill on the next firing.
|
package/skills/qbug/SKILL.md
CHANGED
|
@@ -23,7 +23,7 @@ Shared artifacts with /bugteam are referenced below by path, using the `${CLAUDE
|
|
|
23
23
|
- Code-rules gate script: `${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/code_rules_gate.py`
|
|
24
24
|
- Bug category rubric A–J: [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md#audit-spawn-prompt-xml-bugfind-teammate)
|
|
25
25
|
- **Audit contract** (finding schema, proof-of-absence, adversarial pass, Haiku secondary, post-fix self-audit, diagnostics JSON): [`bugteam/reference/audit-contract.md`](../bugteam/reference/audit-contract.md)
|
|
26
|
-
- PR comment lifecycle shape: [`bugteam/SKILL.md`](../bugteam/SKILL.md#
|
|
26
|
+
- PR comment lifecycle shape: [`bugteam/SKILL.md`](../bugteam/SKILL.md#audit-posting)
|
|
27
27
|
|
|
28
28
|
## When this skill applies
|
|
29
29
|
|
|
@@ -198,17 +198,78 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
198
198
|
<qbug_temp_dir>/loop-<loop_count>-audit.json per the contract's
|
|
199
199
|
persistence schema.
|
|
200
200
|
|
|
201
|
-
Post ONE review per loop
|
|
202
|
-
|
|
203
|
-
the
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
`
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
201
|
+
Post ONE review per loop via `post_audit_thread.py`. Before
|
|
202
|
+
serializing, partition the merged findings into anchored (line
|
|
203
|
+
appears in the captured diff) and unanchored (line is not in the
|
|
204
|
+
diff) buckets. Only anchored findings are written to
|
|
205
|
+
`<qbug_temp_dir>/loop-<loop_count>-findings.json` — the GitHub
|
|
206
|
+
reviews API rejects the entire POST if any inline comment in
|
|
207
|
+
`comments[]` targets a line not in the diff at `--commit`, so a
|
|
208
|
+
single unanchored entry breaks the whole review. Unanchored
|
|
209
|
+
findings surface in the loop's user-facing summary rather than
|
|
210
|
+
as inline anchored comments. The JSON root is a list of objects
|
|
211
|
+
shaped `{path, line, side, severity, description, fix_summary}`.
|
|
212
|
+
For each anchored finding, map `file` → `path`; split the
|
|
213
|
+
finding's `failure_mode` at the literal `Fix:` heading so the
|
|
214
|
+
failure narrative becomes `description` and the suffix beginning
|
|
215
|
+
at `Fix:` (including the trailing `Validation:` clause) becomes
|
|
216
|
+
`fix_summary` (the `failure_mode` field carries the full
|
|
217
|
+
audit-to-fix handoff per
|
|
218
|
+
[`agents/code-quality-agent.md`](../../agents/code-quality-agent.md)).
|
|
219
|
+
When a finding's `failure_mode` omits the `Fix:` heading, write
|
|
220
|
+
the full text to BOTH `description` and `fix_summary`. Set
|
|
221
|
+
`side="RIGHT"` for every entry. Zero anchored findings → write
|
|
222
|
+
`[]` and pass `--state CLEAN`; one or more anchored findings →
|
|
223
|
+
pass `--state DIRTY` with the full list.
|
|
224
|
+
|
|
225
|
+
**Self-PR precondition.** GitHub rejects both `APPROVE` and
|
|
226
|
+
`REQUEST_CHANGES` reviews when the authenticated identity matches
|
|
227
|
+
the PR author with HTTP 422; `post_audit_thread.py` retries and
|
|
228
|
+
then exits 2. To run qbug on a PR you authored, switch `gh auth`
|
|
229
|
+
to an alternate reviewer identity (a separate GitHub account)
|
|
230
|
+
BEFORE invoking the skill. Without this switch, exit 2 is a hard
|
|
231
|
+
halt — there is no automated fallback path. The script does not
|
|
232
|
+
auto-downgrade on the self-PR case.
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/post_audit_thread.py" \
|
|
236
|
+
--skill qbug \
|
|
237
|
+
--owner <owner> \
|
|
238
|
+
--repo <repo> \
|
|
239
|
+
--pr-number <pr_number> \
|
|
240
|
+
--commit <head_sha> \
|
|
241
|
+
--state <CLEAN|DIRTY> \
|
|
242
|
+
--findings-json <qbug_temp_dir>/loop-<loop_count>-findings.json
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The script POSTs a single review with `event=APPROVE` on CLEAN
|
|
246
|
+
(the request event; GitHub stores it as `state=APPROVED`; empty
|
|
247
|
+
`comments[]`, body documents "no findings") or
|
|
248
|
+
`event=REQUEST_CHANGES` on DIRTY (one inline anchored comment per
|
|
249
|
+
finding; each becomes its own resolvable thread on the PR). It
|
|
250
|
+
handles retries internally (1s / 4s / 16s backoff across four
|
|
251
|
+
attempts). Exit 0 emits the new review's `html_url` to stdout;
|
|
252
|
+
extract the numeric review id from the URL's
|
|
253
|
+
`#pullrequestreview-<id>` suffix (the trailing URL fragment of
|
|
254
|
+
`html_url`, the part after `#`). Then harvest child-comment URLs **and PR review
|
|
255
|
+
thread node ids** via
|
|
256
|
+
`pull_request_read(method="get_review_comments",
|
|
257
|
+
owner=<owner>, repo=<repo>, pullNumber=<pr_number>)` filtered to
|
|
258
|
+
that review id. The same response carries each
|
|
259
|
+
comment's PR review thread node id (e.g. `PRRT_kwDOxxx`) — match
|
|
260
|
+
children to findings in the order they appear in the findings
|
|
261
|
+
JSON. Each `loop_comment_index[finding_id]` entry must carry
|
|
262
|
+
`finding_comment_id` (numeric, used by
|
|
263
|
+
`add_reply_to_pull_request_comment`), `finding_comment_url`, and
|
|
264
|
+
`thread_node_id` (`PRRT_kwDOxxx`, used by `resolve_thread`) so
|
|
265
|
+
the FIX step can reply against the comment and resolve the
|
|
266
|
+
thread.
|
|
267
|
+
|
|
268
|
+
Exit 2 means retry exhaustion — exit
|
|
269
|
+
`error: post_audit_thread retry exhausted` without retrying and
|
|
270
|
+
without falling back to a flat issue comment. A hard blocker on
|
|
271
|
+
the audit-posting path is a halt condition, not a fallback
|
|
272
|
+
opportunity.
|
|
212
273
|
|
|
213
274
|
Update state: last_action="audited", last_findings=counts.
|
|
214
275
|
Append `<N> audit: <P0>P0 / <P1>P1 / <P2>P2` to audit_log.
|
|
@@ -245,12 +306,54 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
245
306
|
Write <qbug_temp_dir>/loop-<loop_count>-diagnostics.json per the
|
|
246
307
|
contract's diagnostics schema (all eight keys required).
|
|
247
308
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
309
|
+
For each finding, atomically (a) post the fix reply and
|
|
310
|
+
(b) call `resolve_thread`. The two calls form one logical action
|
|
311
|
+
per thread — do not yield to the lead between them, and do not
|
|
312
|
+
batch all replies before any resolves.
|
|
313
|
+
|
|
314
|
+
(a) Reply via
|
|
315
|
+
`add_reply_to_pull_request_comment(commentId=<loop_comment_index[finding_id].finding_comment_id>,
|
|
316
|
+
body=<reply_body>, owner=<owner>, repo=<repo>,
|
|
317
|
+
pullNumber=<pr_number>)`. The reply body uses the unified template
|
|
318
|
+
at [`../../_shared/pr-loop/audit-reply-template.md`](../../_shared/pr-loop/audit-reply-template.md).
|
|
319
|
+
Skeleton (identical across all paths):
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
**Claude finished @<reviewer>'s task** —— <status_line>
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
### <action_heading> ✅
|
|
326
|
+
|
|
327
|
+
<1–2 paragraph plain-language explanation>
|
|
328
|
+
|
|
329
|
+
**`<file>:<line>`:**
|
|
330
|
+
- <bullet describing change or rationale>
|
|
331
|
+
- <bullet describing change or rationale>
|
|
332
|
+
|
|
333
|
+
<closing paragraph>
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Per-path `<status_line>` / `<action_heading>`:
|
|
337
|
+
- `status=fixed`: `Fixed in <short_sha>` (first 7 chars) /
|
|
338
|
+
finding-specific action verb (e.g.,
|
|
339
|
+
`Replaced Any with concrete type`).
|
|
340
|
+
- `status=could_not_address`: `Could not address this loop` /
|
|
341
|
+
one-line reason text.
|
|
342
|
+
- `status=hook_blocked`: `Hook blocked the fix commit` /
|
|
343
|
+
one-line hook summary.
|
|
344
|
+
|
|
345
|
+
Body text is passed directly as string parameters — no temp
|
|
346
|
+
files, no jq, no shell pipes.
|
|
347
|
+
|
|
348
|
+
(b) Immediately call
|
|
349
|
+
`pull_request_review_write(method="resolve_thread",
|
|
350
|
+
threadId=<loop_comment_index[finding_id].thread_node_id>,
|
|
351
|
+
owner=<owner>, repo=<repo>, pullNumber=<pr_number>)` for the
|
|
352
|
+
same thread (this is the PR review thread node ID —
|
|
353
|
+
`PRRT_kwDOxxx` — distinct from the numeric comment ID; the
|
|
354
|
+
AUDIT step captures it at audit time when calling
|
|
355
|
+
`get_review_comments` and stores it on each
|
|
356
|
+
`loop_comment_index` entry alongside `finding_comment_id`).
|
|
254
357
|
|
|
255
358
|
Update state: last_action="fixed". Append
|
|
256
359
|
`<N> fix: <short_sha> — <fixed>/<could_not_address>/<hook_blocked>`
|
|
@@ -260,14 +363,16 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
260
363
|
</cycle>
|
|
261
364
|
|
|
262
365
|
<example_finding_body>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
366
|
+
Populate these two fields on each finding entry of the JSON payload
|
|
367
|
+
consumed by `post_audit_thread.py` (the script renders the inline
|
|
368
|
+
comment body via `INLINE_COMMENT_BODY_TEMPLATE`):
|
|
369
|
+
|
|
370
|
+
```json
|
|
371
|
+
{
|
|
372
|
+
"description": "Two writers can both pass the existence check at line 88 before either commits the write at line 91 — whichever writes second overwrites the first under contention.",
|
|
373
|
+
"fix_summary": "Hold the cache lock across the check and the write, or use a compare-and-swap primitive. Validation: pytest -k cache_concurrent with the threaded-writer fixture."
|
|
374
|
+
}
|
|
375
|
+
```
|
|
271
376
|
</example_finding_body>
|
|
272
377
|
|
|
273
378
|
<constraints>
|
|
@@ -315,7 +420,7 @@ Delete the resolved `<qbug_temp_dir>` tree and any `.qbug-*.md` temp files in th
|
|
|
315
420
|
- **Code rules gate before every AUDIT.** Same `validate_content` logic as /bugteam.
|
|
316
421
|
- **One commit per FIX action.** Linear branch, fast-forward push only.
|
|
317
422
|
- **Categories A–J.** Same rubric as [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md).
|
|
318
|
-
- **One review per loop.** Anchored findings as `comments[]`; unanchored
|
|
423
|
+
- **One review per loop.** Anchored findings as `comments[]`; unanchored findings surface in the calling skill's user-facing output (chat reply to the user) rather than in the PR review body.
|
|
319
424
|
- **PR description rewrite on every exit**, same as /bugteam Step 4.5.
|
|
320
425
|
- **Temp file cleanup on every exit path.**
|
|
321
426
|
- **No per-loop clean-room.** The single subagent's context accumulates across loops — that is the explicit trade vs /bugteam. For convergence-critical audits where bias isolation matters, use /bugteam.
|