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 {