From 4729b3b0ff2f4129b720af2d48f983184a6f99f7 Mon Sep 17 00:00:00 2001 From: William Easton Date: Fri, 27 Feb 2026 15:20:00 -0600 Subject: [PATCH 1/5] PR Review and mention in PR fixes --- .github/workflows/downstream-users.lock.yml | 3 +- .../workflows/gh-aw-bug-exterminator.lock.yml | 3 +- .../gh-aw-code-duplication-fixer.lock.yml | 3 +- .../workflows/gh-aw-code-simplifier.lock.yml | 3 +- .../gh-aw-fragments/safe-output-create-pr.md | 1 + .../gh-aw-fragments/safe-output-push-to-pr.md | 1 + .github/workflows/gh-aw-issue-fixer.lock.yml | 3 +- ...gh-aw-mention-in-issue-no-sandbox.lock.yml | 3 +- .../workflows/gh-aw-mention-in-issue.lock.yml | 3 +- .../gh-aw-mention-in-pr-by-id.lock.yml | 3 +- .../gh-aw-mention-in-pr-no-sandbox.lock.yml | 60 ++++++++++++++++--- .../gh-aw-mention-in-pr-no-sandbox.md | 13 ++-- .../workflows/gh-aw-mention-in-pr.lock.yml | 60 ++++++++++++++++--- .github/workflows/gh-aw-mention-in-pr.md | 13 ++-- .../gh-aw-newbie-contributor-fixer.lock.yml | 3 +- .../workflows/gh-aw-pr-actions-fixer.lock.yml | 3 +- .../gh-aw-pr-review-addresser.lock.yml | 5 +- .../workflows/gh-aw-pr-review-addresser.md | 2 +- .../workflows/gh-aw-release-update.lock.yml | 3 +- .../workflows/gh-aw-scheduled-fix.lock.yml | 3 +- .../gh-aw-small-problem-fixer.lock.yml | 3 +- .../workflows/gh-aw-test-improvement.lock.yml | 3 +- .../workflows/gh-aw-test-improver.lock.yml | 3 +- .../workflows/gh-aw-text-beautifier.lock.yml | 3 +- 24 files changed, 162 insertions(+), 41 deletions(-) diff --git a/.github/workflows/downstream-users.lock.yml b/.github/workflows/downstream-users.lock.yml index 960063dd..922e579e 100644 --- a/.github/workflows/downstream-users.lock.yml +++ b/.github/workflows/downstream-users.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4d20eda00ac7fcf830c1d74576a9849cbc303f847db7fce88320ebc6930495db"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"18502e4d1b8b3bfb1cf45a9071998bffaf1c1c5938e87c165f4c378deae29586"} name: "Internal: Downstream Users" "on": @@ -881,6 +881,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-bug-exterminator.lock.yml b/.github/workflows/gh-aw-bug-exterminator.lock.yml index 48790d59..a1af21c2 100644 --- a/.github/workflows/gh-aw-bug-exterminator.lock.yml +++ b/.github/workflows/gh-aw-bug-exterminator.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4238d50026baf2597d9d2cbf7a35b30caa0e23317077bc526f936acefb62f21c"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e2ff52df9809ef17cd42993b711e03e185886508f05d85663a9bd8a02a034972"} name: "Gh Aw Bug Exterminator" "on": @@ -930,6 +930,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml index 95cf68b9..a023f34c 100644 --- a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml +++ b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"287b716cb4d03892116ac41b6c12b60417cdae96fa5e531e5508422e13fa449f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"574aebe8dd3dfab22a15a8635b6f4accd95f4c08fc858fa4e9b25719eff60963"} name: "Code Duplication Fixer" "on": @@ -932,6 +932,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-code-simplifier.lock.yml b/.github/workflows/gh-aw-code-simplifier.lock.yml index dc39e3ed..bd97e950 100644 --- a/.github/workflows/gh-aw-code-simplifier.lock.yml +++ b/.github/workflows/gh-aw-code-simplifier.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"bf9d9cc33799d1a67c4a541f1e167753525f51d89ff3069ab5bce6d5e9a1da50"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"24e0556ba30c90998d372031d43c272ef69c3f9dc37a85a50b53b167a40971ee"} name: "Code Simplifier" "on": @@ -947,6 +947,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md index a8c20bb5..4ba691f0 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md @@ -12,6 +12,7 @@ safe-inputs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) safe-outputs: create-pull-request: diff --git a/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md b/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md index f563f684..de7f4358 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md @@ -12,6 +12,7 @@ safe-inputs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) safe-outputs: push-to-pull-request-branch: diff --git a/.github/workflows/gh-aw-issue-fixer.lock.yml b/.github/workflows/gh-aw-issue-fixer.lock.yml index ffbc3b63..d83d7494 100644 --- a/.github/workflows/gh-aw-issue-fixer.lock.yml +++ b/.github/workflows/gh-aw-issue-fixer.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3728d58a902ce5f44551997d7f4db8230187f891b449a33c01addc059692e428"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d2f98427a8a65bbf99909a1618321cd7dd6464e6ebc5adc12ac37bb82ed47ee7"} name: "Issue Fixer" "on": @@ -980,6 +980,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml index 345de00d..32851baf 100644 --- a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"99fbad365c061074cb5bbd772bb195344ffbe6f39a470148d29ffe5bbf4f3bd8"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e0acf6635461edc6fe171ce35dca9c498a88d1a3387b20260f7fa9bc25488509"} name: "Mention in Issue (no sandbox)" "on": @@ -1068,6 +1068,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-mention-in-issue.lock.yml b/.github/workflows/gh-aw-mention-in-issue.lock.yml index 0fd5db81..834faba9 100644 --- a/.github/workflows/gh-aw-mention-in-issue.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0ae9f74f88703cfd13359b3cc4b30465b644fb2b99e7841481b54147175a482f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c91985eeffc36ab1db3074166233fa00c110ee71b24b72cc7da6a78afcddcf62"} name: "Mention in Issue" "on": @@ -1072,6 +1072,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml index 868d1616..180760e8 100644 --- a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml @@ -43,7 +43,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8e546cd412f75da2fc56b687cfed07f0c8fa250c98abf4f79bf121a5a31d007c"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b665402fdbad54042d9d2ae36ad074cce9d558d9c08714469c395b8d1f8063d8"} name: "Mention in PR by ID" "on": @@ -1228,6 +1228,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml index 90a30702..fd7677ee 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml @@ -36,6 +36,7 @@ # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-pr.md # - gh-aw-fragments/safe-output-push-to-pr.md +# - gh-aw-fragments/safe-output-reply-to-review-comment.md # - gh-aw-fragments/safe-output-resolve-thread.md # - gh-aw-fragments/safe-output-review-comment.md # - gh-aw-fragments/safe-output-submit-review.md @@ -43,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"269c9f4d79f0e82ef451152e37a596890a9bed2ffd411ec297a908dd97ef8ab8"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"bcde108572a3fdbbbc6eb36f2e578cfb819f5546f154d305f1c0c7fe044f6a67"} name: "Mention in PR (no sandbox)" "on": @@ -198,7 +199,7 @@ jobs: cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" cat << 'GH_AW_PROMPT_EOF' - Tools: add_comment, create_pull_request_review_comment, submit_pull_request_review, resolve_pull_request_review_thread, push_to_pull_request_branch, missing_tool, missing_data, noop + Tools: add_comment, create_pull_request_review_comment, submit_pull_request_review, reply_to_pull_request_review_comment, resolve_pull_request_review_thread, push_to_pull_request_branch, missing_tool, missing_data, noop GH_AW_PROMPT_EOF cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" cat << 'GH_AW_PROMPT_EOF' @@ -465,6 +466,14 @@ jobs: - **Max per run**: __GH_AW_EXPR_7F2A702A__ thread resolutions per workflow run. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' + ## reply-to-pull-request-review-comment Limitations + + - **Required field**: `comment_id` — the ID of the review comment to reply to. This is the numeric REST comment ID from `get_review_comments`. + - **Body**: Max 65,536 characters. Keep well under this limit. + - **Purpose**: Reply directly to a specific review comment thread to explain your reasoning when you disagree with or skip feedback. Do NOT use `add_comment` for this — use this tool to keep replies in context. + - **Max per run**: 10 replies per workflow run. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -481,7 +490,7 @@ jobs: ## Constraints - - **CAN**: Read files, search code, modify files locally, run tests and commands, leave inline review comments, submit reviews, resolve review threads, push to the PR branch (same-repo only) + - **CAN**: Read files, search code, modify files locally, run tests and commands, leave inline review comments, submit reviews, reply to review threads, resolve review threads, push to the PR branch (same-repo only) - **CANNOT**: Push to fork PR branches, merge PRs, delete branches When pushing changes, the workspace already has the PR branch checked out. Make your changes, commit them locally, then use `push_to_pull_request_branch`. @@ -518,10 +527,13 @@ jobs: **If asked to fix code or address review feedback:** - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - - Make the changes in the workspace. + - For each unresolved thread you address: + - Make the code changes in the workspace. + - Call `reply_to_pull_request_review_comment` on the thread's comment to briefly explain what you changed. + - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. - - After pushing, resolve each addressed review thread by calling `resolve_pull_request_review_thread` with the thread's node ID (the `id` field from `get_review_comments`, e.g., `PRRT_kwDO...`). Only resolve threads you have actually addressed — do not resolve threads you skipped or disagreed with. + - After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads left by other reviewers AND threads from your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. - **Fork PRs**: Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, you cannot push — reply explaining that you do not have permission to push to fork branches and suggest that the PR author apply the changes themselves. This is a GitHub security limitation. You can still review code, make local changes, and provide suggestions. **If asked a question about the code:** @@ -538,7 +550,8 @@ jobs: **Additional tools:** - `push_to_pull_request_branch` — push committed changes to the PR branch (same-repo PRs only) - - `resolve_pull_request_review_thread` — resolve a review thread after addressing the feedback (pass the thread's node ID) + - `reply_to_pull_request_review_comment` — reply inline to a review comment thread to explain what you changed or why you disagree + - `resolve_pull_request_review_thread` — resolve a review thread after addressing the feedback (pass the thread's GraphQL node ID) __GH_AW_EXPR_49B959F1__ @@ -907,6 +920,38 @@ jobs: }, "name": "submit_pull_request_review" }, + { + "description": "Reply to an existing review comment on a pull request. Use this to respond to feedback, answer questions, or acknowledge review comments. The comment_id must be the numeric ID of an existing review comment. CONSTRAINTS: Maximum 10 reply/replies can be created.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "The reply text in Markdown format. Provide a clear response to the review comment.", + "type": "string" + }, + "comment_id": { + "description": "The numeric ID of the review comment to reply to (e.g., 42853901 from the comment URL or API response).", + "type": [ + "number", + "string" + ] + }, + "pull_request_number": { + "description": "Pull request number to reply on. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, replies on the PR that triggered this workflow.", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "comment_id", + "body" + ], + "type": "object" + }, + "name": "reply_to_pull_request_review_comment" + }, { "description": "Resolve a review thread on a pull request. Use this to mark a review conversation as resolved after addressing the feedback. The thread_id must be the node ID of the review thread (e.g., PRRT_kwDO...).", "inputSchema": { @@ -1287,6 +1332,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) @@ -1791,7 +1837,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.docker.com,*.docker.io,*.githubusercontent.com,*.hackage.haskell.org,*.jsr.io,*.pythonhosted.org,*.rvm.io,*.vsblob.vsassets.io,adoptium.net,agents-md-generator.fastmcp.app,anaconda.org,api.adoptium.net,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.foojay.io,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.nuget.org,api.rubygems.org,api.snapcraft.io,apt.llvm.org,apt.releases.hashicorp.com,archive.apache.org,archive.ubuntu.com,archlinux.org,artifacts.elastic.co,auth.docker.io,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bitbucket.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,builds.hex.pm,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.azul.com,cdn.cocoapods.org,cdn.hex.pm,cdn.jsdelivr.net,cdn.playwright.dev,cdn.redhat.com,cdn.sheetjs.com,central.sonatype.com,ci.dot.net,clojars.org,cloud.elastic.co,cocoapods.org,code.jquery.com,codeload.github.com,conda.anaconda.org,conda.binstar.org,cpan.metacpan.org,cpan.org,crates.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,data.jsdelivr.com,dc.services.visualstudio.com,deb.debian.org,deb.nodesource.com,debian.map.fastlydns.net,deno.land,dist.nuget.org,dl-cdn.alpinelinux.org,dl.bintray.com,dl.fedoraproject.org,dl.google.com,dl.k8s.io,dlcdn.apache.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.fedoraproject.org,download.java.net,download.opensuse.org,download.oracle.com,download.swift.org,downloads.gradle-dn.com,downloads.haskell.org,ela.st,elastic.co,elastic.dev,elastic.github.io,esm.sh,fastly.hex.pm,files.pythonhosted.org,fonts.googleapis.com,fonts.gstatic.com,gcr.io,ge.jetbrains.com,gems.rubyforge.org,gems.rubyonrails.org,get-ghcup.haskell.org,get.pnpm.io,getcomposer.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,gradle.org,haskell.org,hex.pm,host.docker.internal,index.crates.io,index.rubygems.org,jcenter.bintray.com,jdk.java.net,jitpack.io,json-schema.org,json.schemastore.org,jsr.io,keyring.debian.org,keyserver.ubuntu.com,kotlin.bintray.com,lfs.github.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,mcr.microsoft.com,metacpan.org,mirror.archlinux.org,mirror.centos.org,mirrors.fedoraproject.org,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.debian.org,packages.jetbrains.team,packages.microsoft.com,packagist.org,pip.pypa.io,pkg.alpinelinux.org,pkg.go.dev,pkg.machengine.org,pkgs.dev.azure.com,pkgs.k8s.io,playwright.download.prss.microsoft.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,production.cloudflare.docker.com,productionresultssa0.blob.core.windows.net,productionresultssa1.blob.core.windows.net,productionresultssa10.blob.core.windows.net,productionresultssa11.blob.core.windows.net,productionresultssa12.blob.core.windows.net,productionresultssa13.blob.core.windows.net,productionresultssa14.blob.core.windows.net,productionresultssa15.blob.core.windows.net,productionresultssa16.blob.core.windows.net,productionresultssa17.blob.core.windows.net,productionresultssa18.blob.core.windows.net,productionresultssa19.blob.core.windows.net,productionresultssa2.blob.core.windows.net,productionresultssa3.blob.core.windows.net,productionresultssa4.blob.core.windows.net,productionresultssa5.blob.core.windows.net,productionresultssa6.blob.core.windows.net,productionresultssa7.blob.core.windows.net,productionresultssa8.blob.core.windows.net,productionresultssa9.blob.core.windows.net,proxy.golang.org,pub.dartlang.org,pub.dev,public-code-search.fastmcp.app,pypi.org,pypi.python.org,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.terraform.io,registry.yarnpkg.com,releases.hashicorp.com,repo.anaconda.com,repo.clojars.org,repo.continuum.io,repo.gradle.org,repo.grails.org,repo.hex.pm,repo.maven.apache.org,repo.packagist.org,repo.scala-sbt.org,repo.spring.io,repo.typesafe.com,repo.yarnpkg.com,repo1.maven.org,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,scala-ci.typesafe.com,security.debian.org,security.ubuntu.com,services.gradle.org,sh.rustup.rs,skimdb.npmjs.com,static.crates.io,static.rust-lang.org,storage.googleapis.com,sum.golang.org,swift.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vault.centos.org,www.cpan.org,www.elastic.co,www.java.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com,yum.releases.hashicorp.com,ziglang.org" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request_review_comment\":{\"max\":\"${{ inputs.create-pull-request-review-comment-max }}\",\"side\":\"RIGHT\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max_patch_size\":10240},\"resolve_pull_request_review_thread\":{\"max\":\"${{ inputs.resolve-pull-request-review-thread-max }}\"},\"submit_pull_request_review\":{\"footer\":\"if-body\",\"max\":1}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request_review_comment\":{\"max\":\"${{ inputs.create-pull-request-review-comment-max }}\",\"side\":\"RIGHT\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max_patch_size\":10240},\"reply_to_pull_request_review_comment\":{\"max\":10},\"resolve_pull_request_review_thread\":{\"max\":\"${{ inputs.resolve-pull-request-review-thread-max }}\"},\"submit_pull_request_review\":{\"footer\":\"if-body\",\"max\":1}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.EXTRA_COMMIT_GITHUB_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md index 710cc433..ed64cc3a 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md @@ -17,6 +17,7 @@ imports: - gh-aw-fragments/safe-output-submit-review.md - gh-aw-fragments/safe-output-push-to-pr.md - gh-aw-fragments/safe-output-resolve-thread.md + - gh-aw-fragments/safe-output-reply-to-review-comment.md - gh-aw-fragments/network-ecosystems.md engine: id: copilot @@ -121,7 +122,7 @@ Assist with pull requests on ${{ github.repository }} — review code, fix issue ## Constraints -- **CAN**: Read files, search code, modify files locally, run tests and commands, leave inline review comments, submit reviews, resolve review threads, push to the PR branch (same-repo only) +- **CAN**: Read files, search code, modify files locally, run tests and commands, leave inline review comments, submit reviews, reply to review threads, resolve review threads, push to the PR branch (same-repo only) - **CANNOT**: Push to fork PR branches, merge PRs, delete branches When pushing changes, the workspace already has the PR branch checked out. Make your changes, commit them locally, then use `push_to_pull_request_branch`. @@ -158,10 +159,13 @@ Based on what's asked, do the appropriate thing: **If asked to fix code or address review feedback:** - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. -- Make the changes in the workspace. +- For each unresolved thread you address: + - Make the code changes in the workspace. + - Call `reply_to_pull_request_review_comment` on the thread's comment to briefly explain what you changed. + - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. -- After pushing, resolve each addressed review thread by calling `resolve_pull_request_review_thread` with the thread's node ID (the `id` field from `get_review_comments`, e.g., `PRRT_kwDO...`). Only resolve threads you have actually addressed — do not resolve threads you skipped or disagreed with. +- After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads left by other reviewers AND threads from your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. - **Fork PRs**: Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, you cannot push — reply explaining that you do not have permission to push to fork branches and suggest that the PR author apply the changes themselves. This is a GitHub security limitation. You can still review code, make local changes, and provide suggestions. **If asked a question about the code:** @@ -178,6 +182,7 @@ If you did not submit a PR review, call `add_comment` with your response. If you **Additional tools:** - `push_to_pull_request_branch` — push committed changes to the PR branch (same-repo PRs only) -- `resolve_pull_request_review_thread` — resolve a review thread after addressing the feedback (pass the thread's node ID) +- `reply_to_pull_request_review_comment` — reply inline to a review comment thread to explain what you changed or why you disagree +- `resolve_pull_request_review_thread` — resolve a review thread after addressing the feedback (pass the thread's GraphQL node ID) ${{ inputs.additional-instructions }} diff --git a/.github/workflows/gh-aw-mention-in-pr.lock.yml b/.github/workflows/gh-aw-mention-in-pr.lock.yml index 4494f59a..06f93e04 100644 --- a/.github/workflows/gh-aw-mention-in-pr.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr.lock.yml @@ -36,6 +36,7 @@ # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-pr.md # - gh-aw-fragments/safe-output-push-to-pr.md +# - gh-aw-fragments/safe-output-reply-to-review-comment.md # - gh-aw-fragments/safe-output-resolve-thread.md # - gh-aw-fragments/safe-output-review-comment.md # - gh-aw-fragments/safe-output-submit-review.md @@ -43,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6e713a7a69e5f678fbf04a92c7aaeabd25538499ab5a5d8d113d91529806197e"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"532cbafdfe22a6758bef86bc1f8837b7d0a5bb977352d9cb2307e61d3b8cbc1a"} name: "Mention in PR" "on": @@ -195,7 +196,7 @@ jobs: cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" cat << 'GH_AW_PROMPT_EOF' - Tools: add_comment, create_pull_request_review_comment, submit_pull_request_review, resolve_pull_request_review_thread, push_to_pull_request_branch, missing_tool, missing_data, noop + Tools: add_comment, create_pull_request_review_comment, submit_pull_request_review, reply_to_pull_request_review_comment, resolve_pull_request_review_thread, push_to_pull_request_branch, missing_tool, missing_data, noop GH_AW_PROMPT_EOF cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" cat << 'GH_AW_PROMPT_EOF' @@ -462,6 +463,14 @@ jobs: - **Max per run**: __GH_AW_EXPR_7F2A702A__ thread resolutions per workflow run. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' + ## reply-to-pull-request-review-comment Limitations + + - **Required field**: `comment_id` — the ID of the review comment to reply to. This is the numeric REST comment ID from `get_review_comments`. + - **Body**: Max 65,536 characters. Keep well under this limit. + - **Purpose**: Reply directly to a specific review comment thread to explain your reasoning when you disagree with or skip feedback. Do NOT use `add_comment` for this — use this tool to keep replies in context. + - **Max per run**: 10 replies per workflow run. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -478,7 +487,7 @@ jobs: ## Constraints - - **CAN**: Read files, search code, modify files locally, run tests and commands, leave inline review comments, submit reviews, resolve review threads, push to the PR branch (same-repo only) + - **CAN**: Read files, search code, modify files locally, run tests and commands, leave inline review comments, submit reviews, reply to review threads, resolve review threads, push to the PR branch (same-repo only) - **CANNOT**: Push to fork PR branches, merge PRs, delete branches When pushing changes, the workspace already has the PR branch checked out. Make your changes, commit them locally, then use `push_to_pull_request_branch`. @@ -515,10 +524,13 @@ jobs: **If asked to fix code or address review feedback:** - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - - Make the changes in the workspace. + - For each unresolved thread you address: + - Make the code changes in the workspace. + - Call `reply_to_pull_request_review_comment` on the thread's comment to briefly explain what you changed. + - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. - - After pushing, resolve each addressed review thread by calling `resolve_pull_request_review_thread` with the thread's node ID (the `id` field from `get_review_comments`, e.g., `PRRT_kwDO...`). Only resolve threads you have actually addressed — do not resolve threads you skipped or disagreed with. + - After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads left by other reviewers AND threads from your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. - **Fork PRs**: Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, you cannot push — reply explaining that you do not have permission to push to fork branches and suggest that the PR author apply the changes themselves. This is a GitHub security limitation. You can still review code, make local changes, and provide suggestions. **If asked a question about the code:** @@ -535,7 +547,8 @@ jobs: **Additional tools:** - `push_to_pull_request_branch` — push committed changes to the PR branch (same-repo PRs only) - - `resolve_pull_request_review_thread` — resolve a review thread after addressing the feedback (pass the thread's node ID) + - `reply_to_pull_request_review_comment` — reply inline to a review comment thread to explain what you changed or why you disagree + - `resolve_pull_request_review_thread` — resolve a review thread after addressing the feedback (pass the thread's GraphQL node ID) __GH_AW_EXPR_49B959F1__ @@ -910,6 +923,38 @@ jobs: }, "name": "submit_pull_request_review" }, + { + "description": "Reply to an existing review comment on a pull request. Use this to respond to feedback, answer questions, or acknowledge review comments. The comment_id must be the numeric ID of an existing review comment. CONSTRAINTS: Maximum 10 reply/replies can be created.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "The reply text in Markdown format. Provide a clear response to the review comment.", + "type": "string" + }, + "comment_id": { + "description": "The numeric ID of the review comment to reply to (e.g., 42853901 from the comment URL or API response).", + "type": [ + "number", + "string" + ] + }, + "pull_request_number": { + "description": "Pull request number to reply on. This is the numeric ID from the GitHub URL (e.g., 876 in github.com/owner/repo/pull/876). If omitted, replies on the PR that triggered this workflow.", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "comment_id", + "body" + ], + "type": "object" + }, + "name": "reply_to_pull_request_review_comment" + }, { "description": "Resolve a review thread on a pull request. Use this to mark a review conversation as resolved after addressing the feedback. The thread_id must be the node ID of the review thread (e.g., PRRT_kwDO...).", "inputSchema": { @@ -1290,6 +1335,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) @@ -1923,7 +1969,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.docker.com,*.docker.io,*.githubusercontent.com,*.hackage.haskell.org,*.jsr.io,*.pythonhosted.org,*.rvm.io,*.vsblob.vsassets.io,adoptium.net,agents-md-generator.fastmcp.app,anaconda.org,api.adoptium.net,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.foojay.io,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.nuget.org,api.rubygems.org,api.snapcraft.io,apt.llvm.org,apt.releases.hashicorp.com,archive.apache.org,archive.ubuntu.com,archlinux.org,artifacts.elastic.co,auth.docker.io,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bitbucket.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,builds.hex.pm,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.azul.com,cdn.cocoapods.org,cdn.hex.pm,cdn.jsdelivr.net,cdn.playwright.dev,cdn.redhat.com,cdn.sheetjs.com,central.sonatype.com,ci.dot.net,clojars.org,cloud.elastic.co,cocoapods.org,code.jquery.com,codeload.github.com,conda.anaconda.org,conda.binstar.org,cpan.metacpan.org,cpan.org,crates.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,data.jsdelivr.com,dc.services.visualstudio.com,deb.debian.org,deb.nodesource.com,debian.map.fastlydns.net,deno.land,dist.nuget.org,dl-cdn.alpinelinux.org,dl.bintray.com,dl.fedoraproject.org,dl.google.com,dl.k8s.io,dlcdn.apache.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.fedoraproject.org,download.java.net,download.opensuse.org,download.oracle.com,download.swift.org,downloads.gradle-dn.com,downloads.haskell.org,ela.st,elastic.co,elastic.dev,elastic.github.io,esm.sh,fastly.hex.pm,files.pythonhosted.org,fonts.googleapis.com,fonts.gstatic.com,gcr.io,ge.jetbrains.com,gems.rubyforge.org,gems.rubyonrails.org,get-ghcup.haskell.org,get.pnpm.io,getcomposer.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,gradle.org,haskell.org,hex.pm,host.docker.internal,index.crates.io,index.rubygems.org,jcenter.bintray.com,jdk.java.net,jitpack.io,json-schema.org,json.schemastore.org,jsr.io,keyring.debian.org,keyserver.ubuntu.com,kotlin.bintray.com,lfs.github.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,mcr.microsoft.com,metacpan.org,mirror.archlinux.org,mirror.centos.org,mirrors.fedoraproject.org,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.debian.org,packages.jetbrains.team,packages.microsoft.com,packagist.org,pip.pypa.io,pkg.alpinelinux.org,pkg.go.dev,pkg.machengine.org,pkgs.dev.azure.com,pkgs.k8s.io,playwright.download.prss.microsoft.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,production.cloudflare.docker.com,productionresultssa0.blob.core.windows.net,productionresultssa1.blob.core.windows.net,productionresultssa10.blob.core.windows.net,productionresultssa11.blob.core.windows.net,productionresultssa12.blob.core.windows.net,productionresultssa13.blob.core.windows.net,productionresultssa14.blob.core.windows.net,productionresultssa15.blob.core.windows.net,productionresultssa16.blob.core.windows.net,productionresultssa17.blob.core.windows.net,productionresultssa18.blob.core.windows.net,productionresultssa19.blob.core.windows.net,productionresultssa2.blob.core.windows.net,productionresultssa3.blob.core.windows.net,productionresultssa4.blob.core.windows.net,productionresultssa5.blob.core.windows.net,productionresultssa6.blob.core.windows.net,productionresultssa7.blob.core.windows.net,productionresultssa8.blob.core.windows.net,productionresultssa9.blob.core.windows.net,proxy.golang.org,pub.dartlang.org,pub.dev,public-code-search.fastmcp.app,pypi.org,pypi.python.org,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.terraform.io,registry.yarnpkg.com,releases.hashicorp.com,repo.anaconda.com,repo.clojars.org,repo.continuum.io,repo.gradle.org,repo.grails.org,repo.hex.pm,repo.maven.apache.org,repo.packagist.org,repo.scala-sbt.org,repo.spring.io,repo.typesafe.com,repo.yarnpkg.com,repo1.maven.org,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,scala-ci.typesafe.com,security.debian.org,security.ubuntu.com,services.gradle.org,sh.rustup.rs,skimdb.npmjs.com,static.crates.io,static.rust-lang.org,storage.googleapis.com,sum.golang.org,swift.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,vault.centos.org,www.cpan.org,www.elastic.co,www.java.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com,yum.releases.hashicorp.com,ziglang.org" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request_review_comment\":{\"max\":\"${{ inputs.create-pull-request-review-comment-max }}\",\"side\":\"RIGHT\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max_patch_size\":10240},\"resolve_pull_request_review_thread\":{\"max\":\"${{ inputs.resolve-pull-request-review-thread-max }}\"},\"submit_pull_request_review\":{\"footer\":\"if-body\",\"max\":1}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request_review_comment\":{\"max\":\"${{ inputs.create-pull-request-review-comment-max }}\",\"side\":\"RIGHT\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max_patch_size\":10240},\"reply_to_pull_request_review_comment\":{\"max\":10},\"resolve_pull_request_review_thread\":{\"max\":\"${{ inputs.resolve-pull-request-review-thread-max }}\"},\"submit_pull_request_review\":{\"footer\":\"if-body\",\"max\":1}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.EXTRA_COMMIT_GITHUB_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/gh-aw-mention-in-pr.md b/.github/workflows/gh-aw-mention-in-pr.md index a668c337..8d417522 100644 --- a/.github/workflows/gh-aw-mention-in-pr.md +++ b/.github/workflows/gh-aw-mention-in-pr.md @@ -17,6 +17,7 @@ imports: - gh-aw-fragments/safe-output-submit-review.md - gh-aw-fragments/safe-output-push-to-pr.md - gh-aw-fragments/safe-output-resolve-thread.md + - gh-aw-fragments/safe-output-reply-to-review-comment.md - gh-aw-fragments/network-ecosystems.md engine: id: copilot @@ -128,7 +129,7 @@ Assist with pull requests on ${{ github.repository }} — review code, fix issue ## Constraints -- **CAN**: Read files, search code, modify files locally, run tests and commands, leave inline review comments, submit reviews, resolve review threads, push to the PR branch (same-repo only) +- **CAN**: Read files, search code, modify files locally, run tests and commands, leave inline review comments, submit reviews, reply to review threads, resolve review threads, push to the PR branch (same-repo only) - **CANNOT**: Push to fork PR branches, merge PRs, delete branches When pushing changes, the workspace already has the PR branch checked out. Make your changes, commit them locally, then use `push_to_pull_request_branch`. @@ -165,10 +166,13 @@ Based on what's asked, do the appropriate thing: **If asked to fix code or address review feedback:** - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. -- Make the changes in the workspace. +- For each unresolved thread you address: + - Make the code changes in the workspace. + - Call `reply_to_pull_request_review_comment` on the thread's comment to briefly explain what you changed. + - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. -- After pushing, resolve each addressed review thread by calling `resolve_pull_request_review_thread` with the thread's node ID (the `id` field from `get_review_comments`, e.g., `PRRT_kwDO...`). Only resolve threads you have actually addressed — do not resolve threads you skipped or disagreed with. +- After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads left by other reviewers AND threads from your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. - **Fork PRs**: Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, you cannot push — reply explaining that you do not have permission to push to fork branches and suggest that the PR author apply the changes themselves. This is a GitHub security limitation. You can still review code, make local changes, and provide suggestions. **If asked a question about the code:** @@ -185,6 +189,7 @@ If you did not submit a PR review, call `add_comment` with your response. If you **Additional tools:** - `push_to_pull_request_branch` — push committed changes to the PR branch (same-repo PRs only) -- `resolve_pull_request_review_thread` — resolve a review thread after addressing the feedback (pass the thread's node ID) +- `reply_to_pull_request_review_comment` — reply inline to a review comment thread to explain what you changed or why you disagree +- `resolve_pull_request_review_thread` — resolve a review thread after addressing the feedback (pass the thread's GraphQL node ID) ${{ inputs.additional-instructions }} diff --git a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml index 3d3ee173..d6dc750b 100644 --- a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml +++ b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"9d3d805c19f223fef9cadc064cd9e781df2ce4ae7a32b24d151de1086b62e168"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"913aa6fee07df1f674e230ddd3b13250ef7147cec7d451019439f70138129b07"} name: "Newbie Contributor Fixer" "on": @@ -933,6 +933,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml index 7cab17af..00652b84 100644 --- a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml +++ b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"70e2654ad8f59cff4dee6d213bffb0b066f53e7560efab02ac18407f226d94d5"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1c2fc68f6a54b8e6310b6ae4925d23fa3195d3f3d3a53032c770ca50c9baf7dd"} name: "PR Actions Fixer" "on": @@ -946,6 +946,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-pr-review-addresser.lock.yml b/.github/workflows/gh-aw-pr-review-addresser.lock.yml index 582b58dd..cf838bab 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.lock.yml +++ b/.github/workflows/gh-aw-pr-review-addresser.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2ebbb62c9c192157de36a031b5f00326c4271d9663da95969d90631a39d74485"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"de4069ae09286bfdffbfe940ffb4b02a0eb7910de9917df65efd883d771b707d"} name: "PR Review Addresser" "on": @@ -374,7 +374,7 @@ jobs: ### Step 4: Resolve Addressed Threads - After pushing, resolve each review thread you addressed with code changes by calling `resolve_pull_request_review_thread` with the thread's node ID (the `id` field from `get_review_comments`, e.g., `PRRT_kwDO...`). Only resolve threads you have actually addressed — do not resolve threads you skipped or disagreed with. + After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads from any reviewer — external reviewers, bots, and your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. ### Step 5: Respond @@ -1040,6 +1040,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-pr-review-addresser.md b/.github/workflows/gh-aw-pr-review-addresser.md index 9b2e1258..6a12d2fd 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.md +++ b/.github/workflows/gh-aw-pr-review-addresser.md @@ -142,7 +142,7 @@ For each unresolved review thread: ### Step 4: Resolve Addressed Threads -After pushing, resolve each review thread you addressed with code changes by calling `resolve_pull_request_review_thread` with the thread's node ID (the `id` field from `get_review_comments`, e.g., `PRRT_kwDO...`). Only resolve threads you have actually addressed — do not resolve threads you skipped or disagreed with. +After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads from any reviewer — external reviewers, bots, and your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. ### Step 5: Respond diff --git a/.github/workflows/gh-aw-release-update.lock.yml b/.github/workflows/gh-aw-release-update.lock.yml index f0e0a132..dd06752a 100644 --- a/.github/workflows/gh-aw-release-update.lock.yml +++ b/.github/workflows/gh-aw-release-update.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"66662cc958a418f86b88e9f30b0aaa25e00c409f0bb83359a79c1a6874a36c97"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c8dfdaf872cbb5e0a64f814cc020c600978b0b53eca7ddd82f8255eb97601fe0"} name: "Release Update Check" "on": @@ -901,6 +901,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-scheduled-fix.lock.yml b/.github/workflows/gh-aw-scheduled-fix.lock.yml index 23ea695e..7a7f219f 100644 --- a/.github/workflows/gh-aw-scheduled-fix.lock.yml +++ b/.github/workflows/gh-aw-scheduled-fix.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"390e57830044e0c5b3d4a68da4a88a522e707e2b78640366d8c331b1c05dd621"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d2c94ca1c330b0c2c27a9a0d003a2f7af1da5196680f79ba4a3420fe13c03de7"} name: "Scheduled Fix" "on": @@ -956,6 +956,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-small-problem-fixer.lock.yml b/.github/workflows/gh-aw-small-problem-fixer.lock.yml index 0cc24f86..3a890824 100644 --- a/.github/workflows/gh-aw-small-problem-fixer.lock.yml +++ b/.github/workflows/gh-aw-small-problem-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e7fcb156e6fc7ca8913fb677d677f5554dd62356160a3f4997e2b57750f4e598"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"13a9b433cf9171a6e12b3e505c03ed415ceacb0c81e1f9412998365329ad53a2"} name: "Small Problem Fixer" "on": @@ -984,6 +984,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-test-improvement.lock.yml b/.github/workflows/gh-aw-test-improvement.lock.yml index ef87580e..153fe21e 100644 --- a/.github/workflows/gh-aw-test-improvement.lock.yml +++ b/.github/workflows/gh-aw-test-improvement.lock.yml @@ -41,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"748ba1ff272dc8ecf46ed625c6db613cb8ccfea1264085eb66319ce2505a5023"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e54b24e47002fbfdd547c9caa6ed8da34c749aab247e8045b03505b5780d739f"} name: "Test Improver" "on": @@ -944,6 +944,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-test-improver.lock.yml b/.github/workflows/gh-aw-test-improver.lock.yml index 68314165..89491b14 100644 --- a/.github/workflows/gh-aw-test-improver.lock.yml +++ b/.github/workflows/gh-aw-test-improver.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"748ba1ff272dc8ecf46ed625c6db613cb8ccfea1264085eb66319ce2505a5023"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e54b24e47002fbfdd547c9caa6ed8da34c749aab247e8045b03505b5780d739f"} name: "Test Improver" "on": @@ -939,6 +939,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-text-beautifier.lock.yml b/.github/workflows/gh-aw-text-beautifier.lock.yml index 528d0d69..7f275270 100644 --- a/.github/workflows/gh-aw-text-beautifier.lock.yml +++ b/.github/workflows/gh-aw-text-beautifier.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"05acaf87ad876a6e7cfe219331cbefb85feb22851837b9b4b903ad0b61e4e7c9"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fbf0fa8517fb0c9af523b975acee1e3a321312e46791a1cfbe2f33e34c1d94a0"} name: "Text Beautifier" "on": @@ -941,6 +941,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) From 40cba6f7487b551507984fd8aca662ea2fdc043f Mon Sep 17 00:00:00 2001 From: William Easton Date: Fri, 27 Feb 2026 15:35:33 -0600 Subject: [PATCH 2/5] Updates to PR-related pipelines --- .github/workflows/downstream-users.lock.yml | 4 ++-- .github/workflows/gh-aw-bug-exterminator.lock.yml | 4 ++-- .github/workflows/gh-aw-code-duplication-fixer.lock.yml | 4 ++-- .github/workflows/gh-aw-code-simplifier.lock.yml | 4 ++-- .github/workflows/gh-aw-fragments/pr-context.md | 3 ++- .../workflows/gh-aw-fragments/safe-output-create-pr.md | 2 +- .../safe-output-reply-to-review-comment.md | 2 +- .github/workflows/gh-aw-issue-fixer.lock.yml | 4 ++-- .../workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml | 4 ++-- .github/workflows/gh-aw-mention-in-issue.lock.yml | 4 ++-- .github/workflows/gh-aw-mention-in-pr-by-id.lock.yml | 4 ++-- .github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml | 8 ++++---- .github/workflows/gh-aw-mention-in-pr-no-sandbox.md | 2 +- .github/workflows/gh-aw-mention-in-pr.lock.yml | 8 ++++---- .github/workflows/gh-aw-mention-in-pr.md | 2 +- .github/workflows/gh-aw-newbie-contributor-fixer.lock.yml | 4 ++-- .github/workflows/gh-aw-pr-review-addresser.lock.yml | 6 +++--- .github/workflows/gh-aw-pr-review-addresser.md | 2 +- .github/workflows/gh-aw-pr-review.lock.yml | 4 ++-- .github/workflows/gh-aw-release-update.lock.yml | 4 ++-- .github/workflows/gh-aw-scheduled-fix.lock.yml | 4 ++-- .github/workflows/gh-aw-small-problem-fixer.lock.yml | 4 ++-- .github/workflows/gh-aw-test-improvement.lock.yml | 4 ++-- .github/workflows/gh-aw-test-improver.lock.yml | 4 ++-- .github/workflows/gh-aw-text-beautifier.lock.yml | 4 ++-- 25 files changed, 50 insertions(+), 49 deletions(-) diff --git a/.github/workflows/downstream-users.lock.yml b/.github/workflows/downstream-users.lock.yml index 922e579e..0a23b115 100644 --- a/.github/workflows/downstream-users.lock.yml +++ b/.github/workflows/downstream-users.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"18502e4d1b8b3bfb1cf45a9071998bffaf1c1c5938e87c165f4c378deae29586"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6d5f7d4ee20ad51a2d5278505ca87512d9ec5d091a7556ec8f5fec0e264dc008"} name: "Internal: Downstream Users" "on": @@ -881,7 +881,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-bug-exterminator.lock.yml b/.github/workflows/gh-aw-bug-exterminator.lock.yml index a1af21c2..34633cae 100644 --- a/.github/workflows/gh-aw-bug-exterminator.lock.yml +++ b/.github/workflows/gh-aw-bug-exterminator.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e2ff52df9809ef17cd42993b711e03e185886508f05d85663a9bd8a02a034972"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"29221e729e8c257515180eb96556388c7fc1e613df1d4e072223923d84efabcb"} name: "Gh Aw Bug Exterminator" "on": @@ -930,7 +930,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml index a023f34c..96bc2aca 100644 --- a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml +++ b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"574aebe8dd3dfab22a15a8635b6f4accd95f4c08fc858fa4e9b25719eff60963"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"df462366880b50b4cc82867fb470477c87a2283deaa23450612c7638eae1ee12"} name: "Code Duplication Fixer" "on": @@ -932,7 +932,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-code-simplifier.lock.yml b/.github/workflows/gh-aw-code-simplifier.lock.yml index bd97e950..d5673c05 100644 --- a/.github/workflows/gh-aw-code-simplifier.lock.yml +++ b/.github/workflows/gh-aw-code-simplifier.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"24e0556ba30c90998d372031d43c272ef69c3f9dc37a85a50b53b167a40971ee"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"561827811e1cd80b731eceaa4e682185d06236538de58778cb4bb425aef74aab"} name: "Code Simplifier" "on": @@ -947,7 +947,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-fragments/pr-context.md b/.github/workflows/gh-aw-fragments/pr-context.md index 11b09b80..bac69c97 100644 --- a/.github/workflows/gh-aw-fragments/pr-context.md +++ b/.github/workflows/gh-aw-fragments/pr-context.md @@ -51,6 +51,7 @@ steps: comments(first: 100) { nodes { id + databaseId body author { login } createdAt @@ -105,7 +106,7 @@ steps: | `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` | | `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` | | `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body | - | `review_comments.json` | All review threads (GraphQL) — each thread has `id`, `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with body/author | + | `review_comments.json` | All review threads (GraphQL) — each thread has `id` (node ID for resolving), `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with `id`, `databaseId` (numeric REST ID for replies), body/author | | `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` | | `comments.json` | PR discussion comments (not inline) | | `issue-{N}.json` | Linked issue details (one file per linked issue, if any) | diff --git a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md index 4ba691f0..557eed7d 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md @@ -12,7 +12,7 @@ safe-inputs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) safe-outputs: create-pull-request: diff --git a/.github/workflows/gh-aw-fragments/safe-output-reply-to-review-comment.md b/.github/workflows/gh-aw-fragments/safe-output-reply-to-review-comment.md index 520d4354..486cb88e 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-reply-to-review-comment.md +++ b/.github/workflows/gh-aw-fragments/safe-output-reply-to-review-comment.md @@ -6,7 +6,7 @@ safe-outputs: ## reply-to-pull-request-review-comment Limitations -- **Required field**: `comment_id` — the ID of the review comment to reply to. This is the numeric REST comment ID from `get_review_comments`. +- **Required field**: `comment_id` — the numeric REST comment ID (e.g., `2481734562`). From `get_review_comments` this is the `id` field. From `/tmp/pr-context/review_comments.json` (GraphQL) this is the `databaseId` field. Do not pass GraphQL node IDs (e.g., `IC_kwDONVGiRc6...`) — those will fail. - **Body**: Max 65,536 characters. Keep well under this limit. - **Purpose**: Reply directly to a specific review comment thread to explain your reasoning when you disagree with or skip feedback. Do NOT use `add_comment` for this — use this tool to keep replies in context. - **Max per run**: 10 replies per workflow run. diff --git a/.github/workflows/gh-aw-issue-fixer.lock.yml b/.github/workflows/gh-aw-issue-fixer.lock.yml index d83d7494..93b5ebc3 100644 --- a/.github/workflows/gh-aw-issue-fixer.lock.yml +++ b/.github/workflows/gh-aw-issue-fixer.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d2f98427a8a65bbf99909a1618321cd7dd6464e6ebc5adc12ac37bb82ed47ee7"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"9d7c864199df8e3e6c1d4c4a8fe731512c22dc2eba9a4faea8c2094f56ee49ec"} name: "Issue Fixer" "on": @@ -980,7 +980,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml index 32851baf..cf21b96f 100644 --- a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e0acf6635461edc6fe171ce35dca9c498a88d1a3387b20260f7fa9bc25488509"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fad0232640786e7e769797694c86083f31806b98893ecd80b9d034c06ed990d5"} name: "Mention in Issue (no sandbox)" "on": @@ -1068,7 +1068,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-mention-in-issue.lock.yml b/.github/workflows/gh-aw-mention-in-issue.lock.yml index 834faba9..28932646 100644 --- a/.github/workflows/gh-aw-mention-in-issue.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c91985eeffc36ab1db3074166233fa00c110ee71b24b72cc7da6a78afcddcf62"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b5955e5f68b6ce719445902bb043599d63445309f25eceb274df9559af2e8c00"} name: "Mention in Issue" "on": @@ -1072,7 +1072,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml index 180760e8..b00eaa91 100644 --- a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml @@ -43,7 +43,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b665402fdbad54042d9d2ae36ad074cce9d558d9c08714469c395b8d1f8063d8"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2d96cee937c594f34e66b68e16dbc2183ef552069aaa262a0a542cf34502961b"} name: "Mention in PR by ID" "on": @@ -649,7 +649,7 @@ jobs: GH_TOKEN: ${{ github.token }} PR_NUMBER: ${{ github.event.pull_request.number || inputs.target-pr-number || github.event.issue.number }} name: Fetch PR context to disk - run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id`, `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" + run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id` (node ID for resolving), `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with `id`, `databaseId` (numeric REST ID for replies), body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" - name: Write review instructions to disk run: "mkdir -p /tmp/pr-context\ncat > /tmp/pr-context/review-instructions.md << 'REVIEW_EOF'\n# Review Instructions for Sub-agents\n\nYou are a code review sub-agent. Read these instructions, then review the PR files in the order provided in your prompt.\n\n## Context\n\nBefore reviewing files, read these to understand the PR:\n\n1. `/tmp/pr-context/pr.json` — PR title, description, author, and branches. Understand what the PR is trying to accomplish.\n2. `/tmp/pr-context/agents.md` — Repository coding conventions and guidelines (if it exists).\n3. `/tmp/pr-context/review_comments.json` — Existing review threads. Note which files already have threads so you don't duplicate.\n4. `/tmp/pr-context/issue-*.json` — Linked issue details (if any). Understand the motivation and acceptance criteria.\n\n## Process\n\nReview the PR diff file by file in your assigned order. For each changed file:\n\n1. **Read the diff** for this file from `/tmp/pr-context/diffs/.diff` to understand what changed. If the diff is empty or truncated (e.g., binary files or very large changes), fall back to reading the full file from the workspace and comparing against context.\n2. **Read the full file from the workspace.** The PR branch is checked out locally — open the file directly to get complete contents with line numbers.\n3. **Check existing threads** for this file from `/tmp/pr-context/threads/.json` (if it exists). Skip issues that are already under discussion — each thread has `isResolved` and `isOutdated` fields.\n4. **Identify potential issues** matching the review criteria below.\n5. **Quick-check each issue** before including it:\n - What specific code pattern or change triggers this concern?\n - Is there an obvious guard, handler, or mitigation visible in the immediate context?\n - Can you describe a concrete failure scenario (the `evidence` field)? If you cannot articulate what specific input or state triggers the problem, drop the finding.\n - If the issue is clearly handled, skip it. If you're unsure, include it — the parent will verify.\n6. **Add to your findings list.** Do NOT leave inline comments — you don't have that tool. Return findings in this format:\n\n```\n- file: path/to/file\n line: 42\n severity: HIGH\n title: Brief title\n description: What the issue is and why it matters\n evidence: The specific code pattern and failure scenario\n suggestion: corrected code here (optional — only if you can provide a concrete fix)\n```\n\n**Review every file in your assigned order.** Files reviewed earlier get more attention, which is why different sub-agents use different orderings.\n\n**Check existing threads** — per-file threads are at `/tmp/pr-context/threads/.json` (step 3 above). The full list is at `/tmp/pr-context/review_comments.json`. Do not flag issues that are already under discussion (resolved or unresolved). For outdated threads, only re-flag if the issue still applies to the current diff.\n\n**Return your full findings list** when done, or an empty list if no issues were found.\n\n## Review Criteria\n\nFocus on these categories in priority order:\n\n1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure)\n2. Logic bugs that could cause runtime failures or incorrect behavior\n3. Data integrity issues (race conditions, missing transactions, corruption risk)\n4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations)\n5. Error handling gaps (unhandled exceptions, missing validation)\n6. Breaking changes to public APIs without migration path\n7. Missing or incorrect test coverage for critical paths\n\n## What NOT to Flag\n\nOnly review the diff — do not flag issues in unchanged code, pre-existing problems not introduced by this PR, or style preferences handled by linters or formatters.\n\n**Common false positives** — these patterns look like issues but usually aren't. Before flagging anything in these categories, confirm the problem is real by reading the surrounding code:\n\n- **Security — input already sanitized:** Don't flag injection or XSS risks when inputs are sanitized upstream, parameterized queries are used, or the framework auto-escapes output.\n- **Null/undefined — guarded elsewhere:** Don't flag potential null dereferences if the value is guaranteed by a type guard, assertion, schema validation, or upstream null check.\n- **Error handling — handled at a different layer:** Don't flag missing try/catch if the caller, middleware, or framework catches and handles the error (e.g., Express error middleware, React error boundaries).\n- **Performance — theoretical, not practical:** Don't flag algorithmic complexity (e.g., O(n^2)) unless N is demonstrably large enough to matter in the actual usage context. \"This could be slow\" without evidence is not actionable.\n- **Validation — exists at another layer:** Don't flag missing input validation if it's handled by an API gateway, middleware, schema validator, or type system.\n- **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods.\n- **Style or naming — not in coding guidelines:** Don't flag naming conventions or code style unless they violate the repository's documented coding guidelines (from `generate_agents_md` or CONTRIBUTING docs).\n\n**Existing review threads** — check BEFORE flagging any issue:\n\n- **Resolved with reviewer reply** (e.g. \"This is intentional\") — reviewer's decision is final. Do NOT re-flag.\n- **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem.\n- **Unresolved** — already flagged. Do NOT duplicate.\n- **Outdated** — only re-flag if the issue still applies to the current diff.\n\nWhen in doubt, do not duplicate. Redundant comments erode trust.\n\nFinding no issues is a valid and valuable outcome. An empty findings list is better than findings that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, return an empty list.\n\n## Severity Classification\n\nDetermine severity AFTER investigating the issue, not before. First identify the problem and trace through the code, then assign a severity based on the evidence you found.\n\n- 🔴 CRITICAL — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs)\n- 🟠 HIGH — Should fix before merge (logic errors, missing validation, significant performance issues)\n- 🟡 MEDIUM — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases)\n- ⚪ LOW — Author discretion (minor improvements, documentation gaps)\n- 💬 NITPICK — Truly optional (stylistic preferences, alternative approaches)\n\n## Review Intensity\n\nThe review intensity is `${{ inputs.intensity || 'balanced' }}`.\n\n- **conservative**: High evidence bar. Only flag when you can demonstrate a concrete failure scenario. If you can construct a reasonable counterargument, do not flag. Approval with zero findings is the expected outcome for most PRs.\n- **balanced**: Standard evidence bar. Flag when you can point to specific code that would fail. If the issue is ambiguous, lean toward not flagging.\n- **aggressive**: Lower evidence bar. Flag when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions welcome but must cite specific code.\n\n## Calibration Examples\n\nUse these examples to calibrate your judgment. Each pair shows a real issue and a similar-looking pattern that is NOT an issue.\n\n### Example 1: Null/Undefined Access\n\n**True positive — flag this:**\n\n```js\n// PR adds this handler\napp.get('/user/:id', async (req, res) => {\n const user = await db.findUser(req.params.id);\n res.json({ name: user.name, email: user.email });\n});\n```\n\nWhy flag: `db.findUser()` can return `null` when no user matches the ID. Accessing `user.name` will throw a TypeError at runtime. No upstream guard exists — the route handler is the entry point.\n\n**False positive — do NOT flag this:**\n\n```ts\n// PR adds this line inside an existing function\nconst settings = user.getSettings();\n```\n\nWhy skip: Reading the full file reveals `user` is typed as `User` (not `User | null`), and the calling function only runs after `authenticateUser()` middleware which guarantees a valid user object. The null case is handled at a different layer.\n\n### Example 2: SQL Injection\n\n**True positive — flag this:**\n\n```python\n# PR adds this query\ncursor.execute(f\"SELECT * FROM orders WHERE customer_id = '{customer_id}'\")\n```\n\nWhy flag: String interpolation in a SQL query with user-controlled input (`customer_id` comes from the request). No parameterization or sanitization anywhere in the call chain.\n\n**False positive — do NOT flag this:**\n\n```python\n# PR adds this query\ncursor.execute(f\"SELECT * FROM orders WHERE status = '{OrderStatus.PENDING.value}'\")\n```\n\nWhy skip: The interpolated value is a hardcoded enum constant (`OrderStatus.PENDING`), not user input. There is no injection vector.\n\n### Example 3: Borderline — Do NOT Flag\n\n```go\n// PR adds this function\nfunc processItems(items []Item) []Result {\n results := make([]Result, 0)\n for _, item := range items {\n for _, tag := range item.Tags {\n results = append(results, process(item, tag))\n }\n }\n return results\n}\n```\n\nThis looks like an O(n*m) performance concern. But without evidence that `items` or `Tags` are large in practice, this is speculative. The function processes a bounded dataset (items from a single user request). Do not flag theoretical performance issues without evidence of real-world impact.\nREVIEW_EOF" - env: diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml index fd7677ee..75986581 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml @@ -44,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"bcde108572a3fdbbbc6eb36f2e578cfb819f5546f154d305f1c0c7fe044f6a67"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"94a94ae8bf13b0357803629b9bf8b5fe4e7686818c50a81d310f9be69ba6afd0"} name: "Mention in PR (no sandbox)" "on": @@ -468,7 +468,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## reply-to-pull-request-review-comment Limitations - - **Required field**: `comment_id` — the ID of the review comment to reply to. This is the numeric REST comment ID from `get_review_comments`. + - **Required field**: `comment_id` — the numeric REST comment ID (e.g., `2481734562`). From `get_review_comments` this is the `id` field. From `/tmp/pr-context/review_comments.json` (GraphQL) this is the `databaseId` field. Do not pass GraphQL node IDs (e.g., `IC_kwDONVGiRc6...`) — those will fail. - **Body**: Max 65,536 characters. Keep well under this limit. - **Purpose**: Reply directly to a specific review comment thread to explain your reasoning when you disagree with or skip feedback. Do NOT use `add_comment` for this — use this tool to keep replies in context. - **Max per run**: 10 replies per workflow run. @@ -529,7 +529,7 @@ jobs: - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - For each unresolved thread you address: - Make the code changes in the workspace. - - Call `reply_to_pull_request_review_comment` on the thread's comment to briefly explain what you changed. + - Call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. @@ -723,7 +723,7 @@ jobs: GH_TOKEN: ${{ github.token }} PR_NUMBER: ${{ github.event.pull_request.number || inputs.target-pr-number || github.event.issue.number }} name: Fetch PR context to disk - run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id`, `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" + run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id` (node ID for resolving), `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with `id`, `databaseId` (numeric REST ID for replies), body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" - name: Write review instructions to disk run: "mkdir -p /tmp/pr-context\ncat > /tmp/pr-context/review-instructions.md << 'REVIEW_EOF'\n# Review Instructions for Sub-agents\n\nYou are a code review sub-agent. Read these instructions, then review the PR files in the order provided in your prompt.\n\n## Context\n\nBefore reviewing files, read these to understand the PR:\n\n1. `/tmp/pr-context/pr.json` — PR title, description, author, and branches. Understand what the PR is trying to accomplish.\n2. `/tmp/pr-context/agents.md` — Repository coding conventions and guidelines (if it exists).\n3. `/tmp/pr-context/review_comments.json` — Existing review threads. Note which files already have threads so you don't duplicate.\n4. `/tmp/pr-context/issue-*.json` — Linked issue details (if any). Understand the motivation and acceptance criteria.\n\n## Process\n\nReview the PR diff file by file in your assigned order. For each changed file:\n\n1. **Read the diff** for this file from `/tmp/pr-context/diffs/.diff` to understand what changed. If the diff is empty or truncated (e.g., binary files or very large changes), fall back to reading the full file from the workspace and comparing against context.\n2. **Read the full file from the workspace.** The PR branch is checked out locally — open the file directly to get complete contents with line numbers.\n3. **Check existing threads** for this file from `/tmp/pr-context/threads/.json` (if it exists). Skip issues that are already under discussion — each thread has `isResolved` and `isOutdated` fields.\n4. **Identify potential issues** matching the review criteria below.\n5. **Quick-check each issue** before including it:\n - What specific code pattern or change triggers this concern?\n - Is there an obvious guard, handler, or mitigation visible in the immediate context?\n - Can you describe a concrete failure scenario (the `evidence` field)? If you cannot articulate what specific input or state triggers the problem, drop the finding.\n - If the issue is clearly handled, skip it. If you're unsure, include it — the parent will verify.\n6. **Add to your findings list.** Do NOT leave inline comments — you don't have that tool. Return findings in this format:\n\n```\n- file: path/to/file\n line: 42\n severity: HIGH\n title: Brief title\n description: What the issue is and why it matters\n evidence: The specific code pattern and failure scenario\n suggestion: corrected code here (optional — only if you can provide a concrete fix)\n```\n\n**Review every file in your assigned order.** Files reviewed earlier get more attention, which is why different sub-agents use different orderings.\n\n**Check existing threads** — per-file threads are at `/tmp/pr-context/threads/.json` (step 3 above). The full list is at `/tmp/pr-context/review_comments.json`. Do not flag issues that are already under discussion (resolved or unresolved). For outdated threads, only re-flag if the issue still applies to the current diff.\n\n**Return your full findings list** when done, or an empty list if no issues were found.\n\n## Review Criteria\n\nFocus on these categories in priority order:\n\n1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure)\n2. Logic bugs that could cause runtime failures or incorrect behavior\n3. Data integrity issues (race conditions, missing transactions, corruption risk)\n4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations)\n5. Error handling gaps (unhandled exceptions, missing validation)\n6. Breaking changes to public APIs without migration path\n7. Missing or incorrect test coverage for critical paths\n\n## What NOT to Flag\n\nOnly review the diff — do not flag issues in unchanged code, pre-existing problems not introduced by this PR, or style preferences handled by linters or formatters.\n\n**Common false positives** — these patterns look like issues but usually aren't. Before flagging anything in these categories, confirm the problem is real by reading the surrounding code:\n\n- **Security — input already sanitized:** Don't flag injection or XSS risks when inputs are sanitized upstream, parameterized queries are used, or the framework auto-escapes output.\n- **Null/undefined — guarded elsewhere:** Don't flag potential null dereferences if the value is guaranteed by a type guard, assertion, schema validation, or upstream null check.\n- **Error handling — handled at a different layer:** Don't flag missing try/catch if the caller, middleware, or framework catches and handles the error (e.g., Express error middleware, React error boundaries).\n- **Performance — theoretical, not practical:** Don't flag algorithmic complexity (e.g., O(n^2)) unless N is demonstrably large enough to matter in the actual usage context. \"This could be slow\" without evidence is not actionable.\n- **Validation — exists at another layer:** Don't flag missing input validation if it's handled by an API gateway, middleware, schema validator, or type system.\n- **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods.\n- **Style or naming — not in coding guidelines:** Don't flag naming conventions or code style unless they violate the repository's documented coding guidelines (from `generate_agents_md` or CONTRIBUTING docs).\n\n**Existing review threads** — check BEFORE flagging any issue:\n\n- **Resolved with reviewer reply** (e.g. \"This is intentional\") — reviewer's decision is final. Do NOT re-flag.\n- **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem.\n- **Unresolved** — already flagged. Do NOT duplicate.\n- **Outdated** — only re-flag if the issue still applies to the current diff.\n\nWhen in doubt, do not duplicate. Redundant comments erode trust.\n\nFinding no issues is a valid and valuable outcome. An empty findings list is better than findings that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, return an empty list.\n\n## Severity Classification\n\nDetermine severity AFTER investigating the issue, not before. First identify the problem and trace through the code, then assign a severity based on the evidence you found.\n\n- 🔴 CRITICAL — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs)\n- 🟠 HIGH — Should fix before merge (logic errors, missing validation, significant performance issues)\n- 🟡 MEDIUM — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases)\n- ⚪ LOW — Author discretion (minor improvements, documentation gaps)\n- 💬 NITPICK — Truly optional (stylistic preferences, alternative approaches)\n\n## Review Intensity\n\nThe review intensity is `${{ inputs.intensity || 'balanced' }}`.\n\n- **conservative**: High evidence bar. Only flag when you can demonstrate a concrete failure scenario. If you can construct a reasonable counterargument, do not flag. Approval with zero findings is the expected outcome for most PRs.\n- **balanced**: Standard evidence bar. Flag when you can point to specific code that would fail. If the issue is ambiguous, lean toward not flagging.\n- **aggressive**: Lower evidence bar. Flag when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions welcome but must cite specific code.\n\n## Calibration Examples\n\nUse these examples to calibrate your judgment. Each pair shows a real issue and a similar-looking pattern that is NOT an issue.\n\n### Example 1: Null/Undefined Access\n\n**True positive — flag this:**\n\n```js\n// PR adds this handler\napp.get('/user/:id', async (req, res) => {\n const user = await db.findUser(req.params.id);\n res.json({ name: user.name, email: user.email });\n});\n```\n\nWhy flag: `db.findUser()` can return `null` when no user matches the ID. Accessing `user.name` will throw a TypeError at runtime. No upstream guard exists — the route handler is the entry point.\n\n**False positive — do NOT flag this:**\n\n```ts\n// PR adds this line inside an existing function\nconst settings = user.getSettings();\n```\n\nWhy skip: Reading the full file reveals `user` is typed as `User` (not `User | null`), and the calling function only runs after `authenticateUser()` middleware which guarantees a valid user object. The null case is handled at a different layer.\n\n### Example 2: SQL Injection\n\n**True positive — flag this:**\n\n```python\n# PR adds this query\ncursor.execute(f\"SELECT * FROM orders WHERE customer_id = '{customer_id}'\")\n```\n\nWhy flag: String interpolation in a SQL query with user-controlled input (`customer_id` comes from the request). No parameterization or sanitization anywhere in the call chain.\n\n**False positive — do NOT flag this:**\n\n```python\n# PR adds this query\ncursor.execute(f\"SELECT * FROM orders WHERE status = '{OrderStatus.PENDING.value}'\")\n```\n\nWhy skip: The interpolated value is a hardcoded enum constant (`OrderStatus.PENDING`), not user input. There is no injection vector.\n\n### Example 3: Borderline — Do NOT Flag\n\n```go\n// PR adds this function\nfunc processItems(items []Item) []Result {\n results := make([]Result, 0)\n for _, item := range items {\n for _, tag := range item.Tags {\n results = append(results, process(item, tag))\n }\n }\n return results\n}\n```\n\nThis looks like an O(n*m) performance concern. But without evidence that `items` or `Tags` are large in practice, this is speculative. The function processes a bounded dataset (items from a single user request). Do not flag theoretical performance issues without evidence of real-world impact.\nREVIEW_EOF" - env: diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md index ed64cc3a..4a01ac9d 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md @@ -161,7 +161,7 @@ Based on what's asked, do the appropriate thing: - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - For each unresolved thread you address: - Make the code changes in the workspace. - - Call `reply_to_pull_request_review_comment` on the thread's comment to briefly explain what you changed. + - Call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. diff --git a/.github/workflows/gh-aw-mention-in-pr.lock.yml b/.github/workflows/gh-aw-mention-in-pr.lock.yml index 06f93e04..9819d550 100644 --- a/.github/workflows/gh-aw-mention-in-pr.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr.lock.yml @@ -44,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"532cbafdfe22a6758bef86bc1f8837b7d0a5bb977352d9cb2307e61d3b8cbc1a"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"f2ac713a7e36a468b1965835f004fd3cfa2a25b96a832fe9c6507031fc6268fd"} name: "Mention in PR" "on": @@ -465,7 +465,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## reply-to-pull-request-review-comment Limitations - - **Required field**: `comment_id` — the ID of the review comment to reply to. This is the numeric REST comment ID from `get_review_comments`. + - **Required field**: `comment_id` — the numeric REST comment ID (e.g., `2481734562`). From `get_review_comments` this is the `id` field. From `/tmp/pr-context/review_comments.json` (GraphQL) this is the `databaseId` field. Do not pass GraphQL node IDs (e.g., `IC_kwDONVGiRc6...`) — those will fail. - **Body**: Max 65,536 characters. Keep well under this limit. - **Purpose**: Reply directly to a specific review comment thread to explain your reasoning when you disagree with or skip feedback. Do NOT use `add_comment` for this — use this tool to keep replies in context. - **Max per run**: 10 replies per workflow run. @@ -526,7 +526,7 @@ jobs: - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - For each unresolved thread you address: - Make the code changes in the workspace. - - Call `reply_to_pull_request_review_comment` on the thread's comment to briefly explain what you changed. + - Call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. @@ -724,7 +724,7 @@ jobs: GH_TOKEN: ${{ github.token }} PR_NUMBER: ${{ github.event.pull_request.number || inputs.target-pr-number || github.event.issue.number }} name: Fetch PR context to disk - run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id`, `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" + run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id` (node ID for resolving), `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with `id`, `databaseId` (numeric REST ID for replies), body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" - name: Write review instructions to disk run: "mkdir -p /tmp/pr-context\ncat > /tmp/pr-context/review-instructions.md << 'REVIEW_EOF'\n# Review Instructions for Sub-agents\n\nYou are a code review sub-agent. Read these instructions, then review the PR files in the order provided in your prompt.\n\n## Context\n\nBefore reviewing files, read these to understand the PR:\n\n1. `/tmp/pr-context/pr.json` — PR title, description, author, and branches. Understand what the PR is trying to accomplish.\n2. `/tmp/pr-context/agents.md` — Repository coding conventions and guidelines (if it exists).\n3. `/tmp/pr-context/review_comments.json` — Existing review threads. Note which files already have threads so you don't duplicate.\n4. `/tmp/pr-context/issue-*.json` — Linked issue details (if any). Understand the motivation and acceptance criteria.\n\n## Process\n\nReview the PR diff file by file in your assigned order. For each changed file:\n\n1. **Read the diff** for this file from `/tmp/pr-context/diffs/.diff` to understand what changed. If the diff is empty or truncated (e.g., binary files or very large changes), fall back to reading the full file from the workspace and comparing against context.\n2. **Read the full file from the workspace.** The PR branch is checked out locally — open the file directly to get complete contents with line numbers.\n3. **Check existing threads** for this file from `/tmp/pr-context/threads/.json` (if it exists). Skip issues that are already under discussion — each thread has `isResolved` and `isOutdated` fields.\n4. **Identify potential issues** matching the review criteria below.\n5. **Quick-check each issue** before including it:\n - What specific code pattern or change triggers this concern?\n - Is there an obvious guard, handler, or mitigation visible in the immediate context?\n - Can you describe a concrete failure scenario (the `evidence` field)? If you cannot articulate what specific input or state triggers the problem, drop the finding.\n - If the issue is clearly handled, skip it. If you're unsure, include it — the parent will verify.\n6. **Add to your findings list.** Do NOT leave inline comments — you don't have that tool. Return findings in this format:\n\n```\n- file: path/to/file\n line: 42\n severity: HIGH\n title: Brief title\n description: What the issue is and why it matters\n evidence: The specific code pattern and failure scenario\n suggestion: corrected code here (optional — only if you can provide a concrete fix)\n```\n\n**Review every file in your assigned order.** Files reviewed earlier get more attention, which is why different sub-agents use different orderings.\n\n**Check existing threads** — per-file threads are at `/tmp/pr-context/threads/.json` (step 3 above). The full list is at `/tmp/pr-context/review_comments.json`. Do not flag issues that are already under discussion (resolved or unresolved). For outdated threads, only re-flag if the issue still applies to the current diff.\n\n**Return your full findings list** when done, or an empty list if no issues were found.\n\n## Review Criteria\n\nFocus on these categories in priority order:\n\n1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure)\n2. Logic bugs that could cause runtime failures or incorrect behavior\n3. Data integrity issues (race conditions, missing transactions, corruption risk)\n4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations)\n5. Error handling gaps (unhandled exceptions, missing validation)\n6. Breaking changes to public APIs without migration path\n7. Missing or incorrect test coverage for critical paths\n\n## What NOT to Flag\n\nOnly review the diff — do not flag issues in unchanged code, pre-existing problems not introduced by this PR, or style preferences handled by linters or formatters.\n\n**Common false positives** — these patterns look like issues but usually aren't. Before flagging anything in these categories, confirm the problem is real by reading the surrounding code:\n\n- **Security — input already sanitized:** Don't flag injection or XSS risks when inputs are sanitized upstream, parameterized queries are used, or the framework auto-escapes output.\n- **Null/undefined — guarded elsewhere:** Don't flag potential null dereferences if the value is guaranteed by a type guard, assertion, schema validation, or upstream null check.\n- **Error handling — handled at a different layer:** Don't flag missing try/catch if the caller, middleware, or framework catches and handles the error (e.g., Express error middleware, React error boundaries).\n- **Performance — theoretical, not practical:** Don't flag algorithmic complexity (e.g., O(n^2)) unless N is demonstrably large enough to matter in the actual usage context. \"This could be slow\" without evidence is not actionable.\n- **Validation — exists at another layer:** Don't flag missing input validation if it's handled by an API gateway, middleware, schema validator, or type system.\n- **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods.\n- **Style or naming — not in coding guidelines:** Don't flag naming conventions or code style unless they violate the repository's documented coding guidelines (from `generate_agents_md` or CONTRIBUTING docs).\n\n**Existing review threads** — check BEFORE flagging any issue:\n\n- **Resolved with reviewer reply** (e.g. \"This is intentional\") — reviewer's decision is final. Do NOT re-flag.\n- **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem.\n- **Unresolved** — already flagged. Do NOT duplicate.\n- **Outdated** — only re-flag if the issue still applies to the current diff.\n\nWhen in doubt, do not duplicate. Redundant comments erode trust.\n\nFinding no issues is a valid and valuable outcome. An empty findings list is better than findings that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, return an empty list.\n\n## Severity Classification\n\nDetermine severity AFTER investigating the issue, not before. First identify the problem and trace through the code, then assign a severity based on the evidence you found.\n\n- 🔴 CRITICAL — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs)\n- 🟠 HIGH — Should fix before merge (logic errors, missing validation, significant performance issues)\n- 🟡 MEDIUM — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases)\n- ⚪ LOW — Author discretion (minor improvements, documentation gaps)\n- 💬 NITPICK — Truly optional (stylistic preferences, alternative approaches)\n\n## Review Intensity\n\nThe review intensity is `${{ inputs.intensity || 'balanced' }}`.\n\n- **conservative**: High evidence bar. Only flag when you can demonstrate a concrete failure scenario. If you can construct a reasonable counterargument, do not flag. Approval with zero findings is the expected outcome for most PRs.\n- **balanced**: Standard evidence bar. Flag when you can point to specific code that would fail. If the issue is ambiguous, lean toward not flagging.\n- **aggressive**: Lower evidence bar. Flag when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions welcome but must cite specific code.\n\n## Calibration Examples\n\nUse these examples to calibrate your judgment. Each pair shows a real issue and a similar-looking pattern that is NOT an issue.\n\n### Example 1: Null/Undefined Access\n\n**True positive — flag this:**\n\n```js\n// PR adds this handler\napp.get('/user/:id', async (req, res) => {\n const user = await db.findUser(req.params.id);\n res.json({ name: user.name, email: user.email });\n});\n```\n\nWhy flag: `db.findUser()` can return `null` when no user matches the ID. Accessing `user.name` will throw a TypeError at runtime. No upstream guard exists — the route handler is the entry point.\n\n**False positive — do NOT flag this:**\n\n```ts\n// PR adds this line inside an existing function\nconst settings = user.getSettings();\n```\n\nWhy skip: Reading the full file reveals `user` is typed as `User` (not `User | null`), and the calling function only runs after `authenticateUser()` middleware which guarantees a valid user object. The null case is handled at a different layer.\n\n### Example 2: SQL Injection\n\n**True positive — flag this:**\n\n```python\n# PR adds this query\ncursor.execute(f\"SELECT * FROM orders WHERE customer_id = '{customer_id}'\")\n```\n\nWhy flag: String interpolation in a SQL query with user-controlled input (`customer_id` comes from the request). No parameterization or sanitization anywhere in the call chain.\n\n**False positive — do NOT flag this:**\n\n```python\n# PR adds this query\ncursor.execute(f\"SELECT * FROM orders WHERE status = '{OrderStatus.PENDING.value}'\")\n```\n\nWhy skip: The interpolated value is a hardcoded enum constant (`OrderStatus.PENDING`), not user input. There is no injection vector.\n\n### Example 3: Borderline — Do NOT Flag\n\n```go\n// PR adds this function\nfunc processItems(items []Item) []Result {\n results := make([]Result, 0)\n for _, item := range items {\n for _, tag := range item.Tags {\n results = append(results, process(item, tag))\n }\n }\n return results\n}\n```\n\nThis looks like an O(n*m) performance concern. But without evidence that `items` or `Tags` are large in practice, this is speculative. The function processes a bounded dataset (items from a single user request). Do not flag theoretical performance issues without evidence of real-world impact.\nREVIEW_EOF" - env: diff --git a/.github/workflows/gh-aw-mention-in-pr.md b/.github/workflows/gh-aw-mention-in-pr.md index 8d417522..828e336a 100644 --- a/.github/workflows/gh-aw-mention-in-pr.md +++ b/.github/workflows/gh-aw-mention-in-pr.md @@ -168,7 +168,7 @@ Based on what's asked, do the appropriate thing: - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - For each unresolved thread you address: - Make the code changes in the workspace. - - Call `reply_to_pull_request_review_comment` on the thread's comment to briefly explain what you changed. + - Call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. diff --git a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml index d6dc750b..82c3885b 100644 --- a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml +++ b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"913aa6fee07df1f674e230ddd3b13250ef7147cec7d451019439f70138129b07"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"101545e69f6610ae74c4124caa53d97a5c874c4b4cdfeed46a6618877f256897"} name: "Newbie Contributor Fixer" "on": @@ -933,7 +933,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-pr-review-addresser.lock.yml b/.github/workflows/gh-aw-pr-review-addresser.lock.yml index cf838bab..7f8ff518 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.lock.yml +++ b/.github/workflows/gh-aw-pr-review-addresser.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"de4069ae09286bfdffbfe940ffb4b02a0eb7910de9917df65efd883d771b707d"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3e6b93ed9fc8e3a5a20369bd267b31418033f61ff8c10da2a0dfa82c7f3f875e"} name: "PR Review Addresser" "on": @@ -318,7 +318,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## reply-to-pull-request-review-comment Limitations - - **Required field**: `comment_id` — the ID of the review comment to reply to. This is the numeric REST comment ID from `get_review_comments`. + - **Required field**: `comment_id` — the numeric REST comment ID (e.g., `2481734562`). From `get_review_comments` this is the `id` field. From `/tmp/pr-context/review_comments.json` (GraphQL) this is the `databaseId` field. Do not pass GraphQL node IDs (e.g., `IC_kwDONVGiRc6...`) — those will fail. - **Body**: Max 65,536 characters. Keep well under this limit. - **Purpose**: Reply directly to a specific review comment thread to explain your reasoning when you disagree with or skip feedback. Do NOT use `add_comment` for this — use this tool to keep replies in context. - **Max per run**: 10 replies per workflow run. @@ -363,7 +363,7 @@ jobs: 1. **Read and understand** the reviewer's feedback carefully. 2. **Decide**: Is the feedback actionable? Use your judgment — don't blindly accept every suggestion. - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. - - **If you disagree or it's unclear**: Call `reply_to_pull_request_review_comment` on the specific review comment to explain your reasoning inline. Do NOT resolve the thread — let the reviewer decide. + - **If you disagree or it's unclear**: Call `reply_to_pull_request_review_comment` with the comment's numeric ID to explain your reasoning inline. Do NOT resolve the thread — let the reviewer decide. 3. **Track** which threads you addressed with code changes vs. which you replied to. ### Step 3: Validate and Push diff --git a/.github/workflows/gh-aw-pr-review-addresser.md b/.github/workflows/gh-aw-pr-review-addresser.md index 6a12d2fd..b7466159 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.md +++ b/.github/workflows/gh-aw-pr-review-addresser.md @@ -131,7 +131,7 @@ For each unresolved review thread: 1. **Read and understand** the reviewer's feedback carefully. 2. **Decide**: Is the feedback actionable? Use your judgment — don't blindly accept every suggestion. - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. - - **If you disagree or it's unclear**: Call `reply_to_pull_request_review_comment` on the specific review comment to explain your reasoning inline. Do NOT resolve the thread — let the reviewer decide. + - **If you disagree or it's unclear**: Call `reply_to_pull_request_review_comment` with the comment's numeric ID to explain your reasoning inline. Do NOT resolve the thread — let the reviewer decide. 3. **Track** which threads you addressed with code changes vs. which you replied to. ### Step 3: Validate and Push diff --git a/.github/workflows/gh-aw-pr-review.lock.yml b/.github/workflows/gh-aw-pr-review.lock.yml index 54647e15..3e493aac 100644 --- a/.github/workflows/gh-aw-pr-review.lock.yml +++ b/.github/workflows/gh-aw-pr-review.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2a1aba9df1886fcd3c1d269eafbfda269603f64460bd155cbf014010883963e7"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d941ffb5b429f1f46680b2464a5997d02b5a2c64fa030af30bd120e30ea5deaa"} name: "PR Review" "on": @@ -682,7 +682,7 @@ jobs: GH_TOKEN: ${{ github.token }} PR_NUMBER: ${{ github.event.pull_request.number || inputs.target-pr-number || github.event.issue.number }} name: Fetch PR context to disk - run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id`, `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" + run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id` (node ID for resolving), `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with `id`, `databaseId` (numeric REST ID for replies), body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" - name: Write review instructions to disk run: "mkdir -p /tmp/pr-context\ncat > /tmp/pr-context/review-instructions.md << 'REVIEW_EOF'\n# Review Instructions for Sub-agents\n\nYou are a code review sub-agent. Read these instructions, then review the PR files in the order provided in your prompt.\n\n## Context\n\nBefore reviewing files, read these to understand the PR:\n\n1. `/tmp/pr-context/pr.json` — PR title, description, author, and branches. Understand what the PR is trying to accomplish.\n2. `/tmp/pr-context/agents.md` — Repository coding conventions and guidelines (if it exists).\n3. `/tmp/pr-context/review_comments.json` — Existing review threads. Note which files already have threads so you don't duplicate.\n4. `/tmp/pr-context/issue-*.json` — Linked issue details (if any). Understand the motivation and acceptance criteria.\n\n## Process\n\nReview the PR diff file by file in your assigned order. For each changed file:\n\n1. **Read the diff** for this file from `/tmp/pr-context/diffs/.diff` to understand what changed. If the diff is empty or truncated (e.g., binary files or very large changes), fall back to reading the full file from the workspace and comparing against context.\n2. **Read the full file from the workspace.** The PR branch is checked out locally — open the file directly to get complete contents with line numbers.\n3. **Check existing threads** for this file from `/tmp/pr-context/threads/.json` (if it exists). Skip issues that are already under discussion — each thread has `isResolved` and `isOutdated` fields.\n4. **Identify potential issues** matching the review criteria below.\n5. **Quick-check each issue** before including it:\n - What specific code pattern or change triggers this concern?\n - Is there an obvious guard, handler, or mitigation visible in the immediate context?\n - Can you describe a concrete failure scenario (the `evidence` field)? If you cannot articulate what specific input or state triggers the problem, drop the finding.\n - If the issue is clearly handled, skip it. If you're unsure, include it — the parent will verify.\n6. **Add to your findings list.** Do NOT leave inline comments — you don't have that tool. Return findings in this format:\n\n```\n- file: path/to/file\n line: 42\n severity: HIGH\n title: Brief title\n description: What the issue is and why it matters\n evidence: The specific code pattern and failure scenario\n suggestion: corrected code here (optional — only if you can provide a concrete fix)\n```\n\n**Review every file in your assigned order.** Files reviewed earlier get more attention, which is why different sub-agents use different orderings.\n\n**Check existing threads** — per-file threads are at `/tmp/pr-context/threads/.json` (step 3 above). The full list is at `/tmp/pr-context/review_comments.json`. Do not flag issues that are already under discussion (resolved or unresolved). For outdated threads, only re-flag if the issue still applies to the current diff.\n\n**Return your full findings list** when done, or an empty list if no issues were found.\n\n## Review Criteria\n\nFocus on these categories in priority order:\n\n1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure)\n2. Logic bugs that could cause runtime failures or incorrect behavior\n3. Data integrity issues (race conditions, missing transactions, corruption risk)\n4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations)\n5. Error handling gaps (unhandled exceptions, missing validation)\n6. Breaking changes to public APIs without migration path\n7. Missing or incorrect test coverage for critical paths\n\n## What NOT to Flag\n\nOnly review the diff — do not flag issues in unchanged code, pre-existing problems not introduced by this PR, or style preferences handled by linters or formatters.\n\n**Common false positives** — these patterns look like issues but usually aren't. Before flagging anything in these categories, confirm the problem is real by reading the surrounding code:\n\n- **Security — input already sanitized:** Don't flag injection or XSS risks when inputs are sanitized upstream, parameterized queries are used, or the framework auto-escapes output.\n- **Null/undefined — guarded elsewhere:** Don't flag potential null dereferences if the value is guaranteed by a type guard, assertion, schema validation, or upstream null check.\n- **Error handling — handled at a different layer:** Don't flag missing try/catch if the caller, middleware, or framework catches and handles the error (e.g., Express error middleware, React error boundaries).\n- **Performance — theoretical, not practical:** Don't flag algorithmic complexity (e.g., O(n^2)) unless N is demonstrably large enough to matter in the actual usage context. \"This could be slow\" without evidence is not actionable.\n- **Validation — exists at another layer:** Don't flag missing input validation if it's handled by an API gateway, middleware, schema validator, or type system.\n- **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods.\n- **Style or naming — not in coding guidelines:** Don't flag naming conventions or code style unless they violate the repository's documented coding guidelines (from `generate_agents_md` or CONTRIBUTING docs).\n\n**Existing review threads** — check BEFORE flagging any issue:\n\n- **Resolved with reviewer reply** (e.g. \"This is intentional\") — reviewer's decision is final. Do NOT re-flag.\n- **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem.\n- **Unresolved** — already flagged. Do NOT duplicate.\n- **Outdated** — only re-flag if the issue still applies to the current diff.\n\nWhen in doubt, do not duplicate. Redundant comments erode trust.\n\nFinding no issues is a valid and valuable outcome. An empty findings list is better than findings that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, return an empty list.\n\n## Severity Classification\n\nDetermine severity AFTER investigating the issue, not before. First identify the problem and trace through the code, then assign a severity based on the evidence you found.\n\n- 🔴 CRITICAL — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs)\n- 🟠 HIGH — Should fix before merge (logic errors, missing validation, significant performance issues)\n- 🟡 MEDIUM — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases)\n- ⚪ LOW — Author discretion (minor improvements, documentation gaps)\n- 💬 NITPICK — Truly optional (stylistic preferences, alternative approaches)\n\n## Review Intensity\n\nThe review intensity is `${{ inputs.intensity || 'balanced' }}`.\n\n- **conservative**: High evidence bar. Only flag when you can demonstrate a concrete failure scenario. If you can construct a reasonable counterargument, do not flag. Approval with zero findings is the expected outcome for most PRs.\n- **balanced**: Standard evidence bar. Flag when you can point to specific code that would fail. If the issue is ambiguous, lean toward not flagging.\n- **aggressive**: Lower evidence bar. Flag when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions welcome but must cite specific code.\n\n## Calibration Examples\n\nUse these examples to calibrate your judgment. Each pair shows a real issue and a similar-looking pattern that is NOT an issue.\n\n### Example 1: Null/Undefined Access\n\n**True positive — flag this:**\n\n```js\n// PR adds this handler\napp.get('/user/:id', async (req, res) => {\n const user = await db.findUser(req.params.id);\n res.json({ name: user.name, email: user.email });\n});\n```\n\nWhy flag: `db.findUser()` can return `null` when no user matches the ID. Accessing `user.name` will throw a TypeError at runtime. No upstream guard exists — the route handler is the entry point.\n\n**False positive — do NOT flag this:**\n\n```ts\n// PR adds this line inside an existing function\nconst settings = user.getSettings();\n```\n\nWhy skip: Reading the full file reveals `user` is typed as `User` (not `User | null`), and the calling function only runs after `authenticateUser()` middleware which guarantees a valid user object. The null case is handled at a different layer.\n\n### Example 2: SQL Injection\n\n**True positive — flag this:**\n\n```python\n# PR adds this query\ncursor.execute(f\"SELECT * FROM orders WHERE customer_id = '{customer_id}'\")\n```\n\nWhy flag: String interpolation in a SQL query with user-controlled input (`customer_id` comes from the request). No parameterization or sanitization anywhere in the call chain.\n\n**False positive — do NOT flag this:**\n\n```python\n# PR adds this query\ncursor.execute(f\"SELECT * FROM orders WHERE status = '{OrderStatus.PENDING.value}'\")\n```\n\nWhy skip: The interpolated value is a hardcoded enum constant (`OrderStatus.PENDING`), not user input. There is no injection vector.\n\n### Example 3: Borderline — Do NOT Flag\n\n```go\n// PR adds this function\nfunc processItems(items []Item) []Result {\n results := make([]Result, 0)\n for _, item := range items {\n for _, tag := range item.Tags {\n results = append(results, process(item, tag))\n }\n }\n return results\n}\n```\n\nThis looks like an O(n*m) performance concern. But without evidence that `items` or `Tags` are large in practice, this is speculative. The function processes a bounded dataset (items from a single user request). Do not flag theoretical performance issues without evidence of real-world impact.\nREVIEW_EOF" - env: diff --git a/.github/workflows/gh-aw-release-update.lock.yml b/.github/workflows/gh-aw-release-update.lock.yml index dd06752a..9e75eea7 100644 --- a/.github/workflows/gh-aw-release-update.lock.yml +++ b/.github/workflows/gh-aw-release-update.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c8dfdaf872cbb5e0a64f814cc020c600978b0b53eca7ddd82f8255eb97601fe0"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"14bf92007ab3e04eff2481ae99cf1d6a89f0a28c4dc11dc1e1aa98b99d7b0806"} name: "Release Update Check" "on": @@ -901,7 +901,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-scheduled-fix.lock.yml b/.github/workflows/gh-aw-scheduled-fix.lock.yml index 7a7f219f..ae9c9f43 100644 --- a/.github/workflows/gh-aw-scheduled-fix.lock.yml +++ b/.github/workflows/gh-aw-scheduled-fix.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d2c94ca1c330b0c2c27a9a0d003a2f7af1da5196680f79ba4a3420fe13c03de7"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d18ec314673b9b071d85fc86ecba5e2758dbe9eeb4f5a910a21eef88aa0d2637"} name: "Scheduled Fix" "on": @@ -956,7 +956,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-small-problem-fixer.lock.yml b/.github/workflows/gh-aw-small-problem-fixer.lock.yml index 3a890824..b005cac5 100644 --- a/.github/workflows/gh-aw-small-problem-fixer.lock.yml +++ b/.github/workflows/gh-aw-small-problem-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"13a9b433cf9171a6e12b3e505c03ed415ceacb0c81e1f9412998365329ad53a2"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b32c25ea1388ab3a252eb9a210c51c3dbb4a0c849d264c1ee69f22f340a8187e"} name: "Small Problem Fixer" "on": @@ -984,7 +984,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-test-improvement.lock.yml b/.github/workflows/gh-aw-test-improvement.lock.yml index 153fe21e..1ff84972 100644 --- a/.github/workflows/gh-aw-test-improvement.lock.yml +++ b/.github/workflows/gh-aw-test-improvement.lock.yml @@ -41,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e54b24e47002fbfdd547c9caa6ed8da34c749aab247e8045b03505b5780d739f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"31cd2b74387dd5721e1f1361b712e7a3ed80c42758b638fcaa3b4eb8f7e84b06"} name: "Test Improver" "on": @@ -944,7 +944,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-test-improver.lock.yml b/.github/workflows/gh-aw-test-improver.lock.yml index 89491b14..e048b941 100644 --- a/.github/workflows/gh-aw-test-improver.lock.yml +++ b/.github/workflows/gh-aw-test-improver.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e54b24e47002fbfdd547c9caa6ed8da34c749aab247e8045b03505b5780d739f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"31cd2b74387dd5721e1f1361b712e7a3ed80c42758b638fcaa3b4eb8f7e84b06"} name: "Test Improver" "on": @@ -939,7 +939,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) diff --git a/.github/workflows/gh-aw-text-beautifier.lock.yml b/.github/workflows/gh-aw-text-beautifier.lock.yml index 7f275270..094e4224 100644 --- a/.github/workflows/gh-aw-text-beautifier.lock.yml +++ b/.github/workflows/gh-aw-text-beautifier.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fbf0fa8517fb0c9af523b975acee1e3a321312e46791a1cfbe2f33e34c1d94a0"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"cdd116c3f943009f91ee90a9d07d4e0013025a200b9daa67b20a419517a08f0d"} name: "Text Beautifier" "on": @@ -941,7 +941,7 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') + checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) From a6082506bc9d8261cd787b0f8146d52d21bbafb4 Mon Sep 17 00:00:00 2001 From: William Easton Date: Fri, 27 Feb 2026 16:16:20 -0600 Subject: [PATCH 3/5] Additional info in ready-to-make-pr --- .github/workflows/downstream-users.lock.yml | 27 ++++++++-- .../workflows/gh-aw-bug-exterminator.lock.yml | 27 ++++++++-- .../gh-aw-code-duplication-fixer.lock.yml | 27 ++++++++-- .../workflows/gh-aw-code-simplifier.lock.yml | 27 ++++++++-- .../gh-aw-fragments/safe-output-create-pr.md | 25 +++++++-- .../gh-aw-fragments/safe-output-push-to-pr.md | 25 +++++++-- .github/workflows/gh-aw-issue-fixer.lock.yml | 27 ++++++++-- ...gh-aw-mention-in-issue-no-sandbox.lock.yml | 27 ++++++++-- .../workflows/gh-aw-mention-in-issue.lock.yml | 27 ++++++++-- .../gh-aw-mention-in-pr-by-id.lock.yml | 27 ++++++++-- .../gh-aw-mention-in-pr-no-sandbox.lock.yml | 27 ++++++++-- .../workflows/gh-aw-mention-in-pr.lock.yml | 27 ++++++++-- .../gh-aw-newbie-contributor-fixer.lock.yml | 27 ++++++++-- .../workflows/gh-aw-pr-actions-fixer.lock.yml | 27 ++++++++-- .../gh-aw-pr-review-addresser.lock.yml | 52 +++++++++++++++---- .../workflows/gh-aw-pr-review-addresser.md | 15 +++--- .../workflows/gh-aw-release-update.lock.yml | 27 ++++++++-- .../workflows/gh-aw-scheduled-fix.lock.yml | 27 ++++++++-- .../gh-aw-small-problem-fixer.lock.yml | 27 ++++++++-- .../workflows/gh-aw-test-improvement.lock.yml | 27 ++++++++-- .../workflows/gh-aw-test-improver.lock.yml | 27 ++++++++-- .../workflows/gh-aw-text-beautifier.lock.yml | 27 ++++++++-- 22 files changed, 509 insertions(+), 94 deletions(-) diff --git a/.github/workflows/downstream-users.lock.yml b/.github/workflows/downstream-users.lock.yml index 0a23b115..4b99268a 100644 --- a/.github/workflows/downstream-users.lock.yml +++ b/.github/workflows/downstream-users.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6d5f7d4ee20ad51a2d5278505ca87512d9ec5d091a7556ec8f5fec0e264dc008"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"de4dc14b6655d1c96955391d333d5ed65811ce83d5cbfeed4c78aa5a62ea6727"} name: "Internal: Downstream Users" "on": @@ -872,17 +872,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-bug-exterminator.lock.yml b/.github/workflows/gh-aw-bug-exterminator.lock.yml index 34633cae..6e1abd2a 100644 --- a/.github/workflows/gh-aw-bug-exterminator.lock.yml +++ b/.github/workflows/gh-aw-bug-exterminator.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"29221e729e8c257515180eb96556388c7fc1e613df1d4e072223923d84efabcb"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"05bd24f7e5d6a3d8e08517cab950a455ecb1bf9f07783a642674571b55b088a0"} name: "Gh Aw Bug Exterminator" "on": @@ -921,17 +921,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml index 96bc2aca..b96dc526 100644 --- a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml +++ b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"df462366880b50b4cc82867fb470477c87a2283deaa23450612c7638eae1ee12"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"ef3150c0d887b421fbfc6d8b3152f2833d662e7cbaaba312cf4fb5b66c9a6e6c"} name: "Code Duplication Fixer" "on": @@ -923,17 +923,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-code-simplifier.lock.yml b/.github/workflows/gh-aw-code-simplifier.lock.yml index d5673c05..6d73c7a9 100644 --- a/.github/workflows/gh-aw-code-simplifier.lock.yml +++ b/.github/workflows/gh-aw-code-simplifier.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"561827811e1cd80b731eceaa4e682185d06236538de58778cb4bb425aef74aab"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"555f2d90408b5f9107d669ede2083d9edbf4230d6249ce7a98e03da93d0cd2b9"} name: "Code Simplifier" "on": @@ -938,17 +938,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md index 557eed7d..1632dbd0 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md @@ -3,17 +3,36 @@ safe-inputs: ready-to-make-pr: description: "Run the PR readiness checklist before creating or updating a PR" py: | - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) safe-outputs: create-pull-request: draft: ${{ inputs.draft-prs }} diff --git a/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md b/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md index de7f4358..65031714 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md @@ -3,17 +3,36 @@ safe-inputs: ready-to-make-pr: description: "Run the PR readiness checklist before creating or updating a PR" py: | - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) safe-outputs: push-to-pull-request-branch: github-token-for-extra-empty-commit: ${{ secrets.EXTRA_COMMIT_GITHUB_TOKEN }} diff --git a/.github/workflows/gh-aw-issue-fixer.lock.yml b/.github/workflows/gh-aw-issue-fixer.lock.yml index 93b5ebc3..dc57727b 100644 --- a/.github/workflows/gh-aw-issue-fixer.lock.yml +++ b/.github/workflows/gh-aw-issue-fixer.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"9d7c864199df8e3e6c1d4c4a8fe731512c22dc2eba9a4faea8c2094f56ee49ec"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8eb5039f83c05aae527feaf764e8cba18770d6d1ed54bb598d384d1f48925506"} name: "Issue Fixer" "on": @@ -971,17 +971,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml index cf21b96f..3df0d44f 100644 --- a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fad0232640786e7e769797694c86083f31806b98893ecd80b9d034c06ed990d5"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"85da537366f7a87eda131e06a8c03343a1bfae9e24dc1edfac33c823cf7caf71"} name: "Mention in Issue (no sandbox)" "on": @@ -1059,17 +1059,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-mention-in-issue.lock.yml b/.github/workflows/gh-aw-mention-in-issue.lock.yml index 28932646..66559607 100644 --- a/.github/workflows/gh-aw-mention-in-issue.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b5955e5f68b6ce719445902bb043599d63445309f25eceb274df9559af2e8c00"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b8b6aabcf3f107b8b32fa466c6900893ec77ec32756fa49eeaa05cc190ea3768"} name: "Mention in Issue" "on": @@ -1063,17 +1063,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml index b00eaa91..8eff50e6 100644 --- a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml @@ -43,7 +43,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2d96cee937c594f34e66b68e16dbc2183ef552069aaa262a0a542cf34502961b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4c2a93d042bdb296d08acedc444840eda0d2a119559f0804b78385bc3c35eada"} name: "Mention in PR by ID" "on": @@ -1219,17 +1219,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml index 75986581..7da2633b 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml @@ -44,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"94a94ae8bf13b0357803629b9bf8b5fe4e7686818c50a81d310f9be69ba6afd0"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"827b6a2a3f61c1fa8e3be968f2ff1ed267c450f1e6ff75a39c8b6b9b07e16e4b"} name: "Mention in PR (no sandbox)" "on": @@ -1323,17 +1323,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-mention-in-pr.lock.yml b/.github/workflows/gh-aw-mention-in-pr.lock.yml index 9819d550..b1989325 100644 --- a/.github/workflows/gh-aw-mention-in-pr.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr.lock.yml @@ -44,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"f2ac713a7e36a468b1965835f004fd3cfa2a25b96a832fe9c6507031fc6268fd"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6b9a01a216e50c315979f2e5c195c61e7c8a27b0fd4da6b346bc2460bdc2a30e"} name: "Mention in PR" "on": @@ -1326,17 +1326,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml index 82c3885b..8289b3c7 100644 --- a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml +++ b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"101545e69f6610ae74c4124caa53d97a5c874c4b4cdfeed46a6618877f256897"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e3d0ea91512d21aca1a65cc41c2c24c78893253846faa0f0d17dc2e42b355663"} name: "Newbie Contributor Fixer" "on": @@ -924,17 +924,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml index 00652b84..a9f9fbd3 100644 --- a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml +++ b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1c2fc68f6a54b8e6310b6ae4925d23fa3195d3f3d3a53032c770ca50c9baf7dd"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"dbff8c018f4b3f073f63a2ece03b09368f96a918033e6e6cae2ebee688686394"} name: "PR Actions Fixer" "on": @@ -937,17 +937,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-pr-review-addresser.lock.yml b/.github/workflows/gh-aw-pr-review-addresser.lock.yml index 7f8ff518..d21b6f60 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.lock.yml +++ b/.github/workflows/gh-aw-pr-review-addresser.lock.yml @@ -30,6 +30,7 @@ # - gh-aw-fragments/mcp-pagination.md # - gh-aw-fragments/messages-footer.md # - gh-aw-fragments/network-ecosystems.md +# - gh-aw-fragments/pr-context.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-pr.md @@ -39,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3e6b93ed9fc8e3a5a20369bd267b31418033f61ff8c10da2a0dfa82c7f3f875e"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fbd4e109b6e7da437396d75887661f38126fcd29cd71fa8a632fbee6c479e68e"} name: "PR Review Addresser" "on": @@ -273,6 +274,11 @@ jobs: - **Use filters**: Combine `perPage` with state, label, or date filters to reduce result size - **Process as you go**: Don't accumulate all pages before acting — process each batch immediately + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## PR Context + + PR data is pre-fetched to `/tmp/pr-context/`. Read `/tmp/pr-context/README.md` for a manifest of all available files. Use these as your primary source for PR metadata, diffs, reviews, comments, and linked issues; fall back to API tools only when required data is unavailable. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -350,11 +356,13 @@ jobs: ### Step 1: Gather Context + PR context is pre-fetched to `/tmp/pr-context/`. Read `/tmp/pr-context/README.md` for a manifest of all available files. + 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Call `pull_request_read` with method `get` on PR #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ to get the full PR details (author, description, branches). Check whether this is a fork PR — if the head repo differs from the base repo, you cannot push changes. - 3. If the PR description references issues (e.g., "Fixes #123"), call `issue_read` with method `get` on each linked issue to understand the motivation and requirements. - 4. Call `pull_request_read` with method `get_review_comments` to get all review threads. Identify which threads are unresolved and need attention. - 5. Call `pull_request_read` with method `get_diff` to understand the current state of changes. + 2. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). Check whether this is a fork PR — if the head repo differs from the base repo, you cannot push changes. + 3. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. + 4. Read `/tmp/pr-context/review_comments.json` to get all review threads. Identify which threads are unresolved and need attention. + 5. Read `/tmp/pr-context/diffs/` to understand the current state of changes. ### Step 2: Address Each Review Thread @@ -362,7 +370,7 @@ jobs: 1. **Read and understand** the reviewer's feedback carefully. 2. **Decide**: Is the feedback actionable? Use your judgment — don't blindly accept every suggestion. - - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. + - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. Then call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - **If you disagree or it's unclear**: Call `reply_to_pull_request_review_comment` with the comment's numeric ID to explain your reasoning inline. Do NOT resolve the thread — let the reviewer decide. 3. **Track** which threads you addressed with code changes vs. which you replied to. @@ -374,7 +382,7 @@ jobs: ### Step 4: Resolve Addressed Threads - After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads from any reviewer — external reviewers, bots, and your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. + After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads from any reviewer — external reviewers, bots, and your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. Fall back to `pull_request_read` with method `get_review_comments` if the pre-fetched data is unavailable. ### Step 5: Respond @@ -552,6 +560,11 @@ jobs: chmod +x "$install_dir/uv" echo "$install_dir" >> "$GITHUB_PATH" shell: bash + - env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number || inputs.target-pr-number || github.event.issue.number }} + name: Fetch PR context to disk + run: "set -euo pipefail\nmkdir -p /tmp/pr-context\n\n# PR metadata\ngh pr view \"$PR_NUMBER\" --json title,body,author,baseRefName,headRefName,url \\\n > /tmp/pr-context/pr.json\n\n# Full diff\nif ! gh pr diff \"$PR_NUMBER\" > /tmp/pr-context/pr.diff; then\n echo \"::warning::Failed to fetch full PR diff; per-file diffs from files.json are still available.\"\n : > /tmp/pr-context/pr.diff\nfi\n\n# Changed files list (--paginate may output concatenated arrays; jq -s 'add' merges them)\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/files.json\n\n# Per-file diffs\njq -c '.[]' /tmp/pr-context/files.json | while IFS= read -r entry; do\n filename=$(echo \"$entry\" | jq -r '.filename')\n mkdir -p \"/tmp/pr-context/diffs/$(dirname \"$filename\")\"\n echo \"$entry\" | jq -r '.patch // empty' > \"/tmp/pr-context/diffs/${filename}.diff\"\ndone\n\n# Existing reviews\ngh api \"repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/reviews.json\n\n# Review threads with resolution status (GraphQL — REST lacks isResolved/isOutdated)\ngh api graphql --paginate -f query='\n query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $number) {\n reviewThreads(first: 100, after: $endCursor) {\n pageInfo { hasNextPage endCursor }\n nodes {\n id\n isResolved\n isOutdated\n isCollapsed\n path\n line\n startLine\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }\n' -F owner=\"${GITHUB_REPOSITORY%/*}\" -F repo=\"${GITHUB_REPOSITORY#*/}\" -F \"number=$PR_NUMBER\" \\\n --jq '.data.repository.pullRequest.reviewThreads.nodes' \\\n | jq -s 'add // []' > /tmp/pr-context/review_comments.json\n\n# Per-file review threads (mirrors diffs/ structure)\njq -c '.[]' /tmp/pr-context/review_comments.json | while IFS= read -r thread; do\n filepath=$(echo \"$thread\" | jq -r '.path // empty')\n [ -z \"$filepath\" ] && continue\n mkdir -p \"/tmp/pr-context/threads/$(dirname \"$filepath\")\"\n echo \"$thread\" >> \"/tmp/pr-context/threads/${filepath}.jsonl\"\ndone\n# Convert per-file JSONL to proper JSON arrays\nmkdir -p /tmp/pr-context/threads\nfind /tmp/pr-context/threads -name '*.jsonl' 2>/dev/null | while IFS= read -r jsonl; do\n jq -s '.' \"$jsonl\" > \"${jsonl%.jsonl}.json\"\n rm \"$jsonl\"\ndone\n\n# PR discussion comments\ngh api \"repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments\" --paginate \\\n | jq -s 'add // []' > /tmp/pr-context/comments.json\n\n# Linked issues\njq -r '.body // \"\"' /tmp/pr-context/pr.json 2>/dev/null \\\n | grep -oiE '(fixes|closes|resolves)\\s+#[0-9]+' \\\n | grep -oE '[0-9]+$' \\\n | sort -u \\\n | while read -r issue; do\n gh api \"repos/$GITHUB_REPOSITORY/issues/$issue\" > \"/tmp/pr-context/issue-${issue}.json\" || true\n done || true\n\n# Write manifest\ncat > /tmp/pr-context/README.md << 'MANIFEST'\n# PR Context\n\nPre-fetched PR data. All files are in `/tmp/pr-context/`.\n\n| File | Description |\n| --- | --- |\n| `pr.json` | PR metadata — title, body, author, base/head branches, URL |\n| `pr.diff` | Full unified diff of all changes |\n| `files.json` | Changed files array — each entry has `filename`, `status`, `additions`, `deletions`, `patch` |\n| `diffs/.diff` | Per-file diffs — one file per changed file, mirroring the repo path under `diffs/` |\n| `reviews.json` | Prior review submissions — author, state (APPROVED/CHANGES_REQUESTED/COMMENTED), body |\n| `review_comments.json` | All review threads (GraphQL) — each thread has `id` (node ID for resolving), `isResolved`, `isOutdated`, `path`, `line`, and nested `comments` with `id`, `databaseId` (numeric REST ID for replies), body/author |\n| `threads/.json` | Per-file review threads — one file per changed file with existing threads, mirroring the repo path under `threads/` |\n| `comments.json` | PR discussion comments (not inline) |\n| `issue-{N}.json` | Linked issue details (one file per linked issue, if any) |\n| `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" - env: GITHUB_TOKEN: ${{ github.token }} REPO_NAME: ${{ github.repository }} @@ -1031,17 +1044,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before pushing. Include the full diff (git diff) in the prompt along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-pr-review-addresser.md b/.github/workflows/gh-aw-pr-review-addresser.md index b7466159..9d204156 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.md +++ b/.github/workflows/gh-aw-pr-review-addresser.md @@ -8,6 +8,7 @@ imports: - gh-aw-fragments/formatting.md - gh-aw-fragments/rigor.md - gh-aw-fragments/mcp-pagination.md + - gh-aw-fragments/pr-context.md - gh-aw-fragments/messages-footer.md - gh-aw-fragments/safe-output-add-comment-pr.md - gh-aw-fragments/safe-output-push-to-pr.md @@ -118,11 +119,13 @@ Address the review feedback surgically — make only the minimum changes needed. ### Step 1: Gather Context +PR context is pre-fetched to `/tmp/pr-context/`. Read `/tmp/pr-context/README.md` for a manifest of all available files. + 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Call `pull_request_read` with method `get` on PR #${{ github.event.pull_request.number }} to get the full PR details (author, description, branches). Check whether this is a fork PR — if the head repo differs from the base repo, you cannot push changes. -3. If the PR description references issues (e.g., "Fixes #123"), call `issue_read` with method `get` on each linked issue to understand the motivation and requirements. -4. Call `pull_request_read` with method `get_review_comments` to get all review threads. Identify which threads are unresolved and need attention. -5. Call `pull_request_read` with method `get_diff` to understand the current state of changes. +2. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). Check whether this is a fork PR — if the head repo differs from the base repo, you cannot push changes. +3. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. +4. Read `/tmp/pr-context/review_comments.json` to get all review threads. Identify which threads are unresolved and need attention. +5. Read `/tmp/pr-context/diffs/` to understand the current state of changes. ### Step 2: Address Each Review Thread @@ -130,7 +133,7 @@ For each unresolved review thread: 1. **Read and understand** the reviewer's feedback carefully. 2. **Decide**: Is the feedback actionable? Use your judgment — don't blindly accept every suggestion. - - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. + - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. Then call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - **If you disagree or it's unclear**: Call `reply_to_pull_request_review_comment` with the comment's numeric ID to explain your reasoning inline. Do NOT resolve the thread — let the reviewer decide. 3. **Track** which threads you addressed with code changes vs. which you replied to. @@ -142,7 +145,7 @@ For each unresolved review thread: ### Step 4: Resolve Addressed Threads -After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads from any reviewer — external reviewers, bots, and your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. +After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads from any reviewer — external reviewers, bots, and your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. Fall back to `pull_request_read` with method `get_review_comments` if the pre-fetched data is unavailable. ### Step 5: Respond diff --git a/.github/workflows/gh-aw-release-update.lock.yml b/.github/workflows/gh-aw-release-update.lock.yml index 9e75eea7..95a01dc7 100644 --- a/.github/workflows/gh-aw-release-update.lock.yml +++ b/.github/workflows/gh-aw-release-update.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"14bf92007ab3e04eff2481ae99cf1d6a89f0a28c4dc11dc1e1aa98b99d7b0806"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fd45f5f2ee9d51f51d78e6b2c85b96ec49fa49c22f1bd62d5e66f84bcccbb619"} name: "Release Update Check" "on": @@ -892,17 +892,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-scheduled-fix.lock.yml b/.github/workflows/gh-aw-scheduled-fix.lock.yml index ae9c9f43..6ab6fb5d 100644 --- a/.github/workflows/gh-aw-scheduled-fix.lock.yml +++ b/.github/workflows/gh-aw-scheduled-fix.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d18ec314673b9b071d85fc86ecba5e2758dbe9eeb4f5a910a21eef88aa0d2637"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"42f37371c2479be6ac987a2382694151da16d3cd71dda4d3c398e4f933c10af9"} name: "Scheduled Fix" "on": @@ -947,17 +947,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-small-problem-fixer.lock.yml b/.github/workflows/gh-aw-small-problem-fixer.lock.yml index b005cac5..931de5dc 100644 --- a/.github/workflows/gh-aw-small-problem-fixer.lock.yml +++ b/.github/workflows/gh-aw-small-problem-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b32c25ea1388ab3a252eb9a210c51c3dbb4a0c849d264c1ee69f22f340a8187e"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"123e86a56cdb49b65d66dee95505070e6dd5fd96d82293985b887f7f4136eefc"} name: "Small Problem Fixer" "on": @@ -975,17 +975,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-test-improvement.lock.yml b/.github/workflows/gh-aw-test-improvement.lock.yml index 1ff84972..a1105a30 100644 --- a/.github/workflows/gh-aw-test-improvement.lock.yml +++ b/.github/workflows/gh-aw-test-improvement.lock.yml @@ -41,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"31cd2b74387dd5721e1f1361b712e7a3ed80c42758b638fcaa3b4eb8f7e84b06"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3599e4d53b786b610fd01c022b40b7a64f75cff185828df2b3383748e8d23b6c"} name: "Test Improver" "on": @@ -935,17 +935,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-test-improver.lock.yml b/.github/workflows/gh-aw-test-improver.lock.yml index e048b941..dc9f114d 100644 --- a/.github/workflows/gh-aw-test-improver.lock.yml +++ b/.github/workflows/gh-aw-test-improver.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"31cd2b74387dd5721e1f1361b712e7a3ed80c42758b638fcaa3b4eb8f7e84b06"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3599e4d53b786b610fd01c022b40b7a64f75cff185828df2b3383748e8d23b6c"} name: "Test Improver" "on": @@ -930,17 +930,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF diff --git a/.github/workflows/gh-aw-text-beautifier.lock.yml b/.github/workflows/gh-aw-text-beautifier.lock.yml index 094e4224..dee27fe8 100644 --- a/.github/workflows/gh-aw-text-beautifier.lock.yml +++ b/.github/workflows/gh-aw-text-beautifier.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"cdd116c3f943009f91ee90a9d07d4e0013025a200b9daa67b20a419517a08f0d"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d3c19763be37a6373049c52b07e3217fbef0b79c5cc403ad644fcadac3f1dc61"} name: "Text Beautifier" "on": @@ -932,17 +932,36 @@ jobs: inputs = {} # User code: - import os, json + import os, json, subprocess def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + return subprocess.run(cmd, capture_output=True, text=True, timeout=30) contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') + # Generate diff of unpushed changes for self-review + diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + diff_text = diff_result.stdout.strip() + if not diff_text: + diff_result = run(['git', 'diff']) + diff_text = diff_result.stdout.strip() + stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_text = stat_result.stdout.strip() + if not stat_text: + stat_result = run(['git', 'diff', '--stat']) + stat_text = stat_result.stdout.strip() + os.makedirs('/tmp/self-review', exist_ok=True) + with open('/tmp/self-review/diff.patch', 'w') as f: + f.write(diff_text) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + diff_line_count = len(diff_text.splitlines()) checklist = [] if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append('Spawn a code-review sub-agent via runSubagent to self-review your changes before creating the PR. Include the full patch in the prompt (git diff for uncommitted changes, or git diff HEAD~1 if already committed) along with relevant context (what was requested, which files changed, and why). The sub-agent should look for bugs, logic errors, missed edge cases, and style issues. Fix any legitimate issues it finds before proceeding.') - print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template})) + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) GH_AW_SAFE_INPUTS_PY_READY-TO-MAKE-PR_EOF From d81024e4a67197dd174204933722468473e0f852 Mon Sep 17 00:00:00 2001 From: William Easton Date: Fri, 27 Feb 2026 16:28:23 -0600 Subject: [PATCH 4/5] Address PR review feedback: self-review diff improvements and pr-review-addresser fixes - Use `git diff --merge-base @{upstream}` to capture committed + staged + unstaged changes (not just committed) for self-review diff generation - Add try/except for subprocess.TimeoutExpired with 60s timeout - Save diff to /tmp/self-review/ files instead of inlining in prompt - Add pr-context.md import to pr-review-addresser so /tmp/pr-context/ exists - Clarify Step 4 to skip for fork PRs where no push occurred - Add inline reply instruction for actionable threads in pr-review-addresser Co-Authored-By: Claude Opus 4.6 --- .github/workflows/downstream-users.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-bug-exterminator.lock.yml | 17 ++++++++++------- .../gh-aw-code-duplication-fixer.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-code-simplifier.lock.yml | 17 ++++++++++------- .../gh-aw-fragments/safe-output-create-pr.md | 15 +++++++++------ .../gh-aw-fragments/safe-output-push-to-pr.md | 15 +++++++++------ .github/workflows/gh-aw-issue-fixer.lock.yml | 17 ++++++++++------- ...gh-aw-mention-in-issue-no-sandbox.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-mention-in-issue.lock.yml | 17 ++++++++++------- .../gh-aw-mention-in-pr-by-id.lock.yml | 17 ++++++++++------- .../gh-aw-mention-in-pr-no-sandbox.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-mention-in-pr.lock.yml | 17 ++++++++++------- .../gh-aw-newbie-contributor-fixer.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-pr-actions-fixer.lock.yml | 17 ++++++++++------- .../gh-aw-pr-review-addresser.lock.yml | 19 ++++++++++++------- .../workflows/gh-aw-pr-review-addresser.md | 2 ++ .../workflows/gh-aw-release-update.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-scheduled-fix.lock.yml | 17 ++++++++++------- .../gh-aw-small-problem-fixer.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-test-improvement.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-test-improver.lock.yml | 17 ++++++++++------- .../workflows/gh-aw-text-beautifier.lock.yml | 17 ++++++++++------- 22 files changed, 212 insertions(+), 145 deletions(-) diff --git a/.github/workflows/downstream-users.lock.yml b/.github/workflows/downstream-users.lock.yml index 4b99268a..2ea2d10e 100644 --- a/.github/workflows/downstream-users.lock.yml +++ b/.github/workflows/downstream-users.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"de4dc14b6655d1c96955391d333d5ed65811ce83d5cbfeed4c78aa5a62ea6727"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"15e6cdbfd85903e9dc0d6c412128a2b4fe3b9e350554100f20ec7a0e32fd8cd7"} name: "Internal: Downstream Users" "on": @@ -876,19 +876,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-bug-exterminator.lock.yml b/.github/workflows/gh-aw-bug-exterminator.lock.yml index 6e1abd2a..db282a33 100644 --- a/.github/workflows/gh-aw-bug-exterminator.lock.yml +++ b/.github/workflows/gh-aw-bug-exterminator.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"05bd24f7e5d6a3d8e08517cab950a455ecb1bf9f07783a642674571b55b088a0"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c6a984314364ed96215b8574ea77a29474e895e3a22325c106e10a449460d43d"} name: "Gh Aw Bug Exterminator" "on": @@ -925,19 +925,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml index b96dc526..024ec9c2 100644 --- a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml +++ b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"ef3150c0d887b421fbfc6d8b3152f2833d662e7cbaaba312cf4fb5b66c9a6e6c"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"da1d0087837e8eca8bfedf96317c6aa5268b34bcff906bfe8b07571abc002b33"} name: "Code Duplication Fixer" "on": @@ -927,19 +927,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-code-simplifier.lock.yml b/.github/workflows/gh-aw-code-simplifier.lock.yml index 6d73c7a9..ff9e9218 100644 --- a/.github/workflows/gh-aw-code-simplifier.lock.yml +++ b/.github/workflows/gh-aw-code-simplifier.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"555f2d90408b5f9107d669ede2083d9edbf4230d6249ce7a98e03da93d0cd2b9"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8830f692524187ac4e283e6fa5aec2e52671fd579e949a800f7875e5849f1163"} name: "Code Simplifier" "on": @@ -942,19 +942,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md index 1632dbd0..785729b5 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md @@ -7,19 +7,22 @@ safe-inputs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md b/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md index 65031714..ed71309c 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md @@ -7,19 +7,22 @@ safe-inputs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-issue-fixer.lock.yml b/.github/workflows/gh-aw-issue-fixer.lock.yml index dc57727b..5db254dd 100644 --- a/.github/workflows/gh-aw-issue-fixer.lock.yml +++ b/.github/workflows/gh-aw-issue-fixer.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8eb5039f83c05aae527feaf764e8cba18770d6d1ed54bb598d384d1f48925506"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"341fc78e8697a4233ba68c3c3ff00388b16a34d01d126200c419006e1fa641ee"} name: "Issue Fixer" "on": @@ -975,19 +975,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml index 3df0d44f..9e6bc199 100644 --- a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"85da537366f7a87eda131e06a8c03343a1bfae9e24dc1edfac33c823cf7caf71"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"22cec2d3443f1d2df7cb77d7db9c32c98b7a4ae21f96bea31b4cf989d1421d95"} name: "Mention in Issue (no sandbox)" "on": @@ -1063,19 +1063,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-mention-in-issue.lock.yml b/.github/workflows/gh-aw-mention-in-issue.lock.yml index 66559607..1f4c76fa 100644 --- a/.github/workflows/gh-aw-mention-in-issue.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b8b6aabcf3f107b8b32fa466c6900893ec77ec32756fa49eeaa05cc190ea3768"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e862cdea6bafb1f9ba82de1d4daabddf3a4f2b93454eb9d99305052a8852b113"} name: "Mention in Issue" "on": @@ -1067,19 +1067,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml index 8eff50e6..aca662be 100644 --- a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml @@ -43,7 +43,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4c2a93d042bdb296d08acedc444840eda0d2a119559f0804b78385bc3c35eada"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"13c80f25f8077127519b108513db3f845c3fc7653b85c0075d8ef25a20c70bd8"} name: "Mention in PR by ID" "on": @@ -1223,19 +1223,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml index 7da2633b..acb3ca96 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml @@ -44,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"827b6a2a3f61c1fa8e3be968f2ff1ed267c450f1e6ff75a39c8b6b9b07e16e4b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"ea81d7ae315d223afa3dc335bab67f0c9b8415f179d4f68e58ea3b4eb2220c15"} name: "Mention in PR (no sandbox)" "on": @@ -1327,19 +1327,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-mention-in-pr.lock.yml b/.github/workflows/gh-aw-mention-in-pr.lock.yml index b1989325..4c2d3bd5 100644 --- a/.github/workflows/gh-aw-mention-in-pr.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr.lock.yml @@ -44,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6b9a01a216e50c315979f2e5c195c61e7c8a27b0fd4da6b346bc2460bdc2a30e"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"affe08920d1c4ba2226a98abdbbc52af6558ad956d13c6ce3a53caf8dd435957"} name: "Mention in PR" "on": @@ -1330,19 +1330,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml index 8289b3c7..15821f51 100644 --- a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml +++ b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e3d0ea91512d21aca1a65cc41c2c24c78893253846faa0f0d17dc2e42b355663"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0d034b7645508c7ce30d1754c9c5f480a56ba853d33e1c298a3b2c4590a759bf"} name: "Newbie Contributor Fixer" "on": @@ -928,19 +928,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml index a9f9fbd3..e66916cf 100644 --- a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml +++ b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"dbff8c018f4b3f073f63a2ece03b09368f96a918033e6e6cae2ebee688686394"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d2435a609a47236eea55c0d4ed2b3153393e703ed6424fab9f715335787e48a0"} name: "PR Actions Fixer" "on": @@ -941,19 +941,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-pr-review-addresser.lock.yml b/.github/workflows/gh-aw-pr-review-addresser.lock.yml index d21b6f60..a79efb73 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.lock.yml +++ b/.github/workflows/gh-aw-pr-review-addresser.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fbd4e109b6e7da437396d75887661f38126fcd29cd71fa8a632fbee6c479e68e"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"f6a957c305d4f5a4449e697c0e760d0550e498d98251fca13f97f7d4172bc395"} name: "PR Review Addresser" "on": @@ -382,6 +382,8 @@ jobs: ### Step 4: Resolve Addressed Threads + Skip this step for fork PRs where you could not push. + After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads from any reviewer — external reviewers, bots, and your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. Fall back to `pull_request_read` with method `get_review_comments` if the pre-fetched data is unavailable. ### Step 5: Respond @@ -1048,19 +1050,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-pr-review-addresser.md b/.github/workflows/gh-aw-pr-review-addresser.md index 9d204156..3c49ffc8 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.md +++ b/.github/workflows/gh-aw-pr-review-addresser.md @@ -145,6 +145,8 @@ For each unresolved review thread: ### Step 4: Resolve Addressed Threads +Skip this step for fork PRs where you could not push. + After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads from any reviewer — external reviewers, bots, and your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. Fall back to `pull_request_read` with method `get_review_comments` if the pre-fetched data is unavailable. ### Step 5: Respond diff --git a/.github/workflows/gh-aw-release-update.lock.yml b/.github/workflows/gh-aw-release-update.lock.yml index 95a01dc7..f4783f6d 100644 --- a/.github/workflows/gh-aw-release-update.lock.yml +++ b/.github/workflows/gh-aw-release-update.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fd45f5f2ee9d51f51d78e6b2c85b96ec49fa49c22f1bd62d5e66f84bcccbb619"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"9305d2b1997aafa08156822c8665b0b79b53a95aa58f50a8b2ffee0173234926"} name: "Release Update Check" "on": @@ -896,19 +896,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-scheduled-fix.lock.yml b/.github/workflows/gh-aw-scheduled-fix.lock.yml index 6ab6fb5d..507557a6 100644 --- a/.github/workflows/gh-aw-scheduled-fix.lock.yml +++ b/.github/workflows/gh-aw-scheduled-fix.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"42f37371c2479be6ac987a2382694151da16d3cd71dda4d3c398e4f933c10af9"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"a6cde40702404b1c1e829fd57b66a4d527eacc6e893fd44763f8804daf032083"} name: "Scheduled Fix" "on": @@ -951,19 +951,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-small-problem-fixer.lock.yml b/.github/workflows/gh-aw-small-problem-fixer.lock.yml index 931de5dc..314b73c5 100644 --- a/.github/workflows/gh-aw-small-problem-fixer.lock.yml +++ b/.github/workflows/gh-aw-small-problem-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"123e86a56cdb49b65d66dee95505070e6dd5fd96d82293985b887f7f4136eefc"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4c6c70b2b7c6088de76ef45ef12c276ea495d4c05135180acb0b16eb7545fbc4"} name: "Small Problem Fixer" "on": @@ -979,19 +979,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-test-improvement.lock.yml b/.github/workflows/gh-aw-test-improvement.lock.yml index a1105a30..e8d582e3 100644 --- a/.github/workflows/gh-aw-test-improvement.lock.yml +++ b/.github/workflows/gh-aw-test-improvement.lock.yml @@ -41,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3599e4d53b786b610fd01c022b40b7a64f75cff185828df2b3383748e8d23b6c"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"262b984be6b1aa6f30d03046c9563500d6a34af05d0ddb9f134df7fac2c9fb20"} name: "Test Improver" "on": @@ -939,19 +939,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-test-improver.lock.yml b/.github/workflows/gh-aw-test-improver.lock.yml index dc9f114d..9cdf1964 100644 --- a/.github/workflows/gh-aw-test-improver.lock.yml +++ b/.github/workflows/gh-aw-test-improver.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3599e4d53b786b610fd01c022b40b7a64f75cff185828df2b3383748e8d23b6c"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"262b984be6b1aa6f30d03046c9563500d6a34af05d0ddb9f134df7fac2c9fb20"} name: "Test Improver" "on": @@ -934,19 +934,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: diff --git a/.github/workflows/gh-aw-text-beautifier.lock.yml b/.github/workflows/gh-aw-text-beautifier.lock.yml index dee27fe8..4d025382 100644 --- a/.github/workflows/gh-aw-text-beautifier.lock.yml +++ b/.github/workflows/gh-aw-text-beautifier.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d3c19763be37a6373049c52b07e3217fbef0b79c5cc403ad644fcadac3f1dc61"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"52661ac1c5f3089a1f7ee34f6d47f6849a21ea9dcb1f57f22ab17110b676f8a3"} name: "Text Beautifier" "on": @@ -936,19 +936,22 @@ jobs: def find(*paths): return next((p for p in paths if os.path.isfile(p)), None) def run(cmd): - return subprocess.run(cmd, capture_output=True, text=True, timeout=30) + try: + return subprocess.run(cmd, capture_output=True, text=True, timeout=60) + except subprocess.TimeoutExpired: + return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of unpushed changes for self-review - diff_result = run(['git', 'diff', '@{upstream}...HEAD']) + # Generate diff of all local changes (committed + staged + unstaged) vs upstream + diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) diff_text = diff_result.stdout.strip() if not diff_text: - diff_result = run(['git', 'diff']) + diff_result = run(['git', 'diff', 'HEAD']) diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '@{upstream}...HEAD']) + stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) stat_text = stat_result.stdout.strip() if not stat_text: - stat_result = run(['git', 'diff', '--stat']) + stat_result = run(['git', 'diff', '--stat', 'HEAD']) stat_text = stat_result.stdout.strip() os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: From 29c18c5b4edb05d944293f6618506c91846a1da9 Mon Sep 17 00:00:00 2001 From: William Easton Date: Fri, 27 Feb 2026 16:43:39 -0600 Subject: [PATCH 5/5] Improve self-review diff generation, add pytest CI, soften reply requirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Diff fallback chain: --merge-base @{upstream} → @{upstream} → HEAD - Skip self-review checklist when diff is empty - Only reply to review threads when fix isn't obvious from the code change - Add pytest tests for safe-input Python scripts (23 tests) - Add ci-tests.yml workflow and `make test` target Co-Authored-By: Claude Opus 4.6 --- .github/workflows/ci-tests.yml | 25 ++ .github/workflows/downstream-users.lock.yml | 39 +- .../workflows/gh-aw-bug-exterminator.lock.yml | 39 +- .../gh-aw-code-duplication-fixer.lock.yml | 39 +- .../workflows/gh-aw-code-simplifier.lock.yml | 39 +- .../gh-aw-fragments/safe-output-create-pr.md | 37 +- .../gh-aw-fragments/safe-output-push-to-pr.md | 37 +- .github/workflows/gh-aw-issue-fixer.lock.yml | 39 +- ...gh-aw-mention-in-issue-no-sandbox.lock.yml | 39 +- .../workflows/gh-aw-mention-in-issue.lock.yml | 39 +- .../gh-aw-mention-in-pr-by-id.lock.yml | 39 +- .../gh-aw-mention-in-pr-no-sandbox.lock.yml | 43 +- .../gh-aw-mention-in-pr-no-sandbox.md | 4 +- .../workflows/gh-aw-mention-in-pr.lock.yml | 43 +- .github/workflows/gh-aw-mention-in-pr.md | 4 +- .../gh-aw-newbie-contributor-fixer.lock.yml | 39 +- .../workflows/gh-aw-pr-actions-fixer.lock.yml | 39 +- .../gh-aw-pr-review-addresser.lock.yml | 41 +- .../workflows/gh-aw-pr-review-addresser.md | 2 +- .../workflows/gh-aw-release-update.lock.yml | 39 +- .../workflows/gh-aw-scheduled-fix.lock.yml | 39 +- .../gh-aw-small-problem-fixer.lock.yml | 39 +- .../workflows/gh-aw-test-improvement.lock.yml | 39 +- .../workflows/gh-aw-test-improver.lock.yml | 39 +- .../workflows/gh-aw-text-beautifier.lock.yml | 39 +- Makefile | 5 +- pyproject.toml | 9 + tests/__init__.py | 0 tests/test_safe_input_ready_to_make_pr.py | 370 ++++++++++++++++++ uv.lock | 120 ++++++ 30 files changed, 1082 insertions(+), 282 deletions(-) create mode 100644 .github/workflows/ci-tests.yml create mode 100644 tests/__init__.py create mode 100644 tests/test_safe_input_ready_to_make_pr.py diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml new file mode 100644 index 00000000..30fc230b --- /dev/null +++ b/.github/workflows/ci-tests.yml @@ -0,0 +1,25 @@ +name: "Internal: Tests" +on: + pull_request: + paths: + - ".github/workflows/gh-aw-fragments/**" + - "tests/**" + - "pyproject.toml" + push: + branches: [main] + paths: + - ".github/workflows/gh-aw-fragments/**" + - "tests/**" + - "pyproject.toml" + +jobs: + test: + name: pytest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v6 + + - name: Run tests + run: uv run --extra test pytest tests/ -v diff --git a/.github/workflows/downstream-users.lock.yml b/.github/workflows/downstream-users.lock.yml index 2ea2d10e..47288110 100644 --- a/.github/workflows/downstream-users.lock.yml +++ b/.github/workflows/downstream-users.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"15e6cdbfd85903e9dc0d6c412128a2b4fe3b9e350554100f20ec7a0e32fd8cd7"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1dba193ef82fd955d37fa68d92ca0bc2f0f511a7add8a6f482e3647cf9cb4f4a"} name: "Internal: Downstream Users" "on": @@ -882,17 +882,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -903,7 +915,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-bug-exterminator.lock.yml b/.github/workflows/gh-aw-bug-exterminator.lock.yml index db282a33..8d7c3718 100644 --- a/.github/workflows/gh-aw-bug-exterminator.lock.yml +++ b/.github/workflows/gh-aw-bug-exterminator.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c6a984314364ed96215b8574ea77a29474e895e3a22325c106e10a449460d43d"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d943487a7e29f0a90dd302d93a91a906e6dc21db6251f79c4037f528818f2fbc"} name: "Gh Aw Bug Exterminator" "on": @@ -931,17 +931,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -952,7 +964,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml index 024ec9c2..c80772d2 100644 --- a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml +++ b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"da1d0087837e8eca8bfedf96317c6aa5268b34bcff906bfe8b07571abc002b33"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"bb7aaec4d46c4fd1735e301a045d4a75ed19ae00193b4d4589446e910fcaee31"} name: "Code Duplication Fixer" "on": @@ -933,17 +933,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -954,7 +966,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-code-simplifier.lock.yml b/.github/workflows/gh-aw-code-simplifier.lock.yml index ff9e9218..14e55770 100644 --- a/.github/workflows/gh-aw-code-simplifier.lock.yml +++ b/.github/workflows/gh-aw-code-simplifier.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8830f692524187ac4e283e6fa5aec2e52671fd579e949a800f7875e5849f1163"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0d2b6c05c633aab7c3f691918e60e77c216872bc01bdc7cc0960dc341aaa32b1"} name: "Code Simplifier" "on": @@ -948,17 +948,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -969,7 +981,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md index 785729b5..ce43e47d 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md @@ -13,17 +13,29 @@ safe-inputs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -34,7 +46,8 @@ safe-inputs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) safe-outputs: create-pull-request: diff --git a/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md b/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md index ed71309c..e7f1c6ae 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-push-to-pr.md @@ -13,17 +13,29 @@ safe-inputs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -34,7 +46,8 @@ safe-inputs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) safe-outputs: push-to-pull-request-branch: diff --git a/.github/workflows/gh-aw-issue-fixer.lock.yml b/.github/workflows/gh-aw-issue-fixer.lock.yml index 5db254dd..e3a097ee 100644 --- a/.github/workflows/gh-aw-issue-fixer.lock.yml +++ b/.github/workflows/gh-aw-issue-fixer.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"341fc78e8697a4233ba68c3c3ff00388b16a34d01d126200c419006e1fa641ee"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"72c94eb38a709725cfed3a3e99d575dc9d93d7c654f20e4540dcac1a8698d5dc"} name: "Issue Fixer" "on": @@ -981,17 +981,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -1002,7 +1014,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml index 9e6bc199..f5b1579d 100644 --- a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"22cec2d3443f1d2df7cb77d7db9c32c98b7a4ae21f96bea31b4cf989d1421d95"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"54a63f522d6040758fcb860c66b5e6ffba194e80bff65818ae64b9012150c25f"} name: "Mention in Issue (no sandbox)" "on": @@ -1069,17 +1069,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -1090,7 +1102,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-mention-in-issue.lock.yml b/.github/workflows/gh-aw-mention-in-issue.lock.yml index 1f4c76fa..a2feb56e 100644 --- a/.github/workflows/gh-aw-mention-in-issue.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e862cdea6bafb1f9ba82de1d4daabddf3a4f2b93454eb9d99305052a8852b113"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2ded4a298690475553c0f0f61d4fb5a3a4bb04e89d59e7559a521b9fc81abc39"} name: "Mention in Issue" "on": @@ -1073,17 +1073,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -1094,7 +1106,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml index aca662be..1f96af55 100644 --- a/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-by-id.lock.yml @@ -43,7 +43,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"13c80f25f8077127519b108513db3f845c3fc7653b85c0075d8ef25a20c70bd8"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"348bb144fcffb257a123572220f49d538279c90a52b15fe61b45c89b846695b5"} name: "Mention in PR by ID" "on": @@ -1229,17 +1229,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -1250,7 +1262,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml index acb3ca96..1904b3bc 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.lock.yml @@ -44,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"ea81d7ae315d223afa3dc335bab67f0c9b8415f179d4f68e58ea3b4eb2220c15"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e3f38a4324efedbed42d66c68f23e3f24397eae498756c8a0fff7a348db99f86"} name: "Mention in PR (no sandbox)" "on": @@ -529,8 +529,8 @@ jobs: - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - For each unresolved thread you address: - Make the code changes in the workspace. - - Call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. + - If the fix isn't obvious from the code change alone, call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. + - If you disagree with feedback or it's unclear, call `reply_to_pull_request_review_comment` to explain your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. - After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads left by other reviewers AND threads from your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. @@ -1333,17 +1333,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -1354,7 +1366,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md index 4a01ac9d..554fce99 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md @@ -161,8 +161,8 @@ Based on what's asked, do the appropriate thing: - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - For each unresolved thread you address: - Make the code changes in the workspace. - - Call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. + - If the fix isn't obvious from the code change alone, call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. + - If you disagree with feedback or it's unclear, call `reply_to_pull_request_review_comment` to explain your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. - After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads left by other reviewers AND threads from your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. diff --git a/.github/workflows/gh-aw-mention-in-pr.lock.yml b/.github/workflows/gh-aw-mention-in-pr.lock.yml index 4c2d3bd5..e2cc4644 100644 --- a/.github/workflows/gh-aw-mention-in-pr.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr.lock.yml @@ -44,7 +44,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"affe08920d1c4ba2226a98abdbbc52af6558ad956d13c6ce3a53caf8dd435957"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8d20590bf294d888940de1885bc1e17dba9d0e7a58b0808cdac0022034157222"} name: "Mention in PR" "on": @@ -526,8 +526,8 @@ jobs: - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - For each unresolved thread you address: - Make the code changes in the workspace. - - Call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. + - If the fix isn't obvious from the code change alone, call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. + - If you disagree with feedback or it's unclear, call `reply_to_pull_request_review_comment` to explain your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. - After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads left by other reviewers AND threads from your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. @@ -1336,17 +1336,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -1357,7 +1369,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-mention-in-pr.md b/.github/workflows/gh-aw-mention-in-pr.md index 828e336a..a00f6f41 100644 --- a/.github/workflows/gh-aw-mention-in-pr.md +++ b/.github/workflows/gh-aw-mention-in-pr.md @@ -168,8 +168,8 @@ Based on what's asked, do the appropriate thing: - Read `/tmp/pr-context/review_comments.json` to see open review threads and understand what needs to be addressed. - For each unresolved thread you address: - Make the code changes in the workspace. - - Call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - - If you disagree with feedback or it's unclear, reply explaining your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. + - If the fix isn't obvious from the code change alone, call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. + - If you disagree with feedback or it's unclear, call `reply_to_pull_request_review_comment` to explain your reasoning instead of making changes. Do NOT resolve the thread — let the reviewer decide. - Run required repo commands (lint/build/test) from README, CONTRIBUTING, DEVELOPING, Makefile, or CI config relevant to the change and include results. If required commands cannot be run, explain why and do not push changes. - Commit your changes locally, then use `push_to_pull_request_branch` to push them. - After pushing, resolve every review thread that your changes address by calling `resolve_pull_request_review_thread` with the thread's GraphQL node ID (the `id` field, e.g., `PRRT_kwDO...`). This includes threads left by other reviewers AND threads from your own prior reviews. Check `/tmp/pr-context/review_comments.json` for all unresolved threads (`isResolved: false`) — `isOutdated` threads have had the underlying code changed since the comment was made, so check whether your changes address them. Do NOT resolve threads you disagreed with, skipped, or only partially addressed — leave those open for the reviewer. diff --git a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml index 15821f51..6bd4ea24 100644 --- a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml +++ b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0d034b7645508c7ce30d1754c9c5f480a56ba853d33e1c298a3b2c4590a759bf"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"73718d7f4a6dc547a830f19821394962de7b454fb94c06a0a0fcd1690c7701b6"} name: "Newbie Contributor Fixer" "on": @@ -934,17 +934,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -955,7 +967,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml index e66916cf..f8461cf1 100644 --- a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml +++ b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d2435a609a47236eea55c0d4ed2b3153393e703ed6424fab9f715335787e48a0"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c711f87882d2c7b8edb9b0c1de8a7822c3bf3e47b8890e4ed2f3acabda9cb216"} name: "PR Actions Fixer" "on": @@ -947,17 +947,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -968,7 +980,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-pr-review-addresser.lock.yml b/.github/workflows/gh-aw-pr-review-addresser.lock.yml index a79efb73..6faee67a 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.lock.yml +++ b/.github/workflows/gh-aw-pr-review-addresser.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"f6a957c305d4f5a4449e697c0e760d0550e498d98251fca13f97f7d4172bc395"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"9564307564f6118e5125f6b8ddff78b350665801035a5e38eb7ddb58c7372256"} name: "PR Review Addresser" "on": @@ -370,7 +370,7 @@ jobs: 1. **Read and understand** the reviewer's feedback carefully. 2. **Decide**: Is the feedback actionable? Use your judgment — don't blindly accept every suggestion. - - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. Then call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. + - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. If the fix isn't obvious from the code change alone, call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - **If you disagree or it's unclear**: Call `reply_to_pull_request_review_comment` with the comment's numeric ID to explain your reasoning inline. Do NOT resolve the thread — let the reviewer decide. 3. **Track** which threads you addressed with code changes vs. which you replied to. @@ -1056,17 +1056,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -1077,7 +1089,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-pr-review-addresser.md b/.github/workflows/gh-aw-pr-review-addresser.md index 3c49ffc8..8661ec35 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.md +++ b/.github/workflows/gh-aw-pr-review-addresser.md @@ -133,7 +133,7 @@ For each unresolved review thread: 1. **Read and understand** the reviewer's feedback carefully. 2. **Decide**: Is the feedback actionable? Use your judgment — don't blindly accept every suggestion. - - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. Then call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. + - **If actionable**: Make the code change. Be surgical — change only what's needed to address the specific feedback. If the fix isn't obvious from the code change alone, call `reply_to_pull_request_review_comment` with the comment's numeric ID to briefly explain what you changed. - **If you disagree or it's unclear**: Call `reply_to_pull_request_review_comment` with the comment's numeric ID to explain your reasoning inline. Do NOT resolve the thread — let the reviewer decide. 3. **Track** which threads you addressed with code changes vs. which you replied to. diff --git a/.github/workflows/gh-aw-release-update.lock.yml b/.github/workflows/gh-aw-release-update.lock.yml index f4783f6d..aed78da4 100644 --- a/.github/workflows/gh-aw-release-update.lock.yml +++ b/.github/workflows/gh-aw-release-update.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"9305d2b1997aafa08156822c8665b0b79b53a95aa58f50a8b2ffee0173234926"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0b6237e0806733f975c6d12c6f256274868588e757ee89bcafc8a9bc8fdd5efd"} name: "Release Update Check" "on": @@ -902,17 +902,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -923,7 +935,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-scheduled-fix.lock.yml b/.github/workflows/gh-aw-scheduled-fix.lock.yml index 507557a6..95b2fa95 100644 --- a/.github/workflows/gh-aw-scheduled-fix.lock.yml +++ b/.github/workflows/gh-aw-scheduled-fix.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"a6cde40702404b1c1e829fd57b66a4d527eacc6e893fd44763f8804daf032083"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"24f475e3a2993dd129ad8be4404ab5fd3e4e081deabbef5b4a10db20862dbe38"} name: "Scheduled Fix" "on": @@ -957,17 +957,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -978,7 +990,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-small-problem-fixer.lock.yml b/.github/workflows/gh-aw-small-problem-fixer.lock.yml index 314b73c5..09ed0983 100644 --- a/.github/workflows/gh-aw-small-problem-fixer.lock.yml +++ b/.github/workflows/gh-aw-small-problem-fixer.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4c6c70b2b7c6088de76ef45ef12c276ea495d4c05135180acb0b16eb7545fbc4"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e0c2c421f13e9ce468bdb7536716845eb1bfe5f3dd96327f2c35c9e06ffc6bec"} name: "Small Problem Fixer" "on": @@ -985,17 +985,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -1006,7 +1018,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-test-improvement.lock.yml b/.github/workflows/gh-aw-test-improvement.lock.yml index e8d582e3..e2284d23 100644 --- a/.github/workflows/gh-aw-test-improvement.lock.yml +++ b/.github/workflows/gh-aw-test-improvement.lock.yml @@ -41,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"262b984be6b1aa6f30d03046c9563500d6a34af05d0ddb9f134df7fac2c9fb20"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8aaa1fb234b83c6087c0ca590188b4b6240204eb8e085d3969d3f017a84f8898"} name: "Test Improver" "on": @@ -945,17 +945,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -966,7 +978,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-test-improver.lock.yml b/.github/workflows/gh-aw-test-improver.lock.yml index 9cdf1964..10a5df80 100644 --- a/.github/workflows/gh-aw-test-improver.lock.yml +++ b/.github/workflows/gh-aw-test-improver.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"262b984be6b1aa6f30d03046c9563500d6a34af05d0ddb9f134df7fac2c9fb20"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8aaa1fb234b83c6087c0ca590188b4b6240204eb8e085d3969d3f017a84f8898"} name: "Test Improver" "on": @@ -940,17 +940,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -961,7 +973,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/.github/workflows/gh-aw-text-beautifier.lock.yml b/.github/workflows/gh-aw-text-beautifier.lock.yml index 4d025382..a89cedb0 100644 --- a/.github/workflows/gh-aw-text-beautifier.lock.yml +++ b/.github/workflows/gh-aw-text-beautifier.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"52661ac1c5f3089a1f7ee34f6d47f6849a21ea9dcb1f57f22ab17110b676f8a3"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2c95f9b03d6a21556f4408e2a3163ae2e781dfe8c8db8793c44f01d3581ee1dc"} name: "Text Beautifier" "on": @@ -942,17 +942,29 @@ jobs: return subprocess.CompletedProcess(cmd, 1, stdout='', stderr='diff timed out') contributing = find('CONTRIBUTING.md', 'CONTRIBUTING.rst', 'docs/CONTRIBUTING.md', 'docs/contributing.md') pr_template = find('.github/pull_request_template.md', '.github/PULL_REQUEST_TEMPLATE.md', '.github/PULL_REQUEST_TEMPLATE/pull_request_template.md') - # Generate diff of all local changes (committed + staged + unstaged) vs upstream - diff_result = run(['git', 'diff', '--merge-base', '@{upstream}']) - diff_text = diff_result.stdout.strip() - if not diff_text: - diff_result = run(['git', 'diff', 'HEAD']) - diff_text = diff_result.stdout.strip() - stat_result = run(['git', 'diff', '--stat', '--merge-base', '@{upstream}']) - stat_text = stat_result.stdout.strip() - if not stat_text: - stat_result = run(['git', 'diff', '--stat', 'HEAD']) - stat_text = stat_result.stdout.strip() + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (committed+staged+unstaged vs upstream), fall back to + # @{upstream} 2-dot (committed only), then HEAD (uncommitted only) + diff_text = '' + for diff_cmd in [ + ['git', 'diff', '--merge-base', '@{upstream}'], + ['git', 'diff', '@{upstream}'], + ['git', 'diff', 'HEAD'], + ]: + result = run(diff_cmd) + if result.stdout.strip(): + diff_text = result.stdout.strip() + break + stat_text = '' + for stat_cmd in [ + ['git', 'diff', '--stat', '--merge-base', '@{upstream}'], + ['git', 'diff', '--stat', '@{upstream}'], + ['git', 'diff', '--stat', 'HEAD'], + ]: + result = run(stat_cmd) + if result.stdout.strip(): + stat_text = result.stdout.strip() + break os.makedirs('/tmp/self-review', exist_ok=True) with open('/tmp/self-review/diff.patch', 'w') as f: f.write(diff_text) @@ -963,7 +975,8 @@ jobs: if contributing: checklist.append(f'Review the contributing guide ({contributing}) before opening or updating a PR.') if pr_template: checklist.append(f'Follow the PR template ({pr_template}) for title, description, and validation notes.') checklist.append('Confirm the requested task is fully completed and validated before creating or pushing PR changes.') - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) has been saved to `/tmp/self-review/diff.patch` (full diff) and `/tmp/self-review/stat.txt` (summary). Spawn a `code-review` sub-agent via `runSubagent` to review the diff against the codebase. Tell it to read `/tmp/self-review/diff.patch` and the relevant source files, and look for bugs, logic errors, missed edge cases, and style issues. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_make_pr` again to regenerate the diff before proceeding.') print(json.dumps({'status': 'ok', 'checklist': checklist, 'contributing_guide': contributing, 'pr_template': pr_template, 'diff_line_count': diff_line_count})) diff --git a/Makefile b/Makefile index c799eb82..e99704ec 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ define download-file fi endef -.PHONY: help setup setup-actionlint setup-action-validator setup-gh setup-gh-macos setup-gh-debian setup-gh-aw compile sync lint-workflows lint-actions docs-install docs-serve docs-build release +.PHONY: help setup setup-actionlint setup-action-validator setup-gh setup-gh-macos setup-gh-debian setup-gh-aw compile sync lint-workflows lint-actions test docs-install docs-serve docs-build release help: @echo "This repository contains GitHub Actions workflows and gh-agent-workflows templates." @@ -225,6 +225,9 @@ lint-actions: setup-action-validator lint: lint-workflows lint-actions @python3 scripts/check-nav-catalog.py +test: + @uv run --extra test pytest tests/ -v + docs-install: @uv sync --dev diff --git a/pyproject.toml b/pyproject.toml index 3711da2c..ef1997c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,12 @@ dependencies = [ "mkdocs>=1.6.0,<2.0", "mkdocs-material>=9.5.0", ] + +[project.optional-dependencies] +test = [ + "pytest>=8.0", + "pyyaml>=6.0", +] + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_safe_input_ready_to_make_pr.py b/tests/test_safe_input_ready_to_make_pr.py new file mode 100644 index 00000000..ee275ef5 --- /dev/null +++ b/tests/test_safe_input_ready_to_make_pr.py @@ -0,0 +1,370 @@ +"""Tests for the ready-to-make-pr safe-input Python scripts. + +Extracts the `py:` blocks from safe-output-push-to-pr.md and +safe-output-create-pr.md, runs them in controlled git environments, +and validates the JSON output and side effects. +""" + +import json +import os +import subprocess +import textwrap +from pathlib import Path + +import pytest +import yaml + +FRAGMENTS_DIR = ( + Path(__file__).resolve().parent.parent + / ".github" + / "workflows" + / "gh-aw-fragments" +) + +PUSH_FRAGMENT = FRAGMENTS_DIR / "safe-output-push-to-pr.md" +CREATE_FRAGMENT = FRAGMENTS_DIR / "safe-output-create-pr.md" + + +def extract_py_block(fragment_path: Path) -> str: + """Extract the py: block from a safe-input fragment's YAML frontmatter.""" + text = fragment_path.read_text() + # Strip leading/trailing --- to get YAML frontmatter + parts = text.split("---", 2) + assert len(parts) >= 3, f"Expected YAML frontmatter in {fragment_path}" + frontmatter = yaml.safe_load(parts[1]) + py_code = frontmatter["safe-inputs"]["ready-to-make-pr"]["py"] + assert py_code, f"No py: block found in {fragment_path}" + return py_code + + +def run_py_in_repo(py_code: str, repo_dir: str) -> dict: + """Run extracted Python code in a git repo and return parsed JSON output.""" + result = subprocess.run( + ["python3", "-c", py_code], + capture_output=True, + text=True, + cwd=repo_dir, + timeout=30, + ) + assert result.returncode == 0, ( + f"Script failed (rc={result.returncode}):\n" + f"stdout: {result.stdout}\n" + f"stderr: {result.stderr}" + ) + output = result.stdout.strip() + assert output, "Script produced no output" + return json.loads(output) + + +def make_git_repo(tmp_path: Path, *, with_upstream: bool = False) -> Path: + """Create a minimal git repo. Optionally set up a remote upstream.""" + repo = tmp_path / "repo" + repo.mkdir() + + def git(*args): + subprocess.run( + ["git"] + list(args), + cwd=str(repo), + capture_output=True, + check=True, + ) + + git("init", "-b", "main") + git("config", "user.email", "test@test.com") + git("config", "user.name", "Test") + + # Initial commit + (repo / "file.txt").write_text("hello\n") + git("add", "file.txt") + git("commit", "-m", "init") + + if with_upstream: + # Create a bare remote and push + remote = tmp_path / "remote.git" + subprocess.run( + ["git", "clone", "--bare", str(repo), str(remote)], + capture_output=True, + check=True, + ) + git("remote", "add", "origin", str(remote)) + git("fetch", "origin") + git("branch", "--set-upstream-to", "origin/main", "main") + + return repo + + +# --------------------------------------------------------------------------- +# Extraction tests +# --------------------------------------------------------------------------- + + +class TestExtraction: + """Verify we can extract valid Python from both fragments.""" + + def test_push_fragment_exists(self): + assert PUSH_FRAGMENT.exists() + + def test_create_fragment_exists(self): + assert CREATE_FRAGMENT.exists() + + def test_push_extract(self): + code = extract_py_block(PUSH_FRAGMENT) + assert "import" in code + assert "json.dumps" in code + + def test_create_extract(self): + code = extract_py_block(CREATE_FRAGMENT) + assert "import" in code + assert "json.dumps" in code + + def test_fragments_have_identical_py(self): + """The two fragments should have identical Python logic.""" + push_code = extract_py_block(PUSH_FRAGMENT) + create_code = extract_py_block(CREATE_FRAGMENT) + assert push_code == create_code + + +# --------------------------------------------------------------------------- +# Output schema tests +# --------------------------------------------------------------------------- + + +class TestOutputSchema: + """Validate JSON output structure across scenarios.""" + + @pytest.fixture + def py_code(self): + return extract_py_block(PUSH_FRAGMENT) + + def test_basic_output_fields(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + output = run_py_in_repo(py_code, str(repo)) + + assert output["status"] == "ok" + assert isinstance(output["checklist"], list) + assert isinstance(output["diff_line_count"], int) + assert "contributing_guide" in output + assert "pr_template" in output + + def test_checklist_always_has_validation_item(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + output = run_py_in_repo(py_code, str(repo)) + + checklist_text = " ".join(output["checklist"]) + assert "fully completed and validated" in checklist_text + + +# --------------------------------------------------------------------------- +# Git diff fallback tests +# --------------------------------------------------------------------------- + + +class TestDiffFallbacks: + """Test the 3-step diff fallback chain.""" + + @pytest.fixture + def py_code(self): + return extract_py_block(PUSH_FRAGMENT) + + def test_with_upstream_uncommitted_changes(self, py_code, tmp_path): + """--merge-base @{upstream} should capture uncommitted changes.""" + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / "file.txt").write_text("hello\nworld\n") + + output = run_py_in_repo(py_code, str(repo)) + assert output["diff_line_count"] > 0 + + diff_file = Path("/tmp/self-review/diff.patch") + assert diff_file.exists() + assert "world" in diff_file.read_text() + + def test_with_upstream_committed_changes(self, py_code, tmp_path): + """--merge-base @{upstream} should capture committed unpushed changes.""" + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / "new_file.txt").write_text("new content\n") + subprocess.run( + ["git", "add", "new_file.txt"], + cwd=str(repo), + capture_output=True, + check=True, + ) + subprocess.run( + ["git", "commit", "-m", "add new file"], + cwd=str(repo), + capture_output=True, + check=True, + ) + + output = run_py_in_repo(py_code, str(repo)) + assert output["diff_line_count"] > 0 + assert "new content" in Path("/tmp/self-review/diff.patch").read_text() + + def test_no_upstream_uncommitted_changes(self, py_code, tmp_path): + """Falls back to git diff HEAD when no upstream is configured.""" + repo = make_git_repo(tmp_path, with_upstream=False) + (repo / "file.txt").write_text("hello\nchanged\n") + + output = run_py_in_repo(py_code, str(repo)) + assert output["diff_line_count"] > 0 + assert "changed" in Path("/tmp/self-review/diff.patch").read_text() + + def test_no_upstream_committed_changes(self, py_code, tmp_path): + """Falls back through chain; git diff @{upstream} (2-dot) also fails, + git diff HEAD shows nothing since changes are committed. diff_line_count is 0.""" + repo = make_git_repo(tmp_path, with_upstream=False) + (repo / "extra.txt").write_text("extra\n") + subprocess.run( + ["git", "add", "extra.txt"], + cwd=str(repo), + capture_output=True, + check=True, + ) + subprocess.run( + ["git", "commit", "-m", "add extra"], + cwd=str(repo), + capture_output=True, + check=True, + ) + + output = run_py_in_repo(py_code, str(repo)) + # No upstream and no uncommitted changes — diff is empty + assert output["diff_line_count"] == 0 + + def test_no_changes_at_all(self, py_code, tmp_path): + """No changes yields empty diff and no self-review checklist item.""" + repo = make_git_repo(tmp_path, with_upstream=True) + + output = run_py_in_repo(py_code, str(repo)) + assert output["diff_line_count"] == 0 + + checklist_text = " ".join(output["checklist"]) + assert "self-review" not in checklist_text + + +# --------------------------------------------------------------------------- +# File output tests +# --------------------------------------------------------------------------- + + +class TestFileOutput: + """Verify files are created with expected content.""" + + @pytest.fixture + def py_code(self): + return extract_py_block(PUSH_FRAGMENT) + + def test_diff_patch_created(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / "file.txt").write_text("modified\n") + + run_py_in_repo(py_code, str(repo)) + + assert Path("/tmp/self-review/diff.patch").exists() + assert Path("/tmp/self-review/stat.txt").exists() + + def test_stat_matches_diff(self, py_code, tmp_path): + """stat.txt should reference the same files as the diff.""" + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / "file.txt").write_text("modified\n") + + run_py_in_repo(py_code, str(repo)) + + stat = Path("/tmp/self-review/stat.txt").read_text() + assert "file.txt" in stat + + def test_empty_diff_writes_empty_files(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + + run_py_in_repo(py_code, str(repo)) + + assert Path("/tmp/self-review/diff.patch").read_text() == "" + assert Path("/tmp/self-review/stat.txt").read_text() == "" + + +# --------------------------------------------------------------------------- +# Contributing / PR template detection +# --------------------------------------------------------------------------- + + +class TestFileDetection: + """Test detection of CONTRIBUTING.md and PR template files.""" + + @pytest.fixture + def py_code(self): + return extract_py_block(PUSH_FRAGMENT) + + def test_finds_contributing(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / "CONTRIBUTING.md").write_text("# Contributing\n") + + output = run_py_in_repo(py_code, str(repo)) + assert output["contributing_guide"] == "CONTRIBUTING.md" + assert any("contributing guide" in c.lower() for c in output["checklist"]) + + def test_finds_nested_contributing(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / "docs").mkdir() + (repo / "docs" / "CONTRIBUTING.md").write_text("# Contributing\n") + + output = run_py_in_repo(py_code, str(repo)) + assert output["contributing_guide"] == "docs/CONTRIBUTING.md" + + def test_no_contributing(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + + output = run_py_in_repo(py_code, str(repo)) + assert output["contributing_guide"] is None + assert not any("contributing guide" in c.lower() for c in output["checklist"]) + + def test_finds_pr_template(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / ".github").mkdir() + (repo / ".github" / "pull_request_template.md").write_text("## PR\n") + + output = run_py_in_repo(py_code, str(repo)) + assert output["pr_template"] == ".github/pull_request_template.md" + + def test_no_pr_template(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + + output = run_py_in_repo(py_code, str(repo)) + assert output["pr_template"] is None + + +# --------------------------------------------------------------------------- +# Self-review checklist gating +# --------------------------------------------------------------------------- + + +class TestSelfReviewGating: + """The self-review checklist item should only appear when there's a diff.""" + + @pytest.fixture + def py_code(self): + return extract_py_block(PUSH_FRAGMENT) + + def test_self_review_present_when_diff(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / "file.txt").write_text("changed\n") + + output = run_py_in_repo(py_code, str(repo)) + checklist_text = " ".join(output["checklist"]) + assert "self-review" in checklist_text + assert "diff.patch" in checklist_text + + def test_self_review_absent_when_no_diff(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + + output = run_py_in_repo(py_code, str(repo)) + checklist_text = " ".join(output["checklist"]) + assert "self-review" not in checklist_text + + def test_diff_line_count_in_checklist(self, py_code, tmp_path): + repo = make_git_repo(tmp_path, with_upstream=True) + (repo / "file.txt").write_text("line1\nline2\nline3\n") + + output = run_py_in_repo(py_code, str(repo)) + count = output["diff_line_count"] + assert count > 0 + # The line count should appear in the checklist text + assert f"({count} lines)" in " ".join(output["checklist"]) diff --git a/uv.lock b/uv.lock index 08b56bdb..efa03341 100644 --- a/uv.lock +++ b/uv.lock @@ -11,11 +11,20 @@ dependencies = [ { name = "mkdocs-material" }, ] +[package.optional-dependencies] +test = [ + { name = "pytest" }, + { name = "pyyaml" }, +] + [package.metadata] requires-dist = [ { name = "mkdocs", specifier = ">=1.6.0,<2.0" }, { name = "mkdocs-material", specifier = ">=9.5.0" }, + { name = "pytest", marker = "extra == 'test'", specifier = ">=8.0" }, + { name = "pyyaml", marker = "extra == 'test'", specifier = ">=6.0" }, ] +provides-extras = ["test"] [[package]] name = "babel" @@ -159,6 +168,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + [[package]] name = "ghp-import" version = "2.1.0" @@ -180,6 +201,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + [[package]] name = "jinja2" version = "3.1.6" @@ -400,6 +430,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168, upload-time = "2026-02-16T03:56:08.891Z" }, ] +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -422,6 +461,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/2c/5b079febdc65e1c3fb2729bf958d18b45be7113828528e8a0b5850dd819a/pymdown_extensions-10.21-py3-none-any.whl", hash = "sha256:91b879f9f864d49794c2d9534372b10150e6141096c3908a455e45ca72ad9d3f", size = 268877, upload-time = "2026-02-15T20:44:05.464Z" }, ] +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -534,6 +591,69 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + [[package]] name = "urllib3" version = "2.6.3"