diff --git a/.github/workflows/ace-editor.lock.yml b/.github/workflows/ace-editor.lock.yml index fd1cda76e7d..6caa6eb886d 100644 --- a/.github/workflows/ace-editor.lock.yml +++ b/.github/workflows/ace-editor.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"6d5aafde7712b01923337ac46b801dfdd5e9ac02a91c9b41a9cd40ac580ef4e3","agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"370c523f11a9b58913c58bb6ab0362ea3b582b0204cd9a3f493b95ad2b830f11","agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -53,10 +53,13 @@ name: "ACE Editor Session" "on": - issue_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -75,13 +78,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/ace ') || startsWith(github.event.comment.body, '/ace\n') || github.event.comment.body == '/ace') && github.event.issue.pull_request != null)" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -236,23 +237,23 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_eec6611805947a64_EOF' + cat << 'GH_AW_PROMPT_484df9ad5d20924a_EOF' - GH_AW_PROMPT_eec6611805947a64_EOF + GH_AW_PROMPT_484df9ad5d20924a_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_eec6611805947a64_EOF' + cat << 'GH_AW_PROMPT_484df9ad5d20924a_EOF' Tools: create_issue - GH_AW_PROMPT_eec6611805947a64_EOF + GH_AW_PROMPT_484df9ad5d20924a_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md" - cat << 'GH_AW_PROMPT_eec6611805947a64_EOF' + cat << 'GH_AW_PROMPT_484df9ad5d20924a_EOF' - GH_AW_PROMPT_eec6611805947a64_EOF + GH_AW_PROMPT_484df9ad5d20924a_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_eec6611805947a64_EOF' + cat << 'GH_AW_PROMPT_484df9ad5d20924a_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -281,13 +282,13 @@ jobs: {{/if}} - GH_AW_PROMPT_eec6611805947a64_EOF + GH_AW_PROMPT_484df9ad5d20924a_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_eec6611805947a64_EOF' + cat << 'GH_AW_PROMPT_484df9ad5d20924a_EOF' {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/ace-editor.md}} - GH_AW_PROMPT_eec6611805947a64_EOF + GH_AW_PROMPT_484df9ad5d20924a_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -482,9 +483,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_0f4b7414d0dba99a_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_d63fa8d751dcbb70_EOF' {"create_issue":{"labels":["ace-editor"],"max":1,"title_prefix":"[ace-editor]"}} - GH_AW_SAFE_OUTPUTS_CONFIG_0f4b7414d0dba99a_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_d63fa8d751dcbb70_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -621,7 +622,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_e4136b9e2e7ea1f0_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_ea272df3369fdd3f_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -668,7 +669,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_e4136b9e2e7ea1f0_EOF + GH_AW_MCP_CONFIG_ea272df3369fdd3f_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1043,7 +1044,6 @@ jobs: }); pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/ace ') || startsWith(github.event.comment.body, '/ace\n') || github.event.comment.body == '/ace') && github.event.issue.pull_request != null)" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/ace-editor.md b/.github/workflows/ace-editor.md index 9ad13a0af25..94c224e7f65 100644 --- a/.github/workflows/ace-editor.md +++ b/.github/workflows/ace-editor.md @@ -3,6 +3,7 @@ name: ACE Editor Session description: Generates an ACE editor session link when invoked with /ace command on pull request comments on: slash_command: + strategy: centralized name: ace events: [pull_request_comment] strict: false diff --git a/.github/workflows/agentic_commands.yml b/.github/workflows/agentic_commands.yml new file mode 100644 index 00000000000..e3f3845224e --- /dev/null +++ b/.github/workflows/agentic_commands.yml @@ -0,0 +1,66 @@ +# gh-aw-commands: {"payload_version":"v1","schema_version":"v1","compiler_version":"b6bd645","commands":["ace","approach-validator","archie","brave","cloclo","craft","grumpy","mergefest","nit","plan","poem-bot","review","security-review","summarize","tidy","unbloat"],"workflows":["ace-editor","approach-validator","archie","brave","cloclo","craft","grumpy-reviewer","mergefest","pdf-summary","plan","poem-bot","pr-code-quality-reviewer","pr-nitpick-reviewer","security-review","tidy","unbloat-docs"]} +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw. DO NOT EDIT. +# +# To regenerate this workflow, run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +name: "Agentic Commands" + +on: + issues: + types: [edited, opened, reopened] + issue_comment: + types: [created, edited] + pull_request: + types: [edited, opened, reopened] + pull_request_review_comment: + types: [created, edited] + discussion: + types: [created, edited] + discussion_comment: + types: [created, edited] + +permissions: {} + +jobs: + route: + runs-on: ubuntu-slim + permissions: + actions: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Scripts + uses: ./actions/setup + with: + destination: ${{ runner.temp }}/gh-aw/actions + + - name: Route slash command + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_SLASH_ROUTING: '{"ace":[{"workflow":"ace-editor","events":["pull_request_comment"]}],"approach-validator":[{"workflow":"approach-validator","events":["issue_comment","pull_request_comment"]}],"archie":[{"workflow":"archie","events":["issue_comment","issues","pull_request","pull_request_comment"]}],"brave":[{"workflow":"brave","events":["issue_comment"]}],"cloclo":[{"workflow":"cloclo","events":["discussion","discussion_comment","issue_comment","issues","pull_request","pull_request_comment","pull_request_review_comment"]}],"craft":[{"workflow":"craft","events":["issues"]}],"grumpy":[{"workflow":"grumpy-reviewer","events":["pull_request_comment","pull_request_review_comment"]}],"mergefest":[{"workflow":"mergefest","events":["pull_request_comment"]}],"nit":[{"workflow":"pr-nitpick-reviewer","events":["pull_request_comment","pull_request_review_comment"]}],"plan":[{"workflow":"plan","events":["discussion_comment","issue_comment"]}],"poem-bot":[{"workflow":"poem-bot","events":["issues"]}],"review":[{"workflow":"pr-code-quality-reviewer","events":["pull_request_comment","pull_request_review_comment"]}],"security-review":[{"workflow":"security-review","events":["pull_request_comment","pull_request_review_comment"]}],"summarize":[{"workflow":"pdf-summary","events":["issue_comment","issues"]}],"tidy":[{"workflow":"tidy","events":["pull_request_comment"]}],"unbloat":[{"workflow":"unbloat-docs","events":["pull_request_comment"]}]}' + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/route_slash_command.cjs'); + await main(); diff --git a/.github/workflows/approach-validator.lock.yml b/.github/workflows/approach-validator.lock.yml index ef4fe0597e1..ebd07688ad5 100644 --- a/.github/workflows/approach-validator.lock.yml +++ b/.github/workflows/approach-validator.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"578875ad833fb057dda9d76379ec71b0d31037777d82bc02518441db2d8d6e5d","strict":true,"agent_id":"claude"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"9e79ec7966984d8bba6373164eab732d58ed682097e00df62bae6fae8f0b37c3","strict":true,"agent_id":"claude"} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -57,16 +57,13 @@ name: "Approach Validator" "on": - issue_comment: - types: - - created - - edited - issues: - types: - - labeled - pull_request: - types: - - labeled + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -85,13 +82,17 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/approach-validator ') || startsWith(github.event.comment.body, '/approach-validator\n') || github.event.comment.body == '/approach-validator') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/approach-validator ') || startsWith(github.event.comment.body, '/approach-validator\n') || github.event.comment.body == '/approach-validator') && github.event.issue.pull_request != null || github.event_name == 'issues' && (github.event.label.name == 'approach-proposal' || github.event.label.name == 'needs-design') || github.event_name == 'pull_request' && (github.event.label.name == 'approach-proposal' || github.event.label.name == 'needs-design'))" + if: > + needs.pre_activation.outputs.activated == 'true' && ((fromJSON(github.event.inputs.aw_context || '{}').event_type == 'issues' || + fromJSON(github.event.inputs.aw_context || '{}').event_type == 'pull_request') && (fromJSON(github.event.inputs.aw_context || + '{}').trigger_label == 'approach-proposal' || fromJSON(github.event.inputs.aw_context || '{}').trigger_label == 'needs-design') || + (!(fromJSON(github.event.inputs.aw_context || '{}').event_type == 'issues')) && (!(fromJSON(github.event.inputs.aw_context || + '{}').event_type == 'pull_request'))) runs-on: ubuntu-slim permissions: actions: read contents: read issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -264,20 +265,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_f6efd604eccbb175_EOF' + cat << 'GH_AW_PROMPT_64b8ea9d5da1d3bf_EOF' - GH_AW_PROMPT_f6efd604eccbb175_EOF + GH_AW_PROMPT_64b8ea9d5da1d3bf_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_f6efd604eccbb175_EOF' + cat << 'GH_AW_PROMPT_64b8ea9d5da1d3bf_EOF' Tools: add_comment(max:2), add_labels, missing_tool, missing_data, noop - GH_AW_PROMPT_f6efd604eccbb175_EOF + GH_AW_PROMPT_64b8ea9d5da1d3bf_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_f6efd604eccbb175_EOF' + cat << 'GH_AW_PROMPT_64b8ea9d5da1d3bf_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -306,18 +307,18 @@ jobs: {{/if}} - GH_AW_PROMPT_f6efd604eccbb175_EOF + GH_AW_PROMPT_64b8ea9d5da1d3bf_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_f6efd604eccbb175_EOF' + cat << 'GH_AW_PROMPT_64b8ea9d5da1d3bf_EOF' {{#runtime-import .github/workflows/shared/safe-output-upload-artifact.md}} {{#runtime-import .github/workflows/shared/reporting.md}} {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/approach-validator.md}} - GH_AW_PROMPT_f6efd604eccbb175_EOF + GH_AW_PROMPT_64b8ea9d5da1d3bf_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -550,9 +551,9 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs/upload-artifacts" - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_1cd5b461e4d94d48_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_f3ebf30c01743305_EOF' {"add_comment":{"hide_older_comments":true,"max":2},"add_labels":{"allowed":["awaiting-approach-approval","approach-approved","approach-rejected"],"max":1},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"upload_artifact":{"max-size-bytes":104857600,"max-uploads":3,"retention-days":30,"skip-archive":true}} - GH_AW_SAFE_OUTPUTS_CONFIG_1cd5b461e4d94d48_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_f3ebf30c01743305_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -764,7 +765,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/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 DOCKER_HOST=unix:///var/run/docker.sock -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_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -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 -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -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.3.6' GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_bea2c9c91f5b4b9f_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_d68f4255db71c049_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -795,7 +796,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_bea2c9c91f5b4b9f_EOF + GH_AW_MCP_CONFIG_d68f4255db71c049_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1517,7 +1518,11 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/approach-validator ') || startsWith(github.event.comment.body, '/approach-validator\n') || github.event.comment.body == '/approach-validator') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/approach-validator ') || startsWith(github.event.comment.body, '/approach-validator\n') || github.event.comment.body == '/approach-validator') && github.event.issue.pull_request != null || github.event_name == 'issues' && (github.event.label.name == 'approach-proposal' || github.event.label.name == 'needs-design') || github.event_name == 'pull_request' && (github.event.label.name == 'approach-proposal' || github.event.label.name == 'needs-design'))" + if: > + (fromJSON(github.event.inputs.aw_context || '{}').event_type == 'issues' || fromJSON(github.event.inputs.aw_context || + '{}').event_type == 'pull_request') && (fromJSON(github.event.inputs.aw_context || '{}').trigger_label == 'approach-proposal' || + fromJSON(github.event.inputs.aw_context || '{}').trigger_label == 'needs-design') || (!(fromJSON(github.event.inputs.aw_context || + '{}').event_type == 'issues')) && (!(fromJSON(github.event.inputs.aw_context || '{}').event_type == 'pull_request')) runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/approach-validator.md b/.github/workflows/approach-validator.md index 7b16c5f69a6..d1cba71f54d 100644 --- a/.github/workflows/approach-validator.md +++ b/.github/workflows/approach-validator.md @@ -6,6 +6,7 @@ on: names: [approach-proposal, needs-design] events: [issues, pull_request] slash_command: + strategy: centralized name: approach-validator events: [issue_comment, pull_request_comment] permissions: diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index c9cbe86923d..51b91c2ba85 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"25e043a10b12ae51af58407610eb74c0d4d1505510e0ae0ee2a45da4550964b7","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"e427885be33ad6455b1ebed0e9281d293ee3a8f441a448a2d6c355823d8c6ec8","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -57,20 +57,13 @@ name: "Archie" "on": - issue_comment: - types: - - created - - edited - issues: - types: - - opened - - edited - - reopened - pull_request: - types: - - opened - - edited - - reopened + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -89,13 +82,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/archie ') || startsWith(github.event.issue.body, '/archie\n') || github.event.issue.body == '/archie') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/archie ') || startsWith(github.event.comment.body, '/archie\n') || github.event.comment.body == '/archie') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/archie ') || startsWith(github.event.comment.body, '/archie\n') || github.event.comment.body == '/archie') && github.event.issue.pull_request != null || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/archie ') || startsWith(github.event.pull_request.body, '/archie\n') || github.event.pull_request.body == '/archie'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -248,20 +239,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_2e7019d9465d12fb_EOF' + cat << 'GH_AW_PROMPT_dcd5b34c2b20e0e6_EOF' - GH_AW_PROMPT_2e7019d9465d12fb_EOF + GH_AW_PROMPT_dcd5b34c2b20e0e6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_2e7019d9465d12fb_EOF' + cat << 'GH_AW_PROMPT_dcd5b34c2b20e0e6_EOF' Tools: add_comment, missing_tool, missing_data, noop - GH_AW_PROMPT_2e7019d9465d12fb_EOF + GH_AW_PROMPT_dcd5b34c2b20e0e6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_2e7019d9465d12fb_EOF' + cat << 'GH_AW_PROMPT_dcd5b34c2b20e0e6_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -290,12 +281,12 @@ jobs: {{/if}} - GH_AW_PROMPT_2e7019d9465d12fb_EOF + GH_AW_PROMPT_dcd5b34c2b20e0e6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_2e7019d9465d12fb_EOF' + cat << 'GH_AW_PROMPT_dcd5b34c2b20e0e6_EOF' ## Serena Code Analysis @@ -332,7 +323,7 @@ jobs: {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/archie.md}} - GH_AW_PROMPT_2e7019d9465d12fb_EOF + GH_AW_PROMPT_dcd5b34c2b20e0e6_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -559,9 +550,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_756f1bb9f50d6955_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_ca474fdaea1f47af_EOF' {"add_comment":{"max":1},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_756f1bb9f50d6955_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_ca474fdaea1f47af_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -754,7 +745,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_0dc2d1bd00cd3635_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_d281e39c2bef3170_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -814,7 +805,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_0dc2d1bd00cd3635_EOF + GH_AW_MCP_CONFIG_d281e39c2bef3170_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1446,7 +1437,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/archie ') || startsWith(github.event.issue.body, '/archie\n') || github.event.issue.body == '/archie') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/archie ') || startsWith(github.event.comment.body, '/archie\n') || github.event.comment.body == '/archie') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/archie ') || startsWith(github.event.comment.body, '/archie\n') || github.event.comment.body == '/archie') && github.event.issue.pull_request != null || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/archie ') || startsWith(github.event.pull_request.body, '/archie\n') || github.event.pull_request.body == '/archie'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/archie.md b/.github/workflows/archie.md index 6c3484d3dea..cbdb5c90b0d 100644 --- a/.github/workflows/archie.md +++ b/.github/workflows/archie.md @@ -4,6 +4,7 @@ description: Generates Mermaid diagrams to visualize issue and pull request rela on: slash_command: name: archie + strategy: centralized events: [issues, issue_comment, pull_request, pull_request_comment] reaction: eyes status-comment: true diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 71f376b7655..2801533b036 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"dbbaf8b75dd3bdafbe8dcac6fe1280216904eb8207df077ebb08651e23b0dd22","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"5863c3180ac0aaf170fe6083be2a8310cef5d121fef48c221fd21aeae6c5dff6","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["BRAVE_API_KEY","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"docker.io/mcp/brave-search","digest":"sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22","pinned_image":"docker.io/mcp/brave-search@sha256:ca96b8acb27d8cf601a8faef86a084602cffa41d8cb18caa1e29ba4d16989d22"},{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -56,10 +56,13 @@ name: "Brave Web Search Agent" "on": - issue_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -78,13 +81,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/brave ') || startsWith(github.event.comment.body, '/brave\n') || github.event.comment.body == '/brave') && github.event.issue.pull_request == null)" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -237,20 +238,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_b28fd8abe8fe56b6_EOF' + cat << 'GH_AW_PROMPT_3b68453501dd6f11_EOF' - GH_AW_PROMPT_b28fd8abe8fe56b6_EOF + GH_AW_PROMPT_3b68453501dd6f11_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_b28fd8abe8fe56b6_EOF' + cat << 'GH_AW_PROMPT_3b68453501dd6f11_EOF' Tools: add_comment, missing_tool, missing_data, noop - GH_AW_PROMPT_b28fd8abe8fe56b6_EOF + GH_AW_PROMPT_3b68453501dd6f11_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_b28fd8abe8fe56b6_EOF' + cat << 'GH_AW_PROMPT_3b68453501dd6f11_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -279,18 +280,18 @@ jobs: {{/if}} - GH_AW_PROMPT_b28fd8abe8fe56b6_EOF + GH_AW_PROMPT_3b68453501dd6f11_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_b28fd8abe8fe56b6_EOF' + cat << 'GH_AW_PROMPT_3b68453501dd6f11_EOF' {{#runtime-import .github/workflows/shared/mcp/brave.md}} {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/brave.md}} - GH_AW_PROMPT_b28fd8abe8fe56b6_EOF + GH_AW_PROMPT_3b68453501dd6f11_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -515,9 +516,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_4a87881f5ed92b05_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_25d0c57c08791256_EOF' {"add_comment":{"max":1},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_4a87881f5ed92b05_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_25d0c57c08791256_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -714,7 +715,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_62428ca1f0fed3d0_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_1bc14b4b122ee402_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "brave-search": { @@ -778,7 +779,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_62428ca1f0fed3d0_EOF + GH_AW_MCP_CONFIG_1bc14b4b122ee402_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1398,7 +1399,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/brave ') || startsWith(github.event.comment.body, '/brave\n') || github.event.comment.body == '/brave') && github.event.issue.pull_request == null)" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/brave.md b/.github/workflows/brave.md index 695fe8f3b27..74ef18a49ef 100644 --- a/.github/workflows/brave.md +++ b/.github/workflows/brave.md @@ -2,6 +2,7 @@ description: Performs web searches using Brave search engine when invoked with /brave command in issues or PRs on: slash_command: + strategy: centralized name: brave events: [issue_comment] permissions: diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 6edd58a03f5..0d07567c51f 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"7580a647bab899ad36421143e1965286e78b15bbb5431b173f3c6afa33b428c2","strict":true,"agent_id":"claude"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"d6432dc724de4e1204e3c0a45bbdd44e700c72795e63ab0638639ebab2b22cec","strict":true,"agent_id":"claude"} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"docker/build-push-action","sha":"bcafcacb16a39f128d818304e6c9c0c18556b85f","version":"v7.1.0"},{"repo":"docker/setup-buildx-action","sha":"4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd","version":"v4.0.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"ghcr.io/github/serena-mcp-server:latest","digest":"sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5","pinned_image":"ghcr.io/github/serena-mcp-server:latest@sha256:bf343399e3725c45528f531a230f3a04521d4cdef29f9a5af6282ff0d3c393c5"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -64,35 +64,13 @@ name: "/cloclo" "on": - discussion: - types: - - created - - edited - - labeled - discussion_comment: - types: - - created - - edited - issue_comment: - types: - - created - - edited - issues: - types: - - opened - - edited - - reopened - - labeled - pull_request: - types: - - opened - - edited - - reopened - - labeled - pull_request_review_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -112,14 +90,18 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/cloclo ') || startsWith(github.event.issue.body, '/cloclo\n') || github.event.issue.body == '/cloclo') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/cloclo ') || startsWith(github.event.pull_request.body, '/cloclo\n') || github.event.pull_request.body == '/cloclo') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/cloclo ') || startsWith(github.event.discussion.body, '/cloclo\n') || github.event.discussion.body == '/cloclo') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') || github.event_name == 'issues' && github.event.label.name == 'cloclo' || github.event_name == 'pull_request' && github.event.label.name == 'cloclo' || github.event_name == 'discussion' && github.event.label.name == 'cloclo')" + if: > + needs.pre_activation.outputs.activated == 'true' && ((fromJSON(github.event.inputs.aw_context || '{}').event_type == 'issues' || + fromJSON(github.event.inputs.aw_context || '{}').event_type == 'pull_request' || fromJSON(github.event.inputs.aw_context || + '{}').event_type == 'discussion') && fromJSON(github.event.inputs.aw_context || '{}').trigger_label == 'cloclo' || + (!(fromJSON(github.event.inputs.aw_context || '{}').event_type == 'issues')) && (!(fromJSON(github.event.inputs.aw_context || + '{}').event_type == 'pull_request')) && (!(fromJSON(github.event.inputs.aw_context || '{}').event_type == 'discussion'))) runs-on: ubuntu-slim permissions: actions: read contents: read discussions: write issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -293,9 +275,9 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_ecf4dc9a517c46f0_EOF' + cat << 'GH_AW_PROMPT_a2de0c26aa2bcf08_EOF' - GH_AW_PROMPT_ecf4dc9a517c46f0_EOF + GH_AW_PROMPT_a2de0c26aa2bcf08_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" @@ -303,16 +285,16 @@ jobs: cat "${RUNNER_TEMP}/gh-aw/prompts/agentic_workflows_guide.md" cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_ecf4dc9a517c46f0_EOF' + cat << 'GH_AW_PROMPT_a2de0c26aa2bcf08_EOF' Tools: add_comment, create_pull_request, missing_tool, missing_data, noop - GH_AW_PROMPT_ecf4dc9a517c46f0_EOF + GH_AW_PROMPT_a2de0c26aa2bcf08_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" - cat << 'GH_AW_PROMPT_ecf4dc9a517c46f0_EOF' + cat << 'GH_AW_PROMPT_a2de0c26aa2bcf08_EOF' - GH_AW_PROMPT_ecf4dc9a517c46f0_EOF + GH_AW_PROMPT_a2de0c26aa2bcf08_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_ecf4dc9a517c46f0_EOF' + cat << 'GH_AW_PROMPT_a2de0c26aa2bcf08_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -341,12 +323,12 @@ jobs: {{/if}} - GH_AW_PROMPT_ecf4dc9a517c46f0_EOF + GH_AW_PROMPT_a2de0c26aa2bcf08_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_ecf4dc9a517c46f0_EOF' + cat << 'GH_AW_PROMPT_a2de0c26aa2bcf08_EOF' ## Serena Code Analysis @@ -385,7 +367,7 @@ jobs: {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/cloclo.md}} - GH_AW_PROMPT_ecf4dc9a517c46f0_EOF + GH_AW_PROMPT_a2de0c26aa2bcf08_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -712,9 +694,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_576530abb8320c80_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_5b26a7d42b804af3_EOF' {"add_comment":{"max":1},"create_pull_request":{"excluded_files":[".github/workflows/*.lock.yml"],"expires":48,"labels":["automation","cloclo"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","CLAUDE.md","AGENTS.md"],"protected_files_policy":"fallback-to-issue","title_prefix":"[cloclo] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_576530abb8320c80_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_5b26a7d42b804af3_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -952,7 +934,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/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 DOCKER_HOST=unix:///var/run/docker.sock -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_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -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 -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -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.3.6' GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_6cb18be545797135_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_e54dacca88c9d411_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "agenticworkflows": { @@ -1042,7 +1024,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_6cb18be545797135_EOF + GH_AW_MCP_CONFIG_e54dacca88c9d411_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1766,7 +1748,12 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/cloclo ') || startsWith(github.event.issue.body, '/cloclo\n') || github.event.issue.body == '/cloclo') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/cloclo ') || startsWith(github.event.pull_request.body, '/cloclo\n') || github.event.pull_request.body == '/cloclo') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/cloclo ') || startsWith(github.event.discussion.body, '/cloclo\n') || github.event.discussion.body == '/cloclo') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/cloclo ') || startsWith(github.event.comment.body, '/cloclo\n') || github.event.comment.body == '/cloclo') || github.event_name == 'issues' && github.event.label.name == 'cloclo' || github.event_name == 'pull_request' && github.event.label.name == 'cloclo' || github.event_name == 'discussion' && github.event.label.name == 'cloclo')" + if: > + (fromJSON(github.event.inputs.aw_context || '{}').event_type == 'issues' || fromJSON(github.event.inputs.aw_context || + '{}').event_type == 'pull_request' || fromJSON(github.event.inputs.aw_context || '{}').event_type == 'discussion') && + fromJSON(github.event.inputs.aw_context || '{}').trigger_label == 'cloclo' || (!(fromJSON(github.event.inputs.aw_context || + '{}').event_type == 'issues')) && (!(fromJSON(github.event.inputs.aw_context || '{}').event_type == 'pull_request')) && + (!(fromJSON(github.event.inputs.aw_context || '{}').event_type == 'discussion')) runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/cloclo.md b/.github/workflows/cloclo.md index 75fe83b78bd..57607e0ca11 100644 --- a/.github/workflows/cloclo.md +++ b/.github/workflows/cloclo.md @@ -2,6 +2,7 @@ on: slash_command: name: cloclo + strategy: centralized label_command: cloclo status-comment: true permissions: diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index 5906f75db07..7cdfaad4912 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"647b315d95e193719dded1c53ac8c40e9afc9482b5dc15e78c39ec2cde767bbe","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"d2f9af0fec608b35c7888471217fe1843f22037ba589027cc5e52ede8afba8d3","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -55,11 +55,13 @@ name: "Workflow Craft Agent" "on": - issues: - types: - - opened - - edited - - reopened + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -78,12 +80,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/craft ') || startsWith(github.event.issue.body, '/craft\n') || github.event.issue.body == '/craft'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -235,23 +236,23 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_aada0b6cd6fb8084_EOF' + cat << 'GH_AW_PROMPT_2d44bd4d81e20334_EOF' - GH_AW_PROMPT_aada0b6cd6fb8084_EOF + GH_AW_PROMPT_2d44bd4d81e20334_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_aada0b6cd6fb8084_EOF' + cat << 'GH_AW_PROMPT_2d44bd4d81e20334_EOF' Tools: add_comment, push_to_pull_request_branch, missing_tool, missing_data, noop - GH_AW_PROMPT_aada0b6cd6fb8084_EOF + GH_AW_PROMPT_2d44bd4d81e20334_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_aada0b6cd6fb8084_EOF' + cat << 'GH_AW_PROMPT_2d44bd4d81e20334_EOF' - GH_AW_PROMPT_aada0b6cd6fb8084_EOF + GH_AW_PROMPT_2d44bd4d81e20334_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_aada0b6cd6fb8084_EOF' + cat << 'GH_AW_PROMPT_2d44bd4d81e20334_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -280,7 +281,7 @@ jobs: {{/if}} - GH_AW_PROMPT_aada0b6cd6fb8084_EOF + GH_AW_PROMPT_2d44bd4d81e20334_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" @@ -288,12 +289,12 @@ jobs: if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_push_to_pr_branch_guidance.md" fi - cat << 'GH_AW_PROMPT_aada0b6cd6fb8084_EOF' + cat << 'GH_AW_PROMPT_2d44bd4d81e20334_EOF' {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/craft.md}} - GH_AW_PROMPT_aada0b6cd6fb8084_EOF + GH_AW_PROMPT_2d44bd4d81e20334_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -520,9 +521,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_7fd0064ae66ac97a_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_7debd37bc8486a79_EOF' {"add_comment":{"max":1},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"]},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_7fd0064ae66ac97a_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_7debd37bc8486a79_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -735,7 +736,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_d8bf39a78dbb4f1f_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_d8b7ac7de46cac63_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -766,7 +767,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_d8bf39a78dbb4f1f_EOF + GH_AW_MCP_CONFIG_d8b7ac7de46cac63_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1400,7 +1401,6 @@ jobs: } pre_activation: - if: "github.event_name == 'issues' && (startsWith(github.event.issue.body, '/craft ') || startsWith(github.event.issue.body, '/craft\n') || github.event.issue.body == '/craft')" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/craft.md b/.github/workflows/craft.md index c5c66147d5a..5d0cffbfe4f 100644 --- a/.github/workflows/craft.md +++ b/.github/workflows/craft.md @@ -2,6 +2,7 @@ description: Generates new agentic workflow markdown files based on user requests when invoked with /craft command on: slash_command: + strategy: centralized name: craft events: [issues] permissions: diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index bfb3c4c7865..34928aae7cb 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"23bea2711923688e67283b0f1fc2e4da195a90f857e2ba7b7121e047e97dd601","strict":true,"agent_id":"codex"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"9d02389aa226846bcccda9bc0415279f27dc508a04cd597f0eb9d89ae9cf142f","strict":true,"agent_id":"codex"} # gh-aw-manifest: {"version":1,"secrets":["CODEX_API_KEY","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -57,14 +57,13 @@ name: "Grumpy Code Reviewer 🔥" "on": - issue_comment: - types: - - created - - edited - pull_request_review_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -83,13 +82,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -248,20 +245,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_39fbcedd9459ea8c_EOF' + cat << 'GH_AW_PROMPT_6f47da74927b7aa3_EOF' - GH_AW_PROMPT_39fbcedd9459ea8c_EOF + GH_AW_PROMPT_6f47da74927b7aa3_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_39fbcedd9459ea8c_EOF' + cat << 'GH_AW_PROMPT_6f47da74927b7aa3_EOF' Tools: create_pull_request_review_comment(max:5), submit_pull_request_review, missing_tool, missing_data, noop - GH_AW_PROMPT_39fbcedd9459ea8c_EOF + GH_AW_PROMPT_6f47da74927b7aa3_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_39fbcedd9459ea8c_EOF' + cat << 'GH_AW_PROMPT_6f47da74927b7aa3_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -290,19 +287,19 @@ jobs: {{/if}} - GH_AW_PROMPT_39fbcedd9459ea8c_EOF + GH_AW_PROMPT_6f47da74927b7aa3_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_39fbcedd9459ea8c_EOF' + cat << 'GH_AW_PROMPT_6f47da74927b7aa3_EOF' {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/github-guard-policy.md}} {{#runtime-import .github/workflows/shared/pr-code-review-config.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/grumpy-reviewer.md}} - GH_AW_PROMPT_39fbcedd9459ea8c_EOF + GH_AW_PROMPT_6f47da74927b7aa3_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -519,9 +516,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_cc72f63e4152b950_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_e46af0b67b311977_EOF' {"create_pull_request_review_comment":{"max":5,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_cc72f63e4152b950_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_e46af0b67b311977_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -748,7 +745,7 @@ jobs: DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/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 DOCKER_HOST=unix:///var/run/docker.sock -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_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -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 -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e CODEX_HOME -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.3.6' - cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_dd9a8dab989d200f_EOF + cat > "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" << GH_AW_MCP_CONFIG_dbaa8ab6db466e86_EOF [history] persistence = "none" @@ -775,11 +772,11 @@ jobs: [mcp_servers.safeoutputs."guard-policies".write-sink] accept = ["*"] - GH_AW_MCP_CONFIG_dd9a8dab989d200f_EOF + GH_AW_MCP_CONFIG_dbaa8ab6db466e86_EOF # Generate JSON config for MCP gateway GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_dd9a8dab989d200f_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_dbaa8ab6db466e86_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -828,11 +825,11 @@ jobs: } } } - GH_AW_MCP_CONFIG_dd9a8dab989d200f_EOF + GH_AW_MCP_CONFIG_dbaa8ab6db466e86_EOF # Sync converter output to writable CODEX_HOME for Codex mkdir -p /tmp/gh-aw/mcp-config - cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_812e28467daf6039_EOF + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_c0013a2f2f45c0dd_EOF model_provider = "openai-proxy" @@ -844,7 +841,7 @@ jobs: [shell_environment_policy] inherit = "core" include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] - GH_AW_CODEX_SHELL_POLICY_812e28467daf6039_EOF + GH_AW_CODEX_SHELL_POLICY_c0013a2f2f45c0dd_EOF awk ' BEGIN { skip_openai_proxy = 0 } /^[[:space:]]*model_provider[[:space:]]*=/ { next } @@ -1516,7 +1513,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/grumpy ') || startsWith(github.event.comment.body, '/grumpy\n') || github.event.comment.body == '/grumpy'))" runs-on: ubuntu-slim permissions: contents: read @@ -1666,4 +1662,3 @@ jobs: /tmp/gh-aw/safe-output-items.jsonl /tmp/gh-aw/temporary-id-map.json if-no-files-found: ignore - diff --git a/.github/workflows/grumpy-reviewer.md b/.github/workflows/grumpy-reviewer.md index 75c09da7c28..89396279100 100644 --- a/.github/workflows/grumpy-reviewer.md +++ b/.github/workflows/grumpy-reviewer.md @@ -2,6 +2,7 @@ description: "⚠️ DEPRECATED: Use PR Code Quality Reviewer (pr-code-quality-reviewer) instead. Performs critical code review with a focus on edge cases, potential bugs, and code quality issues" on: slash_command: + strategy: centralized name: grumpy events: [pull_request_comment, pull_request_review_comment] engine: codex diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index bd51ea468aa..bed3651829b 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"dddd808f28e606992cc4a49196e37ad53b279940521ac1684463d063146a8b55","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"1e59cb388299dac6324e73c39f559081b07e5b3d705ba2d98fb0d08e3cec43cd","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -56,10 +56,13 @@ name: "Mergefest" "on": - issue_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -78,13 +81,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/mergefest ') || startsWith(github.event.comment.body, '/mergefest\n') || github.event.comment.body == '/mergefest') && github.event.issue.pull_request != null)" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -240,23 +241,23 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_55410a7f0473a4e4_EOF' + cat << 'GH_AW_PROMPT_e5875d0a04012b37_EOF' - GH_AW_PROMPT_55410a7f0473a4e4_EOF + GH_AW_PROMPT_e5875d0a04012b37_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_55410a7f0473a4e4_EOF' + cat << 'GH_AW_PROMPT_e5875d0a04012b37_EOF' Tools: push_to_pull_request_branch, missing_tool, missing_data, noop - GH_AW_PROMPT_55410a7f0473a4e4_EOF + GH_AW_PROMPT_e5875d0a04012b37_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_55410a7f0473a4e4_EOF' + cat << 'GH_AW_PROMPT_e5875d0a04012b37_EOF' - GH_AW_PROMPT_55410a7f0473a4e4_EOF + GH_AW_PROMPT_e5875d0a04012b37_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_55410a7f0473a4e4_EOF' + cat << 'GH_AW_PROMPT_e5875d0a04012b37_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -285,7 +286,7 @@ jobs: {{/if}} - GH_AW_PROMPT_55410a7f0473a4e4_EOF + GH_AW_PROMPT_e5875d0a04012b37_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" @@ -293,12 +294,12 @@ jobs: if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_push_to_pr_branch_guidance.md" fi - cat << 'GH_AW_PROMPT_55410a7f0473a4e4_EOF' + cat << 'GH_AW_PROMPT_e5875d0a04012b37_EOF' {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/mergefest.md}} - GH_AW_PROMPT_55410a7f0473a4e4_EOF + GH_AW_PROMPT_e5875d0a04012b37_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -520,9 +521,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_fefaa16691f3aa98_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_eb16dc90db12aa10_EOF' {"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"]},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_fefaa16691f3aa98_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_eb16dc90db12aa10_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -711,7 +712,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_439f36d9a639d867_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_3a0203afbcaea5d1_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -742,7 +743,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_439f36d9a639d867_EOF + GH_AW_MCP_CONFIG_3a0203afbcaea5d1_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1413,7 +1414,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/mergefest ') || startsWith(github.event.comment.body, '/mergefest\n') || github.event.comment.body == '/mergefest') && github.event.issue.pull_request != null)" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/mergefest.md b/.github/workflows/mergefest.md index b0981018550..4b7124d4676 100644 --- a/.github/workflows/mergefest.md +++ b/.github/workflows/mergefest.md @@ -3,6 +3,7 @@ name: Mergefest description: Automatically merges the main branch into pull request branches when invoked with /mergefest command on: slash_command: + strategy: centralized name: mergefest events: [pull_request_comment] permissions: diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index 63939d422ac..398a20ade3e 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"86cfe015e5b74d00b4b579cdee3237db81332ce7848f79c666939fcef84d3f05","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"b3266673ee13f7a563f367e15facff38f8245763cf4cd906c263b2910fa249ff","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"mcp/markitdown","digest":"sha256:1cef3bf502503310ed0884441874ccf6cdaac20136dc1179797fa048269dc4cb","pinned_image":"mcp/markitdown@sha256:1cef3bf502503310ed0884441874ccf6cdaac20136dc1179797fa048269dc4cb"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -59,15 +59,6 @@ name: "Resource Summarizer Agent" "on": - issue_comment: - types: - - created - - edited - issues: - types: - - opened - - edited - - reopened workflow_dispatch: inputs: aw_context: @@ -102,13 +93,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && ((github.event_name == 'issue_comment' || github.event_name == 'issues') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/summarize ') || startsWith(github.event.issue.body, '/summarize\n') || github.event.issue.body == '/summarize') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/summarize ') || startsWith(github.event.comment.body, '/summarize\n') || github.event.comment.body == '/summarize') && github.event.issue.pull_request == null) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'issues')))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -269,21 +258,21 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_e89cb5c69ed2ea3d_EOF' + cat << 'GH_AW_PROMPT_3bff113074d94d42_EOF' - GH_AW_PROMPT_e89cb5c69ed2ea3d_EOF + GH_AW_PROMPT_3bff113074d94d42_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_e89cb5c69ed2ea3d_EOF' + cat << 'GH_AW_PROMPT_3bff113074d94d42_EOF' Tools: add_comment, create_discussion, missing_tool, missing_data, noop - GH_AW_PROMPT_e89cb5c69ed2ea3d_EOF + GH_AW_PROMPT_3bff113074d94d42_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_e89cb5c69ed2ea3d_EOF' + cat << 'GH_AW_PROMPT_3bff113074d94d42_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -312,19 +301,19 @@ jobs: {{/if}} - GH_AW_PROMPT_e89cb5c69ed2ea3d_EOF + GH_AW_PROMPT_3bff113074d94d42_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_e89cb5c69ed2ea3d_EOF' + cat << 'GH_AW_PROMPT_3bff113074d94d42_EOF' {{#runtime-import .github/workflows/shared/mcp/markitdown.md}} {{#runtime-import .github/workflows/shared/reporting.md}} {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/pdf-summary.md}} - GH_AW_PROMPT_e89cb5c69ed2ea3d_EOF + GH_AW_PROMPT_3bff113074d94d42_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -576,9 +565,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_ca3592545c7b7366_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_4f5f6f9e4092e0b3_EOF' {"add_comment":{"max":1},"create_discussion":{"expires":24,"fallback_to_issue":true,"max":1},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_ca3592545c7b7366_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_4f5f6f9e4092e0b3_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -801,7 +790,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_52124769a2adfc71_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_23c6fc4a7264e17e_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -862,7 +851,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_52124769a2adfc71_EOF + GH_AW_MCP_CONFIG_23c6fc4a7264e17e_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1495,7 +1484,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && ((github.event_name == 'issue_comment' || github.event_name == 'issues') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/summarize ') || startsWith(github.event.issue.body, '/summarize\n') || github.event.issue.body == '/summarize') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/summarize ') || startsWith(github.event.comment.body, '/summarize\n') || github.event.comment.body == '/summarize') && github.event.issue.pull_request == null) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'issues')))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/pdf-summary.md b/.github/workflows/pdf-summary.md index 8b6a5fc62d5..a3dca276584 100644 --- a/.github/workflows/pdf-summary.md +++ b/.github/workflows/pdf-summary.md @@ -3,6 +3,7 @@ description: pdf summarizer on: # Command trigger - responds to /summarize mentions slash_command: + strategy: centralized name: summarize events: [issue_comment, issues] diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index a03b7b4efd8..24696c123ef 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"887a55cc6c6e594c3f24ef3fe4daecfdba94bee6b055bb70f270cf40757a1d9d","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"026d0f78766e2ceb57211592f0964e334c5c2a6c9038cdb7d61b90084cf58bee","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -54,14 +54,13 @@ name: "Plan Command" "on": - discussion_comment: - types: - - created - - edited - issue_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -80,14 +79,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/plan ') || startsWith(github.event.comment.body, '/plan\n') || github.event.comment.body == '/plan') && github.event.issue.pull_request == null || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/plan ') || startsWith(github.event.comment.body, '/plan\n') || github.event.comment.body == '/plan'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - discussions: write - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -244,20 +240,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_089bad1a80abb9ff_EOF' + cat << 'GH_AW_PROMPT_a29e2f4de291b4b6_EOF' - GH_AW_PROMPT_089bad1a80abb9ff_EOF + GH_AW_PROMPT_a29e2f4de291b4b6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_089bad1a80abb9ff_EOF' + cat << 'GH_AW_PROMPT_a29e2f4de291b4b6_EOF' Tools: create_issue(max:5), close_discussion, missing_tool, missing_data, noop - GH_AW_PROMPT_089bad1a80abb9ff_EOF + GH_AW_PROMPT_a29e2f4de291b4b6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_089bad1a80abb9ff_EOF' + cat << 'GH_AW_PROMPT_a29e2f4de291b4b6_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -286,17 +282,17 @@ jobs: {{/if}} - GH_AW_PROMPT_089bad1a80abb9ff_EOF + GH_AW_PROMPT_a29e2f4de291b4b6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_089bad1a80abb9ff_EOF' + cat << 'GH_AW_PROMPT_a29e2f4de291b4b6_EOF' {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/plan.md}} - GH_AW_PROMPT_089bad1a80abb9ff_EOF + GH_AW_PROMPT_a29e2f4de291b4b6_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -516,9 +512,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_b1879aea4a628246_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_69791fdf79cc167e_EOF' {"close_discussion":{"max":1},"create_issue":{"expires":48,"group":true,"labels":["plan","ai-generated","cookie"],"max":5,"title_prefix":"[plan] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_b1879aea4a628246_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_69791fdf79cc167e_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -753,7 +749,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_9c51c2aca32856ed_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_02799307106636b8_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -784,7 +780,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_9c51c2aca32856ed_EOF + GH_AW_MCP_CONFIG_02799307106636b8_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1414,7 +1410,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/plan ') || startsWith(github.event.comment.body, '/plan\n') || github.event.comment.body == '/plan') && github.event.issue.pull_request == null || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/plan ') || startsWith(github.event.comment.body, '/plan\n') || github.event.comment.body == '/plan'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/plan.md b/.github/workflows/plan.md index c7c4cac3c55..58690261c9c 100644 --- a/.github/workflows/plan.md +++ b/.github/workflows/plan.md @@ -3,6 +3,7 @@ name: Plan Command description: Generates project plans and task breakdowns when invoked with /plan command in issues or PRs on: slash_command: + strategy: centralized name: plan events: [issue_comment, discussion_comment] permissions: diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index f31d5026ab8..331affab1dd 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"e2c4f0dfc67f4f51cbee99091ba080bca77cf9164e98dc76ee1fc520c74fda2a","strict":true,"agent_id":"copilot","agent_model":"gpt-5"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"5c5c0e5e9d5f891afe434dcb9605ecb1177c6c0626347193395a9f8eb0450c4a","strict":true,"agent_id":"copilot","agent_model":"gpt-5"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_AGENT_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -59,11 +59,6 @@ name: "Poem Bot - A Creative Agentic Workflow" "on": - issues: - types: - - opened - - edited - - reopened # 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 @@ -96,12 +91,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/poem-bot ') || startsWith(github.event.issue.body, '/poem-bot\n') || github.event.issue.body == '/poem-bot') || !(github.event_name == 'issues'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -260,25 +254,25 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_90ec2e1aca70d3cc_EOF' + cat << 'GH_AW_PROMPT_db0850897dc8fed9_EOF' - GH_AW_PROMPT_90ec2e1aca70d3cc_EOF + GH_AW_PROMPT_db0850897dc8fed9_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_90ec2e1aca70d3cc_EOF' + cat << 'GH_AW_PROMPT_db0850897dc8fed9_EOF' Tools: add_comment(max:3), create_issue(max:2), update_issue(max:2), create_discussion(max:2), create_agent_session, create_pull_request, close_pull_request(max:2), create_pull_request_review_comment(max:2), add_labels(max:5), push_to_pull_request_branch, link_sub_issue(max:3), missing_tool, missing_data, noop - GH_AW_PROMPT_90ec2e1aca70d3cc_EOF + GH_AW_PROMPT_db0850897dc8fed9_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_90ec2e1aca70d3cc_EOF' + cat << 'GH_AW_PROMPT_db0850897dc8fed9_EOF' - GH_AW_PROMPT_90ec2e1aca70d3cc_EOF + GH_AW_PROMPT_db0850897dc8fed9_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_90ec2e1aca70d3cc_EOF' + cat << 'GH_AW_PROMPT_db0850897dc8fed9_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -307,7 +301,7 @@ jobs: {{/if}} - GH_AW_PROMPT_90ec2e1aca70d3cc_EOF + GH_AW_PROMPT_db0850897dc8fed9_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" @@ -315,13 +309,13 @@ jobs: if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_push_to_pr_branch_guidance.md" fi - cat << 'GH_AW_PROMPT_90ec2e1aca70d3cc_EOF' + cat << 'GH_AW_PROMPT_db0850897dc8fed9_EOF' {{#runtime-import .github/workflows/shared/reporting.md}} {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/poem-bot.md}} - GH_AW_PROMPT_90ec2e1aca70d3cc_EOF + GH_AW_PROMPT_db0850897dc8fed9_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -567,9 +561,9 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs/upload-artifacts" - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_f834e6394511397e_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_cb3537d68b1f412c_EOF' {"add_comment":{"max":3,"target":"*"},"add_labels":{"allowed":["poetry","creative","automation","ai-generated","epic","haiku","sonnet","limerick"],"max":5},"close_pull_request":{"max":2,"required_labels":["poetry","automation"],"required_title_prefix":"[🎨 POETRY]","target":"*"},"create_agent_session":{"base":"main","max":1},"create_discussion":{"category":"audits","close_older_discussions":true,"expires":24,"fallback_to_issue":true,"labels":["poetry","automation","ai-generated"],"max":2,"title_prefix":"[📜 POETRY] "},"create_issue":{"expires":48,"group":true,"labels":["poetry","automation","ai-generated"],"max":2,"title_prefix":"[🎭 POEM-BOT] "},"create_pull_request":{"draft":false,"expires":48,"labels":["poetry","automation","creative-writing"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"reviewers":["copilot"],"title_prefix":"[🎨 POETRY] "},"create_pull_request_review_comment":{"max":2,"side":"RIGHT"},"create_report_incomplete_issue":{},"link_sub_issue":{"max":3,"parent_required_labels":["poetry","epic"],"parent_title_prefix":"[🎭 POEM-BOT]","sub_required_labels":["poetry"],"sub_title_prefix":"[🎭 POEM-BOT]"},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"]},"report_incomplete":{},"update_issue":{"allow_body":true,"allow_status":true,"allow_title":true,"max":2,"target":"*"},"upload_artifact":{"max-size-bytes":104857600,"max-uploads":1,"retention-days":30}} - GH_AW_SAFE_OUTPUTS_CONFIG_f834e6394511397e_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_cb3537d68b1f412c_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -1055,7 +1049,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_549632f40b32a334_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_90ae4e82d77594f5_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -1086,7 +1080,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_549632f40b32a334_EOF + GH_AW_MCP_CONFIG_90ae4e82d77594f5_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1769,7 +1763,6 @@ jobs: } pre_activation: - if: "github.event_name == 'issues' && (startsWith(github.event.issue.body, '/poem-bot ') || startsWith(github.event.issue.body, '/poem-bot\n') || github.event.issue.body == '/poem-bot') || !(github.event_name == 'issues')" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/poem-bot.md b/.github/workflows/poem-bot.md index cdcf0c050c4..3d3ad5b203d 100644 --- a/.github/workflows/poem-bot.md +++ b/.github/workflows/poem-bot.md @@ -7,6 +7,7 @@ on: - maintainer # Command trigger - responds to /poem-bot mentions slash_command: + strategy: centralized name: poem-bot events: [issues] diff --git a/.github/workflows/pr-code-quality-reviewer.lock.yml b/.github/workflows/pr-code-quality-reviewer.lock.yml index 9310a0f1b24..84d1c982d41 100644 --- a/.github/workflows/pr-code-quality-reviewer.lock.yml +++ b/.github/workflows/pr-code-quality-reviewer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"c18ea01b6d34f3dc5d81d6a5c1cadcdad6534225f38da1392a0bf208fd613912","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"d6729fad4635b3651867bf7ce32aaabcc9882db24b35a7caea43ceb9013ea041","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -57,17 +57,16 @@ name: "PR Code Quality Reviewer" "on": - issue_comment: - types: - - created - - edited pull_request: types: - ready_for_review - pull_request_review_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -86,7 +85,8 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/review ') || startsWith(github.event.comment.body, '/review\n') || github.event.comment.body == '/review') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/review ') || startsWith(github.event.comment.body, '/review\n') || github.event.comment.body == '/review')) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request_review_comment'))) && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id))" + if: > + needs.pre_activation.outputs.activated == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id) runs-on: ubuntu-slim permissions: actions: read @@ -250,20 +250,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_1d5c9b3277c6f72e_EOF' + cat << 'GH_AW_PROMPT_8c0279fb49dd3fb9_EOF' - GH_AW_PROMPT_1d5c9b3277c6f72e_EOF + GH_AW_PROMPT_8c0279fb49dd3fb9_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_1d5c9b3277c6f72e_EOF' + cat << 'GH_AW_PROMPT_8c0279fb49dd3fb9_EOF' Tools: create_pull_request_review_comment(max:10), submit_pull_request_review, missing_tool, missing_data, noop - GH_AW_PROMPT_1d5c9b3277c6f72e_EOF + GH_AW_PROMPT_8c0279fb49dd3fb9_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_1d5c9b3277c6f72e_EOF' + cat << 'GH_AW_PROMPT_8c0279fb49dd3fb9_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -292,12 +292,12 @@ jobs: {{/if}} - GH_AW_PROMPT_1d5c9b3277c6f72e_EOF + GH_AW_PROMPT_8c0279fb49dd3fb9_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_1d5c9b3277c6f72e_EOF' + cat << 'GH_AW_PROMPT_8c0279fb49dd3fb9_EOF' {{#runtime-import .github/workflows/shared/reporting.md}} {{#runtime-import .github/workflows/shared/observability-otlp.md}} @@ -305,7 +305,7 @@ jobs: {{#runtime-import .github/workflows/shared/pr-code-review-config.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/pr-code-quality-reviewer.md}} - GH_AW_PROMPT_1d5c9b3277c6f72e_EOF + GH_AW_PROMPT_8c0279fb49dd3fb9_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -523,9 +523,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_84a7c5b2ab212904_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_863d1eaee434fe82_EOF' {"create_pull_request_review_comment":{"max":10,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_84a7c5b2ab212904_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_863d1eaee434fe82_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -753,7 +753,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_1a0c4c254705b459_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_004a822449ebe2f9_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -803,7 +803,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_1a0c4c254705b459_EOF + GH_AW_MCP_CONFIG_004a822449ebe2f9_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1421,7 +1421,7 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment') && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/review ') || startsWith(github.event.comment.body, '/review\n') || github.event.comment.body == '/review') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/review ') || startsWith(github.event.comment.body, '/review\n') || github.event.comment.body == '/review')) || (!(github.event_name == 'issue_comment')) && (!(github.event_name == 'pull_request_review_comment'))) && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id))" + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/pr-code-quality-reviewer.md b/.github/workflows/pr-code-quality-reviewer.md index ced7831c276..4586c621714 100644 --- a/.github/workflows/pr-code-quality-reviewer.md +++ b/.github/workflows/pr-code-quality-reviewer.md @@ -5,6 +5,7 @@ on: pull_request: types: [ready_for_review] slash_command: + strategy: centralized name: review events: [pull_request_comment, pull_request_review_comment] engine: copilot diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index e24da6f83bc..5a78539af8f 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"c94236800f17adfe450e3390fea4bebe59cb000bc39f80d372f198e18aabca91","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a7457716308b1ae1d9ed4aa5f413e5e93f182f1cff82973fa121caac63da12d9","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -57,14 +57,13 @@ name: "PR Nitpick Reviewer 🔍" "on": - issue_comment: - types: - - created - - edited - pull_request_review_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -83,13 +82,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/nit ') || startsWith(github.event.comment.body, '/nit\n') || github.event.comment.body == '/nit') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/nit ') || startsWith(github.event.comment.body, '/nit\n') || github.event.comment.body == '/nit'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -247,20 +244,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_1db9accd26153e71_EOF' + cat << 'GH_AW_PROMPT_1d2e5717dce668a8_EOF' - GH_AW_PROMPT_1db9accd26153e71_EOF + GH_AW_PROMPT_1d2e5717dce668a8_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_1db9accd26153e71_EOF' + cat << 'GH_AW_PROMPT_1d2e5717dce668a8_EOF' Tools: create_discussion, create_pull_request_review_comment(max:10), submit_pull_request_review, missing_tool, missing_data, noop - GH_AW_PROMPT_1db9accd26153e71_EOF + GH_AW_PROMPT_1d2e5717dce668a8_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_1db9accd26153e71_EOF' + cat << 'GH_AW_PROMPT_1d2e5717dce668a8_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -289,12 +286,12 @@ jobs: {{/if}} - GH_AW_PROMPT_1db9accd26153e71_EOF + GH_AW_PROMPT_1d2e5717dce668a8_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_1db9accd26153e71_EOF' + cat << 'GH_AW_PROMPT_1d2e5717dce668a8_EOF' {{#runtime-import .github/workflows/shared/reporting.md}} {{#runtime-import .github/workflows/shared/observability-otlp.md}} @@ -302,7 +299,7 @@ jobs: {{#runtime-import .github/workflows/shared/pr-code-review-config.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/pr-nitpick-reviewer.md}} - GH_AW_PROMPT_1db9accd26153e71_EOF + GH_AW_PROMPT_1d2e5717dce668a8_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -522,9 +519,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_790258275bdd5d64_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_c8c5b7a57e1a5776_EOF' {"create_discussion":{"category":"audits","expires":24,"fallback_to_issue":true,"max":1,"title_prefix":"[nitpick-report] "},"create_pull_request_review_comment":{"max":10,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_790258275bdd5d64_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_c8c5b7a57e1a5776_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -779,7 +776,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_6906613e777e3245_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_a5bcb2b00d471f53_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -829,7 +826,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_6906613e777e3245_EOF + GH_AW_MCP_CONFIG_a5bcb2b00d471f53_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1451,7 +1448,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/nit ') || startsWith(github.event.comment.body, '/nit\n') || github.event.comment.body == '/nit') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/nit ') || startsWith(github.event.comment.body, '/nit\n') || github.event.comment.body == '/nit'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/pr-nitpick-reviewer.md b/.github/workflows/pr-nitpick-reviewer.md index 2fb5a148b06..c7fbbb13f4f 100644 --- a/.github/workflows/pr-nitpick-reviewer.md +++ b/.github/workflows/pr-nitpick-reviewer.md @@ -2,6 +2,7 @@ description: "⚠️ DEPRECATED: Use PR Code Quality Reviewer (pr-code-quality-reviewer) instead. Provides detailed nitpicky code review focusing on style, best practices, and minor improvements" on: slash_command: + strategy: centralized name: nit events: [pull_request_comment, pull_request_review_comment] permissions: diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml index e47d23ca047..c1ccd6e5060 100644 --- a/.github/workflows/security-review.lock.yml +++ b/.github/workflows/security-review.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"b5ee0a94cfa9054fa0ddfe28c5087c23b35e2bdf1cfbdabb2f5a62c1487826cd","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"6f257aa3430a6a28d7d7fbd318c28617caebfc871ad1774cb7bc2347beae9f25","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"docker/build-push-action","sha":"bcafcacb16a39f128d818304e6c9c0c18556b85f","version":"v7.1.0"},{"repo":"docker/setup-buildx-action","sha":"4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd","version":"v4.0.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -59,14 +59,13 @@ name: "Security Review Agent 🔒" "on": - issue_comment: - types: - - created - - edited - pull_request_review_comment: - types: - - created - - edited + workflow_dispatch: + inputs: + aw_context: + default: "" + description: Agent caller context (used internally by Agentic Workflows). + required: false + type: string permissions: {} @@ -85,13 +84,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/security-review ') || startsWith(github.event.comment.body, '/security-review\n') || github.event.comment.body == '/security-review') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/security-review ') || startsWith(github.event.comment.body, '/security-review\n') || github.event.comment.body == '/security-review'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -243,21 +240,21 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_b8421cf2a0582e37_EOF' + cat << 'GH_AW_PROMPT_5307939dd77dc999_EOF' - GH_AW_PROMPT_b8421cf2a0582e37_EOF + GH_AW_PROMPT_5307939dd77dc999_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/agentic_workflows_guide.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_b8421cf2a0582e37_EOF' + cat << 'GH_AW_PROMPT_5307939dd77dc999_EOF' Tools: create_pull_request_review_comment(max:10), submit_pull_request_review, missing_tool, missing_data, noop - GH_AW_PROMPT_b8421cf2a0582e37_EOF + GH_AW_PROMPT_5307939dd77dc999_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_b8421cf2a0582e37_EOF' + cat << 'GH_AW_PROMPT_5307939dd77dc999_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -286,12 +283,12 @@ jobs: {{/if}} - GH_AW_PROMPT_b8421cf2a0582e37_EOF + GH_AW_PROMPT_5307939dd77dc999_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" fi - cat << 'GH_AW_PROMPT_b8421cf2a0582e37_EOF' + cat << 'GH_AW_PROMPT_5307939dd77dc999_EOF' {{#runtime-import .github/workflows/shared/security-analysis-base.md}} {{#runtime-import .github/workflows/shared/observability-otlp.md}} @@ -299,7 +296,7 @@ jobs: {{#runtime-import .github/workflows/shared/pr-code-review-config.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/security-review.md}} - GH_AW_PROMPT_b8421cf2a0582e37_EOF + GH_AW_PROMPT_5307939dd77dc999_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -580,9 +577,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_87dac5dbc469a09e_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a74df72d8c31161d_EOF' {"create_pull_request_review_comment":{"max":10,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_87dac5dbc469a09e_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_a74df72d8c31161d_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -811,7 +808,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_091397d75440f84c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_60c8d02b44c7c117_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "agenticworkflows": { @@ -880,7 +877,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_091397d75440f84c_EOF + GH_AW_MCP_CONFIG_60c8d02b44c7c117_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1499,7 +1496,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/security-review ') || startsWith(github.event.comment.body, '/security-review\n') || github.event.comment.body == '/security-review') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/security-review ') || startsWith(github.event.comment.body, '/security-review\n') || github.event.comment.body == '/security-review'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/security-review.md b/.github/workflows/security-review.md index c44a326b5c7..dc2ea8f9dff 100644 --- a/.github/workflows/security-review.md +++ b/.github/workflows/security-review.md @@ -2,6 +2,7 @@ description: Security-focused AI agent that reviews pull requests to identify changes that could weaken security posture or extend AWF boundaries on: slash_command: + strategy: centralized name: security-review events: [pull_request_comment, pull_request_review_comment] permissions: diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 0154ee1f4f3..0d14a6d1298 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"4c054ba08af1ba87d0693ce56b1ad4142894dc7be2e7e05c6c5b766fe3a7010b","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"d4bde4bf73cadef7d80425b70c0159ea0a4fdfa9571bcfc7d58aed027d25fbed","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -57,10 +57,6 @@ name: "Tidy" "on": - issue_comment: - types: - - created - - edited push: branches: - main @@ -97,13 +93,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/tidy ') || startsWith(github.event.comment.body, '/tidy\n') || github.event.comment.body == '/tidy') && github.event.issue.pull_request != null || !(github.event_name == 'issue_comment'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -259,24 +253,24 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_5d1b810a27c4eece_EOF' + cat << 'GH_AW_PROMPT_cb6a551224b6ba18_EOF' - GH_AW_PROMPT_5d1b810a27c4eece_EOF + GH_AW_PROMPT_cb6a551224b6ba18_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_5d1b810a27c4eece_EOF' + cat << 'GH_AW_PROMPT_cb6a551224b6ba18_EOF' Tools: create_pull_request, push_to_pull_request_branch, missing_tool, missing_data, noop - GH_AW_PROMPT_5d1b810a27c4eece_EOF + GH_AW_PROMPT_cb6a551224b6ba18_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_5d1b810a27c4eece_EOF' + cat << 'GH_AW_PROMPT_cb6a551224b6ba18_EOF' - GH_AW_PROMPT_5d1b810a27c4eece_EOF + GH_AW_PROMPT_cb6a551224b6ba18_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_5d1b810a27c4eece_EOF' + cat << 'GH_AW_PROMPT_cb6a551224b6ba18_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -305,7 +299,7 @@ jobs: {{/if}} - GH_AW_PROMPT_5d1b810a27c4eece_EOF + GH_AW_PROMPT_cb6a551224b6ba18_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md" @@ -313,12 +307,12 @@ jobs: if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_push_to_pr_branch_guidance.md" fi - cat << 'GH_AW_PROMPT_5d1b810a27c4eece_EOF' + cat << 'GH_AW_PROMPT_cb6a551224b6ba18_EOF' {{#runtime-import .github/workflows/shared/observability-otlp.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/tidy.md}} - GH_AW_PROMPT_5d1b810a27c4eece_EOF + GH_AW_PROMPT_cb6a551224b6ba18_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -548,9 +542,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_0738cd5e16c6f3be_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_7b07b85aad1ec349_EOF' {"create_pull_request":{"draft":false,"expires":48,"labels":["automation","maintenance"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"reviewers":["copilot"],"title_prefix":"[tidy] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"push_to_pull_request_branch":{"if_no_changes":"warn","max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"]},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_0738cd5e16c6f3be_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_7b07b85aad1ec349_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -782,7 +776,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_2878cc75d1ddd82c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_1c7b45374fab1d91_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -813,7 +807,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_2878cc75d1ddd82c_EOF + GH_AW_MCP_CONFIG_1c7b45374fab1d91_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1472,7 +1466,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/tidy ') || startsWith(github.event.comment.body, '/tidy\n') || github.event.comment.body == '/tidy') && github.event.issue.pull_request != null || !(github.event_name == 'issue_comment'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/tidy.md b/.github/workflows/tidy.md index 706c11a567a..15e6ea3ee89 100644 --- a/.github/workflows/tidy.md +++ b/.github/workflows/tidy.md @@ -6,6 +6,7 @@ on: - cron: 'daily around 7:00' # ~7 AM UTC workflow_dispatch: slash_command: + strategy: centralized events: [pull_request_comment] reaction: "eyes" push: diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index f8f49333e9c..f26dff861a3 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"8f769714cba8c96ed4cbfdcabe455ae4f0d00cf691d101bee1e96d0d1773fe13","strict":true,"agent_id":"claude"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"d172fd355f4dc8102cd3720b53f949d95b0f8c13fa915f797e913a99d6f229b5","strict":true,"agent_id":"claude"} # gh-aw-manifest: {"version":1,"secrets":["ANTHROPIC_API_KEY","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_ENDPOINT","GH_AW_OTEL_HEADERS","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/cli-proxy:0.25.43"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.43"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -62,10 +62,6 @@ name: "Documentation Unbloat" "on": - issue_comment: - types: - - created - - edited schedule: - cron: "24 16 * * *" # skip-if-match: is:pr is:open is:draft label:doc-unbloat # Skip-if-match processed as search check in pre-activation job @@ -94,13 +90,11 @@ env: jobs: activation: needs: pre_activation - if: "needs.pre_activation.outputs.activated == 'true' && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/unbloat ') || startsWith(github.event.comment.body, '/unbloat\n') || github.event.comment.body == '/unbloat') && github.event.issue.pull_request != null || !(github.event_name == 'issue_comment'))" + if: needs.pre_activation.outputs.activated == 'true' runs-on: ubuntu-slim permissions: actions: read contents: read - issues: write - pull-requests: write outputs: body: ${{ steps.sanitized.outputs.body }} comment_id: ${{ steps.add-comment.outputs.comment-id }} @@ -256,27 +250,27 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_38b6a43feeb686cf_EOF' + cat << 'GH_AW_PROMPT_bae37a7354e1d9f4_EOF' - GH_AW_PROMPT_38b6a43feeb686cf_EOF + GH_AW_PROMPT_bae37a7354e1d9f4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/playwright_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/cache_memory_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_38b6a43feeb686cf_EOF' + cat << 'GH_AW_PROMPT_bae37a7354e1d9f4_EOF' Tools: add_comment, create_pull_request, upload_asset(max:10), missing_tool, missing_data, noop - GH_AW_PROMPT_38b6a43feeb686cf_EOF + GH_AW_PROMPT_bae37a7354e1d9f4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" - cat << 'GH_AW_PROMPT_38b6a43feeb686cf_EOF' + cat << 'GH_AW_PROMPT_bae37a7354e1d9f4_EOF' upload_asset: provide a file path; returns a URL; assets are published after the workflow completes (safeoutputs). - GH_AW_PROMPT_38b6a43feeb686cf_EOF + GH_AW_PROMPT_bae37a7354e1d9f4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_38b6a43feeb686cf_EOF' + cat << 'GH_AW_PROMPT_bae37a7354e1d9f4_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -305,9 +299,9 @@ jobs: {{/if}} - GH_AW_PROMPT_38b6a43feeb686cf_EOF + GH_AW_PROMPT_bae37a7354e1d9f4_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/cli_proxy_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_38b6a43feeb686cf_EOF' + cat << 'GH_AW_PROMPT_bae37a7354e1d9f4_EOF' {{#runtime-import .github/workflows/shared/docs-server-lifecycle.md}} {{#runtime-import .github/workflows/shared/observability-otlp.md}} @@ -315,7 +309,7 @@ jobs: {{#runtime-import .github/workflows/shared/reporting.md}} {{#runtime-import .github/workflows/shared/noop-reminder.md}} {{#runtime-import .github/workflows/unbloat-docs.md}} - GH_AW_PROMPT_38b6a43feeb686cf_EOF + GH_AW_PROMPT_bae37a7354e1d9f4_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -410,6 +404,8 @@ jobs: contents: read issues: read pull-requests: read + concurrency: + group: "gh-aw-claude-${{ github.workflow }}" env: DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} GH_AW_ASSETS_ALLOWED_EXTS: ".png,.jpg,.jpeg,.svg" @@ -582,9 +578,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << GH_AW_SAFE_OUTPUTS_CONFIG_7b2970f6409813b6_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << GH_AW_SAFE_OUTPUTS_CONFIG_bc445d938315710e_EOF {"add_comment":{"max":1},"create_pull_request":{"auto_merge":true,"draft":true,"expires":48,"fallback_as_issue":false,"labels":["documentation","automation","doc-unbloat"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","CLAUDE.md","AGENTS.md"],"reviewers":["copilot"],"title_prefix":"[docs] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"upload_asset":{"allowed-exts":[".png",".jpg",".jpeg",".svg"],"branch":"assets/${GITHUB_WORKFLOW}","max":10,"max-size":10240}} - GH_AW_SAFE_OUTPUTS_CONFIG_7b2970f6409813b6_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_bc445d938315710e_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -831,7 +827,7 @@ jobs: export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/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 DOCKER_HOST=unix:///var/run/docker.sock -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_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -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 -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -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.3.6' GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_9c701f5cb989dfdf_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_a996d062ac58fa2a_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "safeoutputs": { @@ -862,7 +858,7 @@ jobs: } } } - GH_AW_MCP_CONFIG_9c701f5cb989dfdf_EOF + GH_AW_MCP_CONFIG_a996d062ac58fa2a_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1655,7 +1651,6 @@ jobs: } pre_activation: - if: "(github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment' || contains(fromJSON('[\"OWNER\",\"MEMBER\",\"COLLABORATOR\"]'), github.event.comment.author_association)) && (github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/unbloat ') || startsWith(github.event.comment.body, '/unbloat\n') || github.event.comment.body == '/unbloat') && github.event.issue.pull_request != null || !(github.event_name == 'issue_comment'))" runs-on: ubuntu-slim permissions: contents: read diff --git a/.github/workflows/unbloat-docs.md b/.github/workflows/unbloat-docs.md index 34d32063986..989bd02f7bc 100644 --- a/.github/workflows/unbloat-docs.md +++ b/.github/workflows/unbloat-docs.md @@ -7,6 +7,7 @@ on: # Command trigger for /unbloat in PR comments slash_command: + strategy: centralized name: unbloat events: [pull_request_comment] diff --git a/actions/setup/js/check_command_position.cjs b/actions/setup/js/check_command_position.cjs index 349ccf1f114..e6f9402e392 100644 --- a/actions/setup/js/check_command_position.cjs +++ b/actions/setup/js/check_command_position.cjs @@ -66,6 +66,42 @@ async function main() { text = context.payload.discussion?.body || ""; } else if (eventName === "discussion_comment") { text = context.payload.comment?.body || ""; + } else if (eventName === "workflow_dispatch") { + const rawAwContext = context.payload?.inputs?.aw_context ?? ""; + let inboundCommandName = ""; + if (typeof rawAwContext === "string" && rawAwContext.trim() !== "") { + try { + const parsed = JSON.parse(rawAwContext); + if (parsed && typeof parsed === "object" && typeof parsed.command_name === "string") { + inboundCommandName = parsed.command_name.trim(); + } + } catch { + // ignore malformed aw_context and fall back to manual workflow_dispatch behavior + } + } + + if (inboundCommandName) { + if (commands.includes(inboundCommandName)) { + core.info(`✓ command_name '${inboundCommandName}' resolved from workflow_dispatch aw_context`); + core.setOutput("command_position_ok", "true"); + core.setOutput("matched_command", inboundCommandName); + } else { + core.warning(`⚠️ command_name '${inboundCommandName}' from aw_context is not in allowed commands list.`); + core.setOutput("command_position_ok", "false"); + core.setOutput("matched_command", ""); + await writeDenialSummary( + `Workflow dispatch aw_context.command_name '${inboundCommandName}' is not one of the configured commands.`, + "Ensure the centralized slash-command trigger dispatches only configured commands." + ); + } + return; + } + + // Manual workflow_dispatch without aw_context.command_name is still allowed. + core.info("workflow_dispatch without aw_context.command_name; skipping command position check"); + core.setOutput("command_position_ok", "true"); + core.setOutput("matched_command", ""); + return; } else { // For non-comment events, pass the check core.info(`Event ${eventName} does not require command position check`); diff --git a/actions/setup/js/check_command_position.test.cjs b/actions/setup/js/check_command_position.test.cjs index e71b2a5e16e..3c6f02d1638 100644 --- a/actions/setup/js/check_command_position.test.cjs +++ b/actions/setup/js/check_command_position.test.cjs @@ -76,7 +76,19 @@ const mockCore = { (mockContext.payload = {}), await eval(`(async () => { ${checkCommandPositionScript}; await main(); })()`), expect(mockCore.setOutput).toHaveBeenCalledWith("command_position_ok", "true"), - expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("does not require command position check"))); + expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("without aw_context.command_name"))); + }), + it("should resolve command from workflow_dispatch aw_context", async () => { + process.env.GH_AW_COMMANDS = JSON.stringify(["test-bot"]); + mockContext.eventName = "workflow_dispatch"; + mockContext.payload = { + inputs: { + aw_context: JSON.stringify({ command_name: "test-bot" }), + }, + }; + await eval(`(async () => { ${checkCommandPositionScript}; await main(); })()`); + expect(mockCore.setOutput).toHaveBeenCalledWith("command_position_ok", "true"); + expect(mockCore.setOutput).toHaveBeenCalledWith("matched_command", "test-bot"); }), it("should handle pull_request event with command at start", async () => { ((process.env.GH_AW_COMMANDS = JSON.stringify(["review-bot"])), diff --git a/actions/setup/js/check_membership.cjs b/actions/setup/js/check_membership.cjs index 64b8a31030d..1e4ea3bcaa3 100644 --- a/actions/setup/js/check_membership.cjs +++ b/actions/setup/js/check_membership.cjs @@ -4,25 +4,113 @@ const { parseRequiredPermissions, parseAllowedBots, checkRepositoryPermission, checkBotStatus, isAllowedBot, isConfusedDeputyAttack } = require("./check_permissions_utils.cjs"); const { writeDenialSummary } = require("./pre_activation_summary.cjs"); +function readWorkflowDispatchAwContext(payload) { + try { + const rawAwContext = payload?.inputs?.aw_context; + if (typeof rawAwContext !== "string" || rawAwContext.trim() === "") { + return null; + } + const parsed = JSON.parse(rawAwContext); + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { + return null; + } + return parsed; + } catch { + return null; + } +} + async function main() { const { eventName } = context; const actor = context.actor; const { owner, repo } = context.repo; const requiredPermissions = parseRequiredPermissions(); const allowedBots = parseAllowedBots(); + let actorToValidate = actor; - // For workflow_dispatch, only skip check if "write" is in the allowed roles - // since workflow_dispatch can be triggered by users with write access + // workflow_dispatch is never treated as a trusted event. + // For centralized slash-command dispatches, validate the original triggering actor. if (eventName === "workflow_dispatch") { - const hasWriteRole = requiredPermissions.includes("write"); - if (hasWriteRole) { - core.info(`✅ Event ${eventName} does not require validation (write role allowed)`); - core.setOutput("is_team_member", "true"); - core.setOutput("result", "safe_event"); - return; + const awContext = readWorkflowDispatchAwContext(context.payload); + const commandName = typeof awContext?.command_name === "string" ? awContext.command_name.trim() : ""; + const propagatedActor = typeof awContext?.actor === "string" ? awContext.actor.trim() : ""; + + if (commandName && actor === "github-actions[bot]") { + if (!propagatedActor) { + const errorMessage = "Access denied: workflow_dispatch aw_context.actor is required for centralized slash-command dispatches."; + core.warning(errorMessage); + core.setOutput("is_team_member", "false"); + core.setOutput("result", "config_error"); + core.setOutput("error_message", errorMessage); + await writeDenialSummary(errorMessage, "Ensure centralized slash-command dispatches include aw_context.actor."); + return; + } + + actorToValidate = propagatedActor; + core.info(`Validating centralized workflow_dispatch against originating actor '${actorToValidate}'`); + + const itemType = typeof awContext?.item_type === "string" ? awContext.item_type.trim() : ""; + const rawItemNumber = typeof awContext?.item_number === "string" ? awContext.item_number.trim() : ""; + if (itemType === "pull_request") { + if (!/^\d+$/.test(rawItemNumber)) { + const errorMessage = "Access denied: centralized slash-command dispatch is missing a valid pull request number."; + core.warning(errorMessage); + core.setOutput("is_team_member", "false"); + core.setOutput("result", "fork_pull_request"); + core.setOutput("error_message", errorMessage); + await writeDenialSummary(errorMessage, "Dispatch metadata is incomplete. Re-run from the original PR event."); + return; + } + const pullNumber = Number.parseInt(rawItemNumber, 10); + if (!Number.isInteger(pullNumber) || pullNumber <= 0) { + const errorMessage = "Access denied: centralized slash-command dispatch is missing a valid pull request number."; + core.warning(errorMessage); + core.setOutput("is_team_member", "false"); + core.setOutput("result", "fork_pull_request"); + core.setOutput("error_message", errorMessage); + await writeDenialSummary(errorMessage, "Dispatch metadata is incomplete. Re-run from the original PR event."); + return; + } + + try { + const response = await github.rest.pulls.get({ + owner, + repo, + pull_number: pullNumber, + }); + const pullRequest = response?.data; + const headRepo = pullRequest?.head?.repo?.full_name; + const baseRepo = pullRequest?.base?.repo?.full_name; + if (!headRepo || !baseRepo) { + const errorMessage = "Access denied: centralized slash-command dispatch pull request repository metadata is unavailable."; + core.warning(errorMessage); + core.setOutput("is_team_member", "false"); + core.setOutput("result", "fork_pull_request"); + core.setOutput("error_message", errorMessage); + await writeDenialSummary(errorMessage, "Check the pre_activation log and ensure pull request repository metadata is present."); + return; + } + if (headRepo !== baseRepo) { + const errorMessage = "Access denied: centralized slash-command dispatch from fork-based pull requests is not allowed."; + core.warning(errorMessage); + core.setOutput("is_team_member", "false"); + core.setOutput("result", "fork_pull_request"); + core.setOutput("error_message", errorMessage); + await writeDenialSummary(errorMessage, "Run slash-command workflows from branches in the base repository."); + return; + } + } catch (error) { + const errorMessage = `Repository permission check failed: Unable to verify pull request provenance (${error?.message ?? String(error)}).`; + core.warning(errorMessage); + core.setOutput("is_team_member", "false"); + core.setOutput("result", "api_error"); + core.setOutput("error_message", errorMessage); + await writeDenialSummary(errorMessage, "Check the pre_activation log and ensure the workflow token can read pull request metadata."); + return; + } + } } - // If write is not allowed, continue with permission check - core.info(`Event ${eventName} requires validation (write role not allowed)`); + core.info(`Event ${eventName} requires validation`); } // skip check for other safe events @@ -56,8 +144,8 @@ async function main() { // @dependabot show (for issue_comment events) to make dependabot appear as the // actor, bypassing permission checks that rely solely on github.actor. // Reference: https://labs.boostsecurity.io/articles/weaponizing-dependabot-pwn-request-at-its-finest/ - if (isConfusedDeputyAttack(actor, eventName, context.payload)) { - const errorMessage = `Access denied: Potential confused deputy attack detected. Actor '${actor}' does not match the event author. The workflow may have been triggered indirectly via a bot command.`; + if (isConfusedDeputyAttack(actorToValidate, eventName, context.payload)) { + const errorMessage = `Access denied: Potential confused deputy attack detected. Actor '${actorToValidate}' does not match the event author. The workflow may have been triggered indirectly via a bot command.`; core.warning(errorMessage); core.setOutput("is_team_member", "false"); core.setOutput("result", "confused_deputy"); @@ -67,7 +155,7 @@ async function main() { } // Check if the actor has the required repository permissions - const result = await checkRepositoryPermission(actor, owner, repo, requiredPermissions); + const result = await checkRepositoryPermission(actorToValidate, owner, repo, requiredPermissions); if (result.authorized) { core.setOutput("is_team_member", "true"); @@ -78,23 +166,23 @@ async function main() { // Always attempt the bot allowlist fallback before giving up, so that GitHub Apps whose // actor is not a recognized GitHub user (e.g. "Copilot") are not silently denied. if (allowedBots.length > 0) { - core.info(`Checking if actor '${actor}' is in allowed bots list: ${allowedBots.join(", ")}`); + core.info(`Checking if actor '${actorToValidate}' is in allowed bots list: ${allowedBots.join(", ")}`); - if (isAllowedBot(actor, allowedBots)) { - core.info(`Actor '${actor}' is in the allowed bots list`); + if (isAllowedBot(actorToValidate, allowedBots)) { + core.info(`Actor '${actorToValidate}' is in the allowed bots list`); // Verify the bot is active/installed on the repository - const botStatus = await checkBotStatus(actor, owner, repo); + const botStatus = await checkBotStatus(actorToValidate, owner, repo); if (botStatus.isBot && botStatus.isActive) { - core.info(`✅ Bot '${actor}' is active on the repository and authorized`); + core.info(`✅ Bot '${actorToValidate}' is active on the repository and authorized`); core.setOutput("is_team_member", "true"); core.setOutput("result", "authorized_bot"); core.setOutput("user_permission", "bot"); return; } else if (botStatus.isBot && !botStatus.isActive) { - const errorMessage = `Access denied: Bot '${actor}' is not active/installed on this repository`; - core.warning(`Bot '${actor}' is in the allowed list but not active/installed on ${owner}/${repo}`); + const errorMessage = `Access denied: Bot '${actorToValidate}' is not active/installed on this repository`; + core.warning(`Bot '${actorToValidate}' is in the allowed list but not active/installed on ${owner}/${repo}`); core.setOutput("is_team_member", "false"); core.setOutput("result", "bot_not_active"); core.setOutput("user_permission", result.permission ?? "bot"); @@ -102,7 +190,7 @@ async function main() { await writeDenialSummary(errorMessage, "The bot is in the allowed list but is not installed or active on this repository. Install the GitHub App and try again."); return; } else { - core.info(`Actor '${actor}' is in allowed bots list but bot status check failed`); + core.info(`Actor '${actorToValidate}' is in allowed bots list but bot status check failed`); } } } @@ -116,7 +204,7 @@ async function main() { await writeDenialSummary(errorMessage, "The permission check failed with a GitHub API error. Check the `pre_activation` job log for details."); } else { const errorMessage = - `Access denied: User '${actor}' is not authorized. Required permissions: ${requiredPermissions.join(", ")}. ` + + `Access denied: User '${actorToValidate}' is not authorized. Required permissions: ${requiredPermissions.join(", ")}. ` + `To allow this user to run the workflow, add their role to the frontmatter. Example: roles: [${requiredPermissions.join(", ")}, ${result.permission}]`; core.setOutput("is_team_member", "false"); core.setOutput("result", "insufficient_permissions"); diff --git a/actions/setup/js/check_membership.test.cjs b/actions/setup/js/check_membership.test.cjs index 60429abea97..849b2348a69 100644 --- a/actions/setup/js/check_membership.test.cjs +++ b/actions/setup/js/check_membership.test.cjs @@ -27,6 +27,9 @@ describe("check_membership.cjs", () => { repos: { getCollaboratorPermissionLevel: vi.fn(), }, + pulls: { + get: vi.fn(), + }, }, }; @@ -126,15 +129,17 @@ describe("check_membership.cjs", () => { expect(mockCore.setOutput).toHaveBeenCalledWith("result", "safe_event"); }); - it("should skip check for workflow_dispatch when write role is allowed", async () => { + it("should validate workflow_dispatch when write role is allowed", async () => { mockContext.eventName = "workflow_dispatch"; process.env.GH_AW_REQUIRED_ROLES = "write,read"; + mockGithub.rest.repos.getCollaboratorPermissionLevel.mockResolvedValue({ + data: { permission: "write" }, + }); await runScript(); - expect(mockCore.info).toHaveBeenCalledWith("✅ Event workflow_dispatch does not require validation (write role allowed)"); - expect(mockCore.setOutput).toHaveBeenCalledWith("is_team_member", "true"); - expect(mockCore.setOutput).toHaveBeenCalledWith("result", "safe_event"); + expect(mockCore.info).toHaveBeenCalledWith("Event workflow_dispatch requires validation"); + expect(mockGithub.rest.repos.getCollaboratorPermissionLevel).toHaveBeenCalled(); }); it("should validate workflow_dispatch when write role is not allowed", async () => { @@ -147,9 +152,68 @@ describe("check_membership.cjs", () => { await runScript(); - expect(mockCore.info).toHaveBeenCalledWith("Event workflow_dispatch requires validation (write role not allowed)"); + expect(mockCore.info).toHaveBeenCalledWith("Event workflow_dispatch requires validation"); expect(mockGithub.rest.repos.getCollaboratorPermissionLevel).toHaveBeenCalled(); }); + + it("should validate centralized workflow_dispatch using aw_context actor", async () => { + mockContext.eventName = "workflow_dispatch"; + mockContext.actor = "github-actions[bot]"; + mockContext.payload = { + inputs: { + aw_context: JSON.stringify({ + command_name: "triage", + actor: "octocat", + }), + }, + }; + process.env.GH_AW_REQUIRED_ROLES = "write"; + mockGithub.rest.repos.getCollaboratorPermissionLevel.mockResolvedValue({ + data: { permission: "write" }, + }); + + await runScript(); + + expect(mockCore.info).toHaveBeenCalledWith("Validating centralized workflow_dispatch against originating actor 'octocat'"); + expect(mockGithub.rest.repos.getCollaboratorPermissionLevel).toHaveBeenCalledWith({ + owner: "testorg", + repo: "testrepo", + username: "octocat", + }); + expect(mockCore.setOutput).toHaveBeenCalledWith("result", "authorized"); + }); + + it("should deny centralized workflow_dispatch from fork-based pull requests", async () => { + mockContext.eventName = "workflow_dispatch"; + mockContext.actor = "github-actions[bot]"; + mockContext.payload = { + inputs: { + aw_context: JSON.stringify({ + command_name: "triage", + actor: "octocat", + item_type: "pull_request", + item_number: "42", + }), + }, + }; + process.env.GH_AW_REQUIRED_ROLES = "write"; + mockGithub.rest.pulls.get.mockResolvedValue({ + data: { + head: { repo: { full_name: "someone/fork" } }, + base: { repo: { full_name: "testorg/testrepo" } }, + }, + }); + + await runScript(); + + expect(mockGithub.rest.pulls.get).toHaveBeenCalledWith({ + owner: "testorg", + repo: "testrepo", + pull_number: 42, + }); + expect(mockGithub.rest.repos.getCollaboratorPermissionLevel).not.toHaveBeenCalled(); + expect(mockCore.setOutput).toHaveBeenCalledWith("result", "fork_pull_request"); + }); }); describe("configuration validation", () => { diff --git a/actions/setup/js/route_slash_command.cjs b/actions/setup/js/route_slash_command.cjs new file mode 100644 index 00000000000..de077f2ac57 --- /dev/null +++ b/actions/setup/js/route_slash_command.cjs @@ -0,0 +1,87 @@ +// @ts-check +/// + +function eventIdentifier() { + if (context.eventName !== "issue_comment") { + return context.eventName; + } + return context.payload?.issue?.pull_request ? "pull_request_comment" : "issue_comment"; +} + +function resolveBodyText() { + const bodyByEvent = { + issues: context.payload?.issue?.body ?? "", + pull_request: context.payload?.pull_request?.body ?? "", + issue_comment: context.payload?.comment?.body ?? "", + pull_request_review_comment: context.payload?.comment?.body ?? "", + discussion: context.payload?.discussion?.body ?? "", + discussion_comment: context.payload?.comment?.body ?? "", + }; + return bodyByEvent[context.eventName] ?? ""; +} + +function resolveDispatchRef() { + if (process.env.GITHUB_HEAD_REF) { + return `refs/heads/${process.env.GITHUB_HEAD_REF}`; + } + + const fallbackRef = process.env.GITHUB_REF || context.ref; + if (fallbackRef) { + return fallbackRef; + } + + const defaultBranch = context.payload?.repository?.default_branch || "main"; + return `refs/heads/${defaultBranch}`; +} + +async function main() { + core.info("Starting centralized slash command routing."); + core.info(`Incoming event name: '${context.eventName}'.`); + + const routeMap = JSON.parse(process.env.GH_AW_SLASH_ROUTING || "{}"); + core.info(`Configured centralized commands: ${Object.keys(routeMap).length}.`); + + const text = resolveBodyText(); + core.info(`Resolved payload text length: ${String(text).length}.`); + const firstWord = String(text).trim().split(/\s+/)[0] ?? ""; + core.info(`First token in payload: '${firstWord || ""}'.`); + if (!firstWord.startsWith("/")) { + core.info("No slash command found at start of payload text; skipping dispatch."); + return; + } + + const commandName = firstWord.slice(1); + const identifier = eventIdentifier(); + core.info(`Resolved command '/${commandName}' for event identifier '${identifier}'.`); + const configuredRoutes = routeMap[commandName] ?? []; + core.info(`Configured routes for '/${commandName}': ${configuredRoutes.length}.`); + const routes = configuredRoutes.filter(route => Array.isArray(route.events) && route.events.includes(identifier)); + if (routes.length === 0) { + core.info(`No centralized routes matched command '/${commandName}' for event '${identifier}'.`); + return; + } + core.info(`Matched routes for '/${commandName}' on '${identifier}': ${routes.map(route => route.workflow).join(", ")}.`); + + const { buildAwContext } = require("./aw_context.cjs"); + + const ref = resolveDispatchRef(); + core.info(`Dispatch ref resolved to '${ref}'.`); + for (const route of routes) { + const awContext = buildAwContext(); + awContext.command_name = commandName; + core.info(`Dispatching workflow '${route.workflow}.lock.yml' for '/${commandName}'.`); + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: `${route.workflow}.lock.yml`, + ref, + inputs: { + aw_context: JSON.stringify(awContext), + }, + }); + core.info(`Dispatched '${route.workflow}' for '/${commandName}'`); + } + core.info(`Completed centralized routing for '/${commandName}'.`); +} + +module.exports = { main, eventIdentifier, resolveBodyText, resolveDispatchRef }; diff --git a/actions/setup/js/route_slash_command.test.cjs b/actions/setup/js/route_slash_command.test.cjs new file mode 100644 index 00000000000..98f5a0c2581 --- /dev/null +++ b/actions/setup/js/route_slash_command.test.cjs @@ -0,0 +1,84 @@ +// @ts-check +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; + +const globals = /** @type {any} */ global; +const { main } = require("./route_slash_command.cjs"); + +describe("route_slash_command", () => { + /** @type {{ core: any, github: any, context: any, exec: any, io: any, getOctokit: any }} */ + let savedGlobals; + /** @type {any[]} */ + let dispatchCalls; + + beforeEach(() => { + savedGlobals = { + core: globals.core, + github: globals.github, + context: globals.context, + exec: globals.exec, + io: globals.io, + getOctokit: globals.getOctokit, + }; + dispatchCalls = []; + globals.core = { + info: vi.fn(), + }; + globals.github = { + rest: { + actions: { + createWorkflowDispatch: vi.fn(async params => { + dispatchCalls.push(params); + }), + }, + }, + }; + globals.context = { + eventName: "issue_comment", + ref: "refs/heads/main", + repo: { owner: "github", repo: "gh-aw" }, + payload: { issue: {}, comment: {} }, + }; + globals.exec = {}; + globals.io = {}; + globals.getOctokit = vi.fn(); + process.env.GH_AW_SLASH_ROUTING = JSON.stringify({ + archie: [{ workflow: "archie", events: ["issue_comment", "pull_request_comment"] }], + }); + process.env.GITHUB_WORKSPACE = `${process.cwd()}`; + }); + + afterEach(() => { + globals.core = savedGlobals.core; + globals.github = savedGlobals.github; + globals.context = savedGlobals.context; + globals.exec = savedGlobals.exec; + globals.io = savedGlobals.io; + globals.getOctokit = savedGlobals.getOctokit; + delete process.env.GH_AW_SLASH_ROUTING; + delete process.env.GITHUB_WORKSPACE; + delete process.env.GITHUB_REF; + delete process.env.GITHUB_HEAD_REF; + vi.restoreAllMocks(); + }); + + it("skips dispatch when text does not start with slash command", async () => { + globals.context.payload.comment.body = "hello /archie"; + await main(); + expect(dispatchCalls).toHaveLength(0); + expect(globals.core.info).toHaveBeenCalledWith(expect.stringContaining("No slash command found")); + }); + + it("dispatches only matching command and event routes", async () => { + globals.context.payload.comment.body = "/archie please"; + await main(); + expect(dispatchCalls).toHaveLength(1); + expect(dispatchCalls[0].workflow_id).toBe("archie.lock.yml"); + }); + + it("treats issue_comment on pull requests as pull_request_comment", async () => { + globals.context.payload.issue.pull_request = { url: "https://example.test/pr/1" }; + globals.context.payload.comment.body = "/archie please"; + await main(); + expect(dispatchCalls).toHaveLength(1); + }); +}); diff --git a/docs/adr/31605-centralized-slash-command-routing.md b/docs/adr/31605-centralized-slash-command-routing.md new file mode 100644 index 00000000000..b19259435a6 --- /dev/null +++ b/docs/adr/31605-centralized-slash-command-routing.md @@ -0,0 +1,97 @@ +# ADR-31605: Centralized Slash-Command Routing via Generated Agentic Router Workflow + +**Date**: 2026-05-12 +**Status**: Draft +**Deciders**: Unknown — *[TODO: PR author to confirm]* + +--- + +## Part 1 — Narrative (Human-Friendly) + +### Context + +Each slash-command workflow in this repository previously registered its own listeners for `issues`, `issue_comment`, `pull_request`, `pull_request_review_comment`, `discussion`, and `discussion_comment`. With many such workflows (e.g. `/archie`, `/cloclo`, and a growing fleet), this caused every comment, issue, or PR event to wake up many lock-files, each evaluating long `if:` expressions to decide whether its slash command matched. The duplication inflated GitHub Actions usage, made permissions sprawl across workflows, and produced large compiled `if:` predicates that were hard to read and maintain. The compiler also lacked guidance to nudge authors toward a shared router as the slash-command fleet grew. + +### Decision + +We will introduce a `centralized` strategy for `on.slash_command` and have the compiler generate a single shared router workflow at `.github/workflows/agentic_commands.yml` that owns the merged set of slash-command events and dispatches matching target workflows via `workflow_dispatch` with an `aw_context` input. Participating workflows (those declaring `strategy: centralized`) compile to `workflow_dispatch`-only triggers, retaining their non-slash events (e.g. label-only triggers) but delegating slash detection to the central router. The compiler additionally emits a warning recommending `strategy: centralized` once three or more slash commands are detected and some remain non-centralized, so the convention scales as the fleet grows. + +### Alternatives Considered + +#### Alternative 1: Keep per-workflow inline listeners (status quo) + +Each slash-command workflow continues to declare its own event listeners and inline `if:` predicate. This is simple and decentralized — each workflow is self-contained — but it scales poorly: every comment/issue/PR event fans out to N workflows, each compiling N progressively longer `if:` expressions. It was rejected because the cost is visible today (duplicated runs, large generated predicates) and grows linearly with the slash-command fleet. + +#### Alternative 2: One static, hand-maintained router workflow + +Maintain `agentic_commands.yml` by hand and require workflow authors to register their command in it manually. This avoids compiler complexity but reintroduces a long-running coordination problem: every new slash command requires editing a shared file, and the registry can drift from the per-workflow frontmatter. Rejected because compiler-generation of the router from frontmatter (`strategy: centralized`) preserves a single source of truth and avoids merge conflicts on the shared router. + +#### Alternative 3: Use a `repository_dispatch` or external broker + +Forward slash events to an external service (or `repository_dispatch`) that then triggers the right workflow. This decouples GitHub event listeners entirely but adds an out-of-repo dependency, new auth surface, and operational risk. Rejected because `workflow_dispatch` + a generated router stays inside GitHub Actions and requires no new infrastructure. + +### Consequences + +#### Positive +- One shared workflow (`agentic_commands.yml`) handles slash-event listening for all participating commands, replacing N copies of the same listener set. +- Generated `if:` predicates on participating workflows shrink from large slash-text expressions to simple `workflow_dispatch`-gated activations, improving readability of lock files. +- Workflow-level permissions on participating lock files are reduced (e.g. dropping `issues: write` / `pull-requests: write` from activation jobs that no longer need them) because routing logic lives in the central router with its own scoped `actions: write` job permission. +- A compile-time warning nudges authors toward `strategy: centralized` once three or more slash commands exist, making the convention discoverable without a manual migration push. + +#### Negative +- Adds a new generated file (`.github/workflows/agentic_commands.yml`) whose lifecycle is owned by the compiler — contributors must understand it is regenerated and not edited by hand. +- Introduces an indirection: a slash command now arrives via `workflow_dispatch` triggered by another workflow, which complicates debugging (two runs to inspect instead of one) and shifts some auth context onto `aw_context`. +- The router holds `actions: write` permission to dispatch other workflows; a bug in routing logic could dispatch the wrong workflow, so the route map and event-matching filter must remain trustworthy. +- Legacy trigger-file handling for `agentics-slash-command-trigger.yml` was removed; any external references to that file name become stale. + +#### Neutral +- Two existing workflows (`/archie`, `/cloclo`) were migrated as part of this PR; remaining slash workflows continue to use the default inline strategy until opted in. +- The router serializes inbound command resolution via `aw_context.command_name`, requiring `check_command_position.cjs` to learn a new `workflow_dispatch` code path (added in this PR with tests). +- Documentation under `docs/src/content/docs/reference/command-triggers.md` was updated to describe both strategies side-by-side. + +--- + +## Part 2 — Normative Specification (RFC 2119) + +> The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this section are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119). + +### Centralized Strategy Selection + +1. A slash-command workflow **MAY** opt into centralized routing by setting `on.slash_command.strategy: centralized` in its frontmatter. +2. The compiler **MUST** treat `strategy: centralized` as the participation flag — a workflow without that key **MUST NOT** be wired into the central router. +3. When at least one workflow opts into `strategy: centralized`, the compiler **MUST** generate exactly one router workflow file at `.github/workflows/agentic_commands.yml`. +4. The generated router file **MUST** be regenerable from frontmatter alone and **MUST NOT** be hand-edited; contributors **SHOULD** treat it as compiler output. + +### Router Workflow Structure + +1. The generated router **MUST** declare `permissions: {}` at the top level (no workflow-wide permissions). +2. The router job named `route` **MUST** declare scoped job-level permissions of at minimum `actions: write` and `contents: read`, and **MUST NOT** declare broader permissions than required to dispatch participating workflows. +3. The router **MUST** listen on the **union** of slash-event types declared by participating workflows (e.g. `issues`, `issue_comment`, `pull_request`, `pull_request_review_comment`, `discussion`, `discussion_comment`) and **MUST NOT** listen on events for which no participating workflow has subscribed. +4. The router **MUST** dispatch a participating workflow only when both the command name (parsed from the first token of the payload body) and the inbound event identifier match an entry in the generated route map. +5. The router **MUST** pass an `aw_context` JSON input containing at least `command_name` to the dispatched workflow. + +### Participating Workflow Compilation + +1. A workflow with `strategy: centralized` **MUST** compile with `workflow_dispatch` as a trigger and **MUST** accept an `aw_context` string input. +2. A workflow with `strategy: centralized` **MUST NOT** re-declare slash-text matching on `issue_comment`, `pull_request_review_comment`, `discussion`, or `discussion_comment` in its compiled lock file; slash matching is the router's responsibility. +3. A workflow with `strategy: centralized` **MAY** retain non-slash listeners that do not collide with slash routing (for example, label-only triggers on `issues`, `pull_request`, or `discussion`). +4. The compiled activation `if:` predicate of a centralized workflow **MUST NOT** include slash-text inspection of payload bodies. + +### Inbound Command Resolution + +1. Setup logic processing `workflow_dispatch` events **MUST** read `command_name` from `aw_context.inputs.aw_context` JSON when present. +2. If `aw_context.command_name` is present, the setup logic **MUST** verify it is in the configured commands list and **MUST** fail the command-position check (emit a denial summary) when it is not. +3. Manual `workflow_dispatch` invocations without `aw_context.command_name` **SHOULD** pass the command-position check to preserve existing manual-run behavior. + +### Compiler Guidance + +1. The compiler **SHOULD** emit a warning recommending `strategy: centralized` when three or more slash commands are detected in the repository and at least one of them does not declare `strategy: centralized`. +2. The warning **MUST NOT** block compilation; it is advisory only. + +### Conformance + +An implementation is considered conformant with this ADR if it satisfies all **MUST** and **MUST NOT** requirements above. Failure to meet any **MUST** or **MUST NOT** requirement constitutes non-conformance. + +--- + +*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/25712590786) workflow. The PR author must review, complete, and finalize this document before the PR can merge.* diff --git a/docs/src/content/docs/reference/command-triggers.md b/docs/src/content/docs/reference/command-triggers.md index dc64089232e..7b03f0b579a 100644 --- a/docs/src/content/docs/reference/command-triggers.md +++ b/docs/src/content/docs/reference/command-triggers.md @@ -68,7 +68,21 @@ on: schedule: weekly on monday ``` -**Note**: You cannot combine `slash_command` with `issues`, `issue_comment`, or `pull_request` as they would conflict. +### Centralized trigger strategy + +Set `on.slash_command.strategy: centralized` to opt a workflow into centralized slash-command routing. +When enabled, the workflow compiles as `workflow_dispatch`-centric, and the compiler generates one +shared `agentic_commands.yml` workflow that listens to merged slash-command events and +dispatches matching target workflows with `aw_context`. + +```yaml wrap +on: + slash_command: + name: my-bot + strategy: centralized +``` + +**Note**: With default inline strategy, you cannot combine `slash_command` with `issues`, `issue_comment`, or `pull_request` as they would conflict. With `strategy: centralized`, non-slash events are preserved because slash matching is handled in the generated central trigger workflow. **Exception for Label-Only Events**: You CAN combine `slash_command` with `issues` or `pull_request` if those events are configured for label-only triggers (`labeled` or `unlabeled` types only). This allows workflows to respond to slash commands while also reacting to label changes. diff --git a/docs/src/content/docs/reference/frontmatter-full.md b/docs/src/content/docs/reference/frontmatter-full.md index 4be4ea8db69..1d1c7da4878 100644 --- a/docs/src/content/docs/reference/frontmatter-full.md +++ b/docs/src/content/docs/reference/frontmatter-full.md @@ -152,6 +152,14 @@ on: name: [] # Array items: Command name without leading slash + # Trigger compilation strategy for slash commands. + # - "inline" (default): compile comment/body listeners directly in this workflow + # - "centralized" (experimental): compile this workflow as + # workflow_dispatch-centric and route slash command events via the generated + # central trigger workflow. + # (optional) + strategy: "centralized" + # Events where the command should be active. Default is all comment-related events # ('*'). Use GitHub Actions event names. # (optional) diff --git a/docs/src/content/docs/reference/triggers.md b/docs/src/content/docs/reference/triggers.md index fd85b18a9b6..acfa535f3b3 100644 --- a/docs/src/content/docs/reference/triggers.md +++ b/docs/src/content/docs/reference/triggers.md @@ -395,6 +395,7 @@ on: slash_command: name: investigate events: [issues, issue_comment] # Only respond in issue contexts + # strategy: centralized # Optional: route via generated central trigger workflow ``` See [Command Triggers](/gh-aw/reference/command-triggers/) for complete documentation including event filtering, context text, reactions, and examples. diff --git a/pkg/cli/compile_pipeline.go b/pkg/cli/compile_pipeline.go index 23e5dc72a10..3089398230b 100644 --- a/pkg/cli/compile_pipeline.go +++ b/pkg/cli/compile_pipeline.go @@ -346,6 +346,9 @@ func compileAllFilesInDirectory( } } + // Emit recommendation when many slash commands are present without centralized strategy. + displayCentralizedSlashCommandRecommendation(compiler, workflowDataList, config.JSONOutput) + // Get warning count from compiler stats.Warnings = compiler.GetWarningCount() @@ -525,6 +528,11 @@ func runPostProcessingForDirectory( return err } } + if err := generateCentralSlashCommandWorkflowWrapper(workflowDataList, absWorkflowDir, config.Strict); err != nil { + if config.Strict { + return err + } + } } // Prune stale gh-aw-actions entries before saving diff --git a/pkg/cli/compile_post_processing.go b/pkg/cli/compile_post_processing.go index 4746c45fd0b..e08f6c9bb66 100644 --- a/pkg/cli/compile_post_processing.go +++ b/pkg/cli/compile_post_processing.go @@ -100,6 +100,25 @@ func generateMaintenanceWorkflowWrapper( return nil } +// generateCentralSlashCommandWorkflowWrapper generates a single centralized +// slash-command trigger workflow for all participating workflows. +func generateCentralSlashCommandWorkflowWrapper( + workflowDataList []*workflow.WorkflowData, + workflowsDir string, + strict bool, +) error { + compilePostProcessingLog.Print("Generating centralized slash-command workflow") + + if err := workflow.GenerateCentralSlashCommandWorkflow(workflowDataList, workflowsDir); err != nil { + if strict { + return fmt.Errorf("failed to generate centralized slash-command workflow: %w", err) + } + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to generate centralized slash-command workflow: %v", err))) + } + + return nil +} + // purgeOrphanedLockFiles removes orphaned .lock.yml files // These are lock files that exist but don't have a corresponding .md file func purgeOrphanedLockFiles(workflowsDir string, expectedLockFiles []string, verbose bool) error { @@ -217,6 +236,38 @@ func displaySafeUpdateWarnings(compiler *workflow.Compiler, jsonOutput bool) { } } +// displayCentralizedSlashCommandRecommendation warns when a repository has many +// slash commands still using non-centralized strategy. +func displayCentralizedSlashCommandRecommendation(compiler *workflow.Compiler, workflowDataList []*workflow.WorkflowData, jsonOutput bool) { + if jsonOutput { + return + } + + totalSlashCommands := 0 + nonCentralizedSlashCommands := 0 + for _, wd := range workflowDataList { + if wd == nil || len(wd.Command) == 0 { + continue + } + totalSlashCommands += len(wd.Command) + if !wd.CommandCentralized { + nonCentralizedSlashCommands += len(wd.Command) + } + } + + if totalSlashCommands < 3 || nonCentralizedSlashCommands == 0 { + return + } + + msg := fmt.Sprintf( + "Detected %d slash_command entries in this repository; %d are not using centralized routing. Consider setting `on.slash_command.strategy: centralized` to reduce duplicate triggers and route through `agentic_commands.yml`.", + totalSlashCommands, + nonCentralizedSlashCommands, + ) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(msg)) + compiler.IncrementWarningCount() +} + // pruneStaleActionCacheEntries removes stale gh-aw-actions entries from the // action cache whose version does not match the compiler's current version. // This prevents actions-lock.json from accumulating entries for old compiler diff --git a/pkg/cli/compile_post_processing_warning_test.go b/pkg/cli/compile_post_processing_warning_test.go new file mode 100644 index 00000000000..fa9ac5fd2ee --- /dev/null +++ b/pkg/cli/compile_post_processing_warning_test.go @@ -0,0 +1,81 @@ +//go:build !integration + +package cli + +import ( + "testing" + + "github.com/github/gh-aw/pkg/testutil" + "github.com/github/gh-aw/pkg/workflow" + "github.com/stretchr/testify/require" +) + +func TestDisplayCentralizedSlashCommandRecommendation(t *testing.T) { + tests := []struct { + name string + workflows []*workflow.WorkflowData + jsonOutput bool + expectWarning bool + expectedWarnCount int + }{ + { + name: "warns when three slash commands include non centralized workflows", + workflows: []*workflow.WorkflowData{ + {Command: []string{"a"}, CommandCentralized: false}, + {Command: []string{"b"}, CommandCentralized: false}, + {Command: []string{"c"}, CommandCentralized: true}, + }, + expectWarning: true, + expectedWarnCount: 1, + }, + { + name: "does not warn when fewer than three slash commands exist", + workflows: []*workflow.WorkflowData{ + {Command: []string{"a"}, CommandCentralized: false}, + {Command: []string{"b"}, CommandCentralized: false}, + }, + expectWarning: false, + expectedWarnCount: 0, + }, + { + name: "does not warn when all slash commands are centralized", + workflows: []*workflow.WorkflowData{ + {Command: []string{"a"}, CommandCentralized: true}, + {Command: []string{"b"}, CommandCentralized: true}, + {Command: []string{"c"}, CommandCentralized: true}, + }, + expectWarning: false, + expectedWarnCount: 0, + }, + { + name: "does not warn for json output mode", + workflows: []*workflow.WorkflowData{ + {Command: []string{"a"}, CommandCentralized: false}, + {Command: []string{"b"}, CommandCentralized: false}, + {Command: []string{"c"}, CommandCentralized: false}, + }, + jsonOutput: true, + expectWarning: false, + expectedWarnCount: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + compiler := workflow.NewCompiler() + + stderrOutput := testutil.CaptureStderr(t, func() { + displayCentralizedSlashCommandRecommendation(compiler, tt.workflows, tt.jsonOutput) + }) + + if tt.expectWarning { + require.Contains(t, stderrOutput, "Consider setting `on.slash_command.strategy: centralized`") + require.Contains(t, stderrOutput, "Detected 3 slash_command entries") + } else { + require.NotContains(t, stderrOutput, "on.slash_command.strategy: centralized") + } + + require.Equal(t, tt.expectedWarnCount, compiler.GetWarningCount()) + }) + } +} diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 3178b8d1394..f4b7daf13f6 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -459,6 +459,11 @@ "maxItems": 25 } ] + }, + "strategy": { + "type": "string", + "description": "Slash command trigger compilation strategy. 'inline' (default) compiles direct comment listeners in this workflow. 'centralized' compiles this workflow as workflow_dispatch-centric and routes slash events via the generated central trigger workflow.", + "enum": ["inline", "centralized"] } }, "additionalProperties": false @@ -9896,9 +9901,24 @@ { "properties": { "slash_command": { - "not": { - "type": "null" - } + "allOf": [ + { + "not": { + "type": "null" + } + }, + { + "not": { + "type": "object", + "properties": { + "strategy": { + "const": "centralized" + } + }, + "required": ["strategy"] + } + } + ] } }, "required": ["slash_command"] diff --git a/pkg/workflow/central_slash_command_workflow.go b/pkg/workflow/central_slash_command_workflow.go new file mode 100644 index 00000000000..1cfd4c4e0c7 --- /dev/null +++ b/pkg/workflow/central_slash_command_workflow.go @@ -0,0 +1,287 @@ +package workflow + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "slices" + "sort" + "strings" + + "github.com/github/gh-aw/pkg/constants" + "github.com/github/gh-aw/pkg/logger" +) + +var centralSlashCommandWorkflowLog = logger.New("workflow:central_slash_command_workflow") + +const ( + centralSlashCommandWorkflowFilename = "agentic_commands.yml" + legacyCentralSlashCommandWorkflowFilename = "agentic_slash_commands.yml" +) + +type slashCommandRoute struct { + Workflow string `json:"workflow"` + Events []string `json:"events"` +} + +type commandsHeaderMetadata struct { + PayloadVersion string `json:"payload_version"` + SchemaVersion string `json:"schema_version"` + Compiler string `json:"compiler_version"` + Commands []string `json:"commands"` + Workflows []string `json:"workflows"` +} + +// GenerateCentralSlashCommandWorkflow generates a single centralized slash-command trigger +// workflow for workflows that opt into on.slash_command.strategy: centralized. +// When no centralized slash-command workflows are found, any existing generated file is deleted. +func GenerateCentralSlashCommandWorkflow(workflowDataList []*WorkflowData, workflowDir string) error { + centralSlashCommandWorkflowLog.Printf("Generating centralized slash-command workflow from %d workflow(s)", len(workflowDataList)) + routesByCommand, mergedEvents := collectCentralSlashCommandRoutes(workflowDataList) + + triggerFile := filepath.Join(workflowDir, centralSlashCommandWorkflowFilename) + legacyTriggerFile := filepath.Join(workflowDir, legacyCentralSlashCommandWorkflowFilename) + if len(routesByCommand) == 0 || len(mergedEvents) == 0 { + centralSlashCommandWorkflowLog.Print("No centralized slash-command participants found") + if err := removeIfExists(triggerFile); err != nil { + return fmt.Errorf("failed to delete centralized slash-command workflow: %w", err) + } + if err := cleanupLegacyCentralSlashCommandWorkflow(legacyTriggerFile); err != nil { + return err + } + return nil + } + + actionMode := DetectActionMode(GetVersion()) + setupActionRef := ResolveSetupActionReference(actionMode, GetVersion(), "", nil) + + content, err := buildCentralSlashCommandWorkflowYAML(routesByCommand, mergedEvents, resolveCentralSlashRunsOn(workflowDataList), setupActionRef) + if err != nil { + return err + } + + if err := os.WriteFile(triggerFile, []byte(content), 0644); err != nil { + return fmt.Errorf("failed to write centralized slash-command workflow: %w", err) + } + if err := cleanupLegacyCentralSlashCommandWorkflow(legacyTriggerFile); err != nil { + return err + } + centralSlashCommandWorkflowLog.Printf("Wrote centralized slash-command workflow: %s", triggerFile) + return nil +} + +func cleanupLegacyCentralSlashCommandWorkflow(path string) error { + if err := removeIfExists(path); err != nil { + return fmt.Errorf("failed to delete legacy centralized slash-command workflow: %w", err) + } + return nil +} + +func removeIfExists(path string) error { + if _, err := os.Stat(path); err == nil { + return os.Remove(path) + } else if !os.IsNotExist(err) { + return err + } + return nil +} + +func collectCentralSlashCommandRoutes(workflowDataList []*WorkflowData) (map[string][]slashCommandRoute, map[string]map[string]bool) { + routesByCommand := make(map[string][]slashCommandRoute) + mergedEvents := make(map[string]map[string]bool) + + for _, wd := range workflowDataList { + if wd == nil || !wd.CommandCentralized || len(wd.Command) == 0 { + continue + } + + filteredEvents := FilterCommentEvents(wd.CommandEvents) + if len(filteredEvents) == 0 { + continue + } + + routeEvents := GetCommentEventNames(filteredEvents) + routeEvents = uniqueSorted(routeEvents) + if len(routeEvents) == 0 { + continue + } + + // Merge workflow-level subscriptions using YAML-ready GitHub event names. + for _, event := range MergeEventsForYAML(filteredEvents) { + if mergedEvents[event.EventName] == nil { + mergedEvents[event.EventName] = make(map[string]bool) + } + for _, t := range event.Types { + mergedEvents[event.EventName][t] = true + } + } + + for _, commandName := range wd.Command { + route := slashCommandRoute{ + Workflow: wd.WorkflowID, + Events: slices.Clone(routeEvents), + } + routesByCommand[commandName] = append(routesByCommand[commandName], route) + } + } + + // Stable ordering for deterministic output. + for commandName := range routesByCommand { + sort.Slice(routesByCommand[commandName], func(i, j int) bool { + return routesByCommand[commandName][i].Workflow < routesByCommand[commandName][j].Workflow + }) + } + + return routesByCommand, mergedEvents +} + +func buildCentralSlashCommandWorkflowYAML(routesByCommand map[string][]slashCommandRoute, mergedEvents map[string]map[string]bool, runsOn string, setupActionRef string) (string, error) { + routesJSON, err := json.Marshal(routesByCommand) + if err != nil { + return "", fmt.Errorf("failed to marshal centralized slash-command routes: %w", err) + } + + commandsMetadata, err := json.Marshal(buildCommandsHeaderMetadata(routesByCommand)) + if err != nil { + return "", fmt.Errorf("failed to marshal centralized slash-command metadata: %w", err) + } + + header := GenerateWorkflowHeader("", "gh-aw", "") + + var b strings.Builder + b.WriteString("# gh-aw-commands: ") + b.Write(commandsMetadata) + b.WriteString("\n") + b.WriteString(header) + b.WriteString(`name: "Agentic Commands" + +on: +`) + writeCentralSlashEventsYAML(&b, mergedEvents) + b.WriteString(` +permissions: {} + +jobs: + route: + runs-on: ` + runsOn + ` + permissions: + actions: write + contents: read + steps: + - name: Checkout repository + uses: ` + getActionPin("actions/checkout") + ` + + - name: Setup Scripts + uses: ` + setupActionRef + ` + with: + destination: ` + SetupActionDestination + ` + + - name: Route slash command + uses: ` + getActionPin("actions/github-script") + ` + env: + GH_AW_SLASH_ROUTING: '` + escapeSingleQuotedYAMLString(string(routesJSON)) + `' + with: + script: | + const { setupGlobals } = require('` + SetupActionDestination + `/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('` + SetupActionDestination + `/route_slash_command.cjs'); + await main(); +`) + return b.String(), nil +} + +func buildCommandsHeaderMetadata(routesByCommand map[string][]slashCommandRoute) commandsHeaderMetadata { + commands := make([]string, 0, len(routesByCommand)) + workflowSet := make(map[string]bool) + for command, routes := range routesByCommand { + commands = append(commands, command) + for _, route := range routes { + if route.Workflow != "" { + workflowSet[route.Workflow] = true + } + } + } + sort.Strings(commands) + workflows := make([]string, 0, len(workflowSet)) + for workflowID := range workflowSet { + workflows = append(workflows, workflowID) + } + sort.Strings(workflows) + return commandsHeaderMetadata{ + PayloadVersion: "v1", + SchemaVersion: "v1", + Compiler: GetVersion(), + Commands: commands, + Workflows: workflows, + } +} + +func resolveCentralSlashRunsOn(workflowDataList []*WorkflowData) string { + counts := map[string]int{} + for _, wd := range workflowDataList { + if wd == nil || !wd.CommandCentralized || len(wd.Command) == 0 { + continue + } + + resolved := constants.DefaultActivationJobRunnerImage + if wd.SafeOutputs != nil && strings.TrimSpace(wd.SafeOutputs.RunsOn) != "" { + resolved = strings.TrimSpace(wd.SafeOutputs.RunsOn) + } else if strings.TrimSpace(wd.RunsOnSlim) != "" { + resolved = strings.TrimSpace(wd.RunsOnSlim) + } + counts[resolved]++ + } + + best := constants.DefaultActivationJobRunnerImage + bestCount := counts[best] + for candidate, count := range counts { + if count > bestCount || (count == bestCount && candidate < best) { + best = candidate + bestCount = count + } + } + return best +} + +func writeCentralSlashEventsYAML(b *strings.Builder, mergedEvents map[string]map[string]bool) { + eventOrder := []string{ + "issues", + "issue_comment", + "pull_request", + "pull_request_review_comment", + "discussion", + "discussion_comment", + } + + for _, eventName := range eventOrder { + typeSet := mergedEvents[eventName] + if len(typeSet) == 0 { + continue + } + types := make([]string, 0, len(typeSet)) + for t := range typeSet { + types = append(types, t) + } + sort.Strings(types) + b.WriteString(" " + eventName + ":\n") + b.WriteString(" types: [" + strings.Join(types, ", ") + "]\n") + } +} + +func uniqueSorted(values []string) []string { + seen := make(map[string]bool, len(values)) + for _, v := range values { + seen[v] = true + } + result := make([]string, 0, len(seen)) + for v := range seen { + result = append(result, v) + } + sort.Strings(result) + return result +} + +func escapeSingleQuotedYAMLString(input string) string { + return strings.ReplaceAll(input, "'", "''") +} diff --git a/pkg/workflow/central_slash_command_workflow_test.go b/pkg/workflow/central_slash_command_workflow_test.go new file mode 100644 index 00000000000..f45bdee2693 --- /dev/null +++ b/pkg/workflow/central_slash_command_workflow_test.go @@ -0,0 +1,193 @@ +//go:build !integration + +package workflow + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/github/gh-aw/pkg/testutil" + "github.com/stretchr/testify/require" +) + +func TestGenerateCentralSlashCommandWorkflow_GeneratesWorkflow(t *testing.T) { + tmpDir := testutil.TempDir(t, "central-slash-workflow-test") + t.Setenv("GH_AW_ACTION_MODE", "dev") + + data := []*WorkflowData{ + { + WorkflowID: "triage-issue", + Command: []string{"triage"}, + CommandEvents: []string{"issue_comment", "issues"}, + CommandCentralized: true, + }, + { + WorkflowID: "triage-pr", + Command: []string{"triage"}, + CommandEvents: []string{"pull_request", "pull_request_comment"}, + CommandCentralized: true, + }, + { + WorkflowID: "cloclo", + Command: []string{"cloclo"}, + CommandEvents: []string{"discussion_comment"}, + CommandCentralized: true, + }, + } + + require.NoError(t, GenerateCentralSlashCommandWorkflow(data, tmpDir)) + + generatedPath := filepath.Join(tmpDir, centralSlashCommandWorkflowFilename) + content, err := os.ReadFile(generatedPath) + require.NoError(t, err) + text := string(content) + lines := strings.Split(text, "\n") + require.NotEmpty(t, lines) + require.Contains(t, lines[0], "# gh-aw-commands: ") + metadataJSON := strings.TrimPrefix(lines[0], "# gh-aw-commands: ") + var metadata commandsHeaderMetadata + require.NoError(t, json.Unmarshal([]byte(metadataJSON), &metadata)) + require.Equal(t, "v1", metadata.PayloadVersion) + require.Equal(t, "v1", metadata.SchemaVersion) + require.NotEmpty(t, metadata.Compiler) + require.Equal(t, []string{"cloclo", "triage"}, metadata.Commands) + require.Equal(t, []string{"cloclo", "triage-issue", "triage-pr"}, metadata.Workflows) + + require.Contains(t, text, "name: \"Agentic Commands\"") + require.NotContains(t, text, "Compiler version:") + require.Contains(t, text, "permissions: {}") + require.Contains(t, text, "runs-on: ubuntu-slim") + require.Contains(t, text, " permissions:\n actions: write\n contents: read") + require.Contains(t, text, " - name: Setup Scripts") + require.Contains(t, text, " uses: ./actions/setup") + require.Contains(t, text, " destination: ${{ runner.temp }}/gh-aw/actions") + require.Contains(t, text, "issues:") + require.Contains(t, text, "issue_comment:") + require.Contains(t, text, "pull_request:") + require.Contains(t, text, "discussion_comment:") + require.Contains(t, text, `"triage":[{"workflow":"triage-issue","events":["issue_comment","issues"]},{"workflow":"triage-pr","events":["pull_request","pull_request_comment"]}]`) + require.Contains(t, text, `"cloclo":[{"workflow":"cloclo","events":["discussion_comment"]}]`) + require.Contains(t, text, `require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs')`) + require.Contains(t, text, `setupGlobals(core, github, context, exec, io, getOctokit);`) + require.Contains(t, text, `require('${{ runner.temp }}/gh-aw/actions/route_slash_command.cjs')`) + require.NotContains(t, text, `const routeMap = JSON.parse(process.env.GH_AW_SLASH_ROUTING || "{}");`) + require.NotContains(t, text, `trustedAuthorAssociations`) + require.NotContains(t, text, `isForkBasedPullRequestEvent`) + require.NotContains(t, text, `workflow_id: route.workflow + ".lock.yml"`) +} + +func TestGenerateCentralSlashCommandWorkflow_DeletesWhenUnused(t *testing.T) { + tmpDir := testutil.TempDir(t, "central-slash-workflow-delete-test") + generatedPath := filepath.Join(tmpDir, centralSlashCommandWorkflowFilename) + require.NoError(t, os.WriteFile(generatedPath, []byte("stale"), 0644)) + + data := []*WorkflowData{ + { + WorkflowID: "regular", + Command: []string{"regular"}, + CommandEvents: []string{"issue_comment"}, + CommandCentralized: false, + }, + } + + require.NoError(t, GenerateCentralSlashCommandWorkflow(data, tmpDir)) + _, err := os.Stat(generatedPath) + require.Error(t, err) + require.True(t, os.IsNotExist(err)) +} + +func TestRemoveIfExists(t *testing.T) { + tmpDir := testutil.TempDir(t, "remove-if-exists-test") + existingPath := filepath.Join(tmpDir, "existing.txt") + missingPath := filepath.Join(tmpDir, "missing.txt") + + require.NoError(t, os.WriteFile(existingPath, []byte("content"), 0644)) + require.NoError(t, removeIfExists(existingPath)) + _, err := os.Stat(existingPath) + require.Error(t, err) + require.True(t, os.IsNotExist(err)) + + require.NoError(t, removeIfExists(missingPath)) +} + +func TestCollectCentralSlashCommandRoutes_UnionizesMergedEvents(t *testing.T) { + data := []*WorkflowData{ + { + WorkflowID: "triage-issue", + Command: []string{"triage"}, + CommandEvents: []string{"issues", "issue_comment"}, + CommandCentralized: true, + }, + { + WorkflowID: "triage-pr", + Command: []string{"triage"}, + CommandEvents: []string{"pull_request", "pull_request_comment"}, + CommandCentralized: true, + }, + { + WorkflowID: "non-centralized", + Command: []string{"triage"}, + CommandEvents: []string{"discussion"}, + CommandCentralized: false, + }, + } + + routesByCommand, mergedEvents := collectCentralSlashCommandRoutes(data) + + require.Equal(t, []slashCommandRoute{ + {Workflow: "triage-issue", Events: []string{"issue_comment", "issues"}}, + {Workflow: "triage-pr", Events: []string{"pull_request", "pull_request_comment"}}, + }, routesByCommand["triage"]) + + require.ElementsMatch(t, []string{"opened", "edited", "reopened"}, typeSetKeys(mergedEvents["issues"])) + require.ElementsMatch(t, []string{"created", "edited"}, typeSetKeys(mergedEvents["issue_comment"])) + require.ElementsMatch(t, []string{"opened", "edited", "reopened"}, typeSetKeys(mergedEvents["pull_request"])) + require.NotContains(t, mergedEvents, "discussion") +} + +func TestGenerateCentralSlashCommandWorkflow_UsesCentralizedRunsOnResolution(t *testing.T) { + tmpDir := testutil.TempDir(t, "central-slash-workflow-runs-on-test") + data := []*WorkflowData{ + { + WorkflowID: "one", + Command: []string{"one"}, + CommandEvents: []string{"issue_comment"}, + CommandCentralized: true, + RunsOnSlim: "ubuntu-latest", + }, + { + WorkflowID: "two", + Command: []string{"two"}, + CommandEvents: []string{"issue_comment"}, + CommandCentralized: true, + SafeOutputs: &SafeOutputsConfig{ + RunsOn: "self-hosted", + }, + }, + { + WorkflowID: "three", + Command: []string{"three"}, + CommandEvents: []string{"issue_comment"}, + CommandCentralized: true, + SafeOutputs: &SafeOutputsConfig{ + RunsOn: "self-hosted", + }, + }, + } + + require.NoError(t, GenerateCentralSlashCommandWorkflow(data, tmpDir)) + content, err := os.ReadFile(filepath.Join(tmpDir, centralSlashCommandWorkflowFilename)) + require.NoError(t, err) + require.Contains(t, string(content), "runs-on: self-hosted") +} + +func typeSetKeys(typeSet map[string]bool) []string { + out := make([]string, 0, len(typeSet)) + for key := range typeSet { + out = append(out, key) + } + return out +} diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index 40e03f2cd15..3873be7ab81 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -334,7 +334,7 @@ func (c *Compiler) extractAdditionalConfigurations( workflowData.RepoMemoryConfig = repoMemoryConfig // Extract and process mcp-scripts and safe-outputs - workflowData.Command, workflowData.CommandEvents = c.extractCommandConfig(frontmatter) + workflowData.Command, workflowData.CommandEvents, workflowData.CommandCentralized = c.extractCommandConfig(frontmatter) workflowData.LabelCommand, workflowData.LabelCommandEvents, workflowData.LabelCommandRemoveLabel = c.extractLabelCommandConfig(frontmatter) workflowData.Jobs = c.extractJobsFromFrontmatter(frontmatter) diff --git a/pkg/workflow/compiler_safe_outputs.go b/pkg/workflow/compiler_safe_outputs.go index 00937cc30f2..d6b6c2499c9 100644 --- a/pkg/workflow/compiler_safe_outputs.go +++ b/pkg/workflow/compiler_safe_outputs.go @@ -150,15 +150,19 @@ func (c *Compiler) parseOnSection(frontmatter map[string]any, workflowData *Work baseName := strings.TrimSuffix(filepath.Base(markdownPath), ".md") workflowData.Command = []string{baseName} } - // Check for conflicting events (but allow issues/pull_request with non-conflicting types: labeled/unlabeled/ready_for_review) - conflictingEvents := []string{"issues", "issue_comment", "pull_request", "pull_request_review_comment"} - for _, eventName := range conflictingEvents { - if eventValue, hasConflict := onMap[eventName]; hasConflict { - // Special case: allow issues/pull_request with non-conflicting types - if (eventName == "issues" || eventName == "pull_request") && parser.IsNonConflictingCommandEvent(eventValue) { - continue // Allow this - it doesn't conflict with command triggers + // In centralized mode slash_command no longer compiles broad comment listeners, + // so slash/non-slash event co-existence is allowed. + if !workflowData.CommandCentralized { + // Check for conflicting events (but allow issues/pull_request with non-conflicting types: labeled/unlabeled/ready_for_review) + conflictingEvents := []string{"issues", "issue_comment", "pull_request", "pull_request_review_comment"} + for _, eventName := range conflictingEvents { + if eventValue, hasConflict := onMap[eventName]; hasConflict { + // Special case: allow issues/pull_request with non-conflicting types + if (eventName == "issues" || eventName == "pull_request") && parser.IsNonConflictingCommandEvent(eventValue) { + continue // Allow this - it doesn't conflict with command triggers + } + return fmt.Errorf("cannot use 'slash_command' with '%s' in the same workflow", eventName) } - return fmt.Errorf("cannot use 'slash_command' with '%s' in the same workflow", eventName) } } diff --git a/pkg/workflow/compiler_safe_outputs_test.go b/pkg/workflow/compiler_safe_outputs_test.go index 6735e333247..b820e3135cc 100644 --- a/pkg/workflow/compiler_safe_outputs_test.go +++ b/pkg/workflow/compiler_safe_outputs_test.go @@ -21,6 +21,7 @@ func TestParseOnSection(t *testing.T) { expectedReaction string expectedLockAgent bool expectedOn string + expectedCentralized bool checkCommandEvents bool expectedOtherEvents map[string]any }{ @@ -129,6 +130,26 @@ func TestParseOnSection(t *testing.T) { markdownPath: "/path/to/test.md", expectedError: true, }, + { + name: "slash_command centralized strategy allows non-slash events", + frontmatter: map[string]any{ + "on": map[string]any{ + "slash_command": map[string]any{ + "strategy": "centralized", + }, + "issue_comment": map[string]any{ + "types": []string{"created"}, + }, + }, + }, + workflowData: &WorkflowData{CommandCentralized: true}, + markdownPath: "/path/to/test.md", + expectedError: false, + expectedCommand: []string{"test"}, + expectedReaction: "eyes", + expectedCentralized: true, + checkCommandEvents: true, + }, { name: "slash_command conflicts with issues", frontmatter: map[string]any{ @@ -277,6 +298,7 @@ func TestParseOnSection(t *testing.T) { if tt.expectedReaction != "" { assert.Equal(t, tt.expectedReaction, tt.workflowData.AIReaction, "Reaction mismatch") } + assert.Equal(t, tt.expectedCentralized, tt.workflowData.CommandCentralized, "CommandCentralized mismatch") assert.Equal(t, tt.expectedLockAgent, tt.workflowData.LockForAgent, "LockForAgent mismatch") if tt.checkCommandEvents { assert.NotNil(t, tt.workflowData.CommandOtherEvents, "CommandOtherEvents should be set") @@ -369,6 +391,23 @@ func TestCompilerMergeSafeJobsFromIncludedConfigs(t *testing.T) { } } +func TestExtractCommandConfig_CentralizedStrategy(t *testing.T) { + c := &Compiler{} + names, events, centralized := c.extractCommandConfig(map[string]any{ + "on": map[string]any{ + "slash_command": map[string]any{ + "name": "deploy", + "events": []any{"issue_comment"}, + "strategy": "centralized", + }, + }, + }) + + assert.Equal(t, []string{"deploy"}, names) + assert.Equal(t, []string{"issue_comment"}, events) + assert.True(t, centralized) +} + // TestApplyDefaultTools tests default tool application logic func TestApplyDefaultTools(t *testing.T) { tests := []struct { diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go index a09bb78982a..8d19dc11cf5 100644 --- a/pkg/workflow/compiler_types.go +++ b/pkg/workflow/compiler_types.go @@ -485,6 +485,7 @@ type WorkflowData struct { ManualApproval string // environment name for manual approval from on: section Command []string // for /command trigger support - multiple command names CommandEvents []string // events where command should be active (nil = all events) + CommandCentralized bool // when true, slash_command uses centralized dispatch routing via workflow_dispatch CommandOtherEvents map[string]any // for merging command with other events LabelCommand []string // for label-command trigger support - label names that act as commands LabelCommandEvents []string // events where label-command should be active (nil = all: issues, pull_request, discussion) diff --git a/pkg/workflow/compiler_validators.go b/pkg/workflow/compiler_validators.go index fe129ae3aa2..178bf6b971e 100644 --- a/pkg/workflow/compiler_validators.go +++ b/pkg/workflow/compiler_validators.go @@ -305,6 +305,12 @@ func (c *Compiler) validateToolConfiguration(workflowData *WorkflowData, markdow c.IncrementWarningCount() } + // Emit experimental warning for centralized slash-command routing strategy + if workflowData.CommandCentralized { + fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Using experimental feature: slash_command.strategy: centralized")) + c.IncrementWarningCount() + } + // Warn when slash_command and bots are both configured: if a bot listed in bots: posts // a comment that starts with the slash command text (e.g. /command-name), the // check_command_position check will pass and the bot will trigger the workflow — diff --git a/pkg/workflow/frontmatter_extraction_yaml.go b/pkg/workflow/frontmatter_extraction_yaml.go index 13f82bc39fe..75be714a9c2 100644 --- a/pkg/workflow/frontmatter_extraction_yaml.go +++ b/pkg/workflow/frontmatter_extraction_yaml.go @@ -957,8 +957,9 @@ func (c *Compiler) extractExpressionFromIfString(ifString string) string { return ifString } -// extractCommandConfig extracts command configuration from frontmatter including name and events -func (c *Compiler) extractCommandConfig(frontmatter map[string]any) (commandNames []string, commandEvents []string) { +// extractCommandConfig extracts command configuration from frontmatter including name, events, +// and centralized routing strategy for slash_command. +func (c *Compiler) extractCommandConfig(frontmatter map[string]any) (commandNames []string, commandEvents []string, commandCentralized bool) { frontmatterLog.Print("Extracting command configuration from frontmatter") // Check new format: on.slash_command or on.slash_command.name (preferred) // Also check legacy format: on.command or on.command.name (deprecated) @@ -990,12 +991,13 @@ func (c *Compiler) extractCommandConfig(frontmatter map[string]any) (commandName // Check if command is a string (shorthand format) if commandStr, ok := commandValue.(string); ok { frontmatterLog.Printf("Extracted command name (shorthand): %s", commandStr) - return []string{commandStr}, nil // nil means default (all events) + return []string{commandStr}, nil, false // nil means default (all events) } // Check if command is a map with a name key (object format) if commandMap, ok := commandValue.(map[string]any); ok { var names []string var events []string + centralized := false if nameValue, hasName := commandMap["name"]; hasName { // Handle string or array of strings @@ -1015,14 +1017,20 @@ func (c *Compiler) extractCommandConfig(frontmatter map[string]any) (commandName events = ParseCommandEvents(eventsValue) } - frontmatterLog.Printf("Extracted command config: names=%v, events=%v", names, events) - return names, events + if strategyRaw, hasStrategy := commandMap["strategy"]; hasStrategy { + if strategy, ok := strategyRaw.(string); ok && strings.EqualFold(strings.TrimSpace(strategy), "centralized") { + centralized = true + } + } + + frontmatterLog.Printf("Extracted command config: names=%v, events=%v, centralized=%v", names, events, centralized) + return names, events, centralized } } } } - return nil, nil + return nil, nil, false } // extractLabelCommandConfig extracts the label-command configuration from frontmatter diff --git a/pkg/workflow/label_command.go b/pkg/workflow/label_command.go index f46c3393b26..c51b752c75b 100644 --- a/pkg/workflow/label_command.go +++ b/pkg/workflow/label_command.go @@ -93,3 +93,37 @@ func buildLabelCommandCondition(labelNames []string, labelCommandEvents []string Right: isNotLabelEvent, }, nil } + +// buildCentralizedLabelCommandCondition builds label-command conditions for centralized +// slash-command workflows that trigger through workflow_dispatch. +// For workflow_dispatch events, label routing checks use aw_context fields rather than +// github.event_name/github.event.label. +func buildCentralizedLabelCommandCondition(labelNames []string, labelCommandEvents []string) (ConditionNode, error) { + if len(labelNames) == 0 { + return nil, errors.New("no label names provided for label-command trigger") + } + filteredEvents := FilterLabelCommandEvents(labelCommandEvents) + if len(filteredEvents) == 0 { + return nil, errors.New("no valid events specified for label-command trigger") + } + + labelExpr := BuildPropertyAccess("fromJSON(github.event.inputs.aw_context || '{}').trigger_label") + eventExpr := BuildPropertyAccess("fromJSON(github.event.inputs.aw_context || '{}').event_type") + + var labelChecks []ConditionNode + for _, labelName := range labelNames { + labelChecks = append(labelChecks, BuildEquals(labelExpr, BuildStringLiteral(labelName))) + } + labelNameMatch := BuildDisjunction(false, labelChecks...) + + var eventChecks []ConditionNode + for _, event := range filteredEvents { + eventChecks = append(eventChecks, BuildEquals(eventExpr, BuildStringLiteral(event))) + } + isLabelSourceEvent := BuildDisjunction(false, eventChecks...) + dispatchLabelCondition := &OrNode{ + Left: &AndNode{Left: isLabelSourceEvent, Right: labelNameMatch}, + Right: &NotNode{Child: isLabelSourceEvent}, + } + return dispatchLabelCondition, nil +} diff --git a/pkg/workflow/label_command_test.go b/pkg/workflow/label_command_test.go index 74b7fe5ed83..cbd2e69c2f5 100644 --- a/pkg/workflow/label_command_test.go +++ b/pkg/workflow/label_command_test.go @@ -199,6 +199,15 @@ func TestBuildLabelCommandCondition(t *testing.T) { } } +func TestBuildCentralizedLabelCommandCondition(t *testing.T) { + condition, err := buildCentralizedLabelCommandCondition([]string{"cloclo"}, []string{"issues"}) + require.NoError(t, err) + rendered := condition.Render() + assert.NotContains(t, rendered, "github.event_name") + assert.Contains(t, rendered, "fromJSON(github.event.inputs.aw_context || '{}').event_type == 'issues'") + assert.Contains(t, rendered, "fromJSON(github.event.inputs.aw_context || '{}').trigger_label == 'cloclo'") +} + // TestLabelCommandWorkflowCompile verifies that a workflow with label_command trigger // compiles to a valid GitHub Actions workflow with: // - label-based events (issues, pull_request, discussion) in the on: section diff --git a/pkg/workflow/slash_command_centralized_compile_test.go b/pkg/workflow/slash_command_centralized_compile_test.go new file mode 100644 index 00000000000..7b2d7f6e82e --- /dev/null +++ b/pkg/workflow/slash_command_centralized_compile_test.go @@ -0,0 +1,85 @@ +//go:build !integration + +package workflow + +import ( + "os" + "path/filepath" + "testing" + + "github.com/github/gh-aw/pkg/stringutil" + "github.com/github/gh-aw/pkg/testutil" + "github.com/stretchr/testify/require" +) + +func TestCompileWorkflow_SlashCommandCentralizedStrategy(t *testing.T) { + tmpDir := testutil.TempDir(t, "workflow-centralized-slash-test") + + markdownPath := filepath.Join(tmpDir, "deploy.md") + content := `--- +on: + slash_command: + name: deploy + strategy: centralized + push: + branches: [main] +tools: + github: + allowed: [list_issues] +--- + +# Deploy +` + require.NoError(t, os.WriteFile(markdownPath, []byte(content), 0644)) + + compiler := NewCompiler() + require.NoError(t, compiler.CompileWorkflow(markdownPath)) + + lockPath := stringutil.MarkdownToLockFile(markdownPath) + lockContent, err := os.ReadFile(lockPath) + require.NoError(t, err) + compiled := string(lockContent) + + require.Contains(t, compiled, "workflow_dispatch:") + require.Contains(t, compiled, "push:") + require.NotContains(t, compiled, "issue_comment:") + require.NotContains(t, compiled, "pull_request_review_comment:") + require.NotContains(t, compiled, "startsWith(github.event.comment.body") +} + +func TestCompileWorkflow_SlashCommandCentralizedWithLabelCommand(t *testing.T) { + tmpDir := testutil.TempDir(t, "workflow-centralized-slash-label-test") + + markdownPath := filepath.Join(tmpDir, "triage.md") + content := `--- +on: + slash_command: + name: triage + strategy: centralized + label_command: + name: triage + events: [issues] +tools: + github: + allowed: [list_issues] +--- + +# Triage +` + require.NoError(t, os.WriteFile(markdownPath, []byte(content), 0644)) + + compiler := NewCompiler() + require.NoError(t, compiler.CompileWorkflow(markdownPath)) + + lockPath := stringutil.MarkdownToLockFile(markdownPath) + lockContent, err := os.ReadFile(lockPath) + require.NoError(t, err) + compiled := string(lockContent) + + require.Contains(t, compiled, "\"on\":\n workflow_dispatch:") + require.Contains(t, compiled, "workflow_dispatch:") + require.NotContains(t, compiled, "\n issues:\n types:") + require.NotContains(t, compiled, "github.event_name == 'workflow_dispatch'") + require.Contains(t, compiled, "fromJSON(github.event.inputs.aw_context || '{}').trigger_label == 'triage'") + require.Contains(t, compiled, "fromJSON(github.event.inputs.aw_context || '{}').event_type == 'issues'") +} diff --git a/pkg/workflow/slash_command_centralized_experimental_warning_test.go b/pkg/workflow/slash_command_centralized_experimental_warning_test.go new file mode 100644 index 00000000000..569b6778ce4 --- /dev/null +++ b/pkg/workflow/slash_command_centralized_experimental_warning_test.go @@ -0,0 +1,80 @@ +//go:build integration + +package workflow + +import ( + "bytes" + "io" + "os" + "path/filepath" + "testing" + + "github.com/github/gh-aw/pkg/testutil" + "github.com/stretchr/testify/require" +) + +func TestSlashCommandCentralizedExperimentalWarning(t *testing.T) { + tests := []struct { + name string + content string + expectWarning bool + }{ + { + name: "centralized strategy emits warning", + content: `--- +on: + slash_command: + name: triage + strategy: centralized +--- + +# Test Workflow +`, + expectWarning: true, + }, + { + name: "inline strategy does not emit warning", + content: `--- +on: + slash_command: + name: triage +--- + +# Test Workflow +`, + expectWarning: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir := testutil.TempDir(t, "slash-command-centralized-warning-test") + workflowPath := filepath.Join(tmpDir, "test-workflow.md") + require.NoError(t, os.WriteFile(workflowPath, []byte(tt.content), 0644)) + + oldStderr := os.Stderr + r, w, _ := os.Pipe() + os.Stderr = w + + compiler := NewCompiler() + compiler.SetStrictMode(false) + err := compiler.CompileWorkflow(workflowPath) + + w.Close() + os.Stderr = oldStderr + + var buf bytes.Buffer + _, _ = io.Copy(&buf, r) + stderrOutput := buf.String() + require.NoError(t, err) + + expected := "Using experimental feature: slash_command.strategy: centralized" + if tt.expectWarning { + require.Contains(t, stderrOutput, expected) + require.Greater(t, compiler.GetWarningCount(), 0) + } else { + require.NotContains(t, stderrOutput, expected) + } + }) + } +} diff --git a/pkg/workflow/tools.go b/pkg/workflow/tools.go index e19d6cfa347..bb221755c6d 100644 --- a/pkg/workflow/tools.go +++ b/pkg/workflow/tools.go @@ -82,29 +82,42 @@ func (c *Compiler) applyDefaults(data *WorkflowData, markdownPath string) error if isCommandTrigger { toolsLog.Print("Workflow is command trigger, configuring command events") - // Get the filtered command events based on CommandEvents field - filteredEvents := FilterCommentEvents(data.CommandEvents) + commandEventsMap := make(map[string]any) - // Merge events for YAML generation (combines pull_request_comment and issue_comment into issue_comment) - yamlEvents := MergeEventsForYAML(filteredEvents) + // In centralized slash-command mode, compile slash workflows as + // workflow_dispatch-centric targets and preserve only non-slash events. + var filteredEvents []CommentEventMapping + if data.CommandCentralized { + if len(data.CommandOtherEvents) > 0 { + maps.Copy(commandEventsMap, data.CommandOtherEvents) + } + if _, hasWorkflowDispatch := commandEventsMap["workflow_dispatch"]; !hasWorkflowDispatch { + commandEventsMap["workflow_dispatch"] = nil + } + } else { + // Get the filtered command events based on CommandEvents field + filteredEvents = FilterCommentEvents(data.CommandEvents) - // Build command events map from merged events - commandEventsMap := make(map[string]any) - for _, event := range yamlEvents { - commandEventsMap[event.EventName] = map[string]any{ - "types": event.Types, + // Merge events for YAML generation (combines pull_request_comment and issue_comment into issue_comment) + yamlEvents := MergeEventsForYAML(filteredEvents) + + // Build command events map from merged events + for _, event := range yamlEvents { + commandEventsMap[event.EventName] = map[string]any{ + "types": event.Types, + } } - } - // Check if there are other events to merge - if len(data.CommandOtherEvents) > 0 { - // Merge other events into command events - maps.Copy(commandEventsMap, data.CommandOtherEvents) + // Check if there are other events to merge + if len(data.CommandOtherEvents) > 0 { + // Merge other events into command events + maps.Copy(commandEventsMap, data.CommandOtherEvents) + } } - // If label_command is also configured alongside slash_command, merge label events - // into the existing command events map to avoid duplicate YAML keys. - if len(data.LabelCommand) > 0 { + // If label_command is also configured alongside non-centralized slash_command, merge + // label events into the existing command events map to avoid duplicate YAML keys. + if len(data.LabelCommand) > 0 && !data.CommandCentralized { labelEventNames := FilterLabelCommandEvents(data.LabelCommandEvents) for _, eventName := range labelEventNames { if existingAny, ok := commandEventsMap[eventName]; ok { @@ -141,45 +154,42 @@ func (c *Compiler) applyDefaults(data *WorkflowData, markdownPath string) error // Keep "on" quoted as it's a YAML boolean keyword data.On = yamlStr } else { - // If conversion fails, build a basic YAML string manually - var builder strings.Builder - builder.WriteString(`"on":`) - for _, event := range filteredEvents { - builder.WriteString("\n ") - builder.WriteString(event.EventName) - builder.WriteString(":\n types: [") - for i, t := range event.Types { - if i > 0 { - builder.WriteString(", ") - } - builder.WriteString(t) - } - builder.WriteString("]") - } - data.On = builder.String() + return fmt.Errorf("failed to marshal command events: %w", err) } - // Add conditional logic to check for command in issue content - // Use event-aware condition that only applies command checks to comment-related events - // Pass the filtered events to buildEventAwareCommandCondition - hasOtherEvents := len(data.CommandOtherEvents) > 0 - commandConditionTree, err := buildEventAwareCommandCondition(data.Command, data.CommandEvents, hasOtherEvents) - if err != nil { - return fmt.Errorf("failed to build command condition: %w", err) - } + // Add conditional logic for command workflows unless centralized mode is enabled. + if !data.CommandCentralized { + // Add conditional logic to check for command in issue content + // Use event-aware condition that only applies command checks to comment-related events + // Pass the filtered events to buildEventAwareCommandCondition + hasOtherEvents := len(data.CommandOtherEvents) > 0 + commandConditionTree, err := buildEventAwareCommandCondition(data.Command, data.CommandEvents, hasOtherEvents) + if err != nil { + return fmt.Errorf("failed to build command condition: %w", err) + } - if data.If == "" { - if len(data.LabelCommand) > 0 { - // Combine: (slash_command condition) OR (label_command condition) - // This allows the workflow to activate via either mechanism. - labelConditionTree, err := buildLabelCommandCondition(data.LabelCommand, data.LabelCommandEvents, false) - if err != nil { - return fmt.Errorf("failed to build combined label-command condition: %w", err) + if data.If == "" { + if len(data.LabelCommand) > 0 { + // Combine: (slash_command condition) OR (label_command condition) + // This allows the workflow to activate via either mechanism. + labelConditionTree, err := buildLabelCommandCondition(data.LabelCommand, data.LabelCommandEvents, false) + if err != nil { + return fmt.Errorf("failed to build combined label-command condition: %w", err) + } + combined := &OrNode{Left: commandConditionTree, Right: labelConditionTree} + data.If = RenderCondition(combined) + } else { + data.If = RenderCondition(commandConditionTree) } - combined := &OrNode{Left: commandConditionTree, Right: labelConditionTree} - data.If = RenderCondition(combined) + } + } else if data.If == "" && len(data.LabelCommand) > 0 { + // Centralized command mode compiles slash-command workflows as workflow_dispatch + // targets. Label checks for dispatches must be derived from aw_context metadata. + labelConditionTree, err := buildCentralizedLabelCommandCondition(data.LabelCommand, data.LabelCommandEvents) + if err != nil { + return fmt.Errorf("failed to build label-command condition: %w", err) } else { - data.If = RenderCondition(commandConditionTree) + data.If = RenderCondition(labelConditionTree) } } } else if isLabelCommandTrigger {