From 1cb841ba5244045940ed096df6c1196f9c36c466 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 May 2026 19:46:25 +0000 Subject: [PATCH 1/2] Migrate dispatcher agent path and cleanup legacy location Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .agents/agents/agentic-workflows.md | 196 ++++++++++++++++++++++++++++ pkg/cli/agent_download.go | 117 ++++++++++------- pkg/cli/copilot_agents.go | 10 +- pkg/cli/copilot_agents_test.go | 8 ++ pkg/cli/init_command.go | 2 +- 5 files changed, 280 insertions(+), 53 deletions(-) create mode 100644 .agents/agents/agentic-workflows.md diff --git a/.agents/agents/agentic-workflows.md b/.agents/agents/agentic-workflows.md new file mode 100644 index 00000000000..bcedfcc414d --- /dev/null +++ b/.agents/agents/agentic-workflows.md @@ -0,0 +1,196 @@ +--- +description: GitHub Agentic Workflows (gh-aw) - Create, debug, and upgrade AI-powered workflows with intelligent prompt routing +disable-model-invocation: true +--- + +# GitHub Agentic Workflows Agent + +This agent helps you work with **GitHub Agentic Workflows (gh-aw)**, a CLI extension for creating AI-powered workflows in natural language using markdown files. + +## What This Agent Does + +This is a **dispatcher agent** that routes your request to the appropriate specialized prompt based on your task: + +- **Creating new workflows**: Routes to `create` prompt +- **Updating existing workflows**: Routes to `update` prompt +- **Debugging workflows**: Routes to `debug` prompt +- **Upgrading workflows**: Routes to `upgrade-agentic-workflows` prompt +- **Creating report-generating workflows**: Routes to `report` prompt — consult this whenever the workflow posts status updates, audits, analyses, or any structured output as issues, discussions, or comments +- **Creating shared components**: Routes to `create-shared-agentic-workflow` prompt +- **Fixing Dependabot PRs**: Routes to `dependabot` prompt — use this when Dependabot opens PRs that modify generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`). Never merge those PRs directly; instead update the source `.md` files and rerun `gh aw compile --dependabot` to bundle all fixes +- **Analyzing test coverage**: Routes to `test-coverage` prompt — consult this whenever the workflow reads, analyzes, or reports on test coverage data from PRs or CI runs +- **CLI commands and triggering workflows**: Routes to `cli-commands` guide — consult this whenever the user asks how to run, compile, debug, or manage workflows from the command line, or when they need the MCP tool equivalent of a `gh aw` command + +Workflows may optionally include: + +- **Project tracking / monitoring** (GitHub Projects updates, status reporting) +- **Orchestration / coordination** (one workflow assigning agents or dispatching and coordinating other workflows) + +## Files This Applies To + +- Workflow files: `.github/workflows/*.md` and `.github/workflows/**/*.md` +- Workflow lock files: `.github/workflows/*.lock.yml` +- Shared components: `.github/workflows/shared/*.md` +- Configuration: https://github.com/github/gh-aw/blob/main/.github/aw/github-agentic-workflows.md + +## Problems This Solves + +- **Workflow Creation**: Design secure, validated agentic workflows with proper triggers, tools, and permissions +- **Workflow Debugging**: Analyze logs, identify missing tools, investigate failures, and fix configuration issues +- **Version Upgrades**: Migrate workflows to new gh-aw versions, apply codemods, fix breaking changes +- **Component Design**: Create reusable shared workflow components that wrap MCP servers + +## How to Use + +When you interact with this agent, it will: + +1. **Understand your intent** - Determine what kind of task you're trying to accomplish +2. **Route to the right prompt** - Load the specialized prompt file for your task +3. **Execute the task** - Follow the detailed instructions in the loaded prompt + +## Available Prompts + +### Create New Workflow +**Load when**: User wants to create a new workflow from scratch, add automation, or design a workflow that doesn't exist yet + +**Prompt file**: https://github.com/github/gh-aw/blob/main/.github/aw/create-agentic-workflow.md + +**Use cases**: +- "Create a workflow that triages issues" +- "I need a workflow to label pull requests" +- "Design a weekly research automation" + +### Update Existing Workflow +**Load when**: User wants to modify, improve, or refactor an existing workflow + +**Prompt file**: https://github.com/github/gh-aw/blob/main/.github/aw/update-agentic-workflow.md + +**Use cases**: +- "Add web-fetch tool to the issue-classifier workflow" +- "Update the PR reviewer to use discussions instead of issues" +- "Improve the prompt for the weekly-research workflow" + +### Debug Workflow +**Load when**: User needs to investigate, audit, debug, or understand a workflow, troubleshoot issues, analyze logs, or fix errors + +**Prompt file**: https://github.com/github/gh-aw/blob/main/.github/aw/debug-agentic-workflow.md + +**Use cases**: +- "Why is this workflow failing?" +- "Analyze the logs for workflow X" +- "Investigate missing tool calls in run #12345" + +### Upgrade Agentic Workflows +**Load when**: User wants to upgrade workflows to a new gh-aw version or fix deprecations + +**Prompt file**: https://github.com/github/gh-aw/blob/main/.github/aw/upgrade-agentic-workflows.md + +**Use cases**: +- "Upgrade all workflows to the latest version" +- "Fix deprecated fields in workflows" +- "Apply breaking changes from the new release" + +### Create a Report-Generating Workflow +**Load when**: The workflow being created or updated produces reports — recurring status updates, audit summaries, analyses, or any structured output posted as a GitHub issue, discussion, or comment + +**Prompt file**: https://github.com/github/gh-aw/blob/main/.github/aw/report.md + +**Use cases**: +- "Create a weekly CI health report" +- "Post a daily security audit to Discussions" +- "Add a status update comment to open PRs" + +### Create Shared Agentic Workflow +**Load when**: User wants to create a reusable workflow component or wrap an MCP server + +**Prompt file**: https://github.com/github/gh-aw/blob/main/.github/aw/create-shared-agentic-workflow.md + +**Use cases**: +- "Create a shared component for Notion integration" +- "Wrap the Slack MCP server as a reusable component" +- "Design a shared workflow for database queries" + +### Fix Dependabot PRs +**Load when**: User needs to close or fix open Dependabot PRs that update dependencies in generated manifest files (`.github/workflows/package.json`, `.github/workflows/requirements.txt`, `.github/workflows/go.mod`) + +**Prompt file**: https://github.com/github/gh-aw/blob/main/.github/aw/dependabot.md + +**Use cases**: +- "Fix the open Dependabot PRs for npm dependencies" +- "Bundle and close the Dependabot PRs for workflow dependencies" +- "Update @playwright/test to fix the Dependabot PR" + +### Analyze Test Coverage +**Load when**: The workflow reads, analyzes, or reports test coverage — whether triggered by a PR, a schedule, or a slash command. Always consult this prompt before designing the coverage data strategy. + +**Prompt file**: https://github.com/github/gh-aw/blob/main/.github/aw/test-coverage.md + +**Use cases**: +- "Create a workflow that comments coverage on PRs" +- "Analyze coverage trends over time" +- "Add a coverage gate that blocks PRs below a threshold" + +### CLI Commands Reference +**Load when**: The user asks how to run, compile, debug, or manage workflows from the command line; needs the MCP tool equivalent of a `gh aw` command; or is in a restricted environment (e.g., Copilot Cloud) without direct CLI access. + +**Reference file**: https://github.com/github/gh-aw/blob/main/.github/aw/cli-commands.md + +**Use cases**: +- "How do I trigger workflow X on the main branch?" +- "What's the MCP equivalent of `gh aw logs`?" +- "I'm in Copilot Cloud — how do I compile a workflow?" +- "Show me all available gh aw commands" + +## Instructions + +When a user interacts with you: + +1. **Identify the task type** from the user's request +2. **Load the appropriate prompt** from the GitHub repository URLs listed above +3. **Follow the loaded prompt's instructions** exactly +4. **If uncertain**, ask clarifying questions to determine the right prompt + +## Quick Reference + +```bash +# Initialize repository for agentic workflows +gh aw init + +# Generate the lock file for a workflow +gh aw compile [workflow-name] + +# Trigger a workflow on demand (preferred over gh workflow run) +gh aw run # interactive input collection +gh aw run --ref main # run on a specific branch + +# Debug workflow runs +gh aw logs [workflow-name] +gh aw audit + +# Upgrade workflows +gh aw fix --write +gh aw compile --validate +``` + +## Key Features of gh-aw + +- **Natural Language Workflows**: Write workflows in markdown with YAML frontmatter +- **AI Engine Support**: Copilot, Claude, Codex, or custom engines +- **MCP Server Integration**: Connect to Model Context Protocol servers for tools +- **Safe Outputs**: Structured communication between AI and GitHub API +- **Strict Mode**: Security-first validation and sandboxing +- **Shared Components**: Reusable workflow building blocks +- **Repo Memory**: Persistent git-backed storage for agents +- **Sandboxed Execution**: All workflows run in the Agent Workflow Firewall (AWF) sandbox, enabling full `bash` and `edit` tools by default + +## Important Notes + +- Always reference the instructions file at https://github.com/github/gh-aw/blob/main/.github/aw/github-agentic-workflows.md for complete documentation +- Use the MCP tool `agentic-workflows` when running in GitHub Copilot Cloud +- Workflows must be compiled to `.lock.yml` files before running in GitHub Actions +- **Bash tools are enabled by default** - Don't restrict bash commands unnecessarily since workflows are sandboxed by the AWF +- Follow security best practices: minimal permissions, explicit network access, no template injection +- **Network configuration**: Use ecosystem identifiers (`node`, `python`, `go`, etc.) or explicit FQDNs in `network.allowed`. Bare shorthands like `npm` or `pypi` are **not** valid. See https://github.com/github/gh-aw/blob/main/.github/aw/network.md for the full list of valid ecosystem identifiers and domain patterns. +- **Single-file output**: When creating a workflow, produce exactly **one** workflow `.md` file. Do not create separate documentation files (architecture docs, runbooks, usage guides, etc.). If documentation is needed, add a brief `## Usage` section inside the workflow file itself. +- **Triggering runs**: Always use `gh aw run ` to trigger a workflow on demand — not `gh workflow run .lock.yml`. `gh aw run` handles workflow resolution by short name, input parsing and validation, and correct run-tracking for agentic workflows. Use `--ref ` to run on a specific branch. +- **CLI commands reference**: For a complete guide on all `gh aw` commands and their MCP tool equivalents (for restricted environments), see https://github.com/github/gh-aw/blob/main/.github/aw/cli-commands.md diff --git a/pkg/cli/agent_download.go b/pkg/cli/agent_download.go index d0a9e291cfc..7365e007280 100644 --- a/pkg/cli/agent_download.go +++ b/pkg/cli/agent_download.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "fmt" "io" "net/http" @@ -17,9 +18,9 @@ import ( var agentDownloadLog = logger.New("cli:agent_download") -// downloadAgentFileFromGitHub downloads the agentic-workflows.agent.md file from GitHub +// downloadAgentFileFromGitHub downloads the agentic-workflows dispatcher file from GitHub func downloadAgentFileFromGitHub(verbose bool) (string, error) { - agentDownloadLog.Print("Downloading agentic-workflows.agent.md from GitHub") + agentDownloadLog.Print("Downloading agentic-workflows dispatcher from GitHub") // Determine the ref to use (tag for releases, main for dev builds) ref := "main" @@ -36,57 +37,74 @@ func downloadAgentFileFromGitHub(verbose bool) (string, error) { } } - // Construct the raw GitHub URL - rawURL := fmt.Sprintf("https://raw.githubusercontent.com/github/gh-aw/%s/.github/agents/agentic-workflows.agent.md", ref) - agentDownloadLog.Printf("Downloading from URL: %s", rawURL) + agentPaths := []string{ + ".agents/agents/agentic-workflows.md", + ".github/agents/agentic-workflows.agent.md", + } // Create HTTP client with timeout client := &http.Client{ Timeout: 30 * time.Second, } - // Download the file - resp, err := client.Get(rawURL) - if err != nil { - return "", fmt.Errorf("failed to download agent file: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - // Fall back to gh CLI for authenticated access (e.g., private repos in codespaces) - if resp.StatusCode == http.StatusNotFound && isGHCLIAvailable() { - agentDownloadLog.Print("Unauthenticated download returned 404, trying gh CLI for authenticated access") - if verbose { - fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Retrying download with gh CLI authentication...")) - } - if content, ghErr := downloadAgentFileViaGHCLI(ref); ghErr == nil { - patchedContent := patchAgentFileURLs(content, ref) - agentDownloadLog.Printf("Successfully downloaded agent file via gh CLI (%d bytes)", len(patchedContent)) - return patchedContent, nil - } else { - agentDownloadLog.Printf("gh CLI fallback failed: %v", ghErr) - } + for _, agentPath := range agentPaths { + // Construct the raw GitHub URL + rawURL := fmt.Sprintf("https://raw.githubusercontent.com/github/gh-aw/%s/%s", ref, agentPath) + agentDownloadLog.Printf("Downloading from URL: %s", rawURL) + + // Download the file + resp, err := client.Get(rawURL) + if err != nil { + return "", fmt.Errorf("failed to download agent file: %w", err) } - _, _ = io.Copy(io.Discard, resp.Body) - return "", fmt.Errorf("failed to download agent file: HTTP %d", resp.StatusCode) - } - // Read the content - content, err := io.ReadAll(resp.Body) - if err != nil { - return "", fmt.Errorf("failed to read agent file content: %w", err) - } + if resp.StatusCode == http.StatusNotFound { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + continue + } + + if resp.StatusCode != http.StatusOK { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + return "", fmt.Errorf("failed to download agent file: HTTP %d", resp.StatusCode) + } + + // Read the content + content, err := io.ReadAll(resp.Body) + _ = resp.Body.Close() + if err != nil { + return "", fmt.Errorf("failed to read agent file content: %w", err) + } - contentStr := string(content) + contentStr := string(content) - // Patch URLs to match the current version/ref - patchedContent := patchAgentFileURLs(contentStr, ref) - if patchedContent != contentStr && verbose { - fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Patched URLs to use ref: "+ref)) + // Patch URLs to match the current version/ref + patchedContent := patchAgentFileURLs(contentStr, ref) + if patchedContent != contentStr && verbose { + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Patched URLs to use ref: "+ref)) + } + + agentDownloadLog.Printf("Successfully downloaded agent file (%d bytes)", len(patchedContent)) + return patchedContent, nil } - agentDownloadLog.Printf("Successfully downloaded agent file (%d bytes)", len(patchedContent)) - return patchedContent, nil + // Fall back to gh CLI for authenticated access (e.g., private repos in codespaces) + if isGHCLIAvailable() { + agentDownloadLog.Print("Unauthenticated download returned 404, trying gh CLI for authenticated access") + if verbose { + fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Retrying download with gh CLI authentication...")) + } + if content, ghErr := downloadAgentFileViaGHCLI(ref, agentPaths); ghErr == nil { + patchedContent := patchAgentFileURLs(content, ref) + agentDownloadLog.Printf("Successfully downloaded agent file via gh CLI (%d bytes)", len(patchedContent)) + return patchedContent, nil + } else { + agentDownloadLog.Printf("gh CLI fallback failed: %v", ghErr) + } + } + + return "", fmt.Errorf("failed to download agent file: HTTP %d", http.StatusNotFound) } // patchAgentFileURLs patches URLs in the agent file to use the correct ref @@ -107,14 +125,17 @@ func patchAgentFileURLs(content, ref string) string { // downloadAgentFileViaGHCLI downloads the agent file using the gh CLI with authentication. // This is used as a fallback when the unauthenticated raw.githubusercontent.com download fails // (e.g., for private repositories accessed from codespaces). -func downloadAgentFileViaGHCLI(ref string) (string, error) { - output, err := workflow.RunGH("Downloading agent file...", "api", - "/repos/github/gh-aw/contents/.github/agents/agentic-workflows.agent.md?ref="+url.QueryEscape(ref), - "--header", "Accept: application/vnd.github.raw") - if err != nil { - return "", fmt.Errorf("gh api download failed: %w", err) +func downloadAgentFileViaGHCLI(ref string, agentPaths []string) (string, error) { + for _, agentPath := range agentPaths { + output, err := workflow.RunGH("Downloading agent file...", "api", + "/repos/github/gh-aw/contents/"+agentPath+"?ref="+url.QueryEscape(ref), + "--header", "Accept: application/vnd.github.raw") + if err == nil { + return string(output), nil + } } - return string(output), nil + + return "", errors.New("gh api download failed for all known dispatcher paths") } func isGHCLIAvailable() bool { diff --git a/pkg/cli/copilot_agents.go b/pkg/cli/copilot_agents.go index 5a6c278c7ce..e80114c9a92 100644 --- a/pkg/cli/copilot_agents.go +++ b/pkg/cli/copilot_agents.go @@ -13,7 +13,7 @@ import ( var copilotAgentsLog = logger.New("cli:copilot_agents") -// ensureAgenticWorkflowsDispatcher ensures that .github/agents/agentic-workflows.agent.md contains the dispatcher agent +// ensureAgenticWorkflowsDispatcher ensures that .agents/agents/agentic-workflows.md contains the dispatcher agent func ensureAgenticWorkflowsDispatcher(verbose bool, skipInstructions bool) error { copilotAgentsLog.Print("Ensuring agentic workflows dispatcher agent") @@ -27,12 +27,12 @@ func ensureAgenticWorkflowsDispatcher(verbose bool, skipInstructions bool) error return err // Not in a git repository, skip } - targetDir := filepath.Join(gitRoot, ".github", "agents") - targetPath := filepath.Join(targetDir, "agentic-workflows.agent.md") + targetDir := filepath.Join(gitRoot, ".agents", "agents") + targetPath := filepath.Join(targetDir, "agentic-workflows.md") // Ensure the target directory exists if err := os.MkdirAll(targetDir, 0755); err != nil { - return fmt.Errorf("failed to create .github/agents directory: %w", err) + return fmt.Errorf("failed to create .agents/agents directory: %w", err) } // Download the agent file from GitHub @@ -193,6 +193,8 @@ func deleteOldAgentFiles(verbose bool) error { // Map of subdirectory to list of files to delete filesToDelete := map[string][]string{ "agents": { + "agentic-workflows.agent.md", + "agentic-workflows.agents.md", "create-agentic-workflow.agent.md", "debug-agentic-workflow.agent.md", "create-shared-agentic-workflow.agent.md", diff --git a/pkg/cli/copilot_agents_test.go b/pkg/cli/copilot_agents_test.go index 0d97c7b518b..3084ef1d967 100644 --- a/pkg/cli/copilot_agents_test.go +++ b/pkg/cli/copilot_agents_test.go @@ -21,11 +21,15 @@ func TestDeleteOldAgentFiles(t *testing.T) { { name: "deletes old agent files from .github/agents", filesToCreate: []string{ + ".github/agents/agentic-workflows.agent.md", + ".github/agents/agentic-workflows.agents.md", ".github/agents/create-agentic-workflow.agent.md", ".github/agents/debug-agentic-workflow.agent.md", ".github/agents/create-shared-agentic-workflow.agent.md", }, expectedDeleted: []string{ + ".github/agents/agentic-workflows.agent.md", + ".github/agents/agentic-workflows.agents.md", ".github/agents/create-agentic-workflow.agent.md", ".github/agents/debug-agentic-workflow.agent.md", ".github/agents/create-shared-agentic-workflow.agent.md", @@ -71,6 +75,8 @@ func TestDeleteOldAgentFiles(t *testing.T) { { name: "deletes all old agent files together", filesToCreate: []string{ + ".github/agents/agentic-workflows.agent.md", + ".github/agents/agentic-workflows.agents.md", ".github/agents/create-agentic-workflow.agent.md", ".github/agents/debug-agentic-workflow.agent.md", ".github/agents/create-shared-agentic-workflow.agent.md", @@ -82,6 +88,8 @@ func TestDeleteOldAgentFiles(t *testing.T) { ".github/aw/upgrade-agentic-workflow.md", }, expectedDeleted: []string{ + ".github/agents/agentic-workflows.agent.md", + ".github/agents/agentic-workflows.agents.md", ".github/agents/create-agentic-workflow.agent.md", ".github/agents/debug-agentic-workflow.agent.md", ".github/agents/create-shared-agentic-workflow.agent.md", diff --git a/pkg/cli/init_command.go b/pkg/cli/init_command.go index 75422db0da3..8756ad363f1 100644 --- a/pkg/cli/init_command.go +++ b/pkg/cli/init_command.go @@ -22,7 +22,7 @@ engine selection or secret configuration. This command: - Configures .gitattributes to mark .lock.yml files as generated -- Creates the dispatcher agent at .github/agents/agentic-workflows.agent.md +- Creates the dispatcher agent at .agents/agents/agentic-workflows.md - Removes old prompt files from .github/prompts/ if they exist - Configures VSCode settings (.vscode/settings.json) - Generates/updates .github/workflows/agentics-maintenance.yml if any workflows use expires field for discussions or issues From 7e078bfeb7feae91ce749007d19b6a8df84034c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 10 May 2026 19:51:00 +0000 Subject: [PATCH 2/2] Refine dispatcher download fallback handling Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/agent_download.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pkg/cli/agent_download.go b/pkg/cli/agent_download.go index 7365e007280..a88c9c4720b 100644 --- a/pkg/cli/agent_download.go +++ b/pkg/cli/agent_download.go @@ -59,14 +59,12 @@ func downloadAgentFileFromGitHub(verbose bool) (string, error) { } if resp.StatusCode == http.StatusNotFound { - _, _ = io.Copy(io.Discard, resp.Body) - _ = resp.Body.Close() + discardAndClose(resp) continue } if resp.StatusCode != http.StatusOK { - _, _ = io.Copy(io.Discard, resp.Body) - _ = resp.Body.Close() + discardAndClose(resp) return "", fmt.Errorf("failed to download agent file: HTTP %d", resp.StatusCode) } @@ -95,16 +93,16 @@ func downloadAgentFileFromGitHub(verbose bool) (string, error) { if verbose { fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Retrying download with gh CLI authentication...")) } - if content, ghErr := downloadAgentFileViaGHCLI(ref, agentPaths); ghErr == nil { - patchedContent := patchAgentFileURLs(content, ref) + if ghContent, err := downloadAgentFileViaGHCLI(ref, agentPaths); err == nil { + patchedContent := patchAgentFileURLs(ghContent, ref) agentDownloadLog.Printf("Successfully downloaded agent file via gh CLI (%d bytes)", len(patchedContent)) return patchedContent, nil } else { - agentDownloadLog.Printf("gh CLI fallback failed: %v", ghErr) + agentDownloadLog.Printf("gh CLI fallback failed: %v", err) } } - return "", fmt.Errorf("failed to download agent file: HTTP %d", http.StatusNotFound) + return "", errors.New("failed to download agent file from all known dispatcher paths") } // patchAgentFileURLs patches URLs in the agent file to use the correct ref @@ -142,3 +140,12 @@ func isGHCLIAvailable() bool { cmd := exec.Command("gh", "--version") return cmd.Run() == nil } + +// discardAndClose drains and closes an HTTP response body safely, including nil responses. +func discardAndClose(resp *http.Response) { + if resp == nil || resp.Body == nil { + return + } + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() +}