diff --git a/.github/workflows/cli-for-beginners-sync.lock.yml b/.github/workflows/cli-for-beginners-sync.lock.yml
deleted file mode 100644
index de28bc22b6..0000000000
--- a/.github/workflows/cli-for-beginners-sync.lock.yml
+++ /dev/null
@@ -1,1694 +0,0 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"e3f2550d5791d43527217e604df3e07ee4354209a733e4beb18b02213af380f4","body_hash":"0b544aa6df7ccf92d28c3c515573894ba1ca9d2882c327c2ad4fb1e21644969e","compiler_version":"v0.81.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65"}}
-# gh-aw-manifest: {"version":1,"secrets":["GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","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":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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"},{"repo":"github/gh-aw-actions/setup","sha":"ba6380cc6e5be5d21677bebe04d52fb48e3abec7","version":"v0.81.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]}
-# This file was automatically generated by gh-aw (v0.81.6). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
-#
-# ___ _ _
-# / _ \ | | (_)
-# | |_| | __ _ ___ _ __ | |_ _ ___
-# | _ |/ _` |/ _ \ '_ \| __| |/ __|
-# | | | | (_| | __/ | | | |_| | (__
-# \_| |_/\__, |\___|_| |_|\__|_|\___|
-# __/ |
-# _ _ |___/
-# | | | | / _| |
-# | | | | ___ _ __ _ __| |_| | _____ ____
-# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
-# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
-# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
-#
-#
-# To update this file, edit the corresponding .md file and run:
-# gh aw compile
-# Not all edits will cause changes to this file.
-#
-# For more information: https://github.github.com/gh-aw/introduction/overview/
-#
-# Weekly check for updates to github/copilot-cli-for-beginners. Opens a PR to keep the Learning Hub mirror aligned when substantive upstream course changes are detected.
-#
-# Secrets used:
-# - GH_AW_CI_TRIGGER_TOKEN
-# - GH_AW_GITHUB_MCP_SERVER_TOKEN
-# - GH_AW_GITHUB_TOKEN
-# - GITHUB_TOKEN
-#
-# Custom actions used:
-# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
-# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
-# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
-# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
-# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
-# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
-# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
-# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
-# - github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
-#
-# Container images used:
-# - ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7
-# - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d
-# - ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d
-# - ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be
-# - ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b
-# - ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036
-
-name: "CLI for Beginners Content Sync"
-on:
- schedule:
- - cron: "34 3 * * 5"
- # Friendly format: weekly (scattered)
- workflow_dispatch:
- inputs:
- aw_context:
- default: ""
- description: "Agent caller context (used internally by Agentic Workflows)."
- required: false
- type: string
-
-permissions: {}
-
-concurrency:
- group: "gh-aw-${{ github.workflow }}"
-
-run-name: "CLI for Beginners Content Sync"
-
-jobs:
- activation:
- runs-on: ubuntu-slim
- permissions:
- actions: read
- contents: read
- env:
- GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- comment_id: ""
- comment_repo: ""
- daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }}
- daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }}
- daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }}
- engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
- lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
- model: ${{ steps.generate_aw_info.outputs.model }}
- setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
- setup-span-id: ${{ steps.setup.outputs.span-id }}
- setup-trace-id: ${{ steps.setup.outputs.trace-id }}
- stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/cli-for-beginners-sync.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Generate agentic run info
- id: generate_aw_info
- env:
- GH_AW_INFO_ENGINE_ID: "copilot"
- GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
- GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AGENT_VERSION: "1.0.65"
- GH_AW_INFO_CLI_VERSION: "v0.81.6"
- GH_AW_INFO_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_INFO_EXPERIMENTAL: "false"
- GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
- GH_AW_INFO_STAGED: "false"
- GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
- GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_AWMG_VERSION: ""
- GH_AW_INFO_FIREWALL_TYPE: "squid"
- GH_AW_COMPILED_STRICT: "true"
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/generate_aw_info.cjs');
- await main(core, context);
- - name: Restore daily AIC usage cache
- id: restore-daily-aic-cache
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- continue-on-error: true
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-cliforbeginnerssync-${{ github.run_id }}
- restore-keys: agentic-workflow-usage-cliforbeginnerssync-
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Restore daily AIC usage cache (artifact fallback)
- id: restore-daily-aic-cache-fallback
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }}
- GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- 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/restore_aic_usage_cache_fallback.cjs');
- await main();
- - name: Check daily workflow token guardrail
- id: daily-effective-workflow-guardrail
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_WORKFLOW_ID: "cli-for-beginners-sync"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }}
- GH_AW_HAS_SLASH_COMMAND: "false"
- GH_AW_HAS_LABEL_COMMAND: "false"
- GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- 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/check_daily_aic_workflow_guardrail.cjs');
- await main();
- - name: Checkout .github and .agents folders
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- sparse-checkout: |
- .github
- .agents
- .antigravity
- .claude
- .codex
- .crush
- .gemini
- .opencode
- .pi
- sparse-checkout-cone-mode: true
- fetch-depth: 1
- - name: Save agent config folders for base branch restoration
- env:
- GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
- GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
- - name: Check workflow lock file
- id: check-lock-file
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_WORKFLOW_FILE: "cli-for-beginners-sync.lock.yml"
- GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
- 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/check_workflow_timestamp_api.cjs');
- await main();
- - name: Check compile-agentic version
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_COMPILED_VERSION: "v0.81.6"
- 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/check_version_updates.cjs');
- await main();
- - name: Log runtime features
- if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh"
- - name: Create prompt with built-in context
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- # poutine:ignore untrusted_checkout_exec
- run: |
- bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
- {
- cat << 'GH_AW_PROMPT_08855b4d37bd813d_EOF'
-
- GH_AW_PROMPT_08855b4d37bd813d_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_08855b4d37bd813d_EOF'
-
- Tools: create_pull_request, missing_tool, missing_data, noop
- GH_AW_PROMPT_08855b4d37bd813d_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md"
- cat << 'GH_AW_PROMPT_08855b4d37bd813d_EOF'
-
- GH_AW_PROMPT_08855b4d37bd813d_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_08855b4d37bd813d_EOF'
-
- The following GitHub context information is available for this workflow:
- {{#if github.actor}}
- - **actor**: __GH_AW_GITHUB_ACTOR__
- {{/if}}
- {{#if github.repository}}
- - **repository**: __GH_AW_GITHUB_REPOSITORY__
- {{/if}}
- {{#if github.workspace}}
- - **workspace**: __GH_AW_GITHUB_WORKSPACE__
- {{/if}}
- {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
- - **issue-number**: #__GH_AW_EXPR_802A9F6A__
- {{/if}}
- {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
- - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
- {{/if}}
- {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
- - **pull-request-number**: #__GH_AW_EXPR_463A214A__
- {{/if}}
- {{#if github.event.comment.id || github.aw.context.comment_id}}
- - **comment-id**: __GH_AW_EXPR_FF1D34CE__
- {{/if}}
- {{#if github.run_id}}
- - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
- {{/if}}
-
-
- GH_AW_PROMPT_08855b4d37bd813d_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_08855b4d37bd813d_EOF'
-
- {{#runtime-import .github/workflows/cli-for-beginners-sync.md}}
- GH_AW_PROMPT_08855b4d37bd813d_EOF
- } > "$GH_AW_PROMPT"
- - name: Interpolate variables and render templates
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_ENGINE_ID: "copilot"
- 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/interpolate_prompt.cjs');
- await main();
- - name: Substitute placeholders
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_ALLOWED_EXTENSIONS: ''
- GH_AW_CACHE_DESCRIPTION: ''
- GH_AW_CACHE_DIR: '/tmp/gh-aw/cache-memory/'
- GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
- with:
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io, getOctokit);
-
- const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
-
- // Call the substitution function
- return await substitutePlaceholders({
- file: process.env.GH_AW_PROMPT,
- substitutions: {
- GH_AW_ALLOWED_EXTENSIONS: process.env.GH_AW_ALLOWED_EXTENSIONS,
- GH_AW_CACHE_DESCRIPTION: process.env.GH_AW_CACHE_DESCRIPTION,
- GH_AW_CACHE_DIR: process.env.GH_AW_CACHE_DIR,
- GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
- GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
- GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
- GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
- GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
- GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
- GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
- GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
- }
- });
- - name: Validate prompt placeholders
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
- - name: Print prompt
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
- - name: Upload activation artifact
- if: success()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: activation
- include-hidden-files: true
- path: |
- /tmp/gh-aw/aw_info.json
- /tmp/gh-aw/models.json
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/aw-prompts/prompt-template.txt
- /tmp/gh-aw/aw-prompts/prompt-import-tree.json
- /tmp/gh-aw/github_rate_limits.jsonl
- /tmp/gh-aw/base
- /tmp/gh-aw/.github/agents
- /tmp/gh-aw/.github/skills
- if-no-files-found: ignore
- retention-days: 1
-
- agent:
- needs: activation
- if: needs.activation.outputs.daily_ai_credits_exceeded != 'true'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- copilot-requests: write
- concurrency:
- group: "gh-aw-copilot-${{ github.workflow }}"
- queue: max
- env:
- DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
- GH_AW_ASSETS_ALLOWED_EXTS: ""
- GH_AW_ASSETS_BRANCH: ""
- GH_AW_ASSETS_MAX_SIZE_KB: 0
- GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_WORKFLOW_ID_SANITIZED: cliforbeginnerssync
- outputs:
- agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
- ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }}
- aic: ${{ steps.parse-mcp-gateway.outputs.aic }}
- ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }}
- cache_memory_restore_0_cache_hit: ${{ steps.restore_cache_memory_0.outputs.cache-hit || 'false' }}
- cache_memory_restore_0_matched_key: ${{ steps.restore_cache_memory_0.outputs.cache-matched-key || '' }}
- checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
- effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
- has_patch: ${{ steps.collect_output.outputs.has_patch }}
- inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
- mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
- model: ${{ needs.activation.outputs.model }}
- model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
- output: ${{ steps.collect_output.outputs.output }}
- output_types: ${{ steps.collect_output.outputs.output_types }}
- setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
- setup-span-id: ${{ steps.setup.outputs.span-id }}
- setup-trace-id: ${{ steps.setup.outputs.trace-id }}
- unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/cli-for-beginners-sync.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Set runtime paths
- id: set-runtime-paths
- run: |
- {
- echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
- echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
- echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
- } >> "$GITHUB_OUTPUT"
- - name: Checkout repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- - name: Create gh-aw temp directory
- run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
- - name: Configure gh CLI for GitHub Enterprise
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
- env:
- GH_TOKEN: ${{ github.token }}
- # Cache memory file share configuration from frontmatter processed below
- - name: Create cache-memory directory
- run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
- - name: Restore cache-memory file share data
- id: restore_cache_memory_0
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
- path: /tmp/gh-aw/cache-memory
- restore-keys: |
- memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-
- - name: Setup cache-memory git repository
- env:
- GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
- GH_AW_MIN_INTEGRITY: none
- run: bash "${RUNNER_TEMP}/gh-aw/actions/setup_cache_memory_git.sh"
- - name: Configure Git credentials
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_TOKEN: ${{ github.token }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Checkout PR branch
- id: checkout-pr
- if: |
- github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request'
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io, getOctokit);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
- await main();
- - name: Install GitHub Copilot CLI
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11
- - name: Determine automatic lockdown mode for GitHub MCP Server
- id: determine-automatic-lockdown
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
- env:
- GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- with:
- script: |
- const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
- await determineAutomaticLockdown(github, context, core);
- - name: Download activation artifact
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: activation
- path: /tmp/gh-aw
- - name: Restore agent config folders from base branch
- if: steps.checkout-pr.outcome == 'success'
- env:
- GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
- GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
- - name: Restore inline sub-agents from activation artifact
- env:
- GH_AW_SUB_AGENT_DIR: ".github/agents"
- GH_AW_SUB_AGENT_EXT: ".agent.md"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
- - name: Restore inline skills from activation artifact
- env:
- GH_AW_SKILL_DIR: ".github/skills"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
- - name: Download container images
- run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036
- - name: Generate Safe Outputs Config
- run: |
- 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_9860a40b73cea1ae_EOF'
- {"create_pull_request":{"base_branch":"main","labels":["automated-update","learning-hub","cli-for-beginners"],"max":1,"max_patch_files":100,"max_patch_size":4096,"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"],"protected_files_policy":"request_review","title_prefix":"[bot] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_9860a40b73cea1ae_EOF
- - name: Generate Safe Outputs Tools
- env:
- GH_AW_TOOLS_META_JSON: |
- {
- "description_suffixes": {
- "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[bot] \". Labels [\"automated-update\" \"learning-hub\" \"cli-for-beginners\"] will be automatically added."
- },
- "repo_params": {},
- "dynamic_tools": []
- }
- GH_AW_VALIDATION_JSON: |
- {
- "create_pull_request": {
- "defaultMax": 1,
- "fields": {
- "base": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "body": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "branch": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "draft": {
- "type": "boolean"
- },
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- },
- "title": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "missing_data": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "context": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "data_type": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "reason": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- }
- }
- },
- "missing_tool": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 512
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "tool": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "noop": {
- "defaultMax": 1,
- "fields": {
- "message": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- }
- }
- },
- "report_incomplete": {
- "defaultMax": 5,
- "fields": {
- "details": {
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 1024
- }
- }
- }
- }
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/generate_safe_outputs_tools.cjs');
- await main();
- - name: Start MCP Gateway
- id: start-mcp-gateway
- env:
- GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }}
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }}
- GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
- GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- set -eo pipefail
- mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
-
- # Export gateway environment variables for MCP config and gateway script
- export MCP_GATEWAY_PORT="8080"
- export MCP_GATEWAY_DOMAIN="host.docker.internal"
- export MCP_GATEWAY_HOST_DOMAIN="localhost"
- MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
- echo "::add-mask::${MCP_GATEWAY_API_KEY}"
- export MCP_GATEWAY_API_KEY
- export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
- mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
- export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
- export DEBUG="*"
-
- export GH_AW_ENGINE="copilot"
- MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
- MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
- case "${DOCKER_HOST:-}" in
- unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
- /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
- * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
- esac
- 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 --name awmg-mcpg --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_POLICY_ALLOW_CREATE_PULL_REQUEST -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 RUNNER_TEMP -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 -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30'
-
- mkdir -p "$HOME/.copilot"
- GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_532027a3cd6cbb4c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
- {
- "mcpServers": {
- "github": {
- "type": "stdio",
- "container": "ghcr.io/github/github-mcp-server:v1.4.0",
- "env": {
- "GITHUB_HOST": "${GITHUB_SERVER_URL}",
- "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}",
- "GITHUB_READ_ONLY": "1",
- "GITHUB_TOOLSETS": "repos"
- },
- "guard-policies": {
- "allow-only": {
- "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
- "repos": "$GITHUB_MCP_GUARD_REPOS"
- }
- }
- },
- "safeoutputs": {
- "type": "stdio",
- "container": "ghcr.io/github/gh-aw-node",
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"],
- "args": ["-w", "\${GITHUB_WORKSPACE}"],
- "entrypoint": "sh",
- "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"],
- "env": {
- "DEBUG": "*",
- "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}",
- "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}",
- "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}",
- "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}",
- "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}",
- "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}",
- "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}",
- "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}",
- "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}",
- "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}",
- "GITHUB_TOKEN": "\${GITHUB_TOKEN}",
- "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}",
- "RUNNER_TEMP": "\${RUNNER_TEMP}"
- },
- "guard-policies": {
- "write-sink": {
- "accept": [
- "*"
- ]
- }
- }
- }
- },
- "gateway": {
- "port": $MCP_GATEWAY_PORT,
- "domain": "${MCP_GATEWAY_DOMAIN}",
- "apiKey": "${MCP_GATEWAY_API_KEY}",
- "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
- }
- }
- GH_AW_MCP_CONFIG_532027a3cd6cbb4c_EOF
- - name: Mount MCP servers as CLIs
- id: mount-mcp-clis
- continue-on-error: true
- env:
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
- await main();
- - name: Clean credentials
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
- - name: Audit pre-agent workspace
- id: pre_agent_audit
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
- - name: Execute GitHub Copilot CLI
- id: agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
- trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
- mkdir -p "$HOME/.copilot"
- printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
- export XDG_CONFIG_HOME="$HOME"
- export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json"
- touch /tmp/gh-aw/agent-step-summary.md
- GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
- export GH_AW_NODE_BIN
- export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
- (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
- GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}"
- printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"github.com\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"telemetry.enterprise.githubcopilot.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
- export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json"
- GH_AW_DOCKER_HOST=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST="${DOCKER_HOST}"
- fi
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
- GH_AW_CHROOT_BINARIES_SOURCE_PATH=/tmp/gh-aw GH_AW_CHROOT_IDENTITY_HOME=/tmp/gh-aw/home node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs"
- fi
- GH_AW_TOOL_CACHE_MOUNT=""
- GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"
- if [ -d "$GH_AW_TOOL_CACHE" ]; then
- if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
- GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
- fi
- fi
- # shellcheck disable=SC1003,SC2086
- sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
- -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
- env:
- AWF_REFLECT_ENABLED: 1
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
- COPILOT_GITHUB_TOKEN: ${{ github.token }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_LLM_PROVIDER: github
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
- GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
- GH_AW_PHASE: agent
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_TIMEOUT_MINUTES: 20
- GH_AW_VERSION: v0.81.6
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- RUNNER_TEMP: ${{ runner.temp }}
- S2STOKENS: true
- TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- - name: Detect agent errors
- if: always()
- id: detect-agent-errors
- continue-on-error: true
- run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
- - name: Configure Git credentials
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_TOKEN: ${{ github.token }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Copy Copilot session state files to logs
- if: always()
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
- - name: Stop MCP Gateway
- if: always()
- continue-on-error: true
- env:
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
- run: |
- bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- - name: Redact secrets in logs
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/redact_secrets.cjs');
- await main();
- env:
- GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
- SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Append agent step summary
- if: always()
- run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
- - name: Copy Safe Outputs
- if: always()
- env:
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- run: |
- mkdir -p /tmp/gh-aw
- cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
- - name: Ingest agent output
- id: collect_output
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- 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/collect_ndjson_output.cjs');
- await main();
- - name: Parse agent logs for step summary
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
- 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/parse_copilot_log.cjs');
- await main();
- - name: Parse MCP Gateway logs for step summary
- if: always()
- id: parse-mcp-gateway
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/parse_mcp_gateway_log.cjs');
- await main();
- - name: Print firewall logs
- if: always()
- continue-on-error: true
- env:
- AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
- run: |
- # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts
- # AWF runs with sudo, creating files owned by root
- sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
- # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
- if command -v awf &> /dev/null; then
- awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
- else
- echo 'AWF binary not installed, skipping firewall log summary'
- fi
- - name: Parse token usage for step summary
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/parse_token_usage.cjs');
- await main();
- - name: Print AWF reflect summary
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/awf_reflect_summary.cjs');
- await main();
- - name: Write agent output placeholder if missing
- if: always()
- run: |
- if [ ! -f /tmp/gh-aw/agent_output.json ]; then
- echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
- fi
- - name: Commit cache-memory changes
- if: always()
- env:
- GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
- run: bash "${RUNNER_TEMP}/gh-aw/actions/commit_cache_memory_git.sh"
- - name: Check cache-memory git integrity
- if: always()
- continue-on-error: true
- env:
- GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
- run: bash "${RUNNER_TEMP}/gh-aw/actions/check_cache_memory_git_integrity.sh"
- - name: Upload cache-memory data as artifact
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- if: always()
- with:
- name: cache-memory
- include-hidden-files: true
- path: /tmp/gh-aw/cache-memory
- - name: Upload agent artifacts
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: agent
- path: |
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/sandbox/agent/logs/
- /tmp/gh-aw/redacted-urls.log
- /tmp/gh-aw/mcp-logs/
- /tmp/gh-aw/agent_usage.json
- /tmp/gh-aw/agent-stdio.log
- /tmp/gh-aw/pre-agent-audit.txt
- /tmp/gh-aw/agent/
- /tmp/gh-aw/github_rate_limits.jsonl
- /tmp/gh-aw/safeoutputs.jsonl
- /tmp/gh-aw/agent_output.json
- /tmp/gh-aw/aw-*.patch
- /tmp/gh-aw/aw-*.bundle
- /tmp/gh-aw/awf-config.json
- /tmp/gh-aw/sandbox/firewall/logs/
- /tmp/gh-aw/sandbox/firewall/audit/
- /tmp/gh-aw/sandbox/firewall/awf-reflect.json
- if-no-files-found: ignore
-
- conclusion:
- needs:
- - activation
- - agent
- - detection
- - safe_outputs
- - update_cache_memory
- if: >
- always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
- needs.activation.outputs.stale_lock_file_failed == 'true' || needs.activation.outputs.daily_ai_credits_exceeded == 'true')
- runs-on: ubuntu-slim
- permissions:
- contents: write
- issues: write
- pull-requests: write
- concurrency:
- group: "gh-aw-conclusion-cli-for-beginners-sync"
- cancel-in-progress: false
- queue: max
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
- noop_message: ${{ steps.noop.outputs.noop_message }}
- tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
- total_count: ${{ steps.missing_tool.outputs.total_count }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/cli-for-beginners-sync.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Collect usage artifact files
- if: always()
- continue-on-error: true
- run: |
- mkdir -p /tmp/gh-aw/usage/agent /tmp/gh-aw/usage/detection
- echo "Usage artifact source file status:"
- for file in /tmp/gh-aw/aw_info.json /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/agent_usage.json /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl; do
- [ -f "$file" ] && echo "FOUND: $file" || echo "MISSING: $file"
- done
- [ -f /tmp/gh-aw/aw_info.json ] && cp /tmp/gh-aw/aw_info.json /tmp/gh-aw/usage/aw_info.json || true
- [ -f /tmp/gh-aw/aw-info.jsonl ] && cp /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/usage/aw-info.jsonl || true
- [ -f /tmp/gh-aw/agent_usage.json ] && cp /tmp/gh-aw/agent_usage.json /tmp/gh-aw/usage/agent_usage.json || true
- [ -f /tmp/gh-aw/agent_usage.jsonl ] && cp /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/usage/agent_usage.jsonl || true
- [ -f /tmp/gh-aw/detection_usage.jsonl ] && cp /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/usage/detection_usage.jsonl || true
- [ -f /tmp/gh-aw/github_rate_limits.jsonl ] && cp /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/usage/github_rate_limits.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -f /tmp/gh-aw/usage/agent/token_usage.jsonl ] || : > /tmp/gh-aw/usage/agent/token_usage.jsonl
- [ -f /tmp/gh-aw/usage/detection/token_usage.jsonl ] || : > /tmp/gh-aw/usage/detection/token_usage.jsonl
- mkdir -p /tmp/gh-aw/usage/activity
- node ${{ runner.temp }}/gh-aw/actions/generate_usage_activity_summary.cjs
- find /tmp/gh-aw/usage -type f -print | sort
- - name: Upload usage artifact
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: usage
- path: |
- /tmp/gh-aw/usage/aw_info.json
- /tmp/gh-aw/usage/aw-info.jsonl
- /tmp/gh-aw/usage/agent_usage.json
- /tmp/gh-aw/usage/agent_usage.jsonl
- /tmp/gh-aw/usage/detection_usage.jsonl
- /tmp/gh-aw/usage/github_rate_limits.jsonl
- /tmp/gh-aw/usage/agent/token_usage.jsonl
- /tmp/gh-aw/usage/detection/token_usage.jsonl
- /tmp/gh-aw/usage/activity/summary.json
- if-no-files-found: ignore
- - name: Restore daily AIC usage cache
- id: restore-daily-aic-cache-conclusion
- if: always()
- continue-on-error: true
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-cliforbeginnerssync-${{ github.run_id }}
- restore-keys: agentic-workflow-usage-cliforbeginnerssync-
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Write daily AIC usage cache entry
- id: write-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- github-token: ${{ github.token }}
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/write_daily_aic_usage_cache.cjs');
- await main();
- - name: Save daily AIC usage cache
- id: save-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-cliforbeginnerssync-${{ github.run_id }}
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Upload daily AIC usage cache artifact
- id: upload-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: aic-usage-cache
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- if-no-files-found: ignore
- retention-days: 7
- - name: Process no-op messages
- id: noop
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: "1"
- GH_AW_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/cli-for-beginners-sync.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_NOOP_REPORT_AS_ISSUE: "true"
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
- GH_AW_WORKFLOW_ID: "cli-for-beginners-sync"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_noop_message.cjs');
- await main();
- - name: Log detection run
- id: detection_runs
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/cli-for-beginners-sync.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
- GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_detection_runs.cjs');
- await main();
- - name: Record missing tool
- id: missing_tool
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
- GH_AW_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/cli-for-beginners-sync.md"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/missing_tool.cjs');
- await main();
- - name: Record incomplete
- id: report_incomplete
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
- GH_AW_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/cli-for-beginners-sync.md"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/report_incomplete_handler.cjs');
- await main();
- - name: Handle agent failure
- id: handle_agent_failure
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/cli-for-beginners-sync.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_WORKFLOW_ID: "cli-for-beginners-sync"
- GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
- GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
- GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }}
- GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }}
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
- GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
- GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
- GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
- GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
- GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
- GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }}
- GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }}
- GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
- GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
- GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }}
- GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }}
- GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }}
- GH_AW_GROUP_REPORTS: "false"
- GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
- GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
- GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
- GH_AW_TIMEOUT_MINUTES: "20"
- GH_AW_CACHE_MEMORY_ENABLED: "true"
- GH_AW_CACHE_MEMORY_RESTORE_0_MATCHED_KEY: ${{ needs.agent.outputs.cache_memory_restore_0_matched_key || '' }}
- GH_AW_CACHE_MEMORY_RESTORE_0_CACHE_HIT: ${{ needs.agent.outputs.cache_memory_restore_0_cache_hit || 'false' }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_agent_failure.cjs');
- await main();
-
- detection:
- needs:
- - activation
- - agent
- if: always() && needs.agent.result != 'skipped'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- copilot-requests: write
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- aic: ${{ steps.parse_detection_token_usage.outputs.aic }}
- detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
- detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
- detection_success: ${{ steps.detection_conclusion.outputs.success }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/cli-for-beginners-sync.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Checkout repository for patch context
- if: needs.agent.outputs.has_patch == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- # --- Threat Detection ---
- - name: Clean stale firewall files from agent artifact
- run: |
- rm -rf /tmp/gh-aw/sandbox/firewall/logs
- rm -rf /tmp/gh-aw/sandbox/firewall/audit
- - name: Download container images
- run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d
- - name: Check if detection needed
- id: detection_guard
- if: always()
- env:
- OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- run: |
- if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
- echo "run_detection=true" >> "$GITHUB_OUTPUT"
- echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
- else
- echo "run_detection=false" >> "$GITHUB_OUTPUT"
- echo "Detection skipped: no agent outputs or patches to analyze"
- fi
- - name: Clear MCP Config for detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
- rm -f "$HOME/.copilot/mcp-config.json"
- rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
- - name: Prepare threat detection files
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
- rm -f /tmp/gh-aw/agent_usage.json
- cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
- if [ ! -s /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt ]; then
- echo "::warning::ERR_VALIDATION: Missing or empty detection context prompt at /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt. Ensure the agent artifact includes /tmp/gh-aw/aw-prompts/prompt.txt. Detection will continue with fallback workflow context."
- fi
- cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
- for f in /tmp/gh-aw/aw-*.patch; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- for f in /tmp/gh-aw/aw-*.bundle; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- echo "Prepared threat detection files:"
- ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- - name: Setup threat detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- WORKFLOW_NAME: "CLI for Beginners Content Sync"
- WORKFLOW_DESCRIPTION: "Weekly check for updates to github/copilot-cli-for-beginners. Opens a PR to keep the Learning Hub mirror aligned when substantive upstream course changes are detected."
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- 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/setup_threat_detection.cjs');
- await main();
- - name: Ensure threat-detection directory and log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection
- touch /tmp/gh-aw/threat-detection/detection.log
- - name: Setup Node.js
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
- with:
- node-version: '24'
- package-manager-cache: false
- - name: Install GitHub Copilot CLI
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11
- - name: Execute GitHub Copilot CLI
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- continue-on-error: true
- id: detection_agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
- trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
- mkdir -p "$HOME/.copilot"
- printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
- export XDG_CONFIG_HOME="$HOME"
- touch /tmp/gh-aw/agent-step-summary.md
- GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
- export GH_AW_NODE_BIN
- export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
- (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
- GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}"
- printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
- export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json"
- GH_AW_DOCKER_HOST=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST="${DOCKER_HOST}"
- fi
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
- _GH_AW_CHROOT_JSON=$(jq -c --arg src /tmp/gh-aw --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home /tmp/gh-aw/home '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; }
- printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- printf '%s\n' "$_GH_AW_CHROOT_JSON" > "/tmp/gh-aw/awf-config.json"
- fi
- GH_AW_TOOL_CACHE_MOUNT=""
- GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"
- if [ -d "$GH_AW_TOOL_CACHE" ]; then
- if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
- GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
- fi
- fi
- # shellcheck disable=SC1003,SC2086
- sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
- -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
- env:
- AWF_REFLECT_ENABLED: 1
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
- COPILOT_GITHUB_TOKEN: ${{ github.token }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_LLM_PROVIDER: github
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}
- GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
- GH_AW_PHASE: detection
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_TIMEOUT_MINUTES: 20
- GH_AW_VERSION: v0.81.6
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- RUNNER_TEMP: ${{ runner.temp }}
- S2STOKENS: true
- TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- - name: Parse threat detection token usage for step summary
- id: parse_detection_token_usage
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage
- 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/parse_token_usage.cjs');
- await main();
- - name: Upload threat detection log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: detection
- path: /tmp/gh-aw/threat-detection/detection.log
- if-no-files-found: ignore
- - name: Parse and conclude threat detection
- id: detection_conclusion
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
- DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
- GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
- with:
- script: |
- try {
- 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/parse_threat_detection_results.cjs');
- await main();
- } catch (loadErr) {
- const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
- const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
- const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
- core.error(msg);
- core.setOutput('reason', 'parse_error');
- if (continueOnError && !detectionExecutionFailed) {
- core.warning('\u26A0\uFE0F ' + msg);
- core.setOutput('conclusion', 'warning');
- core.setOutput('success', 'false');
- } else {
- core.setOutput('conclusion', 'failure');
- core.setOutput('success', 'false');
- core.setFailed(msg);
- }
- }
-
- safe_outputs:
- needs:
- - activation
- - agent
- - detection
- if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
- runs-on: ubuntu-slim
- permissions:
- contents: write
- issues: write
- pull-requests: write
- timeout-minutes: 45
- env:
- GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
- GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/cli-for-beginners-sync"
- GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
- GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
- GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
- GH_AW_ENGINE_VERSION: "1.0.65"
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_WORKFLOW_ID: "cli-for-beginners-sync"
- GH_AW_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/cli-for-beginners-sync.md"
- outputs:
- code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
- code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
- create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
- create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_pr_number: ${{ steps.process_safe_outputs.outputs.created_pr_number }}
- created_pr_url: ${{ steps.process_safe_outputs.outputs.created_pr_url }}
- process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
- process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/cli-for-beginners-sync.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Download patch artifact
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Checkout repository
- if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request')
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: true
- token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- - name: Configure Git credentials
- if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request')
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Configure GH_HOST for enterprise compatibility
- id: ghes-host-config
- shell: bash
- run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
- # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
- # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
- GH_HOST="${GITHUB_SERVER_URL#https://}"
- GH_HOST="${GH_HOST#http://}"
- echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- - name: Process Safe Outputs
- id: process_safe_outputs
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"base_branch\":\"main\",\"labels\":[\"automated-update\",\"learning-hub\",\"cli-for-beginners\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"[bot] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
- GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/safe_output_handler_manager.cjs');
- await main();
- - name: Upload Safe Outputs Items
- if: always()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: safe-outputs-items
- path: |
- /tmp/gh-aw/safe-output-items.jsonl
- /tmp/gh-aw/temporary-id-map.json
- if-no-files-found: ignore
-
- update_cache_memory:
- needs:
- - activation
- - agent
- - detection
- if: always() && needs.detection.result == 'success' && needs.agent.result == 'success'
- runs-on: ubuntu-slim
- permissions: {}
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_WORKFLOW_ID_SANITIZED: cliforbeginnerssync
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "CLI for Beginners Content Sync"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/cli-for-beginners-sync.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download cache-memory artifact (default)
- id: download_cache_default
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- continue-on-error: true
- with:
- name: cache-memory
- path: /tmp/gh-aw/cache-memory
- - name: Check if cache-memory folder has content (default)
- id: check_cache_default
- shell: bash
- run: |
- if [ -d "/tmp/gh-aw/cache-memory" ] && [ "$(ls -A /tmp/gh-aw/cache-memory 2>/dev/null)" ]; then
- echo "has_content=true" >> "$GITHUB_OUTPUT"
- else
- echo "has_content=false" >> "$GITHUB_OUTPUT"
- fi
- - name: Save cache-memory to cache (default)
- if: steps.check_cache_default.outputs.has_content == 'true'
- uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
- path: /tmp/gh-aw/cache-memory
-
diff --git a/.github/workflows/cli-for-beginners-sync.md b/.github/workflows/cli-for-beginners-sync.md
deleted file mode 100644
index e41768fcb2..0000000000
--- a/.github/workflows/cli-for-beginners-sync.md
+++ /dev/null
@@ -1,139 +0,0 @@
----
-name: "CLI for Beginners Content Sync"
-description: "Weekly check for updates to github/copilot-cli-for-beginners. Opens a PR to keep the Learning Hub mirror aligned when substantive upstream course changes are detected."
-on:
- schedule: weekly
-permissions:
- contents: read
- copilot-requests: write
-tools:
- github:
- toolsets: [repos]
- cache-memory: true
-safe-outputs:
- create-pull-request:
- labels: [automated-update, learning-hub, cli-for-beginners]
- title-prefix: "[bot] "
- base-branch: main
----
-
-# CLI for Beginners Content Sync
-
-You are a documentation sync agent for the **awesome-copilot** Learning Hub. Your job is to check whether the upstream source repository [`github/copilot-cli-for-beginners`](https://github.com/github/copilot-cli-for-beginners) has received any meaningful updates since your last run, and — if it has — update the Learning Hub mirror so it stays aligned with the upstream course.
-
-## Step 1 — Determine what's new in the upstream repo
-
-1. Read `cache-memory` and look for a file named `cli-for-beginners-sync-state.json`. It may contain:
- - `last_synced_sha` — the most recent commit SHA you processed on your previous run
- - `last_synced_at` — a filesystem-safe timestamp in the format `YYYY-MM-DD-HH-MM-SS`
-
-2. Use GitHub tools to fetch recent commits from `github/copilot-cli-for-beginners` (default branch):
- - If `last_synced_sha` exists, list commits **since that SHA** (stop once you reach it).
- - If no cached state exists, list commits from the **past 7 days**.
-
-3. Identify which files changed across those commits. Focus on:
- - Markdown files (`*.md`) — course content, README, module descriptions
- - Supporting assets referenced by the course material, especially screenshots and GIFs
- - Any configuration or metadata files that materially affect the course content or navigation
-
-4. If **no commits** were found since the last sync, stop here and call the `noop` safe output with a message like: "No new commits found in `github/copilot-cli-for-beginners` since last sync (``). No action needed." Then update the cache with the latest SHA.
-
-## Step 2 — Read the changed upstream content
-
-For each file that changed in the upstream repo, use GitHub tools to fetch the **current file contents** from `github/copilot-cli-for-beginners`. Pay close attention to:
-
-- New sections, commands, flags, or concepts introduced
-- Renamed or restructured sections
-- Deprecated commands or workflows that have been removed
-- Updated screenshots, GIFs, image references, or code examples
-- Links to new official documentation or resources
-
-## Step 3 — Compare against the local Learning Hub content
-
-Read the local files in the canonical Learning Hub course folder `website/src/content/docs/learning-hub/cli-for-beginners/`:
-
-```
-website/src/content/docs/learning-hub/cli-for-beginners/
-├── index.md
-├── 00-quick-start.md
-├── 01-setup-and-first-steps.md
-├── 02-context-and-conversations.md
-├── 03-development-workflows.md
-├── 04-agents-and-custom-instructions.md
-├── 05-skills.md
-├── 06-mcp-servers.md
-└── 07-putting-it-all-together.md
-```
-
-Also inspect local course assets in `website/public/images/learning-hub/copilot-cli-for-beginners/` when upstream changes touch screenshots, banners, or GIFs.
-
-If the upstream changes alter course structure or navigation, you may also need to inspect:
-
-- `website/astro.config.mjs`
-- `website/src/content/docs/learning-hub/index.md`
-
-Map the upstream changes to the relevant local file(s). Ask yourself:
-
-- Is the local mirror missing any upstream content, structure, assignments, examples, or visuals?
-- Is any existing Learning Hub content now outdated or incorrect based on upstream changes?
-- Do local route rewrites, repo-link rewrites, or asset paths need updating so the mirrored pages still work on the website?
-- Do the Astro frontmatter fields (especially `lastUpdated`) need updating because the mirrored page changed?
-
-If the local content is already fully consistent with the upstream changes — or the upstream changes are non-substantive (e.g., only CI config, typo fixes, or internal tooling changes) — stop here and call the `noop` safe output with a brief explanation. Still update the cache with the latest commit SHA.
-
-## Step 4 — Update the Learning Hub files
-
-For each local file that needs updating:
-
-1. Edit the relevant local docs, assets, and supporting navigation files so the website remains a **source-faithful mirror** of the upstream course:
- - Add or update missing concepts, commands, flags, steps, assignments, demos, and visuals
- - Correct or remove outdated information
- - Localize newly added screenshots or GIFs into `website/public/images/learning-hub/copilot-cli-for-beginners/`
- - Bump the `lastUpdated` frontmatter field to today's date (`YYYY-MM-DD`) for any page whose mirrored content changed
-
-2. Keep a **mirror-first** approach:
- - Preserve upstream wording, headings, section order, assignments, and overall chapter flow as closely as practical
- - Do not summarize, reinterpret, or "website-optimize" the course into a different learning experience
- - Only adapt what the website requires: Astro frontmatter, route-safe internal links, GitHub repo links, local asset paths, and minor HTML/CSS hooks needed for presentation
- - Convert repo-root relative links that are invalid on the published website (for example `../.github/agents/`, `./.github/...`, or `.github/...`) into absolute links to `https://github.com/github/copilot-cli-for-beginners` (use `/tree/main/...` for directories and `/blob/main/...` for files)
-
-3. If upstream adds, removes, or renames major sections or chapters:
- - Create, delete, or rename the corresponding markdown files in `website/src/content/docs/learning-hub/cli-for-beginners/`
- - Update `website/astro.config.mjs` if the sidebar chapter list must change
- - Update `website/src/content/docs/learning-hub/index.md` only if the landing page's course entry must change
-
-## Step 5 — Update the sync state cache
-
-Before opening the PR, write an updated `cli-for-beginners-sync-state.json` to `cache-memory` with:
-
-```json
-{
- "last_synced_sha": "",
- "last_synced_at": "",
- "files_reviewed": [""],
- "files_updated": [""]
-}
-```
-
-## Step 6 — Open a pull request
-
-Create a pull request with your changes using the `create-pull-request` safe output. Use `main` as the base branch for all work related to this workflow. The PR body must include:
-
-1. **What changed upstream** — a concise summary of the commits and file changes found in `github/copilot-cli-for-beginners`
-2. **What was updated locally** — list each mirrored Learning Hub file or asset you edited and what changed
-3. **Source links** — links to the relevant upstream commits or files
-4. A note that the markdown body of this workflow can be edited directly on GitHub.com without recompilation
-
-If there is nothing to change after your analysis, do **not** open a PR. Instead, call the `noop` safe output.
-
-## Guidelines
-
-- The canonical course content lives in `website/src/content/docs/learning-hub/cli-for-beginners/`; do not recreate legacy duplicates elsewhere
-- Prefer changes within the course docs and `website/public/images/learning-hub/copilot-cli-for-beginners/`
-- Only edit `website/astro.config.mjs` or `website/src/content/docs/learning-hub/index.md` when upstream course structure or navigation truly requires it
-- Preserve existing frontmatter fields; only update `lastUpdated` and `description` if genuinely warranted
-- Keep the course source-faithful; avoid summaries or interpretive rewrites
-- Use `main` as the base branch for any branch or PR created by this workflow
-- Do not auto-merge; the PR is for human review
-- If you are uncertain whether an upstream change warrants a Learning Hub update, err on the side of creating the PR — a human reviewer can always decline
-- Always call either `create-pull-request` or `noop` at the end of your run so the workflow clearly signals its outcome
diff --git a/.github/workflows/duplicate-resource-detector.lock.yml b/.github/workflows/duplicate-resource-detector.lock.yml
deleted file mode 100644
index ef24ac58e7..0000000000
--- a/.github/workflows/duplicate-resource-detector.lock.yml
+++ /dev/null
@@ -1,1567 +0,0 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a9f23f276f50c5d0ec075a2f482d2ef43d6cb7b3c7042624a461017cb9aeadff","body_hash":"7c3fdf5f640c39bf141ff08d56cd55682af71d86bf98d280b2f2483bdb71ce9b","compiler_version":"v0.81.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65"}}
-# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","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":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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"},{"repo":"github/gh-aw-actions/setup","sha":"ba6380cc6e5be5d21677bebe04d52fb48e3abec7","version":"v0.81.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]}
-# This file was automatically generated by gh-aw (v0.81.6). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
-#
-# ___ _ _
-# / _ \ | | (_)
-# | |_| | __ _ ___ _ __ | |_ _ ___
-# | _ |/ _` |/ _ \ '_ \| __| |/ __|
-# | | | | (_| | __/ | | | |_| | (__
-# \_| |_/\__, |\___|_| |_|\__|_|\___|
-# __/ |
-# _ _ |___/
-# | | | | / _| |
-# | | | | ___ _ __ _ __| |_| | _____ ____
-# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
-# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
-# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
-#
-#
-# To update this file, edit the corresponding .md file and run:
-# gh aw compile
-# Not all edits will cause changes to this file.
-#
-# For more information: https://github.github.com/gh-aw/introduction/overview/
-#
-# Weekly scan of agents, instructions, and skills to identify potential duplicate resources and report them for review
-#
-# Secrets used:
-# - GH_AW_GITHUB_MCP_SERVER_TOKEN
-# - GH_AW_GITHUB_TOKEN
-# - GITHUB_TOKEN
-#
-# Custom actions used:
-# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
-# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
-# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
-# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
-# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
-# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
-# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
-# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
-# - github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
-#
-# Container images used:
-# - ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7
-# - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d
-# - ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d
-# - ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be
-# - ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b
-# - ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036
-
-name: "Duplicate Resource Detector"
-on:
- schedule:
- - cron: "50 21 * * 4"
- # Friendly format: weekly (scattered)
- workflow_dispatch:
- inputs:
- aw_context:
- default: ""
- description: "Agent caller context (used internally by Agentic Workflows)."
- required: false
- type: string
-
-permissions: {}
-
-concurrency:
- group: "gh-aw-${{ github.workflow }}"
-
-run-name: "Duplicate Resource Detector"
-
-jobs:
- activation:
- runs-on: ubuntu-slim
- permissions:
- actions: read
- contents: read
- env:
- GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- comment_id: ""
- comment_repo: ""
- daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }}
- daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }}
- daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }}
- engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
- lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
- model: ${{ steps.generate_aw_info.outputs.model }}
- setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
- setup-span-id: ${{ steps.setup.outputs.span-id }}
- setup-trace-id: ${{ steps.setup.outputs.trace-id }}
- stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/duplicate-resource-detector.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Generate agentic run info
- id: generate_aw_info
- env:
- GH_AW_INFO_ENGINE_ID: "copilot"
- GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
- GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AGENT_VERSION: "1.0.65"
- GH_AW_INFO_CLI_VERSION: "v0.81.6"
- GH_AW_INFO_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_INFO_EXPERIMENTAL: "false"
- GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
- GH_AW_INFO_STAGED: "false"
- GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
- GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_AWMG_VERSION: ""
- GH_AW_INFO_FIREWALL_TYPE: "squid"
- GH_AW_COMPILED_STRICT: "true"
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/generate_aw_info.cjs');
- await main(core, context);
- - name: Restore daily AIC usage cache
- id: restore-daily-aic-cache
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- continue-on-error: true
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-duplicateresourcedetector-${{ github.run_id }}
- restore-keys: agentic-workflow-usage-duplicateresourcedetector-
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Restore daily AIC usage cache (artifact fallback)
- id: restore-daily-aic-cache-fallback
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }}
- GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- 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/restore_aic_usage_cache_fallback.cjs');
- await main();
- - name: Check daily workflow token guardrail
- id: daily-effective-workflow-guardrail
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_WORKFLOW_ID: "duplicate-resource-detector"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }}
- GH_AW_HAS_SLASH_COMMAND: "false"
- GH_AW_HAS_LABEL_COMMAND: "false"
- GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- 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/check_daily_aic_workflow_guardrail.cjs');
- await main();
- - name: Checkout .github and .agents folders
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- sparse-checkout: |
- .github
- .agents
- .antigravity
- .claude
- .codex
- .crush
- .gemini
- .opencode
- .pi
- sparse-checkout-cone-mode: true
- fetch-depth: 1
- - name: Save agent config folders for base branch restoration
- env:
- GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
- GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
- - name: Check workflow lock file
- id: check-lock-file
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_WORKFLOW_FILE: "duplicate-resource-detector.lock.yml"
- GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
- 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/check_workflow_timestamp_api.cjs');
- await main();
- - name: Check compile-agentic version
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_COMPILED_VERSION: "v0.81.6"
- 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/check_version_updates.cjs');
- await main();
- - name: Log runtime features
- if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh"
- - name: Create prompt with built-in context
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- # poutine:ignore untrusted_checkout_exec
- run: |
- bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
- {
- cat << 'GH_AW_PROMPT_af09ff5961555d5c_EOF'
-
- GH_AW_PROMPT_af09ff5961555d5c_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_af09ff5961555d5c_EOF'
-
- Tools: create_issue, missing_tool, missing_data, noop
-
- GH_AW_PROMPT_af09ff5961555d5c_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_af09ff5961555d5c_EOF'
-
- The following GitHub context information is available for this workflow:
- {{#if github.actor}}
- - **actor**: __GH_AW_GITHUB_ACTOR__
- {{/if}}
- {{#if github.repository}}
- - **repository**: __GH_AW_GITHUB_REPOSITORY__
- {{/if}}
- {{#if github.workspace}}
- - **workspace**: __GH_AW_GITHUB_WORKSPACE__
- {{/if}}
- {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
- - **issue-number**: #__GH_AW_EXPR_802A9F6A__
- {{/if}}
- {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
- - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
- {{/if}}
- {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
- - **pull-request-number**: #__GH_AW_EXPR_463A214A__
- {{/if}}
- {{#if github.event.comment.id || github.aw.context.comment_id}}
- - **comment-id**: __GH_AW_EXPR_FF1D34CE__
- {{/if}}
- {{#if github.run_id}}
- - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
- {{/if}}
-
-
- GH_AW_PROMPT_af09ff5961555d5c_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_af09ff5961555d5c_EOF'
-
- {{#runtime-import .github/workflows/duplicate-resource-detector.md}}
- GH_AW_PROMPT_af09ff5961555d5c_EOF
- } > "$GH_AW_PROMPT"
- - name: Interpolate variables and render templates
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_ENGINE_ID: "copilot"
- 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/interpolate_prompt.cjs');
- await main();
- - name: Substitute placeholders
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
- with:
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io, getOctokit);
-
- const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
-
- // Call the substitution function
- return await substitutePlaceholders({
- file: process.env.GH_AW_PROMPT,
- substitutions: {
- GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
- GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
- GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
- GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
- GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
- GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
- GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
- GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
- }
- });
- - name: Validate prompt placeholders
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
- - name: Print prompt
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
- - name: Upload activation artifact
- if: success()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: activation
- include-hidden-files: true
- path: |
- /tmp/gh-aw/aw_info.json
- /tmp/gh-aw/models.json
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/aw-prompts/prompt-template.txt
- /tmp/gh-aw/aw-prompts/prompt-import-tree.json
- /tmp/gh-aw/github_rate_limits.jsonl
- /tmp/gh-aw/base
- /tmp/gh-aw/.github/agents
- /tmp/gh-aw/.github/skills
- if-no-files-found: ignore
- retention-days: 1
-
- agent:
- needs: activation
- if: needs.activation.outputs.daily_ai_credits_exceeded != 'true'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- copilot-requests: write
- issues: read
- concurrency:
- group: "gh-aw-copilot-${{ github.workflow }}"
- queue: max
- env:
- DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
- GH_AW_ASSETS_ALLOWED_EXTS: ""
- GH_AW_ASSETS_BRANCH: ""
- GH_AW_ASSETS_MAX_SIZE_KB: 0
- GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_WORKFLOW_ID_SANITIZED: duplicateresourcedetector
- outputs:
- agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
- ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }}
- aic: ${{ steps.parse-mcp-gateway.outputs.aic }}
- ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }}
- checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
- effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
- has_patch: ${{ steps.collect_output.outputs.has_patch }}
- inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
- mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
- model: ${{ needs.activation.outputs.model }}
- model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
- output: ${{ steps.collect_output.outputs.output }}
- output_types: ${{ steps.collect_output.outputs.output_types }}
- setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
- setup-span-id: ${{ steps.setup.outputs.span-id }}
- setup-trace-id: ${{ steps.setup.outputs.trace-id }}
- unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/duplicate-resource-detector.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Set runtime paths
- id: set-runtime-paths
- run: |
- {
- echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
- echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
- echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
- } >> "$GITHUB_OUTPUT"
- - name: Checkout repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- - name: Create gh-aw temp directory
- run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
- - name: Configure gh CLI for GitHub Enterprise
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
- env:
- GH_TOKEN: ${{ github.token }}
- - name: Configure Git credentials
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_TOKEN: ${{ github.token }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Checkout PR branch
- id: checkout-pr
- if: |
- github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request'
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io, getOctokit);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
- await main();
- - name: Install GitHub Copilot CLI
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11
- - name: Determine automatic lockdown mode for GitHub MCP Server
- id: determine-automatic-lockdown
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
- env:
- GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- with:
- script: |
- const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
- await determineAutomaticLockdown(github, context, core);
- - name: Download activation artifact
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: activation
- path: /tmp/gh-aw
- - name: Restore agent config folders from base branch
- if: steps.checkout-pr.outcome == 'success'
- env:
- GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
- GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
- - name: Restore inline sub-agents from activation artifact
- env:
- GH_AW_SUB_AGENT_DIR: ".github/agents"
- GH_AW_SUB_AGENT_EXT: ".agent.md"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
- - name: Restore inline skills from activation artifact
- env:
- GH_AW_SKILL_DIR: ".github/skills"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
- - name: Download container images
- run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036
- - name: Generate Safe Outputs Config
- run: |
- 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_c7960b6289755389_EOF'
- {"create_issue":{"close_older_issues":true,"labels":["duplicate-review"],"max":1},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_c7960b6289755389_EOF
- - name: Generate Safe Outputs Tools
- env:
- GH_AW_TOOLS_META_JSON: |
- {
- "description_suffixes": {
- "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Labels [\"duplicate-review\"] will be automatically added."
- },
- "repo_params": {},
- "dynamic_tools": []
- }
- GH_AW_VALIDATION_JSON: |
- {
- "create_issue": {
- "defaultMax": 1,
- "fields": {
- "body": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000,
- "minLength": 20
- },
- "fields": {
- "type": "array"
- },
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
- },
- "parent": {
- "issueOrPRNumber": true
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- },
- "temporary_id": {
- "type": "string"
- },
- "title": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "missing_data": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "context": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "data_type": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "reason": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- }
- }
- },
- "missing_tool": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 512
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "tool": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "noop": {
- "defaultMax": 1,
- "fields": {
- "message": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- }
- }
- },
- "report_incomplete": {
- "defaultMax": 5,
- "fields": {
- "details": {
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 1024
- }
- }
- }
- }
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/generate_safe_outputs_tools.cjs');
- await main();
- - name: Start MCP Gateway
- id: start-mcp-gateway
- env:
- GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }}
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }}
- GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
- GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- set -eo pipefail
- mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
-
- # Export gateway environment variables for MCP config and gateway script
- export MCP_GATEWAY_PORT="8080"
- export MCP_GATEWAY_DOMAIN="host.docker.internal"
- export MCP_GATEWAY_HOST_DOMAIN="localhost"
- MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
- echo "::add-mask::${MCP_GATEWAY_API_KEY}"
- export MCP_GATEWAY_API_KEY
- export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
- mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
- export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
- export DEBUG="*"
-
- export GH_AW_ENGINE="copilot"
- MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
- MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
- case "${DOCKER_HOST:-}" in
- unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
- /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
- * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
- esac
- 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 --name awmg-mcpg --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_POLICY_ALLOW_CREATE_PULL_REQUEST -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 RUNNER_TEMP -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 -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30'
-
- mkdir -p "$HOME/.copilot"
- GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_292ec16ff524dce0_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
- {
- "mcpServers": {
- "github": {
- "type": "stdio",
- "container": "ghcr.io/github/github-mcp-server:v1.4.0",
- "env": {
- "GITHUB_HOST": "${GITHUB_SERVER_URL}",
- "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}",
- "GITHUB_READ_ONLY": "1",
- "GITHUB_TOOLSETS": "repos,issues"
- },
- "guard-policies": {
- "allow-only": {
- "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
- "repos": "$GITHUB_MCP_GUARD_REPOS"
- }
- }
- },
- "safeoutputs": {
- "type": "stdio",
- "container": "ghcr.io/github/gh-aw-node",
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"],
- "args": ["-w", "\${GITHUB_WORKSPACE}"],
- "entrypoint": "sh",
- "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"],
- "env": {
- "DEBUG": "*",
- "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}",
- "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}",
- "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}",
- "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}",
- "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}",
- "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}",
- "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}",
- "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}",
- "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}",
- "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}",
- "GITHUB_TOKEN": "\${GITHUB_TOKEN}",
- "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}",
- "RUNNER_TEMP": "\${RUNNER_TEMP}"
- },
- "guard-policies": {
- "write-sink": {
- "accept": [
- "*"
- ]
- }
- }
- }
- },
- "gateway": {
- "port": $MCP_GATEWAY_PORT,
- "domain": "${MCP_GATEWAY_DOMAIN}",
- "apiKey": "${MCP_GATEWAY_API_KEY}",
- "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
- }
- }
- GH_AW_MCP_CONFIG_292ec16ff524dce0_EOF
- - name: Mount MCP servers as CLIs
- id: mount-mcp-clis
- continue-on-error: true
- env:
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
- await main();
- - name: Clean credentials
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
- - name: Audit pre-agent workspace
- id: pre_agent_audit
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
- - name: Execute GitHub Copilot CLI
- id: agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
- trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
- mkdir -p "$HOME/.copilot"
- printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
- export XDG_CONFIG_HOME="$HOME"
- export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json"
- touch /tmp/gh-aw/agent-step-summary.md
- GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
- export GH_AW_NODE_BIN
- export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
- (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
- GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}"
- printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"github.com\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"telemetry.enterprise.githubcopilot.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
- export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json"
- GH_AW_DOCKER_HOST=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST="${DOCKER_HOST}"
- fi
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
- GH_AW_CHROOT_BINARIES_SOURCE_PATH=/tmp/gh-aw GH_AW_CHROOT_IDENTITY_HOME=/tmp/gh-aw/home node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs"
- fi
- GH_AW_TOOL_CACHE_MOUNT=""
- GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"
- if [ -d "$GH_AW_TOOL_CACHE" ]; then
- if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
- GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
- fi
- fi
- # shellcheck disable=SC1003,SC2086
- sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
- -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
- env:
- AWF_REFLECT_ENABLED: 1
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
- COPILOT_GITHUB_TOKEN: ${{ github.token }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_LLM_PROVIDER: github
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
- GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
- GH_AW_PHASE: agent
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_TIMEOUT_MINUTES: 20
- GH_AW_VERSION: v0.81.6
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- RUNNER_TEMP: ${{ runner.temp }}
- S2STOKENS: true
- TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- - name: Detect agent errors
- if: always()
- id: detect-agent-errors
- continue-on-error: true
- run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
- - name: Configure Git credentials
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_TOKEN: ${{ github.token }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Copy Copilot session state files to logs
- if: always()
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
- - name: Stop MCP Gateway
- if: always()
- continue-on-error: true
- env:
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
- run: |
- bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- - name: Redact secrets in logs
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/redact_secrets.cjs');
- await main();
- env:
- GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
- SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Append agent step summary
- if: always()
- run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
- - name: Copy Safe Outputs
- if: always()
- env:
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- run: |
- mkdir -p /tmp/gh-aw
- cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
- - name: Ingest agent output
- id: collect_output
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- 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/collect_ndjson_output.cjs');
- await main();
- - name: Parse agent logs for step summary
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
- 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/parse_copilot_log.cjs');
- await main();
- - name: Parse MCP Gateway logs for step summary
- if: always()
- id: parse-mcp-gateway
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/parse_mcp_gateway_log.cjs');
- await main();
- - name: Print firewall logs
- if: always()
- continue-on-error: true
- env:
- AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
- run: |
- # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts
- # AWF runs with sudo, creating files owned by root
- sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
- # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
- if command -v awf &> /dev/null; then
- awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
- else
- echo 'AWF binary not installed, skipping firewall log summary'
- fi
- - name: Parse token usage for step summary
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/parse_token_usage.cjs');
- await main();
- - name: Print AWF reflect summary
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/awf_reflect_summary.cjs');
- await main();
- - name: Write agent output placeholder if missing
- if: always()
- run: |
- if [ ! -f /tmp/gh-aw/agent_output.json ]; then
- echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
- fi
- - name: Upload agent artifacts
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: agent
- path: |
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/sandbox/agent/logs/
- /tmp/gh-aw/redacted-urls.log
- /tmp/gh-aw/mcp-logs/
- /tmp/gh-aw/agent_usage.json
- /tmp/gh-aw/agent-stdio.log
- /tmp/gh-aw/pre-agent-audit.txt
- /tmp/gh-aw/agent/
- /tmp/gh-aw/github_rate_limits.jsonl
- /tmp/gh-aw/safeoutputs.jsonl
- /tmp/gh-aw/agent_output.json
- /tmp/gh-aw/aw-*.patch
- /tmp/gh-aw/aw-*.bundle
- /tmp/gh-aw/awf-config.json
- /tmp/gh-aw/sandbox/firewall/logs/
- /tmp/gh-aw/sandbox/firewall/audit/
- /tmp/gh-aw/sandbox/firewall/awf-reflect.json
- if-no-files-found: ignore
-
- conclusion:
- needs:
- - activation
- - agent
- - detection
- - safe_outputs
- if: >
- always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
- needs.activation.outputs.stale_lock_file_failed == 'true' || needs.activation.outputs.daily_ai_credits_exceeded == 'true')
- runs-on: ubuntu-slim
- permissions:
- contents: read
- issues: write
- concurrency:
- group: "gh-aw-conclusion-duplicate-resource-detector"
- cancel-in-progress: false
- queue: max
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
- noop_message: ${{ steps.noop.outputs.noop_message }}
- tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
- total_count: ${{ steps.missing_tool.outputs.total_count }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/duplicate-resource-detector.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Collect usage artifact files
- if: always()
- continue-on-error: true
- run: |
- mkdir -p /tmp/gh-aw/usage/agent /tmp/gh-aw/usage/detection
- echo "Usage artifact source file status:"
- for file in /tmp/gh-aw/aw_info.json /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/agent_usage.json /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl; do
- [ -f "$file" ] && echo "FOUND: $file" || echo "MISSING: $file"
- done
- [ -f /tmp/gh-aw/aw_info.json ] && cp /tmp/gh-aw/aw_info.json /tmp/gh-aw/usage/aw_info.json || true
- [ -f /tmp/gh-aw/aw-info.jsonl ] && cp /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/usage/aw-info.jsonl || true
- [ -f /tmp/gh-aw/agent_usage.json ] && cp /tmp/gh-aw/agent_usage.json /tmp/gh-aw/usage/agent_usage.json || true
- [ -f /tmp/gh-aw/agent_usage.jsonl ] && cp /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/usage/agent_usage.jsonl || true
- [ -f /tmp/gh-aw/detection_usage.jsonl ] && cp /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/usage/detection_usage.jsonl || true
- [ -f /tmp/gh-aw/github_rate_limits.jsonl ] && cp /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/usage/github_rate_limits.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -f /tmp/gh-aw/usage/agent/token_usage.jsonl ] || : > /tmp/gh-aw/usage/agent/token_usage.jsonl
- [ -f /tmp/gh-aw/usage/detection/token_usage.jsonl ] || : > /tmp/gh-aw/usage/detection/token_usage.jsonl
- mkdir -p /tmp/gh-aw/usage/activity
- node ${{ runner.temp }}/gh-aw/actions/generate_usage_activity_summary.cjs
- find /tmp/gh-aw/usage -type f -print | sort
- - name: Upload usage artifact
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: usage
- path: |
- /tmp/gh-aw/usage/aw_info.json
- /tmp/gh-aw/usage/aw-info.jsonl
- /tmp/gh-aw/usage/agent_usage.json
- /tmp/gh-aw/usage/agent_usage.jsonl
- /tmp/gh-aw/usage/detection_usage.jsonl
- /tmp/gh-aw/usage/github_rate_limits.jsonl
- /tmp/gh-aw/usage/agent/token_usage.jsonl
- /tmp/gh-aw/usage/detection/token_usage.jsonl
- /tmp/gh-aw/usage/activity/summary.json
- if-no-files-found: ignore
- - name: Restore daily AIC usage cache
- id: restore-daily-aic-cache-conclusion
- if: always()
- continue-on-error: true
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-duplicateresourcedetector-${{ github.run_id }}
- restore-keys: agentic-workflow-usage-duplicateresourcedetector-
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Write daily AIC usage cache entry
- id: write-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- github-token: ${{ github.token }}
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/write_daily_aic_usage_cache.cjs');
- await main();
- - name: Save daily AIC usage cache
- id: save-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-duplicateresourcedetector-${{ github.run_id }}
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Upload daily AIC usage cache artifact
- id: upload-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: aic-usage-cache
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- if-no-files-found: ignore
- retention-days: 7
- - name: Process no-op messages
- id: noop
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: "1"
- GH_AW_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/duplicate-resource-detector.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_NOOP_REPORT_AS_ISSUE: "true"
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
- GH_AW_WORKFLOW_ID: "duplicate-resource-detector"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_noop_message.cjs');
- await main();
- - name: Log detection run
- id: detection_runs
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/duplicate-resource-detector.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
- GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_detection_runs.cjs');
- await main();
- - name: Record missing tool
- id: missing_tool
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
- GH_AW_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/duplicate-resource-detector.md"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/missing_tool.cjs');
- await main();
- - name: Record incomplete
- id: report_incomplete
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
- GH_AW_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/duplicate-resource-detector.md"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/report_incomplete_handler.cjs');
- await main();
- - name: Handle agent failure
- id: handle_agent_failure
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/duplicate-resource-detector.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_WORKFLOW_ID: "duplicate-resource-detector"
- GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
- GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
- GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }}
- GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }}
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
- GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
- GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
- GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
- GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
- GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
- GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
- GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
- GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }}
- GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }}
- GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }}
- GH_AW_GROUP_REPORTS: "false"
- GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
- GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
- GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
- GH_AW_TIMEOUT_MINUTES: "20"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_agent_failure.cjs');
- await main();
-
- detection:
- needs:
- - activation
- - agent
- if: always() && needs.agent.result != 'skipped'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- copilot-requests: write
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- aic: ${{ steps.parse_detection_token_usage.outputs.aic }}
- detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
- detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
- detection_success: ${{ steps.detection_conclusion.outputs.success }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/duplicate-resource-detector.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Checkout repository for patch context
- if: needs.agent.outputs.has_patch == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- # --- Threat Detection ---
- - name: Clean stale firewall files from agent artifact
- run: |
- rm -rf /tmp/gh-aw/sandbox/firewall/logs
- rm -rf /tmp/gh-aw/sandbox/firewall/audit
- - name: Download container images
- run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d
- - name: Check if detection needed
- id: detection_guard
- if: always()
- env:
- OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- run: |
- if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
- echo "run_detection=true" >> "$GITHUB_OUTPUT"
- echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
- else
- echo "run_detection=false" >> "$GITHUB_OUTPUT"
- echo "Detection skipped: no agent outputs or patches to analyze"
- fi
- - name: Clear MCP Config for detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
- rm -f "$HOME/.copilot/mcp-config.json"
- rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
- - name: Prepare threat detection files
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
- rm -f /tmp/gh-aw/agent_usage.json
- cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
- if [ ! -s /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt ]; then
- echo "::warning::ERR_VALIDATION: Missing or empty detection context prompt at /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt. Ensure the agent artifact includes /tmp/gh-aw/aw-prompts/prompt.txt. Detection will continue with fallback workflow context."
- fi
- cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
- for f in /tmp/gh-aw/aw-*.patch; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- for f in /tmp/gh-aw/aw-*.bundle; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- echo "Prepared threat detection files:"
- ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- - name: Setup threat detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- WORKFLOW_NAME: "Duplicate Resource Detector"
- WORKFLOW_DESCRIPTION: "Weekly scan of agents, instructions, and skills to identify potential duplicate resources and report them for review"
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- 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/setup_threat_detection.cjs');
- await main();
- - name: Ensure threat-detection directory and log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection
- touch /tmp/gh-aw/threat-detection/detection.log
- - name: Setup Node.js
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
- with:
- node-version: '24'
- package-manager-cache: false
- - name: Install GitHub Copilot CLI
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11
- - name: Execute GitHub Copilot CLI
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- continue-on-error: true
- id: detection_agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
- trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
- mkdir -p "$HOME/.copilot"
- printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
- export XDG_CONFIG_HOME="$HOME"
- touch /tmp/gh-aw/agent-step-summary.md
- GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
- export GH_AW_NODE_BIN
- export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
- (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
- GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}"
- printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
- export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json"
- GH_AW_DOCKER_HOST=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST="${DOCKER_HOST}"
- fi
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
- _GH_AW_CHROOT_JSON=$(jq -c --arg src /tmp/gh-aw --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home /tmp/gh-aw/home '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; }
- printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- printf '%s\n' "$_GH_AW_CHROOT_JSON" > "/tmp/gh-aw/awf-config.json"
- fi
- GH_AW_TOOL_CACHE_MOUNT=""
- GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"
- if [ -d "$GH_AW_TOOL_CACHE" ]; then
- if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
- GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
- fi
- fi
- # shellcheck disable=SC1003,SC2086
- sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
- -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
- env:
- AWF_REFLECT_ENABLED: 1
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
- COPILOT_GITHUB_TOKEN: ${{ github.token }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_LLM_PROVIDER: github
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}
- GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
- GH_AW_PHASE: detection
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_TIMEOUT_MINUTES: 20
- GH_AW_VERSION: v0.81.6
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- RUNNER_TEMP: ${{ runner.temp }}
- S2STOKENS: true
- TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- - name: Parse threat detection token usage for step summary
- id: parse_detection_token_usage
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage
- 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/parse_token_usage.cjs');
- await main();
- - name: Upload threat detection log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: detection
- path: /tmp/gh-aw/threat-detection/detection.log
- if-no-files-found: ignore
- - name: Parse and conclude threat detection
- id: detection_conclusion
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
- DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
- GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
- with:
- script: |
- try {
- 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/parse_threat_detection_results.cjs');
- await main();
- } catch (loadErr) {
- const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
- const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
- const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
- core.error(msg);
- core.setOutput('reason', 'parse_error');
- if (continueOnError && !detectionExecutionFailed) {
- core.warning('\u26A0\uFE0F ' + msg);
- core.setOutput('conclusion', 'warning');
- core.setOutput('success', 'false');
- } else {
- core.setOutput('conclusion', 'failure');
- core.setOutput('success', 'false');
- core.setFailed(msg);
- }
- }
-
- safe_outputs:
- needs:
- - activation
- - agent
- - detection
- if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
- runs-on: ubuntu-slim
- permissions:
- contents: read
- issues: write
- timeout-minutes: 45
- env:
- GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
- GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/duplicate-resource-detector"
- GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
- GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
- GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
- GH_AW_ENGINE_VERSION: "1.0.65"
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_WORKFLOW_ID: "duplicate-resource-detector"
- GH_AW_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/duplicate-resource-detector.md"
- outputs:
- code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
- code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
- create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
- create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
- process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
- process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Duplicate Resource Detector"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/duplicate-resource-detector.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Configure GH_HOST for enterprise compatibility
- id: ghes-host-config
- shell: bash
- run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
- # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
- # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
- GH_HOST="${GITHUB_SERVER_URL#https://}"
- GH_HOST="${GH_HOST#http://}"
- echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- - name: Process Safe Outputs
- id: process_safe_outputs
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"labels\":[\"duplicate-review\"],\"max\":1},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/safe_output_handler_manager.cjs');
- await main();
- - name: Upload Safe Outputs Items
- if: always()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: safe-outputs-items
- path: |
- /tmp/gh-aw/safe-output-items.jsonl
- /tmp/gh-aw/temporary-id-map.json
- if-no-files-found: ignore
-
diff --git a/.github/workflows/duplicate-resource-detector.md b/.github/workflows/duplicate-resource-detector.md
deleted file mode 100644
index b3c1a9bf45..0000000000
--- a/.github/workflows/duplicate-resource-detector.md
+++ /dev/null
@@ -1,127 +0,0 @@
----
-description: Weekly scan of agents, instructions, and skills to identify potential duplicate resources and report them for review
-on:
- schedule: weekly
-permissions:
- contents: read
- issues: read
- copilot-requests: write
-tools:
- github:
- toolsets: [repos, issues]
-safe-outputs:
- create-issue:
- max: 1
- close-older-issues: true
- labels:
- - duplicate-review
- noop:
----
-
-# Duplicate Resource Detector
-
-You are an AI agent that audits the resources in this repository to find potential duplicates — resources that appear to serve the same or very similar purpose.
-
-## Your Task
-
-Scan all resources in the following directories and identify groups of resources that may be duplicates or near-duplicates based on their **name**, **description**, and **content**:
-
-- `agents/` (`.agent.md` files)
-- `instructions/` (`.instructions.md` files)
-- `skills/` (folders — check `SKILL.md` inside each)
-
-### Step 1: Gather Resource Metadata
-
-For each resource, extract:
-
-1. **File name** (the path)
-2. **Front matter `description`** field
-3. **Front matter `name`** field (if present)
-4. **First ~20 lines of body content** (the markdown after the front matter)
-
-Use bash to read files efficiently. For skills, read `skills//SKILL.md`.
-
-### Step 2: Identify Potential Duplicates
-
-Compare resources and flag groups that look like potential duplicates. Consider resources as potential duplicates when they share **two or more** of the following signals:
-
-- **Similar names** — file names or `name` fields that share key terms (e.g., `react-testing.agent.md` and `react-unit-testing.agent.md`)
-- **Similar descriptions** — descriptions that describe the same task, technology, or domain with only minor wording differences
-- **Overlapping scope** — resources that target the same language/framework/tool and the same activity (e.g., two separate "Python best practices" instructions)
-- **Cross-type overlap** — an agent and an instruction (or instruction and skill) that cover the same topic so thoroughly that one may make the other redundant
-
-Be pragmatic. Resources that cover related but distinct topics are NOT duplicates. For example:
-- `react.instructions.md` (general React coding standards) and `react-testing.agent.md` (React testing agent) are **not** duplicates — they serve different purposes.
-- `python-fastapi.instructions.md` and `python-flask.instructions.md` are **not** duplicates — they target different frameworks.
-- `code-review.agent.md` and `code-review.instructions.md` that both do the same style of code review **are** potential duplicates worth flagging.
-
-### Step 3: Check for Known Accepted Duplicates
-
-Before finalizing the report, search for **previous issues** labeled `duplicate-review` in this repository:
-
-```
-Search for issues with label "duplicate-review" that are closed
-```
-
-Read the comments and body of those past issues to find any pairs or groups that reviewers have explicitly marked as **"accepted"** or **"not duplicates"**. Look for phrases like:
-- "accepted as-is"
-- "not duplicates"
-- "intentionally separate"
-- "keep both"
-- checked task list items (i.e., `- [x]`)
-
-Exclude those known-accepted pairs from the current report. If you include a group that was previously reviewed, add a note: `(previously reviewed — see #)`.
-
-### Step 4: Produce the Report
-
-Create an issue titled: `🔍 Duplicate Resource Review`
-
-Format the body as follows:
-
-```markdown
-### Summary
-
-- **Potential duplicate groups found:** N
-- **Resources involved:** M
-- **Known accepted (excluded):** K pairs from previous reviews
-
-### How to Use This Report
-
-Review each group below. If the resources are intentionally separate, check the box to mark them as accepted. These will be excluded from future reports.
-
-### Potential Duplicates
-
-#### Group 1:
-
-- [ ] Reviewed — these are intentionally separate
-
-| Resource | Type | Description |
-|----------|------|-------------|
-| `agents/foo.agent.md` | Agent | Does X for Y |
-| `instructions/foo.instructions.md` | Instruction | Also does X for Y |
-
-**Why flagged:**
-
----
-
-#### Group 2: ...
-
-
-```
-
-Use `` blocks to collapse groups if there are more than 10.
-
-### Safe Output Guidance
-
-- If you find potential duplicates: use `create-issue` to file the report.
-- If **no** potential duplicates are found (after excluding known accepted ones): call `noop` with the message: "No potential duplicate resources detected. All resources appear to serve distinct purposes."
-
-## Guidelines
-
-- Be conservative — only flag resources where there is a genuine risk of redundancy.
-- Group related duplicates together (don't list the same pair twice in separate groups).
-- Sort groups by confidence (strongest duplicate signals first).
-- Include cross-type duplicates (e.g., an agent and an instruction doing the same thing).
-- Limit the report to the top 20 most likely duplicate groups to keep it actionable.
-- For skills, use the folder name and description from `SKILL.md`.
-- Process resources in batches to stay within time limits — prioritize name and description comparison, then spot-check content for top candidates.
diff --git a/.github/workflows/learning-hub-updater.lock.yml b/.github/workflows/learning-hub-updater.lock.yml
deleted file mode 100644
index 9d73a1202d..0000000000
--- a/.github/workflows/learning-hub-updater.lock.yml
+++ /dev/null
@@ -1,1626 +0,0 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"beab441c9c1223e4f6de1e14679b620313f2c1540a3e844b46994c208b7aef0c","body_hash":"d1646d891ce854f6e674a56c71b0f1d5d7a0e236c1647d9ed85e33e16290bee5","compiler_version":"v0.81.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65"}}
-# gh-aw-manifest: {"version":1,"secrets":["GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","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":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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"},{"repo":"github/gh-aw-actions/setup","sha":"ba6380cc6e5be5d21677bebe04d52fb48e3abec7","version":"v0.81.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]}
-# This file was automatically generated by gh-aw (v0.81.6). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
-#
-# ___ _ _
-# / _ \ | | (_)
-# | |_| | __ _ ___ _ __ | |_ _ ___
-# | _ |/ _` |/ _ \ '_ \| __| |/ __|
-# | | | | (_| | __/ | | | |_| | (__
-# \_| |_/\__, |\___|_| |_|\__|_|\___|
-# __/ |
-# _ _ |___/
-# | | | | / _| |
-# | | | | ___ _ __ _ __| |_| | _____ ____
-# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
-# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
-# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
-#
-#
-# To update this file, edit the corresponding .md file and run:
-# gh aw compile
-# Not all edits will cause changes to this file.
-#
-# For more information: https://github.github.com/gh-aw/introduction/overview/
-#
-# Daily check for new GitHub Copilot features and updates. Opens a PR if the Learning Hub needs updating.
-#
-# Secrets used:
-# - GH_AW_CI_TRIGGER_TOKEN
-# - GH_AW_GITHUB_MCP_SERVER_TOKEN
-# - GH_AW_GITHUB_TOKEN
-# - GITHUB_TOKEN
-#
-# Custom actions used:
-# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
-# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
-# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
-# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
-# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
-# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
-# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
-# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
-# - github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
-#
-# Container images used:
-# - ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7
-# - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d
-# - ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d
-# - ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be
-# - ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b
-# - ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036
-
-name: "Learning Hub Updater"
-on:
- schedule:
- - cron: "38 20 * * *"
- # Friendly format: daily (scattered)
- workflow_dispatch:
- inputs:
- aw_context:
- default: ""
- description: "Agent caller context (used internally by Agentic Workflows)."
- required: false
- type: string
-
-permissions: {}
-
-concurrency:
- group: "gh-aw-${{ github.workflow }}"
-
-run-name: "Learning Hub Updater"
-
-jobs:
- activation:
- runs-on: ubuntu-slim
- permissions:
- actions: read
- contents: read
- env:
- GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- comment_id: ""
- comment_repo: ""
- daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }}
- daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }}
- daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }}
- engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
- lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
- model: ${{ steps.generate_aw_info.outputs.model }}
- setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
- setup-span-id: ${{ steps.setup.outputs.span-id }}
- setup-trace-id: ${{ steps.setup.outputs.trace-id }}
- stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/learning-hub-updater.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Generate agentic run info
- id: generate_aw_info
- env:
- GH_AW_INFO_ENGINE_ID: "copilot"
- GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
- GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AGENT_VERSION: "1.0.65"
- GH_AW_INFO_CLI_VERSION: "v0.81.6"
- GH_AW_INFO_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_INFO_EXPERIMENTAL: "false"
- GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
- GH_AW_INFO_STAGED: "false"
- GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
- GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_AWMG_VERSION: ""
- GH_AW_INFO_FIREWALL_TYPE: "squid"
- GH_AW_COMPILED_STRICT: "true"
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/generate_aw_info.cjs');
- await main(core, context);
- - name: Restore daily AIC usage cache
- id: restore-daily-aic-cache
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- continue-on-error: true
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-learninghubupdater-${{ github.run_id }}
- restore-keys: agentic-workflow-usage-learninghubupdater-
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Restore daily AIC usage cache (artifact fallback)
- id: restore-daily-aic-cache-fallback
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }}
- GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- 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/restore_aic_usage_cache_fallback.cjs');
- await main();
- - name: Check daily workflow token guardrail
- id: daily-effective-workflow-guardrail
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_WORKFLOW_ID: "learning-hub-updater"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }}
- GH_AW_HAS_SLASH_COMMAND: "false"
- GH_AW_HAS_LABEL_COMMAND: "false"
- GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- 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/check_daily_aic_workflow_guardrail.cjs');
- await main();
- - name: Checkout .github and .agents folders
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- sparse-checkout: |
- .github
- .agents
- .antigravity
- .claude
- .codex
- .crush
- .gemini
- .opencode
- .pi
- sparse-checkout-cone-mode: true
- fetch-depth: 1
- - name: Save agent config folders for base branch restoration
- env:
- GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
- GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
- - name: Check workflow lock file
- id: check-lock-file
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_WORKFLOW_FILE: "learning-hub-updater.lock.yml"
- GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
- 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/check_workflow_timestamp_api.cjs');
- await main();
- - name: Check compile-agentic version
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_COMPILED_VERSION: "v0.81.6"
- 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/check_version_updates.cjs');
- await main();
- - name: Log runtime features
- if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh"
- - name: Create prompt with built-in context
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- # poutine:ignore untrusted_checkout_exec
- run: |
- bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
- {
- cat << 'GH_AW_PROMPT_251718cc060c9a3d_EOF'
-
- GH_AW_PROMPT_251718cc060c9a3d_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_251718cc060c9a3d_EOF'
-
- Tools: create_pull_request, missing_tool, missing_data, noop
- GH_AW_PROMPT_251718cc060c9a3d_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md"
- cat << 'GH_AW_PROMPT_251718cc060c9a3d_EOF'
-
- GH_AW_PROMPT_251718cc060c9a3d_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_251718cc060c9a3d_EOF'
-
- The following GitHub context information is available for this workflow:
- {{#if github.actor}}
- - **actor**: __GH_AW_GITHUB_ACTOR__
- {{/if}}
- {{#if github.repository}}
- - **repository**: __GH_AW_GITHUB_REPOSITORY__
- {{/if}}
- {{#if github.workspace}}
- - **workspace**: __GH_AW_GITHUB_WORKSPACE__
- {{/if}}
- {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
- - **issue-number**: #__GH_AW_EXPR_802A9F6A__
- {{/if}}
- {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
- - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
- {{/if}}
- {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
- - **pull-request-number**: #__GH_AW_EXPR_463A214A__
- {{/if}}
- {{#if github.event.comment.id || github.aw.context.comment_id}}
- - **comment-id**: __GH_AW_EXPR_FF1D34CE__
- {{/if}}
- {{#if github.run_id}}
- - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
- {{/if}}
-
-
- GH_AW_PROMPT_251718cc060c9a3d_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_251718cc060c9a3d_EOF'
-
- {{#runtime-import .github/workflows/learning-hub-updater.md}}
- GH_AW_PROMPT_251718cc060c9a3d_EOF
- } > "$GH_AW_PROMPT"
- - name: Interpolate variables and render templates
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_ENGINE_ID: "copilot"
- 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/interpolate_prompt.cjs');
- await main();
- - name: Substitute placeholders
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
- with:
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io, getOctokit);
-
- const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
-
- // Call the substitution function
- return await substitutePlaceholders({
- file: process.env.GH_AW_PROMPT,
- substitutions: {
- GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
- GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
- GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
- GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
- GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
- GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
- GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
- GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
- }
- });
- - name: Validate prompt placeholders
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
- - name: Print prompt
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
- - name: Upload activation artifact
- if: success()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: activation
- include-hidden-files: true
- path: |
- /tmp/gh-aw/aw_info.json
- /tmp/gh-aw/models.json
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/aw-prompts/prompt-template.txt
- /tmp/gh-aw/aw-prompts/prompt-import-tree.json
- /tmp/gh-aw/github_rate_limits.jsonl
- /tmp/gh-aw/base
- /tmp/gh-aw/.github/agents
- /tmp/gh-aw/.github/skills
- if-no-files-found: ignore
- retention-days: 1
-
- agent:
- needs: activation
- if: needs.activation.outputs.daily_ai_credits_exceeded != 'true'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- copilot-requests: write
- concurrency:
- group: "gh-aw-copilot-${{ github.workflow }}"
- queue: max
- env:
- DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
- GH_AW_ASSETS_ALLOWED_EXTS: ""
- GH_AW_ASSETS_BRANCH: ""
- GH_AW_ASSETS_MAX_SIZE_KB: 0
- GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_WORKFLOW_ID_SANITIZED: learninghubupdater
- outputs:
- agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
- ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }}
- aic: ${{ steps.parse-mcp-gateway.outputs.aic }}
- ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }}
- checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
- effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
- has_patch: ${{ steps.collect_output.outputs.has_patch }}
- inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
- mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
- model: ${{ needs.activation.outputs.model }}
- model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
- output: ${{ steps.collect_output.outputs.output }}
- output_types: ${{ steps.collect_output.outputs.output_types }}
- setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
- setup-span-id: ${{ steps.setup.outputs.span-id }}
- setup-trace-id: ${{ steps.setup.outputs.trace-id }}
- unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/learning-hub-updater.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Set runtime paths
- id: set-runtime-paths
- run: |
- {
- echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
- echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
- echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
- } >> "$GITHUB_OUTPUT"
- - name: Checkout repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- - name: Create gh-aw temp directory
- run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
- - name: Configure gh CLI for GitHub Enterprise
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
- env:
- GH_TOKEN: ${{ github.token }}
- - name: Configure Git credentials
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_TOKEN: ${{ github.token }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Checkout PR branch
- id: checkout-pr
- if: |
- github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request'
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io, getOctokit);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
- await main();
- - name: Install GitHub Copilot CLI
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11
- - name: Determine automatic lockdown mode for GitHub MCP Server
- id: determine-automatic-lockdown
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
- env:
- GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- with:
- script: |
- const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
- await determineAutomaticLockdown(github, context, core);
- - name: Download activation artifact
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: activation
- path: /tmp/gh-aw
- - name: Restore agent config folders from base branch
- if: steps.checkout-pr.outcome == 'success'
- env:
- GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
- GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
- - name: Restore inline sub-agents from activation artifact
- env:
- GH_AW_SUB_AGENT_DIR: ".github/agents"
- GH_AW_SUB_AGENT_EXT: ".agent.md"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
- - name: Restore inline skills from activation artifact
- env:
- GH_AW_SKILL_DIR: ".github/skills"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
- - name: Download container images
- run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036
- - name: Generate Safe Outputs Config
- run: |
- 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_bf105b7d2524f53f_EOF'
- {"create_pull_request":{"base_branch":"main","labels":["automated-update","copilot-updates"],"max":1,"max_patch_files":100,"max_patch_size":4096,"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"],"protected_files_policy":"request_review","title_prefix":"[bot] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_bf105b7d2524f53f_EOF
- - name: Generate Safe Outputs Tools
- env:
- GH_AW_TOOLS_META_JSON: |
- {
- "description_suffixes": {
- "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[bot] \". Labels [\"automated-update\" \"copilot-updates\"] will be automatically added."
- },
- "repo_params": {},
- "dynamic_tools": []
- }
- GH_AW_VALIDATION_JSON: |
- {
- "create_pull_request": {
- "defaultMax": 1,
- "fields": {
- "base": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "body": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "branch": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "draft": {
- "type": "boolean"
- },
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- },
- "title": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "missing_data": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "context": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "data_type": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "reason": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- }
- }
- },
- "missing_tool": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 512
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "tool": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "noop": {
- "defaultMax": 1,
- "fields": {
- "message": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- }
- }
- },
- "report_incomplete": {
- "defaultMax": 5,
- "fields": {
- "details": {
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 1024
- }
- }
- }
- }
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/generate_safe_outputs_tools.cjs');
- await main();
- - name: Start MCP Gateway
- id: start-mcp-gateway
- env:
- GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }}
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }}
- GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
- GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- set -eo pipefail
- mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
-
- # Export gateway environment variables for MCP config and gateway script
- export MCP_GATEWAY_PORT="8080"
- export MCP_GATEWAY_DOMAIN="host.docker.internal"
- export MCP_GATEWAY_HOST_DOMAIN="localhost"
- MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
- echo "::add-mask::${MCP_GATEWAY_API_KEY}"
- export MCP_GATEWAY_API_KEY
- export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
- mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
- export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
- export DEBUG="*"
-
- export GH_AW_ENGINE="copilot"
- MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
- MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
- case "${DOCKER_HOST:-}" in
- unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
- /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
- * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
- esac
- 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 --name awmg-mcpg --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_POLICY_ALLOW_CREATE_PULL_REQUEST -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 RUNNER_TEMP -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 -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30'
-
- mkdir -p "$HOME/.copilot"
- GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_532027a3cd6cbb4c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
- {
- "mcpServers": {
- "github": {
- "type": "stdio",
- "container": "ghcr.io/github/github-mcp-server:v1.4.0",
- "env": {
- "GITHUB_HOST": "${GITHUB_SERVER_URL}",
- "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}",
- "GITHUB_READ_ONLY": "1",
- "GITHUB_TOOLSETS": "repos"
- },
- "guard-policies": {
- "allow-only": {
- "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
- "repos": "$GITHUB_MCP_GUARD_REPOS"
- }
- }
- },
- "safeoutputs": {
- "type": "stdio",
- "container": "ghcr.io/github/gh-aw-node",
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"],
- "args": ["-w", "\${GITHUB_WORKSPACE}"],
- "entrypoint": "sh",
- "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"],
- "env": {
- "DEBUG": "*",
- "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}",
- "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}",
- "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}",
- "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}",
- "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}",
- "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}",
- "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}",
- "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}",
- "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}",
- "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}",
- "GITHUB_TOKEN": "\${GITHUB_TOKEN}",
- "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}",
- "RUNNER_TEMP": "\${RUNNER_TEMP}"
- },
- "guard-policies": {
- "write-sink": {
- "accept": [
- "*"
- ]
- }
- }
- }
- },
- "gateway": {
- "port": $MCP_GATEWAY_PORT,
- "domain": "${MCP_GATEWAY_DOMAIN}",
- "apiKey": "${MCP_GATEWAY_API_KEY}",
- "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
- }
- }
- GH_AW_MCP_CONFIG_532027a3cd6cbb4c_EOF
- - name: Mount MCP servers as CLIs
- id: mount-mcp-clis
- continue-on-error: true
- env:
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
- await main();
- - name: Clean credentials
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
- - name: Audit pre-agent workspace
- id: pre_agent_audit
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
- - name: Execute GitHub Copilot CLI
- id: agentic_execution
- # Copilot CLI tool arguments (sorted):
- # --allow-tool github
- # --allow-tool safeoutputs
- # --allow-tool shell(cat)
- # --allow-tool shell(curl:*)
- # --allow-tool shell(date)
- # --allow-tool shell(echo)
- # --allow-tool shell(gh:*)
- # --allow-tool shell(git add:*)
- # --allow-tool shell(git branch:*)
- # --allow-tool shell(git checkout:*)
- # --allow-tool shell(git commit:*)
- # --allow-tool shell(git merge:*)
- # --allow-tool shell(git rm:*)
- # --allow-tool shell(git status)
- # --allow-tool shell(git switch:*)
- # --allow-tool shell(grep)
- # --allow-tool shell(head)
- # --allow-tool shell(ls)
- # --allow-tool shell(printf)
- # --allow-tool shell(pwd)
- # --allow-tool shell(safeoutputs:*)
- # --allow-tool shell(sort)
- # --allow-tool shell(tail)
- # --allow-tool shell(uniq)
- # --allow-tool shell(wc)
- # --allow-tool shell(yq)
- # --allow-tool web_fetch
- # --allow-tool write
- timeout-minutes: 20
- run: |
- set -o pipefail
- printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
- trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
- mkdir -p "$HOME/.copilot"
- printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
- export XDG_CONFIG_HOME="$HOME"
- export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json"
- touch /tmp/gh-aw/agent-step-summary.md
- GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
- export GH_AW_NODE_BIN
- export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
- (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
- GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}"
- printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"github.com\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"telemetry.enterprise.githubcopilot.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
- export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json"
- GH_AW_DOCKER_HOST=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST="${DOCKER_HOST}"
- fi
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
- GH_AW_CHROOT_BINARIES_SOURCE_PATH=/tmp/gh-aw GH_AW_CHROOT_IDENTITY_HOME=/tmp/gh-aw/home node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs"
- fi
- GH_AW_TOOL_CACHE_MOUNT=""
- GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"
- if [ -d "$GH_AW_TOOL_CACHE" ]; then
- if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
- GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
- fi
- fi
- # shellcheck disable=SC1003,SC2086
- sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
- -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-tool github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(curl:*)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(gh:*)'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool web_fetch --allow-tool write --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
- env:
- AWF_REFLECT_ENABLED: 1
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
- COPILOT_GITHUB_TOKEN: ${{ github.token }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_LLM_PROVIDER: github
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
- GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
- GH_AW_PHASE: agent
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_TIMEOUT_MINUTES: 20
- GH_AW_VERSION: v0.81.6
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- RUNNER_TEMP: ${{ runner.temp }}
- S2STOKENS: true
- TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- - name: Detect agent errors
- if: always()
- id: detect-agent-errors
- continue-on-error: true
- run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
- - name: Configure Git credentials
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_TOKEN: ${{ github.token }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Copy Copilot session state files to logs
- if: always()
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
- - name: Stop MCP Gateway
- if: always()
- continue-on-error: true
- env:
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
- run: |
- bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- - name: Redact secrets in logs
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/redact_secrets.cjs');
- await main();
- env:
- GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
- SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Append agent step summary
- if: always()
- run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
- - name: Copy Safe Outputs
- if: always()
- env:
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- run: |
- mkdir -p /tmp/gh-aw
- cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
- - name: Ingest agent output
- id: collect_output
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,code.visualstudio.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.blog,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,localhost,nishanil.github.io,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- 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/collect_ndjson_output.cjs');
- await main();
- - name: Parse agent logs for step summary
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
- 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/parse_copilot_log.cjs');
- await main();
- - name: Parse MCP Gateway logs for step summary
- if: always()
- id: parse-mcp-gateway
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/parse_mcp_gateway_log.cjs');
- await main();
- - name: Print firewall logs
- if: always()
- continue-on-error: true
- env:
- AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
- run: |
- # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts
- # AWF runs with sudo, creating files owned by root
- sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
- # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
- if command -v awf &> /dev/null; then
- awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
- else
- echo 'AWF binary not installed, skipping firewall log summary'
- fi
- - name: Parse token usage for step summary
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/parse_token_usage.cjs');
- await main();
- - name: Print AWF reflect summary
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/awf_reflect_summary.cjs');
- await main();
- - name: Write agent output placeholder if missing
- if: always()
- run: |
- if [ ! -f /tmp/gh-aw/agent_output.json ]; then
- echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
- fi
- - name: Upload agent artifacts
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: agent
- path: |
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/sandbox/agent/logs/
- /tmp/gh-aw/redacted-urls.log
- /tmp/gh-aw/mcp-logs/
- /tmp/gh-aw/agent_usage.json
- /tmp/gh-aw/agent-stdio.log
- /tmp/gh-aw/pre-agent-audit.txt
- /tmp/gh-aw/agent/
- /tmp/gh-aw/github_rate_limits.jsonl
- /tmp/gh-aw/safeoutputs.jsonl
- /tmp/gh-aw/agent_output.json
- /tmp/gh-aw/aw-*.patch
- /tmp/gh-aw/aw-*.bundle
- /tmp/gh-aw/awf-config.json
- /tmp/gh-aw/sandbox/firewall/logs/
- /tmp/gh-aw/sandbox/firewall/audit/
- /tmp/gh-aw/sandbox/firewall/awf-reflect.json
- if-no-files-found: ignore
-
- conclusion:
- needs:
- - activation
- - agent
- - detection
- - safe_outputs
- if: >
- always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
- needs.activation.outputs.stale_lock_file_failed == 'true' || needs.activation.outputs.daily_ai_credits_exceeded == 'true')
- runs-on: ubuntu-slim
- permissions:
- contents: write
- issues: write
- pull-requests: write
- concurrency:
- group: "gh-aw-conclusion-learning-hub-updater"
- cancel-in-progress: false
- queue: max
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
- noop_message: ${{ steps.noop.outputs.noop_message }}
- tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
- total_count: ${{ steps.missing_tool.outputs.total_count }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/learning-hub-updater.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Collect usage artifact files
- if: always()
- continue-on-error: true
- run: |
- mkdir -p /tmp/gh-aw/usage/agent /tmp/gh-aw/usage/detection
- echo "Usage artifact source file status:"
- for file in /tmp/gh-aw/aw_info.json /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/agent_usage.json /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl; do
- [ -f "$file" ] && echo "FOUND: $file" || echo "MISSING: $file"
- done
- [ -f /tmp/gh-aw/aw_info.json ] && cp /tmp/gh-aw/aw_info.json /tmp/gh-aw/usage/aw_info.json || true
- [ -f /tmp/gh-aw/aw-info.jsonl ] && cp /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/usage/aw-info.jsonl || true
- [ -f /tmp/gh-aw/agent_usage.json ] && cp /tmp/gh-aw/agent_usage.json /tmp/gh-aw/usage/agent_usage.json || true
- [ -f /tmp/gh-aw/agent_usage.jsonl ] && cp /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/usage/agent_usage.jsonl || true
- [ -f /tmp/gh-aw/detection_usage.jsonl ] && cp /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/usage/detection_usage.jsonl || true
- [ -f /tmp/gh-aw/github_rate_limits.jsonl ] && cp /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/usage/github_rate_limits.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -f /tmp/gh-aw/usage/agent/token_usage.jsonl ] || : > /tmp/gh-aw/usage/agent/token_usage.jsonl
- [ -f /tmp/gh-aw/usage/detection/token_usage.jsonl ] || : > /tmp/gh-aw/usage/detection/token_usage.jsonl
- mkdir -p /tmp/gh-aw/usage/activity
- node ${{ runner.temp }}/gh-aw/actions/generate_usage_activity_summary.cjs
- find /tmp/gh-aw/usage -type f -print | sort
- - name: Upload usage artifact
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: usage
- path: |
- /tmp/gh-aw/usage/aw_info.json
- /tmp/gh-aw/usage/aw-info.jsonl
- /tmp/gh-aw/usage/agent_usage.json
- /tmp/gh-aw/usage/agent_usage.jsonl
- /tmp/gh-aw/usage/detection_usage.jsonl
- /tmp/gh-aw/usage/github_rate_limits.jsonl
- /tmp/gh-aw/usage/agent/token_usage.jsonl
- /tmp/gh-aw/usage/detection/token_usage.jsonl
- /tmp/gh-aw/usage/activity/summary.json
- if-no-files-found: ignore
- - name: Restore daily AIC usage cache
- id: restore-daily-aic-cache-conclusion
- if: always()
- continue-on-error: true
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-learninghubupdater-${{ github.run_id }}
- restore-keys: agentic-workflow-usage-learninghubupdater-
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Write daily AIC usage cache entry
- id: write-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- github-token: ${{ github.token }}
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/write_daily_aic_usage_cache.cjs');
- await main();
- - name: Save daily AIC usage cache
- id: save-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-learninghubupdater-${{ github.run_id }}
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Upload daily AIC usage cache artifact
- id: upload-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: aic-usage-cache
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- if-no-files-found: ignore
- retention-days: 7
- - name: Process no-op messages
- id: noop
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: "1"
- GH_AW_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/learning-hub-updater.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_NOOP_REPORT_AS_ISSUE: "true"
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
- GH_AW_WORKFLOW_ID: "learning-hub-updater"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_noop_message.cjs');
- await main();
- - name: Log detection run
- id: detection_runs
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/learning-hub-updater.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
- GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_detection_runs.cjs');
- await main();
- - name: Record missing tool
- id: missing_tool
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
- GH_AW_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/learning-hub-updater.md"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/missing_tool.cjs');
- await main();
- - name: Record incomplete
- id: report_incomplete
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
- GH_AW_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/learning-hub-updater.md"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/report_incomplete_handler.cjs');
- await main();
- - name: Handle agent failure
- id: handle_agent_failure
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/learning-hub-updater.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_WORKFLOW_ID: "learning-hub-updater"
- GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
- GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
- GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }}
- GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }}
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
- GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
- GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
- GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
- GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
- GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
- GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }}
- GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }}
- GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
- GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
- GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }}
- GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }}
- GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }}
- GH_AW_GROUP_REPORTS: "false"
- GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
- GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
- GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
- GH_AW_TIMEOUT_MINUTES: "20"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_agent_failure.cjs');
- await main();
-
- detection:
- needs:
- - activation
- - agent
- if: always() && needs.agent.result != 'skipped'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- copilot-requests: write
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- aic: ${{ steps.parse_detection_token_usage.outputs.aic }}
- detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
- detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
- detection_success: ${{ steps.detection_conclusion.outputs.success }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/learning-hub-updater.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Checkout repository for patch context
- if: needs.agent.outputs.has_patch == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- # --- Threat Detection ---
- - name: Clean stale firewall files from agent artifact
- run: |
- rm -rf /tmp/gh-aw/sandbox/firewall/logs
- rm -rf /tmp/gh-aw/sandbox/firewall/audit
- - name: Download container images
- run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d
- - name: Check if detection needed
- id: detection_guard
- if: always()
- env:
- OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- run: |
- if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
- echo "run_detection=true" >> "$GITHUB_OUTPUT"
- echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
- else
- echo "run_detection=false" >> "$GITHUB_OUTPUT"
- echo "Detection skipped: no agent outputs or patches to analyze"
- fi
- - name: Clear MCP Config for detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
- rm -f "$HOME/.copilot/mcp-config.json"
- rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
- - name: Prepare threat detection files
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
- rm -f /tmp/gh-aw/agent_usage.json
- cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
- if [ ! -s /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt ]; then
- echo "::warning::ERR_VALIDATION: Missing or empty detection context prompt at /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt. Ensure the agent artifact includes /tmp/gh-aw/aw-prompts/prompt.txt. Detection will continue with fallback workflow context."
- fi
- cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
- for f in /tmp/gh-aw/aw-*.patch; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- for f in /tmp/gh-aw/aw-*.bundle; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- echo "Prepared threat detection files:"
- ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- - name: Setup threat detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- WORKFLOW_NAME: "Learning Hub Updater"
- WORKFLOW_DESCRIPTION: "Daily check for new GitHub Copilot features and updates. Opens a PR if the Learning Hub needs updating."
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- 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/setup_threat_detection.cjs');
- await main();
- - name: Ensure threat-detection directory and log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection
- touch /tmp/gh-aw/threat-detection/detection.log
- - name: Setup Node.js
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
- with:
- node-version: '24'
- package-manager-cache: false
- - name: Install GitHub Copilot CLI
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11
- - name: Execute GitHub Copilot CLI
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- continue-on-error: true
- id: detection_agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
- trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
- mkdir -p "$HOME/.copilot"
- printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
- export XDG_CONFIG_HOME="$HOME"
- touch /tmp/gh-aw/agent-step-summary.md
- GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
- export GH_AW_NODE_BIN
- export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
- (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
- GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}"
- printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
- export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json"
- GH_AW_DOCKER_HOST=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST="${DOCKER_HOST}"
- fi
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
- _GH_AW_CHROOT_JSON=$(jq -c --arg src /tmp/gh-aw --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home /tmp/gh-aw/home '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; }
- printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- printf '%s\n' "$_GH_AW_CHROOT_JSON" > "/tmp/gh-aw/awf-config.json"
- fi
- GH_AW_TOOL_CACHE_MOUNT=""
- GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"
- if [ -d "$GH_AW_TOOL_CACHE" ]; then
- if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
- GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
- fi
- fi
- # shellcheck disable=SC1003,SC2086
- sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
- -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
- env:
- AWF_REFLECT_ENABLED: 1
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
- COPILOT_GITHUB_TOKEN: ${{ github.token }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_LLM_PROVIDER: github
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}
- GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
- GH_AW_PHASE: detection
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_TIMEOUT_MINUTES: 20
- GH_AW_VERSION: v0.81.6
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- RUNNER_TEMP: ${{ runner.temp }}
- S2STOKENS: true
- TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- - name: Parse threat detection token usage for step summary
- id: parse_detection_token_usage
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage
- 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/parse_token_usage.cjs');
- await main();
- - name: Upload threat detection log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: detection
- path: /tmp/gh-aw/threat-detection/detection.log
- if-no-files-found: ignore
- - name: Parse and conclude threat detection
- id: detection_conclusion
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
- DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
- GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
- with:
- script: |
- try {
- 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/parse_threat_detection_results.cjs');
- await main();
- } catch (loadErr) {
- const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
- const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
- const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
- core.error(msg);
- core.setOutput('reason', 'parse_error');
- if (continueOnError && !detectionExecutionFailed) {
- core.warning('\u26A0\uFE0F ' + msg);
- core.setOutput('conclusion', 'warning');
- core.setOutput('success', 'false');
- } else {
- core.setOutput('conclusion', 'failure');
- core.setOutput('success', 'false');
- core.setFailed(msg);
- }
- }
-
- safe_outputs:
- needs:
- - activation
- - agent
- - detection
- if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
- runs-on: ubuntu-slim
- permissions:
- contents: write
- issues: write
- pull-requests: write
- timeout-minutes: 45
- env:
- GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
- GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/learning-hub-updater"
- GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
- GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
- GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
- GH_AW_ENGINE_VERSION: "1.0.65"
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_WORKFLOW_ID: "learning-hub-updater"
- GH_AW_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/learning-hub-updater.md"
- outputs:
- code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
- code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
- create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
- create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_pr_number: ${{ steps.process_safe_outputs.outputs.created_pr_number }}
- created_pr_url: ${{ steps.process_safe_outputs.outputs.created_pr_url }}
- process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
- process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Learning Hub Updater"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/learning-hub-updater.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Download patch artifact
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Checkout repository
- if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request')
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: true
- token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- - name: Configure Git credentials
- if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request')
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Configure GH_HOST for enterprise compatibility
- id: ghes-host-config
- shell: bash
- run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
- # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
- # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
- GH_HOST="${GITHUB_SERVER_URL#https://}"
- GH_HOST="${GH_HOST#http://}"
- echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- - name: Process Safe Outputs
- id: process_safe_outputs
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,code.visualstudio.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.blog,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,localhost,nishanil.github.io,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"base_branch\":\"main\",\"labels\":[\"automated-update\",\"copilot-updates\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"[bot] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
- GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/safe_output_handler_manager.cjs');
- await main();
- - name: Upload Safe Outputs Items
- if: always()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: safe-outputs-items
- path: |
- /tmp/gh-aw/safe-output-items.jsonl
- /tmp/gh-aw/temporary-id-map.json
- if-no-files-found: ignore
-
diff --git a/.github/workflows/learning-hub-updater.md b/.github/workflows/learning-hub-updater.md
deleted file mode 100644
index ea2a51730d..0000000000
--- a/.github/workflows/learning-hub-updater.md
+++ /dev/null
@@ -1,89 +0,0 @@
----
-name: "Learning Hub Updater"
-description: "Daily check for new GitHub Copilot features and updates. Opens a PR if the Learning Hub needs updating."
-on:
- schedule: daily
- workflow_dispatch:
-permissions:
- contents: read
- copilot-requests: write
-tools:
- bash: ["curl", "gh"]
- edit:
- web-fetch:
- github:
- toolsets: [repos]
-safe-outputs:
- allowed-domains:
- - github.blog
- - code.visualstudio.com
- - nishanil.github.io
- create-pull-request:
- labels: [automated-update, copilot-updates]
- title-prefix: "[bot] "
- base-branch: main
----
-
-# Check for Awesome GitHub Copilot Updates
-
-You are a documentation maintainer for the Awesome GitHub Copilot Learning Hub. Your job is to check for recent updates to GitHub Copilot and determine if the Learning Hub pages in `website/learning-hub` need updating.
-
-## Step 1 — Gather recent Copilot updates
-
-Use `web-fetch` to read the following pages and extract the latest entries from the past 7 days:
-
-- https://github.blog/changelog/label/copilot/ — official changelog
-- https://github.com/github/copilot-cli/blob/main/changelog.md — CLI changelog
-- https://github.blog/ai-and-ml/github-copilot/ — blog posts
-- https://code.visualstudio.com/updates - VS Code release notes (filter for Copilot-related updates)
-- https://nishanil.github.io/copilot-guide/ - community-maintained guide (check for recent commits or updates)
-
-Also use `gh` CLI to check the latest releases and commits in the `github/copilot-cli` repo.
-
-Look for:
-
-- New features or capabilities (new slash commands, new agent modes, new integrations)
-- Significant changes to existing features (renames, deprecations, GA announcements)
-- New customization options (instructions, agents, skills, MCP, hooks, plugins)
-- New platform features (memory, spaces, SDK updates)
-- Notable community projects built on Copilot
-
-## Step 2 — Compare against the current Learning Hub
-
-Read the pages in the current Learning Hub and compare the features documented there against what you found in Step 1, with the exception of the `cli-for-beginners` section as we handle updates to that separately. Any suggested changes to those pages will be rejected.
-
-Identify:
-
-- **Missing features** — new capabilities not yet documented
-- **Outdated information** — features that have been renamed, deprecated, or significantly changed
-- **Missing links** — new official docs or blog posts not in the Further Reading section
-
-If there is nothing new or everything is already up to date, stop here and report that no updates are needed.
-
-## Step 3 — Update the Learning Hub
-
-If updates are needed, make a decision on whether a new page needs to be added (e.g., for a major new feature) or if existing pages can be updated with new sections.
-
-### For new pages:
-
-A new page should be created for major features or capabilities that warrant their own documentation (e.g., a new feature of Copilot, a new pattern for working with Copilot, etc.).
-
-To create a new page:
-
-1. Create a new markdown file in the appropriate section of `website/learning-hub` (e.g., `website/learning-hub/agents/new-agent.md`).
-2. Write a summary of the new feature, how it works, and its use cases.
-3. Add a "Further Reading" section with links to official documentation, blog posts, and relevant community resources.
-
-### For updates to existing pages:
-
-If the new information can be added to existing pages, edit those pages to include refinements, new sections, or updated information as needed. Make sure to update any relevant links in the "Further Reading" sections.
-
-## Step 4 — Open a pull request
-
-Create a pull request with your changes, using the `main` branch as the base branch. The PR title should summarize what was updated (e.g., "Add/plan command and model marketplace documentation"). The PR body should list:
-
-1. What new features or changes were found
-2. What sections of the guide were updated
-3. Links to the source announcements
-
-The PR should target the `main` branch and include the labels `automated-update` and `copilot-updates`.
diff --git a/.github/workflows/resource-staleness-report.lock.yml b/.github/workflows/resource-staleness-report.lock.yml
deleted file mode 100644
index 748b7d4366..0000000000
--- a/.github/workflows/resource-staleness-report.lock.yml
+++ /dev/null
@@ -1,1566 +0,0 @@
-# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a2b3254dba98f5d3a46841e2f2c939a6909c8045158ab079219fa141cbc9aa82","body_hash":"b6123a891bb5638279639c6416b2248e0cd931c198b20818e4b5269887a5dde4","compiler_version":"v0.81.6","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.65"}}
-# gh-aw-manifest: {"version":1,"secrets":["GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","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":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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"},{"repo":"github/gh-aw-actions/setup","sha":"ba6380cc6e5be5d21677bebe04d52fb48e3abec7","version":"v0.81.6"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11","digest":"sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11","digest":"sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11","digest":"sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.30","digest":"sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]}
-# This file was automatically generated by gh-aw (v0.81.6). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md
-#
-# ___ _ _
-# / _ \ | | (_)
-# | |_| | __ _ ___ _ __ | |_ _ ___
-# | _ |/ _` |/ _ \ '_ \| __| |/ __|
-# | | | | (_| | __/ | | | |_| | (__
-# \_| |_/\__, |\___|_| |_|\__|_|\___|
-# __/ |
-# _ _ |___/
-# | | | | / _| |
-# | | | | ___ _ __ _ __| |_| | _____ ____
-# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
-# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
-# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
-#
-#
-# To update this file, edit the corresponding .md file and run:
-# gh aw compile
-# Not all edits will cause changes to this file.
-#
-# For more information: https://github.github.com/gh-aw/introduction/overview/
-#
-# Weekly report identifying stale and aging resources across agents, prompts, instructions, hooks, and skills folders
-#
-# Secrets used:
-# - GH_AW_GITHUB_MCP_SERVER_TOKEN
-# - GH_AW_GITHUB_TOKEN
-# - GITHUB_TOKEN
-#
-# Custom actions used:
-# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
-# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
-# - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
-# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
-# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
-# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
-# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
-# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
-# - github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
-#
-# Container images used:
-# - ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7
-# - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d
-# - ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d
-# - ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be
-# - ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b
-# - ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036
-
-name: "Resource Staleness Report"
-on:
- schedule:
- - cron: "5 3 * * 5"
- # Friendly format: weekly (scattered)
- workflow_dispatch:
- inputs:
- aw_context:
- default: ""
- description: "Agent caller context (used internally by Agentic Workflows)."
- required: false
- type: string
-
-permissions: {}
-
-concurrency:
- group: "gh-aw-${{ github.workflow }}"
-
-run-name: "Resource Staleness Report"
-
-jobs:
- activation:
- runs-on: ubuntu-slim
- permissions:
- actions: read
- contents: read
- env:
- GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- comment_id: ""
- comment_repo: ""
- daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }}
- daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }}
- daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }}
- engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
- lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
- model: ${{ steps.generate_aw_info.outputs.model }}
- setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
- setup-span-id: ${{ steps.setup.outputs.span-id }}
- setup-trace-id: ${{ steps.setup.outputs.trace-id }}
- stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/resource-staleness-report.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Generate agentic run info
- id: generate_aw_info
- env:
- GH_AW_INFO_ENGINE_ID: "copilot"
- GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
- GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AGENT_VERSION: "1.0.65"
- GH_AW_INFO_CLI_VERSION: "v0.81.6"
- GH_AW_INFO_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_INFO_EXPERIMENTAL: "false"
- GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
- GH_AW_INFO_STAGED: "false"
- GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
- GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_AWMG_VERSION: ""
- GH_AW_INFO_FIREWALL_TYPE: "squid"
- GH_AW_COMPILED_STRICT: "true"
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/generate_aw_info.cjs');
- await main(core, context);
- - name: Restore daily AIC usage cache
- id: restore-daily-aic-cache
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- continue-on-error: true
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-resourcestalenessreport-${{ github.run_id }}
- restore-keys: agentic-workflow-usage-resourcestalenessreport-
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Restore daily AIC usage cache (artifact fallback)
- id: restore-daily-aic-cache-fallback
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }}
- GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- 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/restore_aic_usage_cache_fallback.cjs');
- await main();
- - name: Check daily workflow token guardrail
- id: daily-effective-workflow-guardrail
- if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_WORKFLOW_ID: "resource-staleness-report"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }}
- GH_AW_HAS_SLASH_COMMAND: "false"
- GH_AW_HAS_LABEL_COMMAND: "false"
- GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- 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/check_daily_aic_workflow_guardrail.cjs');
- await main();
- - name: Checkout .github and .agents folders
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- sparse-checkout: |
- .github
- .agents
- .antigravity
- .claude
- .codex
- .crush
- .gemini
- .opencode
- .pi
- sparse-checkout-cone-mode: true
- fetch-depth: 1
- - name: Save agent config folders for base branch restoration
- env:
- GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
- GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
- - name: Check workflow lock file
- id: check-lock-file
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_WORKFLOW_FILE: "resource-staleness-report.lock.yml"
- GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
- 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/check_workflow_timestamp_api.cjs');
- await main();
- - name: Check compile-agentic version
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_COMPILED_VERSION: "v0.81.6"
- 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/check_version_updates.cjs');
- await main();
- - name: Log runtime features
- if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh"
- - name: Create prompt with built-in context
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- # poutine:ignore untrusted_checkout_exec
- run: |
- bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
- {
- cat << 'GH_AW_PROMPT_8f9e22362a070b4b_EOF'
-
- GH_AW_PROMPT_8f9e22362a070b4b_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_8f9e22362a070b4b_EOF'
-
- Tools: create_issue, missing_tool, missing_data, noop
-
- GH_AW_PROMPT_8f9e22362a070b4b_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_8f9e22362a070b4b_EOF'
-
- The following GitHub context information is available for this workflow:
- {{#if github.actor}}
- - **actor**: __GH_AW_GITHUB_ACTOR__
- {{/if}}
- {{#if github.repository}}
- - **repository**: __GH_AW_GITHUB_REPOSITORY__
- {{/if}}
- {{#if github.workspace}}
- - **workspace**: __GH_AW_GITHUB_WORKSPACE__
- {{/if}}
- {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
- - **issue-number**: #__GH_AW_EXPR_802A9F6A__
- {{/if}}
- {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
- - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
- {{/if}}
- {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
- - **pull-request-number**: #__GH_AW_EXPR_463A214A__
- {{/if}}
- {{#if github.event.comment.id || github.aw.context.comment_id}}
- - **comment-id**: __GH_AW_EXPR_FF1D34CE__
- {{/if}}
- {{#if github.run_id}}
- - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
- {{/if}}
-
-
- GH_AW_PROMPT_8f9e22362a070b4b_EOF
- cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_8f9e22362a070b4b_EOF'
-
- {{#runtime-import .github/workflows/resource-staleness-report.md}}
- GH_AW_PROMPT_8f9e22362a070b4b_EOF
- } > "$GH_AW_PROMPT"
- - name: Interpolate variables and render templates
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_ENGINE_ID: "copilot"
- 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/interpolate_prompt.cjs');
- await main();
- - name: Substitute placeholders
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
- GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
- with:
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io, getOctokit);
-
- const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
-
- // Call the substitution function
- return await substitutePlaceholders({
- file: process.env.GH_AW_PROMPT,
- substitutions: {
- GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
- GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
- GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
- GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
- GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
- GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
- GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
- GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
- GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
- }
- });
- - name: Validate prompt placeholders
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
- - name: Print prompt
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- # poutine:ignore untrusted_checkout_exec
- run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
- - name: Upload activation artifact
- if: success()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: activation
- include-hidden-files: true
- path: |
- /tmp/gh-aw/aw_info.json
- /tmp/gh-aw/models.json
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/aw-prompts/prompt-template.txt
- /tmp/gh-aw/aw-prompts/prompt-import-tree.json
- /tmp/gh-aw/github_rate_limits.jsonl
- /tmp/gh-aw/base
- /tmp/gh-aw/.github/agents
- /tmp/gh-aw/.github/skills
- if-no-files-found: ignore
- retention-days: 1
-
- agent:
- needs: activation
- if: needs.activation.outputs.daily_ai_credits_exceeded != 'true'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- copilot-requests: write
- concurrency:
- group: "gh-aw-copilot-${{ github.workflow }}"
- queue: max
- env:
- DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
- GH_AW_ASSETS_ALLOWED_EXTS: ""
- GH_AW_ASSETS_BRANCH: ""
- GH_AW_ASSETS_MAX_SIZE_KB: 0
- GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_WORKFLOW_ID_SANITIZED: resourcestalenessreport
- outputs:
- agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
- ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }}
- aic: ${{ steps.parse-mcp-gateway.outputs.aic }}
- ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }}
- checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
- effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
- has_patch: ${{ steps.collect_output.outputs.has_patch }}
- inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
- mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
- model: ${{ needs.activation.outputs.model }}
- model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
- output: ${{ steps.collect_output.outputs.output }}
- output_types: ${{ steps.collect_output.outputs.output_types }}
- setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
- setup-span-id: ${{ steps.setup.outputs.span-id }}
- setup-trace-id: ${{ steps.setup.outputs.trace-id }}
- unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/resource-staleness-report.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Set runtime paths
- id: set-runtime-paths
- run: |
- {
- echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
- echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
- echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
- } >> "$GITHUB_OUTPUT"
- - name: Checkout repository
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- - name: Create gh-aw temp directory
- run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
- - name: Configure gh CLI for GitHub Enterprise
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
- env:
- GH_TOKEN: ${{ github.token }}
- - name: Configure Git credentials
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_TOKEN: ${{ github.token }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Checkout PR branch
- id: checkout-pr
- if: |
- github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request'
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io, getOctokit);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
- await main();
- - name: Install GitHub Copilot CLI
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11
- - name: Determine automatic lockdown mode for GitHub MCP Server
- id: determine-automatic-lockdown
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
- env:
- GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- with:
- script: |
- const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
- await determineAutomaticLockdown(github, context, core);
- - name: Download activation artifact
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: activation
- path: /tmp/gh-aw
- - name: Restore agent config folders from base branch
- if: steps.checkout-pr.outcome == 'success'
- env:
- GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
- GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
- - name: Restore inline sub-agents from activation artifact
- env:
- GH_AW_SUB_AGENT_DIR: ".github/agents"
- GH_AW_SUB_AGENT_EXT: ".agent.md"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
- - name: Restore inline skills from activation artifact
- env:
- GH_AW_SKILL_DIR: ".github/skills"
- run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
- - name: Download container images
- run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d ghcr.io/github/gh-aw-mcpg:v0.3.30@sha256:35625d1a2269b1238606078c879f59a91cffc4ac33eb54bf39c6418822c1a8be ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036
- - name: Generate Safe Outputs Config
- run: |
- 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_a37618c617d2731e_EOF'
- {"create_issue":{"close_older_issues":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_a37618c617d2731e_EOF
- - name: Generate Safe Outputs Tools
- env:
- GH_AW_TOOLS_META_JSON: |
- {
- "description_suffixes": {
- "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created."
- },
- "repo_params": {},
- "dynamic_tools": []
- }
- GH_AW_VALIDATION_JSON: |
- {
- "create_issue": {
- "defaultMax": 1,
- "fields": {
- "body": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000,
- "minLength": 20
- },
- "fields": {
- "type": "array"
- },
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
- },
- "parent": {
- "issueOrPRNumber": true
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- },
- "temporary_id": {
- "type": "string"
- },
- "title": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "missing_data": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "context": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "data_type": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "reason": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- }
- }
- },
- "missing_tool": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 512
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "tool": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "noop": {
- "defaultMax": 1,
- "fields": {
- "message": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- }
- }
- },
- "report_incomplete": {
- "defaultMax": 5,
- "fields": {
- "details": {
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 1024
- }
- }
- }
- }
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/generate_safe_outputs_tools.cjs');
- await main();
- - name: Start MCP Gateway
- id: start-mcp-gateway
- env:
- GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }}
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }}
- GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
- GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- set -eo pipefail
- mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
-
- # Export gateway environment variables for MCP config and gateway script
- export MCP_GATEWAY_PORT="8080"
- export MCP_GATEWAY_DOMAIN="host.docker.internal"
- export MCP_GATEWAY_HOST_DOMAIN="localhost"
- MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
- echo "::add-mask::${MCP_GATEWAY_API_KEY}"
- export MCP_GATEWAY_API_KEY
- export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
- mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
- export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
- export DEBUG="*"
-
- export GH_AW_ENGINE="copilot"
- MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
- MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
- case "${DOCKER_HOST:-}" in
- unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
- /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
- * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
- esac
- 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 --name awmg-mcpg --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_POLICY_ALLOW_CREATE_PULL_REQUEST -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 RUNNER_TEMP -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 -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.30'
-
- mkdir -p "$HOME/.copilot"
- GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_532027a3cd6cbb4c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
- {
- "mcpServers": {
- "github": {
- "type": "stdio",
- "container": "ghcr.io/github/github-mcp-server:v1.4.0",
- "env": {
- "GITHUB_HOST": "${GITHUB_SERVER_URL}",
- "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}",
- "GITHUB_READ_ONLY": "1",
- "GITHUB_TOOLSETS": "repos"
- },
- "guard-policies": {
- "allow-only": {
- "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
- "repos": "$GITHUB_MCP_GUARD_REPOS"
- }
- }
- },
- "safeoutputs": {
- "type": "stdio",
- "container": "ghcr.io/github/gh-aw-node",
- "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"],
- "args": ["-w", "\${GITHUB_WORKSPACE}"],
- "entrypoint": "sh",
- "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"],
- "env": {
- "DEBUG": "*",
- "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}",
- "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}",
- "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}",
- "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}",
- "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}",
- "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}",
- "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}",
- "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}",
- "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}",
- "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}",
- "GITHUB_TOKEN": "\${GITHUB_TOKEN}",
- "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}",
- "RUNNER_TEMP": "\${RUNNER_TEMP}"
- },
- "guard-policies": {
- "write-sink": {
- "accept": [
- "*"
- ]
- }
- }
- }
- },
- "gateway": {
- "port": $MCP_GATEWAY_PORT,
- "domain": "${MCP_GATEWAY_DOMAIN}",
- "apiKey": "${MCP_GATEWAY_API_KEY}",
- "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
- }
- }
- GH_AW_MCP_CONFIG_532027a3cd6cbb4c_EOF
- - name: Mount MCP servers as CLIs
- id: mount-mcp-clis
- continue-on-error: true
- env:
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
- await main();
- - name: Clean credentials
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
- - name: Audit pre-agent workspace
- id: pre_agent_audit
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
- - name: Execute GitHub Copilot CLI
- id: agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
- trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
- mkdir -p "$HOME/.copilot"
- printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
- export XDG_CONFIG_HOME="$HOME"
- export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json"
- touch /tmp/gh-aw/agent-step-summary.md
- GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
- export GH_AW_NODE_BIN
- export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
- (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
- GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}"
- printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.snapcraft.io\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"crl.geotrust.com\",\"crl.globalsign.com\",\"crl.identrust.com\",\"crl.sectigo.com\",\"crl.thawte.com\",\"crl.usertrust.com\",\"crl.verisign.com\",\"crl3.digicert.com\",\"crl4.digicert.com\",\"crls.ssl.com\",\"github.com\",\"host.docker.internal\",\"json-schema.org\",\"json.schemastore.org\",\"keyserver.ubuntu.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"telemetry.enterprise.githubcopilot.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
- export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json"
- GH_AW_DOCKER_HOST=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST="${DOCKER_HOST}"
- fi
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
- GH_AW_CHROOT_BINARIES_SOURCE_PATH=/tmp/gh-aw GH_AW_CHROOT_IDENTITY_HOME=/tmp/gh-aw/home node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs"
- fi
- GH_AW_TOOL_CACHE_MOUNT=""
- GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"
- if [ -d "$GH_AW_TOOL_CACHE" ]; then
- if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
- GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
- fi
- fi
- # shellcheck disable=SC1003,SC2086
- sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
- -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
- env:
- AWF_REFLECT_ENABLED: 1
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
- COPILOT_GITHUB_TOKEN: ${{ github.token }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_LLM_PROVIDER: github
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
- GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
- GH_AW_PHASE: agent
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_TIMEOUT_MINUTES: 20
- GH_AW_VERSION: v0.81.6
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- RUNNER_TEMP: ${{ runner.temp }}
- S2STOKENS: true
- TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- - name: Detect agent errors
- if: always()
- id: detect-agent-errors
- continue-on-error: true
- run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
- - name: Configure Git credentials
- env:
- GITHUB_REPOSITORY: ${{ github.repository }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_TOKEN: ${{ github.token }}
- run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- - name: Copy Copilot session state files to logs
- if: always()
- continue-on-error: true
- run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
- - name: Stop MCP Gateway
- if: always()
- continue-on-error: true
- env:
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
- run: |
- bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- - name: Redact secrets in logs
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/redact_secrets.cjs');
- await main();
- env:
- GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
- SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Append agent step summary
- if: always()
- run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
- - name: Copy Safe Outputs
- if: always()
- env:
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- run: |
- mkdir -p /tmp/gh-aw
- cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
- - name: Ingest agent output
- id: collect_output
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- 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/collect_ndjson_output.cjs');
- await main();
- - name: Parse agent logs for step summary
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
- 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/parse_copilot_log.cjs');
- await main();
- - name: Parse MCP Gateway logs for step summary
- if: always()
- id: parse-mcp-gateway
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/parse_mcp_gateway_log.cjs');
- await main();
- - name: Print firewall logs
- if: always()
- continue-on-error: true
- env:
- AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
- run: |
- # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts
- # AWF runs with sudo, creating files owned by root
- sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
- # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
- if command -v awf &> /dev/null; then
- awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
- else
- echo 'AWF binary not installed, skipping firewall log summary'
- fi
- - name: Parse token usage for step summary
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/parse_token_usage.cjs');
- await main();
- - name: Print AWF reflect summary
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- 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/awf_reflect_summary.cjs');
- await main();
- - name: Write agent output placeholder if missing
- if: always()
- run: |
- if [ ! -f /tmp/gh-aw/agent_output.json ]; then
- echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
- fi
- - name: Upload agent artifacts
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: agent
- path: |
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/sandbox/agent/logs/
- /tmp/gh-aw/redacted-urls.log
- /tmp/gh-aw/mcp-logs/
- /tmp/gh-aw/agent_usage.json
- /tmp/gh-aw/agent-stdio.log
- /tmp/gh-aw/pre-agent-audit.txt
- /tmp/gh-aw/agent/
- /tmp/gh-aw/github_rate_limits.jsonl
- /tmp/gh-aw/safeoutputs.jsonl
- /tmp/gh-aw/agent_output.json
- /tmp/gh-aw/aw-*.patch
- /tmp/gh-aw/aw-*.bundle
- /tmp/gh-aw/awf-config.json
- /tmp/gh-aw/sandbox/firewall/logs/
- /tmp/gh-aw/sandbox/firewall/audit/
- /tmp/gh-aw/sandbox/firewall/awf-reflect.json
- if-no-files-found: ignore
-
- conclusion:
- needs:
- - activation
- - agent
- - detection
- - safe_outputs
- if: >
- always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
- needs.activation.outputs.stale_lock_file_failed == 'true' || needs.activation.outputs.daily_ai_credits_exceeded == 'true')
- runs-on: ubuntu-slim
- permissions:
- contents: read
- issues: write
- concurrency:
- group: "gh-aw-conclusion-resource-staleness-report"
- cancel-in-progress: false
- queue: max
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
- noop_message: ${{ steps.noop.outputs.noop_message }}
- tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
- total_count: ${{ steps.missing_tool.outputs.total_count }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/resource-staleness-report.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Collect usage artifact files
- if: always()
- continue-on-error: true
- run: |
- mkdir -p /tmp/gh-aw/usage/agent /tmp/gh-aw/usage/detection
- echo "Usage artifact source file status:"
- for file in /tmp/gh-aw/aw_info.json /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/agent_usage.json /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl; do
- [ -f "$file" ] && echo "FOUND: $file" || echo "MISSING: $file"
- done
- [ -f /tmp/gh-aw/aw_info.json ] && cp /tmp/gh-aw/aw_info.json /tmp/gh-aw/usage/aw_info.json || true
- [ -f /tmp/gh-aw/aw-info.jsonl ] && cp /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/usage/aw-info.jsonl || true
- [ -f /tmp/gh-aw/agent_usage.json ] && cp /tmp/gh-aw/agent_usage.json /tmp/gh-aw/usage/agent_usage.json || true
- [ -f /tmp/gh-aw/agent_usage.jsonl ] && cp /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/usage/agent_usage.jsonl || true
- [ -f /tmp/gh-aw/detection_usage.jsonl ] && cp /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/usage/detection_usage.jsonl || true
- [ -f /tmp/gh-aw/github_rate_limits.jsonl ] && cp /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/usage/github_rate_limits.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -s /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true
- [ -f /tmp/gh-aw/usage/agent/token_usage.jsonl ] || : > /tmp/gh-aw/usage/agent/token_usage.jsonl
- [ -f /tmp/gh-aw/usage/detection/token_usage.jsonl ] || : > /tmp/gh-aw/usage/detection/token_usage.jsonl
- mkdir -p /tmp/gh-aw/usage/activity
- node ${{ runner.temp }}/gh-aw/actions/generate_usage_activity_summary.cjs
- find /tmp/gh-aw/usage -type f -print | sort
- - name: Upload usage artifact
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: usage
- path: |
- /tmp/gh-aw/usage/aw_info.json
- /tmp/gh-aw/usage/aw-info.jsonl
- /tmp/gh-aw/usage/agent_usage.json
- /tmp/gh-aw/usage/agent_usage.jsonl
- /tmp/gh-aw/usage/detection_usage.jsonl
- /tmp/gh-aw/usage/github_rate_limits.jsonl
- /tmp/gh-aw/usage/agent/token_usage.jsonl
- /tmp/gh-aw/usage/detection/token_usage.jsonl
- /tmp/gh-aw/usage/activity/summary.json
- if-no-files-found: ignore
- - name: Restore daily AIC usage cache
- id: restore-daily-aic-cache-conclusion
- if: always()
- continue-on-error: true
- uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-resourcestalenessreport-${{ github.run_id }}
- restore-keys: agentic-workflow-usage-resourcestalenessreport-
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Write daily AIC usage cache entry
- id: write-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- with:
- github-token: ${{ github.token }}
- script: |
- const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context);
- const { main } = require('${{ runner.temp }}/gh-aw/actions/write_daily_aic_usage_cache.cjs');
- await main();
- - name: Save daily AIC usage cache
- id: save-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
- with:
- key: agentic-workflow-usage-resourcestalenessreport-${{ github.run_id }}
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- - name: Upload daily AIC usage cache artifact
- id: upload-daily-aic-cache
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: aic-usage-cache
- path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl
- if-no-files-found: ignore
- retention-days: 7
- - name: Process no-op messages
- id: noop
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: "1"
- GH_AW_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/resource-staleness-report.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_NOOP_REPORT_AS_ISSUE: "true"
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
- GH_AW_WORKFLOW_ID: "resource-staleness-report"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_noop_message.cjs');
- await main();
- - name: Log detection run
- id: detection_runs
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/resource-staleness-report.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
- GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_detection_runs.cjs');
- await main();
- - name: Record missing tool
- id: missing_tool
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
- GH_AW_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/resource-staleness-report.md"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/missing_tool.cjs');
- await main();
- - name: Record incomplete
- id: report_incomplete
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
- GH_AW_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/resource-staleness-report.md"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/report_incomplete_handler.cjs');
- await main();
- - name: Handle agent failure
- id: handle_agent_failure
- if: always()
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/resource-staleness-report.md"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_WORKFLOW_ID: "resource-staleness-report"
- GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
- GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
- GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }}
- GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }}
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
- GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
- GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
- GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
- GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
- GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
- GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
- GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
- GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }}
- GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }}
- GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }}
- GH_AW_GROUP_REPORTS: "false"
- GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
- GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
- GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
- GH_AW_TIMEOUT_MINUTES: "20"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/handle_agent_failure.cjs');
- await main();
-
- detection:
- needs:
- - activation
- - agent
- if: always() && needs.agent.result != 'skipped'
- runs-on: ubuntu-latest
- permissions:
- contents: read
- copilot-requests: write
- env:
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- outputs:
- aic: ${{ steps.parse_detection_token_usage.outputs.aic }}
- detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
- detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
- detection_success: ${{ steps.detection_conclusion.outputs.success }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/resource-staleness-report.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Checkout repository for patch context
- if: needs.agent.outputs.has_patch == 'true'
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- with:
- persist-credentials: false
- # --- Threat Detection ---
- - name: Clean stale firewall files from agent artifact
- run: |
- rm -rf /tmp/gh-aw/sandbox/firewall/logs
- rm -rf /tmp/gh-aw/sandbox/firewall/audit
- - name: Download container images
- run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.11@sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7 ghcr.io/github/gh-aw-firewall/api-proxy:0.27.11@sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d ghcr.io/github/gh-aw-firewall/squid:0.27.11@sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d
- - name: Check if detection needed
- id: detection_guard
- if: always()
- env:
- OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- run: |
- if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
- echo "run_detection=true" >> "$GITHUB_OUTPUT"
- echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
- else
- echo "run_detection=false" >> "$GITHUB_OUTPUT"
- echo "Detection skipped: no agent outputs or patches to analyze"
- fi
- - name: Clear MCP Config for detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
- rm -f "$HOME/.copilot/mcp-config.json"
- rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
- - name: Prepare threat detection files
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
- rm -f /tmp/gh-aw/agent_usage.json
- cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
- if [ ! -s /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt ]; then
- echo "::warning::ERR_VALIDATION: Missing or empty detection context prompt at /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt. Ensure the agent artifact includes /tmp/gh-aw/aw-prompts/prompt.txt. Detection will continue with fallback workflow context."
- fi
- cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
- for f in /tmp/gh-aw/aw-*.patch; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- for f in /tmp/gh-aw/aw-*.bundle; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- echo "Prepared threat detection files:"
- ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- - name: Setup threat detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- WORKFLOW_NAME: "Resource Staleness Report"
- WORKFLOW_DESCRIPTION: "Weekly report identifying stale and aging resources across agents, prompts, instructions, hooks, and skills folders"
- HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
- 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/setup_threat_detection.cjs');
- await main();
- - name: Ensure threat-detection directory and log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection
- touch /tmp/gh-aw/threat-detection/detection.log
- - name: Setup Node.js
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
- with:
- node-version: '24'
- package-manager-cache: false
- - name: Install GitHub Copilot CLI
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.65
- env:
- GH_HOST: github.com
- - name: Install AWF binary
- run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.11
- - name: Execute GitHub Copilot CLI
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- continue-on-error: true
- id: detection_agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
- trap 'rm -f "$HOME/.copilot/settings.json"' EXIT
- mkdir -p "$HOME/.copilot"
- printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json"
- export XDG_CONFIG_HOME="$HOME"
- touch /tmp/gh-aw/agent-step-summary.md
- GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
- export GH_AW_NODE_BIN
- export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
- (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
- GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}"
- printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.11/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.11,squid=sha256:ff27ea0525ad953a6adee28a5fbe9d2e22be47dbec755c15767af4ea3f91df7d,agent=sha256:979723c628182da7729333f2208bb249fd25ddee579645cf9a3892d681a929c7,api-proxy=sha256:807e4831999b44513b0a66e5859d478dc4da7ae74ab1918cec967d513f95bf9d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
- export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json"
- GH_AW_DOCKER_HOST=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST="${DOCKER_HOST}"
- fi
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
- if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
- GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
- _GH_AW_CHROOT_JSON=$(jq -c --arg src /tmp/gh-aw --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home /tmp/gh-aw/home '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; }
- printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json"
- printf '%s\n' "$_GH_AW_CHROOT_JSON" > "/tmp/gh-aw/awf-config.json"
- fi
- GH_AW_TOOL_CACHE_MOUNT=""
- GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"
- if [ -d "$GH_AW_TOOL_CACHE" ]; then
- if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then
- GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro"
- fi
- fi
- # shellcheck disable=SC1003,SC2086
- sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
- -- /bin/bash -c 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
- env:
- AWF_REFLECT_ENABLED: 1
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
- COPILOT_GITHUB_TOKEN: ${{ github.token }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }}
- GH_AW_LLM_PROVIDER: github
- GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}
- GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
- GH_AW_PHASE: detection
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_TIMEOUT_MINUTES: 20
- GH_AW_VERSION: v0.81.6
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_AW: true
- GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
- GITHUB_WORKSPACE: ${{ github.workspace }}
- GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_AUTHOR_NAME: github-actions[bot]
- GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
- GIT_COMMITTER_NAME: github-actions[bot]
- RUNNER_TEMP: ${{ runner.temp }}
- S2STOKENS: true
- TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- - name: Parse threat detection token usage for step summary
- id: parse_detection_token_usage
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage
- 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/parse_token_usage.cjs');
- await main();
- - name: Upload threat detection log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: detection
- path: /tmp/gh-aw/threat-detection/detection.log
- if-no-files-found: ignore
- - name: Parse and conclude threat detection
- id: detection_conclusion
- if: always()
- continue-on-error: true
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
- DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
- GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
- with:
- script: |
- try {
- 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/parse_threat_detection_results.cjs');
- await main();
- } catch (loadErr) {
- const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
- const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
- const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
- core.error(msg);
- core.setOutput('reason', 'parse_error');
- if (continueOnError && !detectionExecutionFailed) {
- core.warning('\u26A0\uFE0F ' + msg);
- core.setOutput('conclusion', 'warning');
- core.setOutput('success', 'false');
- } else {
- core.setOutput('conclusion', 'failure');
- core.setOutput('success', 'false');
- core.setFailed(msg);
- }
- }
-
- safe_outputs:
- needs:
- - activation
- - agent
- - detection
- if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
- runs-on: ubuntu-slim
- permissions:
- contents: read
- issues: write
- timeout-minutes: 45
- env:
- GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_AIC: ${{ needs.agent.outputs.aic }}
- GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
- GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/resource-staleness-report"
- GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
- GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
- GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
- GH_AW_ENGINE_VERSION: "1.0.65"
- GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
- GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
- GH_AW_WORKFLOW_ID: "resource-staleness-report"
- GH_AW_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/resource-staleness-report.md"
- outputs:
- code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
- code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
- create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
- create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
- process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
- process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
- steps:
- - name: Setup Scripts
- id: setup
- uses: github/gh-aw-actions/setup@ba6380cc6e5be5d21677bebe04d52fb48e3abec7 # v0.81.6
- with:
- destination: ${{ runner.temp }}/gh-aw/actions
- job-name: ${{ github.job }}
- trace-id: ${{ needs.activation.outputs.setup-trace-id }}
- parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
- env:
- GH_AW_SETUP_WORKFLOW_NAME: "Resource Staleness Report"
- GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/resource-staleness-report.lock.yml@${{ github.ref }}
- GH_AW_INFO_VERSION: "1.0.65"
- GH_AW_INFO_AWF_VERSION: "v0.27.11"
- GH_AW_INFO_ENGINE_ID: "copilot"
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- with:
- name: agent
- path: /tmp/gh-aw/
- - name: Setup agent output environment variable
- id: setup-agent-output-env
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/
- find "/tmp/gh-aw/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
- - name: Configure GH_HOST for enterprise compatibility
- id: ghes-host-config
- shell: bash
- run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
- # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
- # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
- GH_HOST="${GITHUB_SERVER_URL#https://}"
- GH_HOST="${GH_HOST#http://}"
- echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- - name: Process Safe Outputs
- id: process_safe_outputs
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
- env:
- GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
- GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"close_older_issues\":true,\"max\":1},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- 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/safe_output_handler_manager.cjs');
- await main();
- - name: Upload Safe Outputs Items
- if: always()
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
- with:
- name: safe-outputs-items
- path: |
- /tmp/gh-aw/safe-output-items.jsonl
- /tmp/gh-aw/temporary-id-map.json
- if-no-files-found: ignore
-
diff --git a/.github/workflows/resource-staleness-report.md b/.github/workflows/resource-staleness-report.md
deleted file mode 100644
index 07b8d32a6c..0000000000
--- a/.github/workflows/resource-staleness-report.md
+++ /dev/null
@@ -1,141 +0,0 @@
----
-description: Weekly report identifying stale and aging resources across agents, prompts, instructions, hooks, and skills folders
-on:
- schedule: weekly
-permissions:
- contents: read
- copilot-requests: write
-tools:
- github:
- toolsets: [repos]
-safe-outputs:
- create-issue:
- max: 1
- close-older-issues: true
- noop:
----
-
-# Resource Staleness Report
-
-You are an AI agent that audits the resources in this repository to identify ones that may need attention based on how long it has been since their last meaningful change.
-
-## Your Task
-
-Analyze all files in the following directories to determine when each file last had a **major** (substantive) change committed:
-
-- `agents/` (`.agent.md` files)
-- `prompts/` (`.prompt.md` files)
-- `instructions/` (`.instructions.md` files)
-- `hooks/` (folders — check the folder's files)
-- `skills/` (folders — check the folder's files)
-
-### What Counts as a Major Change
-
-A **major** change is one that modifies the actual content or behavior of the resource. Use `git log` with `--diff-filter=M` and `--follow` to find when files were last substantively modified.
-
-**Ignore** the following — these are NOT major changes:
-
-- File renames or moves (`R` status in git)
-- Whitespace-only or line-ending fixes
-- Commits whose messages indicate bulk formatting, renaming, or automated updates (e.g., "fix line endings", "rename files", "bulk update", "normalize")
-- Changes that only touch frontmatter metadata without changing the instructions/content body
-
-### How to Determine Last Major Change
-
-For each resource file, run:
-
-```bash
-git log -1 --format="%H %ai" --diff-filter=M --
-```
-
-This gives the most recent commit that **modified** (not just renamed) the file. If a file has never been modified (only added), use the commit that added it:
-
-```bash
-git log -1 --format="%H %ai" --diff-filter=A --
-```
-
-For hook and skill folders, check all files within the folder and use the **most recent** major change date across any file in that folder.
-
-### Classification
-
-Based on today's date, classify each resource:
-
-- **🔴 Stale** — last major change was **more than 30 days ago**
-- **🟡 Aging** — last major change was **between 14 and 30 days ago**
-- Resources changed within the last 14 days are **fresh** and should NOT be listed
-
-### Deeper Review for the Oldest Stale Resources
-
-After producing the age-based inventory, perform a **content review** of the **10 oldest stale resources**.
-
-For each of those 10 resources:
-
-1. Read the current file contents (for hooks and skills, inspect the folder's primary instruction file and any bundled files that materially affect behavior).
-2. Determine whether the resource is:
- - **Materially stale** — guidance is outdated, incomplete in an important way, or references older patterns that should be replaced
- - **Problematic** — guidance is misleading, harmful, overly broad, or likely to produce poor results even if it is not version-stale
- - **Mostly current** — old by date, but still broadly aligned with current best practices
-3. Identify the most important concrete issues. Focus on substantive problems, not minor wording nits.
-4. Recommend what should be done next:
- - **Immediate rework/removal**
- - **Targeted refresh**
- - **Minor modernization**
- - **No urgent change needed**
-
-Do **not** assume that old automatically means bad. The deeper review should separate truly risky resources from ones that are simply old.
-
-### Output Format
-
-Create an issue with the title: `📋 Resource Staleness Report`
-
-Organize the issue body as follows:
-
-```markdown
-### Summary
-
-- **Stale (>30 days):** X resources
-- **Aging (14–30 days):** Y resources
-- **Fresh (<14 days):** Z resources (not listed below)
-
-### 🔴 Stale Resources (>30 days since last major change)
-
-| Resource | Type | Last Major Change | Days Ago |
-|----------|------|-------------------|----------|
-| `agents/example.agent.md` | Agent | 2025-01-15 | 45 |
-
-### 🟡 Aging Resources (14–30 days since last major change)
-
-| Resource | Type | Last Major Change | Days Ago |
-|----------|------|-------------------|----------|
-| `prompts/example.prompt.md` | Prompt | 2025-02-01 | 20 |
-
-### Deep Review: 10 Oldest Stale Resources
-
-| Resource | Verdict | Key Problems | Recommended Action |
-|----------|---------|--------------|--------------------|
-| `instructions/example.instructions.md` | Materially stale | References older framework defaults and misses current patterns | Targeted refresh |
-
-### Priority Actions
-
-1. Immediate rework/removal: `resource-a`, `resource-b`
-2. Targeted refresh: `resource-c`, `resource-d`
-3. Minor modernization: `resource-e`
-4. No urgent change needed: `resource-f`
-```
-
-If a category has no resources, include the header with a note: "✅ No resources in this category."
-
-Use `` blocks to collapse sections with more than 15 entries.
-
-## Guidelines
-
-- Process all resource types: agents, prompts, instructions, hooks, and skills.
-- For **hooks** and **skills**, treat the entire folder as one resource. Report it by folder name and use the most recent change date of any file within.
-- Sort tables by "Days Ago" descending (oldest first).
-- After building the stale table, inspect the **10 oldest stale resources** in more depth and include the deeper review section.
-- In the deeper review, prefer **high-signal issues**: outdated version assumptions, deprecated APIs, misleading instructions, harmful heuristics, unsafe defaults, or instructions that are scoped too broadly.
-- If a resource is old but still sound, explicitly say so. The goal is to prioritize maintenance work, not just restate age.
-- Keep the deeper review concise but specific. Each row should explain the main problem and the best next step.
-- If there are no stale or aging resources at all, call the `noop` safe output with the message: "All resources have been updated within the last 14 days. No staleness report needed."
-- Do not include fresh resources in the tables — only mention the count in the summary.
-- Use the `create-issue` safe output to file the report. Previous reports will be automatically closed.
diff --git a/.github/workflows/traffic-reporting.yml b/.github/workflows/traffic-reporting.yml
index d14055c3d4..29547ba063 100644
--- a/.github/workflows/traffic-reporting.yml
+++ b/.github/workflows/traffic-reporting.yml
@@ -60,12 +60,13 @@ jobs:
run: |
echo "📤 Sending traffic data to reporting endpoint..."
- # Validate that REPORTING_URL is set
+ # If no reporting sink is configured, skip this step instead of failing.
+ # Configure vars.REPORTING_POST_URL (and secrets.REPORTING_API_KEY) to enable it.
if [ -z "${REPORTING_URL}" ]; then
- echo "❌ Error: REPORTING_URL environment variable is not set. Please configure vars.REPORTING_POST_URL."
- exit 1
+ echo "⚠️ REPORTING_URL is not set (vars.REPORTING_POST_URL). Skipping traffic data upload."
+ exit 0
fi
-
+
# Validate that REPORTING_URL uses HTTPS
if [[ ! "${REPORTING_URL}" =~ ^https:// ]]; then
echo "❌ Error: REPORTING_URL must start with https:// to ensure secure transmission of sensitive traffic data."
diff --git a/docs/README.skills.md b/docs/README.skills.md
index 46d2bb38bf..6237ad842b 100644
--- a/docs/README.skills.md
+++ b/docs/README.skills.md
@@ -37,6 +37,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
| [agent-governance](../skills/agent-governance/SKILL.md)
`gh skills install github/awesome-copilot agent-governance` | Patterns and techniques for adding governance, safety, and trust controls to AI agent systems. Use this skill when:
- Building AI agents that call external tools (APIs, databases, file systems)
- Implementing policy-based access controls for agent tool usage
- Adding semantic intent classification to detect dangerous prompts
- Creating trust scoring systems for multi-agent workflows
- Building audit trails for agent actions and decisions
- Enforcing rate limits, content filters, or tool restrictions on agents
- Working with any agent framework (PydanticAI, CrewAI, OpenAI Agents, LangChain, AutoGen) | None |
| [agent-owasp-compliance](../skills/agent-owasp-compliance/SKILL.md)
`gh skills install github/awesome-copilot agent-owasp-compliance` | Check any AI agent codebase against the OWASP Agentic Security Initiative (ASI) Top 10 risks.
Use this skill when:
- Evaluating an agent system's security posture before production deployment
- Running a compliance check against OWASP ASI 2026 standards
- Mapping existing security controls to the 10 agentic risks
- Generating a compliance report for security review or audit
- Comparing agent framework security features against the standard
- Any request like "is my agent OWASP compliant?", "check ASI compliance", or "agentic security audit" | None |
| [agent-skill-stack](../skills/agent-skill-stack/SKILL.md)
`gh skills install github/awesome-copilot agent-skill-stack` | Find, evaluate, and assemble the smallest compatible set of AI Agent Skills for an end-to-end natural-language goal. Use when a user wants Skills for a multi-step workflow, asks which Skills fit a project, needs an installed-Skill audit or conflict check, has low Skill recall, wants indirect helpers such as humanizers or compliance checks, or wants a project-specific Skill Stack with controlled installation. Search local Skills, registries, GitHub, and OpenCLI; compare adoption, verified fit, safety, and overlap. Do not use for locating one known or common Skill; use the generic find-skills workflow. | `agents`
`references/discovery-ranking.md`
`references/local-index-and-profiles.md`
`references/security-installation.md`
`references/workflow-model.md`
`scripts/inventory_skills.py`
`scripts/project_profile.py`
`scripts/render_stack_card.py`
`scripts/skill_index.py`
`scripts/stage_install.py` |
+| [agent-skills-setup](../skills/agent-skills-setup/SKILL.md)
`gh skills install github/awesome-copilot agent-skills-setup` | Migrate ALL AI assistant context between IDEs — MCP servers, rules/instructions, skills, slash commands, agents, hooks, and memory. Detects installed IDEs, converts formats across platforms, safely merges without overwriting, verifies results. WHEN TO USE: trigger when the user wants to migrate, move, transfer, copy, convert, sync, or back up AI assistant context between IDEs — e.g. "migrate MCP config", "move skills from Cursor to Claude", "transfer rules from Windsurf to Cursor", "copy agents between IDEs", "sync memory bank", "ai ide migration". | `assets/public-repo-readme-template.md`
`references/ide-paths.json`
`references/ide-registry.md`
`references/openclaw.md`
`references/publishing.md`
`scripts/auto-configure-openclaw-skills.sh`
`scripts/export-public-skill.sh`
`scripts/prepare-clawhub-release.sh`
`scripts/smart-ide-migration.sh`
`scripts/sync-global-skills.sh`
`scripts/test-ide-paths.sh`
`scripts/test-migration.sh`
`scripts/test-openclaw-support.sh`
`scripts/test-smart-ide-migration.sh`
`scripts/update-openclaw-skills.sh`
`scripts/verify-ide-config.sh` |
| [agent-supply-chain](../skills/agent-supply-chain/SKILL.md)
`gh skills install github/awesome-copilot agent-supply-chain` | Verify supply chain integrity for AI agent plugins, tools, and dependencies. Use this skill when:
- Generating SHA-256 integrity manifests for agent plugins or tool packages
- Verifying that installed plugins match their published manifests
- Detecting tampered, modified, or untracked files in agent tool directories
- Auditing dependency pinning and version policies for agent components
- Building provenance chains for agent plugin promotion (dev → staging → production)
- Any request like "verify plugin integrity", "generate manifest", "check supply chain", or "sign this plugin" | None |
| [agentic-eval](../skills/agentic-eval/SKILL.md)
`gh skills install github/awesome-copilot agentic-eval` | Patterns and techniques for evaluating and improving AI agent outputs. Use this skill when:
- Implementing self-critique and reflection loops
- Building evaluator-optimizer pipelines for quality-critical generation
- Creating test-driven code refinement workflows
- Designing rubric-based or LLM-as-judge evaluation systems
- Adding iterative improvement to agent outputs (code, reports, analysis)
- Measuring and improving agent response quality | None |
| [ai-prompt-engineering-safety-review](../skills/ai-prompt-engineering-safety-review/SKILL.md)
`gh skills install github/awesome-copilot ai-prompt-engineering-safety-review` | Comprehensive AI prompt engineering safety review and improvement prompt. Analyzes prompts for safety, bias, security vulnerabilities, and effectiveness while providing detailed improvement recommendations with extensive frameworks, testing methodologies, and educational content. | None |
diff --git a/skills/agent-skills-setup/SKILL.md b/skills/agent-skills-setup/SKILL.md
new file mode 100644
index 0000000000..14c524f769
--- /dev/null
+++ b/skills/agent-skills-setup/SKILL.md
@@ -0,0 +1,375 @@
+---
+name: agent-skills-setup
+version: 0.5.0
+license: MIT
+description: >
+ Migrate ALL AI assistant context between IDEs — MCP servers, rules/instructions,
+ skills, slash commands, agents, hooks, and memory. Detects installed IDEs,
+ converts formats across platforms, safely merges without overwriting, verifies results.
+ WHEN TO USE: trigger when the user wants to migrate, move, transfer, copy, convert,
+ sync, or back up AI assistant context between IDEs — e.g. "migrate MCP config",
+ "move skills from Cursor to Claude", "transfer rules from Windsurf to Cursor",
+ "copy agents between IDEs", "sync memory bank", "ai ide migration".
+triggers:
+ - migrate mcp config
+ - migrate ai ide settings
+ - move skills from cursor to claude
+ - transfer mcp servers between ide
+ - migrate rules from windsurf to cursor
+ - ai ide migration
+ - copy mcp config to another ide
+ - migrate ai assistant context
+ - move skills between ide
+ - migrate memory bank
+---
+
+# AI IDE Context Migration
+
+Migrate AI assistant context (MCP, rules, skills, commands, agents, hooks, memory) between IDEs with format conversion, safe merging, and verification.
+
+> **MCP Protocol**: 2025-11-25 (stable). SSE deprecated; use Streamable HTTP for remote servers.
+> **Full IDE Registry**: Read `references/ide-registry.md` for detailed per-IDE paths of all migration objects.
+
+---
+
+## Migration Objects
+
+| # | Object | Description |
+|---|--------|-------------|
+| 1 | **mcp** | MCP server configurations (stdio/HTTP) |
+| 2 | **rules** | Instructions/rules/context files |
+| 3 | **skills** | SKILL.md skill directories |
+| 4 | **commands** | Slash commands / prompt templates |
+| 5 | **agents** | Subagent definitions |
+| 6 | **hooks** | Lifecycle event hooks |
+| 7 | **memory** | Persistent memory / memory banks / context files |
+
+**Never migrate**: API keys, tokens, OAuth tokens, chat history/transcripts, IDE UI settings, built-in vector indexes, workspace storage, SQLite databases.
+
+---
+
+## Execution Workflow
+
+```
+1. DETECT — Scan filesystem for installed IDEs using detect_dir in IDE Registry
+2. IDENTIFY — Ask user: source IDE(s) and target IDE(s)
+3. SCAN — Read references/ide-registry.md; scan ALL migration objects for source IDE
+4. DRY-RUN — Generate migration preview: list objects, conversion plan, conflicts
+5. CONFIRM — Show preview to user; wait for explicit approval before writing
+6. BACKUP — Create .bak.TIMESTAMP copies of existing target files that will be modified
+7. MIGRATE — Execute migrations with format conversion per Object Conversion Rules
+8. VERIFY — Validate output files parse correctly; run verification commands; report results
+```
+
+**Critical rules**:
+- Default to DRY-RUN. Never write without user confirmation.
+- Always backup before overwriting. Use `.bak.` suffix.
+- Merge, never overwrite. Conflicts renamed to `_migrated`.
+- Blank all secret values in env (keep key names, set values to "").
+- If source or target config is invalid JSON/TOML/YAML, STOP and report.
+
+---
+
+## IDE Quick Reference
+
+Root key and format differences — the most common migration errors:
+
+| IDE | MCP Root Key | Format | Config Path | Key Pitfall |
+|-----|-------------|--------|-------------|-------------|
+| Claude Desktop | `mcpServers` | JSON | `claude_desktop_config.json` | MCP only; no rules/skills |
+| Claude Code | `mcpServers` | JSON | `~/.claude.json` / `.mcp.json` | MCP NOT in settings.json |
+| Cursor | `mcpServers` | JSON | `~/.cursor/mcp.json` / `.cursor/mcp.json` | Skills since v0.44; hooks supported |
+| Cline | `mcpServers` | JSON | globalStorage path (VS Code) | CLI uses `~/.cline/mcp.json` separately |
+| VS Code Copilot | `servers` | JSON | `.vscode/mcp.json` | `servers` NOT `mcpServers`; needs type |
+| Copilot CLI | `mcpServers` | JSON | `~/.copilot/mcp-config.json` | Needs type; no longer reads .vscode/mcp.json |
+| Windsurf | `mcpServers` | JSON | `~/.codeium/windsurf/mcp_config.json` | Uses `serverUrl` not `url` |
+| Zed | `context_servers` | JSON | `settings.json` | 1.4.2+ uses AGENTS.md, removed @rule |
+| Trae | `mcpServers` | JSON | `.trae/mcp.json` | Project MCP needs manual toggle |
+| Trae Work | `mcpServers` | JSON | `.trae/mcp.json` | Shares Trae IDE config; SOLO/Code mode for agents |
+| Codex CLI | `[mcp_servers.*]` | TOML | `~/.codex/config.toml` | Underscores in names; prompts deprecated→skills |
+| Gemini CLI | `mcpServers` | JSON | `~/.gemini/settings.json` | Server names must use hyphens; commands are TOML |
+| OpenCode | `mcp` | JSON | `opencode.json` | `mcp` not `mcpServers`; command is array; env is `environment` |
+| Goose | `extensions` | YAML | `~/.config/goose/config.yaml` | `extensions` not `mcpServers`; cmd/args/envs |
+| JetBrains Junie | `mcpServers` | JSON | `~/.junie/mcp/mcp.json` | Rules is `.junie/guidelines.md` single file |
+| Kiro | `mcpServers` | JSON | `.kiro/settings/mcp.json` | Path has `settings/` subdir; supports hooks |
+| Continue.dev | `mcpServers` | YAML | `~/.continue/config.yaml` | mcpServers is ARRAY not object |
+| Sourcegraph Amp | CLI-managed | JSON | `~/.config/amp/` | MCP via `amp mcp add`, not config key |
+| Sourcegraph Cody | `mcpServers` | JSON | `~/.config/cody/mcp_servers.json` | Free/Pro sunset 2025-06; MCP via feature flag; agentic gathering not @mentions |
+| PearAI | `mcpServers` | JSON | `~/.pearai/config.json` | Standard object format; file is `config.json` |
+| Forge | `mcpServers` | JSON | `~/.forge/.mcp.json` | Path is `~/.forge/` not `~/.config/forge/` |
+| Void Editor | `mcpServers` | JSON | `~/.config/void/mcp_servers.json` | File is `mcp_servers.json` not `mcp.json` |
+| Kilo Code | `mcpServers` | JSON | `~/.config/kilo/kilo.jsonc` | Global is `~/.config/kilo/`; agents from modes |
+| Roo Code | `mcpServers` | JSON | `.roo/mcp.json` | Archived 2026-05; migrate to Kilo Code |
+| Amazon Q | `mcpServers` | JSON | `~/.aws/amazonq/default.json` | Legacy mcp.json with useLegacyMcpJson flag |
+| antigravity | `mcpServers` | JSON | `~/.gemini/config/mcp_config.json` | Config NOT in antigravity/ (that's cache) |
+| Augment Code | `mcpServers` | JSON | `~/.augment/settings.json` | GUI primary; skills follow agentskills.io |
+| OpenHands | `mcpServers` | JSON | `~/.openhands/mcp.json` | CLI 1.0+ JSON; GUI uses config.toml [mcp] |
+| Aider | N/A | YAML | `~/.aider.conf.yml` | No native MCP; rules via CONVENTIONS.md |
+| Helix | `[mcp_servers.*]` | TOML | `~/.config/helix/config.toml` | Via helix-ai plugin; tools-only |
+| Tabnine | `mcpServers` | JSON | `.tabnine/mcp_servers.json` | Rules via guidelines/rules.md |
+| Tongyi Lingma | `mcpServers` | JSON | `.lingma/mcp-settings.json` | GUI primary; ModelScope integration |
+| Baidu Comate | `mcpServers` | JSON | `.comate/mcp.json` | Rules use .mdr format; supports .agents/skills/ |
+| Tencent CodeBuddy | `mcpServers` | JSON | `~/.codebuddy/.mcp.json` | Full rules/agents/skills/memory system |
+| Kimi Code | `mcpServers` | JSON | `~/.kimi-code/mcp.json` | Path is `.kimi-code` NOT `.kimi`; config is `config.toml` |
+| ZCode (Zhipu) | `mcp.servers` | JSON | `~/.zcode/cli/config.json` | Root key `mcp.servers` (dot); uses AGENTS.md not CLAUDE.md |
+| MiniMax Code | N/A | — | (desktop app) | Built-in Agent Team/Skills/Memory; MCP unconfirmed |
+| mmx-cli (MiniMax) | N/A | JSON | `~/.mmx/config.json` | Region trap: api.minimax.io vs api.minimaxi.com |
+| Qoder CN (fmr Tongyi Lingma) | `mcpServers` | JSON | `~/.qoder/` / `qodercli mcp add` | Renamed 2026-05-20; uses AGENTS.md; ModelScope MCP plaza |
+| Baidu Comate IDE | `mcpServers` | JSON | (desktop app) | Distinct from plugin; Zulu multi-agent; .mdr rules; global config since 2025-08 |
+| Tencent CodeBuddy IDE | `mcpServers` | JSON | (desktop app) | Distinct from plugin; shares config with CodeBuddy CLI |
+| iFlyCode (讯飞) | `mcpServers` | JSON | (UI config) | MCP via UI paste JSON; no file-level config docs |
+| Raccoon AI (商汤) | `mcpServers` | JSON | (UI config) | MCP via UI paste JSON; no file-level config docs |
+| MonkeyCode (长亭) | `mcpServers` | JSON | `~/.monkeycode/` | AGPL-3.0 open source; SDD specs; MonkeyScan; private deploy |
+| veCLI (火山引擎) | `mcpServers` | JSON | `~/.vecli/` | Distinct from Trae CLI (Volcano Engine vs Trae brand) |
+| bolt.new / bolt.diy | `servers` | JSON | `~/.boltai/mcp.json` | Root key `servers` NOT `mcpServers`!; supports import from Cursor/Claude |
+| Qodo (fmr CodiumAI) | `mcpServers` | JSON | IDE Tools Mgmt UI | CodiumAI ≠ Codeium; agents in .toml; CLI supports `--mcp` |
+| Devin (Cognition) | `mcpServers` | JSON | (cloud dashboard) | Cloud SaaS only; bidirectional MCP; also exposes mcp.devin.ai |
+| v0 (Vercel) | UI | — | `v0.app/.../mcp-connections` | Bidirectional MCP; also exposes mcp.v0.dev; whitelisted clients only |
+| Lovable | UI | — | (lovable.dev chat connectors) | Bidirectional MCP; server OAuth limited to 5 clients; Enterprise disabled |
+| gptel + mcp.el (Emacs) | elisp | — | `~/.emacs.d/init.el` | Emacs ecosystem; `mcp-hub-start`; requires lisp |
+| WorkBuddy | `mcpServers` | JSON | `~/.workbuddy/.mcp.json` | Path is `~/.workbuddy/` not `~/.workbuddy` root; skills at `~/.workbuddy/skills/` |
+
+---
+
+## Universal Standard Locations
+
+Migrate to these FIRST for maximum cross-IDE compatibility:
+
+| Path | Purpose | Loaded By |
+|------|---------|-----------|
+| `AGENTS.md` | Universal project instructions | Claude Code, Cursor, Copilot, Codex, Gemini, Zed 1.4.2+, JetBrains/Goose/Kiro/Aider/OpenCode/Augment/Forge/Void/OpenHands/Kimi Code/ZCode/Qoder CN/veCLI |
+| `.agents/skills//SKILL.md` | Universal skills | Cursor, Copilot, Codex, Trae, antigravity, OpenCode, Augment, Amp, Zed, Comate, Kimi Code |
+| `.claude/skills//SKILL.md` | Claude-compatible skills | Claude Code, Cursor (loads natively), OpenCode, Augment, Copilot |
+| `.mcp.json` | Universal project MCP | Claude Code, Copilot CLI |
+| `.vscode/mcp.json` | VS Code ecosystem MCP | Copilot, Cline, Continue, Cody, Kilo Code, Void |
+
+---
+
+## Object Conversion Rules
+
+### MCP Server Conversion
+
+```
+CONVERT_MCP(source_config, source_ide, target_ide):
+
+ 1. Read source MCP config using source_ide paths and root_key
+ 2. For each server entry:
+ a. Extract: command, args, env, url, headers
+ b. BLANK all secret values in env (keep key names, set to "")
+ c. Convert to target format per root_key:
+ - mcpServers (JSON object): direct copy
+ - servers (JSON, VS Code Copilot): rename mcpServers→servers; add type:'stdio'|'http'
+ - context_servers (JSON): rename root key (Zed)
+ - mcp.servers (JSON): rename root key (ZCode; also accepts mcpServers)
+ - mcp (JSON): rename; add type:'local'|'remote'; command→array; env→environment (OpenCode)
+ - mcpServers ARRAY: object → [{name,type,...}] (Continue.dev ONLY)
+ - servers (JSON, bolt.new/bolt.diy): rename mcpServers→servers (NO type field needed)
+ - mcp_servers (TOML): → [mcp_servers.] table; underscores (Codex/Helix)
+ - extensions (YAML): rename; command→cmd, env→envs; add type (Goose)
+ - amp: via `amp mcp add` CLI command (Sourcegraph Amp)
+ d. Handle special fields:
+ - Windsurf HTTP: url → serverUrl
+ - antigravity HTTP: url → serverUrl
+ - Copilot CLI: add type:'local'|'http'
+ - Codex HTTP: uses url+bearer_token_env_var or http_headers
+ - Gemini CLI: server names underscores → hyphens
+ - Continue.dev: convert object to array [{name,type,...}]
+ - Goose: add type:'stdio'|'sse'|'streamable_http'
+ 3. Merge with existing target config (backup first; duplicates → _migrated)
+ 4. Write; verify parses correctly
+```
+
+### Rules/Instructions Conversion
+
+Rules are MARKDOWN. The BODY is always reusable — only frontmatter and filename need adaptation. `AGENTS.md` is the universal intermediate format.
+
+| Source | Conversion |
+|--------|------------|
+| `.cursorrules` / `.windsurfrules` / `.clinerules` / `.voidrules` | Copy body; rename to target's rules file |
+| `.cursor/rules/*.mdc` | Extract body; adapt frontmatter (description, globs, alwaysApply) |
+| `CLAUDE.md` / `GEMINI.md` / `CODY.md` / `CODEBUDDY.md` / `HELIX.md` | Copy body; rename to target's filename |
+| `AGENTS.md` | Copy directly (universal standard) |
+| `.kiro/steering/*.md` | Copy body; note conditional steering (inclusion: always/fileMatch/auto/manual) |
+| `.augment/rules/*.md` | Extract body; adapt frontmatter (always/auto/manual) |
+| `.comate/rules/*.mdr` | Extract body; .mdr is Markdown with Comate extensions |
+| `.junie/guidelines.md` | Copy body; single file not directory |
+| `.tabnine/guidelines/rules.md` | Copy body |
+| Any rules → Zed/ZCode/Kimi Code/Qoder CN/veCLI | Append to AGENTS.md (all use AGENTS.md as universal) |
+| Any rules → Aider | Use CONVENTIONS.md; add to `read:` in .aider.conf.yml |
+| Any rules → Goose | Copy to .goosehints or AGENTS.md |
+
+### Skills Conversion
+
+```
+CONVERT_SKILL(source_skill_dir, target_skill_dir):
+ 1. Keep: name, description (universally supported per agentskills.io)
+ 2. Remove IDE-specific frontmatter: allowed-tools (Claude), agent/fork/color (Augment)
+ 3. Copy entire skill directory (SKILL.md + scripts/ + references/ + assets/)
+ 4. Also copy to .agents/skills/ (universal) and .claude/skills/ (Claude-compatible)
+ 5. Verify SKILL.md has required name + description
+```
+
+### Commands/Prompts Conversion
+
+Commands are markdown files (filename = command name). Exceptions:
+- **Gemini CLI**: commands are `.toml` files, not markdown
+- **VS Code Copilot**: rename to `*.prompt.md`; add mode/model/tools frontmatter
+- **Cody**: convert to JSON format (`{commands: {key: {prompt, description, mode, context}}}`)
+- **OpenCode**: support $ARGUMENTS, $1, !`cmd`, @filepath templates
+- **Kimi Code**: no standalone commands dir; use skills or plugin commands
+
+### Agents Conversion
+
+Copy markdown body; adapt frontmatter. Supported fields by IDE:
+- **Claude Code**: name, description, model, tools
+- **VS Code Copilot**: name, description, tools, model
+- **OpenCode**: description, mode, model, tools, permission
+- **Forge**: name, description
+- **Tencent CodeBuddy**: name, description, tools, model
+- **Kiro**: name, description, prompt, mcpServers, tools, hooks (JSON format)
+- **Kimi Code**: YAML format (extend, name, system_prompt_path, tools, exclude_tools, subagents)
+- **Roo Code/Kilo Code**: Modes system (`.roomodes`/`kilo.jsonc`) — per-mode tool permissions
+- **Gemini CLI**: via `/agents` command and extensions
+- **ZCode**: Markdown (global `~/.zcode/agents/` or project `.zcode/agents/`)
+- **Qodo**: TOML format (`agents/.toml`: instructions, tools, commands)
+
+### Hooks Conversion
+
+| Source | Target | Conversion |
+|--------|--------|------------|
+| Claude Code (`settings.json` hooks) | Cursor (`.cursor/hooks.json`) | Adapt event names; Cursor supports sessionStart/End, preToolUse/postToolUse, etc. |
+| Claude Code hooks | VS Code Copilot (`.github/hooks/*.json`) | Shared format; 8 lifecycle events |
+| Claude Code hooks | Kiro (`.kiro/hooks/*.kiro.hook`) | Convert to JSON; when.type→fileEdited/preToolUse/postToolUse; then.type→runCommand/askAgent |
+| Claude Code hooks | OpenCode (`.opencode/plugins/*.ts`) | Convert to TypeScript event handlers |
+| Claude Code hooks | Kimi Code (`config.toml [[hooks]]`) | Convert to TOML array; 13 events; blocking: PreToolUse/Stop/UserPromptSubmit |
+| Any hooks | Others | Note as manual step |
+
+### Memory Conversion
+
+| Source | Target | Conversion |
+|--------|--------|------------|
+| Claude Code (`~/.claude/projects//memory/MEMORY.md` + user_*.md etc.) | Any rules | Copy MEMORY.md + relevant .md files as project context |
+| Cline `memory-bank/*.md` (6 files: projectbrief, productContext, activeContext, systemPatterns, techContext, progress) | Any rules | Copy as project context; prepend "## Project Context" |
+| Goose `~/.config/goose/memory/` (JSON shards) | Any rules | Extract text from JSON shards; convert to markdown |
+| Trae `~/.trae/memory/user_profile.md` | Any rules | Copy directly |
+| Windsurf memories (`~/.codeium/windsurf/memories/`) | Any rules | Copy global_rules.md; other memories are auto-generated |
+| CodeBuddy `CODEBUDDY.md` | Any rules | Copy directly |
+| Gemini CLI `/memory` | Any rules | Use `/memory show` to export; copy to target |
+| MiniMax Code (3-tier memory) | Any rules | Export via MiniMax Code UI; convert to markdown |
+| Cross-IDE: mem0 MCP / OpenMemory / Pieces LTM | Same | Install as MCP server in target IDE; memory follows API key/device |
+
+**Key rule**: Memory files are markdown/context. Convert to target IDE's rules or memory format. For MCP-based memory (mem0, Pieces), install the MCP server in target IDE.
+
+---
+
+## Safety Boundaries
+
+| Never do this | Why |
+|--------------|-----|
+| Copy/migrate API keys, tokens, secrets | Security; blank values, tell user to fill in |
+| Overwrite existing config without backup | Data loss; always .bak.TIMESTAMP first |
+| Overwrite existing entries | Merge only; conflicts → `_migrated` |
+| Execute package installs (npm install -g, curl \| sh) | Don't modify user's system — NEVER silent; see the OpenClaw exception below |
+| Modify shell rc files | PATH issues noted but not auto-fixed |
+| Kill/restart IDE processes | Tell user to restart in manual steps |
+| Migrate IDE UI settings / chat history / OAuth tokens / SQLite databases | Out of scope or privacy/security risk |
+| Write in dry-run mode | Default to preview; write only after user confirms |
+| Proceed when config is invalid JSON/TOML/YAML | Parse errors = corrupted; stop and report |
+
+### Exception — OpenClaw auto-configuration (explicit, user-consented)
+
+`scripts/auto-configure-openclaw-skills.sh` is the installer for **OpenClaw**, a
+*target runtime* for this skill. It is the single deliberate, user-consented exception to the
+"no global installs / no unverified curl|sh" rule above. It MAY:
+
+- run `npm install -g clawhub` (installs the ClawHub package manager), and
+- download and execute `https://openclaw.ai/install.sh` (the OpenClaw runtime installer),
+
+**but ONLY when ALL of the following hold:**
+
+1. The user passes `--yes` (explicit, recorded consent to modify their system).
+2. For `install.sh`, the downloaded script's SHA-256 is verified against `OPENCLAW_INSTALL_SHA256`
+ when a pin is provided; if no pin is set, the script prints a *prominent WARNING* showing the
+ computed hash rather than silently trusting the download.
+3. In `--dry-run` mode it only previews these actions and never executes them.
+
+This exception applies **solely** to installing the OpenClaw runtime / ClawHub for this skill's own
+target platform. It does **NOT** weaken the general rule for any other operation: no other command
+may run silent global `npm install -g`, pipe a remote script to `sh`, or modify the user's system
+without equivalent explicit consent and integrity verification.
+
+**IDE-specific pitfalls** (will cause silent failure):
+
+| IDE | Pitfall |
+|-----|---------|
+| VS Code Copilot | Root key `servers`; requires type:'stdio'\|'http' |
+| Copilot CLI | Root key `mcpServers` + REQUIRES type:'local'\|'http'; no longer reads .vscode/mcp.json |
+| Zed | Root key `context_servers`; 1.4.2+ uses AGENTS.md not @rule |
+| Codex/Helix | TOML `[mcp_servers.]` uses underscores; prompts deprecated→skills |
+| Gemini CLI | Server names must use hyphens; commands are .toml files |
+| OpenCode | Root key `mcp`; command must be array; requires type; env field is `environment` |
+| Goose | Root key `extensions`; fields: cmd/args/envs; memory is directory not file |
+| Continue.dev | mcpServers is ARRAY not OBJECT (only IDE using array) |
+| Sourcegraph Cody | Free/Pro sunset 2025-06 (migrate to Amp); MCP via feature flag opt-in; agentic gathering NOT @mentions |
+| Sourcegraph Amp | MCP via `amp mcp add` CLI, not config key |
+| Windsurf | Uses `serverUrl` not `url` for HTTP |
+| Kiro | Path has `settings/` subdir: `.kiro/settings/mcp.json`; hooks use .kiro.hook extension |
+| Cline | VS Code extension uses globalStorage path; CLI uses ~/.cline/mcp.json separately |
+| antigravity | Config at `~/.gemini/config/mcp_config.json`; antigravity/ dir is cache |
+| PearAI | Config file is `config.json` not `mcp_config.json`; standard object format |
+| Forge | Path is `~/.forge/` not `~/.config/forge/`; MCP file is `.mcp.json` |
+| Void Editor | File is `mcp_servers.json` not `mcp.json`; .voidrules unconfirmed |
+| Kilo Code | Global is `~/.config/kilo/kilo.jsonc`; agents evolved from modes |
+| Roo Code | Archived 2026-05; migrate to Kilo Code |
+| Aider | No native MCP; rules via CONVENTIONS.md read config |
+| Trae | Project MCP requires manual toggle; SOLO mode unsupported |
+| Kimi Code | Path `~/.kimi-code/` NOT `~/.kimi/`; config is `config.toml` NOT `config.json`; legacy kimi-cli deprecated |
+| ZCode | Root key `mcp.servers` (dot notation); uses `AGENTS.md` not `CLAUDE.md`; ZCode ≠ CodeGeeX |
+| mmx-cli | Region trap: global=api.minimax.io / cn=api.minimaxi.com (extra 'i'); Key+Host must match |
+| MiniMax Code | Desktop config not file-exposed; official recommends mmx CLI over MCP |
+| bolt.new/bolt.diy | Root key is `servers` NOT `mcpServers`; no type field needed (unlike VS Code Copilot) |
+| Qodo | CodiumAI ≠ Codeium (Codeium→Windsurf); Enterprise has "Agentic Tools Allow List"; agents in .toml files |
+| Devin/v0/Lovable | Cloud SaaS only (no local files); bidirectional MCP — distinguish client config from server endpoint |
+| Qoder CN | Renamed from Tongyi Lingma 2026-05-20; path `~/.qoder/` not `~/.lingma/`; uses AGENTS.md |
+| Baidu Comate IDE | Distinct from plugin version; global Rules/MCP only since 2025-08 late version; can't install MS Python/C++ plugins |
+| veCLI | Distinct from Trae CLI (different ByteDance product lines: Volcano Engine vs Trae brand) |
+
+---
+
+## Verification
+
+| IDE | Method |
+|-----|--------|
+| Claude Code | `claude mcp list` |
+| Codex CLI | `codex mcp list` or `/mcp` |
+| Copilot CLI | `copilot mcp list` |
+| OpenCode | `opencode mcp list` |
+| Kimi Code | `kimi mcp list` |
+| Qoder CN | `qodercli mcp list` |
+| Sourcegraph Amp | `amp mcp list` |
+| OpenHands | `openhands mcp list` |
+| Goose | `goose configure` |
+| Cursor | GUI: Settings → MCP (hot reload) |
+| Zed | GUI: Settings → AI → Context Servers (hot reload) |
+| VS Code Copilot | Command Palette → "MCP: List Servers" |
+| Windsurf | GUI: Settings → MCP panel |
+| Trae | GUI: Settings → MCP |
+| Cline | GUI: Cline sidebar → MCP Servers |
+| ZCode | GUI: Settings → MCP Servers |
+| mcphub-nvim | `:McpHub` in Neovim |
+| codecompanion-nvim | `/mcp` in chat buffer |
+| JSON/TOML/YAML | `python3 -c "import json/tomllib/yaml; ..."` parse check |
+| All others | Check files exist, non-empty, parse without errors |
+
+**Universal MCP debugger**: `npx @modelcontextprotocol/inspector`
+
+---
+
+## Existing Migration Script
+
+`scripts/smart-ide-migration.sh` automates file operations between 30+ IDEs. Use it for bulk file copying; use the IDE Registry for format conversion it doesn't handle.
+
+```bash
+bash scripts/smart-ide-migration.sh --source cursor --target claude --dry-run
+bash scripts/smart-ide-migration.sh --source cursor --target windsurf --objects skills,rules --strategy backup
+```
diff --git a/skills/agent-skills-setup/assets/public-repo-readme-template.md b/skills/agent-skills-setup/assets/public-repo-readme-template.md
new file mode 100644
index 0000000000..f26c795347
--- /dev/null
+++ b/skills/agent-skills-setup/assets/public-repo-readme-template.md
@@ -0,0 +1,98 @@
+# {{SKILL_NAME}}
+
+[](https://github.com/{{REPO_NAME}})
+[](LICENSE)
+
+Reusable multi-agent skill for Antigravity, Claude Code, OpenAI Codex, GitHub Copilot, Trae, OpenClaw, and related tooling.
+
+面向 Antigravity、Claude Code、OpenAI Codex、GitHub Copilot、Trae、OpenClaw 及相关工具链的可复用多代理 skill。
+
+日本語: Antigravity を起点に、OpenClaw を含む複数のエージェントへ展開できる再利用可能な skill です。
+
+Español: Skill reutilizable para múltiples agentes, diseñado para distribuirse desde Antigravity hacia OpenClaw y otros entornos.
+
+## At A Glance
+
+| Language | Summary |
+|---|---|
+| English | A publishable skill repository template with install guidance and multi-agent compatibility notes. |
+| 中文 | 一个可直接公开发布的 skill 仓库模板,附带安装说明和多代理兼容提示。 |
+| 日本語 | インストール手順とマルチエージェント対応情報を含む、公開向け skill リポジトリの雛形です。 |
+| Español | Plantilla de repositorio publicable para skills, con instalación y notas de compatibilidad multiagente. |
+
+## Table Of Contents
+
+- [What It Does](#what-it-does)
+- [Supported Agents](#supported-agents)
+- [Repository Layout](#repository-layout)
+- [Install](#install)
+- [Example Prompts](#example-prompts)
+- [Notes](#notes)
+
+## What It Does
+
+Describe the exact workflow or capability this skill adds.
+
+说明这个 skill 具体解决什么问题,以及它覆盖的工作流。
+
+## Supported Agents
+
+- Antigravity
+- Claude Code
+- OpenAI Codex
+- GitHub Copilot
+- Trae
+- Trae CN
+- OpenClaw
+
+Adjust this list if the skill only supports a subset.
+
+如果只支持其中一部分 agent,请按实际情况裁剪列表。
+
+## Repository Layout
+
+```text
+.
+├── README.md
+└── {{SKILL_NAME}}/
+ ├── SKILL.md
+ ├── scripts/
+ ├── references/
+ └── assets/
+```
+
+## Install
+
+### skills.sh CLI
+
+```bash
+npx skills add {{REPO_NAME}}
+```
+
+### ClawHub
+
+```bash
+clawhub publish ./{{SKILL_NAME}} --slug {{SKILL_NAME}} --name "{{SKILL_NAME}}" --version 1.0.0 --tags latest
+```
+
+### Manual Install
+
+Copy the `{{SKILL_NAME}}/` folder into the appropriate global or project-level skills directory for your agent.
+
+将 `{{SKILL_NAME}}/` 目录复制到对应 agent 的全局或项目级 skills 目录中。
+
+## Example Prompts
+
+- Add practical prompt examples here.
+- Explain when the skill should be invoked.
+- 增加实际提示词示例。
+- 说明什么场景下应该触发这个 skill。
+
+## Notes
+
+- Replace any local-only assumptions before publishing.
+- Document OS or shell requirements for bundled scripts.
+- Add a license before publishing publicly.
+- 发布前替换所有本地私有路径或机器特定假设。
+- 为脚本注明操作系统和 shell 要求。
+- 公开发布前补齐许可证和 changelog。
\ No newline at end of file
diff --git a/skills/agent-skills-setup/references/ide-paths.json b/skills/agent-skills-setup/references/ide-paths.json
new file mode 100644
index 0000000000..800a1dd096
--- /dev/null
+++ b/skills/agent-skills-setup/references/ide-paths.json
@@ -0,0 +1,233 @@
+{
+ "antigravity": {
+ "global_skills": "~/.gemini/antigravity/skills",
+ "project_skills": ".agents/skills",
+ "rules": "",
+ "mcp": "",
+ "config": ""
+ },
+ "claude": {
+ "global_skills": "~/.claude/skills",
+ "project_skills": ".claude",
+ "rules": "CLAUDE.md",
+ "mcp": "~/.claude.json",
+ "config": "~/.claude/settings.json"
+ },
+ "codex": {
+ "global_skills": "~/.agents/skills",
+ "project_skills": ".codex",
+ "rules": "AGENTS.md",
+ "mcp": "~/.codex",
+ "config": "~/.codex"
+ },
+ "copilot": {
+ "global_skills": "~/.copilot/skills",
+ "project_skills": ".github/copilot-instructions.md",
+ "rules": ".github/copilot-instructions.md",
+ "mcp": "",
+ "config": ""
+ },
+ "cursor": {
+ "global_skills": "~/.cursor",
+ "project_skills": ".cursor",
+ "rules": ".cursorrules",
+ "mcp": "~/.cursor/mcp.json",
+ "config": "~/.cursor/settings.json"
+ },
+ "windsurf": {
+ "global_skills": "~/.windsurf",
+ "project_skills": ".windsurf",
+ "rules": ".windsurfrules",
+ "mcp": "~/.windsurf/mcp.json",
+ "config": "~/.windsurf/settings.json"
+ },
+ "jetbrains": {
+ "global_skills": "~/.idea",
+ "project_skills": ".idea",
+ "rules": "",
+ "mcp": "",
+ "config": ""
+ },
+ "openclaw": {
+ "global_skills": "~/.openclaw/skills",
+ "project_skills": "skills",
+ "rules": "AGENT_RULES.md",
+ "mcp": "~/.openclaw/openclaw.json",
+ "config": "~/.openclaw/openclaw.json"
+ },
+ "trae": {
+ "global_skills": "~/.trae/skills",
+ "project_skills": ".trae",
+ "rules": "",
+ "mcp": "~/.trae/mcps",
+ "config": "~/.trae/argv.json"
+ },
+ "trae-cn": {
+ "global_skills": "~/.trae-cn/skills",
+ "project_skills": ".trae",
+ "rules": "",
+ "mcp": "~/.trae-cn/mcps",
+ "config": "~/.trae-cn/argv.json"
+ },
+ "vscode": {
+ "global_skills": "~/.vscode",
+ "project_skills": ".vscode",
+ "rules": "",
+ "mcp": "",
+ "config": "~/.vscode/settings.json"
+ },
+ "zed": {
+ "global_skills": "~/.config/zed",
+ "project_skills": ".zed",
+ "rules": "",
+ "mcp": "",
+ "config": "~/.config/zed/settings.json"
+ },
+ "neovim": {
+ "global_skills": "~/.config/nvim",
+ "project_skills": ".nvim",
+ "rules": "",
+ "mcp": "",
+ "config": "~/.config/nvim/init.lua"
+ },
+ "emacs": {
+ "global_skills": "~/.emacs.d",
+ "project_skills": ".dir-locals.el",
+ "rules": "",
+ "mcp": "",
+ "config": "~/.emacs.d/init.el"
+ },
+ "continue": {
+ "global_skills": "~/.continue",
+ "project_skills": ".continue",
+ "rules": ".continuerc.json",
+ "mcp": "~/.continue/config.json",
+ "config": "~/.continue/config.json"
+ },
+ "aider": {
+ "global_skills": "~/.aider",
+ "project_skills": ".aider.conf.yml",
+ "rules": "CONVENTIONS.md",
+ "mcp": "~/.aider.conf.yml",
+ "config": "~/.aider.conf.yml"
+ },
+ "roo-code": {
+ "global_skills": "~/.roo",
+ "project_skills": ".roo",
+ "rules": ".roomotes",
+ "mcp": "~/.roo/mcp.json",
+ "config": "~/.roo/config.json"
+ },
+ "cline": {
+ "global_skills": "~/.cline",
+ "project_skills": ".cline",
+ "rules": ".clinerules",
+ "mcp": "~/.cline/mcp.json",
+ "config": "~/.cline/config.json"
+ },
+ "amazon-q": {
+ "global_skills": "~/.aws/amazon-q",
+ "project_skills": ".amazon-q",
+ "rules": "",
+ "mcp": "",
+ "config": ""
+ },
+ "cody": {
+ "global_skills": "",
+ "project_skills": ".cody",
+ "rules": ".codyrules",
+ "mcp": "",
+ "config": ""
+ },
+ "codeium": {
+ "global_skills": "",
+ "project_skills": ".codeium",
+ "rules": "",
+ "mcp": "",
+ "config": ""
+ },
+ "tabnine": {
+ "global_skills": "",
+ "project_skills": ".tabnine",
+ "rules": "",
+ "mcp": "",
+ "config": ""
+ },
+ "replit": {
+ "global_skills": "~/.replit",
+ "project_skills": ".replit",
+ "rules": "",
+ "mcp": "",
+ "config": "~/.replit/replit.nix"
+ },
+ "pearai": {
+ "global_skills": "~/.pearai",
+ "project_skills": ".pearai",
+ "rules": ".pearairules",
+ "mcp": "",
+ "config": "~/.pearai/settings.json"
+ },
+ "supermaven": {
+ "global_skills": "~/.supermaven",
+ "project_skills": ".supermaven",
+ "rules": "",
+ "mcp": "",
+ "config": ""
+ },
+ "pieces": {
+ "global_skills": "~/.pieces",
+ "project_skills": ".pieces",
+ "rules": "",
+ "mcp": "",
+ "config": ""
+ },
+ "blackbox": {
+ "global_skills": "",
+ "project_skills": ".blackbox",
+ "rules": "",
+ "mcp": "",
+ "config": ""
+ },
+ "gemini-cli": {
+ "global_skills": "~/.gemini",
+ "project_skills": ".gemini",
+ "rules": "GEMINI.md",
+ "mcp": "~/.gemini/settings.json",
+ "config": "~/.gemini/settings.json"
+ },
+ "goose-cli": {
+ "global_skills": "~/.config/goose",
+ "project_skills": ".goose",
+ "rules": "GOOSE.md",
+ "mcp": "~/.config/goose/config.yaml",
+ "config": "~/.config/goose/config.yaml"
+ },
+ "opencode": {
+ "global_skills": "~/.config/opencode",
+ "project_skills": ".opencode",
+ "rules": "OPENCODE.md",
+ "mcp": "",
+ "config": "~/.config/opencode/config.json"
+ },
+ "kilocode": {
+ "global_skills": "~/.kilocode",
+ "project_skills": ".kilocode",
+ "rules": "KILOCODE.md",
+ "mcp": "",
+ "config": "~/.kilocode/config.json"
+ },
+ "kimiai": {
+ "global_skills": "~/.kimi-code/skills",
+ "project_skills": ".kimi-code/skills",
+ "rules": "AGENTS.md",
+ "mcp": "~/.kimi-code/mcp.json",
+ "config": "~/.kimi-code/config.toml"
+ },
+ "workbuddy": {
+ "global_skills": "~/.workbuddy/skills",
+ "project_skills": ".workbuddy/skills",
+ "rules": "",
+ "mcp": "~/.workbuddy/.mcp.json",
+ "config": "~/.workbuddy/settings.json"
+ }
+}
diff --git a/skills/agent-skills-setup/references/ide-registry.md b/skills/agent-skills-setup/references/ide-registry.md
new file mode 100644
index 0000000000..6e62104997
--- /dev/null
+++ b/skills/agent-skills-setup/references/ide-registry.md
@@ -0,0 +1,488 @@
+# IDE Registry — Full Migration Object Reference
+
+Detailed per-IDE paths for all migration objects. Read this when executing STEP 3 (SCAN) of the migration workflow.
+
+> **Verified**: 2026-07-16 via official docs. All paths are macOS/Linux unless noted.
+> Objects not listed for an IDE = not supported by that IDE.
+
+---
+
+## Claude Family
+
+### claude-desktop
+- **detect**: `~/Library/Application Support/Claude/` (mac) · `~/.config/Claude/` (linux) · `%APPDATA%\Claude\` (win)
+- **mcp**: `claude_desktop_config.json` in detect dir · root_key `mcpServers` · JSON · stdio+HTTP
+- Other objects: none (MCP-only product)
+- `.mcpb` (MCP Bundle, formerly `.dxt`) extensions: drag to Settings → Extensions
+
+### claude-code
+- **detect**: `~/.claude/`
+- **mcp**: global `~/.claude.json` · project `.mcp.json` · root_key `mcpServers` · JSON · stdio+HTTP · `claude mcp list`
+- **rules**: project `CLAUDE.md` / `.claude/rules/*.md` · global `~/.claude/CLAUDE.md` / `~/.claude/rules/*.md` · local `CLAUDE.local.md`
+- **skills**: project `.claude/skills//SKILL.md` · global `~/.claude/skills//SKILL.md` · frontmatter: name, description
+- **commands**: project `.claude/commands/*.md` · global `~/.claude/commands/*.md`
+- **agents**: project `.claude/agents/*.md` · global `~/.claude/agents/*.md` · frontmatter: name, description, model, tools
+- **hooks**: `~/.claude/settings.json` key `hooks` · events: PreToolUse, PostToolUse, PreCompact, SessionStart
+- **memory**: `~/.claude/projects//memory/MEMORY.md` + `user_*.md` + `project_*.md` + `feedback_*.md` + `reference_*.md` (v2.1.59+; `/memory` command)
+- **other**: `.claude/output-styles/` · `.claude/workflows/` · `~/.claude/plugins/`
+- **note**: Do NOT use settings.json for MCP; use ~/.claude.json
+
+### cursor
+- **detect**: `~/Library/Application Support/Cursor/` (mac) · `~/.config/Cursor/` (linux) · `%APPDATA%\Cursor\` (win)
+- **mcp**: global `~/.cursor/mcp.json` · project `.cursor/mcp.json` · root_key `mcpServers` · JSON · stdio+HTTP
+- **rules**: legacy `.cursorrules` · project `.cursor/rules/*.mdc` · frontmatter: description, globs, alwaysApply
+- **skills**: project `.cursor/skills//SKILL.md` · global `~/.cursor/skills/` · also loads `.agents/skills/`, `.claude/skills/`, `.codex/skills/` (since v0.44)
+- **commands**: project `.cursor/commands/*.md` · `/migrate-to-skills` converts commands to skills
+- **agents**: project `.cursor/agents/*.md`
+- **hooks**: project `.cursor/hooks.json` · global `~/.cursor/hooks.json` · events: sessionStart/End, preToolUse/postToolUse, subagentStart/Stop, beforeShellExecution, afterFileEdit, stop
+- **memory**: no native file-based memory (use MCP memory server)
+- **other**: `.cursorignore` · `.cursor-plugin/plugin.json`
+
+### cline
+- **detect**: `~/.cline/`
+- **mcp** (VS Code ext): `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` (mac) · `~/.config/Code/User/globalStorage/...` (linux) · `%APPDATA%\Code\User\globalStorage\...` (win)
+- **mcp** (CLI): `~/.cline/mcp.json` · root_key `mcpServers` · JSON · stdio+HTTP · extra fields: disabled, autoApprove
+- **rules**: project `.clinerules` / `.clinerules/*.md` · also reads `.cursorrules`, `.windsurfrules`, `AGENTS.md`
+- **skills**: project `.cline/skills/` · global `~/.cline/skills/` (v3.17.10+)
+- **memory**: `memory-bank/*.md` — community methodology (6 files: projectbrief, productContext, activeContext, systemPatterns, techContext, progress)
+- **other**: `.clineignore`
+
+### roo-code (archived 2026-05)
+- **detect**: `~/.roo/`
+- **mcp**: project `.roo/mcp.json` · global `mcp_settings.json` (extension dir) · root_key `mcpServers` · JSON
+- **rules**: project `.roorules` / `.roo/rules/*.md` / `.roo/rules-{mode}/` · global `~/.roo/rules/`
+- **skills**: project `.roo/skills/` / `.agents/skills/` · global `~/.roo/skills/`
+- **commands**: project `.roo/commands/*.md`
+- **modes**: project `.roomodes` · global `custom_modes.yaml` — per-mode tool permissions (unique to Roo)
+- **memory**: `memory-bank/*.md` (community methodology, inherited from Cline)
+- **note**: Migrate to Kilo Code: `.roo/`→`.kilocode/`, `.roomodes`→`.kilocodemodes`
+
+---
+
+## VS Code Ecosystem
+
+### vscode-copilot
+- **detect**: `~/.vscode/`
+- **mcp**: project `.vscode/mcp.json` · global `~/Library/Application Support/Code/User/mcp.json` (mac, v1.102+) · root_key `servers` · JSON · needs type:'stdio'|'http'
+- **rules**: `.github/copilot-instructions.md` · `.github/instructions/*.instructions.md` · frontmatter: applyTo
+- **skills**: `.github/skills/` / `.claude/skills/` / `.agents/skills/` · global `~/.copilot/skills/`
+- **commands**: `.github/prompts/*.prompt.md` · frontmatter: description, mode, model, tools
+- **agents**: `.github/agents/*.agent.md` · frontmatter: name, description, tools, model
+- **hooks**: `.github/hooks/*.json` — 8 lifecycle events (preview)
+- **other**: `.github/plugins/*.plugin.md` (preview)
+
+### copilot-cli
+- **detect**: `~/.copilot/`
+- **mcp**: global `~/.copilot/mcp-config.json` · project `.mcp.json` / `.github/mcp.json` · root_key `mcpServers` · JSON · REQUIRES type:'local'|'http'
+- **rules**: `.github/copilot-instructions.md` · global `~/.copilot/copilot-instructions.md`
+- **skills**: `.github/skills/` / `.claude/skills/` / `.agents/skills/` (auto-activation unstable in CLI)
+- **commands**: `.github/prompts/*.prompt.md` (shared with VS Code)
+- **agents**: `.github/agents/*.agent.md` (shared with VS Code)
+- **hooks**: `.github/hooks/*.json` (shared)
+- **note**: No longer reads `.vscode/mcp.json` since 2026-06
+
+### windsurf
+- **detect**: `~/.codeium/windsurf/`
+- **mcp**: global `~/.codeium/windsurf/mcp_config.json` · root_key `mcpServers` · JSON · stdio+HTTP · url_field: `serverUrl`
+- **rules**: legacy `.windsurfrules` · project `.windsurf/rules/*.md` · global `~/.codeium/windsurf/memories/global_rules.md` · frontmatter: trigger, description
+- **skills**: project `.windsurf/skills/` · global `~/.windsurf/skills/`
+- **commands**: project `.windsurf/workflows/` (slash commands via `/[name]`)
+- **memory**: `~/.codeium/windsurf/memories/` (Cascade auto-generated, workspace-isolated)
+- **note**: No project-level MCP; 6000 char per rule limit; 100 tool limit
+
+### continue
+- **detect**: `~/.continue/`
+- **mcp**: global `~/.continue/config.yaml` · project `.continue/mcpServers/.yaml` · root_key `mcpServers` · YAML · ARRAY format (not object)
+- **rules**: `.continue/rules/*.md` · `CONTINUE.md` · frontmatter: name, globs, regex, alwaysApply, description
+- **commands**: `.continue/prompts/*.md`
+- **note**: config.yaml replaced config.json; mcpServers is array [{name, type, command, args, env}]
+
+### augment-code
+- **detect**: `~/.augment/`
+- **mcp**: global `~/.augment/settings.json` · root_key `mcpServers` · JSON · stdio+HTTP
+- **rules**: project `.augment/rules/*.md` · frontmatter: always_apply, agent_requested
+- **skills**: project `.augment/skills//SKILL.md` · global `~/.augment/skills/` · also loads `~/.claude/skills/`, `~/.agents/skills/` · frontmatter: name, description, agent, fork, color
+- **commands**: global `~/.augment/commands/`
+- **other**: `~/.augment-plugin/` (plugins marketplace)
+
+### kilo-code
+- **detect**: `~/.kilocode/` (project) · `~/.config/kilo/` (global)
+- **mcp**: project `.kilocode/mcp.json` · root_key `mcpServers` · JSON · stdio+HTTP
+- **rules**: project `.kilo/rules/*.md` / `.kilocode/rules/` · config `kilo.jsonc` instructions array · global `~/.config/kilo/kilo.jsonc`
+- **agents**: project `.kilo/agents/*.md` / `.kilocode/agents/*.md` · global `~/.config/kilo/agent/*.md` · frontmatter: description, mode, model, permission, color (evolved from Roo modes)
+- **other**: `.kilocodemodes` (legacy, migrated to agent markdown)
+
+---
+
+## Standalone IDEs
+
+### zed
+- **detect**: `~/Library/Application Support/Zed/` (mac) · `~/.config/zed/` (linux) · `%APPDATA%\Zed\` (win)
+- **mcp**: global `settings.json` · project `.zed/settings.json` · root_key `context_servers` · JSON · stdio+HTTP
+- **rules**: `AGENTS.md` (since 1.4.2, removed @rule system)
+- **skills**: `.agents/skills//SKILL.md` (universal standard)
+- **agents**: via `agent_servers` config (ACP protocol to external agents)
+- **note**: GUI-launched Zed lacks shell PATH — use absolute paths
+
+### trae
+- **detect**: `~/.trae/`
+- **mcp**: global `~/Library/Application Support/Trae/User/mcp.json` (mac) / `~/.config/Trae/User/mcp.json` (linux) / `%APPDATA%\Trae\User\mcp.json` (win) · project `.trae/mcp.json` · root_key `mcpServers` · JSON
+- **rules**: project `.trae/rules/*.md` · global `~/.trae/rules/*.md`
+- **skills**: project `.trae/skills//SKILL.md` · global `~/.trae/skills//SKILL.md` · frontmatter: name, description
+- **commands**: project `.trae/commands/*.md` (max 3 levels nesting) · global `~/.trae/commands/*.md`
+- **agents**: project `.trae/agents/.md`
+- **memory**: global `~/.trae/memory/user_profile.md` · project `~/.trae/memory/projects//project_memory.md`
+- **note**: Project MCP needs manual toggle; SOLO mode unsupported
+
+### trae-work (2026-06-18, formerly Trae SOLO)
+- Third TRAE product: Web + Desktop + Mobile. Work/Code/Design modes.
+- Config shares Trae IDE体系完全一致: project `.trae/` and global `~/.trae/` (intl) / `~/.trae-cn/` (CN)
+- All Trae IDE migration objects (mcp/rules/skills/commands/agents/hooks/memory) apply unchanged
+- Unique: Cloud Agent, Global Memory (cross-session, 2026-06-23+), Work/Design modes
+- Subagents: only available in SOLO mode (Trae IDE) / Code mode (Trae Work)
+- Migrate to/from Trae IDE = direct copy; no format conversion needed
+
+### trae-cn
+- Same as Trae but global paths use `~/.trae-cn/` instead of `~/.trae/`
+- **mcp** global: `~/Library/Application Support/Trae CN/User/mcp.json` (mac) / `~/.config/Trae CN/User/mcp.json` (linux)
+- **memory**: `~/.trae-cn/memory/user_profile.md`
+
+### jetbrains (Junie)
+- **detect**: `~/.junie/`
+- **mcp**: global `~/.junie/mcp/mcp.json` (mac/linux) · `%USERPROFILE%\.junie\mcp\mcp.json` (win) · root_key `mcpServers` · JSON
+- **rules**: project `.junie/guidelines.md` (single file, NOT directory)
+- **note**: JetBrains AI Assistant (separate from Junie) uses GUI config; MCP via Settings > Tools > AI Assistant
+
+### kiro
+- **detect**: `~/.kiro/`
+- **mcp**: global `~/.kiro/settings/mcp.json` · project `.kiro/settings/mcp.json` · root_key `mcpServers` · JSON · stdio+HTTP+OAuth
+- **rules**: project `.kiro/steering/*.md` · global `~/.kiro/steering/*.md` · frontmatter: inclusion (always|fileMatch|auto|manual)
+- **skills**: Kiro Powers / Agent Skills
+- **agents**: `.kiro/agents/*.json` · fields: name, description, prompt, mcpServers, tools, allowedTools, hooks, model
+- **hooks**: `.kiro/hooks/*.kiro.hook` · when.type: fileEdited/userTriggered/preToolUse/postToolUse · then.type: runCommand/askAgent · version: "1.0.0"
+- **specs**: `.kiro/specs//{requirements,design,tasks}.md` — spec-driven dev docs
+
+---
+
+## CLI Agents
+
+### codex
+- **detect**: `~/.codex/`
+- **mcp**: global `~/.codex/config.toml` · project `.codex/config.toml` · root_key `mcp_servers` · TOML · stdio+HTTP · `codex mcp list`
+- **rules**: project `AGENTS.md` · global `~/.codex/AGENTS.md`
+- **skills**: `.agents/skills//SKILL.md` · global `~/.agents/skills/` · admin `/etc/codex/skills/` (4-layer scan)
+- **commands**: DEPRECATED (v0.117.0 migrated prompts to skills system)
+- **hooks**: `~/.codex/hooks.json` or config.toml
+- **note**: TOML [mcp_servers.] uses underscores; HTTP via url+bearer_token_env_var
+
+### gemini-cli
+- **detect**: `~/.gemini/`
+- **mcp**: global `~/.gemini/settings.json` · project `.gemini/settings.json` · root_key `mcpServers` · JSON · stdio+HTTP
+- **rules**: `GEMINI.md` (configurable via contextFileName setting)
+- **skills**: global `~/.gemini/skills/` / `~/.agents/skills/` · project `.gemini/skills/` / `.agents/skills/`
+- **commands**: global `~/.gemini/commands/*.toml` · project `.gemini/commands/*.toml` · TOML format (not markdown!) · `{{args}}` placeholders
+- **agents**: global `~/.gemini/agents/` · project `.gemini/agents/` · `/agents list/enable/disable`
+- **memory**: `/memory show` · `/memory add` · checkpoint at `~/.gemini/tmp//`
+- **note**: Server names must use HYPHENS; 2026-06-18 personal版停服→migrate to antigravity
+
+### antigravity
+- **detect**: `~/.gemini/antigravity/`
+- **mcp**: global `~/.gemini/config/mcp_config.json` · project `.agents/mcp_config.json` · root_key `mcpServers` · JSON · remote uses `serverUrl` (NOT `url`)
+- **rules**: `AGENTS.md` (recommended) or `GEMINI.md`
+- **skills**: global `~/.gemini/config/skills/` · project `.agents/skills/`
+- **plugins**: `~/.gemini/antigravity-cli/plugins//` (plugin.json, mcp_config.json, hooks.json, skills/, agents/)
+- **hooks**: plugin `hooks.json` or settings.json · `/hooks` to view
+- **note**: `~/.gemini/antigravity/mcp/` is SERVER CACHE, not config
+
+### amazon-q
+- **detect**: `~/.aws/amazonq/`
+- **mcp**: global `~/.aws/amazonq/default.json` · project `.amazonq/default.json` · root_key `mcpServers` · JSON · legacy `mcp.json` with `useLegacyMcpJson:true`
+- **rules**: project `.amazonq/rules/`
+- **commands**: global `~/.aws/amazonq/prompts/*.md` · `/prompts` · `@prompt_name`
+- **agents**: `default.json` agents field
+- **note**: CLI uses `~/.aws/amazonq/cli-agents/` (separate from IDE)
+
+### opencode
+- **detect**: `~/.config/opencode/`
+- **mcp**: global `~/.config/opencode/opencode.json` · project `opencode.json` · root_key `mcp` · JSON · REQUIRES type:'local'|'remote' · command is ARRAY · env field is `environment`
+- **rules**: `AGENTS.md` (via instructions field in config)
+- **skills**: project `.opencode/skills/` · global `~/.config/opencode/skills/` · also loads `.claude/skills/`, `.agents/skills/`
+- **commands**: project `.opencode/commands/*.md` · global `~/.config/opencode/commands/*.md` · frontmatter: description, agent, model · $ARGUMENTS, !`cmd`, @file templates
+- **agents**: project `.opencode/agents/*.md` · global `~/.config/opencode/agents/*.md` · frontmatter: description, mode, model, tools, permission
+- **hooks**: via `.opencode/plugins/*.ts` (TypeScript event-driven)
+- **memory**: via plugins (OpenMemory, short-term-memory, agent-memory)
+- **note**: Config is MERGED not replaced
+
+### goose
+- **detect**: `~/.config/goose/`
+- **mcp**: global `~/.config/goose/config.yaml` · root_key `extensions` · YAML · fields: type(builtin/stdio/sse/streamable_http), cmd, args, envs, enabled
+- **rules**: project `.goosehints`
+- **recipes**: project `.goose/recipes/*.yaml` — NOT skills; contain instructions+extensions+parameters
+- **commands**: global `~/.config/goose/prompts/` · project `.goose/prompts/`
+- **memory**: global `~/.config/goose/memory/` (directory, JSON shards) · project `.goose/memory/`
+- **other**: `~/.config/goose/permission.yaml` · `~/.config/goose/secrets.yaml`
+
+### aider
+- **detect**: `~/.aider.conf.yml`
+- **rules**: `CONVENTIONS.md` (add to `read:` in .aider.conf.yml)
+- **commands**: `.aider.commands.md`
+- **other**: `.aider.model.settings.yml` · `.aider.model.metadata.json`
+- **note**: NO native MCP client (official config has no mcp option)
+
+### openhands
+- **detect**: `~/.openhands/`
+- **mcp** (CLI 1.0+): `~/.openhands/mcp.json` · root_key `mcpServers` · JSON · `openhands mcp list`
+- **mcp** (GUI/legacy): `config.toml` [mcp] section · `sse_servers`/`shttp_servers`/`stdio_servers` arrays
+- **rules**: `AGENTS.md`
+- **skills**: project skills (via `load_project_skills()`, agentskills.io standard)
+- **agents**: `~/.openhands-cli/persist/agent_settings.json`
+- **memory**: Condenser system (config.toml [condenser]: type=amortized/llm_attention/llm_summarizing)
+
+### sourcegraph-amp
+- **detect**: `~/.config/amp/`
+- **mcp**: via `amp mcp add ` CLI (NOT config key) · `amp mcp list`
+- **rules**: `AGENTS.md`
+- **skills**: project `.amp/skills//SKILL.md` · global `~/.config/amp/skills/` / `~/.amp/skills/` / `~/.agents/skills/`
+- **commands**: `~/.config/amp/` (slash commands)
+- **agents**: built-in Oracle, Librarian, Painter, Code Review; custom via plugin API
+- **hooks**: `~/.config/amp/plugins/*.ts` (TypeScript; events: session.start, agent.start/end, tool.result)
+- **note**: Native HTTP+OAuth+DCR; no mcp-remote needed
+
+### sourcegraph-cody
+- **detect**: `~/.vscode/cody.json` · `~/.config/cody/`
+- **mcp**: `~/.config/cody/mcp_servers.json` (since 2025-05-28, all tiers via feature flag opt-in) · root_key `mcpServers` · JSON · agentic context gathering (NOT via @mentions)
+- **commands**: `~/.vscode/cody.json` (user) · `.vscode/cody.json` (workspace) · format: `{commands: {key: {prompt, description, mode, context}}}`
+- **note**: Free/Pro sunset 2025-06/07 (migrate to Amp); Enterprise $59/user/month; Cody repo went private (public snapshot at sourcegraph/cody-public-snapshot); Sourcegraph MCP Server (separate product) exposes code search to external agents
+
+---
+
+## Forge / PearAI / Void
+
+### forge
+- **detect**: `~/.forge/` (FORGE_CONFIG env var can override)
+- **mcp**: global `~/.forge/.mcp.json` · project `./.mcp.json` · root_key `mcpServers` · JSON · `forge mcp import/list`
+- **rules**: `forge.yaml` custom_rules field · also `AGENTS.md`
+- **skills**: project `.forge/skills//SKILL.md` · global `~/forge/skills/` · also `~/.agents/skills/`
+- **commands**: `forge.yaml` commands array
+- **agents**: `.forge/agents/.md` · built-in: Forge, Sage, Muse
+- **other**: `forge.yaml` (main config) · `.forge/templates/`
+
+### pearai
+- **detect**: `~/.pearai/`
+- **mcp**: global `~/.pearai/config.json` (NOT mcp_config.json) · root_key `mcpServers` · JSON · standard OBJECT format
+- **rules**: via `config.json` customCommands field (no independent rules files)
+- **commands**: `config.json` customCommands array
+- **other**: `~/.pearai/config.ts` (TypeScript advanced config)
+- **note**: Fork of Continue.dev; standard mcpServers object (NOT array)
+
+### void-editor
+- **detect**: `~/.config/void/`
+- **mcp**: global `~/.config/void/mcp_servers.json` (NOT mcp.json) · project `.void/mcp_servers.json` · root_key `mcpServers` · JSON
+- **rules**: `.voidrules` (community-reported, unconfirmed by official docs)
+- **note**: VS Code fork (MIT); settings encrypted via VoidSettingsService
+
+---
+
+## Tabnine / Helix / Neovim
+
+### tabnine
+- **detect**: `~/.tabnine/`
+- **mcp**: global `~/.tabnine/mcp_servers.json` · project `.tabnine/mcp_servers.json` · root_key `mcpServers` · JSON · stdio auto from command, HTTP from url
+- **rules**: `.tabnine/guidelines/rules.md`
+- **other**: Enterprise MCP Governance (Admin Console whitelist)
+
+### helix
+- **detect**: `~/.config/helix/`
+- **mcp**: `~/.config/helix/config.toml` · root_key `mcp_servers` · TOML · via helix-ai plugin · tools-only (no Resources/Prompts/Sampling)
+- **rules**: `HELIX.md` (via helix-ai plugin, project-level)
+- **other**: `~/.config/helix/languages.toml` (LSP config)
+
+### mcphub-nvim
+- **detect**: `~/.config/nvim/`
+- **mcp**: `~/.config/mcphub/servers.json` · root_key `mcpServers` (also supports `servers` for VS Code compat) · JSON5 · `${env:VAR}` variables · verify: `:McpHub`
+- **note**: Compatible with `.vscode/mcp.json`; can share config with VS Code/Cursor/Cline/Zed
+
+### codecompanion-nvim
+- **detect**: `~/.config/nvim/`
+- **mcp**: Lua config · `mcp.servers` key · verify: `/mcp` in chat buffer
+- **rules**: reads `.clinerules`, `.cursorrules`, `AGENTS.md`, `CLAUDE.md`, `.goosehints` (configurable)
+- **commands**: `prompt_library` Lua table · `.prompts/*.md` (v18.0.0+)
+- **hooks**: Events/Hooks system (Lua callbacks)
+
+---
+
+## Chinese AI Assistants
+
+### tongyi-lingma
+- **detect**: `~/.lingma/`
+- **mcp**: project `.lingma/mcp-settings.json` · root_key `mcpServers` · JSON · GUI primary · ModelScope MCP plaza 3000+
+- **rules**: project `.lingma/.md`
+- **skills**: project `.lingma/skills//SKILL.md` · global `~/.lingma/skills/`
+- **commands**: project `.lingma/commands/` · global `~/.lingma/commands/`
+- **agents**: project `.lingma/agents/.md` · global `~/.lingma/agents/` · frontmatter: name, description, tools
+
+### baidu-comate
+- **detect**: `~/.comate/`
+- **mcp**: global `~/.comate/mcp.json` · project `.comate/mcp.json` · local `.comate/mcp.local.json` · root_key `mcpServers` · JSON
+- **rules**: `.comate/rules/*.mdr` — unique .mdr format (Markdown + Comate extensions) · Cursor Rules compatible · 4 activation modes
+- **skills**: `.agents/skills/` or `.comate/skills/` · global `~/.comate/skills/`
+- **agents**: `.comate/agents/` · global `~/.comate/agents/`
+- **note**: Three-tier config (global/project/local); .mdr is unique format
+
+### tencent-codebuddy
+- **detect**: `~/.codebuddy/`
+- **mcp**: global `~/.codebuddy/.mcp.json` (leading dot!) · project `.mcp.json` · root_key `mcpServers` · JSON · permissions: allow/ask/deny
+- **rules**: project `.codebuddy/rules/*.md` · frontmatter: alwaysApply, paths
+- **skills**: project `.codebuddy/skills//SKILL.md` · global `~/.codebuddy/skills/` · frontmatter: name, description, allowed-tools, context, agent, model, hooks
+- **commands**: project `.codebuddy/commands/`
+- **agents**: project `.codebuddy/agents/*.md` · global `~/.codebuddy/agents/*.md` · frontmatter: name, description, tools, model
+- **memory**: `CODEBUDDY.md` (project/user) · `settings.json` autoMemoryEnabled, typedMemory
+- **hooks**: SDK hooks + SKILL.md hooks field
+- **other**: `settings.json` / `settings.local.json`
+
+### kimi-code (Moonshot AI / 月之暗面)
+- **detect**: `~/.kimi-code/` (env var `KIMI_CODE_HOME` overrides; legacy kimi-cli used `~/.kimi/` with `KIMI_SHARE_DIR`)
+- **mcp**: global `~/.kimi-code/mcp.json` · project `/.kimi-code/mcp.json` · root_key `mcpServers` · JSON · stdio+HTTP+SSE · `kimi mcp list`
+- **rules**: global `~/.kimi-code/AGENTS.md` · project `AGENTS.md` (also `.kimi-code/AGENTS.md`, any subdir) · `/init` auto-generates
+- **skills**: global `~/.kimi-code/skills/` / `~/.agents/skills/` · project `.kimi-code/skills/` / `.agents/skills/` · extra dirs via `config.toml extra_skill_dirs`
+- **commands**: built-in slash commands (`/mcp`, `/init`, `/skill:`, `/hooks`, `/config`) · plugin commands (`:`) · NO standalone commands dir
+- **agents**: built-in `default`, `okabe` · custom via `--agent-file ` (YAML: extend, name, system_prompt_path, tools, exclude_tools, subagents) · subagents: coder, explore, plan
+- **hooks**: `~/.kimi-code/config.toml` `[[hooks]]` array · 13 events (PreToolUse, PostToolUse, PostToolUseFailure, UserPromptSubmit, Stop, StopFailure, SessionStart, SessionEnd, SubagentStart, SubagentStop, PreCompact, PostCompact, Notification) · blocking: PreToolUse, Stop, UserPromptSubmit
+- **memory**: no native memory · sessions at `~/.kimi-code/sessions///` (context.jsonl, wire.jsonl, state.json) · plans at `~/.kimi-code/plans/.md`
+- **other**: `~/.kimi-code/config.toml` (main config, TOML NOT JSON) · `~/.kimi-code/tui.toml` · `~/.kimi-code/credentials/` · `~/.kimi-code/mcp-oauth/`
+- **note**: Path is `~/.kimi-code/` NOT `~/.kimi/`; config is `config.toml` NOT `config.json`; legacy kimi-cli deprecated
+
+### workbuddy (WorkBuddy)
+- **detect**: `~/.workbuddy/`
+- **mcp**: global `~/.workbuddy/.mcp.json` · root_key `mcpServers` · JSON · stdio+HTTP · `workbuddy mcp list`
+- **skills**: global `~/.workbuddy/skills/` · project `.workbuddy/skills/`
+- **settings**: `~/.workbuddy/settings.json`
+- **note**: Path is `~/.workbuddy/` NOT `~/.workbuddy` root; skills live under `~/.workbuddy/skills/`
+
+### zcode (Zhipu AI / 智谱)
+- **detect**: `~/.zcode/`
+- **mcp**: global `~/.zcode/cli/config.json` · project `.zcode/config.json` · root_key `mcp.servers` (dot-path; also accepts `mcpServers`) · JSON · stdio+SSE+HTTP · can import from ~/.claude, ~/.codex, ~/.config/opencode, ~/.agents
+- **rules**: global `~/.zcode/AGENTS.md` · project `AGENTS.md` (uses AGENTS.md NOT CLAUDE.md; onboarding one-time CLAUDE.md import only)
+- **skills**: global `~/.zcode/skills//SKILL.md` · project skills dir · frontmatter: name, description
+- **commands**: user-level + project-level commands dirs (Markdown)
+- **agents**: global `~/.zcode/agents/` · project `.zcode/agents/` (Markdown)
+- **hooks**: settings.json `hooks` field (JSON)
+- **memory**: agent-memory dirs (global + project, Markdown)
+- **other**: API Key config via GUI (BigModel / Z.AI / Anthropic / OpenRouter / custom)
+- **note**: Root key `mcp.servers` (dot notation); uses `AGENTS.md` not `CLAUDE.md`; ZCode ≠ CodeGeeX (CodeGeeX has NO MCP/skills/rules)
+
+### minimax-code (MiniMax / 稀宇科技)
+- **detect**: MiniMax Code desktop app (user data dir, path not publicly documented)
+- **mcp**: MCP client capability not officially documented; built-in Agent Team / Skills / Memory system instead
+- **skills**: built-in Skills system (domain capabilities: doc processing, table analysis, PDF parsing, content writing)
+- **agents**: Agent Team (multi-agent cluster, auto task decomposition, parallel sub-agents)
+- **memory**: 3-tier: session-level + agent-level + global-level (experience accumulation, long-term knowledge)
+- **schedule**: built-in task scheduler (daily reports, email checks, periodic inspection)
+- **note**: Official recommends mmx CLI over MCP; desktop config not file-exposed
+
+### mmx-cli (MiniMax CLI)
+- **detect**: `~/.mmx/`
+- **mcp**: N/A (mmx IS the tool, not an MCP client) · config `~/.mmx/config.json` · JSON · Zod validated
+- **skills**: `npx skills add MiniMax-AI/cli` symlinks to `~/.claude/skills/`, `~/.openclaw/skills/`, TRAE, OpenCode, etc.
+- **commands**: `mmx text chat`, `mmx image generate`, `mmx video generate`, `mmx speech synthesize`, `mmx music generate`, `mmx vision describe`, `mmx search query`
+- **note**: Region trap: global=api.minimax.io / cn=api.minimaxi.com (extra 'i'); API Key + Host must match region; `mmx config set --key region --value global|cn` if 401
+
+### qoder-cn (Alibaba / 阿里 — formerly Tongyi Lingma, 2026-05-20 renamed)
+- **detect**: `~/.qoder/` (formerly `~/.lingma/`)
+- **mcp** (IDE): Settings → MCP Servers UI panel · root_key `mcpServers` · JSON
+- **mcp** (CLI): `qodercli mcp add -- ` · config at `~/.qoder/`
+- **rules**: project `AGENTS.md` (universal standard, not .cursorrules)
+- **skills**: Quest 2.0 system · Experts (specialist agents) · Subagent system
+- **commands**: `qodercli` CLI commands
+- **agents**: Quest 2.0 multi-agent · Experts (specialist team)
+- **note**: Renamed from Tongyi Lingma on 2026-05-20; Qoder CN (国内版, GLM/DeepSeek/Kimi/MiniMax models) vs Qoder (国际版 qoder.com, GPT/Claude); supports ModelScope MCP plaza
+
+### baidu-comate-ide (Baidu / 百度 — standalone IDE, distinct from plugin)
+- **detect**: Comate AI IDE desktop app (download from comate.baidu.com)
+- **mcp**: Zulu agent dialog → MCP icon config · root_key `mcpServers` · JSON · stdio+HTTP
+- **rules**: `.mdr` files · 3 activation modes (always/manual/fileMatch) · same .mdr format as plugin version
+- **agents**: Zulu multi-agent system (default entry) · Custom Agent · domain agents
+- **commands**: slash commands
+- **note**: Standalone IDE (2025-06-23 released) distinct from plugin version; global Rules/MCP config only since 2025-08 late version; cannot install official MS Python/C++ plugins (use BasedPyright/clangd)
+
+### tencent-codebuddy-ide (Tencent / 腾讯 — standalone IDE, distinct from plugin)
+- **detect**: CodeBuddy IDE desktop app
+- **mcp**: IDE settings → MCP · root_key `mcpServers` · JSON · shares config with CodeBuddy CLI (`~/.codebuddy/.mcp.json`)
+- **rules/skills/commands/agents/hooks/memory**: shares underlying capability with tencent-codebuddy CLI entry (see above)
+- **note**: Standalone IDE distinct from plugin version; "对话即编程" full-flow (requirement→design→dev→deploy)
+
+### iflycode (iFlytek / 讯飞星火)
+- **detect**: iFlyCode desktop client / plugin
+- **mcp**: via UI config bar (paste JSON) · root_key `mcpServers` · JSON
+- **rules/skills/commands/agents/hooks/memory**: primarily UI-configured; no documented file-level project config paths
+- **note**: Limited public docs on file-level config; MCP via UI only
+
+### raccoon-ai (SenseTime / 商汤代码小浣熊)
+- **detect**: Raccoon AI desktop client
+- **mcp**: via UI config bar (paste JSON) · root_key `mcpServers` · JSON
+- **rules/skills**: UI-configured; no documented file-level project config paths
+- **note**: Limited public docs on file-level config; MCP via UI only
+
+### monkeycode (Chaitin Tech / 长亭科技)
+- **detect**: `~/.monkeycode/` (AGPL-3.0 open source, private deploy supported)
+- **mcp**: root_key `mcpServers` · JSON · multi-model dispatch middleware
+- **rules**: SDD (Spec-Driven Development) specification files
+- **skills**: MonkeyScan security scanning · Git async workflow (@Monickname task dispatch)
+- **note**: Enterprise security-focused; AGPL-3.0 fully open source; supports offline private deployment
+
+### vecli (Volcano Engine / 火山引擎)
+- **detect**: `~/.vecli/` (npm `@volcengine/vecli`, 2025-09 released)
+- **mcp**: root_key `mcpServers` · JSON · deep integration with Volcano cloud services · AK/SK or SSO auth
+- **rules**: `AGENTS.md`
+- **models**: Doubao 1.6, Kimi-K2, DeepSeek v3.1 (via Volcano Ark)
+- **note**: Distinct from Trae CLI (different ByteDance product line: Volcano Engine vs Trae brand); `ve`/`@volcengine/cli` is cloud-resource CLI (NOT AI tool) — do not confuse
+
+---
+
+## Cloud / Web AI Platforms
+
+### bolt-new (StackBlitz)
+- **detect**: `~/.boltai/`
+- **mcp**: `~/.boltai/mcp.json` · root_key `servers` (NOT `mcpServers`!) · JSON · stdio · supports import from Cursor/Claude Desktop · Smithery CLI auto-config · remote MCP via `mcp-remote`
+- **note**: bolt.diy (open source) uses same `servers` root key; UI Plugin Dropdown to enable/disable; API key/MCP OAuth/None auth
+
+### qodo (formerly CodiumAI)
+- **detect**: VS Code/JetBrains plugin (Qodo Gen) + CLI (Qodo Command)
+- **mcp**: IDE Tools Management UI · root_key `mcpServers` · JSON · local (stdio) + remote SSE (`url` field) · Qodo CLI supports `--mcp` (self as MCP server)
+- **agents**: `agents/.toml` (TOML format: instructions, tools, commands)
+- **note**: CodiumAI ≠ Codeium (Codeium→Windsurf); Enterprise has "Agentic Tools Allow List"; open-aware context engine at `https://open-aware.qodo.ai/mcp/`
+
+### devin (Cognition)
+- **detect**: Cloud SaaS (dashboard config, no local files)
+- **mcp**: Devin dashboard → MCP marketplace (50+ servers) · root_key `mcpServers` · JSON · stdio+SSE+HTTP · also exposes `https://mcp.devin.ai/mcp` as server
+- **note**: Cloud-config only; bidirectional MCP (client + server); Devin's wiki exposed via MCP
+
+### v0 (Vercel)
+- **detect**: Web platform (`v0.app/chat/settings/mcp-connections`)
+- **mcp**: UI config (chat connectors) · auth: No Auth / Custom Headers / Bearer Token / OAuth 2.1 · also exposes `https://mcp.v0.dev` / `https://mcp.vercel.com` as server
+- **note**: Bidirectional MCP; Vercel MCP server only supports whitelisted AI clients; v0+MCP is 2026-03 feature
+
+### lovable
+- **detect**: Web platform (`lovable.dev`)
+- **mcp**: Chat Connectors UI (client side) · built-in connectors: Notion, Linear, Jira, Confluence, n8n, Miro, Sentry · auth: OAuth/Bearer/None · also exposes `https://mcp.lovable.dev` as server (Research Preview, 2026-05)
+- **note**: Bidirectional MCP; server-side OAuth limited to ChatGPT/Claude/Claude Code/Cursor/VS Code; Enterprise server disabled by default
+
+### gptel-mcp-el (Emacs)
+- **detect**: `~/.emacs.d/` (init.el config)
+- **mcp**: via `mcp.el` package (elisp config) · `mcp-hub-start` · register MCP tools to gptel
+- **rules**: elisp config
+- **note**: Emacs ecosystem; requires lisp knowledge; gptel supports ChatGPT/Claude/Gemini/Ollama/Grok; integrated into Doom Emacs
+
+---
+
+## Cross-IDE Memory Solutions
+
+| Solution | Type | Setup | Privacy | Cross-IDE |
+|----------|------|-------|---------|-----------|
+| **mem0 Cloud** | MCP server (HTTP) | `https://mcp.mem0.ai/mcp` + API key | Cloud | All MCP clients |
+| **OpenMemory MCP** | MCP server (local Docker) | `http://localhost:8765` | Local | All MCP clients |
+| **Pieces LTM-2.7** | MCP server (local PiecesOS) | `http://localhost:39300/.../sse` | Local | All MCP clients |
+| **Cline memory-bank** | Markdown files | `memory-bank/*.md` in project | Local | Manual copy |
+| **Claude Code memory** | Markdown files | `~/.claude/projects//memory/` | Local | Manual copy |
+
+For MCP-based memory (mem0, OpenMemory, Pieces): install the MCP server in target IDE via standard MCP config. Memory follows API key (cloud) or stays on device (local).
diff --git a/skills/agent-skills-setup/references/openclaw.md b/skills/agent-skills-setup/references/openclaw.md
new file mode 100644
index 0000000000..a6db6cbfe7
--- /dev/null
+++ b/skills/agent-skills-setup/references/openclaw.md
@@ -0,0 +1,413 @@
+# OpenClaw Skills Configuration / OpenClaw 技能配置
+
+OpenClaw has the richest skills model in this repository: it supports bundled skills, shared user-managed skills, per-agent workspace skills, ClawHub registry distribution, metadata-driven dependency installers, per-run env injection, and hot reload.
+
+OpenClaw 是本仓库里技能模型最完整的运行时之一,支持运行时内置技能、用户共享技能、按 agent 隔离的工作区技能、ClawHub 注册表分发、基于元数据的依赖安装、按次注入环境变量,以及技能热重载。
+
+This guide maps those official capabilities into a reproducible setup and maintenance workflow.
+
+本指南把这些官方能力整理成可复现的配置、同步和维护流程。
+
+## 1. Official Paths and Load Order / 官方路径与加载顺序
+
+OpenClaw loads skills from four sources.
+
+OpenClaw 会从四类来源加载技能。
+
+| Layer / 层级 | Path / 路径 | Scope / 作用域 | Precedence / 优先级 |
+|---|---|---|---|
+| Workspace skills / 工作区技能 | `/skills/` | One agent only / 单个 agent | Highest / 最高 |
+| Managed skills / 共享托管技能 | `~/.openclaw/skills/` | Shared on one machine / 单机共享 | Medium / 中 |
+| Bundled skills / 内置技能 | Built into OpenClaw / OpenClaw 自带 | Runtime-wide / 运行时级别 | Lower / 较低 |
+| Extra dirs / 额外目录 | `skills.load.extraDirs` | Shared packs / 共享技能包 | Lowest / 最低 |
+
+Resolution order for duplicate skill names:
+
+同名 skill 的解析顺序为:
+
+`/skills` → `~/.openclaw/skills` → bundled skills → `skills.load.extraDirs`
+
+Important filesystem locations from the official docs:
+
+官方文档中最重要的路径:
+
+- Config file / 配置文件: `~/.openclaw/openclaw.json`
+- Global env fallback / 全局环境变量兜底文件: `~/.openclaw/.env`
+- Shared skills / 共享技能目录: `~/.openclaw/skills`
+- Default workspace / 默认工作区: `~/.openclaw/workspace`
+- Agent sessions / agent 会话目录: `~/.openclaw/agents//sessions/`
+- Multi-instance overrides / 多实例覆盖变量: `OPENCLAW_CONFIG_PATH`, `OPENCLAW_STATE_DIR`
+
+## 2. Global Configuration / 全局配置
+
+All OpenClaw skill settings live under the `skills` key in `~/.openclaw/openclaw.json`.
+
+OpenClaw 的技能配置统一写在 `~/.openclaw/openclaw.json` 的 `skills` 节点下。
+
+### 2.1 Core fields / 核心字段
+
+| Field / 字段 | Purpose / 作用 | Official behavior / 官方行为 |
+|---|---|---|
+| `skills.allowBundled` | Allowlist bundled skills / 限定启用哪些内置技能 | Does not affect managed or workspace skills / 不影响托管或工作区技能 |
+| `skills.load.extraDirs` | Extra scan roots / 额外扫描目录 | Lowest precedence / 最低优先级 |
+| `skills.load.watch` | Skill watcher switch / 技能监听开关 | Default `true` / 默认为 `true` |
+| `skills.load.watchDebounceMs` | Watch debounce / 监听防抖 | Default `250` / 默认为 `250` |
+| `skills.install.preferBrew` | Prefer Homebrew / 优先 Homebrew | Default `true` / 默认为 `true` |
+| `skills.install.nodeManager` | Node package backend / Node 包管理器 | `npm`, `pnpm`, `yarn`, `bun` |
+| `skills.entries..enabled` | Enable or disable one skill / 启用或禁用单个技能 | `false` disables it even if present / 即使目录存在也会被禁用 |
+| `skills.entries..env` | Per-skill env injection / 单 skill 环境变量注入 | Added only if absent from process env / 仅在进程环境缺失时补充 |
+| `skills.entries..apiKey` | SecretRef or convenience key / SecretRef 或密钥快捷配置 | Useful with `metadata.openclaw.primaryEnv` / 常用于 `metadata.openclaw.primaryEnv` |
+| `skills.entries..config` | Free-form config bag / 自定义配置对象 | Skill-defined settings / 由 skill 自己解释 |
+
+### 2.2 Example / 示例
+
+```json5
+// ~/.openclaw/openclaw.json
+{
+ agents: {
+ defaults: {
+ workspace: "~/.openclaw/workspace",
+ },
+ },
+ skills: {
+ allowBundled: ["peekaboo", "gifgrep"],
+ load: {
+ extraDirs: [
+ "~/.gemini/antigravity/skills",
+ "~/Projects/internal-openclaw-skill-pack/skills",
+ ],
+ watch: true,
+ watchDebounceMs: 250,
+ },
+ install: {
+ preferBrew: true,
+ nodeManager: "npm",
+ },
+ entries: {
+ "agent-skills-setup": {
+ enabled: true,
+ env: {
+ OPENCLAW_SKILLS_SOURCE: "~/.gemini/antigravity/skills",
+ },
+ config: {
+ preferredScope: "managed",
+ },
+ },
+ },
+ },
+}
+```
+
+### 2.3 Environment resolution / 环境变量解析顺序
+
+OpenClaw reads missing env vars from these sources:
+
+当进程环境变量缺失时,OpenClaw 会依次尝试以下来源:
+
+1. Parent process environment / 父进程环境变量
+2. `.env` in the current working directory / 当前工作目录下的 `.env`
+3. `~/.openclaw/.env`
+4. `skills.entries..env` in `openclaw.json`
+
+Important caveat: `skills.entries.*.env` and `skills.entries.*.apiKey` only affect host runs. They do not automatically populate Docker sandbox env.
+
+重要限制:`skills.entries.*.env` 和 `skills.entries.*.apiKey` 只对宿主机运行有效,不会自动注入 Docker sandbox。
+
+## 3. Per-Agent Configuration, Isolation, and Inheritance / 单 Agent 配置、隔离与继承
+
+OpenClaw isolates agents through workspaces. Each workspace can carry its own `skills/` directory.
+
+OpenClaw 通过工作区实现 agent 隔离,每个工作区都可以拥有独立的 `skills/` 目录。
+
+### 3.1 Default workspace and explicit agents / 默认工作区与显式 agent
+
+```json5
+{
+ agents: {
+ defaults: {
+ workspace: "~/.openclaw/workspace",
+ },
+ list: [
+ {
+ id: "home",
+ default: true,
+ workspace: "~/.openclaw/workspace-home",
+ },
+ {
+ id: "work",
+ workspace: "~/.openclaw/workspace-work",
+ },
+ ],
+ },
+}
+```
+
+Each workspace can then override shared skills through its own directory.
+
+随后每个工作区都可以通过自己的目录覆盖共享技能。
+
+- `~/.openclaw/workspace-home/skills/`
+- `~/.openclaw/workspace-work/skills/`
+
+### 3.2 Scope isolation rules / 作用域隔离规则
+
+| Scope / 范围 | Isolation / 隔离度 | Shared? / 是否共享 | Typical use / 典型用途 |
+|---|---|---|---|
+| `/skills/` | Per agent / 单 agent | No / 否 | Persona or team overrides / 角色或团队定制 |
+| `~/.openclaw/skills/` | Machine-wide / 单机级别 | Yes / 是 | Shared personal pack / 共享个人技能包 |
+| `skills.load.extraDirs` | Machine-wide / 单机级别 | Yes / 是 | Mirrored read-only packs / 镜像只读技能包 |
+| Bundled skills / 内置技能 | Runtime-wide / 运行时级别 | Yes / 是 | Baseline built-ins / 基础内置能力 |
+
+### 3.3 Priority and inheritance / 优先级与继承
+
+OpenClaw skill precedence is path-based:
+
+OpenClaw 的技能优先级是按路径决定的:
+
+1. Workspace skill wins / 工作区技能优先。
+2. Shared managed skill is next / 其次是共享托管技能。
+3. Bundled skill is fallback / 然后是内置技能。
+4. `skills.load.extraDirs` are lowest / `skills.load.extraDirs` 最低。
+
+Config inheritance is different:
+
+配置继承与技能文件继承不同:
+
+- `agents.defaults.*` provides base agent settings.
+- `agents.defaults.*` 提供默认 agent 配置。
+- `agents.list[]` overrides only the matching agent.
+- `agents.list[]` 只覆盖匹配到的 agent。
+- `skills.entries.*` is global to the gateway process.
+- `skills.entries.*` 对当前 gateway 进程全局生效。
+
+If you need full isolation of config, skills, sessions, and credentials, split by `OPENCLAW_CONFIG_PATH` and `OPENCLAW_STATE_DIR`.
+
+如果你需要配置、技能、会话和凭据的完全隔离,应拆分 `OPENCLAW_CONFIG_PATH` 和 `OPENCLAW_STATE_DIR`。
+
+```bash
+OPENCLAW_CONFIG_PATH=~/.openclaw/work.json \
+OPENCLAW_STATE_DIR=~/.openclaw-work \
+openclaw gateway --port 19001
+```
+
+### 3.4 Sandboxed agents / 沙箱 agent
+
+When a skill must run inside Docker sandboxed agents, mirror env into sandbox settings as well.
+
+当 skill 需要在 Docker sandbox 中运行时,除了 `skills.entries` 外,还要把环境变量同步到 sandbox 配置中。
+
+```json5
+{
+ agents: {
+ defaults: {
+ sandbox: {
+ mode: "non-main",
+ backend: "docker",
+ docker: {
+ env: {
+ LANG: "C.UTF-8",
+ GEMINI_API_KEY: "${GEMINI_API_KEY}",
+ },
+ setupCommand: "apt-get update && apt-get install -y git curl jq",
+ },
+ },
+ },
+ },
+}
+```
+
+## 4. Skill Metadata and Installers / 技能元数据与安装器
+
+OpenClaw extends skill metadata under `metadata.openclaw`.
+
+OpenClaw 通过 `metadata.openclaw` 扩展技能元数据。
+
+### 4.1 Example metadata / 元数据示例
+
+```yaml
+---
+name: gifgrep
+description: Search GIF providers with CLI/TUI, download results, and extract stills/sheets.
+homepage: https://gifgrep.com
+metadata: {"openclaw":{"emoji":"🧲","requires":{"bins":["gifgrep"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/gifgrep","bins":["gifgrep"],"label":"Install gifgrep (brew)"}]}}
+---
+```
+
+### 4.2 Official installer kinds / 官方安装器类型
+
+| Kind | Fields |
+|---|---|
+| `brew` | `formula`, `bins`, `label`, `os` |
+| `node` | `package`, `bins`, `label`, `os` |
+| `go` | `module`, `bins`, `label`, `os` |
+| `uv` | `package`, `bins`, `label`, `os` |
+| `download` | `url`, `sha256`, `archive`, `extract`, `stripComponents`, `targetDir`, `bins`, `label`, `os` |
+
+Notes:
+
+说明:
+
+- OpenClaw prefers `brew` when available, otherwise `node` if multiple installers exist.
+- 存在多个安装器时,OpenClaw 优先使用 `brew`,否则通常回落到 `node`。
+- `download` installers default to `~/.openclaw/tools/` if `targetDir` is omitted.
+- `download` 安装器在未指定 `targetDir` 时,默认解压到 `~/.openclaw/tools/`。
+- Set `sha256` to the expected 64-character SHA-256 digest. The helper verifies it before extracting or writing to `targetDir`; legacy specs without it emit a warning.
+- 请把 `sha256` 设为预期的 64 位 SHA-256 摘要。辅助脚本会在解压或写入 `targetDir` 前完成校验;旧版未提供该字段的配置会输出警告。
+- `skills.install.nodeManager` affects skill dependency installs, not the OpenClaw runtime itself.
+- `skills.install.nodeManager` 只控制 skill 依赖安装,不控制 OpenClaw 运行时本身的安装方式。
+
+## 5. Automatic Setup Workflow / 自动化配置流程
+
+This repository includes a dedicated helper:
+
+本仓库已经内置了一个专用自动化脚本:
+
+```bash
+bash skills/agent-skills-setup/scripts/auto-configure-openclaw-skills.sh \
+ --yes \
+ --scope both \
+ --agent home:~/.openclaw/workspace-home \
+ --agent work:~/.openclaw/workspace-work \
+ --default-agent home \
+ --node-manager npm \
+ --env agent-skills-setup:OPENCLAW_SKILLS_SOURCE=~/.gemini/antigravity/skills
+```
+
+What it does:
+
+它会完成以下动作:
+
+1. Install OpenClaw if `openclaw` is missing.
+2. 如果 `openclaw` 缺失,则安装 OpenClaw。
+3. Install ClawHub if `clawhub` is missing.
+4. 如果 `clawhub` 缺失,则安装 ClawHub。
+5. Sync selected skills into `~/.openclaw/skills/` and workspace `skills/` directories.
+6. 把指定技能同步到 `~/.openclaw/skills/` 和各 workspace 的 `skills/` 目录。
+7. Parse `metadata.openclaw.install` and install declared dependencies.
+8. 解析 `metadata.openclaw.install` 并安装声明的依赖。
+9. Patch `~/.openclaw/openclaw.json` with `skills.*` and `agents.*` settings.
+10. 把 `skills.*` 和 `agents.*` 配置写入 `~/.openclaw/openclaw.json`。
+11. Run `openclaw doctor` after a real apply.
+12. 真正执行后运行 `openclaw doctor` 做健康检查。
+
+## 6. Updates and Lifecycle / 更新与生命周期管理
+
+OpenClaw has two first-class update surfaces plus local mirrors.
+
+OpenClaw 存在两个一等更新面,再加上本地镜像更新层。
+
+### 6.1 Runtime updates / 运行时更新
+
+```bash
+openclaw update
+openclaw update --channel beta
+openclaw update --tag main
+openclaw update --dry-run
+```
+
+### 6.2 ClawHub updates / ClawHub 更新
+
+```bash
+clawhub update --all
+```
+
+ClawHub stores install state in `.clawhub/lock.json` and compares local content hashes against published versions.
+
+ClawHub 会把安装状态记录在 `.clawhub/lock.json` 中,并用内容哈希对比本地版本和已发布版本。
+
+### 6.3 Local mirror updates / 本地镜像更新
+
+```bash
+bash skills/agent-skills-setup/scripts/update-openclaw-skills.sh
+```
+
+This helper combines:
+
+这个脚本会把以下流程串起来:
+
+1. `openclaw update`
+2. `clawhub update --all`
+3. `rsync` refresh for `~/.openclaw/skills/` and workspace `skills/`
+
+## 7. OpenClaw vs Other IDE Skills / OpenClaw 与其他 IDE 技能体系对比
+
+| Capability / 能力 | OpenClaw | Claude/Codex/Trae/Copilot |
+|---|---|---|
+| Bundled skills / 内置技能 | Yes | Usually no |
+| Shared managed skills / 共享托管技能 | Yes | Sometimes, but less structured |
+| Per-agent workspaces / 按 agent 工作区隔离 | Yes | Usually project or global only |
+| Load-time gating / 加载时条件门控 | Yes | Rare |
+| Metadata-driven installers / 基于元数据安装依赖 | Yes | Rare |
+| Per-run env injection / 单次运行环境注入 | Yes | Usually external only |
+| Registry updates / 注册表更新 | Yes, via ClawHub | Usually ad hoc Git repos |
+| Watcher and hot reload / 监听与热更新 | Yes | Rare |
+
+Practical implication:
+
+实际含义:
+
+OpenClaw is not just another folder for `SKILL.md`; it is a managed skill runtime.
+
+OpenClaw 不只是多了一个放 `SKILL.md` 的目录,而是一个带安装、门控、更新和发现能力的托管技能运行时。
+
+## 8. Cross-IDE Integration Strategy / 跨 IDE 集成策略
+
+Recommended model:
+
+推荐模型:
+
+1. Keep Antigravity as the authoring source of truth.
+2. 保持 Antigravity 为唯一事实来源。
+3. Run `sync-global-skills.sh --targets claude,codex,copilot,openclaw,trae,trae-cn` for global mirrors.
+4. 通过 `sync-global-skills.sh --targets claude,codex,copilot,openclaw,trae,trae-cn` 做全局镜像。
+5. Use `auto-configure-openclaw-skills.sh` when OpenClaw-specific dependency installs or per-agent routing are required.
+6. 当需要 OpenClaw 特有的依赖安装和按 agent 路由时,使用 `auto-configure-openclaw-skills.sh`。
+7. Use `update-openclaw-skills.sh` for maintenance.
+8. 维护阶段使用 `update-openclaw-skills.sh`。
+
+## 9. Validation Plan / 验证方案
+
+### 9.1 Automated smoke test / 自动化冒烟测试
+
+```bash
+bash skills/agent-skills-setup/scripts/test-openclaw-support.sh
+```
+
+This covers managed sync, workspace sync, config patching, dependency install flow, and update refresh behavior.
+
+这个测试覆盖共享目录同步、工作区同步、配置写入、依赖安装流程以及更新刷新行为。
+
+### 9.2 Real-machine test guidance / 真实机器测试指引
+
+Use isolated state roots when testing on a machine that already has OpenClaw installed.
+
+当你在已经安装 OpenClaw 的机器上测试时,必须使用隔离的状态目录。
+
+```bash
+OPENCLAW_STATE_DIR=/tmp/openclaw-test-state \
+OPENCLAW_CONFIG_PATH=/tmp/openclaw-test-state/openclaw.json \
+AGENT_SKILLS_OPENCLAW_DIR=/tmp/openclaw-test-state/skills \
+bash skills/agent-skills-setup/scripts/auto-configure-openclaw-skills.sh --dry-run
+```
+
+The script refuses global installs, config writes, and replacement syncs unless `--yes` is supplied. Review `--dry-run` output first; existing skill directories and config files receive timestamped `.bak.*` copies before replacement.
+
+该脚本在未提供 `--yes` 时会拒绝全局安装、配置写入和替换同步。请先审查 `--dry-run` 输出;替换前,已有技能目录和配置文件会生成带时间戳的 `.bak.*` 备份。
+
+Important note: even with isolated config paths, `openclaw doctor` may still observe or interact with machine-global gateway state such as a running local service or LaunchAgent. Use dry runs first when non-interference matters.
+
+重要说明:即使配置目录和状态目录已经隔离,`openclaw doctor` 仍然可能探测到本机级别的网关状态,例如正在运行的本地服务或 LaunchAgent。在“绝不干预现有环境”这一要求下,应优先使用 dry run。
+
+### 9.3 Release guardrails / 发布门槛
+
+1. `bash -n` passes for every new script.
+2. `test-openclaw-support.sh` passes locally.
+3. `sync-global-skills.sh --dry-run --targets openclaw` shows only expected changes.
+4. `auto-configure-openclaw-skills.sh --dry-run` prints the expected plan.
+5. Real-machine verification is documented with any observed gateway-side effects.
+
+1. 每个新增脚本都通过 `bash -n`。
+2. `test-openclaw-support.sh` 本地通过。
+3. `sync-global-skills.sh --dry-run --targets openclaw` 只显示预期变更。
+4. `auto-configure-openclaw-skills.sh --dry-run` 能输出正确计划。
+5. 真实机器验证结果已记录,任何网关侧副作用都已明确写出。
diff --git a/skills/agent-skills-setup/references/publishing.md b/skills/agent-skills-setup/references/publishing.md
new file mode 100644
index 0000000000..626b29a0c8
--- /dev/null
+++ b/skills/agent-skills-setup/references/publishing.md
@@ -0,0 +1,81 @@
+# Publishing Skills
+
+How to publish and share your skills.
+
+## Skill Format
+
+```markdown
+---
+name: skill-name
+description: What this skill does
+triggers:
+ - trigger phrase 1
+ - trigger phrase 2
+---
+
+# Skill Title
+
+Content...
+```
+
+## Publishing to GitHub
+
+### 1. Create Repository
+
+```bash
+git init skills-repo
+cd skills-repo
+mkdir -p skills/your-skill-name
+```
+
+### 2. Structure
+
+```
+skills-repo/
+├── SKILL.md
+└── skills/
+ └── your-skill/
+ ├── SKILL.md
+ └── references/
+ └── guide.md
+```
+
+### 3. Commit and Push
+
+```bash
+git add .
+git commit -m "Add your-skill skill"
+git remote add origin https://github.com/username/skills-repo.git
+git push -u origin main
+```
+
+## Using Published Skills
+
+### In Claude Code / Hermes
+
+```
+skill_view(name="your-skill-name")
+```
+
+### From GitHub
+
+```
+Load skill from Luckycat133/skills-repo/your-skill
+```
+
+## Best Practices
+
+1. **Clear triggers**: Include common phrases users might say
+2. **Step-by-step**: Use numbered lists for procedures
+3. **Code examples**: Provide working code snippets
+4. **Verification**: Include commands to test the setup
+5. **Update regularly**: Keep content current
+
+## Categories
+
+Organize skills by category:
+- `devops/` - Infrastructure, CI/CD
+- `frontend/` - UI, React, CSS
+- `backend/` - APIs, databases
+- `migration/` - Tool migrations
+- `general/` - Generic helpers
diff --git a/skills/agent-skills-setup/scripts/auto-configure-openclaw-skills.sh b/skills/agent-skills-setup/scripts/auto-configure-openclaw-skills.sh
new file mode 100644
index 0000000000..b4ca1d1138
--- /dev/null
+++ b/skills/agent-skills-setup/scripts/auto-configure-openclaw-skills.sh
@@ -0,0 +1,923 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+SOURCE_DIR="${AGENT_SKILLS_SOURCE_DIR:-${HOME}/.gemini/antigravity/skills}"
+STATE_DIR="${OPENCLAW_STATE_DIR:-${HOME}/.openclaw}"
+CONFIG_PATH="${OPENCLAW_CONFIG_PATH:-${STATE_DIR}/openclaw.json}"
+MANAGED_DIR="${STATE_DIR}/skills"
+ENV_FILE="${STATE_DIR}/.env"
+BIN_DIR="${STATE_DIR}/bin"
+TOOLS_DIR="${STATE_DIR}/tools"
+NODE_MANAGER="npm"
+PREFER_BREW=1
+WATCH=1
+WATCH_DEBOUNCE_MS=250
+SCOPE="both"
+DRY_RUN=0
+CONFIRM_WRITE=0
+YES=0
+SKIP_OPENCLAW_INSTALL=0
+SKIP_CLAWHUB_INSTALL=0
+SKIP_DOCTOR=0
+DEFAULT_AGENT_ID=""
+
+declare -a WORKSPACES=()
+declare -a AGENTS=()
+declare -a EXTRA_DIRS=()
+declare -a ENV_ASSIGNMENTS=()
+declare -a API_KEY_ENV_ASSIGNMENTS=()
+declare -a REQUESTED_SKILLS=()
+
+usage() {
+ cat <<'EOF'
+Usage: auto-configure-openclaw-skills.sh [options]
+
+Install and configure OpenClaw skill support from the Antigravity source-of-truth.
+
+Options:
+ --source Source skill root. Default: ~/.gemini/antigravity/skills
+ --config OpenClaw config file. Default: ~/.openclaw/openclaw.json
+ --managed-dir Shared managed skill directory. Default: ~/.openclaw/skills
+ --workspace Sync skills into /skills. Repeatable.
+ --agent Add/update agents.list[] and sync /skills. Repeatable.
+ --default-agent Mark the matching --agent entry as default.
+ --skills Comma-separated subset of skills to sync. Default: all skills.
+ --scope managed|workspace|both
+ Where to sync skills. Default: both.
+ --node-manager npm | pnpm | yarn | bun. Default: npm.
+ --prefer-brew true|false Prefer Homebrew installers when available. Default: true.
+ --extra-dir Append a shared skills.load.extraDirs entry. Repeatable.
+ --env Set skills.entries..env.. Repeatable.
+ --api-key-env Set skills.entries..apiKey SecretRef. Repeatable.
+ --watch true|false Configure skills.load.watch. Default: true.
+ --watch-debounce-ms Configure skills.load.watchDebounceMs. Default: 250.
+ --skip-openclaw-install Do not install OpenClaw automatically.
+ --skip-clawhub-install Do not install ClawHub automatically.
+ --skip-doctor Do not run `openclaw doctor` after applying changes.
+ --dry-run Print planned commands without changing the system.
+ --yes Confirm global installs, config writes, and replacement syncs.
+ -h, --help Show this help text.
+EOF
+}
+
+die() {
+ echo "ERROR: $*" >&2
+ exit 1
+}
+
+log() {
+ echo "$*"
+}
+
+run_cmd() {
+ printf '+ '
+ printf '%q ' "$@"
+ printf '\n'
+ if [[ $DRY_RUN -eq 0 ]]; then
+ "$@"
+ fi
+}
+
+command_exists() {
+ command -v "$1" >/dev/null 2>&1
+}
+
+sha256_file() {
+ local file_path="$1"
+
+ if command_exists sha256sum; then
+ sha256sum "$file_path" | awk '{print $1}'
+ elif command_exists shasum; then
+ shasum -a 256 "$file_path" | awk '{print $1}'
+ elif command_exists openssl; then
+ openssl dgst -sha256 "$file_path" | awk '{print $NF}'
+ else
+ die "A SHA-256 tool is required (sha256sum, shasum, or openssl)"
+ fi
+}
+
+check_prerequisites() {
+ local missing=()
+
+ for tool in curl rsync node; do
+ command_exists "$tool" || missing+=("$tool")
+ done
+
+ if ! command_exists tar && ! command_exists unzip; then
+ missing+=("tar-or-unzip")
+ fi
+
+ if [[ ${#missing[@]} -gt 0 ]]; then
+ die "缺少必要的前置工具:${missing[*]}。请先安装后再运行本脚本(例如 macOS: brew install curl rsync gnu-tar node;node 也可从 nodejs.org 下载安装;解压工具需要 tar 或 unzip 之一)。"
+ fi
+}
+
+parse_bool() {
+ local lowered
+
+ lowered="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
+ case "$lowered" in
+ true|1|yes|on)
+ echo 1
+ ;;
+ false|0|no|off)
+ echo 0
+ ;;
+ *)
+ die "Invalid boolean value: $1"
+ ;;
+ esac
+}
+
+split_csv_into_array() {
+ local raw="$1"
+ local output_name="$2"
+ local old_ifs="$IFS"
+ local -a parsed=()
+
+ IFS=',' read -r -a parsed <<< "$raw"
+ IFS="$old_ifs"
+
+ eval "$output_name=()"
+ local item
+ for item in "${parsed[@]}"; do
+ eval "$output_name+=(\"\$item\")"
+ done
+}
+
+ensure_supported_scope() {
+ case "$SCOPE" in
+ managed|workspace|both)
+ ;;
+ *)
+ die "Unsupported scope: $SCOPE"
+ ;;
+ esac
+}
+
+ensure_node_manager_supported() {
+ case "$NODE_MANAGER" in
+ npm|pnpm|yarn|bun)
+ ;;
+ *)
+ die "Unsupported node manager: $NODE_MANAGER"
+ ;;
+ esac
+}
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --source)
+ [[ $# -ge 2 ]] || die "--source requires a value"
+ SOURCE_DIR="$2"
+ shift 2
+ ;;
+ --config)
+ [[ $# -ge 2 ]] || die "--config requires a value"
+ CONFIG_PATH="$2"
+ shift 2
+ ;;
+ --managed-dir)
+ [[ $# -ge 2 ]] || die "--managed-dir requires a value"
+ MANAGED_DIR="$2"
+ shift 2
+ ;;
+ --workspace)
+ [[ $# -ge 2 ]] || die "--workspace requires a value"
+ WORKSPACES+=("$2")
+ shift 2
+ ;;
+ --agent)
+ [[ $# -ge 2 ]] || die "--agent requires a value"
+ AGENTS+=("$2")
+ shift 2
+ ;;
+ --default-agent)
+ [[ $# -ge 2 ]] || die "--default-agent requires a value"
+ DEFAULT_AGENT_ID="$2"
+ shift 2
+ ;;
+ --skills)
+ [[ $# -ge 2 ]] || die "--skills requires a value"
+ split_csv_into_array "$2" REQUESTED_SKILLS
+ shift 2
+ ;;
+ --scope)
+ [[ $# -ge 2 ]] || die "--scope requires a value"
+ SCOPE="$2"
+ shift 2
+ ;;
+ --node-manager)
+ [[ $# -ge 2 ]] || die "--node-manager requires a value"
+ NODE_MANAGER="$2"
+ shift 2
+ ;;
+ --prefer-brew)
+ [[ $# -ge 2 ]] || die "--prefer-brew requires a value"
+ PREFER_BREW="$(parse_bool "$2")"
+ shift 2
+ ;;
+ --extra-dir)
+ [[ $# -ge 2 ]] || die "--extra-dir requires a value"
+ EXTRA_DIRS+=("$2")
+ shift 2
+ ;;
+ --env)
+ [[ $# -ge 2 ]] || die "--env requires a value"
+ ENV_ASSIGNMENTS+=("$2")
+ shift 2
+ ;;
+ --api-key-env)
+ [[ $# -ge 2 ]] || die "--api-key-env requires a value"
+ API_KEY_ENV_ASSIGNMENTS+=("$2")
+ shift 2
+ ;;
+ --watch)
+ [[ $# -ge 2 ]] || die "--watch requires a value"
+ WATCH="$(parse_bool "$2")"
+ shift 2
+ ;;
+ --watch-debounce-ms)
+ [[ $# -ge 2 ]] || die "--watch-debounce-ms requires a value"
+ WATCH_DEBOUNCE_MS="$2"
+ shift 2
+ ;;
+ --skip-openclaw-install)
+ SKIP_OPENCLAW_INSTALL=1
+ shift
+ ;;
+ --skip-clawhub-install)
+ SKIP_CLAWHUB_INSTALL=1
+ shift
+ ;;
+ --skip-doctor)
+ SKIP_DOCTOR=1
+ shift
+ ;;
+ --dry-run)
+ DRY_RUN=1
+ shift
+ ;;
+ --yes)
+ CONFIRM_WRITE=1
+ YES=1
+ shift
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ *)
+ die "Unknown argument: $1"
+ ;;
+ esac
+done
+
+ensure_supported_scope
+ensure_node_manager_supported
+check_prerequisites
+
+[[ -d "$SOURCE_DIR" ]] || die "Source skills directory not found: $SOURCE_DIR"
+
+if [[ ${#REQUESTED_SKILLS[@]} -eq 0 ]]; then
+ while IFS= read -r skill_name; do
+ REQUESTED_SKILLS+=("$skill_name")
+ done < <(find "$SOURCE_DIR" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort)
+fi
+
+[[ ${#REQUESTED_SKILLS[@]} -gt 0 ]] || die "No skills selected"
+
+for skill_name in "${REQUESTED_SKILLS[@]}"; do
+ [[ -d "$SOURCE_DIR/$skill_name" ]] || die "Requested skill not found: $SOURCE_DIR/$skill_name"
+done
+
+if [[ ${#AGENTS[@]} -gt 0 ]]; then
+ for agent_spec in "${AGENTS[@]}"; do
+ [[ "$agent_spec" == *:* ]] || die "--agent must be in id:workspace format"
+ WORKSPACES+=("${agent_spec#*:}")
+ done
+fi
+
+install_openclaw_if_needed() {
+ if command_exists openclaw; then
+ return 0
+ fi
+
+ if [[ $SKIP_OPENCLAW_INSTALL -eq 1 ]]; then
+ die "OpenClaw is not installed and --skip-openclaw-install was specified"
+ fi
+
+ case "$(uname -s)" in
+ Darwin|Linux)
+ if [[ $DRY_RUN -eq 1 ]]; then
+ log "[dry-run] would install OpenClaw runtime via https://openclaw.ai/install.sh (requires --yes + optional checksum)"
+ return 0
+ fi
+
+ if [[ $YES -ne 1 ]]; then
+ die "即将安装外部运行时 OpenClaw(会修改你的系统)。请使用 --yes 显式授权后重试。"
+ fi
+
+ log "WARNING: 即将执行外部安装脚本 install.sh(来源 https://openclaw.ai/install.sh)"
+ log " 该脚本会修改你的系统;仅当你明确授权(--yes)且已核对校验和后才应继续。"
+
+ local tmp_install actual_sha256 expected
+ tmp_install="$(mktemp "/tmp/openclaw-install.XXXXXX.sh")"
+ trap 'rm -f "$tmp_install" 2>/dev/null' EXIT
+ curl -fsSL https://openclaw.ai/install.sh -o "$tmp_install" || die "下载 OpenClaw install.sh 失败"
+ actual_sha256="$(sha256_file "$tmp_install")"
+
+ if [[ -n "${OPENCLAW_INSTALL_SHA256:-}" ]]; then
+ expected="$(printf '%s' "$OPENCLAW_INSTALL_SHA256" | tr '[:upper:]' '[:lower:]')"
+ if [[ "$(printf '%s' "$actual_sha256" | tr '[:upper:]' '[:lower:]')" != "$expected" ]]; then
+ rm -f "$tmp_install"
+ die "OpenClaw install.sh 校验和不符:期望 $expected,实际 $actual_sha256"
+ fi
+ log "install.sh 校验和已验证"
+ else
+ log "WARN: 未设置 OPENCLAW_INSTALL_SHA256,无法校验 install.sh 完整性。下载文件 sha256: $actual_sha256"
+ fi
+
+ run_cmd bash "$tmp_install" --no-onboard --no-prompt
+ rm -f "$tmp_install"
+ ;;
+ *)
+ die "Automatic OpenClaw installation is only supported by this script on macOS/Linux"
+ ;;
+ esac
+}
+
+build_node_install_command() {
+ local package_spec="$1"
+
+ case "$NODE_MANAGER" in
+ npm)
+ printf 'npm\0install\0-g\0%s\0' "$package_spec"
+ ;;
+ pnpm)
+ printf 'pnpm\0add\0-g\0%s\0' "$package_spec"
+ ;;
+ yarn)
+ printf 'yarn\0global\0add\0%s\0' "$package_spec"
+ ;;
+ bun)
+ printf 'bun\0add\0-g\0%s\0' "$package_spec"
+ ;;
+ esac
+}
+
+run_node_install() {
+ local package_spec="$1"
+ local -a cmd=()
+
+ while IFS= read -r -d '' part; do
+ cmd+=("$part")
+ done < <(build_node_install_command "$package_spec")
+
+ run_cmd "${cmd[@]}"
+}
+
+install_clawhub_if_needed() {
+ if command_exists clawhub; then
+ return 0
+ fi
+
+ if [[ $SKIP_CLAWHUB_INSTALL -eq 1 ]]; then
+ return 0
+ fi
+
+ if [[ $DRY_RUN -eq 1 ]]; then
+ log "[dry-run] would install ClawHub via ${NODE_MANAGER} install -g clawhub (requires --yes)"
+ return 0
+ fi
+
+ if [[ $YES -ne 1 ]]; then
+ die "即将安装外部包 ClawHub(${NODE_MANAGER} install -g,会修改你的系统)。请使用 --yes 显式授权后重试。"
+ fi
+
+ log "WARNING: 即将通过 ${NODE_MANAGER} install -g 安装 ClawHub(会修改你的系统);仅当你明确授权(--yes)后才应继续。"
+
+ if ! command_exists "$NODE_MANAGER"; then
+ die "Required node manager not found on PATH: $NODE_MANAGER"
+ fi
+
+ run_node_install "clawhub@latest"
+}
+
+sync_skill_dir() {
+ local skill_name="$1"
+ local destination_root="$2"
+ local destination_dir="$destination_root/$skill_name"
+
+ if [[ -d "$destination_dir" ]]; then
+ run_cmd cp -a "$destination_dir" "${destination_dir}.bak.$(date +%Y%m%d%H%M%S).$$"
+ fi
+ run_cmd mkdir -p "$destination_root"
+ run_cmd rsync -a --delete "$SOURCE_DIR/$skill_name/" "$destination_dir/"
+}
+
+ensure_env_file_path() {
+ local path_entry="$1"
+
+ run_cmd mkdir -p "$(dirname "$ENV_FILE")"
+ if [[ $DRY_RUN -eq 1 ]]; then
+ log "+ ensure PATH entry in $ENV_FILE: $path_entry"
+ return 0
+ fi
+
+ touch "$ENV_FILE"
+ if ! grep -Fqx "PATH=$path_entry:\$PATH" "$ENV_FILE" 2>/dev/null; then
+ printf 'PATH=%s:$PATH\n' "$path_entry" >> "$ENV_FILE"
+ fi
+}
+
+extract_skill_metadata() {
+ local skill_dir="$1"
+ local skill_name="$2"
+ local brew_available=0
+
+ if command_exists brew; then
+ brew_available=1
+ fi
+
+ SKILL_DIR="$skill_dir" \
+ SKILL_NAME="$skill_name" \
+ PLATFORM="$(uname -s | tr '[:upper:]' '[:lower:]')" \
+ PREFER_BREW="$PREFER_BREW" \
+ BREW_AVAILABLE="$brew_available" \
+ node <<'NODE'
+const fs = require("node:fs");
+const path = require("node:path");
+
+const skillDir = process.env.SKILL_DIR;
+const skillName = process.env.SKILL_NAME;
+const platform = process.env.PLATFORM || process.platform;
+const preferBrew = process.env.PREFER_BREW === "1";
+const brewAvailable = process.env.BREW_AVAILABLE === "1";
+const skillFile = path.join(skillDir, "SKILL.md");
+const text = fs.readFileSync(skillFile, "utf8");
+const match = text.match(/^---\n([\s\S]*?)\n---\n?/);
+const frontmatter = {};
+
+if (match) {
+ for (const line of match[1].split(/\r?\n/)) {
+ const entry = line.match(/^([A-Za-z0-9._-]+):\s*(.*)$/);
+ if (entry) {
+ frontmatter[entry[1]] = entry[2];
+ }
+ }
+}
+
+let metadata = {};
+if (frontmatter.metadata) {
+ try {
+ metadata = JSON.parse(frontmatter.metadata);
+ } catch {
+ metadata = {};
+ }
+}
+
+const openclaw = metadata.openclaw && typeof metadata.openclaw === "object" ? metadata.openclaw : {};
+const install = Array.isArray(openclaw.install) ? openclaw.install.filter((spec) => {
+ if (!spec || typeof spec !== "object") {
+ return false;
+ }
+ if (!Array.isArray(spec.os) || spec.os.length === 0) {
+ return true;
+ }
+ return spec.os.includes(platform);
+}) : [];
+
+let preferredInstall = null;
+if (install.length > 0) {
+ if (preferBrew && brewAvailable) {
+ preferredInstall = install.find((spec) => spec.kind === "brew") || null;
+ }
+ if (!preferredInstall) {
+ preferredInstall =
+ install.find((spec) => spec.kind === "node") ||
+ install.find((spec) => spec.kind === "uv") ||
+ install.find((spec) => spec.kind === "go") ||
+ install.find((spec) => spec.kind === "download") ||
+ install[0] ||
+ null;
+ }
+}
+
+const requires = openclaw.requires && typeof openclaw.requires === "object" ? openclaw.requires : {};
+const skillKey = typeof openclaw.skillKey === "string" && openclaw.skillKey.trim() ? openclaw.skillKey.trim() : skillName;
+const result = {
+ skillKey,
+ primaryEnv: typeof openclaw.primaryEnv === "string" ? openclaw.primaryEnv : "",
+ requiredBins: Array.isArray(requires.bins) ? requires.bins : [],
+ preferredInstall,
+};
+
+process.stdout.write(JSON.stringify(result));
+NODE
+}
+
+json_get() {
+ local json_payload="$1"
+ local expression="$2"
+
+ JSON_PAYLOAD="$json_payload" JSON_EXPR="$expression" node <<'NODE'
+const payload = JSON.parse(process.env.JSON_PAYLOAD);
+const expr = process.env.JSON_EXPR;
+const parts = expr.split('.');
+let current = payload;
+for (const part of parts) {
+ if (!part) continue;
+ if (current === null || current === undefined) {
+ current = "";
+ break;
+ }
+ current = current[part];
+}
+if (current === null || current === undefined) {
+ process.stdout.write("");
+} else if (typeof current === "string") {
+ process.stdout.write(current);
+} else {
+ process.stdout.write(JSON.stringify(current));
+}
+NODE
+}
+
+has_all_bins() {
+ local bins_json="$1"
+ local bin_name
+
+ [[ -z "$bins_json" || "$bins_json" == "[]" ]] && return 0
+
+ while IFS= read -r bin_name; do
+ [[ -z "$bin_name" ]] && continue
+ if ! command_exists "$bin_name" && [[ ! -x "$BIN_DIR/$bin_name" ]]; then
+ return 1
+ fi
+ done < <(printf '%s' "$bins_json" | node -e 'const bins=JSON.parse(require("node:fs").readFileSync(0,"utf8")); for (const bin of bins) console.log(bin);')
+
+ return 0
+}
+
+install_download_spec() {
+ local skill_name="$1"
+ local spec_json="$2"
+ local url archive extract strip_components target_dir bins_json expected_sha256 actual_sha256
+ local tmp_dir archive_path extract_dir link_target bin_name
+
+ url="$(json_get "$spec_json" url)"
+ archive="$(json_get "$spec_json" archive)"
+ extract="$(json_get "$spec_json" extract)"
+ strip_components="$(json_get "$spec_json" stripComponents)"
+ target_dir="$(json_get "$spec_json" targetDir)"
+ bins_json="$(json_get "$spec_json" bins)"
+ expected_sha256="$(json_get "$spec_json" sha256)"
+
+ [[ -n "$url" ]] || die "Download installer for $skill_name is missing url"
+ [[ -n "$target_dir" ]] || target_dir="$TOOLS_DIR/$skill_name"
+
+ if [[ -n "$expected_sha256" && ! "$expected_sha256" =~ ^[[:xdigit:]]{64}$ ]]; then
+ die "Download installer for $skill_name has an invalid sha256 value"
+ fi
+
+ tmp_dir="$(mktemp -d /tmp/openclaw-skill-download.XXXXXX)"
+ archive_path="$tmp_dir/archive"
+ extract_dir="$tmp_dir/extracted"
+
+ if [[ $DRY_RUN -eq 1 ]]; then
+ log "+ download $url -> $target_dir"
+ if [[ -n "$expected_sha256" ]]; then
+ log "+ verify sha256 $expected_sha256"
+ else
+ log "WARN: Download installer for $skill_name has no sha256; integrity cannot be verified"
+ fi
+ rm -rf "$tmp_dir"
+ return 0
+ fi
+
+ if [[ $YES -ne 1 ]]; then
+ rm -rf "$tmp_dir"
+ die "即将下载并安装外部依赖(${skill_name} 的 download 类型安装器,会修改你的系统)。请使用 --yes 显式授权后重试。"
+ fi
+
+ mkdir -p "$extract_dir" "$BIN_DIR"
+ curl -fsSL "$url" -o "$archive_path" || { rm -rf "$tmp_dir"; die "下载失败: $url"; }
+
+ if [[ -n "$expected_sha256" ]]; then
+ actual_sha256="$(sha256_file "$archive_path")"
+ expected_sha256="$(printf '%s' "$expected_sha256" | tr '[:upper:]' '[:lower:]')"
+ actual_sha256="$(printf '%s' "$actual_sha256" | tr '[:upper:]' '[:lower:]')"
+ if [[ "$actual_sha256" != "$expected_sha256" ]]; then
+ rm -rf "$tmp_dir"
+ die "SHA-256 mismatch for $skill_name download: expected $expected_sha256, got $actual_sha256"
+ fi
+ else
+ log "WARN: Download installer for $skill_name has no sha256; integrity was not verified"
+ fi
+
+ mkdir -p "$target_dir"
+
+ if [[ "$extract" == "true" || -n "$archive" ]]; then
+ case "$archive" in
+ tar.gz|tgz|"")
+ tar -xzf "$archive_path" -C "$extract_dir" ${strip_components:+--strip-components="$strip_components"}
+ ;;
+ tar.bz2)
+ tar -xjf "$archive_path" -C "$extract_dir" ${strip_components:+--strip-components="$strip_components"}
+ ;;
+ zip)
+ unzip -q "$archive_path" -d "$extract_dir"
+ ;;
+ *)
+ die "Unsupported archive format for $skill_name: $archive"
+ ;;
+ esac
+ rsync -a --delete "$extract_dir/" "$target_dir/"
+ else
+ cp "$archive_path" "$target_dir/"
+ fi
+
+ if [[ -n "$bins_json" && "$bins_json" != "[]" ]]; then
+ while IFS= read -r bin_name; do
+ [[ -z "$bin_name" ]] && continue
+ link_target=""
+ if [[ -x "$target_dir/$bin_name" ]]; then
+ link_target="$target_dir/$bin_name"
+ elif [[ -x "$target_dir/bin/$bin_name" ]]; then
+ link_target="$target_dir/bin/$bin_name"
+ fi
+
+ if [[ -n "$link_target" ]]; then
+ ln -sf "$link_target" "$BIN_DIR/$bin_name"
+ fi
+ done < <(printf '%s' "$bins_json" | node -e 'const bins=JSON.parse(require("node:fs").readFileSync(0,"utf8")); for (const bin of bins) console.log(bin);')
+ ensure_env_file_path "$BIN_DIR"
+ fi
+
+ rm -rf "$tmp_dir"
+}
+
+install_skill_dependencies() {
+ local skill_name="$1"
+ local skill_dir="$2"
+ local metadata_json skill_key required_bins install_spec install_kind bins_json package_spec go_module formula uv_package
+
+ metadata_json="$(extract_skill_metadata "$skill_dir" "$skill_name")"
+ skill_key="$(json_get "$metadata_json" skillKey)"
+ required_bins="$(json_get "$metadata_json" requiredBins)"
+ install_spec="$(json_get "$metadata_json" preferredInstall)"
+
+ if has_all_bins "$required_bins"; then
+ return 0
+ fi
+
+ if [[ -z "$install_spec" || "$install_spec" == "null" ]]; then
+ if [[ -n "$required_bins" && "$required_bins" != "[]" ]]; then
+ die "Skill $skill_key requires missing bins but does not declare metadata.openclaw.install"
+ fi
+ return 0
+ fi
+
+ install_kind="$(json_get "$install_spec" kind)"
+ bins_json="$(json_get "$install_spec" bins)"
+
+ case "$install_kind" in
+ brew)
+ formula="$(json_get "$install_spec" formula)"
+ [[ -n "$formula" ]] || die "Brew installer for $skill_key is missing formula"
+ run_cmd brew install "$formula"
+ ;;
+ node)
+ package_spec="$(json_get "$install_spec" package)"
+ [[ -n "$package_spec" ]] || die "Node installer for $skill_key is missing package"
+ run_node_install "$package_spec"
+ ;;
+ go)
+ go_module="$(json_get "$install_spec" module)"
+ [[ -n "$go_module" ]] || die "Go installer for $skill_key is missing module"
+ if ! command_exists go; then
+ if command_exists brew; then
+ run_cmd brew install go
+ else
+ die "go is required for $skill_key and brew is unavailable"
+ fi
+ fi
+ if command_exists brew; then
+ export GOBIN="$(brew --prefix)/bin"
+ fi
+ run_cmd go install "$go_module"
+ ;;
+ uv)
+ uv_package="$(json_get "$install_spec" package)"
+ [[ -n "$uv_package" ]] || die "uv installer for $skill_key is missing package"
+ if ! command_exists uv; then
+ if command_exists brew; then
+ run_cmd brew install uv
+ else
+ die "uv is required for $skill_key and brew is unavailable"
+ fi
+ fi
+ run_cmd uv tool install "$uv_package"
+ ;;
+ download)
+ install_download_spec "$skill_key" "$install_spec"
+ ;;
+ *)
+ die "Unsupported OpenClaw installer kind: $install_kind"
+ ;;
+ esac
+
+ if ! has_all_bins "$bins_json" && ! has_all_bins "$required_bins"; then
+ die "Dependency installation for $skill_key completed but required bins are still missing"
+ fi
+}
+
+sync_selected_skills() {
+ local destination_root="$1"
+ local skill_name
+
+ for skill_name in "${REQUESTED_SKILLS[@]}"; do
+ sync_skill_dir "$skill_name" "$destination_root"
+ install_skill_dependencies "$skill_name" "$SOURCE_DIR/$skill_name"
+ done
+}
+
+build_json_array() {
+ local input_name="$1"
+ local item_count
+
+ eval "item_count=\${#${input_name}[@]}"
+ if [[ "$item_count" -eq 0 ]]; then
+ printf '[]'
+ return 0
+ fi
+
+ eval "printf '%s\\0' \"\
+\${${input_name}[@]}\"" | node -e '
+const fs = require("node:fs");
+const raw = fs.readFileSync(0);
+const items = raw.length === 0 ? [] : raw.toString("utf8").split("\0").filter(Boolean);
+process.stdout.write(JSON.stringify(items));
+'
+}
+
+patch_openclaw_config() {
+ local workspaces_json agents_json extra_dirs_json env_json api_key_json requested_skills_json default_workspace
+
+ default_workspace="${WORKSPACES[0]:-${STATE_DIR}/workspace}"
+ workspaces_json="$(build_json_array WORKSPACES)"
+ agents_json="$(build_json_array AGENTS)"
+ extra_dirs_json="$(build_json_array EXTRA_DIRS)"
+ env_json="$(build_json_array ENV_ASSIGNMENTS)"
+ api_key_json="$(build_json_array API_KEY_ENV_ASSIGNMENTS)"
+ requested_skills_json="$(build_json_array REQUESTED_SKILLS)"
+
+ run_cmd mkdir -p "$(dirname "$CONFIG_PATH")"
+
+ if [[ -f "$CONFIG_PATH" ]]; then
+ run_cmd cp -a "$CONFIG_PATH" "${CONFIG_PATH}.bak.$(date +%Y%m%d%H%M%S).$$"
+ fi
+
+ if [[ $DRY_RUN -eq 1 ]]; then
+ log "+ patch OpenClaw config: $CONFIG_PATH"
+ return 0
+ fi
+
+ CONFIG_PATH="$CONFIG_PATH" \
+ DEFAULT_WORKSPACE="$default_workspace" \
+ NODE_MANAGER="$NODE_MANAGER" \
+ PREFER_BREW="$PREFER_BREW" \
+ WATCH="$WATCH" \
+ WATCH_DEBOUNCE_MS="$WATCH_DEBOUNCE_MS" \
+ EXTRA_DIRS_JSON="$extra_dirs_json" \
+ ENV_ASSIGNMENTS_JSON="$env_json" \
+ API_KEY_ENV_ASSIGNMENTS_JSON="$api_key_json" \
+ AGENTS_JSON="$agents_json" \
+ REQUESTED_SKILLS_JSON="$requested_skills_json" \
+ DEFAULT_AGENT_ID="$DEFAULT_AGENT_ID" \
+ node <<'NODE'
+const fs = require("node:fs");
+const path = require("node:path");
+
+const configPath = process.env.CONFIG_PATH;
+const defaultWorkspace = process.env.DEFAULT_WORKSPACE;
+const nodeManager = process.env.NODE_MANAGER;
+const preferBrew = process.env.PREFER_BREW === "1";
+const watch = process.env.WATCH === "1";
+const watchDebounceMs = Number(process.env.WATCH_DEBOUNCE_MS || "250");
+const extraDirs = JSON.parse(process.env.EXTRA_DIRS_JSON || "[]");
+const envAssignments = JSON.parse(process.env.ENV_ASSIGNMENTS_JSON || "[]");
+const apiKeyAssignments = JSON.parse(process.env.API_KEY_ENV_ASSIGNMENTS_JSON || "[]");
+const agents = JSON.parse(process.env.AGENTS_JSON || "[]");
+const requestedSkills = JSON.parse(process.env.REQUESTED_SKILLS_JSON || "[]");
+const defaultAgentId = process.env.DEFAULT_AGENT_ID || "";
+
+let config = {};
+if (fs.existsSync(configPath)) {
+ const raw = fs.readFileSync(configPath, "utf8").trim();
+ if (raw) {
+ config = Function(`"use strict"; return (${raw});`)();
+ }
+}
+
+config.agents = config.agents && typeof config.agents === "object" ? config.agents : {};
+config.agents.defaults = config.agents.defaults && typeof config.agents.defaults === "object" ? config.agents.defaults : {};
+config.agents.defaults.workspace = config.agents.defaults.workspace || defaultWorkspace;
+
+config.skills = config.skills && typeof config.skills === "object" ? config.skills : {};
+config.skills.load = config.skills.load && typeof config.skills.load === "object" ? config.skills.load : {};
+config.skills.install = config.skills.install && typeof config.skills.install === "object" ? config.skills.install : {};
+config.skills.entries = config.skills.entries && typeof config.skills.entries === "object" ? config.skills.entries : {};
+
+config.skills.load.watch = watch;
+config.skills.load.watchDebounceMs = watchDebounceMs;
+config.skills.load.extraDirs = Array.from(new Set([...(Array.isArray(config.skills.load.extraDirs) ? config.skills.load.extraDirs : []), ...extraDirs]));
+config.skills.install.nodeManager = nodeManager;
+config.skills.install.preferBrew = preferBrew;
+
+for (const skill of requestedSkills) {
+ config.skills.entries[skill] = config.skills.entries[skill] && typeof config.skills.entries[skill] === "object" ? config.skills.entries[skill] : {};
+ if (typeof config.skills.entries[skill].enabled === "undefined") {
+ config.skills.entries[skill].enabled = true;
+ }
+}
+
+for (const item of envAssignments) {
+ const match = /^([^:]+):([^=]+)=(.*)$/.exec(item);
+ if (!match) continue;
+ const [, skill, key, value] = match;
+ config.skills.entries[skill] = config.skills.entries[skill] && typeof config.skills.entries[skill] === "object" ? config.skills.entries[skill] : {};
+ config.skills.entries[skill].env = config.skills.entries[skill].env && typeof config.skills.entries[skill].env === "object" ? config.skills.entries[skill].env : {};
+ config.skills.entries[skill].env[key] = value;
+}
+
+for (const item of apiKeyAssignments) {
+ const match = /^([^:]+):([A-Z][A-Z0-9_]*)$/.exec(item);
+ if (!match) continue;
+ const [, skill, envVar] = match;
+ config.skills.entries[skill] = config.skills.entries[skill] && typeof config.skills.entries[skill] === "object" ? config.skills.entries[skill] : {};
+ config.skills.entries[skill].apiKey = { source: "env", provider: "default", id: envVar };
+}
+
+if (agents.length > 0) {
+ const parsedAgents = agents
+ .map((entry) => {
+ const idx = entry.indexOf(":");
+ if (idx === -1) return null;
+ return { id: entry.slice(0, idx), workspace: entry.slice(idx + 1) };
+ })
+ .filter(Boolean);
+
+ config.agents.list = Array.isArray(config.agents.list) ? config.agents.list : [];
+ const existingById = new Map(config.agents.list.map((entry) => [entry.id, entry]));
+
+ for (const agent of parsedAgents) {
+ const current = existingById.get(agent.id) || { id: agent.id };
+ current.workspace = agent.workspace;
+ if (defaultAgentId) {
+ current.default = agent.id === defaultAgentId;
+ }
+ existingById.set(agent.id, current);
+ }
+
+ config.agents.list = Array.from(existingById.values());
+}
+
+fs.mkdirSync(path.dirname(configPath), { recursive: true });
+fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
+NODE
+}
+
+install_openclaw_if_needed
+install_clawhub_if_needed
+
+if [[ $DRY_RUN -eq 0 && $CONFIRM_WRITE -eq 0 ]]; then
+ die "Refusing global changes without --yes. Run once with --dry-run, review the plan, then rerun with --yes."
+fi
+
+case "$SCOPE" in
+ managed|both)
+ sync_selected_skills "$MANAGED_DIR"
+ ;;
+esac
+
+case "$SCOPE" in
+ workspace|both)
+ if [[ ${#WORKSPACES[@]} -eq 0 ]]; then
+ WORKSPACES+=("${STATE_DIR}/workspace")
+ fi
+ for workspace in "${WORKSPACES[@]}"; do
+ sync_selected_skills "$workspace/skills"
+ done
+ ;;
+esac
+
+patch_openclaw_config
+
+if [[ $DRY_RUN -eq 0 && $SKIP_DOCTOR -eq 0 ]] && command_exists openclaw; then
+ run_cmd openclaw doctor
+fi
+
+log "OpenClaw skill auto-configuration complete"
diff --git a/skills/agent-skills-setup/scripts/export-public-skill.sh b/skills/agent-skills-setup/scripts/export-public-skill.sh
new file mode 100644
index 0000000000..18bb9c052d
--- /dev/null
+++ b/skills/agent-skills-setup/scripts/export-public-skill.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+#
+# export-public-skill.sh — SINGLE RESPONSIBILITY: prepare the PUBLIC repository layout.
+# Copies a source skill into a standalone public-repo structure (via rsync) and
+# generates/updates the public README from `public-repo-readme-template.md`.
+# This script does NOT publish the skill. Publishing is the job of
+# prepare-clawhub-release.sh (see ./prepare-clawhub-release.sh), whose canonical
+# publish command is `clawhub publish`.
+# NOTE: the `npx skills add ` step printed at the end is for CONSUMERS to
+# install the already-published skill — it is an install/test command, NOT a publish command.
+#
+set -euo pipefail
+
+SOURCE_ROOT="${AGENT_SKILLS_SOURCE_DIR:-${HOME}/.gemini/antigravity/skills}"
+TEMPLATE_PATH="${SOURCE_ROOT}/agent-skills-setup/assets/public-repo-readme-template.md"
+SKILL_NAME=""
+OUTPUT_DIR=""
+REPO_NAME=""
+
+usage() {
+ cat <<'EOF'
+Usage: export-public-skill.sh --skill --output --repo
+
+Exports a skill from Antigravity's global skill store into a standalone public repository layout.
+
+Options:
+ --skill Skill folder name under ~/.gemini/antigravity/skills
+ --output Destination repository directory to create or update
+ --repo Public repository name used in generated install docs
+ -h, --help Show this help text
+EOF
+}
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --skill)
+ SKILL_NAME="$2"
+ shift 2
+ ;;
+ --output)
+ OUTPUT_DIR="$2"
+ shift 2
+ ;;
+ --repo)
+ REPO_NAME="$2"
+ shift 2
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ *)
+ echo "ERROR: Unknown argument: $1" >&2
+ usage >&2
+ exit 1
+ ;;
+ esac
+done
+
+if [[ -z "$SKILL_NAME" || -z "$OUTPUT_DIR" || -z "$REPO_NAME" ]]; then
+ echo "ERROR: --skill, --output, and --repo are required" >&2
+ usage >&2
+ exit 1
+fi
+
+SOURCE_SKILL_DIR="${SOURCE_ROOT}/${SKILL_NAME}"
+
+if [[ ! -d "$SOURCE_SKILL_DIR" ]]; then
+ echo "ERROR: Skill not found: $SOURCE_SKILL_DIR" >&2
+ exit 1
+fi
+
+if [[ ! -f "$TEMPLATE_PATH" ]]; then
+ echo "ERROR: README template not found: $TEMPLATE_PATH" >&2
+ exit 1
+fi
+
+mkdir -p "$OUTPUT_DIR"
+rsync -a --delete "$SOURCE_SKILL_DIR/" "$OUTPUT_DIR/$SKILL_NAME/"
+
+sed \
+ -e "s/{{SKILL_NAME}}/$SKILL_NAME/g" \
+ -e "s#{{REPO_NAME}}#$REPO_NAME#g" \
+ "$TEMPLATE_PATH" > "$OUTPUT_DIR/README.md"
+
+echo "Exported $SKILL_NAME to $OUTPUT_DIR"
+echo "Next steps:"
+echo "1. Review README.md and replace placeholder sections"
+echo "2. Add LICENSE and repository topics"
+echo "3. Publish the directory as a public GitHub repository"
+echo "4. Test install flow with: npx skills add $REPO_NAME"
\ No newline at end of file
diff --git a/skills/agent-skills-setup/scripts/prepare-clawhub-release.sh b/skills/agent-skills-setup/scripts/prepare-clawhub-release.sh
new file mode 100644
index 0000000000..b9b85dfcec
--- /dev/null
+++ b/skills/agent-skills-setup/scripts/prepare-clawhub-release.sh
@@ -0,0 +1,145 @@
+#!/usr/bin/env bash
+#
+# prepare-clawhub-release.sh — SINGLE RESPONSIBILITY: validate a skill folder and
+# construct/run the `clawhub publish` command for release.
+# Canonical publish command: `clawhub publish`. (`npx skills add` is for CONSUMERS
+# installing a published skill, NOT for publishing.)
+# This script does NOT build the public repo layout or README; that is done upstream by
+# export-public-skill.sh (see ./export-public-skill.sh), which must prepare the public
+# GitHub repository before this script publishes it to ClawHub.
+#
+set -euo pipefail
+
+SKILL_DIR=""
+SKILL_DIR_ABS=""
+SLUG=""
+DISPLAY_NAME=""
+VERSION=""
+TAGS="latest"
+CHANGELOG_TEXT=""
+CHANGELOG_FILE=""
+RUN_PUBLISH=0
+
+usage() {
+ cat <<'EOF'
+Usage: prepare-clawhub-release.sh [options]
+
+Validate a skill folder and print or run the exact ClawHub publish command.
+
+Options:
+ --skill-dir Path to the skill folder to publish.
+ --slug Public ClawHub slug.
+ --name Display name.
+ --version Release version, e.g. 1.0.0.
+ --tags Comma-separated tags. Default: latest.
+ --changelog Inline changelog text.
+ --changelog-file Read changelog text from a file.
+ --publish Execute `clawhub publish` after validation.
+ -h, --help Show this help text.
+EOF
+}
+
+die() {
+ echo "ERROR: $*" >&2
+ exit 1
+}
+
+command_exists() {
+ command -v "$1" >/dev/null 2>&1
+}
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --skill-dir)
+ [[ $# -ge 2 ]] || die "--skill-dir requires a value"
+ SKILL_DIR="$2"
+ shift 2
+ ;;
+ --slug)
+ [[ $# -ge 2 ]] || die "--slug requires a value"
+ SLUG="$2"
+ shift 2
+ ;;
+ --name)
+ [[ $# -ge 2 ]] || die "--name requires a value"
+ DISPLAY_NAME="$2"
+ shift 2
+ ;;
+ --version)
+ [[ $# -ge 2 ]] || die "--version requires a value"
+ VERSION="$2"
+ shift 2
+ ;;
+ --tags)
+ [[ $# -ge 2 ]] || die "--tags requires a value"
+ TAGS="$2"
+ shift 2
+ ;;
+ --changelog)
+ [[ $# -ge 2 ]] || die "--changelog requires a value"
+ CHANGELOG_TEXT="$2"
+ shift 2
+ ;;
+ --changelog-file)
+ [[ $# -ge 2 ]] || die "--changelog-file requires a value"
+ CHANGELOG_FILE="$2"
+ shift 2
+ ;;
+ --publish)
+ RUN_PUBLISH=1
+ shift
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ *)
+ die "Unknown argument: $1"
+ ;;
+ esac
+done
+
+[[ -n "$SKILL_DIR" ]] || die "--skill-dir is required"
+[[ -n "$SLUG" ]] || die "--slug is required"
+[[ -n "$DISPLAY_NAME" ]] || die "--name is required"
+[[ -n "$VERSION" ]] || die "--version is required"
+[[ -d "$SKILL_DIR" ]] || die "Skill directory not found: $SKILL_DIR"
+[[ -f "$SKILL_DIR/SKILL.md" ]] || die "Missing SKILL.md in $SKILL_DIR"
+
+if command_exists realpath; then
+ SKILL_DIR_ABS="$(realpath "$SKILL_DIR")"
+else
+ SKILL_DIR_ABS="$(cd "$SKILL_DIR" && pwd)"
+fi
+
+if [[ -n "$CHANGELOG_FILE" ]]; then
+ [[ -f "$CHANGELOG_FILE" ]] || die "Changelog file not found: $CHANGELOG_FILE"
+ CHANGELOG_TEXT="$(cat "$CHANGELOG_FILE")"
+fi
+
+[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][A-Za-z0-9]+)?$ ]] || die "Version must look like semver: $VERSION"
+[[ "$SLUG" =~ ^[a-z0-9-]+$ ]] || die "Slug must contain only lowercase letters, numbers, and hyphens"
+
+command_exists clawhub || die "clawhub is not installed"
+
+if ! clawhub whoami >/dev/null 2>&1; then
+ if [[ $RUN_PUBLISH -eq 1 ]]; then
+ die "Not logged in to ClawHub. Run: clawhub login"
+ fi
+ echo "WARN: Not logged in to ClawHub. Run 'clawhub login' before using --publish." >&2
+fi
+
+PUBLISH_CMD=(clawhub publish "$SKILL_DIR_ABS" --slug "$SLUG" --name "$DISPLAY_NAME" --version "$VERSION" --tags "$TAGS")
+
+if [[ -n "$CHANGELOG_TEXT" ]]; then
+ PUBLISH_CMD+=(--changelog "$CHANGELOG_TEXT")
+fi
+
+echo "Validated skill folder: $SKILL_DIR_ABS"
+echo "Suggested publish command:"
+printf '%q ' "${PUBLISH_CMD[@]}"
+printf '\n'
+
+if [[ $RUN_PUBLISH -eq 1 ]]; then
+ "${PUBLISH_CMD[@]}"
+fi
\ No newline at end of file
diff --git a/skills/agent-skills-setup/scripts/smart-ide-migration.sh b/skills/agent-skills-setup/scripts/smart-ide-migration.sh
new file mode 100644
index 0000000000..808d7cfe11
--- /dev/null
+++ b/skills/agent-skills-setup/scripts/smart-ide-migration.sh
@@ -0,0 +1,1385 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+SOURCE_IDE=""
+TARGET_IDE=""
+WORKSPACE_ROOT="$(pwd)"
+OBJECTS=""
+STRATEGY="backup"
+DRY_RUN=0
+REPORT_FILE=""
+PRINT_PATH_IDE=""
+PRINT_PATH_OBJECT=""
+
+SUPPORTED_IDES="antigravity claude codex copilot cursor windsurf jetbrains openclaw trae trae-cn vscode zed neovim emacs continue aider roo-code cline amazon-q cody codeium tabnine replit pearai supermaven pieces blackbox gemini-cli goose-cli opencode kilocode kimiai workbuddy"
+
+MIGRATION_TOTAL=0
+MIGRATION_SUCCESS=0
+MIGRATION_FAILED=0
+MIGRATION_SKIPPED=0
+
+MIGRATION_STATUS_FILE=""
+MIGRATION_MESSAGES_FILE=""
+MIGRATION_MANUAL_FILE=""
+
+get_ide_name() {
+ local ide="$1"
+ case "$ide" in
+ antigravity) echo "Antigravity" ;;
+ claude) echo "Claude Code" ;;
+ codex) echo "OpenAI Codex CLI" ;;
+ copilot) echo "VS Code Copilot" ;;
+ cursor) echo "Cursor" ;;
+ windsurf) echo "Windsurf" ;;
+ jetbrains) echo "JetBrains IDEs" ;;
+ openclaw) echo "OpenClaw" ;;
+ trae) echo "Trae (International)" ;;
+ trae-cn) echo "Trae CN (China)" ;;
+ vscode) echo "VS Code" ;;
+ zed) echo "Zed Editor" ;;
+ neovim) echo "Neovim" ;;
+ emacs) echo "Emacs" ;;
+ continue) echo "Continue.dev" ;;
+ aider) echo "Aider" ;;
+ roo-code) echo "Roo Code" ;;
+ cline) echo "Cline" ;;
+ amazon-q) echo "Amazon Q Developer" ;;
+ cody) echo "Sourcegraph Cody" ;;
+ codeium) echo "Codeium" ;;
+ tabnine) echo "Tabnine" ;;
+ replit) echo "Replit AI" ;;
+ pearai) echo "PearAI" ;;
+ supermaven) echo "Supermaven" ;;
+ pieces) echo "Pieces" ;;
+ blackbox) echo "Blackbox AI" ;;
+ gemini-cli) echo "Gemini CLI" ;;
+ goose-cli) echo "Goose CLI" ;;
+ opencode) echo "OpenCode" ;;
+ kilocode) echo "Kilocode" ;;
+ kimiai) echo "Kimi AI" ;;
+ workbuddy) echo "WorkBuddy" ;;
+ *) echo "$ide" ;;
+ esac
+}
+
+# SOURCE OF TRUTH: skills/agent-skills-setup/references/ide-registry.md (and ide-paths.json).
+# Keep these functions in sync with that file. Drift is caught by test-ide-paths.sh.
+get_global_path() {
+ local ide="$1"
+ case "$ide" in
+ antigravity) echo "${HOME}/.gemini/antigravity/skills" ;;
+ claude) echo "${HOME}/.claude/skills" ;;
+ codex) echo "${HOME}/.agents/skills" ;;
+ copilot) echo "${HOME}/.copilot/skills" ;;
+ cursor) echo "${HOME}/.cursor" ;;
+ windsurf) echo "${HOME}/.windsurf" ;;
+ jetbrains) echo "${HOME}/.idea" ;;
+ openclaw) echo "${HOME}/.openclaw/skills" ;;
+ trae) echo "${HOME}/.trae/skills" ;;
+ trae-cn) echo "${HOME}/.trae-cn/skills" ;;
+ vscode) echo "${HOME}/.vscode" ;;
+ zed) echo "${HOME}/.config/zed" ;;
+ neovim) echo "${HOME}/.config/nvim" ;;
+ emacs) echo "${HOME}/.emacs.d" ;;
+ continue) echo "${HOME}/.continue" ;;
+ aider) echo "${HOME}/.aider" ;;
+ roo-code) echo "${HOME}/.roo" ;;
+ cline) echo "${HOME}/.cline" ;;
+ amazon-q) echo "${HOME}/.aws/amazon-q" ;;
+ # cody/codeium/tabnine/blackbox: no stable global skills directory.
+ # Returning "" avoids emitting glob literals (e.g. sourcegraph.cody*)
+ # that would otherwise be turned into illegal directory names by mkdir -p.
+ cody) echo "" ;;
+ codeium) echo "" ;;
+ tabnine) echo "" ;;
+ replit) echo "${HOME}/.replit" ;;
+ pearai) echo "${HOME}/.pearai" ;;
+ supermaven) echo "${HOME}/.supermaven" ;;
+ pieces) echo "${HOME}/.pieces" ;;
+ blackbox) echo "" ;;
+ gemini-cli) echo "${HOME}/.gemini" ;;
+ goose-cli) echo "${HOME}/.config/goose" ;;
+ opencode) echo "${HOME}/.config/opencode" ;;
+ kilocode) echo "${HOME}/.kilocode" ;;
+ kimiai) echo "${HOME}/.kimi-code/skills" ;;
+ workbuddy) echo "${HOME}/.workbuddy/skills" ;;
+ *) echo "" ;;
+ esac
+}
+
+get_project_path() {
+ local ide="$1"
+ # Returns the project-level path for an IDE. NOTE: this may be a DIRECTORY
+ # (e.g. .vscode, skills, .cursor) OR a FILE (e.g. .dir-locals.el,
+ # .aider.conf.yml, .github/copilot-instructions.md). Callers that create
+ # paths must guard against file-type returns: use
+ # `mkdir -p "$(dirname "$path")"` for files, never `mkdir -p "$path"` on a
+ # file path.
+ case "$ide" in
+ antigravity) echo ".agents/skills" ;;
+ claude) echo ".claude" ;;
+ codex) echo ".codex" ;;
+ copilot) echo ".github/copilot-instructions.md" ;;
+ cursor) echo ".cursor" ;;
+ windsurf) echo ".windsurf" ;;
+ jetbrains) echo ".idea" ;;
+ openclaw) echo "skills" ;;
+ trae) echo ".trae" ;;
+ trae-cn) echo ".trae" ;;
+ vscode) echo ".vscode" ;;
+ zed) echo ".zed" ;;
+ neovim) echo ".nvim" ;;
+ emacs) echo ".dir-locals.el" ;;
+ continue) echo ".continue" ;;
+ aider) echo ".aider.conf.yml" ;;
+ roo-code) echo ".roo" ;;
+ cline) echo ".cline" ;;
+ amazon-q) echo ".amazon-q" ;;
+ cody) echo ".cody" ;;
+ codeium) echo ".codeium" ;;
+ tabnine) echo ".tabnine" ;;
+ replit) echo ".replit" ;;
+ pearai) echo ".pearai" ;;
+ supermaven) echo ".supermaven" ;;
+ pieces) echo ".pieces" ;;
+ blackbox) echo ".blackbox" ;;
+ gemini-cli) echo ".gemini" ;;
+ goose-cli) echo ".goose" ;;
+ opencode) echo ".opencode" ;;
+ kilocode) echo ".kilocode" ;;
+ kimiai) echo ".kimi-code/skills" ;;
+ workbuddy) echo ".workbuddy/skills" ;;
+ *) echo "" ;;
+ esac
+}
+
+get_rules_file() {
+ local ide="$1"
+ case "$ide" in
+ cursor) echo ".cursorrules" ;;
+ windsurf) echo ".windsurfrules" ;;
+ copilot) echo ".github/copilot-instructions.md" ;;
+ openclaw) echo "AGENT_RULES.md" ;;
+ claude) echo "CLAUDE.md" ;;
+ aider) echo "CONVENTIONS.md" ;;
+ cline) echo ".clinerules" ;;
+ continue) echo ".continuerc.json" ;;
+ roo-code) echo ".roomotes" ;;
+ cody) echo ".codyrules" ;;
+ pearai) echo ".pearairules" ;;
+ codex) echo "AGENTS.md" ;;
+ gemini-cli) echo "GEMINI.md" ;;
+ goose-cli) echo "GOOSE.md" ;;
+ opencode) echo "OPENCODE.md" ;;
+ kilocode) echo "KILOCODE.md" ;;
+ kimiai) echo "AGENTS.md" ;;
+ *) echo "" ;;
+ esac
+}
+
+get_prompts_path() {
+ local ide="$1"
+ case "$ide" in
+ cursor) echo ".cursor/prompts" ;;
+ windsurf) echo ".windsurf/prompts" ;;
+ copilot) echo ".github/prompts" ;;
+ openclaw) echo ".github/prompts" ;;
+ continue) echo ".continue/prompts" ;;
+ cline) echo ".cline/prompts" ;;
+ claude) echo ".claude/commands" ;;
+ gemini-cli) echo ".gemini/commands" ;;
+ goose-cli) echo ".goose/prompts" ;;
+ *) echo "" ;;
+ esac
+}
+
+get_mcp_path() {
+ local ide="$1"
+ case "$ide" in
+ trae) echo "${HOME}/.trae/mcps" ;;
+ trae-cn) echo "${HOME}/.trae-cn/mcps" ;;
+ openclaw) echo "${HOME}/.openclaw/openclaw.json" ;;
+ claude) echo "${HOME}/.claude.json" ;;
+ continue) echo "${HOME}/.continue/config.json" ;;
+ cline) echo "${HOME}/.cline/mcp.json" ;;
+ cursor) echo "${HOME}/.cursor/mcp.json" ;;
+ roo-code) echo "${HOME}/.roo/mcp.json" ;;
+ windsurf) echo "${HOME}/.windsurf/mcp.json" ;;
+ gemini-cli) echo "${HOME}/.gemini/settings.json" ;;
+ goose-cli) echo "${HOME}/.config/goose/config.yaml" ;;
+ codex) echo "${HOME}/.codex" ;;
+ aider) echo "${HOME}/.aider.conf.yml" ;;
+ kimiai) echo "${HOME}/.kimi-code/mcp.json" ;;
+ workbuddy) echo "${HOME}/.workbuddy/.mcp.json" ;;
+ *) echo "" ;;
+ esac
+}
+
+get_config_file() {
+ local ide="$1"
+ case "$ide" in
+ trae) echo "${HOME}/.trae/argv.json" ;;
+ trae-cn) echo "${HOME}/.trae-cn/argv.json" ;;
+ openclaw) echo "${HOME}/.openclaw/openclaw.json" ;;
+ cursor) echo "${HOME}/.cursor/settings.json" ;;
+ windsurf) echo "${HOME}/.windsurf/settings.json" ;;
+ vscode) echo "${HOME}/.vscode/settings.json" ;;
+ zed) echo "${HOME}/.config/zed/settings.json" ;;
+ neovim) echo "${HOME}/.config/nvim/init.lua" ;;
+ emacs) echo "${HOME}/.emacs.d/init.el" ;;
+ continue) echo "${HOME}/.continue/config.json" ;;
+ aider) echo "${HOME}/.aider.conf.yml" ;;
+ cline) echo "${HOME}/.cline/config.json" ;;
+ roo-code) echo "${HOME}/.roo/config.json" ;;
+ claude) echo "${HOME}/.claude/settings.json" ;;
+ replit) echo "${HOME}/.replit/replit.nix" ;;
+ pearai) echo "${HOME}/.pearai/settings.json" ;;
+ gemini-cli) echo "${HOME}/.gemini/settings.json" ;;
+ goose-cli) echo "${HOME}/.config/goose/config.yaml" ;;
+ codex) echo "${HOME}/.codex" ;;
+ opencode) echo "${HOME}/.config/opencode/config.json" ;;
+ kilocode) echo "${HOME}/.kilocode/config.json" ;;
+ kimiai) echo "${HOME}/.kimi-code/config.toml" ;;
+ workbuddy) echo "${HOME}/.workbuddy/settings.json" ;;
+ *) echo "" ;;
+ esac
+}
+
+# Returns the MCP server map root key used by an IDE's MCP config file.
+# Mirrors the IDE Registry (mcpServers | servers | context_servers |
+# mcp.servers | mcp | extensions). Used by convert_mcp_file to map between
+# source and target formats.
+get_mcp_root_key() {
+ local ide="$1"
+ case "$ide" in
+ claude|cursor|windsurf|gemini-cli|trae|trae-cn|openclaw|continue|cline|roo-code|antigravity|amazon-q|kimiai|workbuddy)
+ echo "mcpServers" ;;
+ codex) echo "mcp_servers" ;;
+ goose-cli) echo "extensions" ;;
+ zed) echo "context_servers" ;;
+ opencode) echo "mcp" ;;
+ *) echo "" ;;
+ esac
+}
+
+usage() {
+ cat <<'EOF'
+IDE Migration Tool - 在不同AI IDE之间迁移配置
+
+用法: ide-migrate.sh [选项]
+
+必选参数:
+ --source 源IDE (从哪个IDE迁移)
+ --target 目标IDE (迁移到哪个IDE)
+
+可选参数:
+ --workspace 工作区根目录 (默认: 当前目录)
+ --objects 要迁移的内容类型 (逗号分隔)
+ --strategy 迁移策略: skip, overwrite, backup (默认: backup)
+ --report 保存迁移报告到文件
+ --dry-run 预览模式,不实际修改文件
+ --print-path