From 7f13b5bc9a014b3a1f0abfe200a831af893fc534 Mon Sep 17 00:00:00 2001 From: William Easton Date: Mon, 2 Mar 2026 01:22:10 -0600 Subject: [PATCH 1/3] ready-to-code-review: write review files to disk, offload playwright + criteria - Add safe-output-code-review.md: new ready_to_code_review safe-input tool that writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/ based on PR size. Returns file paths in JSON output. - Move review criteria out of prompts: review-instructions.md written by bash step (review-process.md), parent-review.md written by the tool using GH_AW_INPUTS_MINIMUM_SEVERITY env var. Prompt text trimmed to a 2-line pointer. - Playwright instructions to disk: playwright-mcp-explorer.md now writes /tmp/playwright-instructions.md via bash step; prompt reduced to one line. - self-review uses review-instructions.md: safe-output-create-pr.md conditionally adds review-instructions.md to the self-review sub-agent README when available. Both mention-in-issue variants import review-process.md. - Review workflow ordering fixes: pr-review gathers full context before calling ready_to_code_review; mention-in-pr reads reviews.json in Step 1. - All 4 PR workflows use ready_to_code_review + Pick Three Keep Many pattern: pr-review, mention-in-pr, mention-in-pr-by-id, mention-in-pr-no-sandbox. - Remove generate_agents_md MCP tool: elastic-tools.md no longer registers the agents-md-generator MCP server; curl call in runtime-setup.md remains. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/aw/actions-lock.json | 5 + .github/workflows/agent-deep-dive.lock.yml | 37 +- .github/workflows/agent-efficiency.lock.yml | 37 +- .github/workflows/agentics-maintenance.yml | 2 +- .github/workflows/downstream-users.lock.yml | 49 +- .github/workflows/downstream-users.md | 5 +- .../gh-aw-agent-suggestions.lock.yml | 31 +- ...gh-aw-autonomy-atomicity-analyzer.lock.yml | 31 +- .../gh-aw-branch-actions-detective.lock.yml | 32 +- .../gh-aw-branch-actions-detective.md | 3 +- .../gh-aw-breaking-change-detect.lock.yml | 37 +- .../gh-aw-breaking-change-detector.lock.yml | 37 +- .../workflows/gh-aw-bug-exterminator.lock.yml | 40 +- .github/workflows/gh-aw-bug-hunter.lock.yml | 31 +- .../gh-aw-code-duplication-detector.lock.yml | 37 +- .../gh-aw-code-duplication-fixer.lock.yml | 40 +- .../workflows/gh-aw-code-simplifier.lock.yml | 43 +- .github/workflows/gh-aw-code-simplifier.md | 5 +- .../gh-aw-dependency-review.lock.yml | 44 +- .github/workflows/gh-aw-dependency-review.md | 15 +- .github/workflows/gh-aw-docs-drift.lock.yml | 37 +- .github/workflows/gh-aw-docs-patrol.lock.yml | 37 +- .../gh-aw-duplicate-issue-detector.lock.yml | 28 +- ...-resource-not-accessible-detector.lock.yml | 34 +- ...ctions-resource-not-accessible-detector.md | 5 +- .../gh-aw-estc-docs-patrol-external.lock.yml | 31 +- .../gh-aw-estc-docs-pr-review.lock.yml | 32 +- .../workflows/gh-aw-estc-docs-pr-review.md | 3 +- .../gh-aw-estc-downstream-health.lock.yml | 31 +- ...ewbie-contributor-patrol-external.lock.yml | 31 +- ...gh-aw-estc-pr-buildkite-detective.lock.yml | 38 +- .../gh-aw-estc-pr-buildkite-detective.md | 9 +- .../gh-aw-flaky-test-investigator.lock.yml | 31 +- .../gh-aw-fragments/elastic-tools.md | 5 - .../gh-aw-fragments/network-ecosystems.md | 2 + .../gh-aw-fragments/pick-three-keep-many.md | 6 +- .../playwright-mcp-explorer.md | 92 +- .../workflows/gh-aw-fragments/pr-context.md | 9 +- .../gh-aw-fragments/review-process.md | 89 +- .../gh-aw-fragments/runtime-setup.md | 28 + .../safe-output-code-review.md | 116 + .../gh-aw-fragments/safe-output-create-pr.md | 5 + .../gh-aw-fragments/scheduled-audit.md | 2 +- .../gh-aw-fragments/scheduled-fix.md | 2 +- .../gh-aw-framework-best-practices.lock.yml | 37 +- .../gh-aw-information-architecture.lock.yml | 31 +- .../gh-aw-internal-gemini-cli-web-search.md | 7 +- .../workflows/gh-aw-internal-gemini-cli.md | 7 +- .github/workflows/gh-aw-issue-fixer.lock.yml | 43 +- .github/workflows/gh-aw-issue-fixer.md | 5 +- .github/workflows/gh-aw-issue-triage.lock.yml | 91 +- .github/workflows/gh-aw-issue-triage.md | 5 +- ...gh-aw-mention-in-issue-no-sandbox.lock.yml | 110 +- .../gh-aw-mention-in-issue-no-sandbox.md | 8 +- .../workflows/gh-aw-mention-in-issue.lock.yml | 110 +- .github/workflows/gh-aw-mention-in-issue.md | 8 +- .../gh-aw-mention-in-pr-by-id.invalid.yml | 2127 ++++++++++++++++ .../gh-aw-mention-in-pr-by-id.lock.yml | 363 +-- .../workflows/gh-aw-mention-in-pr-by-id.md | 25 +- ...gh-aw-mention-in-pr-no-sandbox.invalid.yml | 2097 +++++++++++++++ .../gh-aw-mention-in-pr-no-sandbox.lock.yml | 364 +-- .../gh-aw-mention-in-pr-no-sandbox.md | 28 +- .../workflows/gh-aw-mention-in-pr.invalid.yml | 2256 +++++++++++++++++ .../workflows/gh-aw-mention-in-pr.lock.yml | 362 +-- .github/workflows/gh-aw-mention-in-pr.md | 26 +- .../gh-aw-newbie-contributor-fixer.lock.yml | 40 +- .../gh-aw-newbie-contributor-patrol.lock.yml | 31 +- .../gh-aw-performance-profiler.lock.yml | 31 +- .github/workflows/gh-aw-plan.lock.yml | 36 +- .github/workflows/gh-aw-plan.md | 7 +- .../gh-aw-pr-actions-detective.lock.yml | 36 +- .../workflows/gh-aw-pr-actions-detective.md | 7 +- .../workflows/gh-aw-pr-actions-fixer.lock.yml | 32 +- .github/workflows/gh-aw-pr-actions-fixer.md | 3 +- .../workflows/gh-aw-pr-ci-detective.lock.yml | 36 +- .../gh-aw-pr-review-addresser.lock.yml | 40 +- .../workflows/gh-aw-pr-review-addresser.md | 9 +- .github/workflows/gh-aw-pr-review.invalid.yml | 1713 +++++++++++++ .github/workflows/gh-aw-pr-review.lock.yml | 385 ++- .github/workflows/gh-aw-pr-review.md | 26 +- ...h-aw-product-manager-impersonator.lock.yml | 31 +- .../workflows/gh-aw-project-summary.lock.yml | 31 +- .../gh-aw-refactor-opportunist.lock.yml | 31 +- .../workflows/gh-aw-release-update.lock.yml | 45 +- .github/workflows/gh-aw-release-update.md | 7 +- .../workflows/gh-aw-scheduled-audit.lock.yml | 88 +- .../workflows/gh-aw-scheduled-fix.lock.yml | 40 +- .../gh-aw-small-problem-fixer.lock.yml | 41 +- .../workflows/gh-aw-small-problem-fixer.md | 3 +- .../gh-aw-stale-issues-investigator.lock.yml | 31 +- .../gh-aw-stale-issues-remediator.lock.yml | 29 +- .github/workflows/gh-aw-stale-issues.lock.yml | 31 +- .../gh-aw-test-coverage-detector.lock.yml | 31 +- .../workflows/gh-aw-test-improvement.lock.yml | 43 +- .../workflows/gh-aw-test-improver.lock.yml | 43 +- .github/workflows/gh-aw-test-improver.md | 5 +- .github/workflows/gh-aw-text-auditor.lock.yml | 37 +- .../workflows/gh-aw-text-beautifier.lock.yml | 40 +- .../workflows/gh-aw-update-pr-body.lock.yml | 36 +- .github/workflows/gh-aw-update-pr-body.md | 7 +- .../workflows/gh-aw-ux-design-patrol.lock.yml | 31 +- .github/workflows/upgrade-check.lock.yml | 29 +- .github/workflows/workflow-patrol.lock.yml | 29 +- Makefile | 2 +- 104 files changed, 10417 insertions(+), 2130 deletions(-) create mode 100644 .github/workflows/gh-aw-fragments/safe-output-code-review.md create mode 100644 .github/workflows/gh-aw-mention-in-pr-by-id.invalid.yml create mode 100644 .github/workflows/gh-aw-mention-in-pr-no-sandbox.invalid.yml create mode 100644 .github/workflows/gh-aw-mention-in-pr.invalid.yml create mode 100644 .github/workflows/gh-aw-pr-review.invalid.yml diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index 61faabf5..9f9f3015 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -109,6 +109,11 @@ "repo": "github/gh-aw/actions/setup", "version": "v0.51.3", "sha": "5fa65dd15a71cec00f2d14c664467154d343d875" + }, + "github/gh-aw/actions/setup@v0.51.5": { + "repo": "github/gh-aw/actions/setup", + "version": "v0.51.5", + "sha": "88319be75ab1adc60640307a10e5cf04b3deff1e" } } } diff --git a/.github/workflows/agent-deep-dive.lock.yml b/.github/workflows/agent-deep-dive.lock.yml index d1597832..2e09ac44 100644 --- a/.github/workflows/agent-deep-dive.lock.yml +++ b/.github/workflows/agent-deep-dive.lock.yml @@ -35,7 +35,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1c561aa18aab76d8510e430d5a5a4c1ce22a1cf0d66c32b8353ea5b10e9e2b28"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"07b88cf85bf97107f38c28470228888eeb00f69acfdb3b558ab218f8193a843f"} name: "Internal: Agent Deep Dive" "on": @@ -79,7 +79,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -94,7 +94,7 @@ jobs: GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","github","go","node","public-code-search.fastmcp.app","python","ruby","www.elastic.co"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","github","go","node","public-code-search.fastmcp.app","python","ruby","www.elastic.co"]' GH_AW_INFO_FIREWALL_ENABLED: "true" GH_AW_INFO_AWF_VERSION: "v0.23.0" GH_AW_INFO_AWMG_VERSION: "" @@ -192,11 +192,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -279,7 +278,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -527,7 +526,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -581,7 +580,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -916,13 +920,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -970,7 +967,7 @@ jobs: run: | set -o pipefail # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE @@ -1053,7 +1050,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1258,7 +1255,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1360,7 +1357,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1379,7 +1376,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"close_older_issues\":false,\"expires\":336,\"max\":1,\"title_prefix\":\"[agent-deep-dive] \"},\"missing_data\":{},\"missing_tool\":{}}" diff --git a/.github/workflows/agent-efficiency.lock.yml b/.github/workflows/agent-efficiency.lock.yml index 794b669c..6a6c9786 100644 --- a/.github/workflows/agent-efficiency.lock.yml +++ b/.github/workflows/agent-efficiency.lock.yml @@ -35,7 +35,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0606b309d725017eaa1dcb04ed7c74c15a310bc0ff93471670c43e9a2d6527e3"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3ba955a0c4b49e913b4f3daa3726a2601e75e4e992a54b2be07d9cde381853c1"} name: "Internal: Agent Efficiency" "on": @@ -70,7 +70,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -85,7 +85,7 @@ jobs: GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","github","go","node","public-code-search.fastmcp.app","python","ruby","www.elastic.co"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","github","go","node","public-code-search.fastmcp.app","python","ruby","www.elastic.co"]' GH_AW_INFO_FIREWALL_ENABLED: "true" GH_AW_INFO_AWF_VERSION: "v0.23.0" GH_AW_INFO_AWMG_VERSION: "" @@ -181,11 +181,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -268,7 +267,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -493,7 +492,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -547,7 +546,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -880,13 +884,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -934,7 +931,7 @@ jobs: run: | set -o pipefail # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE @@ -1017,7 +1014,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1222,7 +1219,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1324,7 +1321,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1343,7 +1340,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"close_older_issues\":false,\"expires\":168,\"max\":1,\"title_prefix\":\"[agent-efficiency] \"},\"missing_data\":{},\"missing_tool\":{}}" diff --git a/.github/workflows/agentics-maintenance.yml b/.github/workflows/agentics-maintenance.yml index f5713f14..f36e2f5d 100644 --- a/.github/workflows/agentics-maintenance.yml +++ b/.github/workflows/agentics-maintenance.yml @@ -50,7 +50,7 @@ jobs: pull-requests: write steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions diff --git a/.github/workflows/downstream-users.lock.yml b/.github/workflows/downstream-users.lock.yml index e56f9926..c261ce64 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":"ee75a9344e26e7442384ab60e7fb442a6fd9c16970e0f3c871392bcc413eec8b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"dde4780fa1d66105697bff729f28749d24358056ab99e895581ac3d14120ebc0"} name: "Internal: Downstream Users" "on": @@ -71,7 +71,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -86,7 +86,7 @@ jobs: GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","github","go","node","public-code-search.fastmcp.app","python","ruby","www.elastic.co"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","github","go","node","public-code-search.fastmcp.app","python","ruby","www.elastic.co"]' GH_AW_INFO_FIREWALL_ENABLED: "true" GH_AW_INFO_AWF_VERSION: "v0.23.0" GH_AW_INFO_AWMG_VERSION: "" @@ -184,11 +184,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -294,8 +293,7 @@ jobs: ## Step 1: Gather Context - 1. Call `generate_agents_md` to get repository conventions (if it fails, continue). - 2. Read the current `data/downstream-users.json` file (if it exists). + 1. Read the current `data/downstream-users.json` file (if it exists). ## Step 2: Discover Downstream Usage @@ -306,7 +304,7 @@ jobs: - `include_globs`: `**/*.yml`, `**/*.yaml` - Exclude `elastic/ai-github-actions` itself. - 2. For each unique repo + path pair returned: + 1. For each unique repo + path pair returned: - Fetch the workflow file using `github-get_file_contents`. - Extract every `uses: elastic/ai-github-actions/...` line. - Normalize each entry by removing the leading `elastic/ai-github-actions/` and any `@version` suffix. @@ -441,7 +439,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -495,7 +493,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - name: Configure Git credentials @@ -574,6 +577,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -949,6 +956,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1040,13 +1052,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1101,7 +1106,7 @@ jobs: run: | set -o pipefail # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE @@ -1184,7 +1189,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1401,7 +1406,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1524,7 +1529,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1571,7 +1576,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,agents-md-generator.fastmcp.app,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,*.rvm.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.rubygems.org,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,bundler.rubygems.org,cache.ruby-lang.org,cdn.jsdelivr.net,cloud.elastic.co,codeload.github.com,conda.anaconda.org,conda.binstar.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,deb.nodesource.com,deno.land,ela.st,elastic.co,esm.sh,files.pythonhosted.org,gems.rubyforge.org,gems.rubyonrails.org,get.pnpm.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,host.docker.internal,index.crates.io,index.rubygems.org,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,ppa.launchpad.net,proxy.golang.org,public-code-search.fastmcp.app,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,rubygems.org,rubygems.pkg.github.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,static.crates.io,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co,www.npmjs.com,www.npmjs.org,yarnpkg.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"draft\":false,\"max\":1,\"max_patch_size\":10240},\"missing_data\":{},\"missing_tool\":{}}" diff --git a/.github/workflows/downstream-users.md b/.github/workflows/downstream-users.md index 26813596..c444d584 100644 --- a/.github/workflows/downstream-users.md +++ b/.github/workflows/downstream-users.md @@ -69,8 +69,7 @@ Maintain a canonical list of public downstream repositories using elastic/ai-git ## Step 1: Gather Context -1. Call `generate_agents_md` to get repository conventions (if it fails, continue). -2. Read the current `data/downstream-users.json` file (if it exists). +1. Read the current `data/downstream-users.json` file (if it exists). ## Step 2: Discover Downstream Usage @@ -81,7 +80,7 @@ Maintain a canonical list of public downstream repositories using elastic/ai-git - `include_globs`: `**/*.yml`, `**/*.yaml` - Exclude `elastic/ai-github-actions` itself. -2. For each unique repo + path pair returned: +1. For each unique repo + path pair returned: - Fetch the workflow file using `github-get_file_contents`. - Extract every `uses: elastic/ai-github-actions/...` line. - Normalize each entry by removing the leading `elastic/ai-github-actions/` and any `@version` suffix. diff --git a/.github/workflows/gh-aw-agent-suggestions.lock.yml b/.github/workflows/gh-aw-agent-suggestions.lock.yml index 548dee85..4a6941ef 100644 --- a/.github/workflows/gh-aw-agent-suggestions.lock.yml +++ b/.github/workflows/gh-aw-agent-suggestions.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"36c30bad301c81f613b6630b83797c507a9f332b7b1bf305380cba9a4fd5e92b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"32057cb0d36155ffdd95a27d5d54cbea2107056c42ceb306aa4e2d11c66412d3"} name: "Agent Suggestions" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -344,7 +343,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -572,7 +571,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -626,7 +625,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -965,13 +969,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1307,7 +1304,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1393,7 +1390,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1435,7 +1432,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-autonomy-atomicity-analyzer.lock.yml b/.github/workflows/gh-aw-autonomy-atomicity-analyzer.lock.yml index 6d399bd8..545ee216 100644 --- a/.github/workflows/gh-aw-autonomy-atomicity-analyzer.lock.yml +++ b/.github/workflows/gh-aw-autonomy-atomicity-analyzer.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0aabffb6ce355881ccfe9aacb7a874d249d59d28b16bc5b5935be97bb22612d3"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"ab2dd6e2958369920af5375d19fe9e283c94de2651c0f1b6ab6fc4eee4267b7a"} name: "Autonomy Atomicity Analyzer" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -344,7 +343,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -576,7 +575,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -630,7 +629,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -969,13 +973,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1311,7 +1308,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1397,7 +1394,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1439,7 +1436,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-branch-actions-detective.lock.yml b/.github/workflows/gh-aw-branch-actions-detective.lock.yml index 3712a2b9..066dc1bf 100644 --- a/.github/workflows/gh-aw-branch-actions-detective.lock.yml +++ b/.github/workflows/gh-aw-branch-actions-detective.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"12d9f4aa55de5b7038e979965cd3cfd405adac4cac8af9a3f8bf86669e39ba7f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"307b60938604aa0576e0cd17cfbdf6124c49d0862d73c5ed4d614cfa2f1832b8"} name: "Branch Actions Detective" "on": @@ -106,7 +106,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -333,8 +332,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Fetch workflow run details and logs with `bash` + `gh api`: + 1. Fetch workflow run details and logs with `bash` + `gh api`: - List jobs and their conclusions: ````bash gh api repos/__GH_AW_GITHUB_REPOSITORY__/actions/runs/{run_id}/jobs \ @@ -500,7 +498,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -554,7 +552,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -893,13 +896,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1235,7 +1231,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1321,7 +1317,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1363,7 +1359,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-branch-actions-detective.md b/.github/workflows/gh-aw-branch-actions-detective.md index 70f3a25e..aa41ec2a 100644 --- a/.github/workflows/gh-aw-branch-actions-detective.md +++ b/.github/workflows/gh-aw-branch-actions-detective.md @@ -103,8 +103,7 @@ Analyze failed GitHub Actions CI runs on protected branches (e.g. `main`) in ${{ ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Fetch workflow run details and logs with `bash` + `gh api`: +1. Fetch workflow run details and logs with `bash` + `gh api`: - List jobs and their conclusions: ````bash gh api repos/${{ github.repository }}/actions/runs/{run_id}/jobs \ diff --git a/.github/workflows/gh-aw-breaking-change-detect.lock.yml b/.github/workflows/gh-aw-breaking-change-detect.lock.yml index cf5921b7..f54f1502 100644 --- a/.github/workflows/gh-aw-breaking-change-detect.lock.yml +++ b/.github/workflows/gh-aw-breaking-change-detect.lock.yml @@ -45,7 +45,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1df9f4a24642271ad297613f3b1a9eff3d69f09cfacaf69d4ccded904c93c208"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"736eef920b526a4d33cef3e8519505751f428c46a5950922746eb62a86a67941"} name: "Breaking Change Detector" "on": @@ -114,7 +114,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -226,11 +226,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -320,19 +319,19 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ### Pick Three, Keep Many - Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - **Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. @@ -355,7 +354,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -583,7 +582,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -637,7 +636,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -982,13 +986,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1324,7 +1321,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1410,7 +1407,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1452,7 +1449,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-breaking-change-detector.lock.yml b/.github/workflows/gh-aw-breaking-change-detector.lock.yml index 1960c8b5..27427616 100644 --- a/.github/workflows/gh-aw-breaking-change-detector.lock.yml +++ b/.github/workflows/gh-aw-breaking-change-detector.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1df9f4a24642271ad297613f3b1a9eff3d69f09cfacaf69d4ccded904c93c208"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"736eef920b526a4d33cef3e8519505751f428c46a5950922746eb62a86a67941"} name: "Breaking Change Detector" "on": @@ -109,7 +109,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -315,19 +314,19 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ### Pick Three, Keep Many - Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - **Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. @@ -350,7 +349,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -578,7 +577,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -632,7 +631,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -977,13 +981,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1319,7 +1316,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1405,7 +1402,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1447,7 +1444,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-bug-exterminator.lock.yml b/.github/workflows/gh-aw-bug-exterminator.lock.yml index 12009be1..35b00f19 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":"dc50f9e7bea5d0b9f01420b5a891599052e4bc1c16a5eb1cff61b0a186aa64f8"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"89c201c7946f8a6a3578c536620df5c83c808a86ab5f499e58584ae1595c22bd"} name: "Gh Aw Bug Exterminator" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -222,11 +222,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -322,7 +321,7 @@ jobs: ### Step 1: Gather Candidates - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the candidate gathering instructions in the **Fix Assignment** section. 3. For each candidate, read the full issue and comments using `issue_read` (methods `get` and `get_comments`). @@ -486,7 +485,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -540,7 +539,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -624,6 +628,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -999,6 +1007,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1090,13 +1103,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1451,7 +1457,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1553,7 +1559,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1598,7 +1604,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-bug-hunter.lock.yml b/.github/workflows/gh-aw-bug-hunter.lock.yml index 8838eb60..d05c4e63 100644 --- a/.github/workflows/gh-aw-bug-hunter.lock.yml +++ b/.github/workflows/gh-aw-bug-hunter.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b7cf842a4d9f8ec356e887fe15510a6c84a2025145873005fc52e48c7e3117a0"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0515989f3bb561448c6aa1002d4071e19dd3fbfcf9a6a1b5ff19dbedd3f91ced"} name: "Bug Hunter" "on": @@ -109,7 +109,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -222,11 +222,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -348,7 +347,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -573,7 +572,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -627,7 +626,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -972,13 +976,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1314,7 +1311,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1400,7 +1397,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1442,7 +1439,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-code-duplication-detector.lock.yml b/.github/workflows/gh-aw-code-duplication-detector.lock.yml index 9a183ce2..f8972878 100644 --- a/.github/workflows/gh-aw-code-duplication-detector.lock.yml +++ b/.github/workflows/gh-aw-code-duplication-detector.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c5e5a35f457d46094b82c0ffa3164337ed6bdae6472b7f4a2d800ef8eb4fb916"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e3598f55ba687f3ee0c16ac6eaac3d8ed26f7883bfd797084ef0c196e47b30e7"} name: "Code Duplication Detector" "on": @@ -118,7 +118,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -232,11 +232,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -323,19 +322,19 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ### Pick Three, Keep Many - Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - **Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. @@ -358,7 +357,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -618,7 +617,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -672,7 +671,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -1011,13 +1015,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1361,7 +1358,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1447,7 +1444,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1489,7 +1486,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-code-duplication-fixer.lock.yml b/.github/workflows/gh-aw-code-duplication-fixer.lock.yml index 492bad62..ba3252bd 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":"cb102672858f29b7a9b0ade7b42d095aa6fd7d8e79c66f2e0a763334831b1280"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6ce6f1c21fc5eefc1b1293feee2b2d3721a3ff3e471454d044ff6a7476f890d7"} name: "Code Duplication Fixer" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -222,11 +222,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -322,7 +321,7 @@ jobs: ### Step 1: Gather Candidates - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the candidate gathering instructions in the **Fix Assignment** section. 3. For each candidate, read the full issue and comments using `issue_read` (methods `get` and `get_comments`). @@ -488,7 +487,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -542,7 +541,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -626,6 +630,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1001,6 +1009,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1092,13 +1105,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1461,7 +1467,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1563,7 +1569,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1608,7 +1614,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-code-simplifier.lock.yml b/.github/workflows/gh-aw-code-simplifier.lock.yml index d149789f..88918e04 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":"e27c7544fe4d4cf523966cbd290d1b205fb7d7654a37d2f43f05e6bfcd1d6e80"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0e8a154c9a37d845f6c15bd42be9d01457a0127b7f112e3f1f6e63e3ecf89078"} name: "Code Simplifier" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -223,11 +223,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -364,9 +363,8 @@ jobs: ## Step 1: Find candidates - 1. Call `generate_agents_md` to get repository conventions (if it fails, continue). - 2. Use the **Pick Three, Keep One** pattern for the candidate search: spawn 3 `general-purpose` sub-agents, each searching for simplification opportunities from a different angle (e.g., different complexity metrics such as cyclomatic complexity vs. nesting depth vs. function length, different modules or directories, different simplification types such as dead code removal vs. helper reuse vs. control flow flattening). Include the repo conventions, the full "Bar for merit" criteria, and the "Constraints" in each sub-agent prompt. Each sub-agent should return its best candidate simplification with file paths and evidence or recommend `noop`. - 3. Use search and file reading to identify overcomplicated code: + 1. Use the **Pick Three, Keep One** pattern for the candidate search: spawn 3 `general-purpose` sub-agents, each searching for simplification opportunities from a different angle (e.g., different complexity metrics such as cyclomatic complexity vs. nesting depth vs. function length, different modules or directories, different simplification types such as dead code removal vs. helper reuse vs. control flow flattening). Include the repo conventions, the full "Bar for merit" criteria, and the "Constraints" in each sub-agent prompt. Each sub-agent should return its best candidate simplification with file paths and evidence or recommend `noop`. + 2. Use search and file reading to identify overcomplicated code: - deep nesting - redundant conditionals - duplicated logic @@ -503,7 +501,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -557,7 +555,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -641,6 +644,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1016,6 +1023,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1107,13 +1119,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1468,7 +1473,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1570,7 +1575,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1615,7 +1620,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-code-simplifier.md b/.github/workflows/gh-aw-code-simplifier.md index 10be0308..8b09c161 100644 --- a/.github/workflows/gh-aw-code-simplifier.md +++ b/.github/workflows/gh-aw-code-simplifier.md @@ -114,9 +114,8 @@ A simplification must clear at least one of these bars to be worth submitting: ## Step 1: Find candidates -1. Call `generate_agents_md` to get repository conventions (if it fails, continue). -2. Use the **Pick Three, Keep One** pattern for the candidate search: spawn 3 `general-purpose` sub-agents, each searching for simplification opportunities from a different angle (e.g., different complexity metrics such as cyclomatic complexity vs. nesting depth vs. function length, different modules or directories, different simplification types such as dead code removal vs. helper reuse vs. control flow flattening). Include the repo conventions, the full "Bar for merit" criteria, and the "Constraints" in each sub-agent prompt. Each sub-agent should return its best candidate simplification with file paths and evidence or recommend `noop`. -3. Use search and file reading to identify overcomplicated code: +1. Use the **Pick Three, Keep One** pattern for the candidate search: spawn 3 `general-purpose` sub-agents, each searching for simplification opportunities from a different angle (e.g., different complexity metrics such as cyclomatic complexity vs. nesting depth vs. function length, different modules or directories, different simplification types such as dead code removal vs. helper reuse vs. control flow flattening). Include the repo conventions, the full "Bar for merit" criteria, and the "Constraints" in each sub-agent prompt. Each sub-agent should return its best candidate simplification with file paths and evidence or recommend `noop`. +2. Use search and file reading to identify overcomplicated code: - deep nesting - redundant conditionals - duplicated logic diff --git a/.github/workflows/gh-aw-dependency-review.lock.yml b/.github/workflows/gh-aw-dependency-review.lock.yml index 9b0f9025..c5d0b202 100644 --- a/.github/workflows/gh-aw-dependency-review.lock.yml +++ b/.github/workflows/gh-aw-dependency-review.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6c07ad4ba07ff4643d2af151051553103d295a76755cdab029459f4ac9a37034"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"31cf6ff126ca6c58d54a675a80177702e0f2954b4643b294673ce9bc5fabe581"} name: "Dependency Review" "on": @@ -100,7 +100,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -212,11 +212,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -315,10 +314,9 @@ jobs: ### Step 1: Gather Context - 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 full PR details (author, description, branches). - 3. Call `pull_request_read` with method `get_diff` to see exactly what changed. - 4. Call `pull_request_read` with method `get_files` to get the list of changed files. + 1. Call `pull_request_read` with method `get` on PR #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ to get full PR details (author, description, branches). + 2. Call `pull_request_read` with method `get_diff` to see exactly what changed. + 3. Call `pull_request_read` with method `get_files` to get the list of changed files. ### Step 2: Identify and Classify Updated Dependencies @@ -349,8 +347,8 @@ jobs: ```bash gh api repos/{owner}/{repo}/commits/{sha} --jq '.commit.verification.verified' ``` - 2. If the commit is **not verified**, flag this prominently. Unverified commits in pinned actions are a supply-chain risk. - 3. Check whether the commit SHA corresponds to a known release tag: + 1. If the commit is **not verified**, flag this prominently. Unverified commits in pinned actions are a supply-chain risk. + 2. Check whether the commit SHA corresponds to a known release tag: ```bash gh api repos/{owner}/{repo}/git/matching-refs/tags --jq '.[].ref' | head -20 ``` @@ -363,7 +361,7 @@ jobs: ```bash gh api repos/{owner}/{repo}/releases/tags/{new_tag} --jq '.body' 2>/dev/null ``` - 2. If no release exists for the exact tag, check the latest releases: + 1. If no release exists for the exact tag, check the latest releases: ```bash gh api repos/{owner}/{repo}/releases --jq '.[].tag_name' | head -10 ``` @@ -383,7 +381,7 @@ jobs: - **npm/Node**: `grep -rn "require('{package}')\|from '{package}'" --include='*.js' --include='*.ts' --include='*.mjs' --include='*.cjs'` - **Python**: `grep -rn "import {package}\|from {package}" --include='*.py'` - **Java**: `grep -rn '{groupId}' --include='*.java' --include='*.kt' --include='*.gradle' --include='*.xml'` - 2. For each usage, note: + 1. For each usage, note: - Which files and modules use it - What APIs, functions, or features are consumed - For GitHub Actions: what inputs are passed and outputs consumed @@ -601,7 +599,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -655,7 +653,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -994,13 +997,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1337,7 +1333,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1423,7 +1419,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1466,7 +1462,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-dependency-review.md b/.github/workflows/gh-aw-dependency-review.md index 7914170a..53063172 100644 --- a/.github/workflows/gh-aw-dependency-review.md +++ b/.github/workflows/gh-aw-dependency-review.md @@ -100,10 +100,9 @@ This workflow is read-only. You can read files, search code, run commands, and c ### Step 1: Gather Context -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 full PR details (author, description, branches). -3. Call `pull_request_read` with method `get_diff` to see exactly what changed. -4. Call `pull_request_read` with method `get_files` to get the list of changed files. +1. Call `pull_request_read` with method `get` on PR #${{ github.event.pull_request.number }} to get full PR details (author, description, branches). +2. Call `pull_request_read` with method `get_diff` to see exactly what changed. +3. Call `pull_request_read` with method `get_files` to get the list of changed files. ### Step 2: Identify and Classify Updated Dependencies @@ -134,8 +133,8 @@ If the action reference uses a commit SHA (e.g. `uses: actions/checkout@de0fac2e ```bash gh api repos/{owner}/{repo}/commits/{sha} --jq '.commit.verification.verified' ``` -2. If the commit is **not verified**, flag this prominently. Unverified commits in pinned actions are a supply-chain risk. -3. Check whether the commit SHA corresponds to a known release tag: +1. If the commit is **not verified**, flag this prominently. Unverified commits in pinned actions are a supply-chain risk. +2. Check whether the commit SHA corresponds to a known release tag: ```bash gh api repos/{owner}/{repo}/git/matching-refs/tags --jq '.[].ref' | head -20 ``` @@ -148,7 +147,7 @@ For dependencies hosted on GitHub, fetch the release notes: ```bash gh api repos/{owner}/{repo}/releases/tags/{new_tag} --jq '.body' 2>/dev/null ``` -2. If no release exists for the exact tag, check the latest releases: +1. If no release exists for the exact tag, check the latest releases: ```bash gh api repos/{owner}/{repo}/releases --jq '.[].tag_name' | head -10 ``` @@ -168,7 +167,7 @@ For dependencies hosted on GitHub, fetch the release notes: - **npm/Node**: `grep -rn "require('{package}')\|from '{package}'" --include='*.js' --include='*.ts' --include='*.mjs' --include='*.cjs'` - **Python**: `grep -rn "import {package}\|from {package}" --include='*.py'` - **Java**: `grep -rn '{groupId}' --include='*.java' --include='*.kt' --include='*.gradle' --include='*.xml'` -2. For each usage, note: +1. For each usage, note: - Which files and modules use it - What APIs, functions, or features are consumed - For GitHub Actions: what inputs are passed and outputs consumed diff --git a/.github/workflows/gh-aw-docs-drift.lock.yml b/.github/workflows/gh-aw-docs-drift.lock.yml index 96c0d70d..d1d00813 100644 --- a/.github/workflows/gh-aw-docs-drift.lock.yml +++ b/.github/workflows/gh-aw-docs-drift.lock.yml @@ -45,7 +45,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c38197a1da609313561ac3686230bd0faab654c587b710a643063e9d4b5aec87"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"5c30422337f596229353cb4685d9847242f26b5ad7a604c1b823783a7b822061"} name: "Docs Patrol" "on": @@ -119,7 +119,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -232,11 +232,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -326,19 +325,19 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ### Pick Three, Keep Many - Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - **Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. @@ -361,7 +360,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -591,7 +590,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -645,7 +644,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -990,13 +994,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1332,7 +1329,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1418,7 +1415,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1460,7 +1457,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-docs-patrol.lock.yml b/.github/workflows/gh-aw-docs-patrol.lock.yml index d3a79dc3..6c303636 100644 --- a/.github/workflows/gh-aw-docs-patrol.lock.yml +++ b/.github/workflows/gh-aw-docs-patrol.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c38197a1da609313561ac3686230bd0faab654c587b710a643063e9d4b5aec87"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"5c30422337f596229353cb4685d9847242f26b5ad7a604c1b823783a7b822061"} name: "Docs Patrol" "on": @@ -114,7 +114,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -227,11 +227,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -321,19 +320,19 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ### Pick Three, Keep Many - Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - **Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. @@ -356,7 +355,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -586,7 +585,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -640,7 +639,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -985,13 +989,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1327,7 +1324,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1413,7 +1410,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1455,7 +1452,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-duplicate-issue-detector.lock.yml b/.github/workflows/gh-aw-duplicate-issue-detector.lock.yml index 8aa62864..fbb86ee6 100644 --- a/.github/workflows/gh-aw-duplicate-issue-detector.lock.yml +++ b/.github/workflows/gh-aw-duplicate-issue-detector.lock.yml @@ -34,7 +34,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"cfcf3d7441c35e747e21b2be0239d6cbe014338a821542f996f886ece70170cf"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"dddea5c5c3eb3d5af447d1d5e58b7298ef373b2b80594c187ee8a600fba39855"} name: "Duplicate Issue Detector" "on": @@ -101,7 +101,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -116,7 +116,7 @@ jobs: GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","public-code-search.fastmcp.app","www.elastic.co"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","public-code-search.fastmcp.app","www.elastic.co"]' GH_AW_INFO_FIREWALL_ENABLED: "true" GH_AW_INFO_AWF_VERSION: "v0.23.0" GH_AW_INFO_AWMG_VERSION: "" @@ -225,7 +225,6 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -531,7 +530,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -836,13 +835,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -890,7 +882,7 @@ jobs: run: | set -o pipefail # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "agents-md-generator.fastmcp.app,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cloud.elastic.co,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,ela.st,elastic.co,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cloud.elastic.co,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,ela.st,elastic.co,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE @@ -973,7 +965,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "agents-md-generator.fastmcp.app,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cloud.elastic.co,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,ela.st,elastic.co,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cloud.elastic.co,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,ela.st,elastic.co,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1178,7 +1170,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1264,7 +1256,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1306,7 +1298,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1325,7 +1317,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "agents-md-generator.fastmcp.app,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cloud.elastic.co,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,ela.st,elastic.co,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" + GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cloud.elastic.co,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,ela.st,elastic.co,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"missing_data\":{},\"missing_tool\":{}}" diff --git a/.github/workflows/gh-aw-estc-actions-resource-not-accessible-detector.lock.yml b/.github/workflows/gh-aw-estc-actions-resource-not-accessible-detector.lock.yml index f7fdedf0..204285a6 100644 --- a/.github/workflows/gh-aw-estc-actions-resource-not-accessible-detector.lock.yml +++ b/.github/workflows/gh-aw-estc-actions-resource-not-accessible-detector.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6bc77aa8bd60a9a20aa6b2988051ae2cd993582d3539e7118127e43fd9246513"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"cded93cfa5f93c5d9fe1bd438fb2421f897d3f5888fc1e946fdc184feee810fc"} name: "Resource Not Accessible By Integration Detector" "on": @@ -116,7 +116,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -231,11 +231,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -368,9 +367,8 @@ jobs: ## Step 1: Gather context - 1. Call `generate_agents_md` to get repository conventions (if it fails, continue). - 2. Read `/tmp/gh-aw/agent/resource-not-accessible-summary.txt`. - 3. Read `/tmp/gh-aw/agent/resource-not-accessible-findings.tsv`. + 1. Read `/tmp/gh-aw/agent/resource-not-accessible-summary.txt`. + 2. Read `/tmp/gh-aw/agent/resource-not-accessible-findings.tsv`. ## Step 2: Decide whether to noop @@ -538,7 +536,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -592,7 +590,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -938,13 +941,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1280,7 +1276,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1366,7 +1362,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1408,7 +1404,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-estc-actions-resource-not-accessible-detector.md b/.github/workflows/gh-aw-estc-actions-resource-not-accessible-detector.md index dda2f90c..5c340db4 100644 --- a/.github/workflows/gh-aw-estc-actions-resource-not-accessible-detector.md +++ b/.github/workflows/gh-aw-estc-actions-resource-not-accessible-detector.md @@ -211,9 +211,8 @@ The fix is to add an explicit `permissions:` block at the workflow or job level ## Step 1: Gather context -1. Call `generate_agents_md` to get repository conventions (if it fails, continue). -2. Read `/tmp/gh-aw/agent/resource-not-accessible-summary.txt`. -3. Read `/tmp/gh-aw/agent/resource-not-accessible-findings.tsv`. +1. Read `/tmp/gh-aw/agent/resource-not-accessible-summary.txt`. +2. Read `/tmp/gh-aw/agent/resource-not-accessible-findings.tsv`. ## Step 2: Decide whether to noop diff --git a/.github/workflows/gh-aw-estc-docs-patrol-external.lock.yml b/.github/workflows/gh-aw-estc-docs-patrol-external.lock.yml index d9656811..6b42f323 100644 --- a/.github/workflows/gh-aw-estc-docs-patrol-external.lock.yml +++ b/.github/workflows/gh-aw-estc-docs-patrol-external.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0167a60f93bab7174f9e7acf412ab9c101c855de82e481017539bd635c841b8b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"5d0f8b027eb0b63faaa8c5ee5571b9c267f8a60ed3f5f8fbebbd9088842abf9a"} name: "Estc Docs Patrol External" "on": @@ -113,7 +113,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -225,11 +225,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -329,7 +328,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -577,7 +576,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -631,7 +630,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -976,13 +980,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "elastic-docs": { "type": "http", "url": "https://www.elastic.co/docs/_mcp/", @@ -1328,7 +1325,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1414,7 +1411,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1456,7 +1453,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-estc-docs-pr-review.lock.yml b/.github/workflows/gh-aw-estc-docs-pr-review.lock.yml index c8b03f42..3351230d 100644 --- a/.github/workflows/gh-aw-estc-docs-pr-review.lock.yml +++ b/.github/workflows/gh-aw-estc-docs-pr-review.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"08ab74a6a2896ff84b15a75384c068804f94b5be3ad49c8e176d4fa7b7c8f27b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"628d72bf4727704696665d6cac9dbe7922df772f1782f21dffa59d8b4332b04f"} name: "Estc Docs PR Review" "on": @@ -115,7 +115,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -230,11 +230,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -343,8 +342,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. Use these as additional review criteria throughout the review. 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, **labels**). + 1. 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, **labels**). 3. If the PR description references issues (e.g., "Fixes #123", "Closes #456"), call `issue_read` with method `get` on each linked issue to understand the motivation and acceptance criteria. Note any product, deployment, or version context mentioned. 4. Call `pull_request_read` with method `get_review_comments` to check existing review threads. Note which files already have threads and whether threads are resolved, unresolved, or outdated. 5. Call `pull_request_read` with method `get_reviews` to see prior review submissions from this bot. Do not repeat points already made in prior reviews. @@ -602,7 +600,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -656,7 +654,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - name: Download style guide reference pages run: "set -euo pipefail\nmkdir -p /tmp/style-guide\nurls=(\n \"https://www.elastic.co/docs/contribute-docs/style-guide\"\n \"https://www.elastic.co/docs/contribute-docs/style-guide/voice-tone\"\n \"https://www.elastic.co/docs/contribute-docs/style-guide/grammar-spelling\"\n \"https://www.elastic.co/docs/contribute-docs/style-guide/formatting\"\n \"https://www.elastic.co/docs/contribute-docs/style-guide/word-choice\"\n \"https://www.elastic.co/docs/contribute-docs/style-guide/accessibility\"\n \"https://www.elastic.co/docs/contribute-docs/style-guide/ui-writing\"\n \"https://www.elastic.co/docs/contribute-docs/how-to/cumulative-docs/guidelines\"\n \"https://www.elastic.co/docs/contribute-docs/how-to/cumulative-docs/reference\"\n \"https://docs-v3-preview.elastic.dev/elastic/docs-builder/tree/main/syntax/applies\"\n)\nfor url in \"${urls[@]}\"; do\n slug=$(echo \"$url\" | sed 's|https\\?://||; s|/|_|g; s|[^a-zA-Z0-9_-]||g')\n curl -sSfL --retry 2 --max-time 30 \"$url\" -o \"/tmp/style-guide/${slug}.html\" || echo \"::warning::Failed to download ${url}\"\ndone\necho \"Downloaded $(ls /tmp/style-guide/ | wc -l) style guide pages to /tmp/style-guide/\"\n" @@ -1038,13 +1041,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "elastic-docs": { "type": "http", "url": "https://www.elastic.co/docs/_mcp/", @@ -1393,7 +1389,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1479,7 +1475,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1519,7 +1515,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-estc-docs-pr-review.md b/.github/workflows/gh-aw-estc-docs-pr-review.md index 5ad45b5b..f0f9fe68 100644 --- a/.github/workflows/gh-aw-estc-docs-pr-review.md +++ b/.github/workflows/gh-aw-estc-docs-pr-review.md @@ -147,8 +147,7 @@ Follow these steps in order. ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. Use these as additional review criteria throughout the review. 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, **labels**). +1. Call `pull_request_read` with method `get` on PR #${{ github.event.pull_request.number }} to get the full PR details (author, description, branches, **labels**). 3. If the PR description references issues (e.g., "Fixes #123", "Closes #456"), call `issue_read` with method `get` on each linked issue to understand the motivation and acceptance criteria. Note any product, deployment, or version context mentioned. 4. Call `pull_request_read` with method `get_review_comments` to check existing review threads. Note which files already have threads and whether threads are resolved, unresolved, or outdated. 5. Call `pull_request_read` with method `get_reviews` to see prior review submissions from this bot. Do not repeat points already made in prior reviews. diff --git a/.github/workflows/gh-aw-estc-downstream-health.lock.yml b/.github/workflows/gh-aw-estc-downstream-health.lock.yml index 58c4bee6..5205fb3f 100644 --- a/.github/workflows/gh-aw-estc-downstream-health.lock.yml +++ b/.github/workflows/gh-aw-estc-downstream-health.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"79e00e93ad8119fa2b3e519fc0c1061999a59db3dc73734bf65e2cdcedd2e2e3"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fdf9ddc1ae23f25c3ac50f1508d42c7e93df7aca1a71831f9e8b2584d53f2c6f"} name: "Estc Downstream Health" "on": @@ -107,7 +107,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -218,11 +218,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -319,7 +318,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -586,7 +585,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -640,7 +639,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -979,13 +983,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1321,7 +1318,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1407,7 +1404,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1449,7 +1446,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-estc-newbie-contributor-patrol-external.lock.yml b/.github/workflows/gh-aw-estc-newbie-contributor-patrol-external.lock.yml index b06d119c..3e8b69a9 100644 --- a/.github/workflows/gh-aw-estc-newbie-contributor-patrol-external.lock.yml +++ b/.github/workflows/gh-aw-estc-newbie-contributor-patrol-external.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"25bae9ee2fd2dabab0dc8a0da329870ef5cca4dc68cc68765b1c4ae7c9547686"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"f58755fb5df3e0fa6ae366e57df8dd75e789c2cc90a53d39d291c11bdaddac61"} name: "Estc Newbie Contributor Patrol External" "on": @@ -107,7 +107,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -218,11 +218,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -319,7 +318,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -525,7 +524,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -579,7 +578,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -918,13 +922,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "elastic-docs": { "type": "http", "url": "https://www.elastic.co/docs/_mcp/", @@ -1269,7 +1266,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1355,7 +1352,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1397,7 +1394,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-estc-pr-buildkite-detective.lock.yml b/.github/workflows/gh-aw-estc-pr-buildkite-detective.lock.yml index 0dafe1f2..3b6ff019 100644 --- a/.github/workflows/gh-aw-estc-pr-buildkite-detective.lock.yml +++ b/.github/workflows/gh-aw-estc-pr-buildkite-detective.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"639d20e625bdf362e5e690e812052ba140799d50984096906124b6dcb164574f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c746cb697caa3596f3ed713a6f4fa4a06639158c28b4e85c0827b57cf5ac7604"} name: "PR Buildkite Detective" "on": @@ -112,7 +112,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -229,11 +229,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -355,10 +354,9 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Use the commit SHA provided in the Context section above. If it is empty, discover it from the PR's commit statuses or check runs. - 3. Call `list_pull_requests` for the repository (open PRs), then call `pull_request_read` with method `get` on candidates and keep PRs where `head.sha` matches the failed commit SHA. If none match, call `noop` with message "No pull request associated with failed commit status; nothing to do" and stop. - 4. For each matching PR, keep author, branches, and fork status for downstream analysis. + 1. Use the commit SHA provided in the Context section above. If it is empty, discover it from the PR's commit statuses or check runs. + 2. Call `list_pull_requests` for the repository (open PRs), then call `pull_request_read` with method `get` on candidates and keep PRs where `head.sha` matches the failed commit SHA. If none match, call `noop` with message "No pull request associated with failed commit status; nothing to do" and stop. + 3. For each matching PR, keep author, branches, and fork status for downstream analysis. ### Step 2: Find the Buildkite Build @@ -384,7 +382,7 @@ jobs: - Call `pull_request_read` with method `get_status` for the PR to retrieve commit status contexts. - Look for status contexts or check runs whose `target_url` contains `buildkite.com`. The URL typically follows the pattern `https://buildkite.com///builds/`. - 2. **Fetch the public build page**: Use `web-fetch` to retrieve the Buildkite build URL found above. The page contains the build status, job list, and links to individual job logs. + 1. **Fetch the public build page**: Use `web-fetch` to retrieve the Buildkite build URL found above. The page contains the build status, job list, and links to individual job logs. 3. **Collect failure evidence from public pages**: - Parse the fetched build page to identify failed jobs. Look for job links matching the pattern `https://buildkite.com///builds/#`. @@ -538,7 +536,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -592,7 +590,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - name: Resolve event context run: | @@ -905,13 +908,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "buildkite": { "type": "http", "url": "https://mcp.buildkite.com/mcp/readonly", @@ -1262,7 +1258,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1348,7 +1344,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1390,7 +1386,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-estc-pr-buildkite-detective.md b/.github/workflows/gh-aw-estc-pr-buildkite-detective.md index 2c02f656..63f83978 100644 --- a/.github/workflows/gh-aw-estc-pr-buildkite-detective.md +++ b/.github/workflows/gh-aw-estc-pr-buildkite-detective.md @@ -147,10 +147,9 @@ Classify each failure to guide your investigation: ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Use the commit SHA provided in the Context section above. If it is empty, discover it from the PR's commit statuses or check runs. -3. Call `list_pull_requests` for the repository (open PRs), then call `pull_request_read` with method `get` on candidates and keep PRs where `head.sha` matches the failed commit SHA. If none match, call `noop` with message "No pull request associated with failed commit status; nothing to do" and stop. -4. For each matching PR, keep author, branches, and fork status for downstream analysis. +1. Use the commit SHA provided in the Context section above. If it is empty, discover it from the PR's commit statuses or check runs. +2. Call `list_pull_requests` for the repository (open PRs), then call `pull_request_read` with method `get` on candidates and keep PRs where `head.sha` matches the failed commit SHA. If none match, call `noop` with message "No pull request associated with failed commit status; nothing to do" and stop. +3. For each matching PR, keep author, branches, and fork status for downstream analysis. ### Step 2: Find the Buildkite Build @@ -176,7 +175,7 @@ Use this path when the Buildkite MCP server is unavailable (missing token, 401, - Call `pull_request_read` with method `get_status` for the PR to retrieve commit status contexts. - Look for status contexts or check runs whose `target_url` contains `buildkite.com`. The URL typically follows the pattern `https://buildkite.com///builds/`. -2. **Fetch the public build page**: Use `web-fetch` to retrieve the Buildkite build URL found above. The page contains the build status, job list, and links to individual job logs. +1. **Fetch the public build page**: Use `web-fetch` to retrieve the Buildkite build URL found above. The page contains the build status, job list, and links to individual job logs. 3. **Collect failure evidence from public pages**: - Parse the fetched build page to identify failed jobs. Look for job links matching the pattern `https://buildkite.com///builds/#`. diff --git a/.github/workflows/gh-aw-flaky-test-investigator.lock.yml b/.github/workflows/gh-aw-flaky-test-investigator.lock.yml index ddde6a02..cfcb6275 100644 --- a/.github/workflows/gh-aw-flaky-test-investigator.lock.yml +++ b/.github/workflows/gh-aw-flaky-test-investigator.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2caa8e652ea16a22ddc69d9d9561fce13e0c0779bdd293b509d0924441d7a00a"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"276258f6ff05951f620f63ae5111440fd82dcf4ad15ac3a278f22473056383ce"} name: "Flaky Test Investigator" "on": @@ -107,7 +107,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -218,11 +218,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -319,7 +318,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -537,7 +536,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -591,7 +590,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -930,13 +934,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1272,7 +1269,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1358,7 +1355,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1400,7 +1397,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-fragments/elastic-tools.md b/.github/workflows/gh-aw-fragments/elastic-tools.md index 3c978fe4..5f12fc56 100644 --- a/.github/workflows/gh-aw-fragments/elastic-tools.md +++ b/.github/workflows/gh-aw-fragments/elastic-tools.md @@ -3,15 +3,11 @@ # Each workflow defines its own tools (github, bash, web-fetch) and base network allows (defaults, github). # This fragment provides only the Elastic-specific MCP servers and their network entries. mcp-servers: - agents-md-generator: - url: "https://agents-md-generator.fastmcp.app/mcp" - allowed: ["generate_agents_md"] public-code-search: url: "https://public-code-search.fastmcp.app/mcp" allowed: ["search_code"] network: allowed: - - "agents-md-generator.fastmcp.app" - "public-code-search.fastmcp.app" - "elastic.co" - "www.elastic.co" @@ -22,5 +18,4 @@ network: ## MCP Servers -- **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. diff --git a/.github/workflows/gh-aw-fragments/network-ecosystems.md b/.github/workflows/gh-aw-fragments/network-ecosystems.md index 34af0315..e7fe7a8c 100644 --- a/.github/workflows/gh-aw-fragments/network-ecosystems.md +++ b/.github/workflows/gh-aw-fragments/network-ecosystems.md @@ -4,6 +4,8 @@ network: allowed: - defaults + - "agents-md-generator.fastmcp.app" + - "public-code-search.fastmcp.app" - "elastic.co" - "elastic.dev" - "elastic.github.io" diff --git a/.github/workflows/gh-aw-fragments/pick-three-keep-many.md b/.github/workflows/gh-aw-fragments/pick-three-keep-many.md index 08071b26..2aa02d8f 100644 --- a/.github/workflows/gh-aw-fragments/pick-three-keep-many.md +++ b/.github/workflows/gh-aw-fragments/pick-three-keep-many.md @@ -1,18 +1,18 @@ ### Pick Three, Keep Many -Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. +Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "${{ inputs.model }}"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) -- All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) +- All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. -**Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. +**Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. diff --git a/.github/workflows/gh-aw-fragments/playwright-mcp-explorer.md b/.github/workflows/gh-aw-fragments/playwright-mcp-explorer.md index fe32e504..880b4432 100644 --- a/.github/workflows/gh-aw-fragments/playwright-mcp-explorer.md +++ b/.github/workflows/gh-aw-fragments/playwright-mcp-explorer.md @@ -1,49 +1,57 @@ --- tools: playwright: +steps: + - name: Write Playwright instructions to disk + run: | + cat > /tmp/playwright-instructions.md << 'EOF' + # Playwright MCP Tools + + Use Playwright MCP tools for interactive browser automation. + Use these tools to explore the app step by step — do NOT write Node.js scripts. + + ## Available tools + + - `browser_navigate` — go to a URL + - `browser_click` — click an element + - `browser_type` — type text into an input + - `browser_snapshot` — get an accessibility tree (YAML) of the current page + - `browser_take_screenshot` — capture a screenshot + - `browser_console_execute` — run JavaScript in the browser console + + ## Why MCP tools instead of scripts + + MCP tools are interactive: you see the page state after each action and + decide what to do next. This is ideal for exploratory testing where you + need to adapt based on what you find. Scripts are fire-and-forget — if + a selector is wrong, you don't find out until the script fails. + + ## Measuring DOM properties + + For programmatic checks (e.g. element heights, contrast), use + `browser_console_execute`: + + ```javascript + (() => { + const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); + return JSON.stringify(Array.from(els) + .map(el => { + const r = el.getBoundingClientRect(); + return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; + }) + .filter(el => el.top > 50 && el.top < 250)); + })() + ``` + + ## Handling failures + + - Do not retry the same action more than twice — the page is in a different state than expected. + - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. + - Adapt (different selector, different path) or report the failure as a finding. + - Never claim you verified something you didn't — if it failed and you skipped it, say so. + EOF --- ## Playwright MCP Tools -You have Playwright MCP tools available for interactive browser automation. -Use these tools to explore the app step by step — do NOT write Node.js scripts. - -### Available tools - -- `browser_navigate` — go to a URL -- `browser_click` — click an element -- `browser_type` — type text into an input -- `browser_snapshot` — get an accessibility tree (YAML) of the current page -- `browser_take_screenshot` — capture a screenshot -- `browser_console_execute` — run JavaScript in the browser console - -### Why MCP tools instead of scripts - -MCP tools are interactive: you see the page state after each action and -decide what to do next. This is ideal for exploratory testing where you -need to adapt based on what you find. Scripts are fire-and-forget — if -a selector is wrong, you don't find out until the script fails. - -### Measuring DOM properties - -For programmatic checks (e.g. element heights, contrast), use -`browser_console_execute`: - -```javascript -(() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); -})() -``` - -### Handling failures - -- Do not retry the same action more than twice — the page is in a different state than expected. -- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. -- Adapt (different selector, different path) or report the failure as a finding. -- Never claim you verified something you didn't — if it failed and you skipped it, say so. +Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. diff --git a/.github/workflows/gh-aw-fragments/pr-context.md b/.github/workflows/gh-aw-fragments/pr-context.md index 0084f329..a3a74fc9 100644 --- a/.github/workflows/gh-aw-fragments/pr-context.md +++ b/.github/workflows/gh-aw-fragments/pr-context.md @@ -139,8 +139,13 @@ steps: | `comments.json` | PR discussion comments (not inline) | | `issue-{N}.json` | Linked issue details (one file per linked issue, if any) | | `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out | - | `agents.md` | Repository conventions from `generate_agents_md` (if written by agent) | - | `review-instructions.md` | Review instructions, criteria, and calibration examples (if written by review-process fragment) | + | `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) | + | `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents | + | `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) | + | `parent-review.md` | Comment format and inline severity threshold for the parent agent (written when `ready_to_code_review` is called) | + | `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) | + | `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) | + | `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) | MANIFEST echo "PR context written to /tmp/pr-context/" diff --git a/.github/workflows/gh-aw-fragments/review-process.md b/.github/workflows/gh-aw-fragments/review-process.md index cec68209..36b1620a 100644 --- a/.github/workflows/gh-aw-fragments/review-process.md +++ b/.github/workflows/gh-aw-fragments/review-process.md @@ -13,7 +13,7 @@ steps: Before reviewing files, read these to understand the PR: 1. `/tmp/pr-context/pr.json` — PR title, description, author, and branches. Understand what the PR is trying to accomplish. - 2. `/tmp/pr-context/agents.md` — Repository coding conventions and guidelines (if it exists). + 2. `/tmp/agents.md` — Repository coding conventions and guidelines (skip if missing). 3. `/tmp/pr-context/review_comments.json` — Existing review threads. Note which files already have threads so you don't duplicate. 4. `/tmp/pr-context/issue-*.json` — Linked issue details (if any). Understand the motivation and acceptance criteria. @@ -72,7 +72,7 @@ steps: - **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. - **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. - **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods. - - **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). + - **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 `/tmp/agents.md` or CONTRIBUTING docs). **Existing review threads** — check BEFORE flagging any issue: @@ -171,87 +171,4 @@ steps: ## Code Review Reference -### Comment Format - -Call **`create_pull_request_review_comment`** with: -- The file path and the **exact line number from reading the file** (not estimated from the diff) -- The line must be within the diff (an added or context line in the patch) - -````` -**[SEVERITY] Brief title** - -Description of the issue and why it matters. - -```suggestion -corrected code here -``` -````` - -Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line. - -### Inline Comment Threshold - -The minimum severity for inline comments is `${{ inputs.minimum_severity || 'low' }}`. - -Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body instead — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters. - -Severity order (highest to lowest): critical > high > medium > low > nitpick. - -If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`. - -### Review Criteria - -Focus on these categories in priority order: - -1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure) -2. Logic bugs that could cause runtime failures or incorrect behavior -3. Data integrity issues (race conditions, missing transactions, corruption risk) -4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations) -5. Error handling gaps (unhandled exceptions, missing validation) -6. Breaking changes to public APIs without migration path -7. Missing or incorrect test coverage for critical paths - -### What NOT to Flag - -Only 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. - -**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: - -- **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. -- **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. -- **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). -- **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. -- **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. -- **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods. -- **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). - -**Existing review threads** — check BEFORE leaving any comment: - -- **Resolved with reviewer reply** (e.g. "This is intentional") — reviewer's decision is final. Do NOT re-flag. -- **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem. -- **Unresolved** — already flagged. Do NOT duplicate. -- **Outdated** — only re-flag if the issue still applies to the current diff. - -When in doubt, do not duplicate. Redundant comments erode trust. - -Finding no issues is a valid and valuable outcome. An APPROVE with zero inline comments is better than comments that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, approve without comments. - -### Severity Classification - -Determine 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. Do not start with a severity and look for issues to match it. - -- 🔴 **CRITICAL** — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs) -- 🟠 **HIGH** — Should fix before merge (logic errors, missing validation, significant performance issues) -- 🟡 **MEDIUM** — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases) -- ⚪ **LOW** — Author discretion (minor improvements, documentation gaps) -- 💬 **NITPICK** — Truly optional (stylistic preferences, alternative approaches) - -### Review Intensity - -The review intensity is `${{ inputs.intensity || 'balanced' }}`. - -- **`conservative`**: High evidence bar. Only comment when you can demonstrate a concrete failure scenario — what specific input or state triggers the bug. After identifying a potential issue, explicitly challenge your own finding: if you can construct a reasonable counterargument, do not comment. Give the author maximum benefit of the doubt. Approval with zero comments is the expected outcome for most PRs. -- **`balanced`**: Standard evidence bar. Comment when you can point to specific code that would fail and have verified the issue through the full verification protocol. Give the author reasonable benefit of the doubt — if the issue is ambiguous, lean toward not commenting. -- **`aggressive`**: Lower evidence bar. Comment when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions and alternative approaches are welcome but must still cite specific code. Do not speculate without any evidence, and do not duplicate existing threads. - -If the value is unrecognized, treat it as `balanced`. +Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). diff --git a/.github/workflows/gh-aw-fragments/runtime-setup.md b/.github/workflows/gh-aw-fragments/runtime-setup.md index 98aaafba..12dfad47 100644 --- a/.github/workflows/gh-aw-fragments/runtime-setup.md +++ b/.github/workflows/gh-aw-fragments/runtime-setup.md @@ -62,4 +62,32 @@ steps: else echo '{"chat.customAgentInSubagent.enabled":true}' > "$CONFIG" fi + + - name: Fetch repository conventions + shell: bash + env: + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + set -euo pipefail + if [ -f "AGENTS.md" ]; then + cp AGENTS.md /tmp/agents.md + echo "Repository conventions copied from AGENTS.md to /tmp/agents.md" + else + OWNER="${GITHUB_REPOSITORY%/*}" + REPO="${GITHUB_REPOSITORY#*/}" + summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \ + -H "Content-Type: application/json" \ + -H "Accept: application/json, text/event-stream" \ + -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"generate_agents_md\",\"arguments\":{\"owner\":\"${OWNER}\",\"repo\":\"${REPO}\"}}}" \ + | sed 's/^data: //' \ + | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true + if [ -n "$summary" ]; then + echo "$summary" > /tmp/agents.md + echo "Repository conventions written to /tmp/agents.md" + else + echo "::warning::Could not fetch repository conventions; continuing without them" + fi + fi --- + +Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). diff --git a/.github/workflows/gh-aw-fragments/safe-output-code-review.md b/.github/workflows/gh-aw-fragments/safe-output-code-review.md new file mode 100644 index 00000000..c318f705 --- /dev/null +++ b/.github/workflows/gh-aw-fragments/safe-output-code-review.md @@ -0,0 +1,116 @@ +--- +safe-inputs: + ready-to-code-review: + description: "Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/" + py: | + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = os.environ.get('GH_AW_INPUTS_MINIMUM_SEVERITY', 'low') or 'low' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) +--- 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 c1d72586..51358737 100644 --- a/.github/workflows/gh-aw-fragments/safe-output-create-pr.md +++ b/.github/workflows/gh-aw-fragments/safe-output-create-pr.md @@ -102,6 +102,11 @@ safe-inputs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', diff --git a/.github/workflows/gh-aw-fragments/scheduled-audit.md b/.github/workflows/gh-aw-fragments/scheduled-audit.md index 88a1eb09..b3969485 100644 --- a/.github/workflows/gh-aw-fragments/scheduled-audit.md +++ b/.github/workflows/gh-aw-fragments/scheduled-audit.md @@ -10,7 +10,7 @@ Follow these steps in order. ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. +1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze diff --git a/.github/workflows/gh-aw-fragments/scheduled-fix.md b/.github/workflows/gh-aw-fragments/scheduled-fix.md index 789732b2..2bf0de94 100644 --- a/.github/workflows/gh-aw-fragments/scheduled-fix.md +++ b/.github/workflows/gh-aw-fragments/scheduled-fix.md @@ -14,7 +14,7 @@ Follow these steps in order. ### Step 1: Gather Candidates -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. +1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the candidate gathering instructions in the **Fix Assignment** section. 3. For each candidate, read the full issue and comments using `issue_read` (methods `get` and `get_comments`). diff --git a/.github/workflows/gh-aw-framework-best-practices.lock.yml b/.github/workflows/gh-aw-framework-best-practices.lock.yml index 59fd4eb3..0d1395b2 100644 --- a/.github/workflows/gh-aw-framework-best-practices.lock.yml +++ b/.github/workflows/gh-aw-framework-best-practices.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d40e51cc3421862fdc61e96607cd36b1b8394633f403e9e7d7a2707573801de0"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"06b54138ddba12962381375939cefd65995191c0ba8b5eb89208466058f76330"} name: "Framework Best Practices" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -312,19 +311,19 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ### Pick Three, Keep Many - Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - **Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. @@ -347,7 +346,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -582,7 +581,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -636,7 +635,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -975,13 +979,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1317,7 +1314,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1403,7 +1400,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1445,7 +1442,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-information-architecture.lock.yml b/.github/workflows/gh-aw-information-architecture.lock.yml index 2ac18ac5..7abc61ff 100644 --- a/.github/workflows/gh-aw-information-architecture.lock.yml +++ b/.github/workflows/gh-aw-information-architecture.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d978c6dbce791dcec470192e30d44e7ea9cb33e324e43a686ab40dd7961a8e14"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3ec6b0c780bec42d813be8f0e94fb31c0b6673e658ced9886d0d2076cf9ae3fa"} name: "Information Architecture" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -344,7 +343,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -577,7 +576,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -631,7 +630,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -970,13 +974,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1312,7 +1309,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1398,7 +1395,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1440,7 +1437,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-internal-gemini-cli-web-search.md b/.github/workflows/gh-aw-internal-gemini-cli-web-search.md index 0dabb424..c5641121 100644 --- a/.github/workflows/gh-aw-internal-gemini-cli-web-search.md +++ b/.github/workflows/gh-aw-internal-gemini-cli-web-search.md @@ -108,10 +108,9 @@ Understand the request, investigate repository and external context, and respond ### Step 1: Gather Context and Plan -1. Call `generate_agents_md` to get repository conventions. -2. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your research anchor for all subsequent steps. -3. Before searching, decompose the question into sub-questions. For complex or multi-faceted requests, list 2–5 specific sub-questions that, if answered, would fully address the original request. This prevents unfocused searching and ensures complete coverage. -4. Use `search_code` and local file reads only when codebase knowledge is needed to answer the question or prepare an implementation. +1. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your research anchor for all subsequent steps. +2. Before searching, decompose the question into sub-questions. For complex or multi-faceted requests, list 2–5 specific sub-questions that, if answered, would fully address the original request. This prevents unfocused searching and ensures complete coverage. +3. Use `search_code` and local file reads only when codebase knowledge is needed to answer the question or prepare an implementation. ### Step 2: Research Iteratively diff --git a/.github/workflows/gh-aw-internal-gemini-cli.md b/.github/workflows/gh-aw-internal-gemini-cli.md index 1bc63c85..220f66d1 100644 --- a/.github/workflows/gh-aw-internal-gemini-cli.md +++ b/.github/workflows/gh-aw-internal-gemini-cli.md @@ -106,10 +106,9 @@ Understand the request, investigate using code search and bash commands, and res ### Step 1: Gather Context and Plan -1. Call `generate_agents_md` to get repository conventions. -2. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your investigation anchor for all subsequent steps. -3. Before investigating, decompose the question into sub-questions. For complex or multi-faceted requests, list 2-5 specific sub-questions that, if answered, would fully address the original request. -4. Use `search_code` and local file reads to understand relevant code structure. +1. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your investigation anchor for all subsequent steps. +2. Before investigating, decompose the question into sub-questions. For complex or multi-faceted requests, list 2-5 specific sub-questions that, if answered, would fully address the original request. +3. Use `search_code` and local file reads to understand relevant code structure. ### Step 2: Investigate diff --git a/.github/workflows/gh-aw-issue-fixer.lock.yml b/.github/workflows/gh-aw-issue-fixer.lock.yml index fff241bf..671b76ce 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":"b049116d1b44954deb1d6fd4735689d11fdb16abe08af4b4b1a5cc7260343d9e"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b4b7463fc8c2f58ba0aa879ee4dd0f39b33db1c3987536f3f49f06e090c49bb8"} name: "Issue Fixer" "on": @@ -116,7 +116,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -231,11 +231,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -357,9 +356,8 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Read key repository files (README, CONTRIBUTING, etc.) to understand the project. - 3. Search for related issues and PRs (open and closed) that may be relevant. Call `issue_read` with method `get` on the most relevant issues to understand prior discussion, decisions, and whether this is a duplicate. + 1. Read key repository files (README, CONTRIBUTING, etc.) to understand the project. + 2. Search for related issues and PRs (open and closed) that may be relevant. Call `issue_read` with method `get` on the most relevant issues to understand prior discussion, decisions, and whether this is a duplicate. ### Step 2: Investigate the Codebase @@ -503,7 +501,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -557,7 +555,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -662,6 +665,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1055,6 +1062,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1146,13 +1158,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1507,7 +1512,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1613,7 +1618,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1673,7 +1678,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-issue-fixer.md b/.github/workflows/gh-aw-issue-fixer.md index 985d41f8..8aca6ca3 100644 --- a/.github/workflows/gh-aw-issue-fixer.md +++ b/.github/workflows/gh-aw-issue-fixer.md @@ -104,9 +104,8 @@ Follow these steps in order. ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Read key repository files (README, CONTRIBUTING, etc.) to understand the project. -3. Search for related issues and PRs (open and closed) that may be relevant. Call `issue_read` with method `get` on the most relevant issues to understand prior discussion, decisions, and whether this is a duplicate. +1. Read key repository files (README, CONTRIBUTING, etc.) to understand the project. +2. Search for related issues and PRs (open and closed) that may be relevant. Call `issue_read` with method `get` on the most relevant issues to understand prior discussion, decisions, and whether this is a duplicate. ### Step 2: Investigate the Codebase diff --git a/.github/workflows/gh-aw-issue-triage.lock.yml b/.github/workflows/gh-aw-issue-triage.lock.yml index dc1b17c8..de3bb2c8 100644 --- a/.github/workflows/gh-aw-issue-triage.lock.yml +++ b/.github/workflows/gh-aw-issue-triage.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e9322e38ca41a9941dd5054391a2597b0d6735a8459be5acec7a775c94d502e9"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e2d74742b8cd952c4037c0ae2a44a61ae18016e068bc1e07fdc2ee5fa234663c"} name: "Issue Triage" "on": @@ -105,7 +105,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -219,11 +219,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -291,48 +290,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Playwright MCP Tools - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. - - ### Available tools - - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console - - ### Why MCP tools instead of scripts - - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. - - ### Measuring DOM properties - - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: - - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` - - ### Handling failures - - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. + Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## add-comment Limitations @@ -391,9 +349,8 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Read key repository files (README, CONTRIBUTING, etc.) to understand the project. - 3. Search for related issues and PRs (open and closed) that may be relevant. Call `issue_read` with method `get` on the most relevant issues to understand prior discussion, decisions, and whether this is a duplicate. + 1. Read key repository files (README, CONTRIBUTING, etc.) to understand the project. + 2. Search for related issues and PRs (open and closed) that may be relevant. Call `issue_read` with method `get` on the most relevant issues to understand prior discussion, decisions, and whether this is a duplicate. ### Step 2: Investigate the Codebase @@ -572,21 +529,21 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - if: hashFiles('.python-version') != '' name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 @@ -626,8 +583,15 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash + - name: Write Playwright instructions to disk + run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} if: ${{ inputs.setup-commands != '' }} @@ -926,13 +890,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1275,7 +1232,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1365,7 +1322,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1420,7 +1377,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-issue-triage.md b/.github/workflows/gh-aw-issue-triage.md index 94f4f4f2..04a01ae8 100644 --- a/.github/workflows/gh-aw-issue-triage.md +++ b/.github/workflows/gh-aw-issue-triage.md @@ -97,9 +97,8 @@ Follow these steps in order. ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Read key repository files (README, CONTRIBUTING, etc.) to understand the project. -3. Search for related issues and PRs (open and closed) that may be relevant. Call `issue_read` with method `get` on the most relevant issues to understand prior discussion, decisions, and whether this is a duplicate. +1. Read key repository files (README, CONTRIBUTING, etc.) to understand the project. +2. Search for related issues and PRs (open and closed) that may be relevant. Call `issue_read` with method `get` on the most relevant issues to understand prior discussion, decisions, and whether this is a duplicate. ### Step 2: Investigate the Codebase 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 8bb8cb29..e9e9e874 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 @@ -31,6 +31,7 @@ # - gh-aw-fragments/messages-footer.md # - gh-aw-fragments/network-ecosystems.md # - gh-aw-fragments/playwright-mcp-explorer.md +# - gh-aw-fragments/review-process.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-issue.md @@ -40,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3f498e81543e1cc930308b3ae56449c6860329b2068223b9efb6c9cedba443fc"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"7fe1a693cbd894d2f7661c4f973105f7866206f3b06e11f9d89126164984534b"} name: "Mention in Issue (no sandbox)" "on": @@ -129,7 +130,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -257,11 +258,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -327,6 +327,11 @@ jobs: - Do not modify files under `.github/workflows/`. - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Code Review Reference + + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -336,48 +341,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Playwright MCP Tools - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. - - ### Available tools - - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console - - ### Why MCP tools instead of scripts - - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. - - ### Measuring DOM properties - - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: - - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` - - ### Handling failures - - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. + Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## add-comment Limitations @@ -442,10 +406,9 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Read the full issue thread to understand the discussion so far. - 3. If the issue references other issues or PRs, call `issue_read` or `pull_request_read` with method `get` on each to understand the broader context. - 4. Use `grep` and file reading to explore the relevant parts of the codebase. + 1. Read the full issue thread to understand the discussion so far. + 2. If the issue references other issues or PRs, call `issue_read` or `pull_request_read` with method `get` on each to understand the broader context. + 3. Use `grep` and file reading to explore the relevant parts of the codebase. ### Step 2: Investigate and Respond @@ -573,21 +536,21 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - if: hashFiles('.python-version') != '' name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 @@ -627,8 +590,17 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" + shell: bash + - 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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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" + - name: Write Playwright instructions to disk + run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} if: ${{ inputs.setup-commands != '' }} @@ -771,6 +743,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1197,6 +1173,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1289,13 +1270,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1528,7 +1502,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1634,7 +1608,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1696,7 +1670,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.md b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.md index 2427ea3f..20f3b00c 100644 --- a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.md +++ b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.md @@ -9,6 +9,7 @@ imports: - gh-aw-fragments/rigor.md - gh-aw-fragments/mcp-pagination.md - gh-aw-fragments/workflow-edit-guardrails.md + - gh-aw-fragments/review-process.md - gh-aw-fragments/messages-footer.md - gh-aw-fragments/playwright-mcp-explorer.md - gh-aw-fragments/safe-output-add-comment-issue.md @@ -114,10 +115,9 @@ Understand the request, investigate the codebase, and respond with a helpful, ac ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Read the full issue thread to understand the discussion so far. -3. If the issue references other issues or PRs, call `issue_read` or `pull_request_read` with method `get` on each to understand the broader context. -4. Use `grep` and file reading to explore the relevant parts of the codebase. +1. Read the full issue thread to understand the discussion so far. +2. If the issue references other issues or PRs, call `issue_read` or `pull_request_read` with method `get` on each to understand the broader context. +3. Use `grep` and file reading to explore the relevant parts of the codebase. ### Step 2: Investigate and Respond diff --git a/.github/workflows/gh-aw-mention-in-issue.lock.yml b/.github/workflows/gh-aw-mention-in-issue.lock.yml index 321a4663..9b9dd46f 100644 --- a/.github/workflows/gh-aw-mention-in-issue.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue.lock.yml @@ -31,6 +31,7 @@ # - gh-aw-fragments/messages-footer.md # - gh-aw-fragments/network-ecosystems.md # - gh-aw-fragments/playwright-mcp-explorer.md +# - gh-aw-fragments/review-process.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-issue.md @@ -40,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"bd8f6ffb24fa0c365562e2712c9f7aac1bc30d2f83c41ae8ae54ab05352fb1a3"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"88830b37bc3a335b97b2bbb10161349dd44b58ed8dc493f751c286b92daf49d5"} name: "Mention in Issue" "on": @@ -129,7 +130,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -257,11 +258,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -327,6 +327,11 @@ jobs: - Do not modify files under `.github/workflows/`. - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Code Review Reference + + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -336,48 +341,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Playwright MCP Tools - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. - - ### Available tools - - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console - - ### Why MCP tools instead of scripts - - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. - - ### Measuring DOM properties - - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: - - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` - - ### Handling failures - - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. + Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## add-comment Limitations @@ -442,10 +406,9 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Read the full issue thread to understand the discussion so far. - 3. If the issue references other issues or PRs, call `issue_read` or `pull_request_read` with method `get` on each to understand the broader context. - 4. Use `grep` and file reading to explore the relevant parts of the codebase. + 1. Read the full issue thread to understand the discussion so far. + 2. If the issue references other issues or PRs, call `issue_read` or `pull_request_read` with method `get` on each to understand the broader context. + 3. Use `grep` and file reading to explore the relevant parts of the codebase. ### Step 2: Investigate and Respond @@ -575,21 +538,21 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - if: hashFiles('.python-version') != '' name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 @@ -629,8 +592,17 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" + shell: bash + - 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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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" + - name: Write Playwright instructions to disk + run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} if: ${{ inputs.setup-commands != '' }} @@ -775,6 +747,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1201,6 +1177,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1293,13 +1274,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1661,7 +1635,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1767,7 +1741,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1829,7 +1803,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-mention-in-issue.md b/.github/workflows/gh-aw-mention-in-issue.md index a2f257ac..fcc7e5d4 100644 --- a/.github/workflows/gh-aw-mention-in-issue.md +++ b/.github/workflows/gh-aw-mention-in-issue.md @@ -9,6 +9,7 @@ imports: - gh-aw-fragments/rigor.md - gh-aw-fragments/mcp-pagination.md - gh-aw-fragments/workflow-edit-guardrails.md + - gh-aw-fragments/review-process.md - gh-aw-fragments/messages-footer.md - gh-aw-fragments/playwright-mcp-explorer.md - gh-aw-fragments/safe-output-add-comment-issue.md @@ -111,10 +112,9 @@ Understand the request, investigate the codebase, and respond with a helpful, ac ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Read the full issue thread to understand the discussion so far. -3. If the issue references other issues or PRs, call `issue_read` or `pull_request_read` with method `get` on each to understand the broader context. -4. Use `grep` and file reading to explore the relevant parts of the codebase. +1. Read the full issue thread to understand the discussion so far. +2. If the issue references other issues or PRs, call `issue_read` or `pull_request_read` with method `get` on each to understand the broader context. +3. Use `grep` and file reading to explore the relevant parts of the codebase. ### Step 2: Investigate and Respond diff --git a/.github/workflows/gh-aw-mention-in-pr-by-id.invalid.yml b/.github/workflows/gh-aw-mention-in-pr-by-id.invalid.yml new file mode 100644 index 00000000..f103c3b0 --- /dev/null +++ b/.github/workflows/gh-aw-mention-in-pr-by-id.invalid.yml @@ -0,0 +1,2127 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw. DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# AI assistant for a specific PR ID — review, fix code, and push changes on demand +# +# Resolved workflow manifest: +# Imports: +# - gh-aw-fragments/elastic-tools.md +# - gh-aw-fragments/formatting.md +# - gh-aw-fragments/mcp-pagination.md +# - gh-aw-fragments/messages-footer.md +# - gh-aw-fragments/network-ecosystems.md +# - gh-aw-fragments/pick-three-keep-many.md +# - gh-aw-fragments/playwright-mcp-explorer.md +# - gh-aw-fragments/pr-context.md +# - gh-aw-fragments/review-process.md +# - gh-aw-fragments/rigor.md +# - gh-aw-fragments/runtime-setup.md +# - gh-aw-fragments/safe-output-add-comment-pr.md +# - gh-aw-fragments/safe-output-code-review.md +# - gh-aw-fragments/safe-output-push-to-pr.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 +# - gh-aw-fragments/workflow-edit-guardrails.md +# +# inlined-imports: true +# +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"cb8047e55822e668bc5e1fefd4697c54eb15c0fbba56b55d104e124cc2661cd1"} + +name: "Mention in PR by ID" +"on": + workflow_call: + inputs: + additional-instructions: + default: "" + description: Repo-specific instructions appended to the agent prompt + required: false + type: string + allowed-bot-users: + default: github-actions[bot] + description: Allowlisted bot actor usernames (comma-separated) + required: false + type: string + create-pull-request-review-comment-max: + default: "30" + description: Maximum number of review comments the agent can create per run + required: false + type: string + messages-footer: + default: "" + description: Footer appended to all agent comments and reviews + required: false + type: string + model: + default: gpt-5.3-codex + description: AI model to use + required: false + type: string + prompt: + description: Prompt for the agent + required: true + type: string + resolve-pull-request-review-thread-max: + default: "10" + description: Maximum number of review threads the agent can resolve per run + required: false + type: string + setup-commands: + default: "" + description: Shell commands to run before the agent starts (dependency install, build, etc.) + required: false + type: string + target-pr-number: + description: PR number to target + required: true + type: string + outputs: + comment_id: + description: ID of the first added comment + value: ${{ jobs.safe_outputs.outputs.comment_id }} + comment_url: + description: URL of the first added comment + value: ${{ jobs.safe_outputs.outputs.comment_url }} + push_commit_sha: + description: SHA of the pushed commit + value: ${{ jobs.safe_outputs.outputs.push_commit_sha }} + push_commit_url: + description: URL of the pushed commit + value: ${{ jobs.safe_outputs.outputs.push_commit_url }} + secrets: + COPILOT_GITHUB_TOKEN: + required: true + EXTRA_COMMIT_GITHUB_TOKEN: + required: false + +permissions: {} + +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-mention-pr-by-id-${{ inputs.target-pr-number }} + +run-name: "Mention in PR by ID" + +jobs: + activation: + needs: pre_activation + if: needs.pre_activation.outputs.activated == 'true' + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + comment_id: "" + comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: "${{ inputs.model }}" + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "0.0.420" + GH_AW_INFO_WORKFLOW_NAME: "Mention in PR by ID" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","clojure","cloud.elastic.co","containers","dart","defaults","dotnet","ela.st","elastic.co","elastic.dev","elastic.github.io","elixir","fonts","github","github-actions","go","haskell","java","kotlin","linux-distros","node","node-cdns","perl","php","playwright","public-code-search.fastmcp.app","python","ruby","rust","scala","swift","terraform","www.elastic.co","zig"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.23.0" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 + persist-credentials: false + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_WORKFLOW_FILE: "gh-aw-mention-in-pr-by-id.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_EXPR_91DD53F2: ${{ inputs.target-pr-number }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/playwright_prompt.md" + 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 + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" + cat << 'GH_AW_PROMPT_EOF' + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## MCP Servers + + - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Formatting Guidelines + + - Lead with the most important information — your first sentence should be the key takeaway + - Be concise and actionable — no filler or praise + - Use `
` and `` tags for long sections to keep responses scannable + - Wrap branch names and @-references in backticks to avoid pinging users + - Include code snippets with file paths and line numbers when referencing the codebase + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Rigor + + **Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report.** + + - If you claim something is missing or broken, show the exact evidence in the code — file path, line number, and what you observed. + - If a conclusion depends on assumptions you haven't confirmed, do not assert it. Verify first; if you cannot verify, do not report. + - "I don't know" is better than a wrong answer. `noop` is better than a speculative finding. + - It's worth the time to verify now versus guessing and forcing someone else to verify later. + - Before submitting any output, re-read it as a skeptical reviewer. Ask: "Would a senior engineer on this team find this useful, or would they close it immediately?" If the answer is "close," call `noop` instead. + - Only report findings you would confidently defend in a code review. If you feel the need to hedge with "might," "could," or "possibly," the finding is not ready to file. + - Be thorough, spend the time to investigate and verify. There is no rush. Do your best work. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## MCP Pagination + + MCP tool responses have a **25,000 token limit**. When responses exceed this limit, the call fails and you must retry with pagination — wasting turns and tokens. Use proactive pagination to stay under the limit. + + ### Recommended `perPage` Values + + - **5-10**: For detailed items (PR diffs, files with patches, issues with comments) + - **20-30**: For medium-detail lists (commits, review comments, issue lists) + - **50-100**: For simple list operations (branches, labels, tags) + + ### Pagination Pattern + + When you need all results from a paginated API: + + 1. Fetch the first page with a conservative `perPage` value + 2. Process the results before fetching the next page + 3. Continue fetching pages until you receive fewer results than `perPage` (indicating the last page) + + ### Error Recovery + + If you see an error like: + - `MCP tool response exceeds maximum allowed tokens (25000)` + - `Response too large for tool [tool_name]` + + Retry the same call with a smaller `perPage` value (halve it). + + ### Tips + + - **Start small**: It's better to make multiple small requests than one that fails + - **Fetch incrementally**: Get an overview first, then details for specific items + - **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' + ## Workflow Editing Guardrails + + - Do not modify files under `.github/workflows/`. + - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. + + 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. **Never mention these file paths or on-disk data sources in your responses** — they are internal implementation details invisible to users. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Code Review Reference + + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md`. Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md`. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Message Footer + + A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ### Pick Three, Keep Many + + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + + **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: + + - The full task description and objective (restate it, don't summarize) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) + - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) + - The quality criteria and output format you expect + - The specific angle that distinguishes this sub-agent from the others + + Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. + + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + + **Merge and deduplicate findings** across all sub-agents: + 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. + 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. + 3. Drop any finding that does not meet the verification criteria. + + **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Playwright MCP Tools + + You have Playwright MCP tools available for interactive browser automation. + Use these tools to explore the app step by step — do NOT write Node.js scripts. + + ### Available tools + + - `browser_navigate` — go to a URL + - `browser_click` — click an element + - `browser_type` — type text into an input + - `browser_snapshot` — get an accessibility tree (YAML) of the current page + - `browser_take_screenshot` — capture a screenshot + - `browser_console_execute` — run JavaScript in the browser console + + ### Why MCP tools instead of scripts + + MCP tools are interactive: you see the page state after each action and + decide what to do next. This is ideal for exploratory testing where you + need to adapt based on what you find. Scripts are fire-and-forget — if + a selector is wrong, you don't find out until the script fails. + + ### Measuring DOM properties + + For programmatic checks (e.g. element heights, contrast), use + `browser_console_execute`: + + ```javascript + (() => { + const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); + return JSON.stringify(Array.from(els) + .map(el => { + const r = el.getBoundingClientRect(); + return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; + }) + .filter(el => el.top > 50 && el.top < 250)); + })() + ``` + + ### Handling failures + + - Do not retry the same action more than twice — the page is in a different state than expected. + - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. + - Adapt (different selector, different path) or report the failure as a finding. + - Never claim you verified something you didn't — if it failed and you skipped it, say so. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## add-comment Limitations + + - **Body**: Max 65,536 characters (including any footer added by gh-aw). Keep well under this limit. + - **Mentions**: Max 10 `@` mentions per comment. + - **Links**: Max 50 URLs per comment. + - **HTML**: Only safe tags allowed (`details`, `summary`, `code`, `pre`, `blockquote`, `table`, `b`, `em`, `strong`, `h1`–`h6`, `hr`, `br`, `li`, `ol`, `ul`, `p`, `sub`, `sup`). Other tags are converted to parentheses. + - **URLs**: Only HTTPS URLs to allowed domains. Non-HTTPS and non-allowed domains are redacted. + - **Bot triggers**: References like `fixes #123` or `closes #456` are neutralized to prevent unintended issue closures. + + If you exceed 10 mentions or 50 links, the comment will be rejected. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## create-pull-request-review-comment + + - **Required fields**: `path` (file path), `line` (line number), and `body` (comment text). + - **Line**: Must be within the diff — an added or context line in the patch. Must be the **exact line number from reading the file** (not estimated from the patch). Lines outside the diff will fail. + - **Body**: Sanitized with the standard pipeline (mentions neutralized, HTML filtered, URLs restricted). GitHub API limit is ~65,536 characters. + - **Side**: Defaults to `RIGHT` (the new code). Use `LEFT` only when commenting on deleted lines. + - **Suggestion blocks**: Use ` ```suggestion ` fences for concrete code fixes. The suggestion must actually change the code — don't suggest identical code. Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. + + Only flag issues you are confident are real problems — false positives erode trust. Once you have flagged an issue, you cannot unflag it. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## submit-pull-request-review Limitations + + - **Event**: Must be one of `APPROVE`, `REQUEST_CHANGES`, or `COMMENT`. Defaults to `COMMENT` if omitted. + - **Body**: Max 65,000 characters. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. A body is required when event is `REQUEST_CHANGES`. Sanitized (mentions neutralized, HTML filtered, URLs restricted). If you have also used `create-pull-request-review-comment`, you do not need to repeat the same feedback in the body. If you "Approve" and have no comments, do not provide a `body`. + - **Own PRs**: If the workflow actor is also the PR author (e.g., `github-actions[bot]` reviewing its own PR), the event is forced to `COMMENT` regardless of what you specify. `APPROVE` and `REQUEST_CHANGES` will not work. + - **Max per run**: 1 review submission per workflow run. Leave inline comments first, then submit the review as a single final action. + + **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + Before calling `push_to_pull_request_branch`, call `ready_to_push_to_pr` and apply its checklist. + + ## push-to-pull-request-branch Limitations + + - **Patch size**: Max ~10 MB (10,240 KB). Keep changes focused — very large refactors may exceed this. + - **Fork PRs**: Cannot push to fork PR branches. Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, explain that you cannot push and suggest the author apply changes themselves. + - **Committed changes required**: You must have locally committed changes before calling push. Uncommitted or staged-only changes will fail. + - **Branch**: Pushes to the PR's head branch. The workspace must have the PR branch checked out. + - You may not submit code that modifies files in `.github/workflows/`. Doing so will cause the submission to be rejected. If asked to modify workflow files, propose the change in a copy placed in a `github/` folder (without the leading period) and note in the PR that the file needs to be relocated by someone with workflow write access. + + Trying to resolve merge conflicts? Do not use `git merge` or `git rebase` — `push_to_pull_request_branch` uses `git format-patch` which requires single-parent commits. Instead: + 1. Compare with the base branch (from `/tmp/pr-context/pr.json` field `baseRefName`) to see what changed in the conflicting files + 2. Edit the files directly to incorporate the changes from the base branch + 3. Commit the changes as regular (single-parent) commits + 4. Call `ready_to_push_to_pr` (which will catch any merge commits) and then `push_to_pull_request_branch` to push + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## resolve-pull-request-review-thread Limitations + + - **Required field**: `thread_id` — the GraphQL node ID of the review thread (e.g., `PRRT_kwDO...`). This is the `id` field from `get_review_comments`, not the numeric REST comment ID. + - **Only resolve what you've addressed**: Do not resolve threads you skipped, disagreed with, or didn't fix. Only resolve threads where your changes directly address the feedback. + - **Max per run**: __GH_AW_EXPR_7F2A702A__ thread resolutions per workflow run. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + # PR Assistant by ID + + Assist with pull request #__GH_AW_EXPR_91DD53F2__ on __GH_AW_GITHUB_REPOSITORY__. + + ## Context + + - **Repository**: __GH_AW_GITHUB_REPOSITORY__ + - **PR**: #__GH_AW_EXPR_91DD53F2__ + - **PR context on disk**: `/tmp/pr-context/` — PR metadata, diff, files, reviews, comments, and linked issues are pre-fetched. Use these as your primary source; fall back to API tools only when required data is unavailable. + - **Request**: "__GH_AW_INPUTS_PROMPT__" + + ## 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) + - **CANNOT**: Push to fork PR branches, merge PRs, delete branches + + ## Instructions + + 1. Read `/tmp/pr-context/pr.json` for PR details. Read `/tmp/pr-context/README.md` for a manifest of all pre-fetched PR context. + 2. Handle the request in `__GH_AW_INPUTS_PROMPT__` with focused changes and evidence. + 3. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_EXPR_91DD53F2__. + 4. Use safe outputs only against PR #__GH_AW_EXPR_91DD53F2__. + 5. If no code/review action is needed, call `add_comment` with a concise response. + + **If asked to review the PR:** + - Call `ready_to_code_review` to prepare the review approach based on PR size. + - Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. + - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). + - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. + - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. + - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, submit a `COMMENT` review only — `APPROVE` and `REQUEST_CHANGES` will fail. + + __GH_AW_EXPR_49B959F1__ + + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} + GH_AW_EXPR_91DD53F2: ${{ inputs.target-pr-number }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_EXPR_91DD53F2: ${{ inputs.target-pr-number }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, + GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, + GH_AW_EXPR_91DD53F2: process.env.GH_AW_EXPR_91DD53F2, + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, + GH_AW_INPUTS_PROMPT: process.env.GH_AW_INPUTS_PROMPT, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + issues: read + pull-requests: read + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}-mention-pr-by-id-${{ inputs.target-pr-number }}" + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_WORKFLOW_ID_SANITIZED: ghawmentioninprbyid + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + model: ${{ needs.activation.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - if: hashFiles('go.mod') != '' + name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + with: + cache: true + go-version-file: go.mod + - if: hashFiles('.python-version') != '' + name: Setup Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version-file: .python-version + - if: hashFiles('.node-version') != '' + name: Setup Node.js (.node-version) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + with: + node-version-file: .node-version + - if: hashFiles('.node-version') == '' && hashFiles('.nvmrc') != '' + name: Setup Node.js (.nvmrc) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + with: + node-version-file: .nvmrc + - if: hashFiles('.ruby-version') != '' + name: Setup Ruby + uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 + with: + bundler-cache: true + ruby-version: .ruby-version + - id: setup-uv + if: hashFiles('pyproject.toml', 'uv.lock') != '' + name: Setup uv + uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + - env: + UV_PATH: ${{ steps.setup-uv.outputs.uv-path }} + WORKSPACE: ${{ github.workspace }} + if: hashFiles('pyproject.toml', 'uv.lock') != '' + name: Expose uv in workspace + run: | + set -euo pipefail + install_dir="$WORKSPACE/.gh-aw-tools/bin" + mkdir -p "$install_dir" + cp "$UV_PATH" "$install_dir/uv" + chmod +x "$install_dir/uv" + echo "$install_dir" >> "$GITHUB_PATH" + shell: bash + - name: Configure Copilot CLI settings + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" + 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: + GITHUB_TOKEN: ${{ github.token }} + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + name: Ensure origin refs for PR patch generation + run: "SERVER_URL_STRIPPED=\"${SERVER_URL#https://}\"\ngit remote set-url origin \"https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git\"\ngit fetch --no-tags --prune origin '+refs/heads/*:refs/remotes/origin/*'\n" + - env: + SETUP_COMMANDS: ${{ inputs.setup-commands }} + if: ${{ inputs.setup-commands != '' }} + name: Repo-specific setup + run: eval "$SETUP_COMMANDS" + + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420 + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.6 ghcr.io/github/github-mcp-server:v0.31.0 mcr.microsoft.com/playwright/mcp node:lts-alpine + - name: Write Safe Outputs Config + run: | + mkdir -p /opt/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"add_comment":{"max":1,"target":"${{ inputs.target-pr-number }}"},"create_pull_request_review_comment":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":0,"target":"${{ inputs.target-pr-number }}"},"resolve_pull_request_review_thread":{"max":"${{ inputs.resolve-pull-request-review-thread-max }}"},"submit_pull_request_review":{"max":1}} + GH_AW_SAFE_OUTPUTS_CONFIG_EOF + cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' + [ + { + "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added. Target: ${{ inputs.target-pr-number }}.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", + "type": "string" + }, + "item_number": { + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the comment will be silently discarded.", + "type": "number" + } + }, + "required": [ + "body" + ], + "type": "object" + }, + "name": "add_comment" + }, + { + "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", + "type": "string" + }, + "line": { + "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", + "type": [ + "number", + "string" + ] + }, + "path": { + "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", + "type": "string" + }, + "side": { + "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", + "enum": [ + "LEFT", + "RIGHT" + ], + "type": "string" + }, + "start_line": { + "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "path", + "line", + "body" + ], + "type": "object" + }, + "name": "create_pull_request_review_comment" + }, + { + "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", + "type": "string" + }, + "event": { + "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", + "enum": [ + "APPROVE", + "REQUEST_CHANGES", + "COMMENT" + ], + "type": "string" + } + }, + "type": "object" + }, + "name": "submit_pull_request_review" + }, + { + "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": { + "additionalProperties": false, + "properties": { + "thread_id": { + "description": "The node ID of the review thread to resolve (e.g., 'PRRT_kwDOABCD...'). This is the GraphQL node ID, not a numeric ID.", + "type": "string" + } + }, + "required": [ + "thread_id" + ], + "type": "object" + }, + "name": "resolve_pull_request_review_thread" + }, + { + "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "branch": { + "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.", + "type": "string" + }, + "message": { + "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).", + "type": "string" + }, + "pull_request_number": { + "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "push_to_pull_request_branch" + }, + { + "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "reason": { + "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", + "type": "string" + }, + "tool": { + "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + }, + "name": "missing_tool" + }, + { + "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "message": { + "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "noop" + }, + { + "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "context": { + "description": "Additional context about the missing data or where it should come from (max 256 characters).", + "type": "string" + }, + "data_type": { + "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", + "type": "string" + }, + "reason": { + "description": "Explanation of why this data is needed to complete the task (max 256 characters).", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "missing_data" + } + ] + GH_AW_SAFE_OUTPUTS_TOOLS_EOF + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "create_pull_request_review_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "line": { + "required": true, + "positiveInteger": true + }, + "path": { + "required": true, + "type": "string" + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "side": { + "type": "string", + "enum": [ + "LEFT", + "RIGHT" + ] + }, + "start_line": { + "optionalPositiveInteger": true + } + }, + "customValidation": "startLineLessOrEqualLine" + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + }, + "push_to_pull_request_branch": { + "defaultMax": 1, + "fields": { + "branch": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "pull_request_number": { + "issueOrPRNumber": true + } + } + }, + "resolve_pull_request_review_thread": { + "defaultMax": 10, + "fields": { + "thread_id": { + "required": true, + "type": "string" + } + } + }, + "submit_pull_request_review": { + "defaultMax": 1, + "fields": { + "body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "event": { + "type": "string", + "enum": [ + "APPROVE", + "REQUEST_CHANGES", + "COMMENT" + ] + } + } + } + } + GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash /opt/gh-aw/actions/start_safe_outputs_server.sh + + - name: Setup Safe Inputs Config + run: | + mkdir -p /opt/gh-aw/safe-inputs/logs + cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' + { + "serverName": "safeinputs", + "version": "1.0.0", + "logDir": "/opt/gh-aw/safe-inputs/logs", + "tools": [ + { + "name": "ready-to-code-review", + "description": "Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-code-review.py", + "timeout": 60 + }, + { + "name": "ready-to-push-to-pr", + "description": "Run the PR readiness checklist before pushing to a PR", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-push-to-pr.py", + "timeout": 60 + } + ] + } + GH_AW_SAFE_INPUTS_TOOLS_EOF + cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' + const path = require("path"); + const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); + const configPath = path.join(__dirname, "tools.json"); + const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); + const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; + startHttpServer(configPath, { + port: port, + stateless: true, + logDir: "/opt/gh-aw/safe-inputs/logs" + }).catch(error => { + console.error("Failed to start safe-inputs HTTP server:", error); + process.exit(1); + }); + GH_AW_SAFE_INPUTS_SERVER_EOF + chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs + + - name: Setup Safe Inputs Tool Files + run: | + cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-code-review + # Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/ + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = '${{ inputs.minimum_severity || "low" }}' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) + + GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py + cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-push-to-pr + # Run the PR readiness checklist before pushing to a PR + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, subprocess + def find(*paths): + return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + 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') + + # Guard: detect history rewrites and merge commits + pr_json_path = '/tmp/pr-context/pr.json' + if os.path.isfile(pr_json_path): + with open(pr_json_path) as f: + pr_data = json.load(f) + pr_head_sha = pr_data.get('headRefOid', '') + if pr_head_sha: + # Check 1: PR head must be an ancestor of HEAD (no rebase/reset) + anc = run(['git', 'merge-base', '--is-ancestor', pr_head_sha, 'HEAD']) + if anc.returncode != 0: + print(json.dumps({'status': 'error', 'error': f'History rewrite detected: the original PR head ({pr_head_sha[:12]}) is not an ancestor of HEAD. This means git rebase, reset, or cherry-pick rewrote history. push_to_pull_request_branch will fail. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch to its original head, then re-apply your changes as direct file edits and commit as regular commits.'})) + raise SystemExit(0) + # Check 2: no merge commits (multiple parents) since PR head + log = run(['git', 'rev-list', '--min-parents=2', f'{pr_head_sha}..HEAD']) + if log.returncode != 0: + print(json.dumps({'status': 'error', 'error': f'Failed to check for merge commits (git rev-list exited {log.returncode}): {log.stderr.strip()}. Cannot verify commit history is safe for push.'})) + raise SystemExit(0) + merge_shas = log.stdout.strip() + if merge_shas: + print(json.dumps({'status': 'error', 'error': f'Merge commit(s) detected: {merge_shas.splitlines()[0][:12]}... push_to_pull_request_branch uses git format-patch which breaks on merge commits. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch, then re-apply your changes as direct file edits (no git merge/rebase/commit-tree with multiple -p flags) and commit as regular single-parent commits.'})) + raise SystemExit(0) + + 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') + agents_md = find('AGENTS.md', 'agents.md', '.github/agents.md', '.github/AGENTS.md') + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (vs common ancestor), fall back to + # @{upstream} 2-dot (vs upstream tip), 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 + # Capture commit messages so the sub-agent knows what changed and why + commits_text = '' + for log_cmd in [ + ['git', 'log', '--format=### %s%n%n%b', '@{upstream}..HEAD'], + ['git', 'log', '--format=### %s%n%n%b', '-10'], + ]: + result = run(log_cmd) + if result.stdout.strip(): + commits_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) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + with open('/tmp/self-review/commits.txt', 'w') as f: + f.write(commits_text) + # Copy task context if available (PR metadata from the pr-context step) + has_task = False + if os.path.isfile('/tmp/pr-context/pr.json'): + import shutil + shutil.copy('/tmp/pr-context/pr.json', '/tmp/self-review/task.json') + has_task = True + # Write manifest so the sub-agent has structured context without + # relying on the main agent to manually pass it in the prompt + manifest_lines = [ + '# Self-Review Context', + '', + 'You are reviewing unpushed changes before they are submitted to a PR.', + '', + '## Available files', + '', + 'All files are in `/tmp/self-review/`:', + '', + f'- `diff.patch` : Full unified diff of all changes ({len(diff_text.splitlines())} lines)', + '- `stat.txt` : File-level change summary', + '- `commits.txt` : Commit messages describing what was changed and why', + '- `notes.md` : Author notes on what was done, why, and key decisions made', + ] + if has_task: + manifest_lines.append('- `task.json` : Original PR context (title, body, author, branches)') + step = 1 + manifest_lines += ['', '## How to review', ''] + manifest_lines.append(f'{step}. Read `notes.md` to understand what the author did, why, and what decisions they made.') + step += 1 + manifest_lines.append(f'{step}. Read `commits.txt` for the commit-level view of what changed.') + step += 1 + if has_task: + manifest_lines.append(f'{step}. Read `task.json` to understand the original task or issue being addressed.') + step += 1 + manifest_lines.append(f'{step}. Read `stat.txt` for a high-level view of which files changed.') + step += 1 + manifest_lines.append(f'{step}. Read `diff.patch` and the relevant source files from the workspace (the branch is checked out).') + step += 1 + if agents_md: + manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + manifest_lines += [ + '', + '## Focus areas', + '', + 'Look for bugs, logic errors, missed edge cases, and style issues.', + 'Focus on what the author might have MISSED rather than re-deriving their reasoning.', + '', + '## What NOT to flag', + '', + '- Pre-existing issues not introduced by these changes', + '- Style preferences handled by linters or formatters', + '- Theoretical performance concerns without evidence of real-world impact', + ] + with open('/tmp/self-review/README.md', 'w') as f: + f.write('\n'.join(manifest_lines) + '\n') + 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.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) and supporting context have been saved to `/tmp/self-review/`. Before spawning the sub-agent, write `/tmp/self-review/notes.md` with: what you changed and why, which files matter most and what they do, edge cases you already handled, and what test coverage exists. Then spawn a `code-review` sub-agent via `runSubagent` and tell it to start by reading `/tmp/self-review/README.md`. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_push_to_pr` again.') + 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-PUSH-TO-PR_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py + + - name: Generate Safe Inputs MCP Server Config + id: safe-inputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3000 + + # Set outputs for next steps + { + echo "safe_inputs_api_key=${API_KEY}" + echo "safe_inputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Inputs MCP server will run on port ${PORT}" + + - name: Start Safe Inputs MCP HTTP Server + id: safe-inputs-start + env: + DEBUG: '*' + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_INPUTS_PORT + export GH_AW_SAFE_INPUTS_API_KEY + + bash /opt/gh-aw/actions/start_safe_inputs_server.sh + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + mkdir -p /tmp/gh-aw/mcp-logs/playwright + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.31.0", + "env": { + "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "repos,issues,pull_requests,search,actions" + } + }, + "playwright": { + "type": "stdio", + "container": "mcr.microsoft.com/playwright/mcp", + "args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"], + "entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"], + "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"] + }, + "public-code-search": { + "type": "http", + "url": "https://public-code-search.fastmcp.app/mcp", + "tools": [ + "search_code" + ] + }, + "safeinputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_EOF + - name: Download activation artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: activation + path: /tmp/gh-aw + - name: Clean git credentials + run: bash /opt/gh-aw/actions/clean_git_credentials.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 60 + run: | + set -o pipefail + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ inputs.model }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload Safe Outputs + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output + path: ${{ env.GH_AW_SAFE_OUTPUTS }} + if-no-files-found: warn + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + 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 }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Upload sanitized agent output + if: always() && env.GH_AW_AGENT_OUTPUT + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-output + path: ${{ env.GH_AW_AGENT_OUTPUT }} + if-no-files-found: warn + - name: Upload engine output files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent_outputs + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + if-no-files-found: ignore + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse Safe Inputs logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-artifacts + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/safe-inputs/logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + /tmp/gh-aw/aw-*.patch + if-no-files-found: ignore + # --- Threat Detection (inline) --- + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + WORKFLOW_NAME: "Mention in PR by ID" + WORKFLOW_DESCRIPTION: "AI assistant for a specific PR ID — review, fix code, and push changes on demand" + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ inputs.model }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_detection_results + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: threat-detection.log + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Set detection conclusion + id: detection_conclusion + if: always() + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} + run: | + if [[ "$RUN_DETECTION" != "true" ]]; then + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection was not needed, marking as skipped" + elif [[ "$DETECTION_SUCCESS" == "true" ]]; then + echo "conclusion=success" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection passed successfully" + else + echo "conclusion=failure" >> "$GITHUB_OUTPUT" + echo "success=false" >> "$GITHUB_OUTPUT" + echo "Detection found issues" + fi + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: write + discussions: write + issues: write + pull-requests: write + outputs: + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "Mention in PR by ID" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing Tool + id: missing_tool + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR by ID" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent Failure + id: handle_agent_failure + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR by ID" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr-by-id" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} + GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" + GH_AW_GROUP_REPORTS: "false" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op Message + id: handle_noop_message + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR by ID" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + pre_activation: + runs-on: ubuntu-slim + outputs: + activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} + matched_command: '' + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Check team membership for workflow + id: check_membership + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_REQUIRED_ROLES: admin,maintainer,write + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); + await main(); + + safe_outputs: + needs: + - activation + - agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') + runs-on: ubuntu-slim + permissions: + contents: write + discussions: write + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_MODEL: "${{ inputs.model }}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" + GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr-by-id" + GH_AW_WORKFLOW_NAME: "Mention in PR by ID" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + push_commit_sha: ${{ steps.process_safe_outputs.outputs.push_commit_sha }} + push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Download patch artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-artifacts + path: /tmp/gh-aw/ + - name: Checkout repository + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} + token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + persist-credentials: false + fetch-depth: 1 + - name: Configure Git credentials + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + 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,\"target\":\"${{ inputs.target-pr-number }}\"},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\",\"target\":\"${{ inputs.target-pr-number }}\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max_patch_size\":10240,\"target\":\"${{ inputs.target-pr-number }}\"},\"resolve_pull_request_review_thread\":{\"max\":\"${{ inputs.resolve-pull-request-review-thread-max }}\"},\"submit_pull_request_review\":{\"max\":1,\"target\":\"${{ inputs.target-pr-number }}\"}}" + GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.EXTRA_COMMIT_GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload safe output items manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn + 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 6cf553d1..922ab4d5 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 @@ -30,12 +30,14 @@ # - gh-aw-fragments/mcp-pagination.md # - gh-aw-fragments/messages-footer.md # - gh-aw-fragments/network-ecosystems.md +# - gh-aw-fragments/pick-three-keep-many.md # - gh-aw-fragments/playwright-mcp-explorer.md # - gh-aw-fragments/pr-context.md # - gh-aw-fragments/review-process.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-pr.md +# - gh-aw-fragments/safe-output-code-review.md # - gh-aw-fragments/safe-output-push-to-pr.md # - gh-aw-fragments/safe-output-resolve-thread.md # - gh-aw-fragments/safe-output-review-comment.md @@ -44,7 +46,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"362b18bbc4870dc3aa28cfdcecd22e20c6f3ef0c608ecd43ffcf4ccd3b91f6d7"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0dca38278f9109c09297caa208f8cdfbe276764e3859deac129bf82e4c953b23"} name: "Mention in PR by ID" "on": @@ -134,7 +136,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -188,9 +190,7 @@ jobs: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} GH_AW_EXPR_91DD53F2: ${{ inputs.target-pr-number }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} @@ -200,6 +200,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} run: | bash /opt/gh-aw/actions/create_prompt_first.sh @@ -254,11 +255,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -333,90 +333,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Code Review Reference - ### Comment Format - - Call **`create_pull_request_review_comment`** with: - - The file path and the **exact line number from reading the file** (not estimated from the diff) - - The line must be within the diff (an added or context line in the patch) - - ````` - **[SEVERITY] Brief title** - - Description of the issue and why it matters. - - ```suggestion - corrected code here - ``` - ````` - - Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line. - - ### Inline Comment Threshold - - The minimum severity for inline comments is `__GH_AW_EXPR_7B543459__`. - - Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body instead — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters. - - Severity order (highest to lowest): critical > high > medium > low > nitpick. - - If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`. - - ### Review Criteria - - Focus on these categories in priority order: - - 1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure) - 2. Logic bugs that could cause runtime failures or incorrect behavior - 3. Data integrity issues (race conditions, missing transactions, corruption risk) - 4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations) - 5. Error handling gaps (unhandled exceptions, missing validation) - 6. Breaking changes to public APIs without migration path - 7. Missing or incorrect test coverage for critical paths - - ### What NOT to Flag - - Only 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. - - **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: - - - **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. - - **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. - - **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). - - **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. - - **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. - - **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods. - - **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). - - **Existing review threads** — check BEFORE leaving any comment: - - - **Resolved with reviewer reply** (e.g. "This is intentional") — reviewer's decision is final. Do NOT re-flag. - - **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem. - - **Unresolved** — already flagged. Do NOT duplicate. - - **Outdated** — only re-flag if the issue still applies to the current diff. - - When in doubt, do not duplicate. Redundant comments erode trust. - - Finding no issues is a valid and valuable outcome. An APPROVE with zero inline comments is better than comments that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, approve without comments. - - ### Severity Classification - - Determine 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. Do not start with a severity and look for issues to match it. - - - 🔴 **CRITICAL** — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs) - - 🟠 **HIGH** — Should fix before merge (logic errors, missing validation, significant performance issues) - - 🟡 **MEDIUM** — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases) - - ⚪ **LOW** — Author discretion (minor improvements, documentation gaps) - - 💬 **NITPICK** — Truly optional (stylistic preferences, alternative approaches) - - ### Review Intensity - - The review intensity is `__GH_AW_EXPR_8D9F5797__`. - - - **`conservative`**: High evidence bar. Only comment when you can demonstrate a concrete failure scenario — what specific input or state triggers the bug. After identifying a potential issue, explicitly challenge your own finding: if you can construct a reasonable counterargument, do not comment. Give the author maximum benefit of the doubt. Approval with zero comments is the expected outcome for most PRs. - - **`balanced`**: Standard evidence bar. Comment when you can point to specific code that would fail and have verified the issue through the full verification protocol. Give the author reasonable benefit of the doubt — if the issue is ambiguous, lean toward not commenting. - - **`aggressive`**: Lower evidence bar. Comment when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions and alternative approaches are welcome but must still cite specific code. Do not speculate without any evidence, and do not duplicate existing threads. - - If the value is unrecognized, treat it as `balanced`. + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -424,50 +341,37 @@ jobs: A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - ## Playwright MCP Tools + ### Pick Three, Keep Many - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. - ### Available tools + **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console + - The full task description and objective (restate it, don't summarize) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) + - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) + - The quality criteria and output format you expect + - The specific angle that distinguishes this sub-agent from the others - ### Why MCP tools instead of scripts + Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. - ### Measuring DOM properties + **Merge and deduplicate findings** across all sub-agents: + 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. + 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. + 3. Drop any finding that does not meet the verification criteria. - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: + **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' - ### Handling failures + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Playwright MCP Tools - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. + Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## add-comment Limitations @@ -548,12 +452,23 @@ jobs: ## Instructions - 1. Call `generate_agents_md` to get repository conventions. - 2. Read `/tmp/pr-context/pr.json` for PR details. Read `/tmp/pr-context/README.md` for a manifest of all pre-fetched PR context. - 3. Handle the request in `__GH_AW_INPUTS_PROMPT__` with focused changes and evidence. - 4. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_EXPR_91DD53F2__. - 5. Use safe outputs only against PR #__GH_AW_EXPR_91DD53F2__. - 6. If no code/review action is needed, call `add_comment` with a concise response. + 1. Read `/tmp/pr-context/pr.json` for PR details. Read `/tmp/pr-context/README.md` for a manifest of all pre-fetched PR context. + 2. Handle the request in `__GH_AW_INPUTS_PROMPT__` with focused changes and evidence. + 3. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_EXPR_91DD53F2__. + 4. Use safe outputs only against PR #__GH_AW_EXPR_91DD53F2__. + 5. If no code/review action is needed, call `add_comment` with a concise response. + + **If asked to review the PR:** + - Call `ready_to_code_review` to prepare the review approach based on PR size. + - Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. + - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). + - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. + - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. + - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, submit a `COMMENT` review only — `APPROVE` and `REQUEST_CHANGES` will fail. __GH_AW_EXPR_49B959F1__ @@ -563,8 +478,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} @@ -581,9 +495,7 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} GH_AW_EXPR_91DD53F2: ${{ inputs.target-pr-number }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} @@ -593,6 +505,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} with: @@ -607,9 +520,7 @@ jobs: file: process.env.GH_AW_PROMPT, substitutions: { GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, - GH_AW_EXPR_7B543459: process.env.GH_AW_EXPR_7B543459, GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, - GH_AW_EXPR_8D9F5797: process.env.GH_AW_EXPR_8D9F5797, GH_AW_EXPR_91DD53F2: process.env.GH_AW_EXPR_91DD53F2, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, @@ -619,6 +530,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, GH_AW_INPUTS_PROMPT: process.env.GH_AW_INPUTS_PROMPT, GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED } @@ -671,21 +583,21 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - if: hashFiles('.python-version') != '' name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 @@ -725,15 +637,22 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `parent-review.md` | Comment format and inline severity threshold for the parent agent (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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" + 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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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" + - name: Write Playwright instructions to disk + run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: GITHUB_TOKEN: ${{ github.token }} REPO_NAME: ${{ github.repository }} @@ -1210,6 +1129,16 @@ jobs: "version": "1.0.0", "logDir": "/opt/gh-aw/safe-inputs/logs", "tools": [ + { + "name": "ready-to-code-review", + "description": "Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-code-review.py", + "timeout": 60 + }, { "name": "ready-to-push-to-pr", "description": "Run the PR readiness checklist before pushing to a PR", @@ -1242,6 +1171,135 @@ jobs: - name: Setup Safe Inputs Tool Files run: | + cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-code-review + # Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/ + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = os.environ.get('GH_AW_INPUTS_MINIMUM_SEVERITY', 'low') or 'low' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) + + GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' #!/usr/bin/env python3 # Auto-generated safe-input tool: ready-to-push-to-pr @@ -1464,13 +1522,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1833,7 +1884,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1921,7 +1972,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1968,7 +2019,7 @@ jobs: push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-mention-in-pr-by-id.md b/.github/workflows/gh-aw-mention-in-pr-by-id.md index a224f864..6d832993 100644 --- a/.github/workflows/gh-aw-mention-in-pr-by-id.md +++ b/.github/workflows/gh-aw-mention-in-pr-by-id.md @@ -12,6 +12,8 @@ imports: - gh-aw-fragments/pr-context.md - gh-aw-fragments/review-process.md - gh-aw-fragments/messages-footer.md + - gh-aw-fragments/pick-three-keep-many.md + - gh-aw-fragments/safe-output-code-review.md - gh-aw-fragments/playwright-mcp-explorer.md - gh-aw-fragments/safe-output-add-comment-pr.md - gh-aw-fragments/safe-output-review-comment.md @@ -137,11 +139,22 @@ Assist with pull request #${{ inputs.target-pr-number }} on ${{ github.repositor ## Instructions -1. Call `generate_agents_md` to get repository conventions. -2. Read `/tmp/pr-context/pr.json` for PR details. Read `/tmp/pr-context/README.md` for a manifest of all pre-fetched PR context. -3. Handle the request in `${{ inputs.prompt }}` with focused changes and evidence. -4. Do not modify, review, comment on, or resolve threads for any PR other than #${{ inputs.target-pr-number }}. -5. Use safe outputs only against PR #${{ inputs.target-pr-number }}. -6. If no code/review action is needed, call `add_comment` with a concise response. +1. Read `/tmp/pr-context/pr.json` for PR details. Read `/tmp/pr-context/README.md` for a manifest of all pre-fetched PR context. +2. Handle the request in `${{ inputs.prompt }}` with focused changes and evidence. +3. Do not modify, review, comment on, or resolve threads for any PR other than #${{ inputs.target-pr-number }}. +4. Use safe outputs only against PR #${{ inputs.target-pr-number }}. +5. If no code/review action is needed, call `add_comment` with a concise response. + +**If asked to review the PR:** +- Call `ready_to_code_review` to prepare the review approach based on PR size. +- Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. +- Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). +- When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. +- Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. +- **Bot-authored PRs**: If the PR author is `github-actions[bot]`, submit a `COMMENT` review only — `APPROVE` and `REQUEST_CHANGES` will fail. ${{ inputs.additional-instructions }} diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.invalid.yml b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.invalid.yml new file mode 100644 index 00000000..2eca3942 --- /dev/null +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.invalid.yml @@ -0,0 +1,2097 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw. DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# AI assistant for PRs — review, fix code, and push changes on demand (no agent sandbox, Docker access available) +# +# Resolved workflow manifest: +# Imports: +# - gh-aw-fragments/elastic-tools.md +# - gh-aw-fragments/formatting.md +# - gh-aw-fragments/mcp-pagination.md +# - gh-aw-fragments/messages-footer.md +# - gh-aw-fragments/network-ecosystems.md +# - gh-aw-fragments/pick-three-keep-many.md +# - gh-aw-fragments/playwright-mcp-explorer.md +# - gh-aw-fragments/pr-context.md +# - gh-aw-fragments/review-process.md +# - gh-aw-fragments/rigor.md +# - gh-aw-fragments/runtime-setup.md +# - gh-aw-fragments/safe-output-add-comment-pr.md +# - gh-aw-fragments/safe-output-code-review.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 +# - gh-aw-fragments/workflow-edit-guardrails.md +# +# inlined-imports: true +# +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"980c2cd07b38e68ee3965fe18a4d73359dbc670c1cbdd86cb451b8977ee120c4"} + +name: "Mention in PR (no sandbox)" +"on": + workflow_call: + inputs: + additional-instructions: + default: "" + description: Repo-specific instructions appended to the agent prompt + required: false + type: string + allowed-bot-users: + default: github-actions[bot] + description: Allowlisted bot actor usernames (comma-separated) + required: false + type: string + create-pull-request-review-comment-max: + default: "30" + description: Maximum number of review comments the agent can create per run + required: false + type: string + messages-footer: + default: "" + description: Footer appended to all agent comments and reviews + required: false + type: string + model: + default: gpt-5.3-codex + description: AI model to use + required: false + type: string + resolve-pull-request-review-thread-max: + default: "10" + description: Maximum number of review threads the agent can resolve per run + required: false + type: string + setup-commands: + default: "" + description: Shell commands to run before the agent starts (dependency install, build, etc.) + required: false + type: string + outputs: + comment_id: + description: ID of the first added comment + value: ${{ jobs.safe_outputs.outputs.comment_id }} + comment_url: + description: URL of the first added comment + value: ${{ jobs.safe_outputs.outputs.comment_url }} + push_commit_sha: + description: SHA of the pushed commit + value: ${{ jobs.safe_outputs.outputs.push_commit_sha }} + push_commit_url: + description: URL of the pushed commit + value: ${{ jobs.safe_outputs.outputs.push_commit_url }} + secrets: + COPILOT_GITHUB_TOKEN: + required: true + EXTRA_COMMIT_GITHUB_TOKEN: + required: false + +permissions: {} + +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-mention-pr-no-sandbox-${{ github.event.issue.number }} + +run-name: "Mention in PR (no sandbox)" + +jobs: + activation: + needs: pre_activation + if: needs.pre_activation.outputs.activated == 'true' + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + outputs: + body: ${{ steps.sanitized.outputs.body }} + comment_id: "" + comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + text: ${{ steps.sanitized.outputs.text }} + title: ${{ steps.sanitized.outputs.title }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: "${{ inputs.model }}" + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "0.0.420" + GH_AW_INFO_WORKFLOW_NAME: "Mention in PR (no sandbox)" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","clojure","cloud.elastic.co","containers","dart","defaults","dotnet","ela.st","elastic.co","elastic.dev","elastic.github.io","elixir","fonts","github","github-actions","go","haskell","java","kotlin","linux-distros","node","node-cdns","perl","php","playwright","public-code-search.fastmcp.app","python","ruby","rust","scala","swift","terraform","www.elastic.co","zig"]' + GH_AW_INFO_FIREWALL_ENABLED: "false" + GH_AW_INFO_AWF_VERSION: "" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "" + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 + persist-credentials: false + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_WORKFLOW_FILE: "gh-aw-mention-in-pr-no-sandbox.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Compute current body text + id: sanitized + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/compute_text.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/playwright_prompt.md" + 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, 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' + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## MCP Servers + + - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Formatting Guidelines + + - Lead with the most important information — your first sentence should be the key takeaway + - Be concise and actionable — no filler or praise + - Use `
` and `` tags for long sections to keep responses scannable + - Wrap branch names and @-references in backticks to avoid pinging users + - Include code snippets with file paths and line numbers when referencing the codebase + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Rigor + + **Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report.** + + - If you claim something is missing or broken, show the exact evidence in the code — file path, line number, and what you observed. + - If a conclusion depends on assumptions you haven't confirmed, do not assert it. Verify first; if you cannot verify, do not report. + - "I don't know" is better than a wrong answer. `noop` is better than a speculative finding. + - It's worth the time to verify now versus guessing and forcing someone else to verify later. + - Before submitting any output, re-read it as a skeptical reviewer. Ask: "Would a senior engineer on this team find this useful, or would they close it immediately?" If the answer is "close," call `noop` instead. + - Only report findings you would confidently defend in a code review. If you feel the need to hedge with "might," "could," or "possibly," the finding is not ready to file. + - Be thorough, spend the time to investigate and verify. There is no rush. Do your best work. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## MCP Pagination + + MCP tool responses have a **25,000 token limit**. When responses exceed this limit, the call fails and you must retry with pagination — wasting turns and tokens. Use proactive pagination to stay under the limit. + + ### Recommended `perPage` Values + + - **5-10**: For detailed items (PR diffs, files with patches, issues with comments) + - **20-30**: For medium-detail lists (commits, review comments, issue lists) + - **50-100**: For simple list operations (branches, labels, tags) + + ### Pagination Pattern + + When you need all results from a paginated API: + + 1. Fetch the first page with a conservative `perPage` value + 2. Process the results before fetching the next page + 3. Continue fetching pages until you receive fewer results than `perPage` (indicating the last page) + + ### Error Recovery + + If you see an error like: + - `MCP tool response exceeds maximum allowed tokens (25000)` + - `Response too large for tool [tool_name]` + + Retry the same call with a smaller `perPage` value (halve it). + + ### Tips + + - **Start small**: It's better to make multiple small requests than one that fails + - **Fetch incrementally**: Get an overview first, then details for specific items + - **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' + ## Workflow Editing Guardrails + + - Do not modify files under `.github/workflows/`. + - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. + + 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. **Never mention these file paths or on-disk data sources in your responses** — they are internal implementation details invisible to users. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Code Review Reference + + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md`. Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md`. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Message Footer + + A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Playwright MCP Tools + + You have Playwright MCP tools available for interactive browser automation. + Use these tools to explore the app step by step — do NOT write Node.js scripts. + + ### Available tools + + - `browser_navigate` — go to a URL + - `browser_click` — click an element + - `browser_type` — type text into an input + - `browser_snapshot` — get an accessibility tree (YAML) of the current page + - `browser_take_screenshot` — capture a screenshot + - `browser_console_execute` — run JavaScript in the browser console + + ### Why MCP tools instead of scripts + + MCP tools are interactive: you see the page state after each action and + decide what to do next. This is ideal for exploratory testing where you + need to adapt based on what you find. Scripts are fire-and-forget — if + a selector is wrong, you don't find out until the script fails. + + ### Measuring DOM properties + + For programmatic checks (e.g. element heights, contrast), use + `browser_console_execute`: + + ```javascript + (() => { + const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); + return JSON.stringify(Array.from(els) + .map(el => { + const r = el.getBoundingClientRect(); + return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; + }) + .filter(el => el.top > 50 && el.top < 250)); + })() + ``` + + ### Handling failures + + - Do not retry the same action more than twice — the page is in a different state than expected. + - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. + - Adapt (different selector, different path) or report the failure as a finding. + - Never claim you verified something you didn't — if it failed and you skipped it, say so. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ### Pick Three, Keep Many + + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + + **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: + + - The full task description and objective (restate it, don't summarize) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) + - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) + - The quality criteria and output format you expect + - The specific angle that distinguishes this sub-agent from the others + + Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. + + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + + **Merge and deduplicate findings** across all sub-agents: + 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. + 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. + 3. Drop any finding that does not meet the verification criteria. + + **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## add-comment Limitations + + - **Body**: Max 65,536 characters (including any footer added by gh-aw). Keep well under this limit. + - **Mentions**: Max 10 `@` mentions per comment. + - **Links**: Max 50 URLs per comment. + - **HTML**: Only safe tags allowed (`details`, `summary`, `code`, `pre`, `blockquote`, `table`, `b`, `em`, `strong`, `h1`–`h6`, `hr`, `br`, `li`, `ol`, `ul`, `p`, `sub`, `sup`). Other tags are converted to parentheses. + - **URLs**: Only HTTPS URLs to allowed domains. Non-HTTPS and non-allowed domains are redacted. + - **Bot triggers**: References like `fixes #123` or `closes #456` are neutralized to prevent unintended issue closures. + + If you exceed 10 mentions or 50 links, the comment will be rejected. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## create-pull-request-review-comment + + - **Required fields**: `path` (file path), `line` (line number), and `body` (comment text). + - **Line**: Must be within the diff — an added or context line in the patch. Must be the **exact line number from reading the file** (not estimated from the patch). Lines outside the diff will fail. + - **Body**: Sanitized with the standard pipeline (mentions neutralized, HTML filtered, URLs restricted). GitHub API limit is ~65,536 characters. + - **Side**: Defaults to `RIGHT` (the new code). Use `LEFT` only when commenting on deleted lines. + - **Suggestion blocks**: Use ` ```suggestion ` fences for concrete code fixes. The suggestion must actually change the code — don't suggest identical code. Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. + + Only flag issues you are confident are real problems — false positives erode trust. Once you have flagged an issue, you cannot unflag it. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## submit-pull-request-review Limitations + + - **Event**: Must be one of `APPROVE`, `REQUEST_CHANGES`, or `COMMENT`. Defaults to `COMMENT` if omitted. + - **Body**: Max 65,000 characters. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. A body is required when event is `REQUEST_CHANGES`. Sanitized (mentions neutralized, HTML filtered, URLs restricted). If you have also used `create-pull-request-review-comment`, you do not need to repeat the same feedback in the body. If you "Approve" and have no comments, do not provide a `body`. + - **Own PRs**: If the workflow actor is also the PR author (e.g., `github-actions[bot]` reviewing its own PR), the event is forced to `COMMENT` regardless of what you specify. `APPROVE` and `REQUEST_CHANGES` will not work. + - **Max per run**: 1 review submission per workflow run. Leave inline comments first, then submit the review as a single final action. + + **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + Before calling `push_to_pull_request_branch`, call `ready_to_push_to_pr` and apply its checklist. + + ## push-to-pull-request-branch Limitations + + - **Patch size**: Max ~10 MB (10,240 KB). Keep changes focused — very large refactors may exceed this. + - **Fork PRs**: Cannot push to fork PR branches. Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, explain that you cannot push and suggest the author apply changes themselves. + - **Committed changes required**: You must have locally committed changes before calling push. Uncommitted or staged-only changes will fail. + - **Branch**: Pushes to the PR's head branch. The workspace must have the PR branch checked out. + - You may not submit code that modifies files in `.github/workflows/`. Doing so will cause the submission to be rejected. If asked to modify workflow files, propose the change in a copy placed in a `github/` folder (without the leading period) and note in the PR that the file needs to be relocated by someone with workflow write access. + + Trying to resolve merge conflicts? Do not use `git merge` or `git rebase` — `push_to_pull_request_branch` uses `git format-patch` which requires single-parent commits. Instead: + 1. Compare with the base branch (from `/tmp/pr-context/pr.json` field `baseRefName`) to see what changed in the conflicting files + 2. Edit the files directly to incorporate the changes from the base branch + 3. Commit the changes as regular (single-parent) commits + 4. Call `ready_to_push_to_pr` (which will catch any merge commits) and then `push_to_pull_request_branch` to push + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## resolve-pull-request-review-thread Limitations + + - **Required field**: `thread_id` — the GraphQL node ID of the review thread (e.g., `PRRT_kwDO...`). This is the `id` field from `get_review_comments`, not the numeric REST comment ID. + - **Only resolve what you've addressed**: Do not resolve threads you skipped, disagreed with, or didn't fix. Only resolve threads where your changes directly address the feedback. + - **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 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. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + # PR Assistant + + Assist with pull requests on __GH_AW_GITHUB_REPOSITORY__ — review code, fix issues, answer questions, and push changes. + + ## Context + + - **Repository**: __GH_AW_GITHUB_REPOSITORY__ + - **PR**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ — __GH_AW_GITHUB_EVENT_ISSUE_TITLE__ + - **PR context on disk**: `/tmp/pr-context/` — PR metadata, diff, files, reviews, comments, and linked issues are pre-fetched. Use these as your primary source; fall back to API tools only when required data is unavailable. + - **Request**: "__GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT__" + + ## Constraints + + - **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 + + ## Instructions + + Understand the request, investigate the code, and respond appropriately. + + ### 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. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). + 2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. + 3. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. + 4. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__. + + ### Step 2: Handle the Request + + Based on what's asked, do the appropriate thing: + + **If asked to review the PR:** + - Call `ready_to_code_review` to prepare the review approach based on PR size. + - Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. + - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). + - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. + - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. + - **Important**: Substantive feedback belongs in the PR review (inline comments + review submission), NOT in a reply comment. Do NOT add a separate comment after submitting the review unless the user explicitly asked for a comment or the review submission failed. + - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. + + **If asked to fix code or address review feedback:** + - Read `/tmp/pr-context/unresolved_threads.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. + - 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. + - Use `push_to_pull_request_branch` to push your changes. + - 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/unresolved_threads.json` for all unresolved threads — also check `/tmp/pr-context/outdated_threads.json` for threads where the underlying code changed since the comment was made and verify 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:** + - Find the relevant code and explain it. + - Use `grep` and file reading to gather context. + - Use `web-fetch` to look up documentation when needed. + + **If the request is unclear:** + - Ask clarifying questions rather than guessing. + + ### Step 3: Respond + + If you did not submit a PR review, call `add_comment` with your response. If you submitted a review, do NOT call `add_comment` unless explicitly requested or to report a review submission failure. + + **Additional tools:** + - `push_to_pull_request_branch` — push committed changes to the PR branch (same-repo PRs only) + - `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__ + + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, + GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_TITLE: process.env.GH_AW_GITHUB_EVENT_ISSUE_TITLE, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED, + GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: process.env.GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + issues: read + pull-requests: read + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}-mention-pr-no-sandbox-${{ github.event.issue.number }}" + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_WORKFLOW_ID_SANITIZED: ghawmentioninprnosandbox + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + model: ${{ needs.activation.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - if: hashFiles('go.mod') != '' + name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + with: + cache: true + go-version-file: go.mod + - if: hashFiles('.python-version') != '' + name: Setup Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version-file: .python-version + - if: hashFiles('.node-version') != '' + name: Setup Node.js (.node-version) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + with: + node-version-file: .node-version + - if: hashFiles('.node-version') == '' && hashFiles('.nvmrc') != '' + name: Setup Node.js (.nvmrc) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + with: + node-version-file: .nvmrc + - if: hashFiles('.ruby-version') != '' + name: Setup Ruby + uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 + with: + bundler-cache: true + ruby-version: .ruby-version + - id: setup-uv + if: hashFiles('pyproject.toml', 'uv.lock') != '' + name: Setup uv + uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + - env: + UV_PATH: ${{ steps.setup-uv.outputs.uv-path }} + WORKSPACE: ${{ github.workspace }} + if: hashFiles('pyproject.toml', 'uv.lock') != '' + name: Expose uv in workspace + run: | + set -euo pipefail + install_dir="$WORKSPACE/.gh-aw-tools/bin" + mkdir -p "$install_dir" + cp "$UV_PATH" "$install_dir/uv" + chmod +x "$install_dir/uv" + echo "$install_dir" >> "$GITHUB_PATH" + shell: bash + - name: Configure Copilot CLI settings + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" + 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: + GITHUB_TOKEN: ${{ github.token }} + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + name: Ensure origin refs for PR patch generation + run: "SERVER_URL_STRIPPED=\"${SERVER_URL#https://}\"\ngit remote set-url origin \"https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git\"\ngit fetch --no-tags --prune origin '+refs/heads/*:refs/remotes/origin/*'\n" + - env: + SETUP_COMMANDS: ${{ inputs.setup-commands }} + if: ${{ inputs.setup-commands != '' }} + name: Repo-specific setup + run: eval "$SETUP_COMMANDS" + + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/github-mcp-server:v0.31.0 mcr.microsoft.com/playwright/mcp node:lts-alpine + - name: Write Safe Outputs Config + run: | + mkdir -p /opt/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"add_comment":{"max":1},"create_pull_request_review_comment":{"max":"${{ inputs.create-pull-request-review-comment-max }}"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":0},"resolve_pull_request_review_thread":{"max":"${{ inputs.resolve-pull-request-review-thread-max }}"},"submit_pull_request_review":{"max":1}} + GH_AW_SAFE_OUTPUTS_CONFIG_EOF + cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' + [ + { + "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", + "type": "string" + }, + "item_number": { + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the comment will be silently discarded.", + "type": "number" + } + }, + "required": [ + "body" + ], + "type": "object" + }, + "name": "add_comment" + }, + { + "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Comments will be on the RIGHT side of the diff.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", + "type": "string" + }, + "line": { + "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", + "type": [ + "number", + "string" + ] + }, + "path": { + "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", + "type": "string" + }, + "side": { + "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", + "enum": [ + "LEFT", + "RIGHT" + ], + "type": "string" + }, + "start_line": { + "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "path", + "line", + "body" + ], + "type": "object" + }, + "name": "create_pull_request_review_comment" + }, + { + "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", + "type": "string" + }, + "event": { + "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", + "enum": [ + "APPROVE", + "REQUEST_CHANGES", + "COMMENT" + ], + "type": "string" + } + }, + "type": "object" + }, + "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": { + "additionalProperties": false, + "properties": { + "thread_id": { + "description": "The node ID of the review thread to resolve (e.g., 'PRRT_kwDOABCD...'). This is the GraphQL node ID, not a numeric ID.", + "type": "string" + } + }, + "required": [ + "thread_id" + ], + "type": "object" + }, + "name": "resolve_pull_request_review_thread" + }, + { + "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "branch": { + "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.", + "type": "string" + }, + "message": { + "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).", + "type": "string" + }, + "pull_request_number": { + "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "push_to_pull_request_branch" + }, + { + "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "reason": { + "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", + "type": "string" + }, + "tool": { + "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + }, + "name": "missing_tool" + }, + { + "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "message": { + "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "noop" + }, + { + "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "context": { + "description": "Additional context about the missing data or where it should come from (max 256 characters).", + "type": "string" + }, + "data_type": { + "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", + "type": "string" + }, + "reason": { + "description": "Explanation of why this data is needed to complete the task (max 256 characters).", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "missing_data" + } + ] + GH_AW_SAFE_OUTPUTS_TOOLS_EOF + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "create_pull_request_review_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "line": { + "required": true, + "positiveInteger": true + }, + "path": { + "required": true, + "type": "string" + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "side": { + "type": "string", + "enum": [ + "LEFT", + "RIGHT" + ] + }, + "start_line": { + "optionalPositiveInteger": true + } + }, + "customValidation": "startLineLessOrEqualLine" + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + }, + "push_to_pull_request_branch": { + "defaultMax": 1, + "fields": { + "branch": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "pull_request_number": { + "issueOrPRNumber": true + } + } + }, + "resolve_pull_request_review_thread": { + "defaultMax": 10, + "fields": { + "thread_id": { + "required": true, + "type": "string" + } + } + }, + "submit_pull_request_review": { + "defaultMax": 1, + "fields": { + "body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "event": { + "type": "string", + "enum": [ + "APPROVE", + "REQUEST_CHANGES", + "COMMENT" + ] + } + } + } + } + GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash /opt/gh-aw/actions/start_safe_outputs_server.sh + + - name: Setup Safe Inputs Config + run: | + mkdir -p /opt/gh-aw/safe-inputs/logs + cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' + { + "serverName": "safeinputs", + "version": "1.0.0", + "logDir": "/opt/gh-aw/safe-inputs/logs", + "tools": [ + { + "name": "ready-to-code-review", + "description": "Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-code-review.py", + "timeout": 60 + }, + { + "name": "ready-to-push-to-pr", + "description": "Run the PR readiness checklist before pushing to a PR", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-push-to-pr.py", + "timeout": 60 + } + ] + } + GH_AW_SAFE_INPUTS_TOOLS_EOF + cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' + const path = require("path"); + const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); + const configPath = path.join(__dirname, "tools.json"); + const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); + const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; + startHttpServer(configPath, { + port: port, + stateless: true, + logDir: "/opt/gh-aw/safe-inputs/logs" + }).catch(error => { + console.error("Failed to start safe-inputs HTTP server:", error); + process.exit(1); + }); + GH_AW_SAFE_INPUTS_SERVER_EOF + chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs + + - name: Setup Safe Inputs Tool Files + run: | + cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-code-review + # Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/ + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = '${{ inputs.minimum_severity || "low" }}' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) + + GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py + cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-push-to-pr + # Run the PR readiness checklist before pushing to a PR + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, subprocess + def find(*paths): + return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + 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') + + # Guard: detect history rewrites and merge commits + pr_json_path = '/tmp/pr-context/pr.json' + if os.path.isfile(pr_json_path): + with open(pr_json_path) as f: + pr_data = json.load(f) + pr_head_sha = pr_data.get('headRefOid', '') + if pr_head_sha: + # Check 1: PR head must be an ancestor of HEAD (no rebase/reset) + anc = run(['git', 'merge-base', '--is-ancestor', pr_head_sha, 'HEAD']) + if anc.returncode != 0: + print(json.dumps({'status': 'error', 'error': f'History rewrite detected: the original PR head ({pr_head_sha[:12]}) is not an ancestor of HEAD. This means git rebase, reset, or cherry-pick rewrote history. push_to_pull_request_branch will fail. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch to its original head, then re-apply your changes as direct file edits and commit as regular commits.'})) + raise SystemExit(0) + # Check 2: no merge commits (multiple parents) since PR head + log = run(['git', 'rev-list', '--min-parents=2', f'{pr_head_sha}..HEAD']) + if log.returncode != 0: + print(json.dumps({'status': 'error', 'error': f'Failed to check for merge commits (git rev-list exited {log.returncode}): {log.stderr.strip()}. Cannot verify commit history is safe for push.'})) + raise SystemExit(0) + merge_shas = log.stdout.strip() + if merge_shas: + print(json.dumps({'status': 'error', 'error': f'Merge commit(s) detected: {merge_shas.splitlines()[0][:12]}... push_to_pull_request_branch uses git format-patch which breaks on merge commits. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch, then re-apply your changes as direct file edits (no git merge/rebase/commit-tree with multiple -p flags) and commit as regular single-parent commits.'})) + raise SystemExit(0) + + 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') + agents_md = find('AGENTS.md', 'agents.md', '.github/agents.md', '.github/AGENTS.md') + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (vs common ancestor), fall back to + # @{upstream} 2-dot (vs upstream tip), 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 + # Capture commit messages so the sub-agent knows what changed and why + commits_text = '' + for log_cmd in [ + ['git', 'log', '--format=### %s%n%n%b', '@{upstream}..HEAD'], + ['git', 'log', '--format=### %s%n%n%b', '-10'], + ]: + result = run(log_cmd) + if result.stdout.strip(): + commits_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) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + with open('/tmp/self-review/commits.txt', 'w') as f: + f.write(commits_text) + # Copy task context if available (PR metadata from the pr-context step) + has_task = False + if os.path.isfile('/tmp/pr-context/pr.json'): + import shutil + shutil.copy('/tmp/pr-context/pr.json', '/tmp/self-review/task.json') + has_task = True + # Write manifest so the sub-agent has structured context without + # relying on the main agent to manually pass it in the prompt + manifest_lines = [ + '# Self-Review Context', + '', + 'You are reviewing unpushed changes before they are submitted to a PR.', + '', + '## Available files', + '', + 'All files are in `/tmp/self-review/`:', + '', + f'- `diff.patch` : Full unified diff of all changes ({len(diff_text.splitlines())} lines)', + '- `stat.txt` : File-level change summary', + '- `commits.txt` : Commit messages describing what was changed and why', + '- `notes.md` : Author notes on what was done, why, and key decisions made', + ] + if has_task: + manifest_lines.append('- `task.json` : Original PR context (title, body, author, branches)') + step = 1 + manifest_lines += ['', '## How to review', ''] + manifest_lines.append(f'{step}. Read `notes.md` to understand what the author did, why, and what decisions they made.') + step += 1 + manifest_lines.append(f'{step}. Read `commits.txt` for the commit-level view of what changed.') + step += 1 + if has_task: + manifest_lines.append(f'{step}. Read `task.json` to understand the original task or issue being addressed.') + step += 1 + manifest_lines.append(f'{step}. Read `stat.txt` for a high-level view of which files changed.') + step += 1 + manifest_lines.append(f'{step}. Read `diff.patch` and the relevant source files from the workspace (the branch is checked out).') + step += 1 + if agents_md: + manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + manifest_lines += [ + '', + '## Focus areas', + '', + 'Look for bugs, logic errors, missed edge cases, and style issues.', + 'Focus on what the author might have MISSED rather than re-deriving their reasoning.', + '', + '## What NOT to flag', + '', + '- Pre-existing issues not introduced by these changes', + '- Style preferences handled by linters or formatters', + '- Theoretical performance concerns without evidence of real-world impact', + ] + with open('/tmp/self-review/README.md', 'w') as f: + f.write('\n'.join(manifest_lines) + '\n') + 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.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) and supporting context have been saved to `/tmp/self-review/`. Before spawning the sub-agent, write `/tmp/self-review/notes.md` with: what you changed and why, which files matter most and what they do, edge cases you already handled, and what test coverage exists. Then spawn a `code-review` sub-agent via `runSubagent` and tell it to start by reading `/tmp/self-review/README.md`. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_push_to_pr` again.') + 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-PUSH-TO-PR_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py + + - name: Generate Safe Inputs MCP Server Config + id: safe-inputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3000 + + # Set outputs for next steps + { + echo "safe_inputs_api_key=${API_KEY}" + echo "safe_inputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Inputs MCP server will run on port ${PORT}" + + - name: Start Safe Inputs MCP HTTP Server + id: safe-inputs-start + env: + DEBUG: '*' + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_INPUTS_PORT + export GH_AW_SAFE_INPUTS_API_KEY + + bash /opt/gh-aw/actions/start_safe_inputs_server.sh + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + mkdir -p /tmp/gh-aw/mcp-logs/playwright + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="localhost" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.31.0", + "env": { + "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "repos,issues,pull_requests,search,actions" + } + }, + "playwright": { + "type": "stdio", + "container": "mcr.microsoft.com/playwright/mcp", + "args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"], + "entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"], + "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"] + }, + "public-code-search": { + "type": "http", + "url": "https://public-code-search.fastmcp.app/mcp", + "tools": [ + "search_code" + ] + }, + "safeinputs": { + "type": "http", + "url": "http://localhost:$GH_AW_SAFE_INPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" + } + }, + "safeoutputs": { + "type": "http", + "url": "http://localhost:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_EOF + - name: Download activation artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: activation + path: /tmp/gh-aw + - name: Clean git credentials + run: bash /opt/gh-aw/actions/clean_git_credentials.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 60 + run: | + set -o pipefail + COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" + mkdir -p /tmp/ + mkdir -p /tmp/gh-aw/ + mkdir -p /tmp/gh-aw/agent/ + mkdir -p /tmp/gh-aw/sandbox/agent/logs/ + copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$COPILOT_CLI_INSTRUCTION" 2>&1 | tee /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ inputs.model }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload Safe Outputs + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output + path: ${{ env.GH_AW_SAFE_OUTPUTS }} + if-no-files-found: warn + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + 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 }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Upload sanitized agent output + if: always() && env.GH_AW_AGENT_OUTPUT + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-output + path: ${{ env.GH_AW_AGENT_OUTPUT }} + if-no-files-found: warn + - name: Upload engine output files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent_outputs + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + if-no-files-found: ignore + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse Safe Inputs logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-artifacts + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/safe-inputs/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + /tmp/gh-aw/aw-*.patch + if-no-files-found: ignore + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: write + pull-requests: write + outputs: + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing Tool + id: missing_tool + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent Failure + id: handle_agent_failure + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr-no-sandbox" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} + GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" + GH_AW_GROUP_REPORTS: "false" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op Message + id: handle_noop_message + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + pre_activation: + runs-on: ubuntu-slim + permissions: + discussions: write + issues: write + pull-requests: write + outputs: + activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} + matched_command: '' + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Add eyes reaction for immediate feedback + id: react + if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.id == github.repository_id) + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_REACTION: "eyes" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/add_reaction.cjs'); + await main(); + - name: Check team membership for workflow + id: check_membership + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_REQUIRED_ROLES: admin,maintainer,write + GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); + await main(); + + safe_outputs: + needs: + - activation + - agent + if: (!cancelled()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_MODEL: "${{ inputs.model }}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" + GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr-no-sandbox" + GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + push_commit_sha: ${{ steps.process_safe_outputs.outputs.push_commit_sha }} + push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Download patch artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-artifacts + path: /tmp/gh-aw/ + - name: Checkout repository + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} + token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + persist-credentials: false + fetch-depth: 1 + - name: Configure Git credentials + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + 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},\"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 }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload safe output items manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn + 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 94d37dd2..06cd711d 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 @@ -30,12 +30,14 @@ # - gh-aw-fragments/mcp-pagination.md # - gh-aw-fragments/messages-footer.md # - gh-aw-fragments/network-ecosystems.md +# - gh-aw-fragments/pick-three-keep-many.md # - gh-aw-fragments/playwright-mcp-explorer.md # - gh-aw-fragments/pr-context.md # - gh-aw-fragments/review-process.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-pr.md +# - gh-aw-fragments/safe-output-code-review.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 @@ -45,7 +47,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d85c988c65e9dac0594f560572cbf8504d9b8d50bc61eaf49b51087299c5fc63"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"6039e02b70c9c36fa05ad607482e6a9f0a7bf7b144d4985deeaf0f93b15b826e"} name: "Mention in PR (no sandbox)" "on": @@ -133,7 +135,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -198,9 +200,7 @@ jobs: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} @@ -210,6 +210,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} run: | bash /opt/gh-aw/actions/create_prompt_first.sh @@ -264,11 +265,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -343,90 +343,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Code Review Reference - ### Comment Format - - Call **`create_pull_request_review_comment`** with: - - The file path and the **exact line number from reading the file** (not estimated from the diff) - - The line must be within the diff (an added or context line in the patch) - - ````` - **[SEVERITY] Brief title** - - Description of the issue and why it matters. - - ```suggestion - corrected code here - ``` - ````` - - Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line. - - ### Inline Comment Threshold - - The minimum severity for inline comments is `__GH_AW_EXPR_7B543459__`. - - Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body instead — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters. - - Severity order (highest to lowest): critical > high > medium > low > nitpick. - - If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`. - - ### Review Criteria - - Focus on these categories in priority order: - - 1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure) - 2. Logic bugs that could cause runtime failures or incorrect behavior - 3. Data integrity issues (race conditions, missing transactions, corruption risk) - 4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations) - 5. Error handling gaps (unhandled exceptions, missing validation) - 6. Breaking changes to public APIs without migration path - 7. Missing or incorrect test coverage for critical paths - - ### What NOT to Flag - - Only 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. - - **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: - - - **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. - - **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. - - **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). - - **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. - - **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. - - **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods. - - **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). - - **Existing review threads** — check BEFORE leaving any comment: - - - **Resolved with reviewer reply** (e.g. "This is intentional") — reviewer's decision is final. Do NOT re-flag. - - **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem. - - **Unresolved** — already flagged. Do NOT duplicate. - - **Outdated** — only re-flag if the issue still applies to the current diff. - - When in doubt, do not duplicate. Redundant comments erode trust. - - Finding no issues is a valid and valuable outcome. An APPROVE with zero inline comments is better than comments that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, approve without comments. - - ### Severity Classification - - Determine 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. Do not start with a severity and look for issues to match it. - - - 🔴 **CRITICAL** — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs) - - 🟠 **HIGH** — Should fix before merge (logic errors, missing validation, significant performance issues) - - 🟡 **MEDIUM** — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases) - - ⚪ **LOW** — Author discretion (minor improvements, documentation gaps) - - 💬 **NITPICK** — Truly optional (stylistic preferences, alternative approaches) - - ### Review Intensity - - The review intensity is `__GH_AW_EXPR_8D9F5797__`. - - - **`conservative`**: High evidence bar. Only comment when you can demonstrate a concrete failure scenario — what specific input or state triggers the bug. After identifying a potential issue, explicitly challenge your own finding: if you can construct a reasonable counterargument, do not comment. Give the author maximum benefit of the doubt. Approval with zero comments is the expected outcome for most PRs. - - **`balanced`**: Standard evidence bar. Comment when you can point to specific code that would fail and have verified the issue through the full verification protocol. Give the author reasonable benefit of the doubt — if the issue is ambiguous, lean toward not commenting. - - **`aggressive`**: Lower evidence bar. Comment when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions and alternative approaches are welcome but must still cite specific code. Do not speculate without any evidence, and do not duplicate existing threads. - - If the value is unrecognized, treat it as `balanced`. + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -436,48 +353,35 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Playwright MCP Tools - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. + Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ### Pick Three, Keep Many - ### Available tools + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console + **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - ### Why MCP tools instead of scripts + - The full task description and objective (restate it, don't summarize) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) + - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) + - The quality criteria and output format you expect + - The specific angle that distinguishes this sub-agent from the others - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. + Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - ### Measuring DOM properties + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: + **Merge and deduplicate findings** across all sub-agents: + 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. + 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. + 3. Drop any finding that does not meet the verification criteria. - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` + **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. - ### Handling failures + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## add-comment Limitations @@ -572,25 +476,25 @@ jobs: 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. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). - 3. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. - 4. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. - 5. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__. + 1. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). + 2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. + 3. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. + 4. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__. ### Step 2: Handle the Request Based on what's asked, do the appropriate thing: **If asked to review the PR:** - - Read `/tmp/pr-context/review_comments.json` and `/tmp/pr-context/reviews.json` to check existing threads and prior reviews — do not duplicate feedback. - - Review each changed file one at a time using diffs from `/tmp/pr-context/diffs/`. For each file: - 1. Read the diff from `/tmp/pr-context/diffs/.diff`. If the diff is empty or truncated (e.g., binary files or very large changes), fall back to running `git diff` against the base branch or compare the full file against context. - 2. Read the full file from the workspace (the PR branch is checked out) - 3. Check existing threads from `/tmp/pr-context/threads/.json` (if it exists) — skip issues already flagged - 4. Identify and verify issues per the **Code Review Reference** above - 5. Leave inline comments (`create_pull_request_review_comment`) before moving to the next file - - After all files are reviewed, call `submit_pull_request_review`. + - Call `ready_to_code_review` to prepare the review approach based on PR size. + - Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. + - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). + - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. + - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. - **Important**: Substantive feedback belongs in the PR review (inline comments + review submission), NOT in a reply comment. Do NOT add a separate comment after submitting the review unless the user explicitly asked for a comment or the review submission failed. - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. @@ -630,8 +534,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} @@ -649,9 +552,7 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} @@ -661,6 +562,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} with: @@ -675,9 +577,7 @@ jobs: file: process.env.GH_AW_PROMPT, substitutions: { GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, - GH_AW_EXPR_7B543459: process.env.GH_AW_EXPR_7B543459, GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, - GH_AW_EXPR_8D9F5797: process.env.GH_AW_EXPR_8D9F5797, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, @@ -687,6 +587,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED, GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: process.env.GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT } @@ -737,21 +638,21 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - if: hashFiles('.python-version') != '' name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 @@ -791,15 +692,22 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `parent-review.md` | Comment format and inline severity threshold for the parent agent (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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" + 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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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" + - name: Write Playwright instructions to disk + run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: GITHUB_TOKEN: ${{ github.token }} REPO_NAME: ${{ github.repository }} @@ -1306,6 +1214,16 @@ jobs: "version": "1.0.0", "logDir": "/opt/gh-aw/safe-inputs/logs", "tools": [ + { + "name": "ready-to-code-review", + "description": "Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-code-review.py", + "timeout": 60 + }, { "name": "ready-to-push-to-pr", "description": "Run the PR readiness checklist before pushing to a PR", @@ -1338,6 +1256,135 @@ jobs: - name: Setup Safe Inputs Tool Files run: | + cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-code-review + # Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/ + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = os.environ.get('GH_AW_INPUTS_MINIMUM_SEVERITY', 'low') or 'low' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) + + GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' #!/usr/bin/env python3 # Auto-generated safe-input tool: ready-to-push-to-pr @@ -1560,13 +1607,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1798,7 +1838,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1890,7 +1930,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1949,7 +1989,7 @@ jobs: push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact 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 3b91d411..f69a8615 100644 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md +++ b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.md @@ -13,6 +13,8 @@ imports: - gh-aw-fragments/review-process.md - gh-aw-fragments/messages-footer.md - gh-aw-fragments/playwright-mcp-explorer.md + - gh-aw-fragments/pick-three-keep-many.md + - gh-aw-fragments/safe-output-code-review.md - gh-aw-fragments/safe-output-add-comment-pr.md - gh-aw-fragments/safe-output-review-comment.md - gh-aw-fragments/safe-output-submit-review.md @@ -134,25 +136,25 @@ Understand the request, investigate the code, and respond appropriately. 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. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). -3. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. -4. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. -5. Do not modify, review, comment on, or resolve threads for any PR other than #${{ github.event.issue.number }}. +1. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). +2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. +3. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. +4. Do not modify, review, comment on, or resolve threads for any PR other than #${{ github.event.issue.number }}. ### Step 2: Handle the Request Based on what's asked, do the appropriate thing: **If asked to review the PR:** -- Read `/tmp/pr-context/review_comments.json` and `/tmp/pr-context/reviews.json` to check existing threads and prior reviews — do not duplicate feedback. -- Review each changed file one at a time using diffs from `/tmp/pr-context/diffs/`. For each file: - 1. Read the diff from `/tmp/pr-context/diffs/.diff`. If the diff is empty or truncated (e.g., binary files or very large changes), fall back to running `git diff` against the base branch or compare the full file against context. - 2. Read the full file from the workspace (the PR branch is checked out) - 3. Check existing threads from `/tmp/pr-context/threads/.json` (if it exists) — skip issues already flagged - 4. Identify and verify issues per the **Code Review Reference** above - 5. Leave inline comments (`create_pull_request_review_comment`) before moving to the next file -- After all files are reviewed, call `submit_pull_request_review`. +- Call `ready_to_code_review` to prepare the review approach based on PR size. +- Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. +- Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). +- When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. +- Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. - **Important**: Substantive feedback belongs in the PR review (inline comments + review submission), NOT in a reply comment. Do NOT add a separate comment after submitting the review unless the user explicitly asked for a comment or the review submission failed. - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. diff --git a/.github/workflows/gh-aw-mention-in-pr.invalid.yml b/.github/workflows/gh-aw-mention-in-pr.invalid.yml new file mode 100644 index 00000000..0911efa7 --- /dev/null +++ b/.github/workflows/gh-aw-mention-in-pr.invalid.yml @@ -0,0 +1,2256 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw. DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# AI assistant for PRs — review, fix code, and push changes on demand +# +# Resolved workflow manifest: +# Imports: +# - gh-aw-fragments/elastic-tools.md +# - gh-aw-fragments/formatting.md +# - gh-aw-fragments/mcp-pagination.md +# - gh-aw-fragments/messages-footer.md +# - gh-aw-fragments/network-ecosystems.md +# - gh-aw-fragments/pick-three-keep-many.md +# - gh-aw-fragments/playwright-mcp-explorer.md +# - gh-aw-fragments/pr-context.md +# - gh-aw-fragments/review-process.md +# - gh-aw-fragments/rigor.md +# - gh-aw-fragments/runtime-setup.md +# - gh-aw-fragments/safe-output-add-comment-pr.md +# - gh-aw-fragments/safe-output-code-review.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 +# - gh-aw-fragments/workflow-edit-guardrails.md +# +# inlined-imports: true +# +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3313c7ef3e83734c55731f51f5ef44ac2ea89d4da5904fe5b17cdb65d44f42d0"} + +name: "Mention in PR" +"on": + workflow_call: + inputs: + additional-instructions: + default: "" + description: Repo-specific instructions appended to the agent prompt + required: false + type: string + allowed-bot-users: + default: github-actions[bot] + description: Allowlisted bot actor usernames (comma-separated) + required: false + type: string + create-pull-request-review-comment-max: + default: "30" + description: Maximum number of review comments the agent can create per run + required: false + type: string + messages-footer: + default: "" + description: Footer appended to all agent comments and reviews + required: false + type: string + model: + default: gpt-5.3-codex + description: AI model to use + required: false + type: string + prompt: + default: "" + description: Explicit prompt text to run when no comment trigger is present + required: false + type: string + resolve-pull-request-review-thread-max: + default: "10" + description: Maximum number of review threads the agent can resolve per run + required: false + type: string + setup-commands: + default: "" + description: Shell commands to run before the agent starts (dependency install, build, etc.) + required: false + type: string + target-pr-number: + default: "" + description: Explicit PR number to target (used for manual/dispatch triggers) + required: false + type: string + outputs: + comment_id: + description: ID of the first added comment + value: ${{ jobs.safe_outputs.outputs.comment_id }} + comment_url: + description: URL of the first added comment + value: ${{ jobs.safe_outputs.outputs.comment_url }} + push_commit_sha: + description: SHA of the pushed commit + value: ${{ jobs.safe_outputs.outputs.push_commit_sha }} + push_commit_url: + description: URL of the pushed commit + value: ${{ jobs.safe_outputs.outputs.push_commit_url }} + secrets: + COPILOT_GITHUB_TOKEN: + required: true + EXTRA_COMMIT_GITHUB_TOKEN: + required: false + +permissions: {} + +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-mention-pr-${{ inputs.target-pr-number || github.event.issue.number }} + +run-name: "Mention in PR" + +jobs: + activation: + needs: pre_activation + if: needs.pre_activation.outputs.activated == 'true' + runs-on: ubuntu-slim + permissions: + contents: read + discussions: write + issues: write + pull-requests: write + outputs: + body: ${{ steps.sanitized.outputs.body }} + comment_id: "" + comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + text: ${{ steps.sanitized.outputs.text }} + title: ${{ steps.sanitized.outputs.title }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: "${{ inputs.model }}" + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "0.0.420" + GH_AW_INFO_WORKFLOW_NAME: "Mention in PR" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","clojure","cloud.elastic.co","containers","dart","defaults","dotnet","ela.st","elastic.co","elastic.dev","elastic.github.io","elixir","fonts","github","github-actions","go","haskell","java","kotlin","linux-distros","node","node-cdns","perl","php","playwright","public-code-search.fastmcp.app","python","ruby","rust","scala","swift","terraform","www.elastic.co","zig"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.23.0" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 + persist-credentials: false + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_WORKFLOW_FILE: "gh-aw-mention-in-pr.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Compute current body text + id: sanitized + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/compute_text.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_EXPR_95B47D04: ${{ inputs.target-pr-number || github.event.issue.number }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} + GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/playwright_prompt.md" + 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, 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' + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## MCP Servers + + - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Formatting Guidelines + + - Lead with the most important information — your first sentence should be the key takeaway + - Be concise and actionable — no filler or praise + - Use `
` and `` tags for long sections to keep responses scannable + - Wrap branch names and @-references in backticks to avoid pinging users + - Include code snippets with file paths and line numbers when referencing the codebase + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Rigor + + **Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report.** + + - If you claim something is missing or broken, show the exact evidence in the code — file path, line number, and what you observed. + - If a conclusion depends on assumptions you haven't confirmed, do not assert it. Verify first; if you cannot verify, do not report. + - "I don't know" is better than a wrong answer. `noop` is better than a speculative finding. + - It's worth the time to verify now versus guessing and forcing someone else to verify later. + - Before submitting any output, re-read it as a skeptical reviewer. Ask: "Would a senior engineer on this team find this useful, or would they close it immediately?" If the answer is "close," call `noop` instead. + - Only report findings you would confidently defend in a code review. If you feel the need to hedge with "might," "could," or "possibly," the finding is not ready to file. + - Be thorough, spend the time to investigate and verify. There is no rush. Do your best work. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## MCP Pagination + + MCP tool responses have a **25,000 token limit**. When responses exceed this limit, the call fails and you must retry with pagination — wasting turns and tokens. Use proactive pagination to stay under the limit. + + ### Recommended `perPage` Values + + - **5-10**: For detailed items (PR diffs, files with patches, issues with comments) + - **20-30**: For medium-detail lists (commits, review comments, issue lists) + - **50-100**: For simple list operations (branches, labels, tags) + + ### Pagination Pattern + + When you need all results from a paginated API: + + 1. Fetch the first page with a conservative `perPage` value + 2. Process the results before fetching the next page + 3. Continue fetching pages until you receive fewer results than `perPage` (indicating the last page) + + ### Error Recovery + + If you see an error like: + - `MCP tool response exceeds maximum allowed tokens (25000)` + - `Response too large for tool [tool_name]` + + Retry the same call with a smaller `perPage` value (halve it). + + ### Tips + + - **Start small**: It's better to make multiple small requests than one that fails + - **Fetch incrementally**: Get an overview first, then details for specific items + - **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' + ## Workflow Editing Guardrails + + - Do not modify files under `.github/workflows/`. + - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. + + 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. **Never mention these file paths or on-disk data sources in your responses** — they are internal implementation details invisible to users. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Code Review Reference + + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md`. Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md`. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Message Footer + + A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Playwright MCP Tools + + You have Playwright MCP tools available for interactive browser automation. + Use these tools to explore the app step by step — do NOT write Node.js scripts. + + ### Available tools + + - `browser_navigate` — go to a URL + - `browser_click` — click an element + - `browser_type` — type text into an input + - `browser_snapshot` — get an accessibility tree (YAML) of the current page + - `browser_take_screenshot` — capture a screenshot + - `browser_console_execute` — run JavaScript in the browser console + + ### Why MCP tools instead of scripts + + MCP tools are interactive: you see the page state after each action and + decide what to do next. This is ideal for exploratory testing where you + need to adapt based on what you find. Scripts are fire-and-forget — if + a selector is wrong, you don't find out until the script fails. + + ### Measuring DOM properties + + For programmatic checks (e.g. element heights, contrast), use + `browser_console_execute`: + + ```javascript + (() => { + const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); + return JSON.stringify(Array.from(els) + .map(el => { + const r = el.getBoundingClientRect(); + return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; + }) + .filter(el => el.top > 50 && el.top < 250)); + })() + ``` + + ### Handling failures + + - Do not retry the same action more than twice — the page is in a different state than expected. + - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. + - Adapt (different selector, different path) or report the failure as a finding. + - Never claim you verified something you didn't — if it failed and you skipped it, say so. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ### Pick Three, Keep Many + + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + + **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: + + - The full task description and objective (restate it, don't summarize) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) + - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) + - The quality criteria and output format you expect + - The specific angle that distinguishes this sub-agent from the others + + Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. + + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + + **Merge and deduplicate findings** across all sub-agents: + 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. + 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. + 3. Drop any finding that does not meet the verification criteria. + + **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## add-comment Limitations + + - **Body**: Max 65,536 characters (including any footer added by gh-aw). Keep well under this limit. + - **Mentions**: Max 10 `@` mentions per comment. + - **Links**: Max 50 URLs per comment. + - **HTML**: Only safe tags allowed (`details`, `summary`, `code`, `pre`, `blockquote`, `table`, `b`, `em`, `strong`, `h1`–`h6`, `hr`, `br`, `li`, `ol`, `ul`, `p`, `sub`, `sup`). Other tags are converted to parentheses. + - **URLs**: Only HTTPS URLs to allowed domains. Non-HTTPS and non-allowed domains are redacted. + - **Bot triggers**: References like `fixes #123` or `closes #456` are neutralized to prevent unintended issue closures. + + If you exceed 10 mentions or 50 links, the comment will be rejected. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## create-pull-request-review-comment + + - **Required fields**: `path` (file path), `line` (line number), and `body` (comment text). + - **Line**: Must be within the diff — an added or context line in the patch. Must be the **exact line number from reading the file** (not estimated from the patch). Lines outside the diff will fail. + - **Body**: Sanitized with the standard pipeline (mentions neutralized, HTML filtered, URLs restricted). GitHub API limit is ~65,536 characters. + - **Side**: Defaults to `RIGHT` (the new code). Use `LEFT` only when commenting on deleted lines. + - **Suggestion blocks**: Use ` ```suggestion ` fences for concrete code fixes. The suggestion must actually change the code — don't suggest identical code. Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. + + Only flag issues you are confident are real problems — false positives erode trust. Once you have flagged an issue, you cannot unflag it. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## submit-pull-request-review Limitations + + - **Event**: Must be one of `APPROVE`, `REQUEST_CHANGES`, or `COMMENT`. Defaults to `COMMENT` if omitted. + - **Body**: Max 65,000 characters. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. A body is required when event is `REQUEST_CHANGES`. Sanitized (mentions neutralized, HTML filtered, URLs restricted). If you have also used `create-pull-request-review-comment`, you do not need to repeat the same feedback in the body. If you "Approve" and have no comments, do not provide a `body`. + - **Own PRs**: If the workflow actor is also the PR author (e.g., `github-actions[bot]` reviewing its own PR), the event is forced to `COMMENT` regardless of what you specify. `APPROVE` and `REQUEST_CHANGES` will not work. + - **Max per run**: 1 review submission per workflow run. Leave inline comments first, then submit the review as a single final action. + + **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + Before calling `push_to_pull_request_branch`, call `ready_to_push_to_pr` and apply its checklist. + + ## push-to-pull-request-branch Limitations + + - **Patch size**: Max ~10 MB (10,240 KB). Keep changes focused — very large refactors may exceed this. + - **Fork PRs**: Cannot push to fork PR branches. Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, explain that you cannot push and suggest the author apply changes themselves. + - **Committed changes required**: You must have locally committed changes before calling push. Uncommitted or staged-only changes will fail. + - **Branch**: Pushes to the PR's head branch. The workspace must have the PR branch checked out. + - You may not submit code that modifies files in `.github/workflows/`. Doing so will cause the submission to be rejected. If asked to modify workflow files, propose the change in a copy placed in a `github/` folder (without the leading period) and note in the PR that the file needs to be relocated by someone with workflow write access. + + Trying to resolve merge conflicts? Do not use `git merge` or `git rebase` — `push_to_pull_request_branch` uses `git format-patch` which requires single-parent commits. Instead: + 1. Compare with the base branch (from `/tmp/pr-context/pr.json` field `baseRefName`) to see what changed in the conflicting files + 2. Edit the files directly to incorporate the changes from the base branch + 3. Commit the changes as regular (single-parent) commits + 4. Call `ready_to_push_to_pr` (which will catch any merge commits) and then `push_to_pull_request_branch` to push + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## resolve-pull-request-review-thread Limitations + + - **Required field**: `thread_id` — the GraphQL node ID of the review thread (e.g., `PRRT_kwDO...`). This is the `id` field from `get_review_comments`, not the numeric REST comment ID. + - **Only resolve what you've addressed**: Do not resolve threads you skipped, disagreed with, or didn't fix. Only resolve threads where your changes directly address the feedback. + - **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 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. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + # PR Assistant + + Assist with pull requests on __GH_AW_GITHUB_REPOSITORY__ — review code, fix issues, answer questions, and push changes. + + ## Context + + - **Repository**: __GH_AW_GITHUB_REPOSITORY__ + - **PR**: #__GH_AW_EXPR_95B47D04__ — __GH_AW_GITHUB_EVENT_ISSUE_TITLE__ + - **PR context on disk**: `/tmp/pr-context/` — PR metadata, diff, files, reviews, comments, and linked issues are pre-fetched. Use these as your primary source; fall back to API tools only when required data is unavailable. + - **Request**: "__GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT__" + - **Explicit prompt**: "__GH_AW_INPUTS_PROMPT__" + + ## Constraints + + - **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 + + ## Instructions + + Understand the request, investigate the code, and respond appropriately. + + ### 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. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). + 2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. + 3. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. + 4. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_EXPR_95B47D04__. + + ### Step 2: Handle the Request + + Based on what's asked, do the appropriate thing. **Requests can combine multiple actions** (e.g., "fix merge conflicts and address the review feedback"). When they do, handle them in this order: merge conflicts first, then code changes/review feedback, then push once at the end. Do not push between steps — batch all changes into a single push. + + **If asked to review the PR:** + - Call `ready_to_code_review` to prepare the review approach based on PR size. + - Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. + - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). + - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. + - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. + - **Important**: Substantive feedback belongs in the PR review (inline comments + review submission), NOT in a reply comment. Do NOT add a separate comment after submitting the review unless the user explicitly asked for a comment or the review submission failed. + - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. + + **If asked to fix code or address review feedback:** + - Read `/tmp/pr-context/unresolved_threads.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. + - 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. + - Use `push_to_pull_request_branch` to push your changes. + - 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/unresolved_threads.json` for all unresolved threads — also check `/tmp/pr-context/outdated_threads.json` for threads where the underlying code changed since the comment was made and verify 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 to fix merge conflicts:** + - Check via `pull_request_read` (method `get`) whether this is a fork PR. If so, reply that you cannot push to fork branches and suggest the author resolve locally. + - Read `/tmp/pr-context/pr.json` for the head and base branch names. + - Identify conflicting files by comparing with the base branch, then edit each file directly to produce the correct merged result. Commit the resolved changes as regular (single-parent) commits — do not use `git merge` or `git rebase` (the `ready_to_push_to_pr` check will catch merge commits before pushing). + - If conflicts are too complex to resolve confidently (large structural changes, binary files, ambiguous intent), reply explaining what you found and suggest the author resolve locally. + - If the request includes additional work (code fixes, review feedback), complete all of it before pushing — `push_to_pull_request_branch` can only be called once. Resolve merge conflicts first, then make other requested changes on top, then push everything together. + - Use `push_to_pull_request_branch` and reply summarizing what was resolved and how conflicts were handled. + + **If asked a question about the code:** + - Find the relevant code and explain it. + - Use `grep` and file reading to gather context. + - Use `web-fetch` to look up documentation when needed. + + **If the request is unclear:** + - Ask clarifying questions rather than guessing. + + ### Step 3: Respond + + If you did not submit a PR review, call `add_comment` with your response. If you submitted a review, do NOT call `add_comment` unless explicitly requested or to report a review submission failure. + + **Additional tools:** + - `push_to_pull_request_branch` — push committed changes to the PR branch (same-repo PRs only) + - `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__ + + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} + GH_AW_EXPR_95B47D04: ${{ inputs.target-pr-number || github.event.issue.number }} + GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} + GH_AW_EXPR_95B47D04: ${{ inputs.target-pr-number || github.event.issue.number }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, + GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, + GH_AW_EXPR_95B47D04: process.env.GH_AW_EXPR_95B47D04, + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_TITLE: process.env.GH_AW_GITHUB_EVENT_ISSUE_TITLE, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, + GH_AW_INPUTS_PROMPT: process.env.GH_AW_INPUTS_PROMPT, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED, + GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: process.env.GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + issues: read + pull-requests: read + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}-mention-pr-${{ inputs.target-pr-number || github.event.issue.number }}" + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_WORKFLOW_ID_SANITIZED: ghawmentioninpr + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + model: ${{ needs.activation.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - if: hashFiles('go.mod') != '' + name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + with: + cache: true + go-version-file: go.mod + - if: hashFiles('.python-version') != '' + name: Setup Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version-file: .python-version + - if: hashFiles('.node-version') != '' + name: Setup Node.js (.node-version) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + with: + node-version-file: .node-version + - if: hashFiles('.node-version') == '' && hashFiles('.nvmrc') != '' + name: Setup Node.js (.nvmrc) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + with: + node-version-file: .nvmrc + - if: hashFiles('.ruby-version') != '' + name: Setup Ruby + uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 + with: + bundler-cache: true + ruby-version: .ruby-version + - id: setup-uv + if: hashFiles('pyproject.toml', 'uv.lock') != '' + name: Setup uv + uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + - env: + UV_PATH: ${{ steps.setup-uv.outputs.uv-path }} + WORKSPACE: ${{ github.workspace }} + if: hashFiles('pyproject.toml', 'uv.lock') != '' + name: Expose uv in workspace + run: | + set -euo pipefail + install_dir="$WORKSPACE/.gh-aw-tools/bin" + mkdir -p "$install_dir" + cp "$UV_PATH" "$install_dir/uv" + chmod +x "$install_dir/uv" + echo "$install_dir" >> "$GITHUB_PATH" + shell: bash + - name: Configure Copilot CLI settings + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" + 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: + GITHUB_TOKEN: ${{ github.token }} + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + name: Ensure origin refs for PR patch generation + run: "SERVER_URL_STRIPPED=\"${SERVER_URL#https://}\"\ngit remote set-url origin \"https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git\"\ngit fetch --no-tags --prune origin '+refs/heads/*:refs/remotes/origin/*'\n" + - env: + SETUP_COMMANDS: ${{ inputs.setup-commands }} + if: ${{ inputs.setup-commands != '' }} + name: Repo-specific setup + run: eval "$SETUP_COMMANDS" + + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420 + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.6 ghcr.io/github/github-mcp-server:v0.31.0 mcr.microsoft.com/playwright/mcp node:lts-alpine + - name: Write Safe Outputs Config + run: | + mkdir -p /opt/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"add_comment":{"max":1},"create_pull_request_review_comment":{"max":"${{ inputs.create-pull-request-review-comment-max }}"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":0},"resolve_pull_request_review_thread":{"max":"${{ inputs.resolve-pull-request-review-thread-max }}"},"submit_pull_request_review":{"max":1}} + GH_AW_SAFE_OUTPUTS_CONFIG_EOF + cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' + [ + { + "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", + "type": "string" + }, + "item_number": { + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the comment will be silently discarded.", + "type": "number" + } + }, + "required": [ + "body" + ], + "type": "object" + }, + "name": "add_comment" + }, + { + "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Comments will be on the RIGHT side of the diff.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", + "type": "string" + }, + "line": { + "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", + "type": [ + "number", + "string" + ] + }, + "path": { + "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", + "type": "string" + }, + "side": { + "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", + "enum": [ + "LEFT", + "RIGHT" + ], + "type": "string" + }, + "start_line": { + "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "path", + "line", + "body" + ], + "type": "object" + }, + "name": "create_pull_request_review_comment" + }, + { + "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", + "type": "string" + }, + "event": { + "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", + "enum": [ + "APPROVE", + "REQUEST_CHANGES", + "COMMENT" + ], + "type": "string" + } + }, + "type": "object" + }, + "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": { + "additionalProperties": false, + "properties": { + "thread_id": { + "description": "The node ID of the review thread to resolve (e.g., 'PRRT_kwDOABCD...'). This is the GraphQL node ID, not a numeric ID.", + "type": "string" + } + }, + "required": [ + "thread_id" + ], + "type": "object" + }, + "name": "resolve_pull_request_review_thread" + }, + { + "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "branch": { + "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.", + "type": "string" + }, + "message": { + "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).", + "type": "string" + }, + "pull_request_number": { + "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "push_to_pull_request_branch" + }, + { + "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "reason": { + "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", + "type": "string" + }, + "tool": { + "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + }, + "name": "missing_tool" + }, + { + "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "message": { + "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "noop" + }, + { + "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "context": { + "description": "Additional context about the missing data or where it should come from (max 256 characters).", + "type": "string" + }, + "data_type": { + "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", + "type": "string" + }, + "reason": { + "description": "Explanation of why this data is needed to complete the task (max 256 characters).", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "missing_data" + } + ] + GH_AW_SAFE_OUTPUTS_TOOLS_EOF + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "add_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "item_number": { + "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "create_pull_request_review_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "line": { + "required": true, + "positiveInteger": true + }, + "path": { + "required": true, + "type": "string" + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "side": { + "type": "string", + "enum": [ + "LEFT", + "RIGHT" + ] + }, + "start_line": { + "optionalPositiveInteger": true + } + }, + "customValidation": "startLineLessOrEqualLine" + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + }, + "push_to_pull_request_branch": { + "defaultMax": 1, + "fields": { + "branch": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "pull_request_number": { + "issueOrPRNumber": true + } + } + }, + "resolve_pull_request_review_thread": { + "defaultMax": 10, + "fields": { + "thread_id": { + "required": true, + "type": "string" + } + } + }, + "submit_pull_request_review": { + "defaultMax": 1, + "fields": { + "body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "event": { + "type": "string", + "enum": [ + "APPROVE", + "REQUEST_CHANGES", + "COMMENT" + ] + } + } + } + } + GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash /opt/gh-aw/actions/start_safe_outputs_server.sh + + - name: Setup Safe Inputs Config + run: | + mkdir -p /opt/gh-aw/safe-inputs/logs + cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' + { + "serverName": "safeinputs", + "version": "1.0.0", + "logDir": "/opt/gh-aw/safe-inputs/logs", + "tools": [ + { + "name": "ready-to-code-review", + "description": "Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-code-review.py", + "timeout": 60 + }, + { + "name": "ready-to-push-to-pr", + "description": "Run the PR readiness checklist before pushing to a PR", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-push-to-pr.py", + "timeout": 60 + } + ] + } + GH_AW_SAFE_INPUTS_TOOLS_EOF + cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' + const path = require("path"); + const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); + const configPath = path.join(__dirname, "tools.json"); + const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); + const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; + startHttpServer(configPath, { + port: port, + stateless: true, + logDir: "/opt/gh-aw/safe-inputs/logs" + }).catch(error => { + console.error("Failed to start safe-inputs HTTP server:", error); + process.exit(1); + }); + GH_AW_SAFE_INPUTS_SERVER_EOF + chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs + + - name: Setup Safe Inputs Tool Files + run: | + cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-code-review + # Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/ + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = '${{ inputs.minimum_severity || "low" }}' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) + + GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py + cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-push-to-pr + # Run the PR readiness checklist before pushing to a PR + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, subprocess + def find(*paths): + return next((p for p in paths if os.path.isfile(p)), None) + def run(cmd): + 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') + + # Guard: detect history rewrites and merge commits + pr_json_path = '/tmp/pr-context/pr.json' + if os.path.isfile(pr_json_path): + with open(pr_json_path) as f: + pr_data = json.load(f) + pr_head_sha = pr_data.get('headRefOid', '') + if pr_head_sha: + # Check 1: PR head must be an ancestor of HEAD (no rebase/reset) + anc = run(['git', 'merge-base', '--is-ancestor', pr_head_sha, 'HEAD']) + if anc.returncode != 0: + print(json.dumps({'status': 'error', 'error': f'History rewrite detected: the original PR head ({pr_head_sha[:12]}) is not an ancestor of HEAD. This means git rebase, reset, or cherry-pick rewrote history. push_to_pull_request_branch will fail. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch to its original head, then re-apply your changes as direct file edits and commit as regular commits.'})) + raise SystemExit(0) + # Check 2: no merge commits (multiple parents) since PR head + log = run(['git', 'rev-list', '--min-parents=2', f'{pr_head_sha}..HEAD']) + if log.returncode != 0: + print(json.dumps({'status': 'error', 'error': f'Failed to check for merge commits (git rev-list exited {log.returncode}): {log.stderr.strip()}. Cannot verify commit history is safe for push.'})) + raise SystemExit(0) + merge_shas = log.stdout.strip() + if merge_shas: + print(json.dumps({'status': 'error', 'error': f'Merge commit(s) detected: {merge_shas.splitlines()[0][:12]}... push_to_pull_request_branch uses git format-patch which breaks on merge commits. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch, then re-apply your changes as direct file edits (no git merge/rebase/commit-tree with multiple -p flags) and commit as regular single-parent commits.'})) + raise SystemExit(0) + + 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') + agents_md = find('AGENTS.md', 'agents.md', '.github/agents.md', '.github/AGENTS.md') + # Generate diff of all local changes vs upstream for self-review + # Try --merge-base (vs common ancestor), fall back to + # @{upstream} 2-dot (vs upstream tip), 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 + # Capture commit messages so the sub-agent knows what changed and why + commits_text = '' + for log_cmd in [ + ['git', 'log', '--format=### %s%n%n%b', '@{upstream}..HEAD'], + ['git', 'log', '--format=### %s%n%n%b', '-10'], + ]: + result = run(log_cmd) + if result.stdout.strip(): + commits_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) + with open('/tmp/self-review/stat.txt', 'w') as f: + f.write(stat_text) + with open('/tmp/self-review/commits.txt', 'w') as f: + f.write(commits_text) + # Copy task context if available (PR metadata from the pr-context step) + has_task = False + if os.path.isfile('/tmp/pr-context/pr.json'): + import shutil + shutil.copy('/tmp/pr-context/pr.json', '/tmp/self-review/task.json') + has_task = True + # Write manifest so the sub-agent has structured context without + # relying on the main agent to manually pass it in the prompt + manifest_lines = [ + '# Self-Review Context', + '', + 'You are reviewing unpushed changes before they are submitted to a PR.', + '', + '## Available files', + '', + 'All files are in `/tmp/self-review/`:', + '', + f'- `diff.patch` : Full unified diff of all changes ({len(diff_text.splitlines())} lines)', + '- `stat.txt` : File-level change summary', + '- `commits.txt` : Commit messages describing what was changed and why', + '- `notes.md` : Author notes on what was done, why, and key decisions made', + ] + if has_task: + manifest_lines.append('- `task.json` : Original PR context (title, body, author, branches)') + step = 1 + manifest_lines += ['', '## How to review', ''] + manifest_lines.append(f'{step}. Read `notes.md` to understand what the author did, why, and what decisions they made.') + step += 1 + manifest_lines.append(f'{step}. Read `commits.txt` for the commit-level view of what changed.') + step += 1 + if has_task: + manifest_lines.append(f'{step}. Read `task.json` to understand the original task or issue being addressed.') + step += 1 + manifest_lines.append(f'{step}. Read `stat.txt` for a high-level view of which files changed.') + step += 1 + manifest_lines.append(f'{step}. Read `diff.patch` and the relevant source files from the workspace (the branch is checked out).') + step += 1 + if agents_md: + manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + manifest_lines += [ + '', + '## Focus areas', + '', + 'Look for bugs, logic errors, missed edge cases, and style issues.', + 'Focus on what the author might have MISSED rather than re-deriving their reasoning.', + '', + '## What NOT to flag', + '', + '- Pre-existing issues not introduced by these changes', + '- Style preferences handled by linters or formatters', + '- Theoretical performance concerns without evidence of real-world impact', + ] + with open('/tmp/self-review/README.md', 'w') as f: + f.write('\n'.join(manifest_lines) + '\n') + 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.') + if diff_line_count > 0: + checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) and supporting context have been saved to `/tmp/self-review/`. Before spawning the sub-agent, write `/tmp/self-review/notes.md` with: what you changed and why, which files matter most and what they do, edge cases you already handled, and what test coverage exists. Then spawn a `code-review` sub-agent via `runSubagent` and tell it to start by reading `/tmp/self-review/README.md`. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_push_to_pr` again.') + 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-PUSH-TO-PR_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py + + - name: Generate Safe Inputs MCP Server Config + id: safe-inputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3000 + + # Set outputs for next steps + { + echo "safe_inputs_api_key=${API_KEY}" + echo "safe_inputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Inputs MCP server will run on port ${PORT}" + + - name: Start Safe Inputs MCP HTTP Server + id: safe-inputs-start + env: + DEBUG: '*' + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_INPUTS_PORT + export GH_AW_SAFE_INPUTS_API_KEY + + bash /opt/gh-aw/actions/start_safe_inputs_server.sh + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + mkdir -p /tmp/gh-aw/mcp-logs/playwright + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.31.0", + "env": { + "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "repos,issues,pull_requests,search,actions" + } + }, + "playwright": { + "type": "stdio", + "container": "mcr.microsoft.com/playwright/mcp", + "args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"], + "entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"], + "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"] + }, + "public-code-search": { + "type": "http", + "url": "https://public-code-search.fastmcp.app/mcp", + "tools": [ + "search_code" + ] + }, + "safeinputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_EOF + - name: Download activation artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: activation + path: /tmp/gh-aw + - name: Clean git credentials + run: bash /opt/gh-aw/actions/clean_git_credentials.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 60 + run: | + set -o pipefail + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ inputs.model }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload Safe Outputs + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output + path: ${{ env.GH_AW_SAFE_OUTPUTS }} + if-no-files-found: warn + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + 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 }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Upload sanitized agent output + if: always() && env.GH_AW_AGENT_OUTPUT + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-output + path: ${{ env.GH_AW_AGENT_OUTPUT }} + if-no-files-found: warn + - name: Upload engine output files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent_outputs + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + if-no-files-found: ignore + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse Safe Inputs logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-artifacts + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/safe-inputs/logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + /tmp/gh-aw/aw-*.patch + if-no-files-found: ignore + # --- Threat Detection (inline) --- + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + WORKFLOW_NAME: "Mention in PR" + WORKFLOW_DESCRIPTION: "AI assistant for PRs — review, fix code, and push changes on demand" + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ inputs.model }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_detection_results + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: threat-detection.log + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Set detection conclusion + id: detection_conclusion + if: always() + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} + run: | + if [[ "$RUN_DETECTION" != "true" ]]; then + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection was not needed, marking as skipped" + elif [[ "$DETECTION_SUCCESS" == "true" ]]; then + echo "conclusion=success" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection passed successfully" + else + echo "conclusion=failure" >> "$GITHUB_OUTPUT" + echo "success=false" >> "$GITHUB_OUTPUT" + echo "Detection found issues" + fi + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: write + pull-requests: write + outputs: + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "Mention in PR" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing Tool + id: missing_tool + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent Failure + id: handle_agent_failure + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} + GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" + GH_AW_GROUP_REPORTS: "false" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op Message + id: handle_noop_message + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "Mention in PR" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + pre_activation: + runs-on: ubuntu-slim + permissions: + discussions: write + issues: write + pull-requests: write + outputs: + activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} + matched_command: '' + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Add eyes reaction for immediate feedback + id: react + if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.id == github.repository_id) + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_REACTION: "eyes" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/add_reaction.cjs'); + await main(); + - name: Check team membership for workflow + id: check_membership + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_REQUIRED_ROLES: admin,maintainer,write + GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); + await main(); + + safe_outputs: + needs: + - activation + - agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') + runs-on: ubuntu-slim + permissions: + contents: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_MODEL: "${{ inputs.model }}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" + GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr" + GH_AW_WORKFLOW_NAME: "Mention in PR" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + push_commit_sha: ${{ steps.process_safe_outputs.outputs.push_commit_sha }} + push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Download patch artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-artifacts + path: /tmp/gh-aw/ + - name: Checkout repository + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} + token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + persist-credentials: false + fetch-depth: 1 + - name: Configure Git credentials + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + 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},\"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 }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload safe output items manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn + diff --git a/.github/workflows/gh-aw-mention-in-pr.lock.yml b/.github/workflows/gh-aw-mention-in-pr.lock.yml index 06a39ed5..226cbe5e 100644 --- a/.github/workflows/gh-aw-mention-in-pr.lock.yml +++ b/.github/workflows/gh-aw-mention-in-pr.lock.yml @@ -30,12 +30,14 @@ # - gh-aw-fragments/mcp-pagination.md # - gh-aw-fragments/messages-footer.md # - gh-aw-fragments/network-ecosystems.md +# - gh-aw-fragments/pick-three-keep-many.md # - gh-aw-fragments/playwright-mcp-explorer.md # - gh-aw-fragments/pr-context.md # - gh-aw-fragments/review-process.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-pr.md +# - gh-aw-fragments/safe-output-code-review.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 @@ -45,7 +47,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"f336c38598b887329bc77ebc84ce1f81e8ab51cca8c373e77079c3f6b0e2fe07"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"aec76f363ebbb5b8454ecae61cd57b014397bdabd3406eeb3d309602bee21330"} name: "Mention in PR" "on": @@ -143,7 +145,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -208,9 +210,7 @@ jobs: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} GH_AW_EXPR_95B47D04: ${{ inputs.target-pr-number || github.event.issue.number }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} @@ -221,6 +221,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} run: | @@ -276,11 +277,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -355,90 +355,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Code Review Reference - ### Comment Format - - Call **`create_pull_request_review_comment`** with: - - The file path and the **exact line number from reading the file** (not estimated from the diff) - - The line must be within the diff (an added or context line in the patch) - - ````` - **[SEVERITY] Brief title** - - Description of the issue and why it matters. - - ```suggestion - corrected code here - ``` - ````` - - Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line. - - ### Inline Comment Threshold - - The minimum severity for inline comments is `__GH_AW_EXPR_7B543459__`. - - Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body instead — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters. - - Severity order (highest to lowest): critical > high > medium > low > nitpick. - - If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`. - - ### Review Criteria - - Focus on these categories in priority order: - - 1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure) - 2. Logic bugs that could cause runtime failures or incorrect behavior - 3. Data integrity issues (race conditions, missing transactions, corruption risk) - 4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations) - 5. Error handling gaps (unhandled exceptions, missing validation) - 6. Breaking changes to public APIs without migration path - 7. Missing or incorrect test coverage for critical paths - - ### What NOT to Flag - - Only 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. - - **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: - - - **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. - - **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. - - **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). - - **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. - - **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. - - **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods. - - **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). - - **Existing review threads** — check BEFORE leaving any comment: - - - **Resolved with reviewer reply** (e.g. "This is intentional") — reviewer's decision is final. Do NOT re-flag. - - **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem. - - **Unresolved** — already flagged. Do NOT duplicate. - - **Outdated** — only re-flag if the issue still applies to the current diff. - - When in doubt, do not duplicate. Redundant comments erode trust. - - Finding no issues is a valid and valuable outcome. An APPROVE with zero inline comments is better than comments that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, approve without comments. - - ### Severity Classification - - Determine 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. Do not start with a severity and look for issues to match it. - - - 🔴 **CRITICAL** — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs) - - 🟠 **HIGH** — Should fix before merge (logic errors, missing validation, significant performance issues) - - 🟡 **MEDIUM** — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases) - - ⚪ **LOW** — Author discretion (minor improvements, documentation gaps) - - 💬 **NITPICK** — Truly optional (stylistic preferences, alternative approaches) - - ### Review Intensity - - The review intensity is `__GH_AW_EXPR_8D9F5797__`. - - - **`conservative`**: High evidence bar. Only comment when you can demonstrate a concrete failure scenario — what specific input or state triggers the bug. After identifying a potential issue, explicitly challenge your own finding: if you can construct a reasonable counterargument, do not comment. Give the author maximum benefit of the doubt. Approval with zero comments is the expected outcome for most PRs. - - **`balanced`**: Standard evidence bar. Comment when you can point to specific code that would fail and have verified the issue through the full verification protocol. Give the author reasonable benefit of the doubt — if the issue is ambiguous, lean toward not commenting. - - **`aggressive`**: Lower evidence bar. Comment when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions and alternative approaches are welcome but must still cite specific code. Do not speculate without any evidence, and do not duplicate existing threads. - - If the value is unrecognized, treat it as `balanced`. + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -448,48 +365,35 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Playwright MCP Tools - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. + Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ### Pick Three, Keep Many - ### Available tools + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console + **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - ### Why MCP tools instead of scripts + - The full task description and objective (restate it, don't summarize) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) + - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) + - The quality criteria and output format you expect + - The specific angle that distinguishes this sub-agent from the others - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. + Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - ### Measuring DOM properties + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: + **Merge and deduplicate findings** across all sub-agents: + 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. + 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. + 3. Drop any finding that does not meet the verification criteria. - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` + **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. - ### Handling failures + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## add-comment Limitations @@ -585,10 +489,10 @@ jobs: 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. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). - 3. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. - 4. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. + 1. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). + 2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. + 3. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. + 4. Read `/tmp/pr-context/reviews.json` to check prior review submissions — note any prior verdicts to avoid redundant reviews. 5. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_EXPR_95B47D04__. ### Step 2: Handle the Request @@ -596,14 +500,14 @@ jobs: Based on what's asked, do the appropriate thing. **Requests can combine multiple actions** (e.g., "fix merge conflicts and address the review feedback"). When they do, handle them in this order: merge conflicts first, then code changes/review feedback, then push once at the end. Do not push between steps — batch all changes into a single push. **If asked to review the PR:** - - Read `/tmp/pr-context/review_comments.json` and `/tmp/pr-context/reviews.json` to check existing threads and prior reviews — do not duplicate feedback. - - Review each changed file one at a time using diffs from `/tmp/pr-context/diffs/`. For each file: - 1. Read the diff from `/tmp/pr-context/diffs/.diff`. If the diff is empty or truncated (e.g., binary files or very large changes), fall back to running `git diff` against the base branch or compare the full file against context. - 2. Read the full file from the workspace (the PR branch is checked out) - 3. Check existing threads from `/tmp/pr-context/threads/.json` (if it exists) — skip issues already flagged - 4. Identify and verify issues per the **Code Review Reference** above - 5. Leave inline comments (`create_pull_request_review_comment`) before moving to the next file - - After all files are reviewed, call `submit_pull_request_review`. + - Call `ready_to_code_review` — this writes `/tmp/pr-context/agent-review.md` (review approach) and `/tmp/pr-context/parent-review.md` (comment format and inline severity threshold). Read both files. + - Review the PR following `agent-review.md`. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). + - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. + - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. - **Important**: Substantive feedback belongs in the PR review (inline comments + review submission), NOT in a reply comment. Do NOT add a separate comment after submitting the review unless the user explicitly asked for a comment or the review submission failed. - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. @@ -651,8 +555,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} @@ -671,9 +574,7 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} GH_AW_EXPR_95B47D04: ${{ inputs.target-pr-number || github.event.issue.number }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} @@ -684,6 +585,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} @@ -699,9 +601,7 @@ jobs: file: process.env.GH_AW_PROMPT, substitutions: { GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, - GH_AW_EXPR_7B543459: process.env.GH_AW_EXPR_7B543459, GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, - GH_AW_EXPR_8D9F5797: process.env.GH_AW_EXPR_8D9F5797, GH_AW_EXPR_95B47D04: process.env.GH_AW_EXPR_95B47D04, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, @@ -712,6 +612,7 @@ jobs: GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, GH_AW_INPUTS_PROMPT: process.env.GH_AW_INPUTS_PROMPT, GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED, GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: process.env.GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT @@ -765,21 +666,21 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - if: hashFiles('.python-version') != '' name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 @@ -819,15 +720,22 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `parent-review.md` | Comment format and inline severity threshold for the parent agent (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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" + 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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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" + - name: Write Playwright instructions to disk + run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: GITHUB_TOKEN: ${{ github.token }} REPO_NAME: ${{ github.repository }} @@ -1336,6 +1244,16 @@ jobs: "version": "1.0.0", "logDir": "/opt/gh-aw/safe-inputs/logs", "tools": [ + { + "name": "ready-to-code-review", + "description": "Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-code-review.py", + "timeout": 60 + }, { "name": "ready-to-push-to-pr", "description": "Run the PR readiness checklist before pushing to a PR", @@ -1368,6 +1286,135 @@ jobs: - name: Setup Safe Inputs Tool Files run: | + cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-code-review + # Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/ + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = os.environ.get('GH_AW_INPUTS_MINIMUM_SEVERITY', 'low') or 'low' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) + + GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' #!/usr/bin/env python3 # Auto-generated safe-input tool: ready-to-push-to-pr @@ -1590,13 +1637,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1957,7 +1997,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -2049,7 +2089,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -2108,7 +2148,7 @@ jobs: push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-mention-in-pr.md b/.github/workflows/gh-aw-mention-in-pr.md index 8b3ef7aa..c9d5a183 100644 --- a/.github/workflows/gh-aw-mention-in-pr.md +++ b/.github/workflows/gh-aw-mention-in-pr.md @@ -13,6 +13,8 @@ imports: - gh-aw-fragments/review-process.md - gh-aw-fragments/messages-footer.md - gh-aw-fragments/playwright-mcp-explorer.md + - gh-aw-fragments/pick-three-keep-many.md + - gh-aw-fragments/safe-output-code-review.md - gh-aw-fragments/safe-output-add-comment-pr.md - gh-aw-fragments/safe-output-review-comment.md - gh-aw-fragments/safe-output-submit-review.md @@ -142,10 +144,10 @@ Understand the request, investigate the code, and respond appropriately. 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. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). -3. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. -4. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. +1. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). +2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. +3. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. +4. Read `/tmp/pr-context/reviews.json` to check prior review submissions — note any prior verdicts to avoid redundant reviews. 5. Do not modify, review, comment on, or resolve threads for any PR other than #${{ inputs.target-pr-number || github.event.issue.number }}. ### Step 2: Handle the Request @@ -153,14 +155,14 @@ PR context is pre-fetched to `/tmp/pr-context/`. Read `/tmp/pr-context/README.md Based on what's asked, do the appropriate thing. **Requests can combine multiple actions** (e.g., "fix merge conflicts and address the review feedback"). When they do, handle them in this order: merge conflicts first, then code changes/review feedback, then push once at the end. Do not push between steps — batch all changes into a single push. **If asked to review the PR:** -- Read `/tmp/pr-context/review_comments.json` and `/tmp/pr-context/reviews.json` to check existing threads and prior reviews — do not duplicate feedback. -- Review each changed file one at a time using diffs from `/tmp/pr-context/diffs/`. For each file: - 1. Read the diff from `/tmp/pr-context/diffs/.diff`. If the diff is empty or truncated (e.g., binary files or very large changes), fall back to running `git diff` against the base branch or compare the full file against context. - 2. Read the full file from the workspace (the PR branch is checked out) - 3. Check existing threads from `/tmp/pr-context/threads/.json` (if it exists) — skip issues already flagged - 4. Identify and verify issues per the **Code Review Reference** above - 5. Leave inline comments (`create_pull_request_review_comment`) before moving to the next file -- After all files are reviewed, call `submit_pull_request_review`. +- Call `ready_to_code_review` — this writes `/tmp/pr-context/agent-review.md` (review approach) and `/tmp/pr-context/parent-review.md` (comment format and inline severity threshold). Read both files. +- Review the PR following `agent-review.md`. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). +- When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: + 1. **Read the file and surrounding context** — open the full file, not just the diff. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. + 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. +- Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. - **Important**: Substantive feedback belongs in the PR review (inline comments + review submission), NOT in a reply comment. Do NOT add a separate comment after submitting the review unless the user explicitly asked for a comment or the review submission failed. - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. diff --git a/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml b/.github/workflows/gh-aw-newbie-contributor-fixer.lock.yml index cb05744d..e2148a8c 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":"668455559a79bcaa71d52c9042ce5d548470939e3bc60820b6f09d82f3bf7576"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"ab3002196eecef2b8edd5165432bd563401a4e1dced5ab6142fd7e4d04c52e22"} name: "Newbie Contributor Fixer" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -222,11 +222,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -322,7 +321,7 @@ jobs: ### Step 1: Gather Candidates - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the candidate gathering instructions in the **Fix Assignment** section. 3. For each candidate, read the full issue and comments using `issue_read` (methods `get` and `get_comments`). @@ -489,7 +488,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -543,7 +542,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -627,6 +631,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1002,6 +1010,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1093,13 +1106,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1454,7 +1460,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1556,7 +1562,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1601,7 +1607,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-newbie-contributor-patrol.lock.yml b/.github/workflows/gh-aw-newbie-contributor-patrol.lock.yml index cad7ef32..d1aba281 100644 --- a/.github/workflows/gh-aw-newbie-contributor-patrol.lock.yml +++ b/.github/workflows/gh-aw-newbie-contributor-patrol.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"06259a70f1e35fae92419e444034acf23dccb98af6d0e37ed86116dee94e34e2"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2b22ee9fbc52c5de60f89a9e9290665199e6275962e0000ce379814c52c4a893"} name: "Newbie Contributor Patrol" "on": @@ -107,7 +107,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -218,11 +218,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -319,7 +318,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -516,7 +515,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -570,7 +569,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -909,13 +913,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1251,7 +1248,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1337,7 +1334,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1379,7 +1376,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-performance-profiler.lock.yml b/.github/workflows/gh-aw-performance-profiler.lock.yml index 0a127402..ecdda939 100644 --- a/.github/workflows/gh-aw-performance-profiler.lock.yml +++ b/.github/workflows/gh-aw-performance-profiler.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1374cf602e1d83af594cd0be0b85d385ae2398f5e81ed49964623072daf4ffe3"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"135fdda5553f27da60a061300b52142e61fc957ea83a6b7bb69a17ae20853c01"} name: "Performance Profiler" "on": @@ -109,7 +109,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -222,11 +222,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -348,7 +347,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -616,7 +615,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -670,7 +669,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -1015,13 +1019,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1357,7 +1354,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1443,7 +1440,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1485,7 +1482,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-plan.lock.yml b/.github/workflows/gh-aw-plan.lock.yml index ef2e9268..c9044f2e 100644 --- a/.github/workflows/gh-aw-plan.lock.yml +++ b/.github/workflows/gh-aw-plan.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"38507eedd23a70475b544d40a83ab375426411eeb17c163a7a3efdecb5ee6aee"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"352f263e9295cd9e82b9cd61be6f343605f4f0bb3d2e5daa86b3d76d8a46ccf4"} name: "Plan" "on": @@ -118,7 +118,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -242,11 +242,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -358,10 +357,9 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get repository coding guidelines and conventions. If this fails, continue without it. - 2. Read the full issue thread and the request comment. - 3. If related issues or PRs are referenced, call `issue_read` / `pull_request_read` with method `get` for each relevant item. - 4. Use `grep` and file reading to inspect the relevant code paths. + 1. Read the full issue thread and the request comment. + 2. If related issues or PRs are referenced, call `issue_read` / `pull_request_read` with method `get` for each relevant item. + 3. Use `grep` and file reading to inspect the relevant code paths. ### Step 2: Investigate and Plan @@ -490,7 +488,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -544,7 +542,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -917,13 +920,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1259,7 +1255,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1349,7 +1345,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1406,7 +1402,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-plan.md b/.github/workflows/gh-aw-plan.md index 6b5f7113..42b24568 100644 --- a/.github/workflows/gh-aw-plan.md +++ b/.github/workflows/gh-aw-plan.md @@ -105,10 +105,9 @@ Understand the request, investigate the codebase, and produce a concrete impleme ### Step 1: Gather Context -1. Call `generate_agents_md` to get repository coding guidelines and conventions. If this fails, continue without it. -2. Read the full issue thread and the request comment. -3. If related issues or PRs are referenced, call `issue_read` / `pull_request_read` with method `get` for each relevant item. -4. Use `grep` and file reading to inspect the relevant code paths. +1. Read the full issue thread and the request comment. +2. If related issues or PRs are referenced, call `issue_read` / `pull_request_read` with method `get` for each relevant item. +3. Use `grep` and file reading to inspect the relevant code paths. ### Step 2: Investigate and Plan diff --git a/.github/workflows/gh-aw-pr-actions-detective.lock.yml b/.github/workflows/gh-aw-pr-actions-detective.lock.yml index 241337bb..1d97a6f9 100644 --- a/.github/workflows/gh-aw-pr-actions-detective.lock.yml +++ b/.github/workflows/gh-aw-pr-actions-detective.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2636e6e504e1842e1eb957daf386ea09189f3af35dc3a264e4079796de6e581f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"23d8d2db120df3daa5b97bcc7fb6f2d380212d7d4bd16a39af2382e35916bf81"} name: "PR Actions Detective" "on": @@ -100,7 +100,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -213,11 +213,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -317,10 +316,9 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Identify the PRs associated with the workflow run using `github.event.workflow_run.pull_requests`. If there are none, call `noop` with message "No pull request associated with workflow run; nothing to do" and stop. - 3. For each PR, call `pull_request_read` with method `get` to capture the author, branches, and fork status. - 4. Fetch workflow run details and logs with `bash` + `gh api`: + 1. Identify the PRs associated with the workflow run using `github.event.workflow_run.pull_requests`. If there are none, call `noop` with message "No pull request associated with workflow run; nothing to do" and stop. + 2. For each PR, call `pull_request_read` with method `get` to capture the author, branches, and fork status. + 3. Fetch workflow run details and logs with `bash` + `gh api`: - List jobs and their conclusions: ````bash gh api repos/__GH_AW_GITHUB_REPOSITORY__/actions/runs/{run_id}/jobs \ @@ -455,7 +453,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -509,7 +507,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -808,13 +811,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1150,7 +1146,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1236,7 +1232,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1278,7 +1274,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-pr-actions-detective.md b/.github/workflows/gh-aw-pr-actions-detective.md index 3dcc1a72..fcf00cb6 100644 --- a/.github/workflows/gh-aw-pr-actions-detective.md +++ b/.github/workflows/gh-aw-pr-actions-detective.md @@ -92,10 +92,9 @@ Assist with failed GitHub Actions checks for pull requests in ${{ github.reposit ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Identify the PRs associated with the workflow run using `github.event.workflow_run.pull_requests`. If there are none, call `noop` with message "No pull request associated with workflow run; nothing to do" and stop. -3. For each PR, call `pull_request_read` with method `get` to capture the author, branches, and fork status. -4. Fetch workflow run details and logs with `bash` + `gh api`: +1. Identify the PRs associated with the workflow run using `github.event.workflow_run.pull_requests`. If there are none, call `noop` with message "No pull request associated with workflow run; nothing to do" and stop. +2. For each PR, call `pull_request_read` with method `get` to capture the author, branches, and fork status. +3. Fetch workflow run details and logs with `bash` + `gh api`: - List jobs and their conclusions: ````bash gh api repos/${{ github.repository }}/actions/runs/{run_id}/jobs \ diff --git a/.github/workflows/gh-aw-pr-actions-fixer.lock.yml b/.github/workflows/gh-aw-pr-actions-fixer.lock.yml index cb97915b..461d80b1 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":"08b813feb96862e82170249f6c6d7cd556895b7e1ba077737c3f7a775e3b7a53"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8cae26dcfb0ea6972c879d755eff21256de23870d622bf6bad7c183c1f50e657"} name: "PR Actions Fixer" "on": @@ -113,7 +113,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -228,11 +228,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -348,8 +347,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Fetch workflow run details using `inputs.workflow-run-id`: + 1. Fetch workflow run details using `inputs.workflow-run-id`: ````bash gh api repos/__GH_AW_GITHUB_REPOSITORY__/actions/runs/{run_id} \ --jq '{id: .id, html_url: .html_url, pull_requests: .pull_requests}' @@ -492,7 +490,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -546,7 +544,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -1125,13 +1128,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1485,7 +1481,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1573,7 +1569,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1619,7 +1615,7 @@ jobs: push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-pr-actions-fixer.md b/.github/workflows/gh-aw-pr-actions-fixer.md index de53afc8..6cb70c1e 100644 --- a/.github/workflows/gh-aw-pr-actions-fixer.md +++ b/.github/workflows/gh-aw-pr-actions-fixer.md @@ -98,8 +98,7 @@ Assist with failed GitHub Actions checks for pull requests in ${{ github.reposit ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. -2. Fetch workflow run details using `inputs.workflow-run-id`: +1. Fetch workflow run details using `inputs.workflow-run-id`: ````bash gh api repos/${{ github.repository }}/actions/runs/{run_id} \ --jq '{id: .id, html_url: .html_url, pull_requests: .pull_requests}' diff --git a/.github/workflows/gh-aw-pr-ci-detective.lock.yml b/.github/workflows/gh-aw-pr-ci-detective.lock.yml index 10c20158..67ac36ef 100644 --- a/.github/workflows/gh-aw-pr-ci-detective.lock.yml +++ b/.github/workflows/gh-aw-pr-ci-detective.lock.yml @@ -41,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2636e6e504e1842e1eb957daf386ea09189f3af35dc3a264e4079796de6e581f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"23d8d2db120df3daa5b97bcc7fb6f2d380212d7d4bd16a39af2382e35916bf81"} name: "PR Actions Detective" "on": @@ -105,7 +105,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -218,11 +218,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -322,10 +321,9 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. - 2. Identify the PRs associated with the workflow run using `github.event.workflow_run.pull_requests`. If there are none, call `noop` with message "No pull request associated with workflow run; nothing to do" and stop. - 3. For each PR, call `pull_request_read` with method `get` to capture the author, branches, and fork status. - 4. Fetch workflow run details and logs with `bash` + `gh api`: + 1. Identify the PRs associated with the workflow run using `github.event.workflow_run.pull_requests`. If there are none, call `noop` with message "No pull request associated with workflow run; nothing to do" and stop. + 2. For each PR, call `pull_request_read` with method `get` to capture the author, branches, and fork status. + 3. Fetch workflow run details and logs with `bash` + `gh api`: - List jobs and their conclusions: ````bash gh api repos/__GH_AW_GITHUB_REPOSITORY__/actions/runs/{run_id}/jobs \ @@ -460,7 +458,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -514,7 +512,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -813,13 +816,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1155,7 +1151,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1241,7 +1237,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1283,7 +1279,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-pr-review-addresser.lock.yml b/.github/workflows/gh-aw-pr-review-addresser.lock.yml index 7b3904d0..17fc486e 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":"54ab6ea22d092d1d705183e80677330ce4568b345f16a555867f04f55a7e286f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"13741b39aaf4036f92846e5f2b6441746df050f49c42458469d4bcd2b5f64730"} name: "PR Review Addresser" "on": @@ -117,7 +117,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -234,11 +234,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -379,11 +378,10 @@ jobs: 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. 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/unresolved_threads.json` to get unresolved review threads that need attention. For full context including resolved threads, see `review_comments.json`. - 5. Read `/tmp/pr-context/diffs/` to understand the current state of changes. + 1. 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. + 2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. + 3. Read `/tmp/pr-context/unresolved_threads.json` to get unresolved review threads that need attention. For full context including resolved threads, see `review_comments.json`. + 4. Read `/tmp/pr-context/diffs/` to understand the current state of changes. ### Step 2: Address Each Review Thread @@ -532,7 +530,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -586,13 +584,18 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `parent-review.md` | Comment format and inline severity threshold for the parent agent (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\nMANIFEST\n\necho \"PR context written to /tmp/pr-context/\"\nls -la /tmp/pr-context/" - env: GITHUB_TOKEN: ${{ github.token }} REPO_NAME: ${{ github.repository }} @@ -1234,13 +1237,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1594,7 +1590,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1682,7 +1678,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1728,7 +1724,7 @@ jobs: push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-pr-review-addresser.md b/.github/workflows/gh-aw-pr-review-addresser.md index 8907ed77..bc02256a 100644 --- a/.github/workflows/gh-aw-pr-review-addresser.md +++ b/.github/workflows/gh-aw-pr-review-addresser.md @@ -119,11 +119,10 @@ Address the review feedback surgically — make only the minimum changes needed. 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. 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/unresolved_threads.json` to get unresolved review threads that need attention. For full context including resolved threads, see `review_comments.json`. -5. Read `/tmp/pr-context/diffs/` to understand the current state of changes. +1. 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. +2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. +3. Read `/tmp/pr-context/unresolved_threads.json` to get unresolved review threads that need attention. For full context including resolved threads, see `review_comments.json`. +4. Read `/tmp/pr-context/diffs/` to understand the current state of changes. ### Step 2: Address Each Review Thread diff --git a/.github/workflows/gh-aw-pr-review.invalid.yml b/.github/workflows/gh-aw-pr-review.invalid.yml new file mode 100644 index 00000000..5058e93e --- /dev/null +++ b/.github/workflows/gh-aw-pr-review.invalid.yml @@ -0,0 +1,1713 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw. DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# AI code review with inline comments on pull requests +# +# Resolved workflow manifest: +# Imports: +# - gh-aw-fragments/elastic-tools.md +# - gh-aw-fragments/formatting.md +# - gh-aw-fragments/mcp-pagination.md +# - gh-aw-fragments/messages-footer.md +# - gh-aw-fragments/network-ecosystems.md +# - gh-aw-fragments/pick-three-keep-many.md +# - gh-aw-fragments/pr-context.md +# - gh-aw-fragments/review-process.md +# - gh-aw-fragments/rigor.md +# - gh-aw-fragments/runtime-setup.md +# - gh-aw-fragments/safe-output-code-review.md +# - gh-aw-fragments/safe-output-review-comment.md +# - gh-aw-fragments/safe-output-submit-review.md +# +# inlined-imports: true +# +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"52ffb4f3214324bb2765dc259c6e46934f960e2d35e7af73b93069e48cd03e21"} + +name: "PR Review" +"on": + # bots: # Bots processed as bot check in pre-activation job + # - ${{ inputs.allowed-bot-users }} # Bots processed as bot check in pre-activation job + # roles: # Roles processed as role check in pre-activation job + # - admin # Roles processed as role check in pre-activation job + # - maintainer # Roles processed as role check in pre-activation job + # - write # Roles processed as role check in pre-activation job + workflow_call: + inputs: + additional-instructions: + default: "" + description: Repo-specific instructions appended to the agent prompt + required: false + type: string + allowed-bot-users: + default: github-actions[bot] + description: Allowlisted bot actor usernames (comma-separated) + required: false + type: string + create-pull-request-review-comment-max: + default: "30" + description: Maximum number of review comments the agent can create per run + required: false + type: string + intensity: + default: balanced + description: "Review intensity: conservative, balanced, or aggressive" + required: false + type: string + messages-footer: + default: "" + description: Footer appended to all agent comments and reviews + required: false + type: string + minimum_severity: + default: low + description: "Minimum severity for inline comments: critical, high, medium, low, or nitpick. Issues below this threshold go in a collapsible section of the review body instead." + required: false + type: string + model: + default: gpt-5.3-codex + description: AI model to use + required: false + type: string + setup-commands: + default: "" + description: Shell commands to run before the agent starts (dependency install, build, etc.) + required: false + type: string + secrets: + COPILOT_GITHUB_TOKEN: + required: true + +permissions: {} + +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-pr-review-${{ github.event.pull_request.number }} + +run-name: "PR Review" + +jobs: + activation: + needs: pre_activation + if: needs.pre_activation.outputs.activated == 'true' + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + comment_id: "" + comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: "${{ inputs.model }}" + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "0.0.420" + GH_AW_INFO_WORKFLOW_NAME: "PR Review" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","clojure","cloud.elastic.co","containers","dart","defaults","dotnet","ela.st","elastic.co","elastic.dev","elastic.github.io","elixir","fonts","github","github-actions","go","haskell","java","kotlin","linux-distros","node","node-cdns","perl","php","playwright","public-code-search.fastmcp.app","python","ruby","rust","scala","swift","terraform","www.elastic.co","zig"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.23.0" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 + persist-credentials: false + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_WORKFLOW_FILE: "gh-aw-pr-review.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_INTENSITY: ${{ inputs.intensity }} + GH_AW_INPUTS_MINIMUM_SEVERITY: ${{ inputs.minimum_severity }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" + cat << 'GH_AW_PROMPT_EOF' + + Tools: create_pull_request_review_comment, submit_pull_request_review, missing_tool, missing_data, noop + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## MCP Servers + + - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Formatting Guidelines + + - Lead with the most important information — your first sentence should be the key takeaway + - Be concise and actionable — no filler or praise + - Use `
` and `` tags for long sections to keep responses scannable + - Wrap branch names and @-references in backticks to avoid pinging users + - Include code snippets with file paths and line numbers when referencing the codebase + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Rigor + + **Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report.** + + - If you claim something is missing or broken, show the exact evidence in the code — file path, line number, and what you observed. + - If a conclusion depends on assumptions you haven't confirmed, do not assert it. Verify first; if you cannot verify, do not report. + - "I don't know" is better than a wrong answer. `noop` is better than a speculative finding. + - It's worth the time to verify now versus guessing and forcing someone else to verify later. + - Before submitting any output, re-read it as a skeptical reviewer. Ask: "Would a senior engineer on this team find this useful, or would they close it immediately?" If the answer is "close," call `noop` instead. + - Only report findings you would confidently defend in a code review. If you feel the need to hedge with "might," "could," or "possibly," the finding is not ready to file. + - Be thorough, spend the time to investigate and verify. There is no rush. Do your best work. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## MCP Pagination + + MCP tool responses have a **25,000 token limit**. When responses exceed this limit, the call fails and you must retry with pagination — wasting turns and tokens. Use proactive pagination to stay under the limit. + + ### Recommended `perPage` Values + + - **5-10**: For detailed items (PR diffs, files with patches, issues with comments) + - **20-30**: For medium-detail lists (commits, review comments, issue lists) + - **50-100**: For simple list operations (branches, labels, tags) + + ### Pagination Pattern + + When you need all results from a paginated API: + + 1. Fetch the first page with a conservative `perPage` value + 2. Process the results before fetching the next page + 3. Continue fetching pages until you receive fewer results than `perPage` (indicating the last page) + + ### Error Recovery + + If you see an error like: + - `MCP tool response exceeds maximum allowed tokens (25000)` + - `Response too large for tool [tool_name]` + + Retry the same call with a smaller `perPage` value (halve it). + + ### Tips + + - **Start small**: It's better to make multiple small requests than one that fails + - **Fetch incrementally**: Get an overview first, then details for specific items + - **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. **Never mention these file paths or on-disk data sources in your responses** — they are internal implementation details invisible to users. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Code Review Reference + + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md`. Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md`. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## Message Footer + + A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## create-pull-request-review-comment + + - **Required fields**: `path` (file path), `line` (line number), and `body` (comment text). + - **Line**: Must be within the diff — an added or context line in the patch. Must be the **exact line number from reading the file** (not estimated from the patch). Lines outside the diff will fail. + - **Body**: Sanitized with the standard pipeline (mentions neutralized, HTML filtered, URLs restricted). GitHub API limit is ~65,536 characters. + - **Side**: Defaults to `RIGHT` (the new code). Use `LEFT` only when commenting on deleted lines. + - **Suggestion blocks**: Use ` ```suggestion ` fences for concrete code fixes. The suggestion must actually change the code — don't suggest identical code. Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. + + Only flag issues you are confident are real problems — false positives erode trust. Once you have flagged an issue, you cannot unflag it. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ## submit-pull-request-review Limitations + + - **Event**: Must be one of `APPROVE`, `REQUEST_CHANGES`, or `COMMENT`. Defaults to `COMMENT` if omitted. + - **Body**: Max 65,000 characters. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. A body is required when event is `REQUEST_CHANGES`. Sanitized (mentions neutralized, HTML filtered, URLs restricted). If you have also used `create-pull-request-review-comment`, you do not need to repeat the same feedback in the body. If you "Approve" and have no comments, do not provide a `body`. + - **Own PRs**: If the workflow actor is also the PR author (e.g., `github-actions[bot]` reviewing its own PR), the event is forced to `COMMENT` regardless of what you specify. `APPROVE` and `REQUEST_CHANGES` will not work. + - **Max per run**: 1 review submission per workflow run. Leave inline comments first, then submit the review as a single final action. + + **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + ### Pick Three, Keep Many + + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + + **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: + + - The full task description and objective (restate it, don't summarize) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) + - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) + - The quality criteria and output format you expect + - The specific angle that distinguishes this sub-agent from the others + + Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. + + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + + **Merge and deduplicate findings** across all sub-agents: + 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. + 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. + 3. Drop any finding that does not meet the verification criteria. + + **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + # PR Review Agent + + Review pull requests in __GH_AW_GITHUB_REPOSITORY__ and provide actionable feedback via inline review comments on specific code lines. + + ## Context + + - **Repository**: __GH_AW_GITHUB_REPOSITORY__ + - **PR**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ — __GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE__ + - **PR context on disk**: `/tmp/pr-context/` — PR metadata, diff, files, reviews, comments, and linked issues are pre-fetched. Read from these files instead of calling the API. + + ## Constraints + + This workflow is read-only. You can read files, search code, run commands, and interact with PRs and issues — but your only outputs are inline review comments and a review submission. + + ## Review Process + + Follow these steps in order. + + ### Step 1: Gather Context + + 1. Call `ready_to_code_review` to prepare the review approach based on PR size. + 2. Read `/tmp/agents.md` for repository conventions (skip if missing). + 3. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). + 4. Read `/tmp/pr-context/issue-*.json` files if any exist to understand linked issue motivation and acceptance criteria. + 5. Read `/tmp/pr-context/reviews.json` to check prior review submissions from this bot. Note any prior verdicts to avoid redundant reviews. + 6. Read `/tmp/pr-context/review_comments.json` to check existing review threads. Note which files already have threads and whether they are resolved, unresolved, or outdated. + + ### Step 2: Review + + Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. + + ### Step 3: Verify and Comment + + If sub-agents were used, merge and deduplicate findings per the Pick Three, Keep Many process. Verify each finding before leaving a comment. For every finding: + + 1. **Read the file and surrounding context** — open the full file, not just the diff. Understand the broader code. + 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. + 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If "probably not" or "unsure", drop it. + 4. **Check existing threads** — if this issue was already flagged in a prior review (resolved or unresolved), do not duplicate. + + Only leave a comment if the finding survives all four checks. Findings flagged independently by multiple sub-agents are stronger candidates. Findings from only one sub-agent deserve extra scrutiny. + + Leave inline comments (`create_pull_request_review_comment`) per the **Code Review Reference** above for each finding that survives verification. Comment on each file's findings before moving to the next file. If no findings survive verification, proceed directly to Step 4. + + ### Step 4: Submit the Review + + **Skip if nothing new:** If you left zero inline comments during this review AND your verdict would be the same as the most recent review from this bot (compare against reviews in Step 1), call `noop` with a message like "No new findings — prior review still applies" and stop. Do not submit a redundant review. + + After all comments are posted, step back and consider the PR as a whole. Call **`submit_pull_request_review`** with: + - The review type (REQUEST_CHANGES, COMMENT, or APPROVE) + - A review body that is **only the verdict and only if the verdict is not APPROVE**. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. + + **Bot-authored PRs:** If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. + + **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. + + If you have no issues, or you have only provided NITPICK and LOW issues, submit an APPROVE review. Otherwise, submit a REQUEST_CHANGES review. + + ## Review Settings + + - **Intensity**: `__GH_AW_INPUTS_INTENSITY__` + - **Minimum inline severity**: `__GH_AW_INPUTS_MINIMUM_SEVERITY__` + + These override the defaults defined in the Code Review Reference above. + + __GH_AW_EXPR_49B959F1__ + + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_INPUTS_INTENSITY: ${{ inputs.intensity }} + GH_AW_INPUTS_MINIMUM_SEVERITY: ${{ inputs.minimum_severity }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_INPUTS_INTENSITY: ${{ inputs.intensity }} + GH_AW_INPUTS_MINIMUM_SEVERITY: ${{ inputs.minimum_severity }} + GH_AW_INPUTS_MODEL: ${{ inputs.model }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_INPUTS_INTENSITY: process.env.GH_AW_INPUTS_INTENSITY, + GH_AW_INPUTS_MINIMUM_SEVERITY: process.env.GH_AW_INPUTS_MINIMUM_SEVERITY, + GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + issues: read + pull-requests: read + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}-pr-review-${{ github.event.pull_request.number }}" + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_WORKFLOW_ID_SANITIZED: ghawprreview + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + model: ${{ needs.activation.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - if: hashFiles('go.mod') != '' + name: Setup Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + with: + cache: true + go-version-file: go.mod + - if: hashFiles('.python-version') != '' + name: Setup Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version-file: .python-version + - if: hashFiles('.node-version') != '' + name: Setup Node.js (.node-version) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + with: + node-version-file: .node-version + - if: hashFiles('.node-version') == '' && hashFiles('.nvmrc') != '' + name: Setup Node.js (.nvmrc) + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + with: + node-version-file: .nvmrc + - if: hashFiles('.ruby-version') != '' + name: Setup Ruby + uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 + with: + bundler-cache: true + ruby-version: .ruby-version + - id: setup-uv + if: hashFiles('pyproject.toml', 'uv.lock') != '' + name: Setup uv + uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + - env: + UV_PATH: ${{ steps.setup-uv.outputs.uv-path }} + WORKSPACE: ${{ github.workspace }} + if: hashFiles('pyproject.toml', 'uv.lock') != '' + name: Expose uv in workspace + run: | + set -euo pipefail + install_dir="$WORKSPACE/.gh-aw-tools/bin" + mkdir -p "$install_dir" + cp "$UV_PATH" "$install_dir/uv" + chmod +x "$install_dir/uv" + echo "$install_dir" >> "$GITHUB_PATH" + shell: bash + - name: Configure Copilot CLI settings + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" + 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: + SETUP_COMMANDS: ${{ inputs.setup-commands }} + if: ${{ inputs.setup-commands != '' }} + name: Repo-specific setup + run: eval "$SETUP_COMMANDS" + + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420 + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.6 ghcr.io/github/github-mcp-server:v0.31.0 node:lts-alpine + - name: Write Safe Outputs Config + run: | + mkdir -p /opt/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"create_pull_request_review_comment":{"max":"${{ inputs.create-pull-request-review-comment-max }}"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"submit_pull_request_review":{"max":1}} + GH_AW_SAFE_OUTPUTS_CONFIG_EOF + cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' + [ + { + "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Comments will be on the RIGHT side of the diff.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", + "type": "string" + }, + "line": { + "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", + "type": [ + "number", + "string" + ] + }, + "path": { + "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", + "type": "string" + }, + "side": { + "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", + "enum": [ + "LEFT", + "RIGHT" + ], + "type": "string" + }, + "start_line": { + "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", + "type": [ + "number", + "string" + ] + } + }, + "required": [ + "path", + "line", + "body" + ], + "type": "object" + }, + "name": "create_pull_request_review_comment" + }, + { + "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "body": { + "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", + "type": "string" + }, + "event": { + "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", + "enum": [ + "APPROVE", + "REQUEST_CHANGES", + "COMMENT" + ], + "type": "string" + } + }, + "type": "object" + }, + "name": "submit_pull_request_review" + }, + { + "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "reason": { + "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", + "type": "string" + }, + "tool": { + "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", + "type": "string" + } + }, + "required": [ + "reason" + ], + "type": "object" + }, + "name": "missing_tool" + }, + { + "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "message": { + "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "name": "noop" + }, + { + "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "alternatives": { + "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", + "type": "string" + }, + "context": { + "description": "Additional context about the missing data or where it should come from (max 256 characters).", + "type": "string" + }, + "data_type": { + "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", + "type": "string" + }, + "reason": { + "description": "Explanation of why this data is needed to complete the task (max 256 characters).", + "type": "string" + } + }, + "required": [], + "type": "object" + }, + "name": "missing_data" + } + ] + GH_AW_SAFE_OUTPUTS_TOOLS_EOF + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "create_pull_request_review_comment": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "line": { + "required": true, + "positiveInteger": true + }, + "path": { + "required": true, + "type": "string" + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "side": { + "type": "string", + "enum": [ + "LEFT", + "RIGHT" + ] + }, + "start_line": { + "optionalPositiveInteger": true + } + }, + "customValidation": "startLineLessOrEqualLine" + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + }, + "submit_pull_request_review": { + "defaultMax": 1, + "fields": { + "body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "event": { + "type": "string", + "enum": [ + "APPROVE", + "REQUEST_CHANGES", + "COMMENT" + ] + } + } + } + } + GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash /opt/gh-aw/actions/start_safe_outputs_server.sh + + - name: Setup Safe Inputs Config + run: | + mkdir -p /opt/gh-aw/safe-inputs/logs + cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' + { + "serverName": "safeinputs", + "version": "1.0.0", + "logDir": "/opt/gh-aw/safe-inputs/logs", + "tools": [ + { + "name": "ready-to-code-review", + "description": "Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-code-review.py", + "timeout": 60 + } + ] + } + GH_AW_SAFE_INPUTS_TOOLS_EOF + cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' + const path = require("path"); + const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); + const configPath = path.join(__dirname, "tools.json"); + const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); + const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; + startHttpServer(configPath, { + port: port, + stateless: true, + logDir: "/opt/gh-aw/safe-inputs/logs" + }).catch(error => { + console.error("Failed to start safe-inputs HTTP server:", error); + process.exit(1); + }); + GH_AW_SAFE_INPUTS_SERVER_EOF + chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs + + - name: Setup Safe Inputs Tool Files + run: | + cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-code-review + # Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/ + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = '${{ inputs.minimum_severity || "low" }}' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) + + GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py + + - name: Generate Safe Inputs MCP Server Config + id: safe-inputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3000 + + # Set outputs for next steps + { + echo "safe_inputs_api_key=${API_KEY}" + echo "safe_inputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Inputs MCP server will run on port ${PORT}" + + - name: Start Safe Inputs MCP HTTP Server + id: safe-inputs-start + env: + DEBUG: '*' + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_INPUTS_PORT + export GH_AW_SAFE_INPUTS_API_KEY + + bash /opt/gh-aw/actions/start_safe_inputs_server.sh + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.31.0", + "env": { + "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "repos,issues,pull_requests,search,actions" + } + }, + "public-code-search": { + "type": "http", + "url": "https://public-code-search.fastmcp.app/mcp", + "tools": [ + "search_code" + ] + }, + "safeinputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_EOF + - name: Download activation artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: activation + path: /tmp/gh-aw + - name: Clean git credentials + run: bash /opt/gh-aw/actions/clean_git_credentials.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 90 + run: | + set -o pipefail + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ inputs.model }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload Safe Outputs + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output + path: ${{ env.GH_AW_SAFE_OUTPUTS }} + if-no-files-found: warn + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + 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 }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Upload sanitized agent output + if: always() && env.GH_AW_AGENT_OUTPUT + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-output + path: ${{ env.GH_AW_AGENT_OUTPUT }} + if-no-files-found: warn + - name: Upload engine output files + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent_outputs + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + if-no-files-found: ignore + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse Safe Inputs logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: agent-artifacts + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/safe-inputs/logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + if-no-files-found: ignore + # --- Threat Detection (inline) --- + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + WORKFLOW_NAME: "PR Review" + WORKFLOW_DESCRIPTION: "AI code review with inline comments on pull requests" + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ inputs.model }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_detection_results + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: threat-detection.log + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Set detection conclusion + id: detection_conclusion + if: always() + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} + run: | + if [[ "$RUN_DETECTION" != "true" ]]; then + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection was not needed, marking as skipped" + elif [[ "$DETECTION_SUCCESS" == "true" ]]; then + echo "conclusion=success" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection passed successfully" + else + echo "conclusion=failure" >> "$GITHUB_OUTPUT" + echo "success=false" >> "$GITHUB_OUTPUT" + echo "Detection found issues" + fi + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: read + pull-requests: write + outputs: + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "PR Review" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing Tool + id: missing_tool + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "PR Review" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent Failure + id: handle_agent_failure + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "PR Review" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "gh-aw-pr-review" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" + GH_AW_GROUP_REPORTS: "false" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op Message + id: handle_noop_message + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "PR Review" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "true" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + pre_activation: + runs-on: ubuntu-slim + outputs: + activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} + matched_command: '' + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Check team membership for workflow + id: check_membership + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_REQUIRED_ROLES: admin,maintainer,write + GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); + await main(); + + safe_outputs: + needs: agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') + runs-on: ubuntu-slim + permissions: + contents: read + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_ENGINE_ID: "copilot" + GH_AW_ENGINE_MODEL: "${{ inputs.model }}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" + GH_AW_WORKFLOW_ID: "gh-aw-pr-review" + GH_AW_WORKFLOW_NAME: "PR Review" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + steps: + - name: Setup Scripts + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + continue-on-error: true + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 + with: + name: agent-output + path: /tmp/gh-aw/safeoutputs/ + - name: Setup agent output environment variable + run: | + mkdir -p /tmp/gh-aw/safeoutputs/ + find "/tmp/gh-aw/safeoutputs/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + 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: "{\"create_pull_request_review_comment\":{\"max\":\"${{ inputs.create-pull-request-review-comment-max }}\",\"side\":\"RIGHT\"},\"missing_data\":{},\"missing_tool\":{},\"submit_pull_request_review\":{\"footer\":\"if-body\",\"max\":1}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload safe output items manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn + diff --git a/.github/workflows/gh-aw-pr-review.lock.yml b/.github/workflows/gh-aw-pr-review.lock.yml index 8253f0f7..bf850a8f 100644 --- a/.github/workflows/gh-aw-pr-review.lock.yml +++ b/.github/workflows/gh-aw-pr-review.lock.yml @@ -35,12 +35,13 @@ # - gh-aw-fragments/review-process.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md +# - gh-aw-fragments/safe-output-code-review.md # - gh-aw-fragments/safe-output-review-comment.md # - gh-aw-fragments/safe-output-submit-review.md # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"2b55424743c0138975298375ab50c49188a8c8b6c99333daddefe42fcba82d9b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fd7dc0e8bc82541146175ab8479c15fbb4acc7a5c0af5c5ec011a6a8b3e2f30f"} name: "PR Review" "on": @@ -118,7 +119,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -172,8 +173,6 @@ jobs: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} @@ -235,11 +234,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -307,90 +305,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Code Review Reference - ### Comment Format - - Call **`create_pull_request_review_comment`** with: - - The file path and the **exact line number from reading the file** (not estimated from the diff) - - The line must be within the diff (an added or context line in the patch) - - ````` - **[SEVERITY] Brief title** - - Description of the issue and why it matters. - - ```suggestion - corrected code here - ``` - ````` - - Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line. - - ### Inline Comment Threshold - - The minimum severity for inline comments is `__GH_AW_EXPR_7B543459__`. - - Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body instead — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters. - - Severity order (highest to lowest): critical > high > medium > low > nitpick. - - If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`. - - ### Review Criteria - - Focus on these categories in priority order: - - 1. Security vulnerabilities (injection, XSS, auth bypass, secrets exposure) - 2. Logic bugs that could cause runtime failures or incorrect behavior - 3. Data integrity issues (race conditions, missing transactions, corruption risk) - 4. Performance bottlenecks (N+1 queries, memory leaks, blocking operations) - 5. Error handling gaps (unhandled exceptions, missing validation) - 6. Breaking changes to public APIs without migration path - 7. Missing or incorrect test coverage for critical paths - - ### What NOT to Flag - - Only 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. - - **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: - - - **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. - - **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. - - **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). - - **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. - - **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. - - **Test coverage — trivial or generated code:** Don't flag missing tests for trivial getters/setters, auto-generated code, or simple delegation methods. - - **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). - - **Existing review threads** — check BEFORE leaving any comment: - - - **Resolved with reviewer reply** (e.g. "This is intentional") — reviewer's decision is final. Do NOT re-flag. - - **Resolved without reply** — author likely fixed it. Do NOT re-raise unless the fix introduced a new problem. - - **Unresolved** — already flagged. Do NOT duplicate. - - **Outdated** — only re-flag if the issue still applies to the current diff. - - When in doubt, do not duplicate. Redundant comments erode trust. - - Finding no issues is a valid and valuable outcome. An APPROVE with zero inline comments is better than comments that waste the author's time or erode trust. Do not manufacture findings to justify your review — if the code is sound, approve without comments. - - ### Severity Classification - - Determine 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. Do not start with a severity and look for issues to match it. - - - 🔴 **CRITICAL** — Must fix before merge (security vulnerabilities, data corruption, production-breaking bugs) - - 🟠 **HIGH** — Should fix before merge (logic errors, missing validation, significant performance issues) - - 🟡 **MEDIUM** — Address soon, non-blocking (error handling gaps, suboptimal patterns, missing edge cases) - - ⚪ **LOW** — Author discretion (minor improvements, documentation gaps) - - 💬 **NITPICK** — Truly optional (stylistic preferences, alternative approaches) - - ### Review Intensity - - The review intensity is `__GH_AW_EXPR_8D9F5797__`. - - - **`conservative`**: High evidence bar. Only comment when you can demonstrate a concrete failure scenario — what specific input or state triggers the bug. After identifying a potential issue, explicitly challenge your own finding: if you can construct a reasonable counterargument, do not comment. Give the author maximum benefit of the doubt. Approval with zero comments is the expected outcome for most PRs. - - **`balanced`**: Standard evidence bar. Comment when you can point to specific code that would fail and have verified the issue through the full verification protocol. Give the author reasonable benefit of the doubt — if the issue is ambiguous, lean toward not commenting. - - **`aggressive`**: Lower evidence bar. Comment when evidence exists even if the failure scenario is not fully confirmed. Improvement suggestions and alternative approaches are welcome but must still cite specific code. Do not speculate without any evidence, and do not duplicate existing threads. - - If the value is unrecognized, treat it as `balanced`. + Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -421,19 +336,19 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ### Pick Three, Keep Many - Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - **Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. @@ -445,6 +360,9 @@ jobs: GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' # PR Review Agent @@ -467,7 +385,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. Write the result to `/tmp/pr-context/agents.md` so sub-agents can read it. If `generate_agents_md` fails, continue without it. + 1. Read `/tmp/agents.md` for repository conventions (skip if missing). 2. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). 3. Read `/tmp/pr-context/issue-*.json` files if any exist to understand linked issue motivation and acceptance criteria. 4. Read `/tmp/pr-context/reviews.json` to check prior review submissions from this bot. Note any prior verdicts to avoid redundant reviews. @@ -475,27 +393,8 @@ jobs: ### Step 2: Review - Read `/tmp/pr-context/pr-size.txt` for the PR size (file count and diff lines). Use it to decide your review approach: - - - **Small PRs (under 200 diff lines):** Review directly — no sub-agents. Read files in the order from `/tmp/pr-context/file_order_az.txt`. For each file, read the diff from `/tmp/pr-context/diffs/.diff`, read the full file from the workspace, check existing threads from `/tmp/pr-context/threads/.json`, and identify issues per the Code Review Reference criteria. Then proceed to Step 3 with your findings. - - - **Medium PRs (200–800 diff lines):** Follow the **Pick Three, Keep Many** process — spawn 2 `code-review` sub-agents in parallel: - - **Agent 1**: file ordering from `/tmp/pr-context/file_order_az.txt` (A → Z) - - **Agent 2**: file ordering from `/tmp/pr-context/file_order_za.txt` (Z → A) - - - **Large PRs (over 800 diff lines):** Follow the **Pick Three, Keep Many** process — spawn 3 `code-review` sub-agents in parallel: - - **Agent 1**: file ordering from `/tmp/pr-context/file_order_az.txt` (A → Z) - - **Agent 2**: file ordering from `/tmp/pr-context/file_order_za.txt` (Z → A) - - **Agent 3**: file ordering from `/tmp/pr-context/file_order_largest.txt` (largest diff first) - - **When spawning sub-agents**, each prompt must include: - - Instruction to read `/tmp/pr-context/review-instructions.md` for the review process, criteria, and calibration examples - - Instruction to read `/tmp/pr-context/README.md` for a manifest of all available context files - - The review intensity (`__GH_AW_INPUTS_INTENSITY__`) and minimum severity (`__GH_AW_INPUTS_MINIMUM_SEVERITY__`) - - The path to that sub-agent's file ordering — tell it to read the file for its ordered list (per-file diffs are at `/tmp/pr-context/diffs/.diff`) - - Instruction to read changed files from the workspace (the PR branch is checked out) - - Each sub-agent returns a structured findings list. They do NOT leave inline comments. + 1. Call `ready_to_code_review` — this writes `/tmp/pr-context/agent-review.md` (review approach) and `/tmp/pr-context/parent-review.md` (comment format and inline severity threshold). + 2. Read both files, then follow the approach in `agent-review.md`. ### Step 3: Verify and Comment @@ -539,8 +438,6 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} GH_AW_INPUTS_MODEL: ${{ inputs.model }} GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} @@ -559,8 +456,6 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7B543459: ${{ inputs.minimum_severity || 'low' }} - GH_AW_EXPR_8D9F5797: ${{ inputs.intensity || 'balanced' }} GH_AW_GITHUB_ACTOR: ${{ github.actor }} GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} @@ -586,8 +481,6 @@ jobs: file: process.env.GH_AW_PROMPT, substitutions: { GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, - GH_AW_EXPR_7B543459: process.env.GH_AW_EXPR_7B543459, - GH_AW_EXPR_8D9F5797: process.env.GH_AW_EXPR_8D9F5797, GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, @@ -651,7 +544,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -705,15 +598,20 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `parent-review.md` | Comment format and inline severity threshold for the parent agent (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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" + 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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: SETUP_COMMANDS: ${{ inputs.setup-commands }} if: ${{ inputs.setup-commands != '' }} @@ -1062,9 +960,214 @@ jobs: bash /opt/gh-aw/actions/start_safe_outputs_server.sh + - name: Setup Safe Inputs Config + run: | + mkdir -p /opt/gh-aw/safe-inputs/logs + cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' + { + "serverName": "safeinputs", + "version": "1.0.0", + "logDir": "/opt/gh-aw/safe-inputs/logs", + "tools": [ + { + "name": "ready-to-code-review", + "description": "Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/", + "inputSchema": { + "properties": {}, + "type": "object" + }, + "handler": "ready-to-code-review.py", + "timeout": 60 + } + ] + } + GH_AW_SAFE_INPUTS_TOOLS_EOF + cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' + const path = require("path"); + const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); + const configPath = path.join(__dirname, "tools.json"); + const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); + const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; + startHttpServer(configPath, { + port: port, + stateless: true, + logDir: "/opt/gh-aw/safe-inputs/logs" + }).catch(error => { + console.error("Failed to start safe-inputs HTTP server:", error); + process.exit(1); + }); + GH_AW_SAFE_INPUTS_SERVER_EOF + chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs + + - name: Setup Safe Inputs Tool Files + run: | + cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' + #!/usr/bin/env python3 + # Auto-generated safe-input tool: ready-to-code-review + # Prepare code review instructions based on PR size — writes agent-review.md, parent-review.md, and subagent-*.md to /tmp/pr-context/ + + import json + import os + import sys + + # Read inputs from stdin (JSON format) + try: + inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} + except (json.JSONDecodeError, Exception): + inputs = {} + + # User code: + import os, json, re + + os.makedirs('/tmp/pr-context', exist_ok=True) + + # Read PR size written by the pr-context step + try: + with open('/tmp/pr-context/pr-size.txt') as f: + pr_size = f.read().strip() + m = re.search(r', (\d+) diff', pr_size) + diff_lines = int(m.group(1)) if m else 0 + except Exception: + pr_size = 'unknown size' + diff_lines = 0 + + # Write one instruction file per sub-agent file ordering strategy + for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: + lines = [ + '# PR Review Sub-Agent', + '', + 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', + '', + '## Instructions', + '', + 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', + '', + '## Context', + '', + '- Repository conventions: `/tmp/agents.md` (skip if missing)', + '- PR details: `/tmp/pr-context/pr.json`', + '- All context files: `/tmp/pr-context/README.md`', + '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', + '- Full file contents: read from the workspace (PR branch is checked out)', + '', + '## Your File Order', + '', + f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', + ] + with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: + f.write('\n'.join(lines) + '\n') + + # Determine review approach based on PR size + if diff_lines < 200: + approach_lines = [ + f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', + ] + size_key = 'small' + elif diff_lines < 800: + approach_lines = [ + f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'medium' + else: + approach_lines = [ + f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', + '', + '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', + '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', + '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', + '', + 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', + ] + size_key = 'large' + + with open('/tmp/pr-context/agent-review.md', 'w') as f: + f.write('\n'.join(approach_lines) + '\n') + + # Write parent-agent comment format and threshold instructions + t3 = chr(96) * 3 + t5 = chr(96) * 5 + threshold = os.environ.get('GH_AW_INPUTS_MINIMUM_SEVERITY', 'low') or 'low' + parent_lines = [ + '# Code Review: Comment Format and Threshold', + '', + '## Comment Format', + '', + 'Call **`create_pull_request_review_comment`** with:', + '- The file path and the **exact line number from reading the file** (not estimated from the diff)', + '- The line must be within the diff (an added or context line in the patch)', + '', + t5, + '**[SEVERITY] Brief title**', + '', + 'Description of the issue and why it matters.', + '', + f'{t3}suggestion', + 'corrected code here', + t3, + t5, + '', + 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', + '', + '## Inline Comment Threshold', + '', + f'The minimum severity for inline comments is `{threshold}`.', + '', + 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', + '', + 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', + '', + 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', + ] + with open('/tmp/pr-context/parent-review.md', 'w') as f: + f.write('\n'.join(parent_lines) + '\n') + + print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) + + GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF + chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py + + - name: Generate Safe Inputs MCP Server Config + id: safe-inputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3000 + + # Set outputs for next steps + { + echo "safe_inputs_api_key=${API_KEY}" + echo "safe_inputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Inputs MCP server will run on port ${PORT}" + + - name: Start Safe Inputs MCP HTTP Server + id: safe-inputs-start + env: + DEBUG: '*' + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_INPUTS_PORT + export GH_AW_SAFE_INPUTS_API_KEY + + bash /opt/gh-aw/actions/start_safe_inputs_server.sh + - name: Start MCP Gateway id: start-mcp-gateway env: + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} @@ -1086,19 +1189,12 @@ jobs: export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1116,6 +1212,13 @@ jobs: "search_code" ] }, + "safeinputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" + } + }, "safeoutputs": { "type": "http", "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", @@ -1264,6 +1367,15 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); await main(); + - name: Parse Safe Inputs logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); + await main(); - name: Parse MCP Gateway logs for step summary if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1297,6 +1409,7 @@ jobs: path: | /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/safe-inputs/logs/ /tmp/gh-aw/sandbox/firewall/logs/ /tmp/gh-aw/agent-stdio.log /tmp/gh-aw/agent/ @@ -1434,7 +1547,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1520,7 +1633,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1560,7 +1673,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-pr-review.md b/.github/workflows/gh-aw-pr-review.md index 73ed58d7..46836d40 100644 --- a/.github/workflows/gh-aw-pr-review.md +++ b/.github/workflows/gh-aw-pr-review.md @@ -14,6 +14,7 @@ imports: - gh-aw-fragments/safe-output-review-comment.md - gh-aw-fragments/safe-output-submit-review.md - gh-aw-fragments/pick-three-keep-many.md + - gh-aw-fragments/safe-output-code-review.md - gh-aw-fragments/network-ecosystems.md engine: id: copilot @@ -114,7 +115,7 @@ Follow these steps in order. ### Step 1: Gather Context -1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. Write the result to `/tmp/pr-context/agents.md` so sub-agents can read it. If `generate_agents_md` fails, continue without it. +1. Read `/tmp/agents.md` for repository conventions (skip if missing). 2. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). 3. Read `/tmp/pr-context/issue-*.json` files if any exist to understand linked issue motivation and acceptance criteria. 4. Read `/tmp/pr-context/reviews.json` to check prior review submissions from this bot. Note any prior verdicts to avoid redundant reviews. @@ -122,27 +123,8 @@ Follow these steps in order. ### Step 2: Review -Read `/tmp/pr-context/pr-size.txt` for the PR size (file count and diff lines). Use it to decide your review approach: - -- **Small PRs (under 200 diff lines):** Review directly — no sub-agents. Read files in the order from `/tmp/pr-context/file_order_az.txt`. For each file, read the diff from `/tmp/pr-context/diffs/.diff`, read the full file from the workspace, check existing threads from `/tmp/pr-context/threads/.json`, and identify issues per the Code Review Reference criteria. Then proceed to Step 3 with your findings. - -- **Medium PRs (200–800 diff lines):** Follow the **Pick Three, Keep Many** process — spawn 2 `code-review` sub-agents in parallel: - - **Agent 1**: file ordering from `/tmp/pr-context/file_order_az.txt` (A → Z) - - **Agent 2**: file ordering from `/tmp/pr-context/file_order_za.txt` (Z → A) - -- **Large PRs (over 800 diff lines):** Follow the **Pick Three, Keep Many** process — spawn 3 `code-review` sub-agents in parallel: - - **Agent 1**: file ordering from `/tmp/pr-context/file_order_az.txt` (A → Z) - - **Agent 2**: file ordering from `/tmp/pr-context/file_order_za.txt` (Z → A) - - **Agent 3**: file ordering from `/tmp/pr-context/file_order_largest.txt` (largest diff first) - -**When spawning sub-agents**, each prompt must include: -- Instruction to read `/tmp/pr-context/review-instructions.md` for the review process, criteria, and calibration examples -- Instruction to read `/tmp/pr-context/README.md` for a manifest of all available context files -- The review intensity (`${{ inputs.intensity }}`) and minimum severity (`${{ inputs.minimum_severity }}`) -- The path to that sub-agent's file ordering — tell it to read the file for its ordered list (per-file diffs are at `/tmp/pr-context/diffs/.diff`) -- Instruction to read changed files from the workspace (the PR branch is checked out) - -Each sub-agent returns a structured findings list. They do NOT leave inline comments. +1. Call `ready_to_code_review` — this writes `/tmp/pr-context/agent-review.md` (review approach) and `/tmp/pr-context/parent-review.md` (comment format and inline severity threshold). +2. Read both files, then follow the approach in `agent-review.md`. ### Step 3: Verify and Comment diff --git a/.github/workflows/gh-aw-product-manager-impersonator.lock.yml b/.github/workflows/gh-aw-product-manager-impersonator.lock.yml index 7f3b4de3..520711a6 100644 --- a/.github/workflows/gh-aw-product-manager-impersonator.lock.yml +++ b/.github/workflows/gh-aw-product-manager-impersonator.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"22dd40796a5fa4e2399d8af08bec589e9299f90d6699f4894154cc4770ac7daa"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fa1bc9c00ac83b5c2c48cb7a81363619f77be76e0dbb9caebf68b008484bf9c6"} name: "Product Manager Impersonator" "on": @@ -118,7 +118,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -233,11 +233,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -356,7 +355,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -625,7 +624,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -679,7 +678,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -1018,13 +1022,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1360,7 +1357,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1446,7 +1443,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1488,7 +1485,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-project-summary.lock.yml b/.github/workflows/gh-aw-project-summary.lock.yml index 312cef35..6a59d374 100644 --- a/.github/workflows/gh-aw-project-summary.lock.yml +++ b/.github/workflows/gh-aw-project-summary.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"623fcfef6432451f8b4f60a5be6617296247e24b4b7ccaad61033e001541be9b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"efe7a4e2abba6d9cb80391f820e08d754ee29a35cb2da17c9ab0f854a0eae689"} name: "Project Summary" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -220,11 +220,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -324,7 +323,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -538,7 +537,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -592,7 +591,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -937,13 +941,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1279,7 +1276,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1365,7 +1362,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1407,7 +1404,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-refactor-opportunist.lock.yml b/.github/workflows/gh-aw-refactor-opportunist.lock.yml index c8e6566e..d540a284 100644 --- a/.github/workflows/gh-aw-refactor-opportunist.lock.yml +++ b/.github/workflows/gh-aw-refactor-opportunist.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c41c1c182fe2a9bdf64e734fd9add9cb55f916666262ef89d491d31a17c288c2"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d05ede233d0733440b7fd235b571f44316edd47bd3bbdf1afc8a7cea8070468b"} name: "Refactor Opportunist" "on": @@ -109,7 +109,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -347,7 +346,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -593,7 +592,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -647,7 +646,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -992,13 +996,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1334,7 +1331,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1420,7 +1417,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1462,7 +1459,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-release-update.lock.yml b/.github/workflows/gh-aw-release-update.lock.yml index b0a73442..b40f304f 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":"bfa86aa31b4e6f0a870f7c18e438b0134675b3efddc5d2213a4e78e3e4e037c6"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"da960eff5026ab291f24911be0e6d7703316d22d651f3e329c55a73bef79157e"} name: "Release Update Check" "on": @@ -107,7 +107,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -326,10 +325,9 @@ jobs: ## Step 1: Gather context - 1. Call `generate_agents_md` to get repository conventions (if it fails, continue). - 2. Use `github-get_latest_release` for `elastic/ai-github-actions` to obtain the latest tag and release notes. If no release exists, call `noop` and stop. - 3. Resolve the tag to a commit SHA using `github-get_tag`. - 4. Find pinned workflow references in this repository: + 1. Use `github-get_latest_release` for `elastic/ai-github-actions` to obtain the latest tag and release notes. If no release exists, call `noop` and stop. + 2. Resolve the tag to a commit SHA using `github-get_tag`. + 3. Find pinned workflow references in this repository: ````text rg -n "elastic/ai-github-actions/.github/workflows/gh-aw-.*\\.lock\\.yml@\\S+" . @@ -457,7 +455,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -511,7 +509,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -595,6 +598,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -970,6 +977,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1061,13 +1073,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1422,7 +1427,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1524,7 +1529,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1569,7 +1574,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-release-update.md b/.github/workflows/gh-aw-release-update.md index 47a363fb..9953d968 100644 --- a/.github/workflows/gh-aw-release-update.md +++ b/.github/workflows/gh-aw-release-update.md @@ -99,10 +99,9 @@ Check for new releases of `elastic/ai-github-actions` and open a PR that updates ## Step 1: Gather context -1. Call `generate_agents_md` to get repository conventions (if it fails, continue). -2. Use `github-get_latest_release` for `elastic/ai-github-actions` to obtain the latest tag and release notes. If no release exists, call `noop` and stop. -3. Resolve the tag to a commit SHA using `github-get_tag`. -4. Find pinned workflow references in this repository: +1. Use `github-get_latest_release` for `elastic/ai-github-actions` to obtain the latest tag and release notes. If no release exists, call `noop` and stop. +2. Resolve the tag to a commit SHA using `github-get_tag`. +3. Find pinned workflow references in this repository: ````text rg -n "elastic/ai-github-actions/.github/workflows/gh-aw-.*\\.lock\\.yml@\\S+" . diff --git a/.github/workflows/gh-aw-scheduled-audit.lock.yml b/.github/workflows/gh-aw-scheduled-audit.lock.yml index 0be3c73b..6e91e091 100644 --- a/.github/workflows/gh-aw-scheduled-audit.lock.yml +++ b/.github/workflows/gh-aw-scheduled-audit.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"267cb6099b46065e14507f4584d0eb8c1ee3915d803745c2db3646b1e089c595"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"1c0805ff3e28a94d4a4f5aff556e6fe333828faa7d7cfaa5753f02e64afe487e"} name: "Scheduled Audit" "on": @@ -118,7 +118,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -243,11 +243,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -315,48 +314,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Playwright MCP Tools - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. - - ### Available tools - - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console - - ### Why MCP tools instead of scripts - - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. - - ### Measuring DOM properties - - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: - - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` - - ### Handling failures - - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. + Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## create-issue Limitations @@ -381,7 +339,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -554,21 +512,21 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - if: hashFiles('.python-version') != '' name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 @@ -608,8 +566,15 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash + - name: Write Playwright instructions to disk + run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: GH_TOKEN: ${{ github.token }} TITLE_PREFIX: ${{ inputs.title-prefix }} @@ -949,13 +914,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1298,7 +1256,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1384,7 +1342,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1426,7 +1384,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-scheduled-fix.lock.yml b/.github/workflows/gh-aw-scheduled-fix.lock.yml index 454d1efc..9a8ac126 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":"bffc9dc9b2e0ab4ba3fd0e07a75e52995fa1af1a854e304d7bbee5eef75678dd"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"84de09b94f13811dc14b4c0d6600131050741c32ff0acd449d420fbaadc20eb5"} name: "Scheduled Fix" "on": @@ -116,7 +116,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -232,11 +232,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -332,7 +331,7 @@ jobs: ### Step 1: Gather Candidates - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the candidate gathering instructions in the **Fix Assignment** section. 3. For each candidate, read the full issue and comments using `issue_read` (methods `get` and `get_comments`). @@ -507,7 +506,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -561,7 +560,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -650,6 +654,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1025,6 +1033,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1116,13 +1129,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1477,7 +1483,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1579,7 +1585,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1624,7 +1630,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-small-problem-fixer.lock.yml b/.github/workflows/gh-aw-small-problem-fixer.lock.yml index 405641dc..8a680591 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":"142bcd3b6f889b8b153aa8a9d44df18005d036e716d2afb0cf217f3fa08d9504"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0faf631c48eba53bee2732c3de2340ff74d526c033f57d7bb42cbea85386bc6e"} name: "Small Problem Fixer" "on": @@ -114,7 +114,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -228,11 +228,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -347,8 +346,7 @@ jobs: ## Step 1: Gather candidates - 1. Call `generate_agents_md` to get repository conventions (if it fails, continue). - 2. Search for small, low-effort issues: + 1. Search for small, low-effort issues: ````text github-search_issues: query="repo:__GH_AW_GITHUB_REPOSITORY__ is:issue is:open -label:bug-hunter -\"[bug-hunter]\" (label:\"good first issue\" OR label:small OR label:\"quick fix\" OR label:\"easy\") sort:updated-asc" @@ -507,7 +505,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -561,7 +559,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -666,6 +669,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1059,6 +1066,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1150,13 +1162,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1511,7 +1516,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1613,7 +1618,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1660,7 +1665,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-small-problem-fixer.md b/.github/workflows/gh-aw-small-problem-fixer.md index ec3cc199..24ba5ca6 100644 --- a/.github/workflows/gh-aw-small-problem-fixer.md +++ b/.github/workflows/gh-aw-small-problem-fixer.md @@ -102,8 +102,7 @@ Find a small, clearly-scoped issue (or a very small set of related issues) and o ## Step 1: Gather candidates -1. Call `generate_agents_md` to get repository conventions (if it fails, continue). -2. Search for small, low-effort issues: +1. Search for small, low-effort issues: ````text github-search_issues: query="repo:${{ github.repository }} is:issue is:open -label:bug-hunter -\"[bug-hunter]\" (label:\"good first issue\" OR label:small OR label:\"quick fix\" OR label:\"easy\") sort:updated-asc" diff --git a/.github/workflows/gh-aw-stale-issues-investigator.lock.yml b/.github/workflows/gh-aw-stale-issues-investigator.lock.yml index 431e47b7..ac0893b1 100644 --- a/.github/workflows/gh-aw-stale-issues-investigator.lock.yml +++ b/.github/workflows/gh-aw-stale-issues-investigator.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"21c938f9c93eee458bba0d53d652fe8760583ea3173bafb40820d880fe517b1e"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4dfdab018e3682f8a70825c400c5287b9fe224307894fbc9c77ce2a807f04229"} name: "Stale Issues Investigator" "on": @@ -112,7 +112,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -224,11 +224,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -325,7 +324,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -593,7 +592,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -647,7 +646,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -1030,13 +1034,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1373,7 +1370,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1459,7 +1456,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1502,7 +1499,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-stale-issues-remediator.lock.yml b/.github/workflows/gh-aw-stale-issues-remediator.lock.yml index 8b4b38aa..44acef64 100644 --- a/.github/workflows/gh-aw-stale-issues-remediator.lock.yml +++ b/.github/workflows/gh-aw-stale-issues-remediator.lock.yml @@ -35,7 +35,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"80e212d5cd367559753ed7b71359aa48527f897f08e42480692f6eed3ecb7ce3"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fe3b84f78a29475f4c33b4df2805596272c34774f5b48ba0e0f1448586401789"} name: "Stale Issues Remediator" "on": @@ -103,7 +103,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -215,11 +215,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -417,7 +416,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -471,7 +470,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -821,13 +825,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1164,7 +1161,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1250,7 +1247,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1291,7 +1288,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-stale-issues.lock.yml b/.github/workflows/gh-aw-stale-issues.lock.yml index ede2c22b..9a8cc5bb 100644 --- a/.github/workflows/gh-aw-stale-issues.lock.yml +++ b/.github/workflows/gh-aw-stale-issues.lock.yml @@ -43,7 +43,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"21c938f9c93eee458bba0d53d652fe8760583ea3173bafb40820d880fe517b1e"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4dfdab018e3682f8a70825c400c5287b9fe224307894fbc9c77ce2a807f04229"} name: "Stale Issues Investigator" "on": @@ -117,7 +117,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -229,11 +229,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -330,7 +329,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -598,7 +597,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -652,7 +651,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -1035,13 +1039,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1378,7 +1375,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1464,7 +1461,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1507,7 +1504,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-test-coverage-detector.lock.yml b/.github/workflows/gh-aw-test-coverage-detector.lock.yml index c0430844..8b0c4cfb 100644 --- a/.github/workflows/gh-aw-test-coverage-detector.lock.yml +++ b/.github/workflows/gh-aw-test-coverage-detector.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"08ad3400fb43110249656035b12673f63a74f2f3ee2a7a52d11579a12a47af78"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"0cc4dafccea92c3906464d10ef9a824e1e280d3e6778fdc7a4b082710955139e"} name: "Test Coverage Detector" "on": @@ -108,7 +108,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -344,7 +343,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -571,7 +570,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -625,7 +624,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -964,13 +968,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1306,7 +1303,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1392,7 +1389,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1434,7 +1431,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-test-improvement.lock.yml b/.github/workflows/gh-aw-test-improvement.lock.yml index e61678c3..dc60ba88 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":"246e3e183218a73daca6a54d125c0a4b4857c25ec725e5d9cc7a57072f24cd73"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c9e1d6fbb89c5766fc5e6f71903015889a056fdf7800fa0628305c5debea88e8"} name: "Test Improver" "on": @@ -112,7 +112,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -226,11 +226,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -332,8 +331,7 @@ jobs: ## Step 1: Gather context - 1. Call `generate_agents_md` to get repository conventions (if it fails, continue). - 2. Determine required repo commands (lint/build/test) and how to run tests: + 1. Determine required repo commands (lint/build/test) and how to run tests: - Check README, CONTRIBUTING, DEVELOPING, Makefile, CI config, package.json, pyproject.toml, and similar files. 3. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). - If coverage is available and reasonably fast, run it to find low-coverage files. @@ -367,7 +365,7 @@ jobs: 1. Run each new or modified test **at least 5 times** in sequence and confirm every run passes. - Use the test framework's built-in repeat/count flag when available (e.g., `go test -count=5`, `pytest -x --count 5` with `pytest-repeat`, `--repeat 5` in Jest/Vitest). - If no built-in mechanism exists, use a simple shell loop: `for i in $(seq 1 5); do || exit 1; done` - 2. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: + 1. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: - Reliance on timing, sleep, or wall-clock assertions - Shared mutable state between test cases - Non-deterministic iteration order (e.g., map/set ordering) @@ -500,7 +498,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -554,7 +552,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -638,6 +641,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1013,6 +1020,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1104,13 +1116,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1465,7 +1470,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1567,7 +1572,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1612,7 +1617,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-test-improver.lock.yml b/.github/workflows/gh-aw-test-improver.lock.yml index fa4b3bc2..6f3823d5 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":"246e3e183218a73daca6a54d125c0a4b4857c25ec725e5d9cc7a57072f24cd73"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c9e1d6fbb89c5766fc5e6f71903015889a056fdf7800fa0628305c5debea88e8"} name: "Test Improver" "on": @@ -107,7 +107,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -221,11 +221,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -327,8 +326,7 @@ jobs: ## Step 1: Gather context - 1. Call `generate_agents_md` to get repository conventions (if it fails, continue). - 2. Determine required repo commands (lint/build/test) and how to run tests: + 1. Determine required repo commands (lint/build/test) and how to run tests: - Check README, CONTRIBUTING, DEVELOPING, Makefile, CI config, package.json, pyproject.toml, and similar files. 3. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). - If coverage is available and reasonably fast, run it to find low-coverage files. @@ -362,7 +360,7 @@ jobs: 1. Run each new or modified test **at least 5 times** in sequence and confirm every run passes. - Use the test framework's built-in repeat/count flag when available (e.g., `go test -count=5`, `pytest -x --count 5` with `pytest-repeat`, `--repeat 5` in Jest/Vitest). - If no built-in mechanism exists, use a simple shell loop: `for i in $(seq 1 5); do || exit 1; done` - 2. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: + 1. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: - Reliance on timing, sleep, or wall-clock assertions - Shared mutable state between test cases - Non-deterministic iteration order (e.g., map/set ordering) @@ -495,7 +493,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -549,7 +547,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -633,6 +636,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1008,6 +1015,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1099,13 +1111,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1460,7 +1465,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1562,7 +1567,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1607,7 +1612,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-test-improver.md b/.github/workflows/gh-aw-test-improver.md index b03f8127..4c287460 100644 --- a/.github/workflows/gh-aw-test-improver.md +++ b/.github/workflows/gh-aw-test-improver.md @@ -101,8 +101,7 @@ Identify under-tested code paths, add focused tests, and remove or consolidate d ## Step 1: Gather context -1. Call `generate_agents_md` to get repository conventions (if it fails, continue). -2. Determine required repo commands (lint/build/test) and how to run tests: +1. Determine required repo commands (lint/build/test) and how to run tests: - Check README, CONTRIBUTING, DEVELOPING, Makefile, CI config, package.json, pyproject.toml, and similar files. 3. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). - If coverage is available and reasonably fast, run it to find low-coverage files. @@ -136,7 +135,7 @@ New tests that pass once may still be flaky. Before filing a PR, verify stabilit 1. Run each new or modified test **at least 5 times** in sequence and confirm every run passes. - Use the test framework's built-in repeat/count flag when available (e.g., `go test -count=5`, `pytest -x --count 5` with `pytest-repeat`, `--repeat 5` in Jest/Vitest). - If no built-in mechanism exists, use a simple shell loop: `for i in $(seq 1 5); do || exit 1; done` -2. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: +1. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: - Reliance on timing, sleep, or wall-clock assertions - Shared mutable state between test cases - Non-deterministic iteration order (e.g., map/set ordering) diff --git a/.github/workflows/gh-aw-text-auditor.lock.yml b/.github/workflows/gh-aw-text-auditor.lock.yml index 2293f32f..c5d03e0a 100644 --- a/.github/workflows/gh-aw-text-auditor.lock.yml +++ b/.github/workflows/gh-aw-text-auditor.lock.yml @@ -39,7 +39,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"f378866177996321173bd1b5526ed95e6db706f680f7aed0bc79d4bce50fa6f0"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"8571987087d2c0f22ff6dccad99ccc8cba1e20d4374e29aae867bd92b0d1e4b1"} name: "Text Auditor" "on": @@ -133,7 +133,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -251,11 +251,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -342,19 +341,19 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ### Pick Three, Keep Many - Parallelize your work using sub-agents. Spawn 3 sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. + Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `generate_agents_md`) + - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - The quality criteria and output format you expect - The specific angle that distinguishes this sub-agent from the others Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - **Wait for all 3 sub-agents to complete.** Do not proceed until every sub-agent has returned its result. + **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. **Merge and deduplicate findings** across all sub-agents: 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. @@ -377,7 +376,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -662,7 +661,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -716,7 +715,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -1055,13 +1059,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1397,7 +1394,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1483,7 +1480,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1525,7 +1522,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-text-beautifier.lock.yml b/.github/workflows/gh-aw-text-beautifier.lock.yml index c4eb749c..9dbf52ad 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":"9d3dc1b0069393eae652e0eeb1af9a89f52ba1d1e74c7af019b7e5ee5f7d4964"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"bd2767826c2c626ee4257f59610c6d3910c5a670043147d6a86a7fadd32febef"} name: "Text Beautifier" "on": @@ -109,7 +109,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -223,11 +223,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -330,7 +329,7 @@ jobs: ### Step 1: Gather Candidates - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the candidate gathering instructions in the **Fix Assignment** section. 3. For each candidate, read the full issue and comments using `issue_read` (methods `get` and `get_comments`). @@ -497,7 +496,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -551,7 +550,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -635,6 +639,10 @@ jobs: }, "type": "array" }, + "repo": { + "description": "Target repository in 'owner/repo' format. Required when changes are in a subdirectory checkout (e.g., 'repos/repo-a/'). Must be in the allowed-repos list. If omitted, uses the repository at the workspace root.", + "type": "string" + }, "title": { "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", "type": "string" @@ -1010,6 +1018,11 @@ jobs: step = 5 if agents_md: manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') + step += 1 + review_instructions = '/tmp/pr-context/review-instructions.md' + if os.path.isfile(review_instructions): + manifest_lines.append(f'{step}. Read `{review_instructions}` for full review criteria, severity levels, false positive guidance, and calibration examples.') + step += 1 manifest_lines += [ '', '## Focus areas', @@ -1101,13 +1114,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1462,7 +1468,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1564,7 +1570,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1609,7 +1615,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-update-pr-body.lock.yml b/.github/workflows/gh-aw-update-pr-body.lock.yml index 6bc2ed30..33bc7bd3 100644 --- a/.github/workflows/gh-aw-update-pr-body.lock.yml +++ b/.github/workflows/gh-aw-update-pr-body.lock.yml @@ -35,7 +35,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"4ee20384906d76d93bb18f7718f2236ca7a42b567a3a4afc1d73a5667107e895"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"b9d429c2940d6e979d92754f8818b55cebbc5d23d7bc6f96d12f291d24ff1a2a"} name: "Update PR Body" "on": @@ -113,7 +113,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -231,11 +231,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -342,10 +341,9 @@ jobs: ### Step 1: Gather Context - 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 — current body, commits, and file list. - 3. Call `pull_request_read` with method `get_files` to get the list of changed files. - 4. If the PR description references issues (e.g., "Fixes #123", "Closes #456"), call `issue_read` with method `get` on each linked issue to understand the original motivation. + 1. Call `pull_request_read` with method `get` on PR #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ to get the full PR details — current body, commits, and file list. + 2. Call `pull_request_read` with method `get_files` to get the list of changed files. + 3. If the PR description references issues (e.g., "Fixes #123", "Closes #456"), call `issue_read` with method `get` on each linked issue to understand the original motivation. ### Step 2: Analyze the Diff @@ -538,7 +536,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -592,7 +590,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -924,13 +927,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1266,7 +1262,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1352,7 +1348,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1392,7 +1388,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/gh-aw-update-pr-body.md b/.github/workflows/gh-aw-update-pr-body.md index c1a38d28..6aedaab9 100644 --- a/.github/workflows/gh-aw-update-pr-body.md +++ b/.github/workflows/gh-aw-update-pr-body.md @@ -123,10 +123,9 @@ Evaluate the PR body against all configured edit dimensions. Apply changes only ### Step 1: Gather Context -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 — current body, commits, and file list. -3. Call `pull_request_read` with method `get_files` to get the list of changed files. -4. If the PR description references issues (e.g., "Fixes #123", "Closes #456"), call `issue_read` with method `get` on each linked issue to understand the original motivation. +1. Call `pull_request_read` with method `get` on PR #${{ github.event.pull_request.number }} to get the full PR details — current body, commits, and file list. +2. Call `pull_request_read` with method `get_files` to get the list of changed files. +3. If the PR description references issues (e.g., "Fixes #123", "Closes #456"), call `issue_read` with method `get` on each linked issue to understand the original motivation. ### Step 2: Analyze the Diff diff --git a/.github/workflows/gh-aw-ux-design-patrol.lock.yml b/.github/workflows/gh-aw-ux-design-patrol.lock.yml index 238bc11c..cdc63a34 100644 --- a/.github/workflows/gh-aw-ux-design-patrol.lock.yml +++ b/.github/workflows/gh-aw-ux-design-patrol.lock.yml @@ -40,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"7afc26a3514c31f594b0a201efe113c5c2939c7e2957afac6263d9fc7a463c6b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"852a85f73311c7dd80a68119a56a4d7f479bd038953d4145804bae8a53e49402"} name: "UX Design Patrol" "on": @@ -114,7 +114,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -227,11 +227,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' @@ -353,7 +352,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -592,7 +591,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -646,7 +645,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GITHUB_TOKEN: ${{ github.token }} @@ -991,13 +995,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1333,7 +1330,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1419,7 +1416,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow @@ -1461,7 +1458,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/upgrade-check.lock.yml b/.github/workflows/upgrade-check.lock.yml index 3e442384..e870d31e 100644 --- a/.github/workflows/upgrade-check.lock.yml +++ b/.github/workflows/upgrade-check.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e7abde23ac2d7f176c077eb0688dd946ae383deed8fa2b86195dec6c8d5d9660"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"c61f29b17d22e90d25fa9dd2fd61ead5cc5cfff58a171802757c4e4eca63a19b"} name: "Internal: Upgrade Check" "on": @@ -78,7 +78,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -189,11 +189,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -276,7 +275,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -528,7 +527,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -582,7 +581,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -916,13 +920,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1258,7 +1255,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1360,7 +1357,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/.github/workflows/workflow-patrol.lock.yml b/.github/workflows/workflow-patrol.lock.yml index 2fd5a829..9bc44e61 100644 --- a/.github/workflows/workflow-patrol.lock.yml +++ b/.github/workflows/workflow-patrol.lock.yml @@ -38,7 +38,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"7c9bbc9ce50d57a9d759c1a596394b6e75f0d0c906a2fda7914c4c7753c8d858"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e49e29d4ccef1a330ef1e458c9a3cdd165ee3e87c1f5a5611f56c318f658b934"} name: "Internal: Workflow Patrol" "on": @@ -78,7 +78,7 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -189,11 +189,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -276,7 +275,7 @@ jobs: ### Step 1: Gather Context - 1. Call `generate_agents_md` to get the repository's coding guidelines and conventions. If this fails, continue without it. + 1. Read `/tmp/agents.md` for the repository's coding guidelines and conventions (skip if missing). 2. Follow the data gathering instructions in the **Report Assignment** section. ### Step 2: Analyze @@ -518,7 +517,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -572,7 +571,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: GH_TOKEN: ${{ github.token }} @@ -906,13 +910,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp", - "tools": [ - "generate_agents_md" - ] - }, "github": { "type": "stdio", "container": "ghcr.io/github/github-mcp-server:v0.31.0", @@ -1248,7 +1245,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1350,7 +1347,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact diff --git a/Makefile b/Makefile index 413b0f06..d7418f15 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Tool versions ACTIONLINT_VERSION := 1.7.10 ACTION_VALIDATOR_VERSION := 0.8.0 -GH_AW_VERSION := v0.51.3 +GH_AW_VERSION := v0.51.5 GH_AW_COMPAT_VERSION := v0.49.4 # Workflows that must be compiled with the compat compiler From e8ab21312047953ff2a1a017abacb7edff122a1e Mon Sep 17 00:00:00 2001 From: William Easton Date: Mon, 2 Mar 2026 01:22:16 -0600 Subject: [PATCH 2/3] Remove stale .invalid.yml artifacts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../workflows/gh-aw-deep-research.lock.yml | 41 +- ...aw-internal-gemini-cli-web-search.lock.yml | 41 +- .../gh-aw-internal-gemini-cli.lock.yml | 98 +- .../gh-aw-mention-in-pr-by-id.invalid.yml | 2127 ---------------- ...gh-aw-mention-in-pr-no-sandbox.invalid.yml | 2097 --------------- .../workflows/gh-aw-mention-in-pr.invalid.yml | 2256 ----------------- .github/workflows/gh-aw-pr-review.invalid.yml | 1713 ------------- 7 files changed, 69 insertions(+), 8304 deletions(-) delete mode 100644 .github/workflows/gh-aw-mention-in-pr-by-id.invalid.yml delete mode 100644 .github/workflows/gh-aw-mention-in-pr-no-sandbox.invalid.yml delete mode 100644 .github/workflows/gh-aw-mention-in-pr.invalid.yml delete mode 100644 .github/workflows/gh-aw-pr-review.invalid.yml diff --git a/.github/workflows/gh-aw-deep-research.lock.yml b/.github/workflows/gh-aw-deep-research.lock.yml index 5d6f4125..adcb8f4c 100644 --- a/.github/workflows/gh-aw-deep-research.lock.yml +++ b/.github/workflows/gh-aw-deep-research.lock.yml @@ -41,7 +41,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"7d8e7f95c477dd40b717d7094ed6af86318e3e8d836274f1c0c3bba09699004f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d68406a3029cece5bdbaa1f2e67b442ddf727e137a97fa42f3f994e607aebf12"} name: "Internal Gemini CLI Web Search" "on": @@ -122,7 +122,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -137,7 +137,7 @@ jobs: GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","cloud.elastic.co","ela.st","elastic.co","public-code-search.fastmcp.app","www.elastic.co"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["artifacts.elastic.co","cloud.elastic.co","ela.st","elastic.co","public-code-search.fastmcp.app","www.elastic.co"]' GH_AW_INFO_FIREWALL_ENABLED: "false" GH_AW_INFO_AWF_VERSION: "" GH_AW_INFO_AWMG_VERSION: "" @@ -246,11 +246,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -359,10 +358,9 @@ jobs: ### Step 1: Gather Context and Plan - 1. Call `generate_agents_md` to get repository conventions. - 2. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your research anchor for all subsequent steps. - 3. Before searching, decompose the question into sub-questions. For complex or multi-faceted requests, list 2–5 specific sub-questions that, if answered, would fully address the original request. This prevents unfocused searching and ensures complete coverage. - 4. Use `search_code` and local file reads only when codebase knowledge is needed to answer the question or prepare an implementation. + 1. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your research anchor for all subsequent steps. + 2. Before searching, decompose the question into sub-questions. For complex or multi-faceted requests, list 2–5 specific sub-questions that, if answered, would fully address the original request. This prevents unfocused searching and ensures complete coverage. + 3. Use `search_code` and local file reads only when codebase knowledge is needed to answer the question or prepare an implementation. ### Step 2: Research Iteratively @@ -501,7 +499,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -555,7 +553,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -932,10 +935,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp" - }, "github": { "container": "ghcr.io/github/github-mcp-server:v0.31.0", "env": { @@ -991,7 +990,7 @@ jobs: run: | set -o pipefail # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.googleapis.com,agents-md-generator.fastmcp.app,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.googleapis.com,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && gemini --yolo --output-format stream-json --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: DEBUG: gemini-cli:* @@ -1051,7 +1050,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,agents-md-generator.fastmcp.app,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" + GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1241,7 +1240,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1331,7 +1330,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1388,7 +1387,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1407,7 +1406,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,agents-md-generator.fastmcp.app,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" + GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_issue\":{\"close_older_issues\":true,\"expires\":168,\"max\":1,\"title_prefix\":\"${{ inputs.title-prefix }} \"},\"missing_data\":{},\"missing_tool\":{}}" diff --git a/.github/workflows/gh-aw-internal-gemini-cli-web-search.lock.yml b/.github/workflows/gh-aw-internal-gemini-cli-web-search.lock.yml index 77926721..aa405a9b 100644 --- a/.github/workflows/gh-aw-internal-gemini-cli-web-search.lock.yml +++ b/.github/workflows/gh-aw-internal-gemini-cli-web-search.lock.yml @@ -36,7 +36,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"7d8e7f95c477dd40b717d7094ed6af86318e3e8d836274f1c0c3bba09699004f"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"d68406a3029cece5bdbaa1f2e67b442ddf727e137a97fa42f3f994e607aebf12"} name: "Internal Gemini CLI Web Search" "on": @@ -117,7 +117,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -132,7 +132,7 @@ jobs: GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","cloud.elastic.co","ela.st","elastic.co","public-code-search.fastmcp.app","www.elastic.co"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["artifacts.elastic.co","cloud.elastic.co","ela.st","elastic.co","public-code-search.fastmcp.app","www.elastic.co"]' GH_AW_INFO_FIREWALL_ENABLED: "false" GH_AW_INFO_AWF_VERSION: "" GH_AW_INFO_AWMG_VERSION: "" @@ -241,11 +241,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -354,10 +353,9 @@ jobs: ### Step 1: Gather Context and Plan - 1. Call `generate_agents_md` to get repository conventions. - 2. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your research anchor for all subsequent steps. - 3. Before searching, decompose the question into sub-questions. For complex or multi-faceted requests, list 2–5 specific sub-questions that, if answered, would fully address the original request. This prevents unfocused searching and ensures complete coverage. - 4. Use `search_code` and local file reads only when codebase knowledge is needed to answer the question or prepare an implementation. + 1. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your research anchor for all subsequent steps. + 2. Before searching, decompose the question into sub-questions. For complex or multi-faceted requests, list 2–5 specific sub-questions that, if answered, would fully address the original request. This prevents unfocused searching and ensures complete coverage. + 3. Use `search_code` and local file reads only when codebase knowledge is needed to answer the question or prepare an implementation. ### Step 2: Research Iteratively @@ -496,7 +494,7 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -550,7 +548,12 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" + shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} @@ -927,10 +930,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp" - }, "github": { "container": "ghcr.io/github/github-mcp-server:v0.31.0", "env": { @@ -986,7 +985,7 @@ jobs: run: | set -o pipefail # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.googleapis.com,agents-md-generator.fastmcp.app,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.googleapis.com,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && gemini --yolo --output-format stream-json --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: DEBUG: gemini-cli:* @@ -1046,7 +1045,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,agents-md-generator.fastmcp.app,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" + GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1236,7 +1235,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1326,7 +1325,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1383,7 +1382,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1402,7 +1401,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,agents-md-generator.fastmcp.app,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" + GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,artifacts.elastic.co,cloud.elastic.co,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,www.elastic.co" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_issue\":{\"close_older_issues\":true,\"expires\":168,\"max\":1,\"title_prefix\":\"${{ inputs.title-prefix }} \"},\"missing_data\":{},\"missing_tool\":{}}" diff --git a/.github/workflows/gh-aw-internal-gemini-cli.lock.yml b/.github/workflows/gh-aw-internal-gemini-cli.lock.yml index 03e5435c..85c1ba09 100644 --- a/.github/workflows/gh-aw-internal-gemini-cli.lock.yml +++ b/.github/workflows/gh-aw-internal-gemini-cli.lock.yml @@ -37,7 +37,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"e3a4192a9c6da16c5c5777b1be870cf32d8ccad40082f180ee0cca4dced26f40"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"250a7187b37dcfcbd3645661157491f402ed7b02c43d4557d38e0b98e38db818"} name: "Internal Gemini CLI" "on": @@ -118,7 +118,7 @@ jobs: title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Generate agentic run info @@ -133,7 +133,7 @@ jobs: GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","public-code-search.fastmcp.app","www.elastic.co"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["artifacts.elastic.co","cloud.elastic.co","defaults","ela.st","elastic.co","public-code-search.fastmcp.app","www.elastic.co"]' GH_AW_INFO_FIREWALL_ENABLED: "false" GH_AW_INFO_AWF_VERSION: "" GH_AW_INFO_AWMG_VERSION: "" @@ -243,11 +243,10 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## MCP Servers - - **`generate_agents_md`** — generates a summary of the repository's structure, conventions, and coding guidelines from AGENTS.md and related files. Call at the start of every workflow to get repo context. - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' - + Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Formatting Guidelines @@ -315,48 +314,7 @@ jobs: cat << 'GH_AW_PROMPT_EOF' ## Playwright MCP Tools - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. - - ### Available tools - - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console - - ### Why MCP tools instead of scripts - - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. - - ### Measuring DOM properties - - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: - - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` - - ### Handling failures - - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. + Playwright MCP tools are available for interactive browser automation. Full instructions are in `/tmp/playwright-instructions.md` — read it before using any Playwright tools. GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## add-comment Limitations @@ -402,10 +360,9 @@ jobs: ### Step 1: Gather Context and Plan - 1. Call `generate_agents_md` to get repository conventions. - 2. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your investigation anchor for all subsequent steps. - 3. Before investigating, decompose the question into sub-questions. For complex or multi-faceted requests, list 2-5 specific sub-questions that, if answered, would fully address the original request. - 4. Use `search_code` and local file reads to understand relevant code structure. + 1. Read the full issue thread and any referenced issues. Identify the specific question or goal — this is your investigation anchor for all subsequent steps. + 2. Before investigating, decompose the question into sub-questions. For complex or multi-faceted requests, list 2-5 specific sub-questions that, if answered, would fully address the original request. + 3. Use `search_code` and local file reads to understand relevant code structure. ### Step 2: Investigate @@ -542,21 +499,21 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> "$GITHUB_ENV" - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - if: hashFiles('.python-version') != '' name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 @@ -596,8 +553,15 @@ jobs: echo "$install_dir" >> "$GITHUB_PATH" shell: bash - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi" + run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" shell: bash + - env: + GITHUB_REPOSITORY: ${{ github.repository }} + name: Fetch repository conventions + run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" + shell: bash + - name: Write Playwright instructions to disk + run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: SETUP_COMMANDS: ${{ inputs.setup-commands }} if: ${{ inputs.setup-commands != '' }} @@ -974,10 +938,6 @@ jobs: cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh { "mcpServers": { - "agents-md-generator": { - "type": "http", - "url": "https://agents-md-generator.fastmcp.app/mcp" - }, "github": { "container": "ghcr.io/github/github-mcp-server:v0.31.0", "env": { @@ -1047,7 +1007,7 @@ jobs: run: | set -o pipefail # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.googleapis.com,agents-md-generator.fastmcp.app,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cdn.playwright.dev,cloud.elastic.co,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,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.googleapis.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cdn.playwright.dev,cloud.elastic.co,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,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && gemini --yolo --output-format stream-json --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: DEBUG: gemini-cli:* @@ -1107,7 +1067,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,agents-md-generator.fastmcp.app,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cdn.playwright.dev,cloud.elastic.co,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,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" + GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cdn.playwright.dev,cloud.elastic.co,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,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1297,7 +1257,7 @@ jobs: total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1387,7 +1347,7 @@ jobs: matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1444,7 +1404,7 @@ jobs: process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@5fa65dd15a71cec00f2d14c664467154d343d875 # v0.51.3 + uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1463,7 +1423,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,agents-md-generator.fastmcp.app,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cdn.playwright.dev,cloud.elastic.co,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,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" + GH_AW_ALLOWED_DOMAINS: "*.googleapis.com,api.snapcraft.io,archive.ubuntu.com,artifacts.elastic.co,azure.archive.ubuntu.com,cdn.playwright.dev,cloud.elastic.co,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,ela.st,elastic.co,generativelanguage.googleapis.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,public-code-search.fastmcp.app,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.elastic.co" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_issue\":{\"close_older_issues\":true,\"expires\":168,\"max\":1,\"title_prefix\":\"${{ inputs.title-prefix }} \"},\"missing_data\":{},\"missing_tool\":{}}" diff --git a/.github/workflows/gh-aw-mention-in-pr-by-id.invalid.yml b/.github/workflows/gh-aw-mention-in-pr-by-id.invalid.yml deleted file mode 100644 index f103c3b0..00000000 --- a/.github/workflows/gh-aw-mention-in-pr-by-id.invalid.yml +++ /dev/null @@ -1,2127 +0,0 @@ -# -# ___ _ _ -# / _ \ | | (_) -# | |_| | __ _ ___ _ __ | |_ _ ___ -# | _ |/ _` |/ _ \ '_ \| __| |/ __| -# | | | | (_| | __/ | | | |_| | (__ -# \_| |_/\__, |\___|_| |_|\__|_|\___| -# __/ | -# _ _ |___/ -# | | | | / _| | -# | | | | ___ _ __ _ __| |_| | _____ ____ -# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| -# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ -# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ -# -# This file was automatically generated by gh-aw. DO NOT EDIT. -# -# To update this file, edit the corresponding .md file and run: -# gh aw compile -# Not all edits will cause changes to this file. -# -# For more information: https://github.github.com/gh-aw/introduction/overview/ -# -# AI assistant for a specific PR ID — review, fix code, and push changes on demand -# -# Resolved workflow manifest: -# Imports: -# - gh-aw-fragments/elastic-tools.md -# - gh-aw-fragments/formatting.md -# - gh-aw-fragments/mcp-pagination.md -# - gh-aw-fragments/messages-footer.md -# - gh-aw-fragments/network-ecosystems.md -# - gh-aw-fragments/pick-three-keep-many.md -# - gh-aw-fragments/playwright-mcp-explorer.md -# - gh-aw-fragments/pr-context.md -# - gh-aw-fragments/review-process.md -# - gh-aw-fragments/rigor.md -# - gh-aw-fragments/runtime-setup.md -# - gh-aw-fragments/safe-output-add-comment-pr.md -# - gh-aw-fragments/safe-output-code-review.md -# - gh-aw-fragments/safe-output-push-to-pr.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 -# - gh-aw-fragments/workflow-edit-guardrails.md -# -# inlined-imports: true -# -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"cb8047e55822e668bc5e1fefd4697c54eb15c0fbba56b55d104e124cc2661cd1"} - -name: "Mention in PR by ID" -"on": - workflow_call: - inputs: - additional-instructions: - default: "" - description: Repo-specific instructions appended to the agent prompt - required: false - type: string - allowed-bot-users: - default: github-actions[bot] - description: Allowlisted bot actor usernames (comma-separated) - required: false - type: string - create-pull-request-review-comment-max: - default: "30" - description: Maximum number of review comments the agent can create per run - required: false - type: string - messages-footer: - default: "" - description: Footer appended to all agent comments and reviews - required: false - type: string - model: - default: gpt-5.3-codex - description: AI model to use - required: false - type: string - prompt: - description: Prompt for the agent - required: true - type: string - resolve-pull-request-review-thread-max: - default: "10" - description: Maximum number of review threads the agent can resolve per run - required: false - type: string - setup-commands: - default: "" - description: Shell commands to run before the agent starts (dependency install, build, etc.) - required: false - type: string - target-pr-number: - description: PR number to target - required: true - type: string - outputs: - comment_id: - description: ID of the first added comment - value: ${{ jobs.safe_outputs.outputs.comment_id }} - comment_url: - description: URL of the first added comment - value: ${{ jobs.safe_outputs.outputs.comment_url }} - push_commit_sha: - description: SHA of the pushed commit - value: ${{ jobs.safe_outputs.outputs.push_commit_sha }} - push_commit_url: - description: URL of the pushed commit - value: ${{ jobs.safe_outputs.outputs.push_commit_url }} - secrets: - COPILOT_GITHUB_TOKEN: - required: true - EXTRA_COMMIT_GITHUB_TOKEN: - required: false - -permissions: {} - -concurrency: - cancel-in-progress: true - group: ${{ github.workflow }}-mention-pr-by-id-${{ inputs.target-pr-number }} - -run-name: "Mention in PR by ID" - -jobs: - activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' - runs-on: ubuntu-slim - permissions: - contents: read - outputs: - comment_id: "" - comment_repo: "" - model: ${{ steps.generate_aw_info.outputs.model }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Generate agentic run info - id: generate_aw_info - env: - GH_AW_INFO_ENGINE_ID: "copilot" - GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" - GH_AW_INFO_MODEL: "${{ inputs.model }}" - GH_AW_INFO_VERSION: "" - GH_AW_INFO_AGENT_VERSION: "0.0.420" - GH_AW_INFO_WORKFLOW_NAME: "Mention in PR by ID" - GH_AW_INFO_EXPERIMENTAL: "false" - GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" - GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","clojure","cloud.elastic.co","containers","dart","defaults","dotnet","ela.st","elastic.co","elastic.dev","elastic.github.io","elixir","fonts","github","github-actions","go","haskell","java","kotlin","linux-distros","node","node-cdns","perl","php","playwright","public-code-search.fastmcp.app","python","ruby","rust","scala","swift","terraform","www.elastic.co","zig"]' - GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.23.0" - GH_AW_INFO_AWMG_VERSION: "" - GH_AW_INFO_FIREWALL_TYPE: "squid" - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); - await main(core, context); - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Checkout .github and .agents folders - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - .github - .agents - sparse-checkout-cone-mode: true - fetch-depth: 1 - persist-credentials: false - - name: Check workflow file timestamps - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_WORKFLOW_FILE: "gh-aw-mention-in-pr-by-id.lock.yml" - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); - await main(); - - name: Create prompt with built-in context - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_91DD53F2: ${{ inputs.target-pr-number }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} - run: | - bash /opt/gh-aw/actions/create_prompt_first.sh - { - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat "/opt/gh-aw/prompts/xpia.md" - cat "/opt/gh-aw/prompts/temp_folder_prompt.md" - cat "/opt/gh-aw/prompts/markdown.md" - cat "/opt/gh-aw/prompts/playwright_prompt.md" - 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 - GH_AW_PROMPT_EOF - cat "/opt/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_EOF' - - - The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} - - **actor**: __GH_AW_GITHUB_ACTOR__ - {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} - - **repository**: __GH_AW_GITHUB_REPOSITORY__ - {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} - - **workspace**: __GH_AW_GITHUB_WORKSPACE__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ - {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} - - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ - {{/if}} - - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## MCP Servers - - - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Formatting Guidelines - - - Lead with the most important information — your first sentence should be the key takeaway - - Be concise and actionable — no filler or praise - - Use `
` and `` tags for long sections to keep responses scannable - - Wrap branch names and @-references in backticks to avoid pinging users - - Include code snippets with file paths and line numbers when referencing the codebase - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Rigor - - **Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report.** - - - If you claim something is missing or broken, show the exact evidence in the code — file path, line number, and what you observed. - - If a conclusion depends on assumptions you haven't confirmed, do not assert it. Verify first; if you cannot verify, do not report. - - "I don't know" is better than a wrong answer. `noop` is better than a speculative finding. - - It's worth the time to verify now versus guessing and forcing someone else to verify later. - - Before submitting any output, re-read it as a skeptical reviewer. Ask: "Would a senior engineer on this team find this useful, or would they close it immediately?" If the answer is "close," call `noop` instead. - - Only report findings you would confidently defend in a code review. If you feel the need to hedge with "might," "could," or "possibly," the finding is not ready to file. - - Be thorough, spend the time to investigate and verify. There is no rush. Do your best work. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## MCP Pagination - - MCP tool responses have a **25,000 token limit**. When responses exceed this limit, the call fails and you must retry with pagination — wasting turns and tokens. Use proactive pagination to stay under the limit. - - ### Recommended `perPage` Values - - - **5-10**: For detailed items (PR diffs, files with patches, issues with comments) - - **20-30**: For medium-detail lists (commits, review comments, issue lists) - - **50-100**: For simple list operations (branches, labels, tags) - - ### Pagination Pattern - - When you need all results from a paginated API: - - 1. Fetch the first page with a conservative `perPage` value - 2. Process the results before fetching the next page - 3. Continue fetching pages until you receive fewer results than `perPage` (indicating the last page) - - ### Error Recovery - - If you see an error like: - - `MCP tool response exceeds maximum allowed tokens (25000)` - - `Response too large for tool [tool_name]` - - Retry the same call with a smaller `perPage` value (halve it). - - ### Tips - - - **Start small**: It's better to make multiple small requests than one that fails - - **Fetch incrementally**: Get an overview first, then details for specific items - - **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' - ## Workflow Editing Guardrails - - - Do not modify files under `.github/workflows/`. - - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. - - 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. **Never mention these file paths or on-disk data sources in your responses** — they are internal implementation details invisible to users. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Code Review Reference - - Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md`. Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md`. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Message Footer - - A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ### Pick Three, Keep Many - - Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. - - **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - - - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - - The quality criteria and output format you expect - - The specific angle that distinguishes this sub-agent from the others - - Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - - **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. - - **Merge and deduplicate findings** across all sub-agents: - 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. - 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. - 3. Drop any finding that does not meet the verification criteria. - - **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Playwright MCP Tools - - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. - - ### Available tools - - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console - - ### Why MCP tools instead of scripts - - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. - - ### Measuring DOM properties - - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: - - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` - - ### Handling failures - - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## add-comment Limitations - - - **Body**: Max 65,536 characters (including any footer added by gh-aw). Keep well under this limit. - - **Mentions**: Max 10 `@` mentions per comment. - - **Links**: Max 50 URLs per comment. - - **HTML**: Only safe tags allowed (`details`, `summary`, `code`, `pre`, `blockquote`, `table`, `b`, `em`, `strong`, `h1`–`h6`, `hr`, `br`, `li`, `ol`, `ul`, `p`, `sub`, `sup`). Other tags are converted to parentheses. - - **URLs**: Only HTTPS URLs to allowed domains. Non-HTTPS and non-allowed domains are redacted. - - **Bot triggers**: References like `fixes #123` or `closes #456` are neutralized to prevent unintended issue closures. - - If you exceed 10 mentions or 50 links, the comment will be rejected. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## create-pull-request-review-comment - - - **Required fields**: `path` (file path), `line` (line number), and `body` (comment text). - - **Line**: Must be within the diff — an added or context line in the patch. Must be the **exact line number from reading the file** (not estimated from the patch). Lines outside the diff will fail. - - **Body**: Sanitized with the standard pipeline (mentions neutralized, HTML filtered, URLs restricted). GitHub API limit is ~65,536 characters. - - **Side**: Defaults to `RIGHT` (the new code). Use `LEFT` only when commenting on deleted lines. - - **Suggestion blocks**: Use ` ```suggestion ` fences for concrete code fixes. The suggestion must actually change the code — don't suggest identical code. Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. - - Only flag issues you are confident are real problems — false positives erode trust. Once you have flagged an issue, you cannot unflag it. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## submit-pull-request-review Limitations - - - **Event**: Must be one of `APPROVE`, `REQUEST_CHANGES`, or `COMMENT`. Defaults to `COMMENT` if omitted. - - **Body**: Max 65,000 characters. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. A body is required when event is `REQUEST_CHANGES`. Sanitized (mentions neutralized, HTML filtered, URLs restricted). If you have also used `create-pull-request-review-comment`, you do not need to repeat the same feedback in the body. If you "Approve" and have no comments, do not provide a `body`. - - **Own PRs**: If the workflow actor is also the PR author (e.g., `github-actions[bot]` reviewing its own PR), the event is forced to `COMMENT` regardless of what you specify. `APPROVE` and `REQUEST_CHANGES` will not work. - - **Max per run**: 1 review submission per workflow run. Leave inline comments first, then submit the review as a single final action. - - **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - Before calling `push_to_pull_request_branch`, call `ready_to_push_to_pr` and apply its checklist. - - ## push-to-pull-request-branch Limitations - - - **Patch size**: Max ~10 MB (10,240 KB). Keep changes focused — very large refactors may exceed this. - - **Fork PRs**: Cannot push to fork PR branches. Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, explain that you cannot push and suggest the author apply changes themselves. - - **Committed changes required**: You must have locally committed changes before calling push. Uncommitted or staged-only changes will fail. - - **Branch**: Pushes to the PR's head branch. The workspace must have the PR branch checked out. - - You may not submit code that modifies files in `.github/workflows/`. Doing so will cause the submission to be rejected. If asked to modify workflow files, propose the change in a copy placed in a `github/` folder (without the leading period) and note in the PR that the file needs to be relocated by someone with workflow write access. - - Trying to resolve merge conflicts? Do not use `git merge` or `git rebase` — `push_to_pull_request_branch` uses `git format-patch` which requires single-parent commits. Instead: - 1. Compare with the base branch (from `/tmp/pr-context/pr.json` field `baseRefName`) to see what changed in the conflicting files - 2. Edit the files directly to incorporate the changes from the base branch - 3. Commit the changes as regular (single-parent) commits - 4. Call `ready_to_push_to_pr` (which will catch any merge commits) and then `push_to_pull_request_branch` to push - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## resolve-pull-request-review-thread Limitations - - - **Required field**: `thread_id` — the GraphQL node ID of the review thread (e.g., `PRRT_kwDO...`). This is the `id` field from `get_review_comments`, not the numeric REST comment ID. - - **Only resolve what you've addressed**: Do not resolve threads you skipped, disagreed with, or didn't fix. Only resolve threads where your changes directly address the feedback. - - **Max per run**: __GH_AW_EXPR_7F2A702A__ thread resolutions per workflow run. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - # PR Assistant by ID - - Assist with pull request #__GH_AW_EXPR_91DD53F2__ on __GH_AW_GITHUB_REPOSITORY__. - - ## Context - - - **Repository**: __GH_AW_GITHUB_REPOSITORY__ - - **PR**: #__GH_AW_EXPR_91DD53F2__ - - **PR context on disk**: `/tmp/pr-context/` — PR metadata, diff, files, reviews, comments, and linked issues are pre-fetched. Use these as your primary source; fall back to API tools only when required data is unavailable. - - **Request**: "__GH_AW_INPUTS_PROMPT__" - - ## 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) - - **CANNOT**: Push to fork PR branches, merge PRs, delete branches - - ## Instructions - - 1. Read `/tmp/pr-context/pr.json` for PR details. Read `/tmp/pr-context/README.md` for a manifest of all pre-fetched PR context. - 2. Handle the request in `__GH_AW_INPUTS_PROMPT__` with focused changes and evidence. - 3. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_EXPR_91DD53F2__. - 4. Use safe outputs only against PR #__GH_AW_EXPR_91DD53F2__. - 5. If no code/review action is needed, call `add_comment` with a concise response. - - **If asked to review the PR:** - - Call `ready_to_code_review` to prepare the review approach based on PR size. - - Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. - - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). - - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: - 1. **Read the file and surrounding context** — open the full file, not just the diff. - 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. - 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. - 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. - - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. - - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, submit a `COMMENT` review only — `APPROVE` and `REQUEST_CHANGES` will fail. - - __GH_AW_EXPR_49B959F1__ - - GH_AW_PROMPT_EOF - } > "$GH_AW_PROMPT" - - name: Interpolate variables and render templates - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} - GH_AW_EXPR_91DD53F2: ${{ inputs.target-pr-number }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); - await main(); - - name: Substitute placeholders - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_91DD53F2: ${{ inputs.target-pr-number }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} - GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - - const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); - - // Call the substitution function - return await substitutePlaceholders({ - file: process.env.GH_AW_PROMPT, - substitutions: { - GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, - GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, - GH_AW_EXPR_91DD53F2: process.env.GH_AW_EXPR_91DD53F2, - GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, - GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, - GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, - GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, - GH_AW_INPUTS_PROMPT: process.env.GH_AW_INPUTS_PROMPT, - GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED - } - }); - - name: Validate prompt placeholders - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh - - name: Print prompt - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/print_prompt_summary.sh - - name: Upload activation artifact - if: success() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: activation - path: | - /tmp/gh-aw/aw_info.json - /tmp/gh-aw/aw-prompts/prompt.txt - retention-days: 1 - - agent: - needs: activation - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - issues: read - pull-requests: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}-mention-pr-by-id-${{ inputs.target-pr-number }}" - env: - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - GH_AW_ASSETS_ALLOWED_EXTS: "" - GH_AW_ASSETS_BRANCH: "" - GH_AW_ASSETS_MAX_SIZE_KB: 0 - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_WORKFLOW_ID_SANITIZED: ghawmentioninprbyid - outputs: - checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} - detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} - detection_success: ${{ steps.detection_conclusion.outputs.success }} - has_patch: ${{ steps.collect_output.outputs.has_patch }} - model: ${{ needs.activation.outputs.model }} - output: ${{ steps.collect_output.outputs.output }} - output_types: ${{ steps.collect_output.outputs.output_types }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Create gh-aw temp directory - run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - - if: hashFiles('.python-version') != '' - name: Setup Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version-file: .python-version - - if: hashFiles('.node-version') != '' - name: Setup Node.js (.node-version) - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 - with: - node-version-file: .node-version - - if: hashFiles('.node-version') == '' && hashFiles('.nvmrc') != '' - name: Setup Node.js (.nvmrc) - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 - with: - node-version-file: .nvmrc - - if: hashFiles('.ruby-version') != '' - name: Setup Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 - with: - bundler-cache: true - ruby-version: .ruby-version - - id: setup-uv - if: hashFiles('pyproject.toml', 'uv.lock') != '' - name: Setup uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 - - env: - UV_PATH: ${{ steps.setup-uv.outputs.uv-path }} - WORKSPACE: ${{ github.workspace }} - if: hashFiles('pyproject.toml', 'uv.lock') != '' - name: Expose uv in workspace - run: | - set -euo pipefail - install_dir="$WORKSPACE/.gh-aw-tools/bin" - mkdir -p "$install_dir" - cp "$UV_PATH" "$install_dir/uv" - chmod +x "$install_dir/uv" - echo "$install_dir" >> "$GITHUB_PATH" - shell: bash - - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" - shell: bash - - env: - GITHUB_REPOSITORY: ${{ github.repository }} - name: Fetch repository conventions - run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" - 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: - GITHUB_TOKEN: ${{ github.token }} - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - name: Ensure origin refs for PR patch generation - run: "SERVER_URL_STRIPPED=\"${SERVER_URL#https://}\"\ngit remote set-url origin \"https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git\"\ngit fetch --no-tags --prune origin '+refs/heads/*:refs/remotes/origin/*'\n" - - env: - SETUP_COMMANDS: ${{ inputs.setup-commands }} - if: ${{ inputs.setup-commands != '' }} - name: Repo-specific setup - run: eval "$SETUP_COMMANDS" - - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Checkout PR branch - id: checkout-pr - if: | - (github.event.pull_request) || (github.event.issue.pull_request) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); - await main(); - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420 - - name: Install awf binary - run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 - - name: Determine automatic lockdown mode for GitHub MCP Server - id: determine-automatic-lockdown - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - with: - script: | - const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); - await determineAutomaticLockdown(github, context, core); - - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.6 ghcr.io/github/github-mcp-server:v0.31.0 mcr.microsoft.com/playwright/mcp node:lts-alpine - - name: Write Safe Outputs Config - run: | - mkdir -p /opt/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":1,"target":"${{ inputs.target-pr-number }}"},"create_pull_request_review_comment":{"max":10},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":0,"target":"${{ inputs.target-pr-number }}"},"resolve_pull_request_review_thread":{"max":"${{ inputs.resolve-pull-request-review-thread-max }}"},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_EOF - cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' - [ - { - "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added. Target: ${{ inputs.target-pr-number }}.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", - "type": "string" - }, - "item_number": { - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the comment will be silently discarded.", - "type": "number" - } - }, - "required": [ - "body" - ], - "type": "object" - }, - "name": "add_comment" - }, - { - "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", - "type": "string" - }, - "line": { - "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", - "type": [ - "number", - "string" - ] - }, - "path": { - "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", - "type": "string" - }, - "side": { - "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", - "enum": [ - "LEFT", - "RIGHT" - ], - "type": "string" - }, - "start_line": { - "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", - "type": [ - "number", - "string" - ] - } - }, - "required": [ - "path", - "line", - "body" - ], - "type": "object" - }, - "name": "create_pull_request_review_comment" - }, - { - "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", - "type": "string" - }, - "event": { - "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", - "enum": [ - "APPROVE", - "REQUEST_CHANGES", - "COMMENT" - ], - "type": "string" - } - }, - "type": "object" - }, - "name": "submit_pull_request_review" - }, - { - "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": { - "additionalProperties": false, - "properties": { - "thread_id": { - "description": "The node ID of the review thread to resolve (e.g., 'PRRT_kwDOABCD...'). This is the GraphQL node ID, not a numeric ID.", - "type": "string" - } - }, - "required": [ - "thread_id" - ], - "type": "object" - }, - "name": "resolve_pull_request_review_thread" - }, - { - "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "branch": { - "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.", - "type": "string" - }, - "message": { - "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).", - "type": "string" - }, - "pull_request_number": { - "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).", - "type": [ - "number", - "string" - ] - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "name": "push_to_pull_request_branch" - }, - { - "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "reason": { - "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", - "type": "string" - }, - "tool": { - "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", - "type": "string" - } - }, - "required": [ - "reason" - ], - "type": "object" - }, - "name": "missing_tool" - }, - { - "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "message": { - "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "name": "noop" - }, - { - "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "context": { - "description": "Additional context about the missing data or where it should come from (max 256 characters).", - "type": "string" - }, - "data_type": { - "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", - "type": "string" - }, - "reason": { - "description": "Explanation of why this data is needed to complete the task (max 256 characters).", - "type": "string" - } - }, - "required": [], - "type": "object" - }, - "name": "missing_data" - } - ] - GH_AW_SAFE_OUTPUTS_TOOLS_EOF - cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' - { - "add_comment": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "item_number": { - "issueOrPRNumber": true - }, - "repo": { - "type": "string", - "maxLength": 256 - } - } - }, - "create_pull_request_review_comment": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "line": { - "required": true, - "positiveInteger": true - }, - "path": { - "required": true, - "type": "string" - }, - "repo": { - "type": "string", - "maxLength": 256 - }, - "side": { - "type": "string", - "enum": [ - "LEFT", - "RIGHT" - ] - }, - "start_line": { - "optionalPositiveInteger": true - } - }, - "customValidation": "startLineLessOrEqualLine" - }, - "missing_data": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "context": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "data_type": { - "type": "string", - "sanitize": true, - "maxLength": 128 - }, - "reason": { - "type": "string", - "sanitize": true, - "maxLength": 256 - } - } - }, - "missing_tool": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 512 - }, - "reason": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "tool": { - "type": "string", - "sanitize": true, - "maxLength": 128 - } - } - }, - "noop": { - "defaultMax": 1, - "fields": { - "message": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - } - } - }, - "push_to_pull_request_branch": { - "defaultMax": 1, - "fields": { - "branch": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "message": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "pull_request_number": { - "issueOrPRNumber": true - } - } - }, - "resolve_pull_request_review_thread": { - "defaultMax": 10, - "fields": { - "thread_id": { - "required": true, - "type": "string" - } - } - }, - "submit_pull_request_review": { - "defaultMax": 1, - "fields": { - "body": { - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "event": { - "type": "string", - "enum": [ - "APPROVE", - "REQUEST_CHANGES", - "COMMENT" - ] - } - } - } - } - GH_AW_SAFE_OUTPUTS_VALIDATION_EOF - - name: Generate Safe Outputs MCP Server Config - id: safe-outputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3001 - - # Set outputs for next steps - { - echo "safe_outputs_api_key=${API_KEY}" - echo "safe_outputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Outputs MCP server will run on port ${PORT}" - - - name: Start Safe Outputs MCP HTTP Server - id: safe-outputs-start - env: - DEBUG: '*' - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_OUTPUTS_PORT - export GH_AW_SAFE_OUTPUTS_API_KEY - export GH_AW_SAFE_OUTPUTS_TOOLS_PATH - export GH_AW_SAFE_OUTPUTS_CONFIG_PATH - export GH_AW_MCP_LOG_DIR - - bash /opt/gh-aw/actions/start_safe_outputs_server.sh - - - name: Setup Safe Inputs Config - run: | - mkdir -p /opt/gh-aw/safe-inputs/logs - cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' - { - "serverName": "safeinputs", - "version": "1.0.0", - "logDir": "/opt/gh-aw/safe-inputs/logs", - "tools": [ - { - "name": "ready-to-code-review", - "description": "Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/", - "inputSchema": { - "properties": {}, - "type": "object" - }, - "handler": "ready-to-code-review.py", - "timeout": 60 - }, - { - "name": "ready-to-push-to-pr", - "description": "Run the PR readiness checklist before pushing to a PR", - "inputSchema": { - "properties": {}, - "type": "object" - }, - "handler": "ready-to-push-to-pr.py", - "timeout": 60 - } - ] - } - GH_AW_SAFE_INPUTS_TOOLS_EOF - cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' - const path = require("path"); - const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); - const configPath = path.join(__dirname, "tools.json"); - const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); - const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; - startHttpServer(configPath, { - port: port, - stateless: true, - logDir: "/opt/gh-aw/safe-inputs/logs" - }).catch(error => { - console.error("Failed to start safe-inputs HTTP server:", error); - process.exit(1); - }); - GH_AW_SAFE_INPUTS_SERVER_EOF - chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs - - - name: Setup Safe Inputs Tool Files - run: | - cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' - #!/usr/bin/env python3 - # Auto-generated safe-input tool: ready-to-code-review - # Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/ - - import json - import os - import sys - - # Read inputs from stdin (JSON format) - try: - inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} - except (json.JSONDecodeError, Exception): - inputs = {} - - # User code: - import os, json, re - - os.makedirs('/tmp/pr-context', exist_ok=True) - - # Read PR size written by the pr-context step - try: - with open('/tmp/pr-context/pr-size.txt') as f: - pr_size = f.read().strip() - m = re.search(r', (\d+) diff', pr_size) - diff_lines = int(m.group(1)) if m else 0 - except Exception: - pr_size = 'unknown size' - diff_lines = 0 - - # Write one instruction file per sub-agent file ordering strategy - for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: - lines = [ - '# PR Review Sub-Agent', - '', - 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', - '', - '## Instructions', - '', - 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', - '', - '## Context', - '', - '- Repository conventions: `/tmp/agents.md` (skip if missing)', - '- PR details: `/tmp/pr-context/pr.json`', - '- All context files: `/tmp/pr-context/README.md`', - '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', - '- Full file contents: read from the workspace (PR branch is checked out)', - '', - '## Your File Order', - '', - f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', - ] - with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: - f.write('\n'.join(lines) + '\n') - - # Determine review approach based on PR size - if diff_lines < 200: - approach_lines = [ - f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', - ] - size_key = 'small' - elif diff_lines < 800: - approach_lines = [ - f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', - '', - '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', - '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', - '', - 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', - ] - size_key = 'medium' - else: - approach_lines = [ - f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', - '', - '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', - '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', - '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', - '', - 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', - ] - size_key = 'large' - - with open('/tmp/pr-context/agent-review.md', 'w') as f: - f.write('\n'.join(approach_lines) + '\n') - - # Write parent-agent comment format and threshold instructions - t3 = chr(96) * 3 - t5 = chr(96) * 5 - threshold = '${{ inputs.minimum_severity || "low" }}' - parent_lines = [ - '# Code Review: Comment Format and Threshold', - '', - '## Comment Format', - '', - 'Call **`create_pull_request_review_comment`** with:', - '- The file path and the **exact line number from reading the file** (not estimated from the diff)', - '- The line must be within the diff (an added or context line in the patch)', - '', - t5, - '**[SEVERITY] Brief title**', - '', - 'Description of the issue and why it matters.', - '', - f'{t3}suggestion', - 'corrected code here', - t3, - t5, - '', - 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', - '', - '## Inline Comment Threshold', - '', - f'The minimum severity for inline comments is `{threshold}`.', - '', - 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', - '', - 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', - '', - 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', - ] - with open('/tmp/pr-context/parent-review.md', 'w') as f: - f.write('\n'.join(parent_lines) + '\n') - - print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) - - GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF - chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py - cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' - #!/usr/bin/env python3 - # Auto-generated safe-input tool: ready-to-push-to-pr - # Run the PR readiness checklist before pushing to a PR - - import json - import os - import sys - - # Read inputs from stdin (JSON format) - try: - inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} - except (json.JSONDecodeError, Exception): - inputs = {} - - # User code: - import os, json, subprocess - def find(*paths): - return next((p for p in paths if os.path.isfile(p)), None) - def run(cmd): - 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') - - # Guard: detect history rewrites and merge commits - pr_json_path = '/tmp/pr-context/pr.json' - if os.path.isfile(pr_json_path): - with open(pr_json_path) as f: - pr_data = json.load(f) - pr_head_sha = pr_data.get('headRefOid', '') - if pr_head_sha: - # Check 1: PR head must be an ancestor of HEAD (no rebase/reset) - anc = run(['git', 'merge-base', '--is-ancestor', pr_head_sha, 'HEAD']) - if anc.returncode != 0: - print(json.dumps({'status': 'error', 'error': f'History rewrite detected: the original PR head ({pr_head_sha[:12]}) is not an ancestor of HEAD. This means git rebase, reset, or cherry-pick rewrote history. push_to_pull_request_branch will fail. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch to its original head, then re-apply your changes as direct file edits and commit as regular commits.'})) - raise SystemExit(0) - # Check 2: no merge commits (multiple parents) since PR head - log = run(['git', 'rev-list', '--min-parents=2', f'{pr_head_sha}..HEAD']) - if log.returncode != 0: - print(json.dumps({'status': 'error', 'error': f'Failed to check for merge commits (git rev-list exited {log.returncode}): {log.stderr.strip()}. Cannot verify commit history is safe for push.'})) - raise SystemExit(0) - merge_shas = log.stdout.strip() - if merge_shas: - print(json.dumps({'status': 'error', 'error': f'Merge commit(s) detected: {merge_shas.splitlines()[0][:12]}... push_to_pull_request_branch uses git format-patch which breaks on merge commits. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch, then re-apply your changes as direct file edits (no git merge/rebase/commit-tree with multiple -p flags) and commit as regular single-parent commits.'})) - raise SystemExit(0) - - 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') - agents_md = find('AGENTS.md', 'agents.md', '.github/agents.md', '.github/AGENTS.md') - # Generate diff of all local changes vs upstream for self-review - # Try --merge-base (vs common ancestor), fall back to - # @{upstream} 2-dot (vs upstream tip), 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 - # Capture commit messages so the sub-agent knows what changed and why - commits_text = '' - for log_cmd in [ - ['git', 'log', '--format=### %s%n%n%b', '@{upstream}..HEAD'], - ['git', 'log', '--format=### %s%n%n%b', '-10'], - ]: - result = run(log_cmd) - if result.stdout.strip(): - commits_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) - with open('/tmp/self-review/stat.txt', 'w') as f: - f.write(stat_text) - with open('/tmp/self-review/commits.txt', 'w') as f: - f.write(commits_text) - # Copy task context if available (PR metadata from the pr-context step) - has_task = False - if os.path.isfile('/tmp/pr-context/pr.json'): - import shutil - shutil.copy('/tmp/pr-context/pr.json', '/tmp/self-review/task.json') - has_task = True - # Write manifest so the sub-agent has structured context without - # relying on the main agent to manually pass it in the prompt - manifest_lines = [ - '# Self-Review Context', - '', - 'You are reviewing unpushed changes before they are submitted to a PR.', - '', - '## Available files', - '', - 'All files are in `/tmp/self-review/`:', - '', - f'- `diff.patch` : Full unified diff of all changes ({len(diff_text.splitlines())} lines)', - '- `stat.txt` : File-level change summary', - '- `commits.txt` : Commit messages describing what was changed and why', - '- `notes.md` : Author notes on what was done, why, and key decisions made', - ] - if has_task: - manifest_lines.append('- `task.json` : Original PR context (title, body, author, branches)') - step = 1 - manifest_lines += ['', '## How to review', ''] - manifest_lines.append(f'{step}. Read `notes.md` to understand what the author did, why, and what decisions they made.') - step += 1 - manifest_lines.append(f'{step}. Read `commits.txt` for the commit-level view of what changed.') - step += 1 - if has_task: - manifest_lines.append(f'{step}. Read `task.json` to understand the original task or issue being addressed.') - step += 1 - manifest_lines.append(f'{step}. Read `stat.txt` for a high-level view of which files changed.') - step += 1 - manifest_lines.append(f'{step}. Read `diff.patch` and the relevant source files from the workspace (the branch is checked out).') - step += 1 - if agents_md: - manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') - manifest_lines += [ - '', - '## Focus areas', - '', - 'Look for bugs, logic errors, missed edge cases, and style issues.', - 'Focus on what the author might have MISSED rather than re-deriving their reasoning.', - '', - '## What NOT to flag', - '', - '- Pre-existing issues not introduced by these changes', - '- Style preferences handled by linters or formatters', - '- Theoretical performance concerns without evidence of real-world impact', - ] - with open('/tmp/self-review/README.md', 'w') as f: - f.write('\n'.join(manifest_lines) + '\n') - 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.') - if diff_line_count > 0: - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) and supporting context have been saved to `/tmp/self-review/`. Before spawning the sub-agent, write `/tmp/self-review/notes.md` with: what you changed and why, which files matter most and what they do, edge cases you already handled, and what test coverage exists. Then spawn a `code-review` sub-agent via `runSubagent` and tell it to start by reading `/tmp/self-review/README.md`. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_push_to_pr` again.') - 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-PUSH-TO-PR_EOF - chmod +x /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py - - - name: Generate Safe Inputs MCP Server Config - id: safe-inputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3000 - - # Set outputs for next steps - { - echo "safe_inputs_api_key=${API_KEY}" - echo "safe_inputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Inputs MCP server will run on port ${PORT}" - - - name: Start Safe Inputs MCP HTTP Server - id: safe-inputs-start - env: - DEBUG: '*' - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_INPUTS_PORT - export GH_AW_SAFE_INPUTS_API_KEY - - bash /opt/gh-aw/actions/start_safe_inputs_server.sh - - - name: Start MCP Gateway - id: start-mcp-gateway - env: - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} - GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - run: | - set -eo pipefail - mkdir -p /tmp/gh-aw/mcp-config - mkdir -p /tmp/gh-aw/mcp-logs/playwright - - # Export gateway environment variables for MCP config and gateway script - export MCP_GATEWAY_PORT="80" - export MCP_GATEWAY_DOMAIN="host.docker.internal" - MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${MCP_GATEWAY_API_KEY}" - export MCP_GATEWAY_API_KEY - export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" - mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" - export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" - export DEBUG="*" - - export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' - - mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh - { - "mcpServers": { - "github": { - "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v0.31.0", - "env": { - "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", - "GITHUB_READ_ONLY": "1", - "GITHUB_TOOLSETS": "repos,issues,pull_requests,search,actions" - } - }, - "playwright": { - "type": "stdio", - "container": "mcr.microsoft.com/playwright/mcp", - "args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"], - "entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"], - "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"] - }, - "public-code-search": { - "type": "http", - "url": "https://public-code-search.fastmcp.app/mcp", - "tools": [ - "search_code" - ] - }, - "safeinputs": { - "type": "http", - "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" - } - }, - "safeoutputs": { - "type": "http", - "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" - } - } - }, - "gateway": { - "port": $MCP_GATEWAY_PORT, - "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" - } - } - GH_AW_MCP_CONFIG_EOF - - name: Download activation artifact - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: activation - path: /tmp/gh-aw - - name: Clean git credentials - run: bash /opt/gh-aw/actions/clean_git_credentials.sh - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 60 - run: | - set -o pipefail - # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ inputs.model }} - GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Copy Copilot session state files to logs - if: always() - continue-on-error: true - run: | - # Copy Copilot session state files to logs folder for artifact collection - # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them - SESSION_STATE_DIR="$HOME/.copilot/session-state" - LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" - - if [ -d "$SESSION_STATE_DIR" ]; then - echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" - mkdir -p "$LOGS_DIR" - cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true - echo "Session state files copied successfully" - else - echo "No session-state directory found at $SESSION_STATE_DIR" - fi - - name: Stop MCP Gateway - if: always() - continue-on-error: true - env: - MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} - MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} - GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} - run: | - bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" - - name: Redact secrets in logs - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); - await main(); - env: - GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' - SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload Safe Outputs - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output - path: ${{ env.GH_AW_SAFE_OUTPUTS }} - if-no-files-found: warn - - name: Ingest agent output - id: collect_output - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - 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 }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); - await main(); - - name: Upload sanitized agent output - if: always() && env.GH_AW_AGENT_OUTPUT - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-output - path: ${{ env.GH_AW_AGENT_OUTPUT }} - if-no-files-found: warn - - name: Upload engine output files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent_outputs - path: | - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - if-no-files-found: ignore - - name: Parse agent logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); - await main(); - - name: Parse Safe Inputs logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); - await main(); - - name: Parse MCP Gateway logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); - await main(); - - name: Print firewall logs - if: always() - continue-on-error: true - env: - AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs - run: | - # Fix permissions on firewall logs so they can be uploaded as artifacts - # AWF runs with sudo, creating files owned by root - sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true - # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) - if command -v awf &> /dev/null; then - awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" - else - echo 'AWF binary not installed, skipping firewall log summary' - fi - - name: Upload agent artifacts - if: always() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-artifacts - path: | - /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/mcp-logs/ - /tmp/gh-aw/safe-inputs/logs/ - /tmp/gh-aw/sandbox/firewall/logs/ - /tmp/gh-aw/agent-stdio.log - /tmp/gh-aw/agent/ - /tmp/gh-aw/aw-*.patch - if-no-files-found: ignore - # --- Threat Detection (inline) --- - - name: Check if detection needed - id: detection_guard - if: always() - env: - OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} - HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} - run: | - if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then - echo "run_detection=true" >> "$GITHUB_OUTPUT" - echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" - else - echo "run_detection=false" >> "$GITHUB_OUTPUT" - echo "Detection skipped: no agent outputs or patches to analyze" - fi - - name: Clear MCP configuration for detection - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - rm -f /tmp/gh-aw/mcp-config/mcp-servers.json - rm -f /home/runner/.copilot/mcp-config.json - rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" - - name: Prepare threat detection files - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - mkdir -p /tmp/gh-aw/threat-detection/aw-prompts - cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true - cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true - for f in /tmp/gh-aw/aw-*.patch; do - [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true - done - echo "Prepared threat detection files:" - ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true - - name: Setup threat detection - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - WORKFLOW_NAME: "Mention in PR by ID" - WORKFLOW_DESCRIPTION: "AI assistant for a specific PR ID — review, fix code, and push changes on demand" - HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); - await main(); - - name: Ensure threat-detection directory and log - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - mkdir -p /tmp/gh-aw/threat-detection - touch /tmp/gh-aw/threat-detection/detection.log - - name: Execute GitHub Copilot CLI - if: always() && steps.detection_guard.outputs.run_detection == 'true' - id: detection_agentic_execution - # Copilot CLI tool arguments (sorted): - # --allow-tool shell(cat) - # --allow-tool shell(grep) - # --allow-tool shell(head) - # --allow-tool shell(jq) - # --allow-tool shell(ls) - # --allow-tool shell(tail) - # --allow-tool shell(wc) - timeout-minutes: 20 - run: | - set -o pipefail - # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ inputs.model }} - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Parse threat detection results - id: parse_detection_results - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); - await main(); - - name: Upload threat detection log - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: threat-detection.log - path: /tmp/gh-aw/threat-detection/detection.log - if-no-files-found: ignore - - name: Set detection conclusion - id: detection_conclusion - if: always() - env: - RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} - DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} - run: | - if [[ "$RUN_DETECTION" != "true" ]]; then - echo "conclusion=skipped" >> "$GITHUB_OUTPUT" - echo "success=true" >> "$GITHUB_OUTPUT" - echo "Detection was not needed, marking as skipped" - elif [[ "$DETECTION_SUCCESS" == "true" ]]; then - echo "conclusion=success" >> "$GITHUB_OUTPUT" - echo "success=true" >> "$GITHUB_OUTPUT" - echo "Detection passed successfully" - else - echo "conclusion=failure" >> "$GITHUB_OUTPUT" - echo "success=false" >> "$GITHUB_OUTPUT" - echo "Detection found issues" - fi - - conclusion: - needs: - - activation - - agent - - safe_outputs - if: (always()) && (needs.agent.result != 'skipped') - runs-on: ubuntu-slim - permissions: - contents: write - discussions: write - issues: write - pull-requests: write - outputs: - noop_message: ${{ steps.noop.outputs.noop_message }} - tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} - total_count: ${{ steps.missing_tool.outputs.total_count }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ - - name: Setup agent output environment variable - run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Process No-Op Messages - id: noop - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_NOOP_MAX: "1" - GH_AW_WORKFLOW_NAME: "Mention in PR by ID" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/noop.cjs'); - await main(); - - name: Record Missing Tool - id: missing_tool - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR by ID" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); - await main(); - - name: Handle Agent Failure - id: handle_agent_failure - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR by ID" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr-by-id" - GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} - GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} - GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} - GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" - GH_AW_GROUP_REPORTS: "false" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); - await main(); - - name: Handle No-Op Message - id: handle_noop_message - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR by ID" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} - GH_AW_NOOP_REPORT_AS_ISSUE: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); - await main(); - - pre_activation: - runs-on: ubuntu-slim - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - matched_command: '' - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - - safe_outputs: - needs: - - activation - - agent - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') - runs-on: ubuntu-slim - permissions: - contents: write - discussions: write - issues: write - pull-requests: write - timeout-minutes: 15 - env: - GH_AW_ENGINE_ID: "copilot" - GH_AW_ENGINE_MODEL: "${{ inputs.model }}" - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" - GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr-by-id" - GH_AW_WORKFLOW_NAME: "Mention in PR by ID" - outputs: - code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} - code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} - comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} - comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} - create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} - create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} - process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} - process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} - push_commit_sha: ${{ steps.process_safe_outputs.outputs.push_commit_sha }} - push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ - - name: Setup agent output environment variable - run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Download patch artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-artifacts - path: /tmp/gh-aw/ - - name: Checkout repository - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} - token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - persist-credentials: false - fetch-depth: 1 - - name: Configure Git credentials - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Process Safe Outputs - id: process_safe_outputs - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - 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,\"target\":\"${{ inputs.target-pr-number }}\"},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\",\"target\":\"${{ inputs.target-pr-number }}\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max_patch_size\":10240,\"target\":\"${{ inputs.target-pr-number }}\"},\"resolve_pull_request_review_thread\":{\"max\":\"${{ inputs.resolve-pull-request-review-thread-max }}\"},\"submit_pull_request_review\":{\"max\":1,\"target\":\"${{ inputs.target-pr-number }}\"}}" - GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.EXTRA_COMMIT_GITHUB_TOKEN }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); - await main(); - - name: Upload safe output items manifest - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output-items - path: /tmp/safe-output-items.jsonl - if-no-files-found: warn - diff --git a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.invalid.yml b/.github/workflows/gh-aw-mention-in-pr-no-sandbox.invalid.yml deleted file mode 100644 index 2eca3942..00000000 --- a/.github/workflows/gh-aw-mention-in-pr-no-sandbox.invalid.yml +++ /dev/null @@ -1,2097 +0,0 @@ -# -# ___ _ _ -# / _ \ | | (_) -# | |_| | __ _ ___ _ __ | |_ _ ___ -# | _ |/ _` |/ _ \ '_ \| __| |/ __| -# | | | | (_| | __/ | | | |_| | (__ -# \_| |_/\__, |\___|_| |_|\__|_|\___| -# __/ | -# _ _ |___/ -# | | | | / _| | -# | | | | ___ _ __ _ __| |_| | _____ ____ -# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| -# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ -# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ -# -# This file was automatically generated by gh-aw. DO NOT EDIT. -# -# To update this file, edit the corresponding .md file and run: -# gh aw compile -# Not all edits will cause changes to this file. -# -# For more information: https://github.github.com/gh-aw/introduction/overview/ -# -# AI assistant for PRs — review, fix code, and push changes on demand (no agent sandbox, Docker access available) -# -# Resolved workflow manifest: -# Imports: -# - gh-aw-fragments/elastic-tools.md -# - gh-aw-fragments/formatting.md -# - gh-aw-fragments/mcp-pagination.md -# - gh-aw-fragments/messages-footer.md -# - gh-aw-fragments/network-ecosystems.md -# - gh-aw-fragments/pick-three-keep-many.md -# - gh-aw-fragments/playwright-mcp-explorer.md -# - gh-aw-fragments/pr-context.md -# - gh-aw-fragments/review-process.md -# - gh-aw-fragments/rigor.md -# - gh-aw-fragments/runtime-setup.md -# - gh-aw-fragments/safe-output-add-comment-pr.md -# - gh-aw-fragments/safe-output-code-review.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 -# - gh-aw-fragments/workflow-edit-guardrails.md -# -# inlined-imports: true -# -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"980c2cd07b38e68ee3965fe18a4d73359dbc670c1cbdd86cb451b8977ee120c4"} - -name: "Mention in PR (no sandbox)" -"on": - workflow_call: - inputs: - additional-instructions: - default: "" - description: Repo-specific instructions appended to the agent prompt - required: false - type: string - allowed-bot-users: - default: github-actions[bot] - description: Allowlisted bot actor usernames (comma-separated) - required: false - type: string - create-pull-request-review-comment-max: - default: "30" - description: Maximum number of review comments the agent can create per run - required: false - type: string - messages-footer: - default: "" - description: Footer appended to all agent comments and reviews - required: false - type: string - model: - default: gpt-5.3-codex - description: AI model to use - required: false - type: string - resolve-pull-request-review-thread-max: - default: "10" - description: Maximum number of review threads the agent can resolve per run - required: false - type: string - setup-commands: - default: "" - description: Shell commands to run before the agent starts (dependency install, build, etc.) - required: false - type: string - outputs: - comment_id: - description: ID of the first added comment - value: ${{ jobs.safe_outputs.outputs.comment_id }} - comment_url: - description: URL of the first added comment - value: ${{ jobs.safe_outputs.outputs.comment_url }} - push_commit_sha: - description: SHA of the pushed commit - value: ${{ jobs.safe_outputs.outputs.push_commit_sha }} - push_commit_url: - description: URL of the pushed commit - value: ${{ jobs.safe_outputs.outputs.push_commit_url }} - secrets: - COPILOT_GITHUB_TOKEN: - required: true - EXTRA_COMMIT_GITHUB_TOKEN: - required: false - -permissions: {} - -concurrency: - cancel-in-progress: true - group: ${{ github.workflow }}-mention-pr-no-sandbox-${{ github.event.issue.number }} - -run-name: "Mention in PR (no sandbox)" - -jobs: - activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' - runs-on: ubuntu-slim - permissions: - contents: read - discussions: write - issues: write - pull-requests: write - outputs: - body: ${{ steps.sanitized.outputs.body }} - comment_id: "" - comment_repo: "" - model: ${{ steps.generate_aw_info.outputs.model }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} - text: ${{ steps.sanitized.outputs.text }} - title: ${{ steps.sanitized.outputs.title }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Generate agentic run info - id: generate_aw_info - env: - GH_AW_INFO_ENGINE_ID: "copilot" - GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" - GH_AW_INFO_MODEL: "${{ inputs.model }}" - GH_AW_INFO_VERSION: "" - GH_AW_INFO_AGENT_VERSION: "0.0.420" - GH_AW_INFO_WORKFLOW_NAME: "Mention in PR (no sandbox)" - GH_AW_INFO_EXPERIMENTAL: "false" - GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" - GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","clojure","cloud.elastic.co","containers","dart","defaults","dotnet","ela.st","elastic.co","elastic.dev","elastic.github.io","elixir","fonts","github","github-actions","go","haskell","java","kotlin","linux-distros","node","node-cdns","perl","php","playwright","public-code-search.fastmcp.app","python","ruby","rust","scala","swift","terraform","www.elastic.co","zig"]' - GH_AW_INFO_FIREWALL_ENABLED: "false" - GH_AW_INFO_AWF_VERSION: "" - GH_AW_INFO_AWMG_VERSION: "" - GH_AW_INFO_FIREWALL_TYPE: "" - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); - await main(core, context); - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Checkout .github and .agents folders - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - .github - .agents - sparse-checkout-cone-mode: true - fetch-depth: 1 - persist-credentials: false - - name: Check workflow file timestamps - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_WORKFLOW_FILE: "gh-aw-mention-in-pr-no-sandbox.lock.yml" - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); - await main(); - - name: Compute current body text - id: sanitized - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/compute_text.cjs'); - await main(); - - name: Create prompt with built-in context - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} - run: | - bash /opt/gh-aw/actions/create_prompt_first.sh - { - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat "/opt/gh-aw/prompts/xpia.md" - cat "/opt/gh-aw/prompts/temp_folder_prompt.md" - cat "/opt/gh-aw/prompts/markdown.md" - cat "/opt/gh-aw/prompts/playwright_prompt.md" - 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, 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' - - - The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} - - **actor**: __GH_AW_GITHUB_ACTOR__ - {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} - - **repository**: __GH_AW_GITHUB_REPOSITORY__ - {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} - - **workspace**: __GH_AW_GITHUB_WORKSPACE__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ - {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} - - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ - {{/if}} - - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## MCP Servers - - - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Formatting Guidelines - - - Lead with the most important information — your first sentence should be the key takeaway - - Be concise and actionable — no filler or praise - - Use `
` and `` tags for long sections to keep responses scannable - - Wrap branch names and @-references in backticks to avoid pinging users - - Include code snippets with file paths and line numbers when referencing the codebase - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Rigor - - **Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report.** - - - If you claim something is missing or broken, show the exact evidence in the code — file path, line number, and what you observed. - - If a conclusion depends on assumptions you haven't confirmed, do not assert it. Verify first; if you cannot verify, do not report. - - "I don't know" is better than a wrong answer. `noop` is better than a speculative finding. - - It's worth the time to verify now versus guessing and forcing someone else to verify later. - - Before submitting any output, re-read it as a skeptical reviewer. Ask: "Would a senior engineer on this team find this useful, or would they close it immediately?" If the answer is "close," call `noop` instead. - - Only report findings you would confidently defend in a code review. If you feel the need to hedge with "might," "could," or "possibly," the finding is not ready to file. - - Be thorough, spend the time to investigate and verify. There is no rush. Do your best work. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## MCP Pagination - - MCP tool responses have a **25,000 token limit**. When responses exceed this limit, the call fails and you must retry with pagination — wasting turns and tokens. Use proactive pagination to stay under the limit. - - ### Recommended `perPage` Values - - - **5-10**: For detailed items (PR diffs, files with patches, issues with comments) - - **20-30**: For medium-detail lists (commits, review comments, issue lists) - - **50-100**: For simple list operations (branches, labels, tags) - - ### Pagination Pattern - - When you need all results from a paginated API: - - 1. Fetch the first page with a conservative `perPage` value - 2. Process the results before fetching the next page - 3. Continue fetching pages until you receive fewer results than `perPage` (indicating the last page) - - ### Error Recovery - - If you see an error like: - - `MCP tool response exceeds maximum allowed tokens (25000)` - - `Response too large for tool [tool_name]` - - Retry the same call with a smaller `perPage` value (halve it). - - ### Tips - - - **Start small**: It's better to make multiple small requests than one that fails - - **Fetch incrementally**: Get an overview first, then details for specific items - - **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' - ## Workflow Editing Guardrails - - - Do not modify files under `.github/workflows/`. - - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. - - 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. **Never mention these file paths or on-disk data sources in your responses** — they are internal implementation details invisible to users. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Code Review Reference - - Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md`. Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md`. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Message Footer - - A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Playwright MCP Tools - - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. - - ### Available tools - - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console - - ### Why MCP tools instead of scripts - - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. - - ### Measuring DOM properties - - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: - - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` - - ### Handling failures - - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ### Pick Three, Keep Many - - Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. - - **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - - - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - - The quality criteria and output format you expect - - The specific angle that distinguishes this sub-agent from the others - - Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - - **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. - - **Merge and deduplicate findings** across all sub-agents: - 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. - 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. - 3. Drop any finding that does not meet the verification criteria. - - **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## add-comment Limitations - - - **Body**: Max 65,536 characters (including any footer added by gh-aw). Keep well under this limit. - - **Mentions**: Max 10 `@` mentions per comment. - - **Links**: Max 50 URLs per comment. - - **HTML**: Only safe tags allowed (`details`, `summary`, `code`, `pre`, `blockquote`, `table`, `b`, `em`, `strong`, `h1`–`h6`, `hr`, `br`, `li`, `ol`, `ul`, `p`, `sub`, `sup`). Other tags are converted to parentheses. - - **URLs**: Only HTTPS URLs to allowed domains. Non-HTTPS and non-allowed domains are redacted. - - **Bot triggers**: References like `fixes #123` or `closes #456` are neutralized to prevent unintended issue closures. - - If you exceed 10 mentions or 50 links, the comment will be rejected. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## create-pull-request-review-comment - - - **Required fields**: `path` (file path), `line` (line number), and `body` (comment text). - - **Line**: Must be within the diff — an added or context line in the patch. Must be the **exact line number from reading the file** (not estimated from the patch). Lines outside the diff will fail. - - **Body**: Sanitized with the standard pipeline (mentions neutralized, HTML filtered, URLs restricted). GitHub API limit is ~65,536 characters. - - **Side**: Defaults to `RIGHT` (the new code). Use `LEFT` only when commenting on deleted lines. - - **Suggestion blocks**: Use ` ```suggestion ` fences for concrete code fixes. The suggestion must actually change the code — don't suggest identical code. Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. - - Only flag issues you are confident are real problems — false positives erode trust. Once you have flagged an issue, you cannot unflag it. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## submit-pull-request-review Limitations - - - **Event**: Must be one of `APPROVE`, `REQUEST_CHANGES`, or `COMMENT`. Defaults to `COMMENT` if omitted. - - **Body**: Max 65,000 characters. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. A body is required when event is `REQUEST_CHANGES`. Sanitized (mentions neutralized, HTML filtered, URLs restricted). If you have also used `create-pull-request-review-comment`, you do not need to repeat the same feedback in the body. If you "Approve" and have no comments, do not provide a `body`. - - **Own PRs**: If the workflow actor is also the PR author (e.g., `github-actions[bot]` reviewing its own PR), the event is forced to `COMMENT` regardless of what you specify. `APPROVE` and `REQUEST_CHANGES` will not work. - - **Max per run**: 1 review submission per workflow run. Leave inline comments first, then submit the review as a single final action. - - **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - Before calling `push_to_pull_request_branch`, call `ready_to_push_to_pr` and apply its checklist. - - ## push-to-pull-request-branch Limitations - - - **Patch size**: Max ~10 MB (10,240 KB). Keep changes focused — very large refactors may exceed this. - - **Fork PRs**: Cannot push to fork PR branches. Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, explain that you cannot push and suggest the author apply changes themselves. - - **Committed changes required**: You must have locally committed changes before calling push. Uncommitted or staged-only changes will fail. - - **Branch**: Pushes to the PR's head branch. The workspace must have the PR branch checked out. - - You may not submit code that modifies files in `.github/workflows/`. Doing so will cause the submission to be rejected. If asked to modify workflow files, propose the change in a copy placed in a `github/` folder (without the leading period) and note in the PR that the file needs to be relocated by someone with workflow write access. - - Trying to resolve merge conflicts? Do not use `git merge` or `git rebase` — `push_to_pull_request_branch` uses `git format-patch` which requires single-parent commits. Instead: - 1. Compare with the base branch (from `/tmp/pr-context/pr.json` field `baseRefName`) to see what changed in the conflicting files - 2. Edit the files directly to incorporate the changes from the base branch - 3. Commit the changes as regular (single-parent) commits - 4. Call `ready_to_push_to_pr` (which will catch any merge commits) and then `push_to_pull_request_branch` to push - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## resolve-pull-request-review-thread Limitations - - - **Required field**: `thread_id` — the GraphQL node ID of the review thread (e.g., `PRRT_kwDO...`). This is the `id` field from `get_review_comments`, not the numeric REST comment ID. - - **Only resolve what you've addressed**: Do not resolve threads you skipped, disagreed with, or didn't fix. Only resolve threads where your changes directly address the feedback. - - **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 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. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - # PR Assistant - - Assist with pull requests on __GH_AW_GITHUB_REPOSITORY__ — review code, fix issues, answer questions, and push changes. - - ## Context - - - **Repository**: __GH_AW_GITHUB_REPOSITORY__ - - **PR**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ — __GH_AW_GITHUB_EVENT_ISSUE_TITLE__ - - **PR context on disk**: `/tmp/pr-context/` — PR metadata, diff, files, reviews, comments, and linked issues are pre-fetched. Use these as your primary source; fall back to API tools only when required data is unavailable. - - **Request**: "__GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT__" - - ## Constraints - - - **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 - - ## Instructions - - Understand the request, investigate the code, and respond appropriately. - - ### 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. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). - 2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. - 3. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. - 4. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__. - - ### Step 2: Handle the Request - - Based on what's asked, do the appropriate thing: - - **If asked to review the PR:** - - Call `ready_to_code_review` to prepare the review approach based on PR size. - - Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. - - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). - - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: - 1. **Read the file and surrounding context** — open the full file, not just the diff. - 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. - 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. - 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. - - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. - - **Important**: Substantive feedback belongs in the PR review (inline comments + review submission), NOT in a reply comment. Do NOT add a separate comment after submitting the review unless the user explicitly asked for a comment or the review submission failed. - - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. - - **If asked to fix code or address review feedback:** - - Read `/tmp/pr-context/unresolved_threads.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. - - 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. - - Use `push_to_pull_request_branch` to push your changes. - - 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/unresolved_threads.json` for all unresolved threads — also check `/tmp/pr-context/outdated_threads.json` for threads where the underlying code changed since the comment was made and verify 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:** - - Find the relevant code and explain it. - - Use `grep` and file reading to gather context. - - Use `web-fetch` to look up documentation when needed. - - **If the request is unclear:** - - Ask clarifying questions rather than guessing. - - ### Step 3: Respond - - If you did not submit a PR review, call `add_comment` with your response. If you submitted a review, do NOT call `add_comment` unless explicitly requested or to report a review submission failure. - - **Additional tools:** - - `push_to_pull_request_branch` — push committed changes to the PR branch (same-repo PRs only) - - `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__ - - GH_AW_PROMPT_EOF - } > "$GH_AW_PROMPT" - - name: Interpolate variables and render templates - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); - await main(); - - name: Substitute placeholders - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} - GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - - const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); - - // Call the substitution function - return await substitutePlaceholders({ - file: process.env.GH_AW_PROMPT, - substitutions: { - GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, - GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, - GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_TITLE: process.env.GH_AW_GITHUB_EVENT_ISSUE_TITLE, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, - GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, - GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, - GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, - GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED, - GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: process.env.GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT - } - }); - - name: Validate prompt placeholders - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh - - name: Print prompt - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/print_prompt_summary.sh - - name: Upload activation artifact - if: success() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: activation - path: | - /tmp/gh-aw/aw_info.json - /tmp/gh-aw/aw-prompts/prompt.txt - retention-days: 1 - - agent: - needs: activation - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - issues: read - pull-requests: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}-mention-pr-no-sandbox-${{ github.event.issue.number }}" - env: - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - GH_AW_ASSETS_ALLOWED_EXTS: "" - GH_AW_ASSETS_BRANCH: "" - GH_AW_ASSETS_MAX_SIZE_KB: 0 - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_WORKFLOW_ID_SANITIZED: ghawmentioninprnosandbox - outputs: - checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} - has_patch: ${{ steps.collect_output.outputs.has_patch }} - model: ${{ needs.activation.outputs.model }} - output: ${{ steps.collect_output.outputs.output }} - output_types: ${{ steps.collect_output.outputs.output_types }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Create gh-aw temp directory - run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - - if: hashFiles('.python-version') != '' - name: Setup Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version-file: .python-version - - if: hashFiles('.node-version') != '' - name: Setup Node.js (.node-version) - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 - with: - node-version-file: .node-version - - if: hashFiles('.node-version') == '' && hashFiles('.nvmrc') != '' - name: Setup Node.js (.nvmrc) - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 - with: - node-version-file: .nvmrc - - if: hashFiles('.ruby-version') != '' - name: Setup Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 - with: - bundler-cache: true - ruby-version: .ruby-version - - id: setup-uv - if: hashFiles('pyproject.toml', 'uv.lock') != '' - name: Setup uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 - - env: - UV_PATH: ${{ steps.setup-uv.outputs.uv-path }} - WORKSPACE: ${{ github.workspace }} - if: hashFiles('pyproject.toml', 'uv.lock') != '' - name: Expose uv in workspace - run: | - set -euo pipefail - install_dir="$WORKSPACE/.gh-aw-tools/bin" - mkdir -p "$install_dir" - cp "$UV_PATH" "$install_dir/uv" - chmod +x "$install_dir/uv" - echo "$install_dir" >> "$GITHUB_PATH" - shell: bash - - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" - shell: bash - - env: - GITHUB_REPOSITORY: ${{ github.repository }} - name: Fetch repository conventions - run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" - 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: - GITHUB_TOKEN: ${{ github.token }} - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - name: Ensure origin refs for PR patch generation - run: "SERVER_URL_STRIPPED=\"${SERVER_URL#https://}\"\ngit remote set-url origin \"https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git\"\ngit fetch --no-tags --prune origin '+refs/heads/*:refs/remotes/origin/*'\n" - - env: - SETUP_COMMANDS: ${{ inputs.setup-commands }} - if: ${{ inputs.setup-commands != '' }} - name: Repo-specific setup - run: eval "$SETUP_COMMANDS" - - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Checkout PR branch - id: checkout-pr - if: | - (github.event.pull_request) || (github.event.issue.pull_request) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); - await main(); - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420 - - name: Determine automatic lockdown mode for GitHub MCP Server - id: determine-automatic-lockdown - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - with: - script: | - const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); - await determineAutomaticLockdown(github, context, core); - - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/github-mcp-server:v0.31.0 mcr.microsoft.com/playwright/mcp node:lts-alpine - - name: Write Safe Outputs Config - run: | - mkdir -p /opt/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":1},"create_pull_request_review_comment":{"max":"${{ inputs.create-pull-request-review-comment-max }}"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":0},"resolve_pull_request_review_thread":{"max":"${{ inputs.resolve-pull-request-review-thread-max }}"},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_EOF - cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' - [ - { - "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", - "type": "string" - }, - "item_number": { - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the comment will be silently discarded.", - "type": "number" - } - }, - "required": [ - "body" - ], - "type": "object" - }, - "name": "add_comment" - }, - { - "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Comments will be on the RIGHT side of the diff.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", - "type": "string" - }, - "line": { - "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", - "type": [ - "number", - "string" - ] - }, - "path": { - "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", - "type": "string" - }, - "side": { - "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", - "enum": [ - "LEFT", - "RIGHT" - ], - "type": "string" - }, - "start_line": { - "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", - "type": [ - "number", - "string" - ] - } - }, - "required": [ - "path", - "line", - "body" - ], - "type": "object" - }, - "name": "create_pull_request_review_comment" - }, - { - "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", - "type": "string" - }, - "event": { - "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", - "enum": [ - "APPROVE", - "REQUEST_CHANGES", - "COMMENT" - ], - "type": "string" - } - }, - "type": "object" - }, - "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": { - "additionalProperties": false, - "properties": { - "thread_id": { - "description": "The node ID of the review thread to resolve (e.g., 'PRRT_kwDOABCD...'). This is the GraphQL node ID, not a numeric ID.", - "type": "string" - } - }, - "required": [ - "thread_id" - ], - "type": "object" - }, - "name": "resolve_pull_request_review_thread" - }, - { - "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "branch": { - "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.", - "type": "string" - }, - "message": { - "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).", - "type": "string" - }, - "pull_request_number": { - "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).", - "type": [ - "number", - "string" - ] - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "name": "push_to_pull_request_branch" - }, - { - "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "reason": { - "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", - "type": "string" - }, - "tool": { - "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", - "type": "string" - } - }, - "required": [ - "reason" - ], - "type": "object" - }, - "name": "missing_tool" - }, - { - "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "message": { - "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "name": "noop" - }, - { - "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "context": { - "description": "Additional context about the missing data or where it should come from (max 256 characters).", - "type": "string" - }, - "data_type": { - "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", - "type": "string" - }, - "reason": { - "description": "Explanation of why this data is needed to complete the task (max 256 characters).", - "type": "string" - } - }, - "required": [], - "type": "object" - }, - "name": "missing_data" - } - ] - GH_AW_SAFE_OUTPUTS_TOOLS_EOF - cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' - { - "add_comment": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "item_number": { - "issueOrPRNumber": true - }, - "repo": { - "type": "string", - "maxLength": 256 - } - } - }, - "create_pull_request_review_comment": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "line": { - "required": true, - "positiveInteger": true - }, - "path": { - "required": true, - "type": "string" - }, - "repo": { - "type": "string", - "maxLength": 256 - }, - "side": { - "type": "string", - "enum": [ - "LEFT", - "RIGHT" - ] - }, - "start_line": { - "optionalPositiveInteger": true - } - }, - "customValidation": "startLineLessOrEqualLine" - }, - "missing_data": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "context": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "data_type": { - "type": "string", - "sanitize": true, - "maxLength": 128 - }, - "reason": { - "type": "string", - "sanitize": true, - "maxLength": 256 - } - } - }, - "missing_tool": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 512 - }, - "reason": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "tool": { - "type": "string", - "sanitize": true, - "maxLength": 128 - } - } - }, - "noop": { - "defaultMax": 1, - "fields": { - "message": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - } - } - }, - "push_to_pull_request_branch": { - "defaultMax": 1, - "fields": { - "branch": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "message": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "pull_request_number": { - "issueOrPRNumber": true - } - } - }, - "resolve_pull_request_review_thread": { - "defaultMax": 10, - "fields": { - "thread_id": { - "required": true, - "type": "string" - } - } - }, - "submit_pull_request_review": { - "defaultMax": 1, - "fields": { - "body": { - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "event": { - "type": "string", - "enum": [ - "APPROVE", - "REQUEST_CHANGES", - "COMMENT" - ] - } - } - } - } - GH_AW_SAFE_OUTPUTS_VALIDATION_EOF - - name: Generate Safe Outputs MCP Server Config - id: safe-outputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3001 - - # Set outputs for next steps - { - echo "safe_outputs_api_key=${API_KEY}" - echo "safe_outputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Outputs MCP server will run on port ${PORT}" - - - name: Start Safe Outputs MCP HTTP Server - id: safe-outputs-start - env: - DEBUG: '*' - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_OUTPUTS_PORT - export GH_AW_SAFE_OUTPUTS_API_KEY - export GH_AW_SAFE_OUTPUTS_TOOLS_PATH - export GH_AW_SAFE_OUTPUTS_CONFIG_PATH - export GH_AW_MCP_LOG_DIR - - bash /opt/gh-aw/actions/start_safe_outputs_server.sh - - - name: Setup Safe Inputs Config - run: | - mkdir -p /opt/gh-aw/safe-inputs/logs - cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' - { - "serverName": "safeinputs", - "version": "1.0.0", - "logDir": "/opt/gh-aw/safe-inputs/logs", - "tools": [ - { - "name": "ready-to-code-review", - "description": "Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/", - "inputSchema": { - "properties": {}, - "type": "object" - }, - "handler": "ready-to-code-review.py", - "timeout": 60 - }, - { - "name": "ready-to-push-to-pr", - "description": "Run the PR readiness checklist before pushing to a PR", - "inputSchema": { - "properties": {}, - "type": "object" - }, - "handler": "ready-to-push-to-pr.py", - "timeout": 60 - } - ] - } - GH_AW_SAFE_INPUTS_TOOLS_EOF - cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' - const path = require("path"); - const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); - const configPath = path.join(__dirname, "tools.json"); - const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); - const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; - startHttpServer(configPath, { - port: port, - stateless: true, - logDir: "/opt/gh-aw/safe-inputs/logs" - }).catch(error => { - console.error("Failed to start safe-inputs HTTP server:", error); - process.exit(1); - }); - GH_AW_SAFE_INPUTS_SERVER_EOF - chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs - - - name: Setup Safe Inputs Tool Files - run: | - cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' - #!/usr/bin/env python3 - # Auto-generated safe-input tool: ready-to-code-review - # Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/ - - import json - import os - import sys - - # Read inputs from stdin (JSON format) - try: - inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} - except (json.JSONDecodeError, Exception): - inputs = {} - - # User code: - import os, json, re - - os.makedirs('/tmp/pr-context', exist_ok=True) - - # Read PR size written by the pr-context step - try: - with open('/tmp/pr-context/pr-size.txt') as f: - pr_size = f.read().strip() - m = re.search(r', (\d+) diff', pr_size) - diff_lines = int(m.group(1)) if m else 0 - except Exception: - pr_size = 'unknown size' - diff_lines = 0 - - # Write one instruction file per sub-agent file ordering strategy - for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: - lines = [ - '# PR Review Sub-Agent', - '', - 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', - '', - '## Instructions', - '', - 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', - '', - '## Context', - '', - '- Repository conventions: `/tmp/agents.md` (skip if missing)', - '- PR details: `/tmp/pr-context/pr.json`', - '- All context files: `/tmp/pr-context/README.md`', - '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', - '- Full file contents: read from the workspace (PR branch is checked out)', - '', - '## Your File Order', - '', - f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', - ] - with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: - f.write('\n'.join(lines) + '\n') - - # Determine review approach based on PR size - if diff_lines < 200: - approach_lines = [ - f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', - ] - size_key = 'small' - elif diff_lines < 800: - approach_lines = [ - f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', - '', - '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', - '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', - '', - 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', - ] - size_key = 'medium' - else: - approach_lines = [ - f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', - '', - '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', - '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', - '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', - '', - 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', - ] - size_key = 'large' - - with open('/tmp/pr-context/agent-review.md', 'w') as f: - f.write('\n'.join(approach_lines) + '\n') - - # Write parent-agent comment format and threshold instructions - t3 = chr(96) * 3 - t5 = chr(96) * 5 - threshold = '${{ inputs.minimum_severity || "low" }}' - parent_lines = [ - '# Code Review: Comment Format and Threshold', - '', - '## Comment Format', - '', - 'Call **`create_pull_request_review_comment`** with:', - '- The file path and the **exact line number from reading the file** (not estimated from the diff)', - '- The line must be within the diff (an added or context line in the patch)', - '', - t5, - '**[SEVERITY] Brief title**', - '', - 'Description of the issue and why it matters.', - '', - f'{t3}suggestion', - 'corrected code here', - t3, - t5, - '', - 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', - '', - '## Inline Comment Threshold', - '', - f'The minimum severity for inline comments is `{threshold}`.', - '', - 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', - '', - 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', - '', - 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', - ] - with open('/tmp/pr-context/parent-review.md', 'w') as f: - f.write('\n'.join(parent_lines) + '\n') - - print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) - - GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF - chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py - cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' - #!/usr/bin/env python3 - # Auto-generated safe-input tool: ready-to-push-to-pr - # Run the PR readiness checklist before pushing to a PR - - import json - import os - import sys - - # Read inputs from stdin (JSON format) - try: - inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} - except (json.JSONDecodeError, Exception): - inputs = {} - - # User code: - import os, json, subprocess - def find(*paths): - return next((p for p in paths if os.path.isfile(p)), None) - def run(cmd): - 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') - - # Guard: detect history rewrites and merge commits - pr_json_path = '/tmp/pr-context/pr.json' - if os.path.isfile(pr_json_path): - with open(pr_json_path) as f: - pr_data = json.load(f) - pr_head_sha = pr_data.get('headRefOid', '') - if pr_head_sha: - # Check 1: PR head must be an ancestor of HEAD (no rebase/reset) - anc = run(['git', 'merge-base', '--is-ancestor', pr_head_sha, 'HEAD']) - if anc.returncode != 0: - print(json.dumps({'status': 'error', 'error': f'History rewrite detected: the original PR head ({pr_head_sha[:12]}) is not an ancestor of HEAD. This means git rebase, reset, or cherry-pick rewrote history. push_to_pull_request_branch will fail. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch to its original head, then re-apply your changes as direct file edits and commit as regular commits.'})) - raise SystemExit(0) - # Check 2: no merge commits (multiple parents) since PR head - log = run(['git', 'rev-list', '--min-parents=2', f'{pr_head_sha}..HEAD']) - if log.returncode != 0: - print(json.dumps({'status': 'error', 'error': f'Failed to check for merge commits (git rev-list exited {log.returncode}): {log.stderr.strip()}. Cannot verify commit history is safe for push.'})) - raise SystemExit(0) - merge_shas = log.stdout.strip() - if merge_shas: - print(json.dumps({'status': 'error', 'error': f'Merge commit(s) detected: {merge_shas.splitlines()[0][:12]}... push_to_pull_request_branch uses git format-patch which breaks on merge commits. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch, then re-apply your changes as direct file edits (no git merge/rebase/commit-tree with multiple -p flags) and commit as regular single-parent commits.'})) - raise SystemExit(0) - - 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') - agents_md = find('AGENTS.md', 'agents.md', '.github/agents.md', '.github/AGENTS.md') - # Generate diff of all local changes vs upstream for self-review - # Try --merge-base (vs common ancestor), fall back to - # @{upstream} 2-dot (vs upstream tip), 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 - # Capture commit messages so the sub-agent knows what changed and why - commits_text = '' - for log_cmd in [ - ['git', 'log', '--format=### %s%n%n%b', '@{upstream}..HEAD'], - ['git', 'log', '--format=### %s%n%n%b', '-10'], - ]: - result = run(log_cmd) - if result.stdout.strip(): - commits_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) - with open('/tmp/self-review/stat.txt', 'w') as f: - f.write(stat_text) - with open('/tmp/self-review/commits.txt', 'w') as f: - f.write(commits_text) - # Copy task context if available (PR metadata from the pr-context step) - has_task = False - if os.path.isfile('/tmp/pr-context/pr.json'): - import shutil - shutil.copy('/tmp/pr-context/pr.json', '/tmp/self-review/task.json') - has_task = True - # Write manifest so the sub-agent has structured context without - # relying on the main agent to manually pass it in the prompt - manifest_lines = [ - '# Self-Review Context', - '', - 'You are reviewing unpushed changes before they are submitted to a PR.', - '', - '## Available files', - '', - 'All files are in `/tmp/self-review/`:', - '', - f'- `diff.patch` : Full unified diff of all changes ({len(diff_text.splitlines())} lines)', - '- `stat.txt` : File-level change summary', - '- `commits.txt` : Commit messages describing what was changed and why', - '- `notes.md` : Author notes on what was done, why, and key decisions made', - ] - if has_task: - manifest_lines.append('- `task.json` : Original PR context (title, body, author, branches)') - step = 1 - manifest_lines += ['', '## How to review', ''] - manifest_lines.append(f'{step}. Read `notes.md` to understand what the author did, why, and what decisions they made.') - step += 1 - manifest_lines.append(f'{step}. Read `commits.txt` for the commit-level view of what changed.') - step += 1 - if has_task: - manifest_lines.append(f'{step}. Read `task.json` to understand the original task or issue being addressed.') - step += 1 - manifest_lines.append(f'{step}. Read `stat.txt` for a high-level view of which files changed.') - step += 1 - manifest_lines.append(f'{step}. Read `diff.patch` and the relevant source files from the workspace (the branch is checked out).') - step += 1 - if agents_md: - manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') - manifest_lines += [ - '', - '## Focus areas', - '', - 'Look for bugs, logic errors, missed edge cases, and style issues.', - 'Focus on what the author might have MISSED rather than re-deriving their reasoning.', - '', - '## What NOT to flag', - '', - '- Pre-existing issues not introduced by these changes', - '- Style preferences handled by linters or formatters', - '- Theoretical performance concerns without evidence of real-world impact', - ] - with open('/tmp/self-review/README.md', 'w') as f: - f.write('\n'.join(manifest_lines) + '\n') - 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.') - if diff_line_count > 0: - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) and supporting context have been saved to `/tmp/self-review/`. Before spawning the sub-agent, write `/tmp/self-review/notes.md` with: what you changed and why, which files matter most and what they do, edge cases you already handled, and what test coverage exists. Then spawn a `code-review` sub-agent via `runSubagent` and tell it to start by reading `/tmp/self-review/README.md`. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_push_to_pr` again.') - 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-PUSH-TO-PR_EOF - chmod +x /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py - - - name: Generate Safe Inputs MCP Server Config - id: safe-inputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3000 - - # Set outputs for next steps - { - echo "safe_inputs_api_key=${API_KEY}" - echo "safe_inputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Inputs MCP server will run on port ${PORT}" - - - name: Start Safe Inputs MCP HTTP Server - id: safe-inputs-start - env: - DEBUG: '*' - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_INPUTS_PORT - export GH_AW_SAFE_INPUTS_API_KEY - - bash /opt/gh-aw/actions/start_safe_inputs_server.sh - - - name: Start MCP Gateway - id: start-mcp-gateway - env: - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} - GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - run: | - set -eo pipefail - mkdir -p /tmp/gh-aw/mcp-config - mkdir -p /tmp/gh-aw/mcp-logs/playwright - - # Export gateway environment variables for MCP config and gateway script - export MCP_GATEWAY_PORT="80" - export MCP_GATEWAY_DOMAIN="localhost" - MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${MCP_GATEWAY_API_KEY}" - export MCP_GATEWAY_API_KEY - export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" - mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" - export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" - export DEBUG="*" - - export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' - - mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh - { - "mcpServers": { - "github": { - "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v0.31.0", - "env": { - "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", - "GITHUB_READ_ONLY": "1", - "GITHUB_TOOLSETS": "repos,issues,pull_requests,search,actions" - } - }, - "playwright": { - "type": "stdio", - "container": "mcr.microsoft.com/playwright/mcp", - "args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"], - "entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"], - "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"] - }, - "public-code-search": { - "type": "http", - "url": "https://public-code-search.fastmcp.app/mcp", - "tools": [ - "search_code" - ] - }, - "safeinputs": { - "type": "http", - "url": "http://localhost:$GH_AW_SAFE_INPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" - } - }, - "safeoutputs": { - "type": "http", - "url": "http://localhost:$GH_AW_SAFE_OUTPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" - } - } - }, - "gateway": { - "port": $MCP_GATEWAY_PORT, - "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" - } - } - GH_AW_MCP_CONFIG_EOF - - name: Download activation artifact - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: activation - path: /tmp/gh-aw - - name: Clean git credentials - run: bash /opt/gh-aw/actions/clean_git_credentials.sh - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 60 - run: | - set -o pipefail - COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" - mkdir -p /tmp/ - mkdir -p /tmp/gh-aw/ - mkdir -p /tmp/gh-aw/agent/ - mkdir -p /tmp/gh-aw/sandbox/agent/logs/ - copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$COPILOT_CLI_INSTRUCTION" 2>&1 | tee /tmp/gh-aw/agent-stdio.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ inputs.model }} - GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Copy Copilot session state files to logs - if: always() - continue-on-error: true - run: | - # Copy Copilot session state files to logs folder for artifact collection - # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them - SESSION_STATE_DIR="$HOME/.copilot/session-state" - LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" - - if [ -d "$SESSION_STATE_DIR" ]; then - echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" - mkdir -p "$LOGS_DIR" - cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true - echo "Session state files copied successfully" - else - echo "No session-state directory found at $SESSION_STATE_DIR" - fi - - name: Stop MCP Gateway - if: always() - continue-on-error: true - env: - MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} - MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} - GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} - run: | - bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" - - name: Redact secrets in logs - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); - await main(); - env: - GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' - SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload Safe Outputs - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output - path: ${{ env.GH_AW_SAFE_OUTPUTS }} - if-no-files-found: warn - - name: Ingest agent output - id: collect_output - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - 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 }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); - await main(); - - name: Upload sanitized agent output - if: always() && env.GH_AW_AGENT_OUTPUT - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-output - path: ${{ env.GH_AW_AGENT_OUTPUT }} - if-no-files-found: warn - - name: Upload engine output files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent_outputs - path: | - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - if-no-files-found: ignore - - name: Parse agent logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); - await main(); - - name: Parse Safe Inputs logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); - await main(); - - name: Parse MCP Gateway logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); - await main(); - - name: Upload agent artifacts - if: always() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-artifacts - path: | - /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/mcp-logs/ - /tmp/gh-aw/safe-inputs/logs/ - /tmp/gh-aw/agent-stdio.log - /tmp/gh-aw/agent/ - /tmp/gh-aw/aw-*.patch - if-no-files-found: ignore - - conclusion: - needs: - - activation - - agent - - safe_outputs - if: (always()) && (needs.agent.result != 'skipped') - runs-on: ubuntu-slim - permissions: - contents: write - pull-requests: write - outputs: - noop_message: ${{ steps.noop.outputs.noop_message }} - tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} - total_count: ${{ steps.missing_tool.outputs.total_count }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ - - name: Setup agent output environment variable - run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Process No-Op Messages - id: noop - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_NOOP_MAX: "1" - GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/noop.cjs'); - await main(); - - name: Record Missing Tool - id: missing_tool - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); - await main(); - - name: Handle Agent Failure - id: handle_agent_failure - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr-no-sandbox" - GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} - GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} - GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} - GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" - GH_AW_GROUP_REPORTS: "false" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); - await main(); - - name: Handle No-Op Message - id: handle_noop_message - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} - GH_AW_NOOP_REPORT_AS_ISSUE: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); - await main(); - - pre_activation: - runs-on: ubuntu-slim - permissions: - discussions: write - issues: write - pull-requests: write - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - matched_command: '' - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Add eyes reaction for immediate feedback - id: react - if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.id == github.repository_id) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REACTION: "eyes" - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/add_reaction.cjs'); - await main(); - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - - safe_outputs: - needs: - - activation - - agent - if: (!cancelled()) && (needs.agent.result != 'skipped') - runs-on: ubuntu-slim - permissions: - contents: write - pull-requests: write - timeout-minutes: 15 - env: - GH_AW_ENGINE_ID: "copilot" - GH_AW_ENGINE_MODEL: "${{ inputs.model }}" - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" - GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr-no-sandbox" - GH_AW_WORKFLOW_NAME: "Mention in PR (no sandbox)" - outputs: - code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} - code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} - comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} - comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} - create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} - create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} - process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} - process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} - push_commit_sha: ${{ steps.process_safe_outputs.outputs.push_commit_sha }} - push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ - - name: Setup agent output environment variable - run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Download patch artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-artifacts - path: /tmp/gh-aw/ - - name: Checkout repository - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} - token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - persist-credentials: false - fetch-depth: 1 - - name: Configure Git credentials - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Process Safe Outputs - id: process_safe_outputs - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - 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},\"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 }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); - await main(); - - name: Upload safe output items manifest - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output-items - path: /tmp/safe-output-items.jsonl - if-no-files-found: warn - diff --git a/.github/workflows/gh-aw-mention-in-pr.invalid.yml b/.github/workflows/gh-aw-mention-in-pr.invalid.yml deleted file mode 100644 index 0911efa7..00000000 --- a/.github/workflows/gh-aw-mention-in-pr.invalid.yml +++ /dev/null @@ -1,2256 +0,0 @@ -# -# ___ _ _ -# / _ \ | | (_) -# | |_| | __ _ ___ _ __ | |_ _ ___ -# | _ |/ _` |/ _ \ '_ \| __| |/ __| -# | | | | (_| | __/ | | | |_| | (__ -# \_| |_/\__, |\___|_| |_|\__|_|\___| -# __/ | -# _ _ |___/ -# | | | | / _| | -# | | | | ___ _ __ _ __| |_| | _____ ____ -# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| -# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ -# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ -# -# This file was automatically generated by gh-aw. DO NOT EDIT. -# -# To update this file, edit the corresponding .md file and run: -# gh aw compile -# Not all edits will cause changes to this file. -# -# For more information: https://github.github.com/gh-aw/introduction/overview/ -# -# AI assistant for PRs — review, fix code, and push changes on demand -# -# Resolved workflow manifest: -# Imports: -# - gh-aw-fragments/elastic-tools.md -# - gh-aw-fragments/formatting.md -# - gh-aw-fragments/mcp-pagination.md -# - gh-aw-fragments/messages-footer.md -# - gh-aw-fragments/network-ecosystems.md -# - gh-aw-fragments/pick-three-keep-many.md -# - gh-aw-fragments/playwright-mcp-explorer.md -# - gh-aw-fragments/pr-context.md -# - gh-aw-fragments/review-process.md -# - gh-aw-fragments/rigor.md -# - gh-aw-fragments/runtime-setup.md -# - gh-aw-fragments/safe-output-add-comment-pr.md -# - gh-aw-fragments/safe-output-code-review.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 -# - gh-aw-fragments/workflow-edit-guardrails.md -# -# inlined-imports: true -# -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"3313c7ef3e83734c55731f51f5ef44ac2ea89d4da5904fe5b17cdb65d44f42d0"} - -name: "Mention in PR" -"on": - workflow_call: - inputs: - additional-instructions: - default: "" - description: Repo-specific instructions appended to the agent prompt - required: false - type: string - allowed-bot-users: - default: github-actions[bot] - description: Allowlisted bot actor usernames (comma-separated) - required: false - type: string - create-pull-request-review-comment-max: - default: "30" - description: Maximum number of review comments the agent can create per run - required: false - type: string - messages-footer: - default: "" - description: Footer appended to all agent comments and reviews - required: false - type: string - model: - default: gpt-5.3-codex - description: AI model to use - required: false - type: string - prompt: - default: "" - description: Explicit prompt text to run when no comment trigger is present - required: false - type: string - resolve-pull-request-review-thread-max: - default: "10" - description: Maximum number of review threads the agent can resolve per run - required: false - type: string - setup-commands: - default: "" - description: Shell commands to run before the agent starts (dependency install, build, etc.) - required: false - type: string - target-pr-number: - default: "" - description: Explicit PR number to target (used for manual/dispatch triggers) - required: false - type: string - outputs: - comment_id: - description: ID of the first added comment - value: ${{ jobs.safe_outputs.outputs.comment_id }} - comment_url: - description: URL of the first added comment - value: ${{ jobs.safe_outputs.outputs.comment_url }} - push_commit_sha: - description: SHA of the pushed commit - value: ${{ jobs.safe_outputs.outputs.push_commit_sha }} - push_commit_url: - description: URL of the pushed commit - value: ${{ jobs.safe_outputs.outputs.push_commit_url }} - secrets: - COPILOT_GITHUB_TOKEN: - required: true - EXTRA_COMMIT_GITHUB_TOKEN: - required: false - -permissions: {} - -concurrency: - cancel-in-progress: true - group: ${{ github.workflow }}-mention-pr-${{ inputs.target-pr-number || github.event.issue.number }} - -run-name: "Mention in PR" - -jobs: - activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' - runs-on: ubuntu-slim - permissions: - contents: read - discussions: write - issues: write - pull-requests: write - outputs: - body: ${{ steps.sanitized.outputs.body }} - comment_id: "" - comment_repo: "" - model: ${{ steps.generate_aw_info.outputs.model }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} - text: ${{ steps.sanitized.outputs.text }} - title: ${{ steps.sanitized.outputs.title }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Generate agentic run info - id: generate_aw_info - env: - GH_AW_INFO_ENGINE_ID: "copilot" - GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" - GH_AW_INFO_MODEL: "${{ inputs.model }}" - GH_AW_INFO_VERSION: "" - GH_AW_INFO_AGENT_VERSION: "0.0.420" - GH_AW_INFO_WORKFLOW_NAME: "Mention in PR" - GH_AW_INFO_EXPERIMENTAL: "false" - GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" - GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","clojure","cloud.elastic.co","containers","dart","defaults","dotnet","ela.st","elastic.co","elastic.dev","elastic.github.io","elixir","fonts","github","github-actions","go","haskell","java","kotlin","linux-distros","node","node-cdns","perl","php","playwright","public-code-search.fastmcp.app","python","ruby","rust","scala","swift","terraform","www.elastic.co","zig"]' - GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.23.0" - GH_AW_INFO_AWMG_VERSION: "" - GH_AW_INFO_FIREWALL_TYPE: "squid" - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); - await main(core, context); - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Checkout .github and .agents folders - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - .github - .agents - sparse-checkout-cone-mode: true - fetch-depth: 1 - persist-credentials: false - - name: Check workflow file timestamps - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_WORKFLOW_FILE: "gh-aw-mention-in-pr.lock.yml" - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); - await main(); - - name: Compute current body text - id: sanitized - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/compute_text.cjs'); - await main(); - - name: Create prompt with built-in context - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_95B47D04: ${{ inputs.target-pr-number || github.event.issue.number }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} - GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} - run: | - bash /opt/gh-aw/actions/create_prompt_first.sh - { - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat "/opt/gh-aw/prompts/xpia.md" - cat "/opt/gh-aw/prompts/temp_folder_prompt.md" - cat "/opt/gh-aw/prompts/markdown.md" - cat "/opt/gh-aw/prompts/playwright_prompt.md" - 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, 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' - - - The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} - - **actor**: __GH_AW_GITHUB_ACTOR__ - {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} - - **repository**: __GH_AW_GITHUB_REPOSITORY__ - {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} - - **workspace**: __GH_AW_GITHUB_WORKSPACE__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ - {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} - - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ - {{/if}} - - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## MCP Servers - - - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Formatting Guidelines - - - Lead with the most important information — your first sentence should be the key takeaway - - Be concise and actionable — no filler or praise - - Use `
` and `` tags for long sections to keep responses scannable - - Wrap branch names and @-references in backticks to avoid pinging users - - Include code snippets with file paths and line numbers when referencing the codebase - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Rigor - - **Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report.** - - - If you claim something is missing or broken, show the exact evidence in the code — file path, line number, and what you observed. - - If a conclusion depends on assumptions you haven't confirmed, do not assert it. Verify first; if you cannot verify, do not report. - - "I don't know" is better than a wrong answer. `noop` is better than a speculative finding. - - It's worth the time to verify now versus guessing and forcing someone else to verify later. - - Before submitting any output, re-read it as a skeptical reviewer. Ask: "Would a senior engineer on this team find this useful, or would they close it immediately?" If the answer is "close," call `noop` instead. - - Only report findings you would confidently defend in a code review. If you feel the need to hedge with "might," "could," or "possibly," the finding is not ready to file. - - Be thorough, spend the time to investigate and verify. There is no rush. Do your best work. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## MCP Pagination - - MCP tool responses have a **25,000 token limit**. When responses exceed this limit, the call fails and you must retry with pagination — wasting turns and tokens. Use proactive pagination to stay under the limit. - - ### Recommended `perPage` Values - - - **5-10**: For detailed items (PR diffs, files with patches, issues with comments) - - **20-30**: For medium-detail lists (commits, review comments, issue lists) - - **50-100**: For simple list operations (branches, labels, tags) - - ### Pagination Pattern - - When you need all results from a paginated API: - - 1. Fetch the first page with a conservative `perPage` value - 2. Process the results before fetching the next page - 3. Continue fetching pages until you receive fewer results than `perPage` (indicating the last page) - - ### Error Recovery - - If you see an error like: - - `MCP tool response exceeds maximum allowed tokens (25000)` - - `Response too large for tool [tool_name]` - - Retry the same call with a smaller `perPage` value (halve it). - - ### Tips - - - **Start small**: It's better to make multiple small requests than one that fails - - **Fetch incrementally**: Get an overview first, then details for specific items - - **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' - ## Workflow Editing Guardrails - - - Do not modify files under `.github/workflows/`. - - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. - - 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. **Never mention these file paths or on-disk data sources in your responses** — they are internal implementation details invisible to users. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Code Review Reference - - Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md`. Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md`. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Message Footer - - A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Playwright MCP Tools - - You have Playwright MCP tools available for interactive browser automation. - Use these tools to explore the app step by step — do NOT write Node.js scripts. - - ### Available tools - - - `browser_navigate` — go to a URL - - `browser_click` — click an element - - `browser_type` — type text into an input - - `browser_snapshot` — get an accessibility tree (YAML) of the current page - - `browser_take_screenshot` — capture a screenshot - - `browser_console_execute` — run JavaScript in the browser console - - ### Why MCP tools instead of scripts - - MCP tools are interactive: you see the page state after each action and - decide what to do next. This is ideal for exploratory testing where you - need to adapt based on what you find. Scripts are fire-and-forget — if - a selector is wrong, you don't find out until the script fails. - - ### Measuring DOM properties - - For programmatic checks (e.g. element heights, contrast), use - `browser_console_execute`: - - ```javascript - (() => { - const els = document.querySelectorAll('input, button, [role="combobox"], [role="button"]'); - return JSON.stringify(Array.from(els) - .map(el => { - const r = el.getBoundingClientRect(); - return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) }; - }) - .filter(el => el.top > 50 && el.top < 250)); - })() - ``` - - ### Handling failures - - - Do not retry the same action more than twice — the page is in a different state than expected. - - Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page. - - Adapt (different selector, different path) or report the failure as a finding. - - Never claim you verified something you didn't — if it failed and you skipped it, say so. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ### Pick Three, Keep Many - - Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. - - **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - - - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - - The quality criteria and output format you expect - - The specific angle that distinguishes this sub-agent from the others - - Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - - **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. - - **Merge and deduplicate findings** across all sub-agents: - 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. - 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. - 3. Drop any finding that does not meet the verification criteria. - - **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## add-comment Limitations - - - **Body**: Max 65,536 characters (including any footer added by gh-aw). Keep well under this limit. - - **Mentions**: Max 10 `@` mentions per comment. - - **Links**: Max 50 URLs per comment. - - **HTML**: Only safe tags allowed (`details`, `summary`, `code`, `pre`, `blockquote`, `table`, `b`, `em`, `strong`, `h1`–`h6`, `hr`, `br`, `li`, `ol`, `ul`, `p`, `sub`, `sup`). Other tags are converted to parentheses. - - **URLs**: Only HTTPS URLs to allowed domains. Non-HTTPS and non-allowed domains are redacted. - - **Bot triggers**: References like `fixes #123` or `closes #456` are neutralized to prevent unintended issue closures. - - If you exceed 10 mentions or 50 links, the comment will be rejected. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## create-pull-request-review-comment - - - **Required fields**: `path` (file path), `line` (line number), and `body` (comment text). - - **Line**: Must be within the diff — an added or context line in the patch. Must be the **exact line number from reading the file** (not estimated from the patch). Lines outside the diff will fail. - - **Body**: Sanitized with the standard pipeline (mentions neutralized, HTML filtered, URLs restricted). GitHub API limit is ~65,536 characters. - - **Side**: Defaults to `RIGHT` (the new code). Use `LEFT` only when commenting on deleted lines. - - **Suggestion blocks**: Use ` ```suggestion ` fences for concrete code fixes. The suggestion must actually change the code — don't suggest identical code. Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. - - Only flag issues you are confident are real problems — false positives erode trust. Once you have flagged an issue, you cannot unflag it. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## submit-pull-request-review Limitations - - - **Event**: Must be one of `APPROVE`, `REQUEST_CHANGES`, or `COMMENT`. Defaults to `COMMENT` if omitted. - - **Body**: Max 65,000 characters. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. A body is required when event is `REQUEST_CHANGES`. Sanitized (mentions neutralized, HTML filtered, URLs restricted). If you have also used `create-pull-request-review-comment`, you do not need to repeat the same feedback in the body. If you "Approve" and have no comments, do not provide a `body`. - - **Own PRs**: If the workflow actor is also the PR author (e.g., `github-actions[bot]` reviewing its own PR), the event is forced to `COMMENT` regardless of what you specify. `APPROVE` and `REQUEST_CHANGES` will not work. - - **Max per run**: 1 review submission per workflow run. Leave inline comments first, then submit the review as a single final action. - - **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - Before calling `push_to_pull_request_branch`, call `ready_to_push_to_pr` and apply its checklist. - - ## push-to-pull-request-branch Limitations - - - **Patch size**: Max ~10 MB (10,240 KB). Keep changes focused — very large refactors may exceed this. - - **Fork PRs**: Cannot push to fork PR branches. Check via `pull_request_read` with method `get` whether the PR head repo differs from the base repo. If it's a fork, explain that you cannot push and suggest the author apply changes themselves. - - **Committed changes required**: You must have locally committed changes before calling push. Uncommitted or staged-only changes will fail. - - **Branch**: Pushes to the PR's head branch. The workspace must have the PR branch checked out. - - You may not submit code that modifies files in `.github/workflows/`. Doing so will cause the submission to be rejected. If asked to modify workflow files, propose the change in a copy placed in a `github/` folder (without the leading period) and note in the PR that the file needs to be relocated by someone with workflow write access. - - Trying to resolve merge conflicts? Do not use `git merge` or `git rebase` — `push_to_pull_request_branch` uses `git format-patch` which requires single-parent commits. Instead: - 1. Compare with the base branch (from `/tmp/pr-context/pr.json` field `baseRefName`) to see what changed in the conflicting files - 2. Edit the files directly to incorporate the changes from the base branch - 3. Commit the changes as regular (single-parent) commits - 4. Call `ready_to_push_to_pr` (which will catch any merge commits) and then `push_to_pull_request_branch` to push - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## resolve-pull-request-review-thread Limitations - - - **Required field**: `thread_id` — the GraphQL node ID of the review thread (e.g., `PRRT_kwDO...`). This is the `id` field from `get_review_comments`, not the numeric REST comment ID. - - **Only resolve what you've addressed**: Do not resolve threads you skipped, disagreed with, or didn't fix. Only resolve threads where your changes directly address the feedback. - - **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 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. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - # PR Assistant - - Assist with pull requests on __GH_AW_GITHUB_REPOSITORY__ — review code, fix issues, answer questions, and push changes. - - ## Context - - - **Repository**: __GH_AW_GITHUB_REPOSITORY__ - - **PR**: #__GH_AW_EXPR_95B47D04__ — __GH_AW_GITHUB_EVENT_ISSUE_TITLE__ - - **PR context on disk**: `/tmp/pr-context/` — PR metadata, diff, files, reviews, comments, and linked issues are pre-fetched. Use these as your primary source; fall back to API tools only when required data is unavailable. - - **Request**: "__GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT__" - - **Explicit prompt**: "__GH_AW_INPUTS_PROMPT__" - - ## Constraints - - - **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 - - ## Instructions - - Understand the request, investigate the code, and respond appropriately. - - ### 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. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). - 2. Read `/tmp/pr-context/issue-*.json` if any exist to understand linked issue motivation and requirements. - 3. Read `/tmp/pr-context/comments.json` and `/tmp/pr-context/review_comments.json` to understand the conversation context and what's being asked. - 4. Do not modify, review, comment on, or resolve threads for any PR other than #__GH_AW_EXPR_95B47D04__. - - ### Step 2: Handle the Request - - Based on what's asked, do the appropriate thing. **Requests can combine multiple actions** (e.g., "fix merge conflicts and address the review feedback"). When they do, handle them in this order: merge conflicts first, then code changes/review feedback, then push once at the end. Do not push between steps — batch all changes into a single push. - - **If asked to review the PR:** - - Call `ready_to_code_review` to prepare the review approach based on PR size. - - Read `/tmp/pr-context/reviews.json` and `/tmp/pr-context/review_comments.json` to check prior reviews and existing threads — do not duplicate feedback. - - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. For small PRs, review directly. For medium/large PRs, spawn the specified number of `code-review` sub-agents in parallel (each reads its `/tmp/pr-context/subagent-*.md` instruction file). - - When sub-agents return findings, merge and deduplicate per the Pick Three, Keep Many process. Then verify each surviving finding before leaving a comment: - 1. **Read the file and surrounding context** — open the full file, not just the diff. - 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. - 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If unsure, drop it. - 4. **Check existing threads** — if this issue was already flagged (resolved or unresolved), do not duplicate. - - Leave inline comments per the **Code Review Reference** for each finding that survives verification. Then call `submit_pull_request_review`. - - **Important**: Substantive feedback belongs in the PR review (inline comments + review submission), NOT in a reply comment. Do NOT add a separate comment after submitting the review unless the user explicitly asked for a comment or the review submission failed. - - **Bot-authored PRs**: If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. - - **If asked to fix code or address review feedback:** - - Read `/tmp/pr-context/unresolved_threads.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. - - 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. - - Use `push_to_pull_request_branch` to push your changes. - - 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/unresolved_threads.json` for all unresolved threads — also check `/tmp/pr-context/outdated_threads.json` for threads where the underlying code changed since the comment was made and verify 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 to fix merge conflicts:** - - Check via `pull_request_read` (method `get`) whether this is a fork PR. If so, reply that you cannot push to fork branches and suggest the author resolve locally. - - Read `/tmp/pr-context/pr.json` for the head and base branch names. - - Identify conflicting files by comparing with the base branch, then edit each file directly to produce the correct merged result. Commit the resolved changes as regular (single-parent) commits — do not use `git merge` or `git rebase` (the `ready_to_push_to_pr` check will catch merge commits before pushing). - - If conflicts are too complex to resolve confidently (large structural changes, binary files, ambiguous intent), reply explaining what you found and suggest the author resolve locally. - - If the request includes additional work (code fixes, review feedback), complete all of it before pushing — `push_to_pull_request_branch` can only be called once. Resolve merge conflicts first, then make other requested changes on top, then push everything together. - - Use `push_to_pull_request_branch` and reply summarizing what was resolved and how conflicts were handled. - - **If asked a question about the code:** - - Find the relevant code and explain it. - - Use `grep` and file reading to gather context. - - Use `web-fetch` to look up documentation when needed. - - **If the request is unclear:** - - Ask clarifying questions rather than guessing. - - ### Step 3: Respond - - If you did not submit a PR review, call `add_comment` with your response. If you submitted a review, do NOT call `add_comment` unless explicitly requested or to report a review submission failure. - - **Additional tools:** - - `push_to_pull_request_branch` — push committed changes to the PR branch (same-repo PRs only) - - `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__ - - GH_AW_PROMPT_EOF - } > "$GH_AW_PROMPT" - - name: Interpolate variables and render templates - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} - GH_AW_EXPR_95B47D04: ${{ inputs.target-pr-number || github.event.issue.number }} - GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); - await main(); - - name: Substitute placeholders - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_EXPR_7F2A702A: ${{ inputs.resolve-pull-request-review-thread-max }} - GH_AW_EXPR_95B47D04: ${{ inputs.target-pr-number || github.event.issue.number }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_ISSUE_TITLE: ${{ github.event.issue.title }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_INPUTS_PROMPT: ${{ inputs.prompt }} - GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} - GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - - const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); - - // Call the substitution function - return await substitutePlaceholders({ - file: process.env.GH_AW_PROMPT, - substitutions: { - GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, - GH_AW_EXPR_7F2A702A: process.env.GH_AW_EXPR_7F2A702A, - GH_AW_EXPR_95B47D04: process.env.GH_AW_EXPR_95B47D04, - GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_TITLE: process.env.GH_AW_GITHUB_EVENT_ISSUE_TITLE, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, - GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, - GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, - GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, - GH_AW_INPUTS_PROMPT: process.env.GH_AW_INPUTS_PROMPT, - GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED, - GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: process.env.GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT - } - }); - - name: Validate prompt placeholders - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh - - name: Print prompt - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/print_prompt_summary.sh - - name: Upload activation artifact - if: success() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: activation - path: | - /tmp/gh-aw/aw_info.json - /tmp/gh-aw/aw-prompts/prompt.txt - retention-days: 1 - - agent: - needs: activation - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - issues: read - pull-requests: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}-mention-pr-${{ inputs.target-pr-number || github.event.issue.number }}" - env: - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - GH_AW_ASSETS_ALLOWED_EXTS: "" - GH_AW_ASSETS_BRANCH: "" - GH_AW_ASSETS_MAX_SIZE_KB: 0 - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_WORKFLOW_ID_SANITIZED: ghawmentioninpr - outputs: - checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} - detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} - detection_success: ${{ steps.detection_conclusion.outputs.success }} - has_patch: ${{ steps.collect_output.outputs.has_patch }} - model: ${{ needs.activation.outputs.model }} - output: ${{ steps.collect_output.outputs.output }} - output_types: ${{ steps.collect_output.outputs.output_types }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Create gh-aw temp directory - run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - - if: hashFiles('.python-version') != '' - name: Setup Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version-file: .python-version - - if: hashFiles('.node-version') != '' - name: Setup Node.js (.node-version) - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 - with: - node-version-file: .node-version - - if: hashFiles('.node-version') == '' && hashFiles('.nvmrc') != '' - name: Setup Node.js (.nvmrc) - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 - with: - node-version-file: .nvmrc - - if: hashFiles('.ruby-version') != '' - name: Setup Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 - with: - bundler-cache: true - ruby-version: .ruby-version - - id: setup-uv - if: hashFiles('pyproject.toml', 'uv.lock') != '' - name: Setup uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 - - env: - UV_PATH: ${{ steps.setup-uv.outputs.uv-path }} - WORKSPACE: ${{ github.workspace }} - if: hashFiles('pyproject.toml', 'uv.lock') != '' - name: Expose uv in workspace - run: | - set -euo pipefail - install_dir="$WORKSPACE/.gh-aw-tools/bin" - mkdir -p "$install_dir" - cp "$UV_PATH" "$install_dir/uv" - chmod +x "$install_dir/uv" - echo "$install_dir" >> "$GITHUB_PATH" - shell: bash - - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" - shell: bash - - env: - GITHUB_REPOSITORY: ${{ github.repository }} - name: Fetch repository conventions - run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" - 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: - GITHUB_TOKEN: ${{ github.token }} - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - name: Ensure origin refs for PR patch generation - run: "SERVER_URL_STRIPPED=\"${SERVER_URL#https://}\"\ngit remote set-url origin \"https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git\"\ngit fetch --no-tags --prune origin '+refs/heads/*:refs/remotes/origin/*'\n" - - env: - SETUP_COMMANDS: ${{ inputs.setup-commands }} - if: ${{ inputs.setup-commands != '' }} - name: Repo-specific setup - run: eval "$SETUP_COMMANDS" - - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Checkout PR branch - id: checkout-pr - if: | - (github.event.pull_request) || (github.event.issue.pull_request) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); - await main(); - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420 - - name: Install awf binary - run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 - - name: Determine automatic lockdown mode for GitHub MCP Server - id: determine-automatic-lockdown - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - with: - script: | - const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); - await determineAutomaticLockdown(github, context, core); - - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.6 ghcr.io/github/github-mcp-server:v0.31.0 mcr.microsoft.com/playwright/mcp node:lts-alpine - - name: Write Safe Outputs Config - run: | - mkdir -p /opt/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":1},"create_pull_request_review_comment":{"max":"${{ inputs.create-pull-request-review-comment-max }}"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_to_pull_request_branch":{"max":0},"resolve_pull_request_review_thread":{"max":"${{ inputs.resolve-pull-request-review-thread-max }}"},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_EOF - cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' - [ - { - "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 1 comment(s) can be added.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.", - "type": "string" - }, - "item_number": { - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the comment will be silently discarded.", - "type": "number" - } - }, - "required": [ - "body" - ], - "type": "object" - }, - "name": "add_comment" - }, - { - "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Comments will be on the RIGHT side of the diff.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", - "type": "string" - }, - "line": { - "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", - "type": [ - "number", - "string" - ] - }, - "path": { - "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", - "type": "string" - }, - "side": { - "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", - "enum": [ - "LEFT", - "RIGHT" - ], - "type": "string" - }, - "start_line": { - "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", - "type": [ - "number", - "string" - ] - } - }, - "required": [ - "path", - "line", - "body" - ], - "type": "object" - }, - "name": "create_pull_request_review_comment" - }, - { - "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", - "type": "string" - }, - "event": { - "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", - "enum": [ - "APPROVE", - "REQUEST_CHANGES", - "COMMENT" - ], - "type": "string" - } - }, - "type": "object" - }, - "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": { - "additionalProperties": false, - "properties": { - "thread_id": { - "description": "The node ID of the review thread to resolve (e.g., 'PRRT_kwDOABCD...'). This is the GraphQL node ID, not a numeric ID.", - "type": "string" - } - }, - "required": [ - "thread_id" - ], - "type": "object" - }, - "name": "resolve_pull_request_review_thread" - }, - { - "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "branch": { - "description": "Branch name to push changes from. If omitted, uses the current working branch. Only specify if you need to push from a different branch.", - "type": "string" - }, - "message": { - "description": "Commit message describing the changes. Follow repository commit message conventions (e.g., conventional commits).", - "type": "string" - }, - "pull_request_number": { - "description": "Pull request number to push changes to. This is the numeric ID from the GitHub URL (e.g., 654 in github.com/owner/repo/pull/654). Required when the workflow target is '*' (any PR).", - "type": [ - "number", - "string" - ] - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "name": "push_to_pull_request_branch" - }, - { - "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "reason": { - "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", - "type": "string" - }, - "tool": { - "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", - "type": "string" - } - }, - "required": [ - "reason" - ], - "type": "object" - }, - "name": "missing_tool" - }, - { - "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "message": { - "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "name": "noop" - }, - { - "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "context": { - "description": "Additional context about the missing data or where it should come from (max 256 characters).", - "type": "string" - }, - "data_type": { - "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", - "type": "string" - }, - "reason": { - "description": "Explanation of why this data is needed to complete the task (max 256 characters).", - "type": "string" - } - }, - "required": [], - "type": "object" - }, - "name": "missing_data" - } - ] - GH_AW_SAFE_OUTPUTS_TOOLS_EOF - cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' - { - "add_comment": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "item_number": { - "issueOrPRNumber": true - }, - "repo": { - "type": "string", - "maxLength": 256 - } - } - }, - "create_pull_request_review_comment": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "line": { - "required": true, - "positiveInteger": true - }, - "path": { - "required": true, - "type": "string" - }, - "repo": { - "type": "string", - "maxLength": 256 - }, - "side": { - "type": "string", - "enum": [ - "LEFT", - "RIGHT" - ] - }, - "start_line": { - "optionalPositiveInteger": true - } - }, - "customValidation": "startLineLessOrEqualLine" - }, - "missing_data": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "context": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "data_type": { - "type": "string", - "sanitize": true, - "maxLength": 128 - }, - "reason": { - "type": "string", - "sanitize": true, - "maxLength": 256 - } - } - }, - "missing_tool": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 512 - }, - "reason": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "tool": { - "type": "string", - "sanitize": true, - "maxLength": 128 - } - } - }, - "noop": { - "defaultMax": 1, - "fields": { - "message": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - } - } - }, - "push_to_pull_request_branch": { - "defaultMax": 1, - "fields": { - "branch": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "message": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "pull_request_number": { - "issueOrPRNumber": true - } - } - }, - "resolve_pull_request_review_thread": { - "defaultMax": 10, - "fields": { - "thread_id": { - "required": true, - "type": "string" - } - } - }, - "submit_pull_request_review": { - "defaultMax": 1, - "fields": { - "body": { - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "event": { - "type": "string", - "enum": [ - "APPROVE", - "REQUEST_CHANGES", - "COMMENT" - ] - } - } - } - } - GH_AW_SAFE_OUTPUTS_VALIDATION_EOF - - name: Generate Safe Outputs MCP Server Config - id: safe-outputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3001 - - # Set outputs for next steps - { - echo "safe_outputs_api_key=${API_KEY}" - echo "safe_outputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Outputs MCP server will run on port ${PORT}" - - - name: Start Safe Outputs MCP HTTP Server - id: safe-outputs-start - env: - DEBUG: '*' - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_OUTPUTS_PORT - export GH_AW_SAFE_OUTPUTS_API_KEY - export GH_AW_SAFE_OUTPUTS_TOOLS_PATH - export GH_AW_SAFE_OUTPUTS_CONFIG_PATH - export GH_AW_MCP_LOG_DIR - - bash /opt/gh-aw/actions/start_safe_outputs_server.sh - - - name: Setup Safe Inputs Config - run: | - mkdir -p /opt/gh-aw/safe-inputs/logs - cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' - { - "serverName": "safeinputs", - "version": "1.0.0", - "logDir": "/opt/gh-aw/safe-inputs/logs", - "tools": [ - { - "name": "ready-to-code-review", - "description": "Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/", - "inputSchema": { - "properties": {}, - "type": "object" - }, - "handler": "ready-to-code-review.py", - "timeout": 60 - }, - { - "name": "ready-to-push-to-pr", - "description": "Run the PR readiness checklist before pushing to a PR", - "inputSchema": { - "properties": {}, - "type": "object" - }, - "handler": "ready-to-push-to-pr.py", - "timeout": 60 - } - ] - } - GH_AW_SAFE_INPUTS_TOOLS_EOF - cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' - const path = require("path"); - const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); - const configPath = path.join(__dirname, "tools.json"); - const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); - const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; - startHttpServer(configPath, { - port: port, - stateless: true, - logDir: "/opt/gh-aw/safe-inputs/logs" - }).catch(error => { - console.error("Failed to start safe-inputs HTTP server:", error); - process.exit(1); - }); - GH_AW_SAFE_INPUTS_SERVER_EOF - chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs - - - name: Setup Safe Inputs Tool Files - run: | - cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' - #!/usr/bin/env python3 - # Auto-generated safe-input tool: ready-to-code-review - # Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/ - - import json - import os - import sys - - # Read inputs from stdin (JSON format) - try: - inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} - except (json.JSONDecodeError, Exception): - inputs = {} - - # User code: - import os, json, re - - os.makedirs('/tmp/pr-context', exist_ok=True) - - # Read PR size written by the pr-context step - try: - with open('/tmp/pr-context/pr-size.txt') as f: - pr_size = f.read().strip() - m = re.search(r', (\d+) diff', pr_size) - diff_lines = int(m.group(1)) if m else 0 - except Exception: - pr_size = 'unknown size' - diff_lines = 0 - - # Write one instruction file per sub-agent file ordering strategy - for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: - lines = [ - '# PR Review Sub-Agent', - '', - 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', - '', - '## Instructions', - '', - 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', - '', - '## Context', - '', - '- Repository conventions: `/tmp/agents.md` (skip if missing)', - '- PR details: `/tmp/pr-context/pr.json`', - '- All context files: `/tmp/pr-context/README.md`', - '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', - '- Full file contents: read from the workspace (PR branch is checked out)', - '', - '## Your File Order', - '', - f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', - ] - with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: - f.write('\n'.join(lines) + '\n') - - # Determine review approach based on PR size - if diff_lines < 200: - approach_lines = [ - f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', - ] - size_key = 'small' - elif diff_lines < 800: - approach_lines = [ - f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', - '', - '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', - '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', - '', - 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', - ] - size_key = 'medium' - else: - approach_lines = [ - f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', - '', - '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', - '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', - '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', - '', - 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', - ] - size_key = 'large' - - with open('/tmp/pr-context/agent-review.md', 'w') as f: - f.write('\n'.join(approach_lines) + '\n') - - # Write parent-agent comment format and threshold instructions - t3 = chr(96) * 3 - t5 = chr(96) * 5 - threshold = '${{ inputs.minimum_severity || "low" }}' - parent_lines = [ - '# Code Review: Comment Format and Threshold', - '', - '## Comment Format', - '', - 'Call **`create_pull_request_review_comment`** with:', - '- The file path and the **exact line number from reading the file** (not estimated from the diff)', - '- The line must be within the diff (an added or context line in the patch)', - '', - t5, - '**[SEVERITY] Brief title**', - '', - 'Description of the issue and why it matters.', - '', - f'{t3}suggestion', - 'corrected code here', - t3, - t5, - '', - 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', - '', - '## Inline Comment Threshold', - '', - f'The minimum severity for inline comments is `{threshold}`.', - '', - 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', - '', - 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', - '', - 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', - ] - with open('/tmp/pr-context/parent-review.md', 'w') as f: - f.write('\n'.join(parent_lines) + '\n') - - print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) - - GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF - chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py - cat > /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-PUSH-TO-PR_EOF' - #!/usr/bin/env python3 - # Auto-generated safe-input tool: ready-to-push-to-pr - # Run the PR readiness checklist before pushing to a PR - - import json - import os - import sys - - # Read inputs from stdin (JSON format) - try: - inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} - except (json.JSONDecodeError, Exception): - inputs = {} - - # User code: - import os, json, subprocess - def find(*paths): - return next((p for p in paths if os.path.isfile(p)), None) - def run(cmd): - 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') - - # Guard: detect history rewrites and merge commits - pr_json_path = '/tmp/pr-context/pr.json' - if os.path.isfile(pr_json_path): - with open(pr_json_path) as f: - pr_data = json.load(f) - pr_head_sha = pr_data.get('headRefOid', '') - if pr_head_sha: - # Check 1: PR head must be an ancestor of HEAD (no rebase/reset) - anc = run(['git', 'merge-base', '--is-ancestor', pr_head_sha, 'HEAD']) - if anc.returncode != 0: - print(json.dumps({'status': 'error', 'error': f'History rewrite detected: the original PR head ({pr_head_sha[:12]}) is not an ancestor of HEAD. This means git rebase, reset, or cherry-pick rewrote history. push_to_pull_request_branch will fail. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch to its original head, then re-apply your changes as direct file edits and commit as regular commits.'})) - raise SystemExit(0) - # Check 2: no merge commits (multiple parents) since PR head - log = run(['git', 'rev-list', '--min-parents=2', f'{pr_head_sha}..HEAD']) - if log.returncode != 0: - print(json.dumps({'status': 'error', 'error': f'Failed to check for merge commits (git rev-list exited {log.returncode}): {log.stderr.strip()}. Cannot verify commit history is safe for push.'})) - raise SystemExit(0) - merge_shas = log.stdout.strip() - if merge_shas: - print(json.dumps({'status': 'error', 'error': f'Merge commit(s) detected: {merge_shas.splitlines()[0][:12]}... push_to_pull_request_branch uses git format-patch which breaks on merge commits. Fix: run `git reset --hard {pr_head_sha}` to restore the PR branch, then re-apply your changes as direct file edits (no git merge/rebase/commit-tree with multiple -p flags) and commit as regular single-parent commits.'})) - raise SystemExit(0) - - 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') - agents_md = find('AGENTS.md', 'agents.md', '.github/agents.md', '.github/AGENTS.md') - # Generate diff of all local changes vs upstream for self-review - # Try --merge-base (vs common ancestor), fall back to - # @{upstream} 2-dot (vs upstream tip), 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 - # Capture commit messages so the sub-agent knows what changed and why - commits_text = '' - for log_cmd in [ - ['git', 'log', '--format=### %s%n%n%b', '@{upstream}..HEAD'], - ['git', 'log', '--format=### %s%n%n%b', '-10'], - ]: - result = run(log_cmd) - if result.stdout.strip(): - commits_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) - with open('/tmp/self-review/stat.txt', 'w') as f: - f.write(stat_text) - with open('/tmp/self-review/commits.txt', 'w') as f: - f.write(commits_text) - # Copy task context if available (PR metadata from the pr-context step) - has_task = False - if os.path.isfile('/tmp/pr-context/pr.json'): - import shutil - shutil.copy('/tmp/pr-context/pr.json', '/tmp/self-review/task.json') - has_task = True - # Write manifest so the sub-agent has structured context without - # relying on the main agent to manually pass it in the prompt - manifest_lines = [ - '# Self-Review Context', - '', - 'You are reviewing unpushed changes before they are submitted to a PR.', - '', - '## Available files', - '', - 'All files are in `/tmp/self-review/`:', - '', - f'- `diff.patch` : Full unified diff of all changes ({len(diff_text.splitlines())} lines)', - '- `stat.txt` : File-level change summary', - '- `commits.txt` : Commit messages describing what was changed and why', - '- `notes.md` : Author notes on what was done, why, and key decisions made', - ] - if has_task: - manifest_lines.append('- `task.json` : Original PR context (title, body, author, branches)') - step = 1 - manifest_lines += ['', '## How to review', ''] - manifest_lines.append(f'{step}. Read `notes.md` to understand what the author did, why, and what decisions they made.') - step += 1 - manifest_lines.append(f'{step}. Read `commits.txt` for the commit-level view of what changed.') - step += 1 - if has_task: - manifest_lines.append(f'{step}. Read `task.json` to understand the original task or issue being addressed.') - step += 1 - manifest_lines.append(f'{step}. Read `stat.txt` for a high-level view of which files changed.') - step += 1 - manifest_lines.append(f'{step}. Read `diff.patch` and the relevant source files from the workspace (the branch is checked out).') - step += 1 - if agents_md: - manifest_lines.append(f'{step}. Read `{agents_md}` in the workspace for repository coding conventions.') - manifest_lines += [ - '', - '## Focus areas', - '', - 'Look for bugs, logic errors, missed edge cases, and style issues.', - 'Focus on what the author might have MISSED rather than re-deriving their reasoning.', - '', - '## What NOT to flag', - '', - '- Pre-existing issues not introduced by these changes', - '- Style preferences handled by linters or formatters', - '- Theoretical performance concerns without evidence of real-world impact', - ] - with open('/tmp/self-review/README.md', 'w') as f: - f.write('\n'.join(manifest_lines) + '\n') - 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.') - if diff_line_count > 0: - checklist.append(f'A diff of your unpushed changes ({diff_line_count} lines) and supporting context have been saved to `/tmp/self-review/`. Before spawning the sub-agent, write `/tmp/self-review/notes.md` with: what you changed and why, which files matter most and what they do, edge cases you already handled, and what test coverage exists. Then spawn a `code-review` sub-agent via `runSubagent` and tell it to start by reading `/tmp/self-review/README.md`. If the sub-agent finds legitimate issues, fix them, commit, and call `ready_to_push_to_pr` again.') - 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-PUSH-TO-PR_EOF - chmod +x /opt/gh-aw/safe-inputs/ready-to-push-to-pr.py - - - name: Generate Safe Inputs MCP Server Config - id: safe-inputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3000 - - # Set outputs for next steps - { - echo "safe_inputs_api_key=${API_KEY}" - echo "safe_inputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Inputs MCP server will run on port ${PORT}" - - - name: Start Safe Inputs MCP HTTP Server - id: safe-inputs-start - env: - DEBUG: '*' - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_INPUTS_PORT - export GH_AW_SAFE_INPUTS_API_KEY - - bash /opt/gh-aw/actions/start_safe_inputs_server.sh - - - name: Start MCP Gateway - id: start-mcp-gateway - env: - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} - GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - run: | - set -eo pipefail - mkdir -p /tmp/gh-aw/mcp-config - mkdir -p /tmp/gh-aw/mcp-logs/playwright - - # Export gateway environment variables for MCP config and gateway script - export MCP_GATEWAY_PORT="80" - export MCP_GATEWAY_DOMAIN="host.docker.internal" - MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${MCP_GATEWAY_API_KEY}" - export MCP_GATEWAY_API_KEY - export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" - mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" - export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" - export DEBUG="*" - - export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' - - mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh - { - "mcpServers": { - "github": { - "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v0.31.0", - "env": { - "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", - "GITHUB_READ_ONLY": "1", - "GITHUB_TOOLSETS": "repos,issues,pull_requests,search,actions" - } - }, - "playwright": { - "type": "stdio", - "container": "mcr.microsoft.com/playwright/mcp", - "args": ["--init", "--network", "host", "--security-opt", "seccomp=unconfined", "--ipc=host"], - "entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--no-sandbox"], - "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"] - }, - "public-code-search": { - "type": "http", - "url": "https://public-code-search.fastmcp.app/mcp", - "tools": [ - "search_code" - ] - }, - "safeinputs": { - "type": "http", - "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" - } - }, - "safeoutputs": { - "type": "http", - "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" - } - } - }, - "gateway": { - "port": $MCP_GATEWAY_PORT, - "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" - } - } - GH_AW_MCP_CONFIG_EOF - - name: Download activation artifact - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: activation - path: /tmp/gh-aw - - name: Clean git credentials - run: bash /opt/gh-aw/actions/clean_git_credentials.sh - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 60 - run: | - set -o pipefail - # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ inputs.model }} - GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Copy Copilot session state files to logs - if: always() - continue-on-error: true - run: | - # Copy Copilot session state files to logs folder for artifact collection - # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them - SESSION_STATE_DIR="$HOME/.copilot/session-state" - LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" - - if [ -d "$SESSION_STATE_DIR" ]; then - echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" - mkdir -p "$LOGS_DIR" - cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true - echo "Session state files copied successfully" - else - echo "No session-state directory found at $SESSION_STATE_DIR" - fi - - name: Stop MCP Gateway - if: always() - continue-on-error: true - env: - MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} - MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} - GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} - run: | - bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" - - name: Redact secrets in logs - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); - await main(); - env: - GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' - SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload Safe Outputs - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output - path: ${{ env.GH_AW_SAFE_OUTPUTS }} - if-no-files-found: warn - - name: Ingest agent output - id: collect_output - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - 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 }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); - await main(); - - name: Upload sanitized agent output - if: always() && env.GH_AW_AGENT_OUTPUT - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-output - path: ${{ env.GH_AW_AGENT_OUTPUT }} - if-no-files-found: warn - - name: Upload engine output files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent_outputs - path: | - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - if-no-files-found: ignore - - name: Parse agent logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); - await main(); - - name: Parse Safe Inputs logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); - await main(); - - name: Parse MCP Gateway logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); - await main(); - - name: Print firewall logs - if: always() - continue-on-error: true - env: - AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs - run: | - # Fix permissions on firewall logs so they can be uploaded as artifacts - # AWF runs with sudo, creating files owned by root - sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true - # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) - if command -v awf &> /dev/null; then - awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" - else - echo 'AWF binary not installed, skipping firewall log summary' - fi - - name: Upload agent artifacts - if: always() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-artifacts - path: | - /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/mcp-logs/ - /tmp/gh-aw/safe-inputs/logs/ - /tmp/gh-aw/sandbox/firewall/logs/ - /tmp/gh-aw/agent-stdio.log - /tmp/gh-aw/agent/ - /tmp/gh-aw/aw-*.patch - if-no-files-found: ignore - # --- Threat Detection (inline) --- - - name: Check if detection needed - id: detection_guard - if: always() - env: - OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} - HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} - run: | - if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then - echo "run_detection=true" >> "$GITHUB_OUTPUT" - echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" - else - echo "run_detection=false" >> "$GITHUB_OUTPUT" - echo "Detection skipped: no agent outputs or patches to analyze" - fi - - name: Clear MCP configuration for detection - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - rm -f /tmp/gh-aw/mcp-config/mcp-servers.json - rm -f /home/runner/.copilot/mcp-config.json - rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" - - name: Prepare threat detection files - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - mkdir -p /tmp/gh-aw/threat-detection/aw-prompts - cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true - cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true - for f in /tmp/gh-aw/aw-*.patch; do - [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true - done - echo "Prepared threat detection files:" - ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true - - name: Setup threat detection - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - WORKFLOW_NAME: "Mention in PR" - WORKFLOW_DESCRIPTION: "AI assistant for PRs — review, fix code, and push changes on demand" - HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); - await main(); - - name: Ensure threat-detection directory and log - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - mkdir -p /tmp/gh-aw/threat-detection - touch /tmp/gh-aw/threat-detection/detection.log - - name: Execute GitHub Copilot CLI - if: always() && steps.detection_guard.outputs.run_detection == 'true' - id: detection_agentic_execution - # Copilot CLI tool arguments (sorted): - # --allow-tool shell(cat) - # --allow-tool shell(grep) - # --allow-tool shell(head) - # --allow-tool shell(jq) - # --allow-tool shell(ls) - # --allow-tool shell(tail) - # --allow-tool shell(wc) - timeout-minutes: 20 - run: | - set -o pipefail - # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ inputs.model }} - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Parse threat detection results - id: parse_detection_results - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); - await main(); - - name: Upload threat detection log - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: threat-detection.log - path: /tmp/gh-aw/threat-detection/detection.log - if-no-files-found: ignore - - name: Set detection conclusion - id: detection_conclusion - if: always() - env: - RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} - DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} - run: | - if [[ "$RUN_DETECTION" != "true" ]]; then - echo "conclusion=skipped" >> "$GITHUB_OUTPUT" - echo "success=true" >> "$GITHUB_OUTPUT" - echo "Detection was not needed, marking as skipped" - elif [[ "$DETECTION_SUCCESS" == "true" ]]; then - echo "conclusion=success" >> "$GITHUB_OUTPUT" - echo "success=true" >> "$GITHUB_OUTPUT" - echo "Detection passed successfully" - else - echo "conclusion=failure" >> "$GITHUB_OUTPUT" - echo "success=false" >> "$GITHUB_OUTPUT" - echo "Detection found issues" - fi - - conclusion: - needs: - - activation - - agent - - safe_outputs - if: (always()) && (needs.agent.result != 'skipped') - runs-on: ubuntu-slim - permissions: - contents: write - pull-requests: write - outputs: - noop_message: ${{ steps.noop.outputs.noop_message }} - tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} - total_count: ${{ steps.missing_tool.outputs.total_count }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ - - name: Setup agent output environment variable - run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Process No-Op Messages - id: noop - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_NOOP_MAX: "1" - GH_AW_WORKFLOW_NAME: "Mention in PR" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/noop.cjs'); - await main(); - - name: Record Missing Tool - id: missing_tool - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); - await main(); - - name: Handle Agent Failure - id: handle_agent_failure - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr" - GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} - GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} - GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} - GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" - GH_AW_GROUP_REPORTS: "false" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); - await main(); - - name: Handle No-Op Message - id: handle_noop_message - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "Mention in PR" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} - GH_AW_NOOP_REPORT_AS_ISSUE: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); - await main(); - - pre_activation: - runs-on: ubuntu-slim - permissions: - discussions: write - issues: write - pull-requests: write - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - matched_command: '' - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Add eyes reaction for immediate feedback - id: react - if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.id == github.repository_id) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REACTION: "eyes" - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/add_reaction.cjs'); - await main(); - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - - safe_outputs: - needs: - - activation - - agent - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') - runs-on: ubuntu-slim - permissions: - contents: write - pull-requests: write - timeout-minutes: 15 - env: - GH_AW_ENGINE_ID: "copilot" - GH_AW_ENGINE_MODEL: "${{ inputs.model }}" - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" - GH_AW_WORKFLOW_ID: "gh-aw-mention-in-pr" - GH_AW_WORKFLOW_NAME: "Mention in PR" - outputs: - code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} - code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} - comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} - comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} - create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} - create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} - process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} - process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} - push_commit_sha: ${{ steps.process_safe_outputs.outputs.push_commit_sha }} - push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ - - name: Setup agent output environment variable - run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Download patch artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-artifacts - path: /tmp/gh-aw/ - - name: Checkout repository - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} - token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - persist-credentials: false - fetch-depth: 1 - - name: Configure Git credentials - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Process Safe Outputs - id: process_safe_outputs - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - 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},\"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 }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); - await main(); - - name: Upload safe output items manifest - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output-items - path: /tmp/safe-output-items.jsonl - if-no-files-found: warn - diff --git a/.github/workflows/gh-aw-pr-review.invalid.yml b/.github/workflows/gh-aw-pr-review.invalid.yml deleted file mode 100644 index 5058e93e..00000000 --- a/.github/workflows/gh-aw-pr-review.invalid.yml +++ /dev/null @@ -1,1713 +0,0 @@ -# -# ___ _ _ -# / _ \ | | (_) -# | |_| | __ _ ___ _ __ | |_ _ ___ -# | _ |/ _` |/ _ \ '_ \| __| |/ __| -# | | | | (_| | __/ | | | |_| | (__ -# \_| |_/\__, |\___|_| |_|\__|_|\___| -# __/ | -# _ _ |___/ -# | | | | / _| | -# | | | | ___ _ __ _ __| |_| | _____ ____ -# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| -# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ -# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ -# -# This file was automatically generated by gh-aw. DO NOT EDIT. -# -# To update this file, edit the corresponding .md file and run: -# gh aw compile -# Not all edits will cause changes to this file. -# -# For more information: https://github.github.com/gh-aw/introduction/overview/ -# -# AI code review with inline comments on pull requests -# -# Resolved workflow manifest: -# Imports: -# - gh-aw-fragments/elastic-tools.md -# - gh-aw-fragments/formatting.md -# - gh-aw-fragments/mcp-pagination.md -# - gh-aw-fragments/messages-footer.md -# - gh-aw-fragments/network-ecosystems.md -# - gh-aw-fragments/pick-three-keep-many.md -# - gh-aw-fragments/pr-context.md -# - gh-aw-fragments/review-process.md -# - gh-aw-fragments/rigor.md -# - gh-aw-fragments/runtime-setup.md -# - gh-aw-fragments/safe-output-code-review.md -# - gh-aw-fragments/safe-output-review-comment.md -# - gh-aw-fragments/safe-output-submit-review.md -# -# inlined-imports: true -# -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"52ffb4f3214324bb2765dc259c6e46934f960e2d35e7af73b93069e48cd03e21"} - -name: "PR Review" -"on": - # bots: # Bots processed as bot check in pre-activation job - # - ${{ inputs.allowed-bot-users }} # Bots processed as bot check in pre-activation job - # roles: # Roles processed as role check in pre-activation job - # - admin # Roles processed as role check in pre-activation job - # - maintainer # Roles processed as role check in pre-activation job - # - write # Roles processed as role check in pre-activation job - workflow_call: - inputs: - additional-instructions: - default: "" - description: Repo-specific instructions appended to the agent prompt - required: false - type: string - allowed-bot-users: - default: github-actions[bot] - description: Allowlisted bot actor usernames (comma-separated) - required: false - type: string - create-pull-request-review-comment-max: - default: "30" - description: Maximum number of review comments the agent can create per run - required: false - type: string - intensity: - default: balanced - description: "Review intensity: conservative, balanced, or aggressive" - required: false - type: string - messages-footer: - default: "" - description: Footer appended to all agent comments and reviews - required: false - type: string - minimum_severity: - default: low - description: "Minimum severity for inline comments: critical, high, medium, low, or nitpick. Issues below this threshold go in a collapsible section of the review body instead." - required: false - type: string - model: - default: gpt-5.3-codex - description: AI model to use - required: false - type: string - setup-commands: - default: "" - description: Shell commands to run before the agent starts (dependency install, build, etc.) - required: false - type: string - secrets: - COPILOT_GITHUB_TOKEN: - required: true - -permissions: {} - -concurrency: - cancel-in-progress: true - group: ${{ github.workflow }}-pr-review-${{ github.event.pull_request.number }} - -run-name: "PR Review" - -jobs: - activation: - needs: pre_activation - if: needs.pre_activation.outputs.activated == 'true' - runs-on: ubuntu-slim - permissions: - contents: read - outputs: - comment_id: "" - comment_repo: "" - model: ${{ steps.generate_aw_info.outputs.model }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Generate agentic run info - id: generate_aw_info - env: - GH_AW_INFO_ENGINE_ID: "copilot" - GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" - GH_AW_INFO_MODEL: "${{ inputs.model }}" - GH_AW_INFO_VERSION: "" - GH_AW_INFO_AGENT_VERSION: "0.0.420" - GH_AW_INFO_WORKFLOW_NAME: "PR Review" - GH_AW_INFO_EXPERIMENTAL: "false" - GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" - GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["agents-md-generator.fastmcp.app","artifacts.elastic.co","clojure","cloud.elastic.co","containers","dart","defaults","dotnet","ela.st","elastic.co","elastic.dev","elastic.github.io","elixir","fonts","github","github-actions","go","haskell","java","kotlin","linux-distros","node","node-cdns","perl","php","playwright","public-code-search.fastmcp.app","python","ruby","rust","scala","swift","terraform","www.elastic.co","zig"]' - GH_AW_INFO_FIREWALL_ENABLED: "true" - GH_AW_INFO_AWF_VERSION: "v0.23.0" - GH_AW_INFO_AWMG_VERSION: "" - GH_AW_INFO_FIREWALL_TYPE: "squid" - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); - await main(core, context); - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Checkout .github and .agents folders - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: | - .github - .agents - sparse-checkout-cone-mode: true - fetch-depth: 1 - persist-credentials: false - - name: Check workflow file timestamps - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_WORKFLOW_FILE: "gh-aw-pr-review.lock.yml" - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); - await main(); - - name: Create prompt with built-in context - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_INPUTS_INTENSITY: ${{ inputs.intensity }} - GH_AW_INPUTS_MINIMUM_SEVERITY: ${{ inputs.minimum_severity }} - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - run: | - bash /opt/gh-aw/actions/create_prompt_first.sh - { - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat "/opt/gh-aw/prompts/xpia.md" - cat "/opt/gh-aw/prompts/temp_folder_prompt.md" - cat "/opt/gh-aw/prompts/markdown.md" - cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_EOF' - - Tools: create_pull_request_review_comment, submit_pull_request_review, missing_tool, missing_data, noop - - - The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} - - **actor**: __GH_AW_GITHUB_ACTOR__ - {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} - - **repository**: __GH_AW_GITHUB_REPOSITORY__ - {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} - - **workspace**: __GH_AW_GITHUB_WORKSPACE__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ - {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} - - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ - {{/if}} - - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## MCP Servers - - - **`search_code`** — grep-style search across public GitHub repositories. Use for finding usage patterns in upstream libraries, reference implementations, or examples in open-source projects. This searches *public GitHub repos*, not the local codebase — if available you can use `grep` and file reading for local code. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - Repository conventions are pre-fetched to `/tmp/agents.md`. Read this file early in your task to understand the codebase's conventions, guidelines, and patterns. If the file doesn't exist, continue without it. When spawning sub-agents, include the contents of `/tmp/agents.md` in each sub-agent's prompt (or tell the sub-agent to read the file directly). - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Formatting Guidelines - - - Lead with the most important information — your first sentence should be the key takeaway - - Be concise and actionable — no filler or praise - - Use `
` and `` tags for long sections to keep responses scannable - - Wrap branch names and @-references in backticks to avoid pinging users - - Include code snippets with file paths and line numbers when referencing the codebase - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Rigor - - **Silence is better than noise. A false positive wastes a human's time and erodes trust in every future report.** - - - If you claim something is missing or broken, show the exact evidence in the code — file path, line number, and what you observed. - - If a conclusion depends on assumptions you haven't confirmed, do not assert it. Verify first; if you cannot verify, do not report. - - "I don't know" is better than a wrong answer. `noop` is better than a speculative finding. - - It's worth the time to verify now versus guessing and forcing someone else to verify later. - - Before submitting any output, re-read it as a skeptical reviewer. Ask: "Would a senior engineer on this team find this useful, or would they close it immediately?" If the answer is "close," call `noop` instead. - - Only report findings you would confidently defend in a code review. If you feel the need to hedge with "might," "could," or "possibly," the finding is not ready to file. - - Be thorough, spend the time to investigate and verify. There is no rush. Do your best work. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## MCP Pagination - - MCP tool responses have a **25,000 token limit**. When responses exceed this limit, the call fails and you must retry with pagination — wasting turns and tokens. Use proactive pagination to stay under the limit. - - ### Recommended `perPage` Values - - - **5-10**: For detailed items (PR diffs, files with patches, issues with comments) - - **20-30**: For medium-detail lists (commits, review comments, issue lists) - - **50-100**: For simple list operations (branches, labels, tags) - - ### Pagination Pattern - - When you need all results from a paginated API: - - 1. Fetch the first page with a conservative `perPage` value - 2. Process the results before fetching the next page - 3. Continue fetching pages until you receive fewer results than `perPage` (indicating the last page) - - ### Error Recovery - - If you see an error like: - - `MCP tool response exceeds maximum allowed tokens (25000)` - - `Response too large for tool [tool_name]` - - Retry the same call with a smaller `perPage` value (halve it). - - ### Tips - - - **Start small**: It's better to make multiple small requests than one that fails - - **Fetch incrementally**: Get an overview first, then details for specific items - - **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. **Never mention these file paths or on-disk data sources in your responses** — they are internal implementation details invisible to users. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Code Review Reference - - Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md`. Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md`. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Message Footer - - A footer is automatically appended to all comments and reviews. Do not add your own footer or sign-off — the runtime handles this. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## create-pull-request-review-comment - - - **Required fields**: `path` (file path), `line` (line number), and `body` (comment text). - - **Line**: Must be within the diff — an added or context line in the patch. Must be the **exact line number from reading the file** (not estimated from the patch). Lines outside the diff will fail. - - **Body**: Sanitized with the standard pipeline (mentions neutralized, HTML filtered, URLs restricted). GitHub API limit is ~65,536 characters. - - **Side**: Defaults to `RIGHT` (the new code). Use `LEFT` only when commenting on deleted lines. - - **Suggestion blocks**: Use ` ```suggestion ` fences for concrete code fixes. The suggestion must actually change the code — don't suggest identical code. Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. - - Only flag issues you are confident are real problems — false positives erode trust. Once you have flagged an issue, you cannot unflag it. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## submit-pull-request-review Limitations - - - **Event**: Must be one of `APPROVE`, `REQUEST_CHANGES`, or `COMMENT`. Defaults to `COMMENT` if omitted. - - **Body**: Max 65,000 characters. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. A body is required when event is `REQUEST_CHANGES`. Sanitized (mentions neutralized, HTML filtered, URLs restricted). If you have also used `create-pull-request-review-comment`, you do not need to repeat the same feedback in the body. If you "Approve" and have no comments, do not provide a `body`. - - **Own PRs**: If the workflow actor is also the PR author (e.g., `github-actions[bot]` reviewing its own PR), the event is forced to `COMMENT` regardless of what you specify. `APPROVE` and `REQUEST_CHANGES` will not work. - - **Max per run**: 1 review submission per workflow run. Leave inline comments first, then submit the review as a single final action. - - **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ### Pick Three, Keep Many - - Parallelize your work using sub-agents. Spawn multiple sub-agents, each approaching the task from a different angle — e.g., different focus areas, different heuristics, or different parts of the codebase. Each sub-agent works independently and should return its own list of findings. - - **How to spawn sub-agents:** Call `runSubagent` with the `agentType` and `model` specified by the workflow instructions below (defaulting to `agentType: "general-purpose"` and `model: "__GH_AW_INPUTS_MODEL__"` if none are specified). Sub-agents cannot see your conversation history, the other sub-agents' results, or any context you have gathered so far. Each prompt must be **fully self-contained** — include everything the sub-agent needs: - - - The full task description and objective (restate it, don't summarize) - - All repository context, conventions, and constraints you've gathered (e.g., from `/tmp/agents.md`) - - Any relevant data the sub-agent needs to do its job (diffs, file contents, existing threads) - - The quality criteria and output format you expect - - The specific angle that distinguishes this sub-agent from the others - - Err on the side of providing too much context rather than too little. A well-informed sub-agent with a 10,000-token prompt will produce far better results than one that has to rediscover the codebase from scratch. - - **Wait for all sub-agents to complete.** Do not proceed until every sub-agent has returned its result. - - **Merge and deduplicate findings** across all sub-agents: - 1. If multiple sub-agents flagged the same issue, keep the version with the strongest evidence and clearest explanation. - 2. If a finding is unique to one sub-agent, include it only if it passes the quality gate on its own merits — a finding flagged by only one sub-agent deserves extra scrutiny. - 3. Drop any finding that does not meet the verification criteria. - - **Filter aggressively for quality.** Your job as the parent agent is to be the quality gate. Sub-agents cast a wide net; you decide what's worth keeping. For each surviving finding, verify it yourself — check that file paths exist, line numbers are accurate, the problem is real, and the finding is actionable. Discard anything vague, speculative, or already addressed. If no findings survive filtering, call `noop`. - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - # PR Review Agent - - Review pull requests in __GH_AW_GITHUB_REPOSITORY__ and provide actionable feedback via inline review comments on specific code lines. - - ## Context - - - **Repository**: __GH_AW_GITHUB_REPOSITORY__ - - **PR**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ — __GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE__ - - **PR context on disk**: `/tmp/pr-context/` — PR metadata, diff, files, reviews, comments, and linked issues are pre-fetched. Read from these files instead of calling the API. - - ## Constraints - - This workflow is read-only. You can read files, search code, run commands, and interact with PRs and issues — but your only outputs are inline review comments and a review submission. - - ## Review Process - - Follow these steps in order. - - ### Step 1: Gather Context - - 1. Call `ready_to_code_review` to prepare the review approach based on PR size. - 2. Read `/tmp/agents.md` for repository conventions (skip if missing). - 3. Read `/tmp/pr-context/pr.json` for PR details (author, description, branches). - 4. Read `/tmp/pr-context/issue-*.json` files if any exist to understand linked issue motivation and acceptance criteria. - 5. Read `/tmp/pr-context/reviews.json` to check prior review submissions from this bot. Note any prior verdicts to avoid redundant reviews. - 6. Read `/tmp/pr-context/review_comments.json` to check existing review threads. Note which files already have threads and whether they are resolved, unresolved, or outdated. - - ### Step 2: Review - - Read `/tmp/pr-context/agent-review.md` for the review approach and follow it. - - ### Step 3: Verify and Comment - - If sub-agents were used, merge and deduplicate findings per the Pick Three, Keep Many process. Verify each finding before leaving a comment. For every finding: - - 1. **Read the file and surrounding context** — open the full file, not just the diff. Understand the broader code. - 2. **Construct a concrete failure scenario** — what specific input or state causes the bug? If you cannot describe one, drop the finding. - 3. **Challenge the finding** — would a senior engineer familiar with this codebase agree this is a real issue? If "probably not" or "unsure", drop it. - 4. **Check existing threads** — if this issue was already flagged in a prior review (resolved or unresolved), do not duplicate. - - Only leave a comment if the finding survives all four checks. Findings flagged independently by multiple sub-agents are stronger candidates. Findings from only one sub-agent deserve extra scrutiny. - - Leave inline comments (`create_pull_request_review_comment`) per the **Code Review Reference** above for each finding that survives verification. Comment on each file's findings before moving to the next file. If no findings survive verification, proceed directly to Step 4. - - ### Step 4: Submit the Review - - **Skip if nothing new:** If you left zero inline comments during this review AND your verdict would be the same as the most recent review from this bot (compare against reviews in Step 1), call `noop` with a message like "No new findings — prior review still applies" and stop. Do not submit a redundant review. - - After all comments are posted, step back and consider the PR as a whole. Call **`submit_pull_request_review`** with: - - The review type (REQUEST_CHANGES, COMMENT, or APPROVE) - - A review body that is **only the verdict and only if the verdict is not APPROVE**. If you have cross-cutting feedback that spans multiple files or cannot be expressed as inline comments, include it here. Otherwise, leave the review body empty — your inline comments already contain the detail. - - **Bot-authored PRs:** If the PR author is `github-actions[bot]`, you can only submit a `COMMENT` review — `APPROVE` and `REQUEST_CHANGES` will fail because GitHub does not allow bot accounts to approve or request changes on their own PRs. Use `COMMENT` and state your verdict in the review body instead. - - **Do NOT** describe what the PR does, list the files you reviewed, summarize inline comments, or restate prior review feedback. The PR author already knows what their PR does. Your inline comments already contain all the detail. The review body exists solely to communicate the approve/request-changes decision and important/critical feedback that cannot be covered in inline comments. - - If you have no issues, or you have only provided NITPICK and LOW issues, submit an APPROVE review. Otherwise, submit a REQUEST_CHANGES review. - - ## Review Settings - - - **Intensity**: `__GH_AW_INPUTS_INTENSITY__` - - **Minimum inline severity**: `__GH_AW_INPUTS_MINIMUM_SEVERITY__` - - These override the defaults defined in the Code Review Reference above. - - __GH_AW_EXPR_49B959F1__ - - GH_AW_PROMPT_EOF - } > "$GH_AW_PROMPT" - - name: Interpolate variables and render templates - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_INPUTS_INTENSITY: ${{ inputs.intensity }} - GH_AW_INPUTS_MINIMUM_SEVERITY: ${{ inputs.minimum_severity }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); - await main(); - - name: Substitute placeholders - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_EXPR_49B959F1: ${{ inputs.additional-instructions }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: ${{ github.event.pull_request.title }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_INPUTS_INTENSITY: ${{ inputs.intensity }} - GH_AW_INPUTS_MINIMUM_SEVERITY: ${{ inputs.minimum_severity }} - GH_AW_INPUTS_MODEL: ${{ inputs.model }} - GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - - const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); - - // Call the substitution function - return await substitutePlaceholders({ - file: process.env.GH_AW_PROMPT, - substitutions: { - GH_AW_EXPR_49B959F1: process.env.GH_AW_EXPR_49B959F1, - GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_TITLE, - GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, - GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, - GH_AW_INPUTS_INTENSITY: process.env.GH_AW_INPUTS_INTENSITY, - GH_AW_INPUTS_MINIMUM_SEVERITY: process.env.GH_AW_INPUTS_MINIMUM_SEVERITY, - GH_AW_INPUTS_MODEL: process.env.GH_AW_INPUTS_MODEL, - GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED - } - }); - - name: Validate prompt placeholders - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh - - name: Print prompt - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/print_prompt_summary.sh - - name: Upload activation artifact - if: success() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: activation - path: | - /tmp/gh-aw/aw_info.json - /tmp/gh-aw/aw-prompts/prompt.txt - retention-days: 1 - - agent: - needs: activation - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - issues: read - pull-requests: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}-pr-review-${{ github.event.pull_request.number }}" - env: - DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} - GH_AW_ASSETS_ALLOWED_EXTS: "" - GH_AW_ASSETS_BRANCH: "" - GH_AW_ASSETS_MAX_SIZE_KB: 0 - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_WORKFLOW_ID_SANITIZED: ghawprreview - outputs: - checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} - detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} - detection_success: ${{ steps.detection_conclusion.outputs.success }} - has_patch: ${{ steps.collect_output.outputs.has_patch }} - model: ${{ needs.activation.outputs.model }} - output: ${{ steps.collect_output.outputs.output }} - output_types: ${{ steps.collect_output.outputs.output_types }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: Create gh-aw temp directory - run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - if: hashFiles('go.mod') != '' - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 - with: - cache: true - go-version-file: go.mod - - if: hashFiles('.python-version') != '' - name: Setup Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 - with: - python-version-file: .python-version - - if: hashFiles('.node-version') != '' - name: Setup Node.js (.node-version) - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 - with: - node-version-file: .node-version - - if: hashFiles('.node-version') == '' && hashFiles('.nvmrc') != '' - name: Setup Node.js (.nvmrc) - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 - with: - node-version-file: .nvmrc - - if: hashFiles('.ruby-version') != '' - name: Setup Ruby - uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1 - with: - bundler-cache: true - ruby-version: .ruby-version - - id: setup-uv - if: hashFiles('pyproject.toml', 'uv.lock') != '' - name: Setup uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 - - env: - UV_PATH: ${{ steps.setup-uv.outputs.uv-path }} - WORKSPACE: ${{ github.workspace }} - if: hashFiles('pyproject.toml', 'uv.lock') != '' - name: Expose uv in workspace - run: | - set -euo pipefail - install_dir="$WORKSPACE/.gh-aw-tools/bin" - mkdir -p "$install_dir" - cp "$UV_PATH" "$install_dir/uv" - chmod +x "$install_dir/uv" - echo "$install_dir" >> "$GITHUB_PATH" - shell: bash - - name: Configure Copilot CLI settings - run: "set -euo pipefail\nmkdir -p ~/.copilot\nCONFIG=\"$HOME/.copilot/config.json\"\nif [ -f \"$CONFIG\" ]; then\n jq '. + {\"chat.customAgentInSubagent.enabled\": true}' \"$CONFIG\" > \"$CONFIG.tmp\" && mv \"$CONFIG.tmp\" \"$CONFIG\"\nelse\n echo '{\"chat.customAgentInSubagent.enabled\":true}' > \"$CONFIG\"\nfi\n" - shell: bash - - env: - GITHUB_REPOSITORY: ${{ github.repository }} - name: Fetch repository conventions - run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" - 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,headRefOid,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# File orderings for sub-agent review (3 strategies)\njq -r '[.[] | .filename] | sort | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_az.txt\njq -r '[.[] | .filename] | sort | reverse | .[]' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_za.txt\njq -r '[.[] | {filename, size: ((.additions // 0) + (.deletions // 0))}] | sort_by(-.size) | .[].filename' /tmp/pr-context/files.json \\\n > /tmp/pr-context/file_order_largest.txt\n\n# Compute PR size metrics for review fan-out decisions\nFILE_COUNT=$(jq 'length' /tmp/pr-context/files.json)\nDIFF_LINES=$(wc -l < /tmp/pr-context/pr.diff | tr -d ' ')\necho \"${FILE_COUNT} files, ${DIFF_LINES} diff lines\" > /tmp/pr-context/pr-size.txt\necho \"PR size: ${FILE_COUNT} files, ${DIFF_LINES} diff lines\"\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# Filtered review thread views (pre-computed so agents don't need to parse review_comments.json)\njq '[.[] | select(.isResolved == false)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/unresolved_threads.json\njq '[.[] | select(.isResolved == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/resolved_threads.json\njq '[.[] | select(.isOutdated == true)]' /tmp/pr-context/review_comments.json \\\n > /tmp/pr-context/outdated_threads.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, head commit SHA (`headRefOid`), 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| `file_order_az.txt` | Changed files sorted alphabetically (A→Z), one filename per line |\n| `file_order_za.txt` | Changed files sorted reverse-alphabetically (Z→A), one filename per line |\n| `file_order_largest.txt` | Changed files sorted by diff size descending (largest first), one filename per line |\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| `unresolved_threads.json` | Unresolved review threads — subset of `review_comments.json` where `isResolved` is false |\n| `resolved_threads.json` | Resolved review threads — subset of `review_comments.json` where `isResolved` is true |\n| `outdated_threads.json` | Outdated review threads — subset of `review_comments.json` where `isOutdated` is true (code changed since comment) |\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| `pr-size.txt` | PR size metrics: `{N} files, {M} diff lines` — used by the agent to decide review fan-out |\n| `agents.md` | Not used in PR context — repository conventions are at `/tmp/agents.md` (pre-fetched) |\n| `review-instructions.md` | Review instructions, criteria, and calibration examples for sub-agents |\n| `agent-review.md` | Main agent instructions — review approach resolved from PR size (written when `ready_to_code_review` is called) |\n| `subagent-az.md` | Sub-agent instructions: review files A → Z (written when `ready_to_code_review` is called) |\n| `subagent-za.md` | Sub-agent instructions: review files Z → A (written when `ready_to_code_review` is called) |\n| `subagent-largest.md` | Sub-agent instructions: review files largest diff first (written when `ready_to_code_review` is called) |\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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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: - SETUP_COMMANDS: ${{ inputs.setup-commands }} - if: ${{ inputs.setup-commands != '' }} - name: Repo-specific setup - run: eval "$SETUP_COMMANDS" - - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Checkout PR branch - id: checkout-pr - if: | - (github.event.pull_request) || (github.event.issue.pull_request) - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); - await main(); - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420 - - name: Install awf binary - run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 - - name: Determine automatic lockdown mode for GitHub MCP Server - id: determine-automatic-lockdown - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - with: - script: | - const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); - await determineAutomaticLockdown(github, context, core); - - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.6 ghcr.io/github/github-mcp-server:v0.31.0 node:lts-alpine - - name: Write Safe Outputs Config - run: | - mkdir -p /opt/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/safeoutputs - mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"create_pull_request_review_comment":{"max":"${{ inputs.create-pull-request-review-comment-max }}"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_EOF - cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' - [ - { - "description": "Create a review comment on a specific line of code in a pull request. Use this for inline code review feedback, suggestions, or questions about specific code changes. For general PR comments not tied to specific lines, use add_comment instead. CONSTRAINTS: Comments will be on the RIGHT side of the diff.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Review comment content in Markdown. Provide specific, actionable feedback about the code at this location.", - "type": "string" - }, - "line": { - "description": "Line number for the comment. For single-line comments, this is the target line. For multi-line comments, this is the ending line.", - "type": [ - "number", - "string" - ] - }, - "path": { - "description": "File path relative to the repository root (e.g., 'src/auth/login.js'). Must be a file that was changed in the PR.", - "type": "string" - }, - "side": { - "description": "Side of the diff to comment on: RIGHT for the new version (additions), LEFT for the old version (deletions). Defaults to RIGHT.", - "enum": [ - "LEFT", - "RIGHT" - ], - "type": "string" - }, - "start_line": { - "description": "Starting line number for multi-line comments. When set, the comment spans from start_line to line. Omit for single-line comments.", - "type": [ - "number", - "string" - ] - } - }, - "required": [ - "path", - "line", - "body" - ], - "type": "object" - }, - "name": "create_pull_request_review_comment" - }, - { - "description": "Submit a pull request review with a status decision. All create_pull_request_review_comment outputs are automatically collected and included as inline comments in this review. Use APPROVE to approve the PR, REQUEST_CHANGES to request changes, or COMMENT for general feedback without a decision. If you don't call this tool, review comments are still submitted as a COMMENT review. CONSTRAINTS: Maximum 1 review(s) can be submitted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Overall review summary in Markdown. Provide a high-level assessment of the changes. Required for REQUEST_CHANGES; optional for APPROVE and COMMENT.", - "type": "string" - }, - "event": { - "description": "Review decision: APPROVE to approve the pull request, REQUEST_CHANGES to formally request changes before merging, or COMMENT for general feedback without a formal decision. Defaults to COMMENT when omitted.", - "enum": [ - "APPROVE", - "REQUEST_CHANGES", - "COMMENT" - ], - "type": "string" - } - }, - "type": "object" - }, - "name": "submit_pull_request_review" - }, - { - "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "reason": { - "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", - "type": "string" - }, - "tool": { - "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", - "type": "string" - } - }, - "required": [ - "reason" - ], - "type": "object" - }, - "name": "missing_tool" - }, - { - "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "message": { - "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').", - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "name": "noop" - }, - { - "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "context": { - "description": "Additional context about the missing data or where it should come from (max 256 characters).", - "type": "string" - }, - "data_type": { - "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", - "type": "string" - }, - "reason": { - "description": "Explanation of why this data is needed to complete the task (max 256 characters).", - "type": "string" - } - }, - "required": [], - "type": "object" - }, - "name": "missing_data" - } - ] - GH_AW_SAFE_OUTPUTS_TOOLS_EOF - cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' - { - "create_pull_request_review_comment": { - "defaultMax": 1, - "fields": { - "body": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "line": { - "required": true, - "positiveInteger": true - }, - "path": { - "required": true, - "type": "string" - }, - "repo": { - "type": "string", - "maxLength": 256 - }, - "side": { - "type": "string", - "enum": [ - "LEFT", - "RIGHT" - ] - }, - "start_line": { - "optionalPositiveInteger": true - } - }, - "customValidation": "startLineLessOrEqualLine" - }, - "missing_data": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "context": { - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "data_type": { - "type": "string", - "sanitize": true, - "maxLength": 128 - }, - "reason": { - "type": "string", - "sanitize": true, - "maxLength": 256 - } - } - }, - "missing_tool": { - "defaultMax": 20, - "fields": { - "alternatives": { - "type": "string", - "sanitize": true, - "maxLength": 512 - }, - "reason": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 256 - }, - "tool": { - "type": "string", - "sanitize": true, - "maxLength": 128 - } - } - }, - "noop": { - "defaultMax": 1, - "fields": { - "message": { - "required": true, - "type": "string", - "sanitize": true, - "maxLength": 65000 - } - } - }, - "submit_pull_request_review": { - "defaultMax": 1, - "fields": { - "body": { - "type": "string", - "sanitize": true, - "maxLength": 65000 - }, - "event": { - "type": "string", - "enum": [ - "APPROVE", - "REQUEST_CHANGES", - "COMMENT" - ] - } - } - } - } - GH_AW_SAFE_OUTPUTS_VALIDATION_EOF - - name: Generate Safe Outputs MCP Server Config - id: safe-outputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3001 - - # Set outputs for next steps - { - echo "safe_outputs_api_key=${API_KEY}" - echo "safe_outputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Outputs MCP server will run on port ${PORT}" - - - name: Start Safe Outputs MCP HTTP Server - id: safe-outputs-start - env: - DEBUG: '*' - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} - GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json - GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json - GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_OUTPUTS_PORT - export GH_AW_SAFE_OUTPUTS_API_KEY - export GH_AW_SAFE_OUTPUTS_TOOLS_PATH - export GH_AW_SAFE_OUTPUTS_CONFIG_PATH - export GH_AW_MCP_LOG_DIR - - bash /opt/gh-aw/actions/start_safe_outputs_server.sh - - - name: Setup Safe Inputs Config - run: | - mkdir -p /opt/gh-aw/safe-inputs/logs - cat > /opt/gh-aw/safe-inputs/tools.json << 'GH_AW_SAFE_INPUTS_TOOLS_EOF' - { - "serverName": "safeinputs", - "version": "1.0.0", - "logDir": "/opt/gh-aw/safe-inputs/logs", - "tools": [ - { - "name": "ready-to-code-review", - "description": "Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/", - "inputSchema": { - "properties": {}, - "type": "object" - }, - "handler": "ready-to-code-review.py", - "timeout": 60 - } - ] - } - GH_AW_SAFE_INPUTS_TOOLS_EOF - cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'GH_AW_SAFE_INPUTS_SERVER_EOF' - const path = require("path"); - const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); - const configPath = path.join(__dirname, "tools.json"); - const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); - const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; - startHttpServer(configPath, { - port: port, - stateless: true, - logDir: "/opt/gh-aw/safe-inputs/logs" - }).catch(error => { - console.error("Failed to start safe-inputs HTTP server:", error); - process.exit(1); - }); - GH_AW_SAFE_INPUTS_SERVER_EOF - chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs - - - name: Setup Safe Inputs Tool Files - run: | - cat > /opt/gh-aw/safe-inputs/ready-to-code-review.py << 'GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF' - #!/usr/bin/env python3 - # Auto-generated safe-input tool: ready-to-code-review - # Prepare code review instructions based on PR size — writes agent-review.md and subagent-*.md to /tmp/pr-context/ - - import json - import os - import sys - - # Read inputs from stdin (JSON format) - try: - inputs = json.loads(sys.stdin.read()) if not sys.stdin.isatty() else {} - except (json.JSONDecodeError, Exception): - inputs = {} - - # User code: - import os, json, re - - os.makedirs('/tmp/pr-context', exist_ok=True) - - # Read PR size written by the pr-context step - try: - with open('/tmp/pr-context/pr-size.txt') as f: - pr_size = f.read().strip() - m = re.search(r', (\d+) diff', pr_size) - diff_lines = int(m.group(1)) if m else 0 - except Exception: - pr_size = 'unknown size' - diff_lines = 0 - - # Write one instruction file per sub-agent file ordering strategy - for key, desc in [('az', 'A \u2192 Z (alphabetical)'), ('za', 'Z \u2192 A (reverse alphabetical)'), ('largest', 'largest diff first')]: - lines = [ - '# PR Review Sub-Agent', - '', - 'Review the PR as a code review sub-agent. Return findings only \u2014 do NOT leave inline comments.', - '', - '## Instructions', - '', - 'Read `/tmp/pr-context/review-instructions.md` for the full review process, criteria, calibration examples, and output format.', - '', - '## Context', - '', - '- Repository conventions: `/tmp/agents.md` (skip if missing)', - '- PR details: `/tmp/pr-context/pr.json`', - '- All context files: `/tmp/pr-context/README.md`', - '- Per-file diffs: `/tmp/pr-context/diffs/.diff`', - '- Full file contents: read from the workspace (PR branch is checked out)', - '', - '## Your File Order', - '', - f'Review files in this order: `/tmp/pr-context/file_order_{key}.txt` ({desc})', - ] - with open(f'/tmp/pr-context/subagent-{key}.md', 'w') as f: - f.write('\n'.join(lines) + '\n') - - # Determine review approach based on PR size - if diff_lines < 200: - approach_lines = [ - f'**Small PR ({pr_size}):** Review directly \u2014 no sub-agents. Review files in order from `/tmp/pr-context/file_order_az.txt`, reading each diff from `/tmp/pr-context/diffs/.diff` and the full file from the workspace.', - ] - size_key = 'small' - elif diff_lines < 800: - approach_lines = [ - f'**Medium PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 2 `code-review` sub-agents in parallel:', - '', - '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', - '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', - '', - 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', - ] - size_key = 'medium' - else: - approach_lines = [ - f'**Large PR ({pr_size}):** Use the **Pick Three, Keep Many** process \u2014 spawn 3 `code-review` sub-agents in parallel:', - '', - '- **Agent 1**: prompt it to read `/tmp/pr-context/subagent-az.md` and follow it', - '- **Agent 2**: prompt it to read `/tmp/pr-context/subagent-za.md` and follow it', - '- **Agent 3**: prompt it to read `/tmp/pr-context/subagent-largest.md` and follow it', - '', - 'Each sub-agent returns a structured findings list. They do NOT leave inline comments.', - ] - size_key = 'large' - - with open('/tmp/pr-context/agent-review.md', 'w') as f: - f.write('\n'.join(approach_lines) + '\n') - - # Write parent-agent comment format and threshold instructions - t3 = chr(96) * 3 - t5 = chr(96) * 5 - threshold = '${{ inputs.minimum_severity || "low" }}' - parent_lines = [ - '# Code Review: Comment Format and Threshold', - '', - '## Comment Format', - '', - 'Call **`create_pull_request_review_comment`** with:', - '- The file path and the **exact line number from reading the file** (not estimated from the diff)', - '- The line must be within the diff (an added or context line in the patch)', - '', - t5, - '**[SEVERITY] Brief title**', - '', - 'Description of the issue and why it matters.', - '', - f'{t3}suggestion', - 'corrected code here', - t3, - t5, - '', - 'Only include a `suggestion` block when you can provide a concrete code fix that **actually changes** the code. If the fix requires structural changes, describe the fix in prose instead — never include a suggestion identical to the original line.', - '', - '## Inline Comment Threshold', - '', - f'The minimum severity for inline comments is `{threshold}`.', - '', - 'Issues at or above the threshold get **inline review comments** on the specific code line. Issues below the threshold should be collected into a **collapsible section** of the review body — use a `
` block titled "Lower-priority observations (N)" with each item listing its severity, title, file:line, and why it matters.', - '', - 'Severity order (highest to lowest): critical > high > medium > low > nitpick.', - '', - 'If the threshold is `low`, only nitpick-severity issues go in the review body. If `medium`, both low and nitpick go in the body. If the value is unrecognized, treat it as `low`.', - ] - with open('/tmp/pr-context/parent-review.md', 'w') as f: - f.write('\n'.join(parent_lines) + '\n') - - print(json.dumps({'status': 'ok', 'size': size_key, 'diff_lines': diff_lines, 'agent_review': '/tmp/pr-context/agent-review.md', 'parent_review': '/tmp/pr-context/parent-review.md'})) - - GH_AW_SAFE_INPUTS_PY_READY-TO-CODE-REVIEW_EOF - chmod +x /opt/gh-aw/safe-inputs/ready-to-code-review.py - - - name: Generate Safe Inputs MCP Server Config - id: safe-inputs-config - run: | - # Generate a secure random API key (360 bits of entropy, 40+ chars) - # Mask immediately to prevent timing vulnerabilities - API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${API_KEY}" - - PORT=3000 - - # Set outputs for next steps - { - echo "safe_inputs_api_key=${API_KEY}" - echo "safe_inputs_port=${PORT}" - } >> "$GITHUB_OUTPUT" - - echo "Safe Inputs MCP server will run on port ${PORT}" - - - name: Start Safe Inputs MCP HTTP Server - id: safe-inputs-start - env: - DEBUG: '*' - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} - run: | - # Environment variables are set above to prevent template injection - export DEBUG - export GH_AW_SAFE_INPUTS_PORT - export GH_AW_SAFE_INPUTS_API_KEY - - bash /opt/gh-aw/actions/start_safe_inputs_server.sh - - - name: Start MCP Gateway - id: start-mcp-gateway - env: - GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} - GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} - GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} - GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - run: | - set -eo pipefail - mkdir -p /tmp/gh-aw/mcp-config - - # Export gateway environment variables for MCP config and gateway script - export MCP_GATEWAY_PORT="80" - export MCP_GATEWAY_DOMAIN="host.docker.internal" - MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - echo "::add-mask::${MCP_GATEWAY_API_KEY}" - export MCP_GATEWAY_API_KEY - export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" - mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" - export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" - export DEBUG="*" - - export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.6' - - mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh - { - "mcpServers": { - "github": { - "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v0.31.0", - "env": { - "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", - "GITHUB_READ_ONLY": "1", - "GITHUB_TOOLSETS": "repos,issues,pull_requests,search,actions" - } - }, - "public-code-search": { - "type": "http", - "url": "https://public-code-search.fastmcp.app/mcp", - "tools": [ - "search_code" - ] - }, - "safeinputs": { - "type": "http", - "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" - } - }, - "safeoutputs": { - "type": "http", - "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", - "headers": { - "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" - } - } - }, - "gateway": { - "port": $MCP_GATEWAY_PORT, - "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" - } - } - GH_AW_MCP_CONFIG_EOF - - name: Download activation artifact - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: activation - path: /tmp/gh-aw - - name: Clean git credentials - run: bash /opt/gh-aw/actions/clean_git_credentials.sh - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 90 - run: | - set -o pipefail - # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ inputs.model }} - GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - git config --global am.keepcr true - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Copy Copilot session state files to logs - if: always() - continue-on-error: true - run: | - # Copy Copilot session state files to logs folder for artifact collection - # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them - SESSION_STATE_DIR="$HOME/.copilot/session-state" - LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" - - if [ -d "$SESSION_STATE_DIR" ]; then - echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" - mkdir -p "$LOGS_DIR" - cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true - echo "Session state files copied successfully" - else - echo "No session-state directory found at $SESSION_STATE_DIR" - fi - - name: Stop MCP Gateway - if: always() - continue-on-error: true - env: - MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} - MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} - GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} - run: | - bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" - - name: Redact secrets in logs - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); - await main(); - env: - GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' - SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload Safe Outputs - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output - path: ${{ env.GH_AW_SAFE_OUTPUTS }} - if-no-files-found: warn - - name: Ingest agent output - id: collect_output - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - 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 }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); - await main(); - - name: Upload sanitized agent output - if: always() && env.GH_AW_AGENT_OUTPUT - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-output - path: ${{ env.GH_AW_AGENT_OUTPUT }} - if-no-files-found: warn - - name: Upload engine output files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent_outputs - path: | - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - if-no-files-found: ignore - - name: Parse agent logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); - await main(); - - name: Parse Safe Inputs logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); - await main(); - - name: Parse MCP Gateway logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); - await main(); - - name: Print firewall logs - if: always() - continue-on-error: true - env: - AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs - run: | - # Fix permissions on firewall logs so they can be uploaded as artifacts - # AWF runs with sudo, creating files owned by root - sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true - # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) - if command -v awf &> /dev/null; then - awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" - else - echo 'AWF binary not installed, skipping firewall log summary' - fi - - name: Upload agent artifacts - if: always() - continue-on-error: true - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: agent-artifacts - path: | - /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/mcp-logs/ - /tmp/gh-aw/safe-inputs/logs/ - /tmp/gh-aw/sandbox/firewall/logs/ - /tmp/gh-aw/agent-stdio.log - /tmp/gh-aw/agent/ - if-no-files-found: ignore - # --- Threat Detection (inline) --- - - name: Check if detection needed - id: detection_guard - if: always() - env: - OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} - HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} - run: | - if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then - echo "run_detection=true" >> "$GITHUB_OUTPUT" - echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" - else - echo "run_detection=false" >> "$GITHUB_OUTPUT" - echo "Detection skipped: no agent outputs or patches to analyze" - fi - - name: Clear MCP configuration for detection - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - rm -f /tmp/gh-aw/mcp-config/mcp-servers.json - rm -f /home/runner/.copilot/mcp-config.json - rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" - - name: Prepare threat detection files - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - mkdir -p /tmp/gh-aw/threat-detection/aw-prompts - cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true - cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true - for f in /tmp/gh-aw/aw-*.patch; do - [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true - done - echo "Prepared threat detection files:" - ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true - - name: Setup threat detection - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - WORKFLOW_NAME: "PR Review" - WORKFLOW_DESCRIPTION: "AI code review with inline comments on pull requests" - HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); - await main(); - - name: Ensure threat-detection directory and log - if: always() && steps.detection_guard.outputs.run_detection == 'true' - run: | - mkdir -p /tmp/gh-aw/threat-detection - touch /tmp/gh-aw/threat-detection/detection.log - - name: Execute GitHub Copilot CLI - if: always() && steps.detection_guard.outputs.run_detection == 'true' - id: detection_agentic_execution - # Copilot CLI tool arguments (sorted): - # --allow-tool shell(cat) - # --allow-tool shell(grep) - # --allow-tool shell(head) - # --allow-tool shell(jq) - # --allow-tool shell(ls) - # --allow-tool shell(tail) - # --allow-tool shell(wc) - timeout-minutes: 20 - run: | - set -o pipefail - # shellcheck disable=SC1003 - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ inputs.model }} - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GITHUB_API_URL: ${{ github.api_url }} - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_SERVER_URL: ${{ github.server_url }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Parse threat detection results - id: parse_detection_results - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); - await main(); - - name: Upload threat detection log - if: always() && steps.detection_guard.outputs.run_detection == 'true' - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: threat-detection.log - path: /tmp/gh-aw/threat-detection/detection.log - if-no-files-found: ignore - - name: Set detection conclusion - id: detection_conclusion - if: always() - env: - RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} - DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} - run: | - if [[ "$RUN_DETECTION" != "true" ]]; then - echo "conclusion=skipped" >> "$GITHUB_OUTPUT" - echo "success=true" >> "$GITHUB_OUTPUT" - echo "Detection was not needed, marking as skipped" - elif [[ "$DETECTION_SUCCESS" == "true" ]]; then - echo "conclusion=success" >> "$GITHUB_OUTPUT" - echo "success=true" >> "$GITHUB_OUTPUT" - echo "Detection passed successfully" - else - echo "conclusion=failure" >> "$GITHUB_OUTPUT" - echo "success=false" >> "$GITHUB_OUTPUT" - echo "Detection found issues" - fi - - conclusion: - needs: - - activation - - agent - - safe_outputs - if: (always()) && (needs.agent.result != 'skipped') - runs-on: ubuntu-slim - permissions: - contents: read - pull-requests: write - outputs: - noop_message: ${{ steps.noop.outputs.noop_message }} - tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} - total_count: ${{ steps.missing_tool.outputs.total_count }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ - - name: Setup agent output environment variable - run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Process No-Op Messages - id: noop - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_NOOP_MAX: "1" - GH_AW_WORKFLOW_NAME: "PR Review" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/noop.cjs'); - await main(); - - name: Record Missing Tool - id: missing_tool - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "PR Review" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); - await main(); - - name: Handle Agent Failure - id: handle_agent_failure - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "PR Review" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_WORKFLOW_ID: "gh-aw-pr-review" - GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} - GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" - GH_AW_GROUP_REPORTS: "false" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); - await main(); - - name: Handle No-Op Message - id: handle_noop_message - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_WORKFLOW_NAME: "PR Review" - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} - GH_AW_NOOP_REPORT_AS_ISSUE: "true" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); - await main(); - - pre_activation: - runs-on: ubuntu-slim - outputs: - activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} - matched_command: '' - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - GH_AW_ALLOWED_BOTS: ${{ inputs.allowed-bot-users }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_membership.cjs'); - await main(); - - safe_outputs: - needs: agent - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') - runs-on: ubuntu-slim - permissions: - contents: read - pull-requests: write - timeout-minutes: 15 - env: - GH_AW_ENGINE_ID: "copilot" - GH_AW_ENGINE_MODEL: "${{ inputs.model }}" - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"${{ inputs.messages-footer || '---\\n[What is this?](https://ela.st/github-ai-tools) | [From workflow: {workflow_name}]({run_url})\\n\\nGive us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.' }}\",\"activationComments\":\"false\"}" - GH_AW_WORKFLOW_ID: "gh-aw-pr-review" - GH_AW_WORKFLOW_NAME: "PR Review" - outputs: - code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} - code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} - create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} - create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} - process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} - process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@88319be75ab1adc60640307a10e5cf04b3deff1e # v0.51.5 - with: - destination: /opt/gh-aw/actions - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8 - with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ - - name: Setup agent output environment variable - run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" - - name: Process Safe Outputs - id: process_safe_outputs - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - 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: "{\"create_pull_request_review_comment\":{\"max\":\"${{ inputs.create-pull-request-review-comment-max }}\",\"side\":\"RIGHT\"},\"missing_data\":{},\"missing_tool\":{},\"submit_pull_request_review\":{\"footer\":\"if-body\",\"max\":1}}" - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); - await main(); - - name: Upload safe output items manifest - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 - with: - name: safe-output-items - path: /tmp/safe-output-items.jsonl - if-no-files-found: warn - From 0d423a0ad33dd6ed0d32de149bc22c864ab9cf43 Mon Sep 17 00:00:00 2001 From: William Easton Date: Mon, 2 Mar 2026 01:46:40 -0600 Subject: [PATCH 3/3] Address PR review feedback: fix list numbering, remove review-process from issue workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gh-aw-test-improver.md: fix two numbered list bugs (1→3 jump, duplicate 1) - mention-in-issue.md, mention-in-issue-no-sandbox.md: remove review-process.md import since it references ready_to_code_review tool not available in issue workflows Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../gh-aw-mention-in-issue-no-sandbox.lock.yml | 10 +--------- .github/workflows/gh-aw-mention-in-issue-no-sandbox.md | 1 - .github/workflows/gh-aw-mention-in-issue.lock.yml | 10 +--------- .github/workflows/gh-aw-mention-in-issue.md | 1 - .github/workflows/gh-aw-test-improvement.lock.yml | 6 +++--- .github/workflows/gh-aw-test-improver.lock.yml | 6 +++--- .github/workflows/gh-aw-test-improver.md | 4 ++-- 7 files changed, 10 insertions(+), 28 deletions(-) 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 e9e9e874..76b0eb45 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 @@ -31,7 +31,6 @@ # - gh-aw-fragments/messages-footer.md # - gh-aw-fragments/network-ecosystems.md # - gh-aw-fragments/playwright-mcp-explorer.md -# - gh-aw-fragments/review-process.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-issue.md @@ -41,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"7fe1a693cbd894d2f7661c4f973105f7866206f3b06e11f9d89126164984534b"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"02c8efe0bf83be1ca95425667e6121aaf56dfe62f844e7bfa0b89eafc9efbedb"} name: "Mention in Issue (no sandbox)" "on": @@ -327,11 +326,6 @@ jobs: - Do not modify files under `.github/workflows/`. - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Code Review Reference - - Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -597,8 +591,6 @@ jobs: name: Fetch repository conventions run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - - 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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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" - name: Write Playwright instructions to disk run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: diff --git a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.md b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.md index 20f3b00c..0f005bbf 100644 --- a/.github/workflows/gh-aw-mention-in-issue-no-sandbox.md +++ b/.github/workflows/gh-aw-mention-in-issue-no-sandbox.md @@ -9,7 +9,6 @@ imports: - gh-aw-fragments/rigor.md - gh-aw-fragments/mcp-pagination.md - gh-aw-fragments/workflow-edit-guardrails.md - - gh-aw-fragments/review-process.md - gh-aw-fragments/messages-footer.md - gh-aw-fragments/playwright-mcp-explorer.md - gh-aw-fragments/safe-output-add-comment-issue.md diff --git a/.github/workflows/gh-aw-mention-in-issue.lock.yml b/.github/workflows/gh-aw-mention-in-issue.lock.yml index 9b9dd46f..7b1462b4 100644 --- a/.github/workflows/gh-aw-mention-in-issue.lock.yml +++ b/.github/workflows/gh-aw-mention-in-issue.lock.yml @@ -31,7 +31,6 @@ # - gh-aw-fragments/messages-footer.md # - gh-aw-fragments/network-ecosystems.md # - gh-aw-fragments/playwright-mcp-explorer.md -# - gh-aw-fragments/review-process.md # - gh-aw-fragments/rigor.md # - gh-aw-fragments/runtime-setup.md # - gh-aw-fragments/safe-output-add-comment-issue.md @@ -41,7 +40,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"88830b37bc3a335b97b2bbb10161349dd44b58ed8dc493f751c286b92daf49d5"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"f074c152071f41578edc15fc606ffcd2ef1127fbe435d25a577c0b6e19f21c4f"} name: "Mention in Issue" "on": @@ -327,11 +326,6 @@ jobs: - Do not modify files under `.github/workflows/`. - If asked to change workflow files, place a copy under `github/` (no leading dot) and note that a maintainer must relocate it into `.github/workflows/`. - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' - ## Code Review Reference - - Review criteria, severity levels, intensity, false positives, and calibration examples are in `/tmp/pr-context/review-instructions.md` (pre-written at startup). Inline comment format and the minimum severity threshold are in `/tmp/pr-context/parent-review.md` (written when `ready_to_code_review` is called). GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' ## Message Footer @@ -599,8 +593,6 @@ jobs: name: Fetch repository conventions run: "set -euo pipefail\nif [ -f \"AGENTS.md\" ]; then\n cp AGENTS.md /tmp/agents.md\n echo \"Repository conventions copied from AGENTS.md to /tmp/agents.md\"\nelse\n OWNER=\"${GITHUB_REPOSITORY%/*}\"\n REPO=\"${GITHUB_REPOSITORY#*/}\"\n summary=$(curl -sf --max-time 15 -X POST https://agents-md-generator.fastmcp.app/mcp \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d \"{\\\"jsonrpc\\\":\\\"2.0\\\",\\\"id\\\":1,\\\"method\\\":\\\"tools/call\\\",\\\"params\\\":{\\\"name\\\":\\\"generate_agents_md\\\",\\\"arguments\\\":{\\\"owner\\\":\\\"${OWNER}\\\",\\\"repo\\\":\\\"${REPO}\\\"}}}\" \\\n | sed 's/^data: //' \\\n | jq -r '.result.structuredContent.summary // empty' 2>/dev/null) || true\n if [ -n \"$summary\" ]; then\n echo \"$summary\" > /tmp/agents.md\n echo \"Repository conventions written to /tmp/agents.md\"\n else\n echo \"::warning::Could not fetch repository conventions; continuing without them\"\n fi\nfi" shell: bash - - 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/agents.md` — Repository coding conventions and guidelines (skip if missing).\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 `/tmp/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" - name: Write Playwright instructions to disk run: "cat > /tmp/playwright-instructions.md << 'EOF'\n# Playwright MCP Tools\n\nUse Playwright MCP tools for interactive browser automation.\nUse these tools to explore the app step by step — do NOT write Node.js scripts.\n\n## Available tools\n\n- `browser_navigate` — go to a URL\n- `browser_click` — click an element\n- `browser_type` — type text into an input\n- `browser_snapshot` — get an accessibility tree (YAML) of the current page\n- `browser_take_screenshot` — capture a screenshot\n- `browser_console_execute` — run JavaScript in the browser console\n\n## Why MCP tools instead of scripts\n\nMCP tools are interactive: you see the page state after each action and\ndecide what to do next. This is ideal for exploratory testing where you\nneed to adapt based on what you find. Scripts are fire-and-forget — if\na selector is wrong, you don't find out until the script fails.\n\n## Measuring DOM properties\n\nFor programmatic checks (e.g. element heights, contrast), use\n`browser_console_execute`:\n\n```javascript\n(() => {\n const els = document.querySelectorAll('input, button, [role=\"combobox\"], [role=\"button\"]');\n return JSON.stringify(Array.from(els)\n .map(el => {\n const r = el.getBoundingClientRect();\n return { tag: el.tagName, h: Math.round(r.height), top: Math.round(r.top), text: el.textContent?.trim().slice(0, 20) };\n })\n .filter(el => el.top > 50 && el.top < 250));\n})()\n```\n\n## Handling failures\n\n- Do not retry the same action more than twice — the page is in a different state than expected.\n- Diagnose before moving on: use `browser_take_screenshot` and `browser_snapshot` to see what's on the page.\n- Adapt (different selector, different path) or report the failure as a finding.\n- Never claim you verified something you didn't — if it failed and you skipped it, say so.\nEOF" - env: diff --git a/.github/workflows/gh-aw-mention-in-issue.md b/.github/workflows/gh-aw-mention-in-issue.md index fcc7e5d4..04ce1fe6 100644 --- a/.github/workflows/gh-aw-mention-in-issue.md +++ b/.github/workflows/gh-aw-mention-in-issue.md @@ -9,7 +9,6 @@ imports: - gh-aw-fragments/rigor.md - gh-aw-fragments/mcp-pagination.md - gh-aw-fragments/workflow-edit-guardrails.md - - gh-aw-fragments/review-process.md - gh-aw-fragments/messages-footer.md - gh-aw-fragments/playwright-mcp-explorer.md - gh-aw-fragments/safe-output-add-comment-issue.md diff --git a/.github/workflows/gh-aw-test-improvement.lock.yml b/.github/workflows/gh-aw-test-improvement.lock.yml index dc60ba88..317e8254 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":"c9e1d6fbb89c5766fc5e6f71903015889a056fdf7800fa0628305c5debea88e8"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"86d7e141b6617ea98e6186b6796104d0889649efc61250de5f323f64cccde792"} name: "Test Improver" "on": @@ -333,7 +333,7 @@ jobs: 1. Determine required repo commands (lint/build/test) and how to run tests: - Check README, CONTRIBUTING, DEVELOPING, Makefile, CI config, package.json, pyproject.toml, and similar files. - 3. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). + 2. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). - If coverage is available and reasonably fast, run it to find low-coverage files. ## Step 2: Identify targets @@ -365,7 +365,7 @@ jobs: 1. Run each new or modified test **at least 5 times** in sequence and confirm every run passes. - Use the test framework's built-in repeat/count flag when available (e.g., `go test -count=5`, `pytest -x --count 5` with `pytest-repeat`, `--repeat 5` in Jest/Vitest). - If no built-in mechanism exists, use a simple shell loop: `for i in $(seq 1 5); do || exit 1; done` - 1. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: + 2. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: - Reliance on timing, sleep, or wall-clock assertions - Shared mutable state between test cases - Non-deterministic iteration order (e.g., map/set ordering) diff --git a/.github/workflows/gh-aw-test-improver.lock.yml b/.github/workflows/gh-aw-test-improver.lock.yml index 6f3823d5..c4494169 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":"c9e1d6fbb89c5766fc5e6f71903015889a056fdf7800fa0628305c5debea88e8"} +# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"86d7e141b6617ea98e6186b6796104d0889649efc61250de5f323f64cccde792"} name: "Test Improver" "on": @@ -328,7 +328,7 @@ jobs: 1. Determine required repo commands (lint/build/test) and how to run tests: - Check README, CONTRIBUTING, DEVELOPING, Makefile, CI config, package.json, pyproject.toml, and similar files. - 3. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). + 2. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). - If coverage is available and reasonably fast, run it to find low-coverage files. ## Step 2: Identify targets @@ -360,7 +360,7 @@ jobs: 1. Run each new or modified test **at least 5 times** in sequence and confirm every run passes. - Use the test framework's built-in repeat/count flag when available (e.g., `go test -count=5`, `pytest -x --count 5` with `pytest-repeat`, `--repeat 5` in Jest/Vitest). - If no built-in mechanism exists, use a simple shell loop: `for i in $(seq 1 5); do || exit 1; done` - 1. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: + 2. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: - Reliance on timing, sleep, or wall-clock assertions - Shared mutable state between test cases - Non-deterministic iteration order (e.g., map/set ordering) diff --git a/.github/workflows/gh-aw-test-improver.md b/.github/workflows/gh-aw-test-improver.md index 4c287460..27712fc5 100644 --- a/.github/workflows/gh-aw-test-improver.md +++ b/.github/workflows/gh-aw-test-improver.md @@ -103,7 +103,7 @@ Identify under-tested code paths, add focused tests, and remove or consolidate d 1. Determine required repo commands (lint/build/test) and how to run tests: - Check README, CONTRIBUTING, DEVELOPING, Makefile, CI config, package.json, pyproject.toml, and similar files. -3. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). +2. Identify coverage tooling (nyc, jest --coverage, pytest --cov, go test -cover, etc.). - If coverage is available and reasonably fast, run it to find low-coverage files. ## Step 2: Identify targets @@ -135,7 +135,7 @@ New tests that pass once may still be flaky. Before filing a PR, verify stabilit 1. Run each new or modified test **at least 5 times** in sequence and confirm every run passes. - Use the test framework's built-in repeat/count flag when available (e.g., `go test -count=5`, `pytest -x --count 5` with `pytest-repeat`, `--repeat 5` in Jest/Vitest). - If no built-in mechanism exists, use a simple shell loop: `for i in $(seq 1 5); do || exit 1; done` -1. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: +2. If any run fails intermittently, investigate the root cause before proceeding. Common sources of flakiness: - Reliance on timing, sleep, or wall-clock assertions - Shared mutable state between test cases - Non-deterministic iteration order (e.g., map/set ordering)