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,13 @@
|
|
|
1
|
+
# Re-trigger bugbot
|
|
2
|
+
|
|
3
|
+
Run Step 3 bugbot re-trigger in same tick after push
|
|
4
|
+
|
|
5
|
+
## Self-population
|
|
6
|
+
|
|
7
|
+
Hit a wall on this step? Spawn a background agent to record it. Keep working.
|
|
8
|
+
|
|
9
|
+
1. Spawn — brief it: check for an open PR touching this file, add a commit to it, or create a fresh branch + worktree + draft PR.
|
|
10
|
+
2. Tell the agent your obstacle and what fixed it. It writes the entry below.
|
|
11
|
+
|
|
12
|
+
## Obstacles
|
|
13
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Write failing test
|
|
2
|
+
|
|
3
|
+
Write failing test first when finding has behavior to test
|
|
4
|
+
|
|
5
|
+
## Self-population
|
|
6
|
+
|
|
7
|
+
Hit a wall on this step? Spawn a background agent to record it. Keep working.
|
|
8
|
+
|
|
9
|
+
1. Spawn `general-purpose` — brief it: check for an open PR touching this file, add a commit to it, or create a fresh branch + worktree + draft PR.
|
|
10
|
+
2. Tell the agent your obstacle and what fixed it. It writes the entry below.
|
|
11
|
+
|
|
12
|
+
## Obstacles
|
|
13
|
+
|
|
@@ -23,8 +23,7 @@ protocol per [fix-protocol.md](fix-protocol.md). Pacing stays in main session vi
|
|
|
23
23
|
Read [`../workflows/schedule-wakeup-loop.md`](../workflows/schedule-wakeup-loop.md)
|
|
24
24
|
(installed copy under `$HOME/.claude/skills/pr-converge/workflows/`) before
|
|
25
25
|
Step 4. The pre-flight gate guarantees `ScheduleWakeup` is invokable; the
|
|
26
|
-
workflow file specifies delays, prompts, convergence cleanup
|
|
27
|
-
inline-lag handling.
|
|
26
|
+
workflow file specifies delays, prompts, and convergence cleanup.
|
|
28
27
|
|
|
29
28
|
- **`/pr-converge`** (default): loops until convergence. After each tick
|
|
30
29
|
(unless converged or stopped), run **Step 4**.
|
|
@@ -41,6 +40,8 @@ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get") → `
|
|
|
41
40
|
```
|
|
42
41
|
|
|
43
42
|
If owner/repo/number are not yet known, extract them from the PR URL.
|
|
43
|
+
If `current_head` changed since last tick, reset `bugbot_down` to `false`
|
|
44
|
+
(new HEAD invalidates prior down-detection state).
|
|
44
45
|
|
|
45
46
|
Capture `number`, `head.sha` (= `current_head`), owner/repo, branch.
|
|
46
47
|
|
|
@@ -69,15 +70,19 @@ Capture `commit_id`, `submitted_at`, body, `classification` of index-0
|
|
|
69
70
|
review for decisions below. When branch routes to **Fix protocol**, address
|
|
70
71
|
**every** entry in `$dirty_reviews_path` — not just index 0.
|
|
71
72
|
|
|
72
|
-
b. Fetch
|
|
73
|
-
review on `current_head`:
|
|
73
|
+
b. Fetch ALL unresolved inline comment threads on the PR:
|
|
74
74
|
|
|
75
75
|
```
|
|
76
76
|
pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_review_comments")
|
|
77
|
-
→ filter threads where `
|
|
78
|
-
AND any comment has `.author` matching cursor/bugbot (case-insensitive substring)
|
|
77
|
+
→ filter threads where `is_resolved == false`
|
|
79
78
|
```
|
|
80
79
|
|
|
80
|
+
For each unresolved thread, you still need to know **who** wrote it
|
|
81
|
+
and **what commit** it anchors to so you can decide how to address it
|
|
82
|
+
— but the gate itself doesn't filter on those fields. Verify each
|
|
83
|
+
thread's concern against current HEAD; either fix-and-resolve or
|
|
84
|
+
reply-with-note-and-resolve.
|
|
85
|
+
|
|
81
86
|
c. Decide (four branches; match first whose predicate holds):
|
|
82
87
|
- **No bugbot review yet, OR latest review's `commit_id` ≠
|
|
83
88
|
`current_head`:** Re-trigger bugbot (Step 3), set `bugbot_clean_at =
|
|
@@ -93,15 +98,10 @@ c. Decide (four branches; match first whose predicate holds):
|
|
|
93
98
|
`state.json`, goes idle; Step 3 on new HEAD runs after via
|
|
94
99
|
orchestrator-spawned follow-up agent (§Fix result → general-purpose).
|
|
95
100
|
No `state.json` (single-PR): spawn Agent (subagent_type: clean-coder) to implement → push → reply inline on each thread
|
|
96
|
-
via `
|
|
101
|
+
via `python ~/.claude/skills/pr-converge/scripts/post_fix_reply.py` → Step 3 in same tick (see
|
|
97
102
|
[Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow) for
|
|
98
103
|
full contract).
|
|
99
104
|
Schedule next wakeup, return.
|
|
100
|
-
- **`commit_id == current_head` AND review body findings AND inline
|
|
101
|
-
API zero matching for `current_head`:** Transient API lag. Increment
|
|
102
|
-
`inline_lag_streak`. `>= 3` → hard blocker; report and terminate with
|
|
103
|
-
no loop pacing. Else Step 4 uses the BUGBOT inline-lag section of
|
|
104
|
-
`../workflows/schedule-wakeup-loop.md` (`delaySeconds: 90`).
|
|
105
105
|
|
|
106
106
|
### `phase == BUGTEAM`
|
|
107
107
|
|
|
@@ -119,13 +119,28 @@ Skill({skill: "bugteam", args:
|
|
|
119
119
|
bugteam by reading [`../../bugteam/SKILL.md`](../../bugteam/SKILL.md). Same
|
|
120
120
|
loop and gates; only harness steps differ.
|
|
121
121
|
|
|
122
|
-
b. **Re-resolve current HEAD
|
|
123
|
-
its run. `current_head` from Step 1 is
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
b. **Re-resolve current HEAD (MANDATORY — never skip).** Bugteam may have
|
|
123
|
+
pushed commits during its run. `current_head` from Step 1 is stale:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get") → `.head.sha`
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Capture `new_head`. Then check the most recent commit timestamp:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
list_commits(owner=OWNER, repo=REPO, sha="<branch>")
|
|
133
|
+
→ sort by `.commit.committer.date` descending → index 0 `.commit.committer.date`
|
|
126
134
|
```
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
|
|
136
|
+
If the most recent commit timestamp is **less than 60 seconds ago**, the
|
|
137
|
+
GitHub API may not have propagated it to review endpoints yet. Do not
|
|
138
|
+
proceed with convergence-gates — schedule a 90s wakeup and return.
|
|
139
|
+
Re-resolve HEAD next tick.
|
|
140
|
+
|
|
141
|
+
If `new_head != current_head`, set `current_head = new_head`,
|
|
142
|
+
`bugbot_clean_at = null`, `bugbot_down = false`. New commits invalidate
|
|
143
|
+
bugbot's prior clean and down-detection state.
|
|
129
144
|
|
|
130
145
|
c. Inspect bugteam outcome. Reports `convergence (zero findings)` or list
|
|
131
146
|
of unfixed findings with file:line.
|
|
@@ -137,34 +152,95 @@ never falsely terminates:
|
|
|
137
152
|
Re-trigger bugbot same tick (Step 3) so new HEAD enters queue, `phase
|
|
138
153
|
= BUGBOT`, schedule next wakeup, return.
|
|
139
154
|
- **Convergence AND `bugbot_clean_at == current_head` (no push):**
|
|
140
|
-
Back-to-back clean — necessary, not sufficient. Run **[convergence-gates.md](convergence-gates.md)** to clear Copilot
|
|
141
|
-
|
|
155
|
+
Back-to-back clean — necessary, not sufficient. Run **[convergence-gates.md](convergence-gates.md)** to clear all six gates: Copilot findings,
|
|
156
|
+
Claude reviewer, mergeability, post-convergence Copilot request,
|
|
157
|
+
thread resolution. Only when all six gates pass mark PR ready and
|
|
142
158
|
**omit loop pacing** per **Convergence** of active pacing workflow.
|
|
143
159
|
- **Convergence BUT `bugbot_clean_at != current_head` (no push):**
|
|
144
160
|
`phase = BUGBOT`, schedule next wakeup, return.
|
|
145
|
-
- **Findings without committed fixes:** spawn Agent (subagent_type: clean-coder) to implement fixes and push, then reply inline via `
|
|
161
|
+
- **Findings without committed fixes:** spawn Agent (subagent_type: clean-coder) to implement fixes and push, then reply inline via `python ~/.claude/skills/pr-converge/scripts/post_fix_reply.py`, following [Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow).
|
|
146
162
|
`phase = BUGBOT`, schedule next wakeup, return.
|
|
147
163
|
|
|
148
|
-
|
|
164
|
+
### `phase == COPILOT_WAIT`
|
|
149
165
|
|
|
150
|
-
|
|
166
|
+
Post-convergence Copilot re-check. Enters after gate (d) requests Copilot
|
|
167
|
+
review. Do **not** run bugteam here — that only happens after BUGBOT clean
|
|
168
|
+
on this HEAD.
|
|
151
169
|
|
|
152
|
-
|
|
170
|
+
a. Fetch latest Copilot review at `current_head` plus unaddressed inline
|
|
171
|
+
comments:
|
|
153
172
|
|
|
154
|
-
|
|
155
|
-
|
|
173
|
+
```
|
|
174
|
+
python ~/.claude/skills/pr-converge/scripts/fetch_copilot_reviews.py --owner <O> --repo <R> --pr-number <N>
|
|
175
|
+
→ filter by `.commit_id == current_head`, sort by `.submitted_at` descending
|
|
176
|
+
|
|
177
|
+
python ~/.claude/skills/pr-converge/scripts/fetch_copilot_inline_comments.py --owner <O> --repo <R> --pr-number <N> --commit <current_head>
|
|
178
|
+
→ unaddressed inline threads on the latest Copilot review at current_head
|
|
179
|
+
```
|
|
156
180
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
181
|
+
b. Decide (three branches; match first whose predicate holds):
|
|
182
|
+
|
|
183
|
+
- **Copilot review `state: APPROVED` at `current_head`:** Set
|
|
184
|
+
`copilot_clean_at = current_head`. Record "Copilot APPROVED". Set
|
|
185
|
+
`phase = BUGTEAM`. Continue to convergence-gates.md gate (b) in same
|
|
186
|
+
tick — back-to-back convergence requires all gates on same HEAD.
|
|
187
|
+
- **Copilot review dirty (CHANGES_REQUESTED or COMMENTED with findings)
|
|
188
|
+
at `current_head`:** Apply **Fix protocol** — spawn Agent
|
|
189
|
+
(subagent_type: clean-coder) to implement → push → reply inline on each
|
|
190
|
+
thread via `python ~/.claude/skills/pr-converge/scripts/post_fix_reply.py`. For body-only
|
|
191
|
+
findings (no inline threads), post top-level review reply citing new
|
|
192
|
+
HEAD SHA. Reset
|
|
193
|
+
`bugbot_clean_at = null` AND `copilot_clean_at = null`. **Set
|
|
194
|
+
`phase = BUGBOT`** (NOT COPILOT_WAIT) — every fix push requires a full
|
|
195
|
+
back-to-back-clean cycle on the new HEAD. Schedule next wakeup, return.
|
|
196
|
+
- **No Copilot review at `current_head` yet:** Increment
|
|
197
|
+
`copilot_wait_count` (init 0 on COPILOT_WAIT entry; reset to 0 on
|
|
198
|
+
every push and on every successful Copilot review). `>= 3` → hard
|
|
199
|
+
blocker per [stop-conditions.md](stop-conditions.md). Otherwise
|
|
200
|
+
schedule next wakeup (360s), return.
|
|
201
|
+
|
|
202
|
+
**Non-negotiable:** After any Copilot fix push, `phase` MUST route to
|
|
203
|
+
`BUGBOT`. Never cycle COPILOT_WAIT → fix → COPILOT_WAIT. The
|
|
204
|
+
back-to-back-clean guarantee (bugbot ∧ bugteam both clean on same HEAD
|
|
205
|
+
before gates re-open) only holds when every fix commit re-enters through
|
|
206
|
+
BUGBOT.
|
|
207
|
+
|
|
208
|
+
## Step 3: Re-trigger bugbot
|
|
209
|
+
|
|
210
|
+
- [ ] **Opt-out gate.** When `CLAUDE_REVIEWS_DISABLED` (comma-separated,
|
|
211
|
+
case-insensitive, whitespace-tolerant) contains `bugbot`, set
|
|
212
|
+
`bugbot_down = true`, skip every check below, set `phase = BUGTEAM`,
|
|
213
|
+
and continue BUGTEAM in the same tick. The downstream loop branches on
|
|
214
|
+
`bugbot_down` exactly the way it does when bugbot CI is unavailable.
|
|
215
|
+
- [ ] **Silent-pass pre-check.** Run `python ~/.claude/skills/pr-converge/scripts/check_bugbot_ci.py --check-clean --owner <O> --repo <R> --sha <current_head>`
|
|
216
|
+
- [ ] Exit 0 → bugbot CI completed clean with no review (silent pass); set `bugbot_clean_at = current_head`, `phase = BUGTEAM`, continue BUGTEAM same tick
|
|
217
|
+
- [ ] Exit 1 (not a silent pass) or Exit 2 (gh CLI error — silent pass not confirmable) → continue with the trigger flow below
|
|
218
|
+
- [ ] Run `python ~/.claude/skills/pr-converge/scripts/check_bugbot_ci.py --check-active --owner <O> --repo <R> --sha <current_head>`
|
|
219
|
+
- [ ] Exit 0 → bugbot already queued on this commit; skip posting, wait for completion
|
|
220
|
+
- [ ] Exit 1 → post trigger via `add_issue_comment(owner="OWNER", repo="REPO", issueNumber=NUMBER, body="bugbot run")`
|
|
221
|
+
- [ ] Wait 8s
|
|
222
|
+
- [ ] Run `python ~/.claude/skills/pr-converge/scripts/check_bugbot_ci.py --owner <O> --repo <R> --sha <current_head>`
|
|
223
|
+
- [ ] Exit non-zero → bugbot is down; set `bugbot_down = true`, `phase = BUGTEAM`, continue BUGTEAM same tick
|
|
224
|
+
- [ ] Exit 0 (check run present) → record `bugbot_acknowledged_at = <now ISO 8601>`, proceed to Step 4
|
|
225
|
+
|
|
226
|
+
The silent-pass pre-check fires FIRST so we never re-trigger a bot that
|
|
227
|
+
already finished cleanly. Cursor Bugbot communicates "no findings" by
|
|
228
|
+
completing the CI check with `conclusion: success` (or `neutral`) and
|
|
229
|
+
posting no review. The pre-check treats that outcome as
|
|
230
|
+
`bugbot_clean_at = current_head`, equivalent to an explicit clean
|
|
231
|
+
review. Without it, the trigger flow would re-prompt a bot that has
|
|
232
|
+
already evaluated this commit and refuses to re-run, and the bypass
|
|
233
|
+
branch would falsely mark `bugbot_down = true`.
|
|
234
|
+
|
|
235
|
+
`bugbot run` is empirically the only re-trigger Cursor Bugbot recognizes;
|
|
236
|
+
alternative phrasings silently no-op.
|
|
160
237
|
|
|
161
238
|
## Step 4: Loop pacing
|
|
162
239
|
|
|
163
240
|
**`ScheduleWakeup` field hints** (prefer [Pacing
|
|
164
241
|
workflow](#pacing-workflow)):
|
|
165
242
|
|
|
166
|
-
- `delaySeconds:
|
|
167
|
-
min; 270s stays under 5-min prompt-cache TTL with margin. Exception:
|
|
243
|
+
- `delaySeconds: 360` after bugbot re-trigger. Exception:
|
|
168
244
|
BUGBOT inline-lag branch uses `delaySeconds: 90` (no re-trigger;
|
|
169
245
|
awaiting GitHub inline API).
|
|
170
246
|
- `reason`: short sentence on what is awaited, including `phase` and
|
|
@@ -6,12 +6,33 @@ counters, status, not conversation transcript. No `state.json` (typical
|
|
|
6
6
|
single-PR `/pr-converge`) → track in each assistant turn as
|
|
7
7
|
plain text so next tick re-reads from context:
|
|
8
8
|
|
|
9
|
-
- `phase`: `BUGBOT` or `
|
|
9
|
+
- `phase`: `BUGBOT`, `BUGTEAM`, or `COPILOT_WAIT`. Start `BUGBOT` on first tick.
|
|
10
10
|
- `bugbot_clean_at`: HEAD SHA where bugbot last reported clean, or `null`.
|
|
11
11
|
Reset to `null` on every push.
|
|
12
|
+
- `copilot_clean_at`: HEAD SHA where Copilot last reported clean, or `null`.
|
|
13
|
+
Reset to `null` on every push.
|
|
14
|
+
- `copilot_wait_count`: integer, init `0`. Consecutive COPILOT_WAIT ticks
|
|
15
|
+
with no Copilot review surfaced at `current_head`. Escalate as hard blocker
|
|
16
|
+
at `>= 3`. Reset to `0` when a Copilot review surfaces at `current_head`
|
|
17
|
+
(APPROVED or dirty) or on any non-COPILOT_WAIT branch.
|
|
12
18
|
- `inline_lag_streak`: integer, init `0`. Consecutive ticks where review
|
|
13
19
|
body shows findings against `current_head` but inline API returns zero
|
|
14
20
|
matching. Reset to `0` on any other branch outcome.
|
|
21
|
+
- `bugbot_down`: boolean, init `false`. Set `true` when bugbot fails to
|
|
22
|
+
acknowledge a trigger comment; forces phase to BUGTEAM. Also set `true`
|
|
23
|
+
when an acknowledged trigger has been outstanding more than 30 minutes
|
|
24
|
+
with no surfaced review at `current_head` (per Step 2 BUGBOT (c)
|
|
25
|
+
30-minute budget — see `per-tick.md`). Reset to `false` on every push.
|
|
26
|
+
Once set, remains `true` until the next push; if bugbot stays down
|
|
27
|
+
across ticks, the flag persists and BUGTEAM continues.
|
|
28
|
+
- `bugbot_acknowledged_at`: ISO 8601 timestamp string or `null`. Records
|
|
29
|
+
the wall-clock moment Cursor Bugbot acknowledged the most recent
|
|
30
|
+
`bugbot run` trigger comment (i.e. the trigger comment carries an
|
|
31
|
+
`:eyes:`/`:+1:` reaction). Init `null`. Set in Step 3 once the
|
|
32
|
+
reaction-check fires positive. Reset to `null` on every push and on
|
|
33
|
+
every BUGTEAM jump triggered by the 30-minute budget. Step 2 BUGBOT
|
|
34
|
+
(c) reads this field to decide between "schedule next wakeup" and
|
|
35
|
+
"escalate to bugbot-down".
|
|
15
36
|
- `tick_count`: integer, init `0`. Increment every tick.
|
|
16
37
|
|
|
17
38
|
Tick begins reading prior state line from most recent assistant message
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Stop conditions
|
|
2
2
|
|
|
3
|
-
- **Convergence** (back-to-back clean ∧
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
`
|
|
3
|
+
- **Convergence** (back-to-back clean ∧ all six gates pass per
|
|
4
|
+
[convergence-gates.md](convergence-gates.md) gate (f) checklist:
|
|
5
|
+
Copilot clean at `current_head`, Claude clean or absent,
|
|
6
|
+
`mergeable_state == "clean"` with `mergeable == true`,
|
|
7
|
+
post-convergence Copilot review `APPROVED`, zero unresolved bot
|
|
8
|
+
threads): use `update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)`. With
|
|
7
9
|
`state.json`, append convergence row to
|
|
8
10
|
`<TMPDIR>/pr-converge-<session_id>/converged.log` per `multi-pr-orchestration.md` §Memory; else
|
|
9
|
-
skip. Report [convergence-gates.md](convergence-gates.md) (
|
|
11
|
+
skip. Report [convergence-gates.md](convergence-gates.md) (f) summary, then **omit loop pacing**
|
|
10
12
|
per **Convergence** in `../workflows/schedule-wakeup-loop.md`. End all loops
|
|
11
13
|
once all PRs terminal (converged or blocked).
|
|
12
14
|
- **Hard blocker:** API auth failure across two ticks, CI regression
|
|
@@ -17,8 +19,8 @@
|
|
|
17
19
|
blocker and diagnosis, **omit loop pacing** per
|
|
18
20
|
`../workflows/schedule-wakeup-loop.md`.
|
|
19
21
|
- **Hard blocker (`mergeable_state` non-clean non-dirty):**
|
|
20
|
-
`mergeable_state` is `"blocked"`, `"unknown"`, or `"
|
|
21
|
-
checks pending, branch behind base without textual conflicts, or
|
|
22
|
+
`mergeable_state` is `"blocked"`, `"unknown"`, `"behind"`, or `"unstable"` (required
|
|
23
|
+
checks pending/failing, branch behind base without textual conflicts, or
|
|
22
24
|
GitHub indeterminate). Investigate before retrying; `rebase` skill
|
|
23
25
|
handles `"dirty"` (textual conflicts) only. Report specific
|
|
24
26
|
`mergeable_state`, **omit loop pacing**.
|
|
@@ -20,11 +20,11 @@ Fetch all reviews and filter for `cursor[bot]`:
|
|
|
20
20
|
|
|
21
21
|
Classification: clean if review body has no findings count; dirty if body matches "found N potential issue".
|
|
22
22
|
|
|
23
|
-
###
|
|
23
|
+
### Inline comment threads (used by BUGBOT phase and convergence gate (e))
|
|
24
24
|
|
|
25
|
-
Fetch all
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
Fetch all unresolved inline threads on the PR. The convergence gate is
|
|
26
|
+
author-agnostic and commit-agnostic — see "Inline comment threads"
|
|
27
|
+
below for the canonical fetch.
|
|
28
28
|
|
|
29
29
|
### PR head SHA: `pull_request_read(method="get")`
|
|
30
30
|
|
|
@@ -38,7 +38,7 @@ Access `.head.sha` from the response.
|
|
|
38
38
|
|
|
39
39
|
Post the bugbot re-trigger comment:
|
|
40
40
|
|
|
41
|
-
add_issue_comment(owner="OWNER", repo="REPO",
|
|
41
|
+
add_issue_comment(owner="OWNER", repo="REPO", issue_number=NUMBER, body="bugbot run")
|
|
42
42
|
|
|
43
43
|
`bugbot run` is the only recognized re-trigger phrase; alternative phrasings silently no-op.
|
|
44
44
|
|
|
@@ -48,17 +48,16 @@ Mark a draft PR as ready for review:
|
|
|
48
48
|
|
|
49
49
|
update_pull_request(pullNumber=NUMBER, owner=OWNER, repo=REPO, draft=false)
|
|
50
50
|
|
|
51
|
-
### Reply to inline comment: `
|
|
51
|
+
### Reply to inline comment: `python scripts/post_fix_reply.py`
|
|
52
52
|
|
|
53
53
|
Reply to an inline review comment thread:
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
55
|
+
```
|
|
56
|
+
python scripts/post_fix_reply.py --owner <O> --repo <R> --pr-number <N> \
|
|
57
|
+
--in-reply-to <COMMENT_ID> --body "<reply text>"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Omit `--in-reply-to` to post a general PR comment instead.
|
|
62
61
|
|
|
63
62
|
### Mergeability: `pull_request_read(method="get")`
|
|
64
63
|
|
|
@@ -68,52 +67,41 @@ Read mergeability fields from the PR:
|
|
|
68
67
|
|
|
69
68
|
Fields: `.mergeable`, `.mergeable_state`, `.head.sha`.
|
|
70
69
|
|
|
71
|
-
### Copilot reviews: `
|
|
70
|
+
### Copilot reviews: `python scripts/fetch_copilot_reviews.py`
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
```
|
|
73
|
+
python scripts/fetch_copilot_reviews.py --owner <O> --repo <R> --pr-number <N>
|
|
74
|
+
```
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
Returns JSON array of Copilot reviews newest-first. Classification:
|
|
77
|
+
`APPROVED` → clean, `CHANGES_REQUESTED` → dirty, `COMMENTED` with
|
|
78
|
+
non-empty body → dirty.
|
|
76
79
|
|
|
77
|
-
|
|
80
|
+
### Inline comment threads: `pull_request_read(method="get_review_comments")`
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Fetch all review comments and filter for `copilot-pull-request-reviewer[bot]`, matching `pull_request_review_id` to the newest Copilot review on the target commit:
|
|
82
|
+
Fetch via MCP. The convergence gate filters ONLY on resolution state:
|
|
82
83
|
|
|
83
84
|
pull_request_read(method="get_review_comments", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
85
|
+
→ filter threads where `is_resolved == false`
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
1. Comment-based trigger:
|
|
90
|
-
```
|
|
91
|
-
add_issue_comment(owner=OWNER, repo=REPO, issueNumber=NUMBER, body="@copilot review")
|
|
92
|
-
```
|
|
93
|
-
2. Dedicated MCP tool (when available):
|
|
94
|
-
```
|
|
95
|
-
request_copilot_review(owner=OWNER, repo=REPO, pullNumber=NUMBER)
|
|
96
|
-
```
|
|
87
|
+
The fields `comment.author`, `comment.commit.oid`, and `is_outdated`
|
|
88
|
+
remain useful for **deciding how to address** each unresolved thread
|
|
89
|
+
(re-fix? reply-with-note?), but they do not exclude a thread from the
|
|
90
|
+
count.
|
|
97
91
|
|
|
98
|
-
|
|
92
|
+
### Request Copilot review: `gh api` REST endpoint
|
|
99
93
|
|
|
100
|
-
|
|
94
|
+
```
|
|
95
|
+
gh api --method POST repos/<O>/<R>/pulls/<N>/requested_reviewers \
|
|
96
|
+
-f 'reviewers[]=copilot-pull-request-reviewer[bot]'
|
|
97
|
+
```
|
|
101
98
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
pull_request_read(method="get_reviews", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
105
|
-
|
|
106
|
-
Classification: same state-based rules as Copilot reviews.
|
|
107
|
-
|
|
108
|
-
### Claude inline comments: `pull_request_read(method="get_review_comments")`
|
|
109
|
-
|
|
110
|
-
Fetch all review comments and filter for login containing `claude`, matching `pull_request_review_id` to the newest Claude review on the target commit:
|
|
111
|
-
|
|
112
|
-
pull_request_read(method="get_review_comments", pullNumber=NUMBER, owner=OWNER, repo=REPO)
|
|
99
|
+
Check for an existing pending review first with
|
|
100
|
+
`python scripts/check_pending_reviews.py --owner <O> --repo <R> --pr-number <N> --user copilot`.
|
|
113
101
|
|
|
114
102
|
## Shared modules
|
|
115
103
|
|
|
116
|
-
|
|
104
|
+
Shared Python utilities live under `_shared/pr-loop/scripts/` — `_xml_utils.py` for XML serialization, `_cli_utils.py` for CLI guards, `_path_resolver.py` for canonical path resolution. These serve `/bugteam`, `/qbug`, `/findbugs`, and `/fixbugs` equally.
|
|
117
105
|
|
|
118
106
|
## Tests
|
|
119
107
|
|