diff --git a/.github/workflows/shared/github-mcp-app.md b/.github/workflows/shared/github-mcp-app.md new file mode 100644 index 00000000..1695dcbf --- /dev/null +++ b/.github/workflows/shared/github-mcp-app.md @@ -0,0 +1,30 @@ +--- +#tools: +# github: +# app: +# app-id: ${{ vars.APP_ID }} +# private-key: ${{ secrets.APP_PRIVATE_KEY }} +--- + + diff --git a/.github/workflows/shared/github-queries-safe-input.md b/.github/workflows/shared/github-queries-safe-input.md new file mode 100644 index 00000000..c2013ac7 --- /dev/null +++ b/.github/workflows/shared/github-queries-safe-input.md @@ -0,0 +1,340 @@ +--- +safe-inputs: + github-issue-query: + description: "Query GitHub issues with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter." + inputs: + repo: + type: string + description: "Repository in owner/repo format (defaults to current repository)" + required: false + state: + type: string + description: "Issue state: open, closed, all (default: open)" + required: false + limit: + type: number + description: "Maximum number of issues to fetch (default: 30)" + required: false + jq: + type: string + description: "jq filter expression to apply to output. If not provided, returns schema info instead of full data." + required: false + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e + + # Default values + REPO="${INPUT_REPO:-}" + STATE="${INPUT_STATE:-open}" + LIMIT="${INPUT_LIMIT:-30}" + JQ_FILTER="${INPUT_JQ:-}" + + # JSON fields to fetch + JSON_FIELDS="number,title,state,author,createdAt,updatedAt,closedAt,body,labels,assignees,comments,milestone,url" + + # Build and execute gh command + if [[ -n "$REPO" ]]; then + OUTPUT=$(gh issue list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS" --repo "$REPO") + else + OUTPUT=$(gh issue list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS") + fi + + # Apply jq filter if specified + if [[ -n "$JQ_FILTER" ]]; then + jq "$JQ_FILTER" <<< "$OUTPUT" + else + # Return schema and size instead of full data + ITEM_COUNT=$(jq 'length' <<< "$OUTPUT") + DATA_SIZE=${#OUTPUT} + + # Validate values are numeric + if ! [[ "$ITEM_COUNT" =~ ^[0-9]+$ ]]; then + ITEM_COUNT=0 + fi + if ! [[ "$DATA_SIZE" =~ ^[0-9]+$ ]]; then + DATA_SIZE=0 + fi + + cat << EOF + { + "message": "No --jq filter provided. Use --jq to filter and retrieve data.", + "item_count": $ITEM_COUNT, + "data_size_bytes": $DATA_SIZE, + "schema": { + "type": "array", + "description": "Array of issue objects", + "item_fields": { + "number": "integer - Issue number", + "title": "string - Issue title", + "state": "string - Issue state (OPEN, CLOSED)", + "author": "object - Author info with login field", + "createdAt": "string - ISO timestamp of creation", + "updatedAt": "string - ISO timestamp of last update", + "closedAt": "string|null - ISO timestamp of close", + "body": "string - Issue body content", + "labels": "array - Array of label objects with name field", + "assignees": "array - Array of assignee objects with login field", + "comments": "object - Comments info with totalCount field", + "milestone": "object|null - Milestone info with title field", + "url": "string - Issue URL" + } + }, + "suggested_queries": [ + {"description": "Get all data", "query": "."}, + {"description": "Get issue numbers and titles", "query": ".[] | {number, title}"}, + {"description": "Get open issues only", "query": ".[] | select(.state == \"OPEN\")"}, + {"description": "Get issues by author", "query": ".[] | select(.author.login == \"USERNAME\")"}, + {"description": "Get issues with label", "query": ".[] | select(.labels | map(.name) | index(\"bug\"))"}, + {"description": "Get issues with many comments", "query": ".[] | select(.comments.totalCount > 5) | {number, title, comments: .comments.totalCount}"}, + {"description": "Count by state", "query": "group_by(.state) | map({state: .[0].state, count: length})"} + ] + } + EOF + fi + + github-pr-query: + description: "Query GitHub pull requests with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter." + inputs: + repo: + type: string + description: "Repository in owner/repo format (defaults to current repository)" + required: false + state: + type: string + description: "PR state: open, closed, merged, all (default: open)" + required: false + limit: + type: number + description: "Maximum number of PRs to fetch (default: 30)" + required: false + jq: + type: string + description: "jq filter expression to apply to output. If not provided, returns schema info instead of full data." + required: false + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e + + # Default values + REPO="${INPUT_REPO:-}" + STATE="${INPUT_STATE:-open}" + LIMIT="${INPUT_LIMIT:-30}" + JQ_FILTER="${INPUT_JQ:-}" + + # JSON fields to fetch + JSON_FIELDS="number,title,state,author,createdAt,updatedAt,mergedAt,closedAt,headRefName,baseRefName,isDraft,reviewDecision,additions,deletions,changedFiles,labels,assignees,reviewRequests,url" + + # Build and execute gh command + if [[ -n "$REPO" ]]; then + OUTPUT=$(gh pr list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS" --repo "$REPO") + else + OUTPUT=$(gh pr list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS") + fi + + # Apply jq filter if specified + if [[ -n "$JQ_FILTER" ]]; then + jq "$JQ_FILTER" <<< "$OUTPUT" + else + # Return schema and size instead of full data + ITEM_COUNT=$(jq 'length' <<< "$OUTPUT") + DATA_SIZE=${#OUTPUT} + + # Validate values are numeric + if ! [[ "$ITEM_COUNT" =~ ^[0-9]+$ ]]; then + ITEM_COUNT=0 + fi + if ! [[ "$DATA_SIZE" =~ ^[0-9]+$ ]]; then + DATA_SIZE=0 + fi + + cat << EOF + { + "message": "No --jq filter provided. Use --jq to filter and retrieve data.", + "item_count": $ITEM_COUNT, + "data_size_bytes": $DATA_SIZE, + "schema": { + "type": "array", + "description": "Array of pull request objects", + "item_fields": { + "number": "integer - PR number", + "title": "string - PR title", + "state": "string - PR state (OPEN, CLOSED, MERGED)", + "author": "object - Author info with login field", + "createdAt": "string - ISO timestamp of creation", + "updatedAt": "string - ISO timestamp of last update", + "mergedAt": "string|null - ISO timestamp of merge", + "closedAt": "string|null - ISO timestamp of close", + "headRefName": "string - Source branch name", + "baseRefName": "string - Target branch name", + "isDraft": "boolean - Whether PR is a draft", + "reviewDecision": "string|null - Review decision (APPROVED, CHANGES_REQUESTED, REVIEW_REQUIRED)", + "additions": "integer - Lines added", + "deletions": "integer - Lines deleted", + "changedFiles": "integer - Number of files changed", + "labels": "array - Array of label objects with name field", + "assignees": "array - Array of assignee objects with login field", + "reviewRequests": "array - Array of review request objects", + "url": "string - PR URL" + } + }, + "suggested_queries": [ + {"description": "Get all data", "query": "."}, + {"description": "Get PR numbers and titles", "query": ".[] | {number, title}"}, + {"description": "Get open PRs only", "query": ".[] | select(.state == \"OPEN\")"}, + {"description": "Get merged PRs", "query": ".[] | select(.mergedAt != null)"}, + {"description": "Get PRs by author", "query": ".[] | select(.author.login == \"USERNAME\")"}, + {"description": "Get large PRs", "query": ".[] | select(.changedFiles > 10) | {number, title, changedFiles}"}, + {"description": "Count by state", "query": "group_by(.state) | map({state: .[0].state, count: length})"} + ] + } + EOF + fi + + github-discussion-query: + description: "Query GitHub discussions with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter." + inputs: + repo: + type: string + description: "Repository in owner/repo format (defaults to current repository)" + required: false + limit: + type: number + description: "Maximum number of discussions to fetch (default: 30)" + required: false + jq: + type: string + description: "jq filter expression to apply to output. If not provided, returns schema info instead of full data." + required: false + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e + + # Default values + REPO="${INPUT_REPO:-}" + LIMIT="${INPUT_LIMIT:-30}" + JQ_FILTER="${INPUT_JQ:-}" + + # Parse repository owner and name + if [[ -n "$REPO" ]]; then + OWNER=$(echo "$REPO" | cut -d'/' -f1) + NAME=$(echo "$REPO" | cut -d'/' -f2) + else + # Get current repository from GitHub context + OWNER="${GITHUB_REPOSITORY_OWNER:-}" + NAME=$(echo "${GITHUB_REPOSITORY:-}" | cut -d'/' -f2) + fi + + # Validate owner and name + if [[ -z "$OWNER" || -z "$NAME" ]]; then + echo "Error: Could not determine repository owner and name" >&2 + exit 1 + fi + + # Build GraphQL query for discussions + GRAPHQL_QUERY=$(cat <. Use single quotes ' for complex args to avoid shell interpretation issues." + inputs: + args: + type: string + description: "Arguments to pass to go CLI (without the 'go' prefix). Examples: 'test ./...', 'build ./cmd/gh-aw', 'mod tidy', 'fmt ./...', 'vet ./...'" + required: true + run: | + echo "go $INPUT_ARGS" + go $INPUT_ARGS + + make: + description: "Execute any Make target. This tool is accessible as 'safeinputs-make'. Provide the target name(s) (e.g., args: 'build'). The tool will run: make . Use single quotes ' for complex args to avoid shell interpretation issues." + inputs: + args: + type: string + description: "Arguments to pass to make (target names and options). Examples: 'build', 'test-unit', 'lint', 'recompile', 'agent-finish', 'fmt build test-unit'" + required: true + run: | + echo "make $INPUT_ARGS" + make $INPUT_ARGS +--- + +**IMPORTANT**: Always use the `safeinputs-go` and `safeinputs-make` tools for Go and Make commands instead of running them directly via bash. These safe-input tools provide consistent execution and proper logging. + +**Correct**: +``` +Use the safeinputs-go tool with args: "test ./..." +Use the safeinputs-make tool with args: "build" +Use the safeinputs-make tool with args: "lint" +Use the safeinputs-make tool with args: "test-unit" +``` + +**Incorrect**: +``` +Use the go safe-input tool with args: "test ./..." ❌ (Wrong tool name - use safeinputs-go) +Run: go test ./... ❌ (Use safeinputs-go instead) +Execute bash: make build ❌ (Use safeinputs-make instead) +``` diff --git a/.github/workflows/shared/mcp-pagination.md b/.github/workflows/shared/mcp-pagination.md new file mode 100644 index 00000000..f3a9928f --- /dev/null +++ b/.github/workflows/shared/mcp-pagination.md @@ -0,0 +1,110 @@ +## MCP Response Size Limits + +MCP tool responses have a **25,000 token limit**. When GitHub API responses exceed this limit, workflows must retry with pagination parameters, wasting turns and tokens. + +### Common Scenarios + +**Problem**: Fetching large result sets without pagination +- `list_pull_requests` with many PRs (75,897 tokens in one case) +- `pull_request_read` with large diff/comments (31,675 tokens observed) +- `search_issues`, `search_code` with many results + +**Solution**: Use proactive pagination to stay under token limits + +### Pagination Best Practices + +#### 1. Use `perPage` Parameter + +Limit results per request to prevent oversized responses: + +```bash +# Good: Fetch PRs in small batches +list_pull_requests --perPage 10 + +# Good: Get issue with limited comments +issue_read --method get_comments --perPage 20 + +# Bad: Default pagination may return too much data +list_pull_requests # May exceed 25k tokens +``` + +#### 2. Common `perPage` Values + +- **10-20**: For detailed items (PRs with diffs, issues with comments) +- **50-100**: For simpler list operations (commits, branches, labels) +- **1-5**: For exploratory queries or schema discovery + +#### 3. Handle Pagination Loops + +When you need all results: + +```bash +# Step 1: Fetch first page +result=$(list_pull_requests --perPage 20 --page 1) + +# Step 2: Check if more pages exist +# Most list operations return metadata about total count or next page + +# Step 3: Fetch subsequent pages if needed +result=$(list_pull_requests --perPage 20 --page 2) +``` + +### Tool-Specific Guidance + +#### Pull Requests + +```bash +# Fetch recent PRs in small batches +list_pull_requests --state all --perPage 10 --sort updated --direction desc + +# Get PR details without full diff/comments +pull_request_read --method get --pullNumber 123 + +# Get PR files separately if needed +pull_request_read --method get_files --pullNumber 123 --perPage 30 +``` + +#### Issues + +```bash +# List issues with pagination +list_issues --perPage 20 --page 1 + +# Get issue comments in batches +issue_read --method get_comments --issue_number 123 --perPage 20 +``` + +#### Code Search + +```bash +# Search with limited results +search_code --query "function language:go" --perPage 10 +``` + +### Error Messages to Watch For + +If you see these errors, add pagination: + +- `MCP tool "list_pull_requests" response (75897 tokens) exceeds maximum allowed tokens (25000)` +- `MCP tool "pull_request_read" response (31675 tokens) exceeds maximum allowed tokens (25000)` +- `Response too large for tool [tool_name]` + +### Performance Tips + +1. **Start small**: Use `perPage: 10` initially, increase if needed +2. **Fetch incrementally**: Get overview first, then details for specific items +3. **Avoid wildcards**: Don't fetch all data when you need specific items +4. **Use filters**: Combine `perPage` with state/label/date filters to reduce results + +### Example Workflow Pattern + +```markdown +# Analyze Recent Pull Requests + +1. Fetch 10 most recent PRs (stay under token limit) +2. For each PR, get summary without full diff +3. If detailed analysis needed, fetch files for specific PR separately +4. Process results incrementally rather than loading everything at once +``` + +This proactive approach eliminates retry loops and reduces token consumption. diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 3066b444..e2f158c4 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -13,15 +13,25 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.41.1). DO NOT EDIT. +# This file was automatically generated by gh-aw. DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile # For more information: https://github.com/github/gh-aw/blob/main/.github/aw/github-agentic-workflows.md # -# Smoke Copilot +# Smoke test workflow that validates Copilot engine functionality by reviewing recent PRs twice daily # -# frontmatter-hash: d85e07847e5e59b6ff5a79a458c6351027eab65247ff3e511a03f45c9f40e445 +# Resolved workflow manifest: +# Imports: +# - shared/gh.md +# - shared/github-mcp-app.md +# - shared/github-queries-safe-input.md +# - shared/go-make.md +# - shared/mcp-pagination.md +# - shared/mcp/tavily.md +# - shared/reporting.md +# +# frontmatter-hash: 8eb252da0f4f6451e7f9ca872f9048c5786712af01b1549dd4a8b594bf6dc7bc name: "Smoke Copilot" "on": @@ -31,7 +41,7 @@ name: "Smoke Copilot" types: - labeled schedule: - - cron: "55 */12 * * *" + - cron: "38 */12 * * *" workflow_dispatch: null permissions: {} @@ -59,8 +69,14 @@ jobs: comment_repo: ${{ steps.add-comment.outputs.comment-repo }} comment_url: ${{ steps.add-comment.outputs.comment-url }} steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts - uses: github/gh-aw/actions/setup@d47697b2bf91080b55fb9c6905576c36e061011a # v0.41.1 + uses: ./actions/setup with: destination: /opt/gh-aw/actions - name: Check workflow file timestamps @@ -93,6 +109,7 @@ jobs: permissions: actions: read contents: read + discussions: read issues: read pull-requests: read env: @@ -112,14 +129,26 @@ jobs: output_types: ${{ steps.collect_output.outputs.output_types }} secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts - uses: github/gh-aw/actions/setup@d47697b2bf91080b55fb9c6905576c36e061011a # v0.41.1 + uses: ./actions/setup with: destination: /opt/gh-aw/actions - name: Checkout repository uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 with: persist-credentials: false + - name: Setup Go + uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 + with: + go-version: '1.25' + - name: Capture GOROOT for AWF chroot mode + run: echo "GOROOT=$(go env GOROOT)" >> $GITHUB_ENV - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh # Cache memory file share configuration from frontmatter processed below @@ -164,9 +193,9 @@ jobs: env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.402 + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.403 - name: Install awf binary - run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.13.1 + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.13.4 - name: Determine automatic lockdown mode for GitHub MCP server id: determine-automatic-lockdown env: @@ -178,7 +207,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh alpine:latest ghcr.io/github/gh-aw-mcpg:v0.0.98 ghcr.io/github/github-mcp-server:v0.30.3 mcr.microsoft.com/playwright/mcp node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh alpine:latest ghcr.io/github/gh-aw-firewall/agent-act:0.13.4 ghcr.io/github/gh-aw-firewall/squid:0.13.4 ghcr.io/github/gh-aw-mcpg:v0.0.99 ghcr.io/github/github-mcp-server:v0.30.3 ghcr.io/github/serena-mcp-server:latest mcr.microsoft.com/playwright/mcp node:lts-alpine - name: Install gh-aw extension env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} @@ -209,7 +238,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'EOF' - {"add_comment":{"max":1},"add_labels":{"allowed":["smoke-copilot"],"max":3},"create_issue":{"expires":2,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":2},"add_labels":{"allowed":["smoke-copilot"],"max":3},"create_issue":{"expires":2,"group":true,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'EOF' [ @@ -254,7 +283,7 @@ jobs: "name": "create_issue" }, { - "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. CONSTRAINTS: Maximum 1 comment(s) can be added.", + "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. CONSTRAINTS: Maximum 2 comment(s) can be added.", "inputSchema": { "additionalProperties": false, "properties": { @@ -503,15 +532,576 @@ jobs: bash /opt/gh-aw/actions/start_safe_outputs_server.sh + - name: Setup Safe Inputs Config + run: | + mkdir -p /opt/gh-aw/safe-inputs/logs + cat > /opt/gh-aw/safe-inputs/tools.json << 'EOF_TOOLS_JSON' + { + "serverName": "safeinputs", + "version": "1.0.0", + "logDir": "/opt/gh-aw/safe-inputs/logs", + "tools": [ + { + "name": "gh", + "description": "Execute any gh CLI command. This tool is accessible as 'safeinputs-gh'. Provide the full command after 'gh' (e.g., args: 'pr list --limit 5'). The tool will run: gh \u003cargs\u003e. Use single quotes ' for complex args to avoid shell interpretation issues.", + "inputSchema": { + "properties": { + "args": { + "description": "Arguments to pass to gh CLI (without the 'gh' prefix). Examples: 'pr list --limit 5', 'issue view 123', 'api repos/{owner}/{repo}'", + "type": "string" + } + }, + "required": [ + "args" + ], + "type": "object" + }, + "handler": "gh.sh", + "env": { + "GH_AW_GH_TOKEN": "GH_AW_GH_TOKEN", + "GH_DEBUG": "GH_DEBUG" + }, + "timeout": 60 + }, + { + "name": "github-discussion-query", + "description": "Query GitHub discussions with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.", + "inputSchema": { + "properties": { + "jq": { + "description": "jq filter expression to apply to output. If not provided, returns schema info instead of full data.", + "type": "string" + }, + "limit": { + "description": "Maximum number of discussions to fetch (default: 30)", + "type": "number" + }, + "repo": { + "description": "Repository in owner/repo format (defaults to current repository)", + "type": "string" + } + }, + "type": "object" + }, + "handler": "github-discussion-query.sh", + "env": { + "GH_TOKEN": "GH_TOKEN" + }, + "timeout": 60 + }, + { + "name": "github-issue-query", + "description": "Query GitHub issues with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.", + "inputSchema": { + "properties": { + "jq": { + "description": "jq filter expression to apply to output. If not provided, returns schema info instead of full data.", + "type": "string" + }, + "limit": { + "description": "Maximum number of issues to fetch (default: 30)", + "type": "number" + }, + "repo": { + "description": "Repository in owner/repo format (defaults to current repository)", + "type": "string" + }, + "state": { + "description": "Issue state: open, closed, all (default: open)", + "type": "string" + } + }, + "type": "object" + }, + "handler": "github-issue-query.sh", + "env": { + "GH_TOKEN": "GH_TOKEN" + }, + "timeout": 60 + }, + { + "name": "github-pr-query", + "description": "Query GitHub pull requests with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter.", + "inputSchema": { + "properties": { + "jq": { + "description": "jq filter expression to apply to output. If not provided, returns schema info instead of full data.", + "type": "string" + }, + "limit": { + "description": "Maximum number of PRs to fetch (default: 30)", + "type": "number" + }, + "repo": { + "description": "Repository in owner/repo format (defaults to current repository)", + "type": "string" + }, + "state": { + "description": "PR state: open, closed, merged, all (default: open)", + "type": "string" + } + }, + "type": "object" + }, + "handler": "github-pr-query.sh", + "env": { + "GH_TOKEN": "GH_TOKEN" + }, + "timeout": 60 + }, + { + "name": "go", + "description": "Execute any Go command. This tool is accessible as 'safeinputs-go'. Provide the full command after 'go' (e.g., args: 'test ./...'). The tool will run: go \u003cargs\u003e. Use single quotes ' for complex args to avoid shell interpretation issues.", + "inputSchema": { + "properties": { + "args": { + "description": "Arguments to pass to go CLI (without the 'go' prefix). Examples: 'test ./...', 'build ./cmd/gh-aw', 'mod tidy', 'fmt ./...', 'vet ./...'", + "type": "string" + } + }, + "required": [ + "args" + ], + "type": "object" + }, + "handler": "go.sh", + "timeout": 60 + }, + { + "name": "make", + "description": "Execute any Make target. This tool is accessible as 'safeinputs-make'. Provide the target name(s) (e.g., args: 'build'). The tool will run: make \u003cargs\u003e. Use single quotes ' for complex args to avoid shell interpretation issues.", + "inputSchema": { + "properties": { + "args": { + "description": "Arguments to pass to make (target names and options). Examples: 'build', 'test-unit', 'lint', 'recompile', 'agent-finish', 'fmt build test-unit'", + "type": "string" + } + }, + "required": [ + "args" + ], + "type": "object" + }, + "handler": "make.sh", + "timeout": 60 + } + ] + } + EOF_TOOLS_JSON + cat > /opt/gh-aw/safe-inputs/mcp-server.cjs << 'EOFSI' + const path = require("path"); + const { startHttpServer } = require("./safe_inputs_mcp_server_http.cjs"); + const configPath = path.join(__dirname, "tools.json"); + const port = parseInt(process.env.GH_AW_SAFE_INPUTS_PORT || "3000", 10); + const apiKey = process.env.GH_AW_SAFE_INPUTS_API_KEY || ""; + startHttpServer(configPath, { + port: port, + stateless: true, + logDir: "/opt/gh-aw/safe-inputs/logs" + }).catch(error => { + console.error("Failed to start safe-inputs HTTP server:", error); + process.exit(1); + }); + EOFSI + chmod +x /opt/gh-aw/safe-inputs/mcp-server.cjs + + - name: Setup Safe Inputs Tool Files + run: | + cat > /opt/gh-aw/safe-inputs/gh.sh << 'EOFSH_gh' + #!/bin/bash + # Auto-generated safe-input tool: gh + # Execute any gh CLI command. This tool is accessible as 'safeinputs-gh'. Provide the full command after 'gh' (e.g., args: 'pr list --limit 5'). The tool will run: gh . Use single quotes ' for complex args to avoid shell interpretation issues. + + set -euo pipefail + + echo "gh $INPUT_ARGS" + echo " token: ${GH_AW_GH_TOKEN:0:6}..." + GH_TOKEN="$GH_AW_GH_TOKEN" gh $INPUT_ARGS + + EOFSH_gh + chmod +x /opt/gh-aw/safe-inputs/gh.sh + cat > /opt/gh-aw/safe-inputs/github-discussion-query.sh << 'EOFSH_github-discussion-query' + #!/bin/bash + # Auto-generated safe-input tool: github-discussion-query + # Query GitHub discussions with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. + + set -euo pipefail + + set -e + + # Default values + REPO="${INPUT_REPO:-}" + LIMIT="${INPUT_LIMIT:-30}" + JQ_FILTER="${INPUT_JQ:-}" + + # Parse repository owner and name + if [[ -n "$REPO" ]]; then + OWNER=$(echo "$REPO" | cut -d'/' -f1) + NAME=$(echo "$REPO" | cut -d'/' -f2) + else + # Get current repository from GitHub context + OWNER="${GITHUB_REPOSITORY_OWNER:-}" + NAME=$(echo "${GITHUB_REPOSITORY:-}" | cut -d'/' -f2) + fi + + # Validate owner and name + if [[ -z "$OWNER" || -z "$NAME" ]]; then + echo "Error: Could not determine repository owner and name" >&2 + exit 1 + fi + + # Build GraphQL query for discussions + GRAPHQL_QUERY=$(cat < /opt/gh-aw/safe-inputs/github-issue-query.sh << 'EOFSH_github-issue-query' + #!/bin/bash + # Auto-generated safe-input tool: github-issue-query + # Query GitHub issues with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. + + set -euo pipefail + + set -e + + # Default values + REPO="${INPUT_REPO:-}" + STATE="${INPUT_STATE:-open}" + LIMIT="${INPUT_LIMIT:-30}" + JQ_FILTER="${INPUT_JQ:-}" + + # JSON fields to fetch + JSON_FIELDS="number,title,state,author,createdAt,updatedAt,closedAt,body,labels,assignees,comments,milestone,url" + + # Build and execute gh command + if [[ -n "$REPO" ]]; then + OUTPUT=$(gh issue list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS" --repo "$REPO") + else + OUTPUT=$(gh issue list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS") + fi + + # Apply jq filter if specified + if [[ -n "$JQ_FILTER" ]]; then + jq "$JQ_FILTER" <<< "$OUTPUT" + else + # Return schema and size instead of full data + ITEM_COUNT=$(jq 'length' <<< "$OUTPUT") + DATA_SIZE=${#OUTPUT} + + # Validate values are numeric + if ! [[ "$ITEM_COUNT" =~ ^[0-9]+$ ]]; then + ITEM_COUNT=0 + fi + if ! [[ "$DATA_SIZE" =~ ^[0-9]+$ ]]; then + DATA_SIZE=0 + fi + + cat << EOF + { + "message": "No --jq filter provided. Use --jq to filter and retrieve data.", + "item_count": $ITEM_COUNT, + "data_size_bytes": $DATA_SIZE, + "schema": { + "type": "array", + "description": "Array of issue objects", + "item_fields": { + "number": "integer - Issue number", + "title": "string - Issue title", + "state": "string - Issue state (OPEN, CLOSED)", + "author": "object - Author info with login field", + "createdAt": "string - ISO timestamp of creation", + "updatedAt": "string - ISO timestamp of last update", + "closedAt": "string|null - ISO timestamp of close", + "body": "string - Issue body content", + "labels": "array - Array of label objects with name field", + "assignees": "array - Array of assignee objects with login field", + "comments": "object - Comments info with totalCount field", + "milestone": "object|null - Milestone info with title field", + "url": "string - Issue URL" + } + }, + "suggested_queries": [ + {"description": "Get all data", "query": "."}, + {"description": "Get issue numbers and titles", "query": ".[] | {number, title}"}, + {"description": "Get open issues only", "query": ".[] | select(.state == \"OPEN\")"}, + {"description": "Get issues by author", "query": ".[] | select(.author.login == \"USERNAME\")"}, + {"description": "Get issues with label", "query": ".[] | select(.labels | map(.name) | index(\"bug\"))"}, + {"description": "Get issues with many comments", "query": ".[] | select(.comments.totalCount > 5) | {number, title, comments: .comments.totalCount}"}, + {"description": "Count by state", "query": "group_by(.state) | map({state: .[0].state, count: length})"} + ] + } + EOF + fi + + + EOFSH_github-issue-query + chmod +x /opt/gh-aw/safe-inputs/github-issue-query.sh + cat > /opt/gh-aw/safe-inputs/github-pr-query.sh << 'EOFSH_github-pr-query' + #!/bin/bash + # Auto-generated safe-input tool: github-pr-query + # Query GitHub pull requests with jq filtering support. Without --jq, returns schema and data size info. Use --jq '.' to get all data, or specific jq expressions to filter. + + set -euo pipefail + + set -e + + # Default values + REPO="${INPUT_REPO:-}" + STATE="${INPUT_STATE:-open}" + LIMIT="${INPUT_LIMIT:-30}" + JQ_FILTER="${INPUT_JQ:-}" + + # JSON fields to fetch + JSON_FIELDS="number,title,state,author,createdAt,updatedAt,mergedAt,closedAt,headRefName,baseRefName,isDraft,reviewDecision,additions,deletions,changedFiles,labels,assignees,reviewRequests,url" + + # Build and execute gh command + if [[ -n "$REPO" ]]; then + OUTPUT=$(gh pr list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS" --repo "$REPO") + else + OUTPUT=$(gh pr list --state "$STATE" --limit "$LIMIT" --json "$JSON_FIELDS") + fi + + # Apply jq filter if specified + if [[ -n "$JQ_FILTER" ]]; then + jq "$JQ_FILTER" <<< "$OUTPUT" + else + # Return schema and size instead of full data + ITEM_COUNT=$(jq 'length' <<< "$OUTPUT") + DATA_SIZE=${#OUTPUT} + + # Validate values are numeric + if ! [[ "$ITEM_COUNT" =~ ^[0-9]+$ ]]; then + ITEM_COUNT=0 + fi + if ! [[ "$DATA_SIZE" =~ ^[0-9]+$ ]]; then + DATA_SIZE=0 + fi + + cat << EOF + { + "message": "No --jq filter provided. Use --jq to filter and retrieve data.", + "item_count": $ITEM_COUNT, + "data_size_bytes": $DATA_SIZE, + "schema": { + "type": "array", + "description": "Array of pull request objects", + "item_fields": { + "number": "integer - PR number", + "title": "string - PR title", + "state": "string - PR state (OPEN, CLOSED, MERGED)", + "author": "object - Author info with login field", + "createdAt": "string - ISO timestamp of creation", + "updatedAt": "string - ISO timestamp of last update", + "mergedAt": "string|null - ISO timestamp of merge", + "closedAt": "string|null - ISO timestamp of close", + "headRefName": "string - Source branch name", + "baseRefName": "string - Target branch name", + "isDraft": "boolean - Whether PR is a draft", + "reviewDecision": "string|null - Review decision (APPROVED, CHANGES_REQUESTED, REVIEW_REQUIRED)", + "additions": "integer - Lines added", + "deletions": "integer - Lines deleted", + "changedFiles": "integer - Number of files changed", + "labels": "array - Array of label objects with name field", + "assignees": "array - Array of assignee objects with login field", + "reviewRequests": "array - Array of review request objects", + "url": "string - PR URL" + } + }, + "suggested_queries": [ + {"description": "Get all data", "query": "."}, + {"description": "Get PR numbers and titles", "query": ".[] | {number, title}"}, + {"description": "Get open PRs only", "query": ".[] | select(.state == \"OPEN\")"}, + {"description": "Get merged PRs", "query": ".[] | select(.mergedAt != null)"}, + {"description": "Get PRs by author", "query": ".[] | select(.author.login == \"USERNAME\")"}, + {"description": "Get large PRs", "query": ".[] | select(.changedFiles > 10) | {number, title, changedFiles}"}, + {"description": "Count by state", "query": "group_by(.state) | map({state: .[0].state, count: length})"} + ] + } + EOF + fi + + + EOFSH_github-pr-query + chmod +x /opt/gh-aw/safe-inputs/github-pr-query.sh + cat > /opt/gh-aw/safe-inputs/go.sh << 'EOFSH_go' + #!/bin/bash + # Auto-generated safe-input tool: go + # Execute any Go command. This tool is accessible as 'safeinputs-go'. Provide the full command after 'go' (e.g., args: 'test ./...'). The tool will run: go . Use single quotes ' for complex args to avoid shell interpretation issues. + + set -euo pipefail + + echo "go $INPUT_ARGS" + go $INPUT_ARGS + + + EOFSH_go + chmod +x /opt/gh-aw/safe-inputs/go.sh + cat > /opt/gh-aw/safe-inputs/make.sh << 'EOFSH_make' + #!/bin/bash + # Auto-generated safe-input tool: make + # Execute any Make target. This tool is accessible as 'safeinputs-make'. Provide the target name(s) (e.g., args: 'build'). The tool will run: make . Use single quotes ' for complex args to avoid shell interpretation issues. + + set -euo pipefail + + echo "make $INPUT_ARGS" + make $INPUT_ARGS + + EOFSH_make + chmod +x /opt/gh-aw/safe-inputs/make.sh + + - name: Generate Safe Inputs MCP Server Config + id: safe-inputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + API_KEY="" + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + PORT=3000 + + # Register API key as secret to mask it from logs + echo "::add-mask::${API_KEY}" + + # Set outputs for next steps + { + echo "safe_inputs_api_key=${API_KEY}" + echo "safe_inputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Inputs MCP server will run on port ${PORT}" + + - name: Start Safe Inputs MCP HTTP Server + id: safe-inputs-start + env: + DEBUG: '*' + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-config.outputs.safe_inputs_port }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-config.outputs.safe_inputs_api_key }} + GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_DEBUG: 1 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_INPUTS_PORT + export GH_AW_SAFE_INPUTS_API_KEY + + bash /opt/gh-aw/actions/start_safe_inputs_server.sh + - name: Start MCP gateway id: start-mcp-gateway env: + GH_AW_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_AW_SAFE_INPUTS_API_KEY: ${{ steps.safe-inputs-start.outputs.api_key }} + GH_AW_SAFE_INPUTS_PORT: ${{ steps.safe-inputs-start.outputs.port }} GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GH_DEBUG: 1 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }} run: | set -eo pipefail mkdir -p /tmp/gh-aw/mcp-config @@ -527,7 +1117,7 @@ jobs: # Register API key as secret to mask it from logs echo "::add-mask::${MCP_GATEWAY_API_KEY}" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.0.98' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_INPUTS_PORT -e GH_AW_SAFE_INPUTS_API_KEY -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -e GH_AW_GH_TOKEN -e GH_DEBUG -e GH_TOKEN -e TAVILY_API_KEY -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.0.99' mkdir -p /home/runner/.copilot cat << MCPCONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -538,7 +1128,7 @@ jobs: "container": "alpine:latest", "entrypoint": "/opt/gh-aw/gh-aw", "entrypointArgs": ["mcp-server"], - "mounts": ["/opt/gh-aw:/opt/gh-aw:ro", "${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "mounts": ["/opt/gh-aw:/opt/gh-aw:ro", "/usr/bin/gh:/usr/bin/gh:ro", "${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], "env": { "GITHUB_TOKEN": "\${GITHUB_TOKEN}" } @@ -550,7 +1140,7 @@ jobs: "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", - "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" + "GITHUB_TOOLSETS": "repos,pull_requests" } }, "playwright": { @@ -560,6 +1150,13 @@ jobs: "entrypointArgs": ["--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--allowed-hosts", "localhost,localhost:*,127.0.0.1,127.0.0.1:*,github.com", "--allowed-origins", "localhost;localhost:*;127.0.0.1;127.0.0.1:*;github.com"], "mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"] }, + "safeinputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_INPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_INPUTS_API_KEY}" + } + }, "safeoutputs": { "type": "http", "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", @@ -574,6 +1171,19 @@ jobs: "entrypoint": "serena", "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + }, + "tavily": { + "type": "http", + "url": "https://mcp.tavily.com/mcp/", + "headers": { + "Authorization": "Bearer \${TAVILY_API_KEY}" + }, + "tools": [ + "*" + ], + "env": { + "TAVILY_API_KEY": "\${TAVILY_API_KEY}" + } } }, "gateway": { @@ -595,8 +1205,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", version: "", - agent_version: "0.0.402", - cli_version: "v0.41.1", + agent_version: "0.0.403", workflow_name: "Smoke Copilot", experimental: false, supports_tools_allowlist: true, @@ -610,10 +1219,10 @@ jobs: actor: context.actor, event_name: context.eventName, staged: false, - allowed_domains: ["defaults","node","github","playwright"], + allowed_domains: ["api.github.com","defaults","github","playwright"], firewall_enabled: true, - awf_version: "v0.13.1", - awmg_version: "v0.0.98", + awf_version: "v0.13.4", + awmg_version: "v0.0.99", steps: { firewall: "squid" }, @@ -704,7 +1313,172 @@ jobs: PROMPT_EOF cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" - {{#runtime-import workflows/smoke-copilot.md}} + ## MCP Response Size Limits + + MCP tool responses have a **25,000 token limit**. When GitHub API responses exceed this limit, workflows must retry with pagination parameters, wasting turns and tokens. + + ### Common Scenarios + + **Problem**: Fetching large result sets without pagination + - `list_pull_requests` with many PRs (75,897 tokens in one case) + - `pull_request_read` with large diff/comments (31,675 tokens observed) + - `search_issues`, `search_code` with many results + + **Solution**: Use proactive pagination to stay under token limits + + ### Pagination Best Practices + + #### 1. Use `perPage` Parameter + + Limit results per request to prevent oversized responses: + + ```bash + # Good: Fetch PRs in small batches + list_pull_requests --perPage 10 + + # Good: Get issue with limited comments + issue_read --method get_comments --perPage 20 + + # Bad: Default pagination may return too much data + list_pull_requests # May exceed 25k tokens + ``` + + #### 2. Common `perPage` Values + + - **10-20**: For detailed items (PRs with diffs, issues with comments) + - **50-100**: For simpler list operations (commits, branches, labels) + - **1-5**: For exploratory queries or schema discovery + + #### 3. Handle Pagination Loops + + When you need all results: + + ```bash + # Step 1: Fetch first page + result=$(list_pull_requests --perPage 20 --page 1) + + # Step 2: Check if more pages exist + # Most list operations return metadata about total count or next page + + # Step 3: Fetch subsequent pages if needed + result=$(list_pull_requests --perPage 20 --page 2) + ``` + + ### Tool-Specific Guidance + + #### Pull Requests + + ```bash + # Fetch recent PRs in small batches + list_pull_requests --state all --perPage 10 --sort updated --direction desc + + # Get PR details without full diff/comments + pull_request_read --method get --pullNumber 123 + + # Get PR files separately if needed + pull_request_read --method get_files --pullNumber 123 --perPage 30 + ``` + + #### Issues + + ```bash + # List issues with pagination + list_issues --perPage 20 --page 1 + + # Get issue comments in batches + issue_read --method get_comments --issue_number 123 --perPage 20 + ``` + + #### Code Search + + ```bash + # Search with limited results + search_code --query "function language:go" --perPage 10 + ``` + + ### Error Messages to Watch For + + If you see these errors, add pagination: + + - `MCP tool "list_pull_requests" response (75897 tokens) exceeds maximum allowed tokens (25000)` + - `MCP tool "pull_request_read" response (31675 tokens) exceeds maximum allowed tokens (25000)` + - `Response too large for tool [tool_name]` + + ### Performance Tips + + 1. **Start small**: Use `perPage: 10` initially, increase if needed + 2. **Fetch incrementally**: Get overview first, then details for specific items + 3. **Avoid wildcards**: Don't fetch all data when you need specific items + 4. **Use filters**: Combine `perPage` with state/label/date filters to reduce results + + ### Example Workflow Pattern + + ```markdown + # Analyze Recent Pull Requests + + 1. Fetch 10 most recent PRs (stay under token limit) + 2. For each PR, get summary without full diff + 3. If detailed analysis needed, fetch files for specific PR separately + 4. Process results incrementally rather than loading everything at once + ``` + + This proactive approach eliminates retry loops and reduces token consumption. + + **IMPORTANT**: Always use the `safeinputs-gh` tool for GitHub CLI commands instead of running `gh` directly via bash. The `safeinputs-gh` tool has proper authentication configured with `GITHUB_TOKEN`, while bash commands do not have GitHub CLI authentication by default. + + **Correct**: + ``` + Use the safeinputs-gh tool with args: "pr list --limit 5" + Use the safeinputs-gh tool with args: "issue view 123" + ``` + + **Incorrect**: + ``` + Use the gh safe-input tool with args: "pr list --limit 5" ❌ (Wrong tool name - use safeinputs-gh) + Run: gh pr list --limit 5 ❌ (No authentication in bash) + Execute bash: gh issue view 123 ❌ (No authentication in bash) + ``` + + + + + + ## Report Structure + + 1. **Overview**: 1-2 paragraphs summarizing key findings + 2. **Details**: Use `
Full Report` for expanded content + + ## Workflow Run References + + - Format run IDs as links: `[§12345](https://github.com/owner/repo/actions/runs/12345)` + - Include up to 3 most relevant run URLs at end under `**References:**` + - Do NOT add footer attribution (system adds automatically) + + + + **IMPORTANT**: Always use the `safeinputs-go` and `safeinputs-make` tools for Go and Make commands instead of running them directly via bash. These safe-input tools provide consistent execution and proper logging. + + **Correct**: + ``` + Use the safeinputs-go tool with args: "test ./..." + Use the safeinputs-make tool with args: "build" + Use the safeinputs-make tool with args: "lint" + Use the safeinputs-make tool with args: "test-unit" + ``` + + **Incorrect**: + ``` + Use the go safe-input tool with args: "test ./..." ❌ (Wrong tool name - use safeinputs-go) + Run: go test ./... ❌ (Use safeinputs-go instead) + Execute bash: make build ❌ (Use safeinputs-make instead) + ``` + + + + + PROMPT_EOF + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + {{#runtime-import .github/workflows/smoke-copilot.md}} PROMPT_EOF - name: Substitute placeholders uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 @@ -761,13 +1535,11 @@ jobs: - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): - timeout-minutes: 5 + timeout-minutes: 15 run: | set -o pipefail - GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS - mkdir -p "$HOME/.cache" - sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,codeload.github.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,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.13.1 --agent-image act \ - -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \ + sudo -E awf --enable-chroot --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains '*.githubusercontent.com,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,cdn.playwright.dev,codeload.github.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-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,mcp.tavily.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.13.4 --skip-pull --agent-image act \ + -- '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \ 2>&1 | tee /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE @@ -776,10 +1548,12 @@ jobs: GH_AW_MODEL_AGENT_COPILOT: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_DEBUG: 1 GITHUB_HEAD_REF: ${{ github.head_ref }} GITHUB_REF_NAME: ${{ github.ref_name }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_WORKSPACE: ${{ github.workspace }} + TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }} XDG_CONFIG_HOME: /home/runner - name: Copy Copilot session state files to logs if: always() @@ -817,11 +1591,12 @@ jobs: const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); await main(); env: - GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN,TAVILY_API_KEY' SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SECRET_TAVILY_API_KEY: ${{ secrets.TAVILY_API_KEY }} - name: Upload Safe Outputs if: always() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 @@ -834,7 +1609,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,codeload.github.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,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,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,cdn.playwright.dev,codeload.github.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-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -869,6 +1644,15 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); await main(); + - name: Parse safe-inputs logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_safe_inputs_logs.cjs'); + await main(); - name: Parse MCP gateway logs for step summary if: always() uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 @@ -904,8 +1688,10 @@ jobs: /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/aw_info.json /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/safe-inputs/logs/ /tmp/gh-aw/sandbox/firewall/logs/ /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ if-no-files-found: ignore conclusion: @@ -927,8 +1713,14 @@ jobs: tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} total_count: ${{ steps.missing_tool.outputs.total_count }} steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts - uses: github/gh-aw/actions/setup@d47697b2bf91080b55fb9c6905576c36e061011a # v0.41.1 + uses: ./actions/setup with: destination: /opt/gh-aw/actions - name: Debug job inputs @@ -1027,8 +1819,14 @@ jobs: outputs: success: ${{ steps.parse_results.outputs.success }} steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts - uses: github/gh-aw/actions/setup@d47697b2bf91080b55fb9c6905576c36e061011a # v0.41.1 + uses: ./actions/setup with: destination: /opt/gh-aw/actions - name: Download agent artifacts @@ -1052,7 +1850,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: WORKFLOW_NAME: "Smoke Copilot" - WORKFLOW_DESCRIPTION: "Smoke Copilot" + WORKFLOW_DESCRIPTION: "Smoke test workflow that validates Copilot engine functionality by reviewing recent PRs twice daily" HAS_PATCH: ${{ needs.agent.outputs.has_patch }} with: script: | @@ -1070,7 +1868,7 @@ jobs: env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.402 + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.403 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): @@ -1123,14 +1921,21 @@ jobs: ((github.event_name != 'pull_request') || ((github.event.action != 'labeled') || (github.event.label.name == 'smoke'))) runs-on: ubuntu-slim permissions: + contents: read discussions: write issues: write pull-requests: write outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts - uses: github/gh-aw/actions/setup@d47697b2bf91080b55fb9c6905576c36e061011a # v0.41.1 + uses: ./actions/setup with: destination: /opt/gh-aw/actions - name: Add eyes reaction for immediate feedback @@ -1182,8 +1987,14 @@ jobs: 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: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts - uses: github/gh-aw/actions/setup@d47697b2bf91080b55fb9c6905576c36e061011a # v0.41.1 + uses: ./actions/setup with: destination: /opt/gh-aw/actions - name: Download agent output artifact @@ -1202,7 +2013,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"add_labels\":{\"allowed\":[\"smoke-copilot\"]},\"create_issue\":{\"expires\":2,\"max\":1},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"add_labels\":{\"allowed\":[\"smoke-copilot\"]},\"create_issue\":{\"close_older_issues\":true,\"expires\":2,\"group\":true,\"max\":1},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1217,10 +2028,17 @@ jobs: - detection if: always() && needs.detection.outputs.success == 'true' runs-on: ubuntu-latest - permissions: {} + permissions: + contents: read steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false - name: Setup Scripts - uses: github/gh-aw/actions/setup@d47697b2bf91080b55fb9c6905576c36e061011a # v0.41.1 + uses: ./actions/setup with: destination: /opt/gh-aw/actions - name: Download cache-memory artifact (default) diff --git a/.github/workflows/smoke-copilot.md b/.github/workflows/smoke-copilot.md index 94b79bfc..5786d781 100644 --- a/.github/workflows/smoke-copilot.md +++ b/.github/workflows/smoke-copilot.md @@ -1,5 +1,5 @@ --- -description: Smoke Copilot +description: Smoke test workflow that validates Copilot engine functionality by reviewing recent PRs twice daily on: schedule: every 12h workflow_dispatch: @@ -9,39 +9,56 @@ on: reaction: "eyes" permissions: contents: read - pull-requests: read issues: read + pull-requests: read + discussions: read actions: read + name: Smoke Copilot -engine: copilot +engine: + id: copilot +strict: true +imports: + - shared/mcp-pagination.md + - shared/gh.md + - shared/mcp/tavily.md + - shared/reporting.md + - shared/github-queries-safe-input.md + - shared/go-make.md + - shared/github-mcp-app.md network: allowed: - defaults - - node - github - playwright tools: agentic-workflows: cache-memory: true - edit: - bash: - - "*" github: + toolsets: [repos, pull_requests] playwright: allowed_domains: - github.com + edit: + bash: + - "*" serena: languages: go: {} - web-fetch: +runtimes: + go: + version: "1.25" sandbox: mcp: container: "ghcr.io/github/gh-aw-mcpg" safe-outputs: add-comment: hide-older-comments: true + max: 2 create-issue: expires: 2h + group: true + close-older-issues: true add-labels: allowed: [smoke-copilot] messages: @@ -49,28 +66,50 @@ safe-outputs: run-started: "📰 BREAKING: [{workflow_name}]({run_url}) is now investigating this {event_type}. Sources say the story is developing..." run-success: "📰 VERDICT: [{workflow_name}]({run_url}) has concluded. All systems operational. This is a developing story. 🎤" run-failure: "📰 DEVELOPING STORY: [{workflow_name}]({run_url}) reports {status}. Our correspondents are investigating the incident..." -timeout-minutes: 5 -strict: true +timeout-minutes: 15 --- -# Smoke Test: Copilot Engine Validation +# Smoke Test: Copilot Engine Validation. **IMPORTANT: Keep all outputs extremely short and concise. Use single-line responses where possible. No verbose explanations.** ## Test Requirements 1. **GitHub MCP Testing**: Review the last 2 merged pull requests in ${{ github.repository }} -2. **Serena MCP Testing**: Use the Serena MCP server tool `activate_project` to initialize the workspace and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools) -3. **Playwright Testing**: Use playwright to navigate to and verify the page title contains "GitHub" -4. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-copilot-${{ github.run_id }}.txt` with content "Smoke test passed for Copilot at $(date)" (create the directory if it doesn't exist) -5. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back) +2. **Safe Inputs GH CLI Testing**: Use the `safeinputs-gh` tool to query 2 pull requests from ${{ github.repository }} (use args: "pr list --repo ${{ github.repository }} --limit 2 --json number,title,author") +3. **Serena MCP Testing**: + - Use the Serena MCP server tool `activate_project` to initialize the workspace at `${{ github.workspace }}` and verify it succeeds (do NOT use bash to run go commands - use Serena's MCP tools or the safeinputs-go/safeinputs-make tools from the go-make shared workflow) + - After initialization, use the `find_symbol` tool to search for symbols (find which tool to call) and verify that at least 3 symbols are found in the results +4. **Make Build Testing**: Use the `safeinputs-make` tool to build the project (use args: "build") and verify it succeeds +5. **Playwright Testing**: Use the playwright tools to navigate to https://github.com and verify the page title contains "GitHub" (do NOT try to install playwright - use the provided MCP tools) +6. **Tavily Web Search Testing**: Use the Tavily MCP server to perform a web search for "GitHub Agentic Workflows" and verify that results are returned with at least one item +7. **File Writing Testing**: Create a test file `/tmp/gh-aw/agent/smoke-test-copilot-${{ github.run_id }}.txt` with content "Smoke test passed for Copilot at $(date)" (create the directory if it doesn't exist) +8. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back) +9. **Discussion Interaction Testing**: + - Use the `github-discussion-query` safe-input tool with params: `limit=1, jq=".[0]"` to get the latest discussion from ${{ github.repository }} + - Extract the discussion number from the result (e.g., if the result is `{"number": 123, "title": "...", ...}`, extract 123) + - Use the `add_comment` tool with `discussion_number: ` to add a fun, news-reporter style comment stating that the smoke test agent was here +10. **Agentic Workflows MCP Testing**: + - Use the `agentic-workflows` MCP tool with the `status` method to query the status of the "smoke-copilot" workflow in ${{ github.repository }} + - Extract key information: total runs, recent success/failure status, last run time + - Write a summary of the smoke-copilot workflow status to `/tmp/gh-aw/agent/smoke-copilot-status-${{ github.run_id }}.txt` + - Use bash to display the file contents ## Output -Add a **very brief** comment (max 5-10 lines) to the current pull request with: -- PR titles only (no descriptions) -- ✅ or ❌ for each test result -- Overall status: PASS or FAIL -- Mention the pull request author and any assignees +1. **Create an issue** with a summary of the smoke test run: + - Title: "Smoke Test: Copilot - ${{ github.run_id }}" + - Body should include: + - Test results (✅ or ❌ for each test) + - Overall status: PASS or FAIL + - Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + - Timestamp + +2. **Only if this workflow was triggered by a pull_request event**: Use the `add_comment` tool to add a **very brief** comment (max 5-10 lines) to the triggering pull request (omit the `item_number` parameter to auto-target the triggering PR) with: + - PR titles only (no descriptions) + - ✅ or ❌ for each test result + - Overall status: PASS or FAIL + +3. Use the `add_comment` tool with `item_number` set to the discussion number you extracted in step 9 to add a **fun news-reporter style comment** to that discussion - be playful and use reporter language like "📰 BREAKING NEWS!" -If all tests pass, add the label `smoke-copilot` to the pull request. \ No newline at end of file +If all tests pass, use the `add_labels` tool to add the label `smoke-copilot` to the pull request (omit the `item_number` parameter to auto-target the triggering PR if this workflow was triggered by a pull_request event). \ No newline at end of file