From eb6367cf9419506bba28fb59ceb66d17d85eab02 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:55:19 +0000 Subject: [PATCH 1/4] Initial plan From fd556ca2443ca12a6b44555f5053b74c7a5cd7de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:02:30 +0000 Subject: [PATCH 2/4] Fix custom safe-outputs documentation Co-authored-by: dsyme <7204669+dsyme@users.noreply.github.com> --- .../docs/guides/custom-safe-outputs.md | 320 +++++++++++++++--- 1 file changed, 272 insertions(+), 48 deletions(-) diff --git a/docs/src/content/docs/guides/custom-safe-outputs.md b/docs/src/content/docs/guides/custom-safe-outputs.md index 9cd9cadf773..b3173af15b0 100644 --- a/docs/src/content/docs/guides/custom-safe-outputs.md +++ b/docs/src/content/docs/guides/custom-safe-outputs.md @@ -38,13 +38,18 @@ safe-outputs: - name: Send Slack message env: SLACK_WEBHOOK: "${{ secrets.SLACK_WEBHOOK }}" - MESSAGE: "${{ inputs.message }}" run: | - # Use jq to safely escape JSON content - PAYLOAD=$(jq -n --arg text "$MESSAGE" '{text: $text}') - curl -X POST "$SLACK_WEBHOOK" \ - -H 'Content-Type: application/json' \ - -d "$PAYLOAD" + if [ -f "$GH_AW_AGENT_OUTPUT" ]; then + MESSAGE=$(cat "$GH_AW_AGENT_OUTPUT" | jq -r '.items[] | select(.type == "slack_notify") | .message') + # Use jq to safely escape JSON content + PAYLOAD=$(jq -n --arg text "$MESSAGE" '{text: $text}') + curl -X POST "$SLACK_WEBHOOK" \ + -H 'Content-Type: application/json' \ + -d "$PAYLOAD" + else + echo "No agent output found" + exit 1 + fi --- ``` @@ -150,49 +155,65 @@ safe-outputs: uses: actions/github-script@v8 env: NOTION_TOKEN: "${{ secrets.NOTION_TOKEN }}" - PAGE_ID: "${{ inputs.page_id }}" - COMMENT: "${{ inputs.comment }}" with: script: | + const fs = require('fs'); const notionToken = process.env.NOTION_TOKEN; - const pageId = process.env.PAGE_ID; - const comment = process.env.COMMENT; + const outputFile = process.env.GH_AW_AGENT_OUTPUT; if (!notionToken) { core.setFailed('NOTION_TOKEN secret is not configured'); return; } - core.info(`Adding comment to Notion page: ${pageId}`); + if (!outputFile) { + core.info('No GH_AW_AGENT_OUTPUT environment variable found'); + return; + } - try { - const response = await fetch('https://api.notion.com/v1/comments', { - method: 'POST', - headers: { - 'Authorization': `Bearer ${notionToken}`, - 'Notion-Version': '2022-06-28', - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - parent: { page_id: pageId }, - rich_text: [{ - type: 'text', - text: { content: comment } - }] - }) - }); + // Read and parse agent output + const fileContent = fs.readFileSync(outputFile, 'utf8'); + const agentOutput = JSON.parse(fileContent); + + // Filter for notion-add-comment items (job name with dashes → underscores) + const items = agentOutput.items.filter(item => item.type === 'notion_add_comment'); + + for (const item of items) { + const pageId = item.page_id; + const comment = item.comment; - if (!response.ok) { - const errorData = await response.text(); - core.setFailed(`Notion API error (${response.status}): ${errorData}`); + core.info(`Adding comment to Notion page: ${pageId}`); + + try { + const response = await fetch('https://api.notion.com/v1/comments', { + method: 'POST', + headers: { + 'Authorization': `Bearer ${notionToken}`, + 'Notion-Version': '2022-06-28', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + parent: { page_id: pageId }, + rich_text: [{ + type: 'text', + text: { content: comment } + }] + }) + }); + + if (!response.ok) { + const errorData = await response.text(); + core.setFailed(`Notion API error (${response.status}): ${errorData}`); + return; + } + + const data = await response.json(); + core.info('Comment added successfully'); + core.info(`Comment ID: ${data.id}`); + } catch (error) { + core.setFailed(`Failed to add comment: ${error.message}`); return; } - - const data = await response.json(); - core.info('Comment added successfully'); - core.info(`Comment ID: ${data.id}`); - } catch (error) { - core.setFailed(`Failed to add comment: ${error.message}`); } --- ``` @@ -271,13 +292,93 @@ inputs: ### Environment Variables +Custom safe-output jobs have access to these environment variables: + | Variable | Description | |----------|-------------| -| `GH_AW_AGENT_OUTPUT` | Path to JSON file with agent output | -| `GH_AW_SAFE_OUTPUTS_STAGED` | Set to `"true"` in staged mode | -| `${{ inputs.NAME }}` | Each input as a variable | +| `GH_AW_AGENT_OUTPUT` | Path to JSON file containing the agent's output data | +| `GH_AW_SAFE_OUTPUTS_STAGED` | Set to `"true"` when running in staged/preview mode | + +**Important**: The `inputs:` field defines the MCP tool schema for the AI agent, not GitHub Actions workflow inputs. You **cannot** use `${{ inputs.name }}` syntax in custom safe-output jobs. Instead, read and parse the `GH_AW_AGENT_OUTPUT` JSON file to access the agent's provided values. + +### Accessing Agent Output + +Custom safe-output jobs receive the agent's data through the `GH_AW_AGENT_OUTPUT` environment variable, which contains a path to a JSON file. This file has the structure: + +```json +{ + "items": [ + { + "type": "job_name_with_underscores", + "field1": "value1", + "field2": "value2" + } + ] +} +``` + +The `type` field matches your job name with dashes converted to underscores (e.g., job `webhook-notify` → type `webhook_notify`). + +#### Bash Example + +```yaml +steps: + - name: Process output + run: | + if [ -f "$GH_AW_AGENT_OUTPUT" ]; then + # Extract specific field from matching items + MESSAGE=$(cat "$GH_AW_AGENT_OUTPUT" | jq -r '.items[] | select(.type == "my_job") | .message') + echo "Message: $MESSAGE" + else + echo "No agent output found" + exit 1 + fi +``` + +#### JavaScript Example + +```yaml +steps: + - name: Process output + uses: actions/github-script@v8 + with: + script: | + const fs = require('fs'); + const outputFile = process.env.GH_AW_AGENT_OUTPUT; + + if (!outputFile) { + core.info('No GH_AW_AGENT_OUTPUT environment variable found'); + return; + } + + // Read and parse the JSON file + const fileContent = fs.readFileSync(outputFile, 'utf8'); + const agentOutput = JSON.parse(fileContent); + + // Filter for items matching this job (job-name → job_name) + const items = agentOutput.items.filter(item => item.type === 'my_job'); + + // Process each item + for (const item of items) { + const message = item.message; + core.info(`Processing: ${message}`); + // Your logic here + } +``` + +### Understanding `inputs:` + +The `inputs:` field in your job definition serves **two purposes**: + +1. **Tool Discovery**: Defines the MCP tool schema that the AI agent sees +2. **Validation**: Describes what fields the agent should provide in its output + +**What `inputs:` does NOT do**: +- ❌ Create GitHub Actions workflow inputs +- ❌ Make `${{ inputs.* }}` expressions available in steps +- ❌ Pass data directly to job steps -Access inputs using `${{ inputs.name }}` in steps or via `process.env` in JavaScript. +The agent uses the `inputs:` schema to understand what parameters to include when calling your custom job. The actual values are written to the `GH_AW_AGENT_OUTPUT` JSON file, which your job must read and parse. ## Complete Example @@ -299,16 +400,49 @@ safe-outputs: type: string steps: - name: Send webhook + uses: actions/github-script@v8 env: WEBHOOK_URL: "${{ secrets.WEBHOOK_URL }}" - TITLE: "${{ inputs.title }}" - BODY: "${{ inputs.body }}" - run: | - PAYLOAD=$(jq -n --arg title "$TITLE" --arg body "$BODY" \ - '{title: $title, body: $body}') - curl -X POST "$WEBHOOK_URL" \ - -H "Content-Type: application/json" \ - -d "$PAYLOAD" + with: + script: | + const fs = require('fs'); + const webhookUrl = process.env.WEBHOOK_URL; + const outputFile = process.env.GH_AW_AGENT_OUTPUT; + + if (!webhookUrl) { + core.setFailed('WEBHOOK_URL secret is not configured'); + return; + } + + if (!outputFile) { + core.info('No GH_AW_AGENT_OUTPUT environment variable found'); + return; + } + + // Read and parse agent output + const fileContent = fs.readFileSync(outputFile, 'utf8'); + const agentOutput = JSON.parse(fileContent); + + // Filter for webhook-notify items (job name with dashes → underscores) + const items = agentOutput.items.filter(item => item.type === 'webhook_notify'); + + for (const item of items) { + const title = item.title; + const body = item.body; + + const response = await fetch(webhookUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ title, body }) + }); + + if (!response.ok) { + core.setFailed(`Webhook failed: ${response.status}`); + return; + } + } + + core.info('Notification sent successfully'); ``` ## Importing Custom Jobs @@ -373,6 +507,96 @@ if (process.env.GH_AW_SAFE_OUTPUTS_STAGED === 'true') { // Actually send the notification ``` +## Common Mistakes + +### ❌ Using `${{ inputs.* }}` Syntax + +**This does NOT work:** + +```yaml +steps: + - name: Wrong approach + env: + MESSAGE: "${{ inputs.message }}" # ❌ This will be empty! + run: | + echo "$MESSAGE" # Empty - inputs.* is not available +``` + +**Why it fails**: The `inputs:` field defines the MCP tool schema for the AI agent, not GitHub Actions workflow inputs. The `${{ inputs.* }}` syntax only works with workflow_dispatch inputs, not custom safe-output jobs. + +**✅ Correct approach:** + +```yaml +steps: + - name: Correct approach + run: | + if [ -f "$GH_AW_AGENT_OUTPUT" ]; then + MESSAGE=$(cat "$GH_AW_AGENT_OUTPUT" | jq -r '.items[] | select(.type == "my_job") | .message') + echo "$MESSAGE" + fi +``` + +### ❌ Wrong Job Type Name + +**This does NOT work:** + +```yaml +safe-outputs: + jobs: + notify-user: # Job name with dashes + # ... + steps: + - run: | + # Wrong: using dashes in type filter + jq '.items[] | select(.type == "notify-user")' # ❌ Won't match! +``` + +**Why it fails**: Job names with dashes are converted to underscores in the `type` field. + +**✅ Correct approach:** + +```yaml +safe-outputs: + jobs: + notify-user: # Job name with dashes + # ... + steps: + - run: | + # Correct: use underscores in type filter + jq '.items[] | select(.type == "notify_user")' # ✅ Matches! +``` + +### ❌ Not Checking for Agent Output + +**This does NOT work:** + +```yaml +steps: + - run: | + # ❌ Assumes file exists without checking + MESSAGE=$(cat "$GH_AW_AGENT_OUTPUT" | jq -r '.items[0].message') +``` + +**Why it fails**: If the agent doesn't call your tool, the file might not exist or be empty, causing errors. + +**✅ Correct approach:** + +```yaml +steps: + - run: | + if [ -f "$GH_AW_AGENT_OUTPUT" ]; then + ITEMS=$(cat "$GH_AW_AGENT_OUTPUT" | jq '.items[] | select(.type == "my_job")') + if [ -z "$ITEMS" ]; then + echo "No items found for this job" + exit 0 + fi + # Process items... + else + echo "No agent output file found" + exit 1 + fi +``` + ## Troubleshooting | Issue | Solution | From 136077a4a1759a9eb23be060d42da56cf8cc99cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 16:08:48 +0000 Subject: [PATCH 3/4] Complete documentation fix Co-authored-by: dsyme <7204669+dsyme@users.noreply.github.com> --- docs/package-lock.json | 19 ++++++++++++++++++- .../src/content/docs/agent-factory-status.mdx | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index ba024078c4f..874f76f0452 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -165,6 +165,7 @@ "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.37.3.tgz", "integrity": "sha512-p7cqbAkBYkBTiK1NIomxAEoF9Wko+mTV503qDm5Wgh+0MGGJnSsIzCSSJ+rWm8toFk9mnzNwNbxcnjwzIBEU3w==", "license": "MIT", + "peer": true, "dependencies": { "@astrojs/markdown-remark": "^6.3.1", "@astrojs/mdx": "^4.2.3", @@ -400,6 +401,7 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -1235,6 +1237,7 @@ "integrity": "sha512-DNCbwkAKugzCtiHJg/7DciIRwnKwAI2QH3VWWC1cVxoBBQTPnH5D9HcWqpDdduUqnCuW2PY78afVo+QlaInDdQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@csstools/postcss-is-pseudo-class": "^5.0.3", "cssesc": "^3.0.0", @@ -2258,6 +2261,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2456,6 +2460,7 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-5.16.12.tgz", "integrity": "sha512-R2LH1GW4JexpSMWffbpzkeZDAwbdZwI5BclXEwi+vIUPxQI+7Y9ZogJ5u4ukdYFGrcrie/0z+Od4i3MlR0nELQ==", "license": "MIT", + "peer": true, "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", @@ -2979,6 +2984,7 @@ "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", @@ -3409,6 +3415,7 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10" } @@ -3809,6 +3816,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -4049,7 +4057,8 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1534754.tgz", "integrity": "sha512-26T91cV5dbOYnXdJi5qQHoTtUoNEqwkHcAyu/IKtjIAxiEqPMrDiRkDOPWVsGfNZGmlQVHQbZRSjD8sxagWVsQ==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/diff": { "version": "8.0.3", @@ -6020,6 +6029,7 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", "license": "MIT", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -6422,6 +6432,7 @@ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.2.tgz", "integrity": "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==", "license": "MIT", + "peer": true, "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.1", @@ -7833,6 +7844,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7947,6 +7959,7 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -8579,6 +8592,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -10157,6 +10171,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -10447,6 +10462,7 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -10585,6 +10601,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/docs/src/content/docs/agent-factory-status.mdx b/docs/src/content/docs/agent-factory-status.mdx index a7566330623..545d0068e43 100644 --- a/docs/src/content/docs/agent-factory-status.mdx +++ b/docs/src/content/docs/agent-factory-status.mdx @@ -25,6 +25,7 @@ These are experimental agentic workflows used by the GitHub Next team to learn, | [Brave Web Search Agent](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/brave.md) | copilot | [![Brave Web Search Agent](https://github.com/githubnext/gh-aw/actions/workflows/brave.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/brave.lock.yml) | - | `/brave` | | [Breaking Change Checker](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/breaking-change-checker.md) | copilot | [![Breaking Change Checker](https://github.com/githubnext/gh-aw/actions/workflows/breaking-change-checker.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/breaking-change-checker.lock.yml) | - | - | | [Changeset Generator](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/changeset.md) | codex | [![Changeset Generator](https://github.com/githubnext/gh-aw/actions/workflows/changeset.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/changeset.lock.yml) | - | - | +| [Chroma Issue Indexer](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/chroma-issue-indexer.md) | copilot | [![Chroma Issue Indexer](https://github.com/githubnext/gh-aw/actions/workflows/chroma-issue-indexer.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/chroma-issue-indexer.lock.yml) | `0 */4 * * *` | - | | [CI Cleaner](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/hourly-ci-cleaner.md) | copilot | [![CI Cleaner](https://github.com/githubnext/gh-aw/actions/workflows/hourly-ci-cleaner.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/hourly-ci-cleaner.lock.yml) | `0 6,18 * * *` | - | | [CI Failure Doctor](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/ci-doctor.md) | copilot | [![CI Failure Doctor](https://github.com/githubnext/gh-aw/actions/workflows/ci-doctor.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/ci-doctor.lock.yml) | - | - | | [CI Optimization Coach](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/ci-coach.md) | copilot | [![CI Optimization Coach](https://github.com/githubnext/gh-aw/actions/workflows/ci-coach.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/ci-coach.lock.yml) | `0 13 * * 1-5` | - | @@ -59,6 +60,7 @@ These are experimental agentic workflows used by the GitHub Next team to learn, | [Daily Regulatory Report Generator](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-regulatory.md) | copilot | [![Daily Regulatory Report Generator](https://github.com/githubnext/gh-aw/actions/workflows/daily-regulatory.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-regulatory.lock.yml) | - | - | | [Daily Safe Output Tool Optimizer](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-safe-output-optimizer.md) | claude | [![Daily Safe Output Tool Optimizer](https://github.com/githubnext/gh-aw/actions/workflows/daily-safe-output-optimizer.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-safe-output-optimizer.lock.yml) | - | - | | [Daily Secrets Analysis Agent](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-secrets-analysis.md) | copilot | [![Daily Secrets Analysis Agent](https://github.com/githubnext/gh-aw/actions/workflows/daily-secrets-analysis.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-secrets-analysis.lock.yml) | - | - | +| [Daily Semgrep Scan](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-semgrep-scan.md) | copilot | [![Daily Semgrep Scan](https://github.com/githubnext/gh-aw/actions/workflows/daily-semgrep-scan.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-semgrep-scan.lock.yml) | - | - | | [Daily Team Evolution Insights](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-team-evolution-insights.md) | claude | [![Daily Team Evolution Insights](https://github.com/githubnext/gh-aw/actions/workflows/daily-team-evolution-insights.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-team-evolution-insights.lock.yml) | - | - | | [Daily Team Status](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-team-status.md) | copilot | [![Daily Team Status](https://github.com/githubnext/gh-aw/actions/workflows/daily-team-status.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-team-status.lock.yml) | - | - | | [Daily Testify Uber Super Expert](https://github.com/githubnext/gh-aw/blob/main/.github/workflows/daily-testify-uber-super-expert.md) | copilot | [![Daily Testify Uber Super Expert](https://github.com/githubnext/gh-aw/actions/workflows/daily-testify-uber-super-expert.lock.yml/badge.svg)](https://github.com/githubnext/gh-aw/actions/workflows/daily-testify-uber-super-expert.lock.yml) | - | - | From 9f13d5e8bdb46115259d6704c62ffa7990851d33 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 23 Jan 2026 16:15:43 +0000 Subject: [PATCH 4/4] Clarify 'inputs:' field usage in custom safe-outputs Removed misleading information about the 'inputs:' field in custom safe-output jobs and added clarification on its purpose. Also, removed a complete example of sending a webhook notification to streamline the documentation. --- .../docs/guides/custom-safe-outputs.md | 74 ------------------- 1 file changed, 74 deletions(-) diff --git a/docs/src/content/docs/guides/custom-safe-outputs.md b/docs/src/content/docs/guides/custom-safe-outputs.md index b3173af15b0..cacf7dce6e3 100644 --- a/docs/src/content/docs/guides/custom-safe-outputs.md +++ b/docs/src/content/docs/guides/custom-safe-outputs.md @@ -299,8 +299,6 @@ Custom safe-output jobs have access to these environment variables: | `GH_AW_AGENT_OUTPUT` | Path to JSON file containing the agent's output data | | `GH_AW_SAFE_OUTPUTS_STAGED` | Set to `"true"` when running in staged/preview mode | -**Important**: The `inputs:` field defines the MCP tool schema for the AI agent, not GitHub Actions workflow inputs. You **cannot** use `${{ inputs.name }}` syntax in custom safe-output jobs. Instead, read and parse the `GH_AW_AGENT_OUTPUT` JSON file to access the agent's provided values. - ### Accessing Agent Output Custom safe-output jobs receive the agent's data through the `GH_AW_AGENT_OUTPUT` environment variable, which contains a path to a JSON file. This file has the structure: @@ -373,78 +371,8 @@ The `inputs:` field in your job definition serves **two purposes**: 1. **Tool Discovery**: Defines the MCP tool schema that the AI agent sees 2. **Validation**: Describes what fields the agent should provide in its output -**What `inputs:` does NOT do**: -- ❌ Create GitHub Actions workflow inputs -- ❌ Make `${{ inputs.* }}` expressions available in steps -- ❌ Pass data directly to job steps - The agent uses the `inputs:` schema to understand what parameters to include when calling your custom job. The actual values are written to the `GH_AW_AGENT_OUTPUT` JSON file, which your job must read and parse. -## Complete Example - -```yaml wrap title="Send a webhook notification" -safe-outputs: - jobs: - webhook-notify: - description: "Send a notification to a webhook URL" - runs-on: ubuntu-latest - output: "Notification sent!" - inputs: - title: - description: "Notification title" - required: true - type: string - body: - description: "Notification body" - required: true - type: string - steps: - - name: Send webhook - uses: actions/github-script@v8 - env: - WEBHOOK_URL: "${{ secrets.WEBHOOK_URL }}" - with: - script: | - const fs = require('fs'); - const webhookUrl = process.env.WEBHOOK_URL; - const outputFile = process.env.GH_AW_AGENT_OUTPUT; - - if (!webhookUrl) { - core.setFailed('WEBHOOK_URL secret is not configured'); - return; - } - - if (!outputFile) { - core.info('No GH_AW_AGENT_OUTPUT environment variable found'); - return; - } - - // Read and parse agent output - const fileContent = fs.readFileSync(outputFile, 'utf8'); - const agentOutput = JSON.parse(fileContent); - - // Filter for webhook-notify items (job name with dashes → underscores) - const items = agentOutput.items.filter(item => item.type === 'webhook_notify'); - - for (const item of items) { - const title = item.title; - const body = item.body; - - const response = await fetch(webhookUrl, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ title, body }) - }); - - if (!response.ok) { - core.setFailed(`Webhook failed: ${response.status}`); - return; - } - } - - core.info('Notification sent successfully'); -``` - ## Importing Custom Jobs Define jobs in shared files under `.github/workflows/shared/` and import them: @@ -522,8 +450,6 @@ steps: echo "$MESSAGE" # Empty - inputs.* is not available ``` -**Why it fails**: The `inputs:` field defines the MCP tool schema for the AI agent, not GitHub Actions workflow inputs. The `${{ inputs.* }}` syntax only works with workflow_dispatch inputs, not custom safe-output jobs. - **✅ Correct approach:** ```yaml