GitHub Agentic Workflows

Imports

Use imports: in frontmatter or {{#import ...}} in markdown to share workflow components across multiple workflows.

---
on: issues
engine: copilot
imports:
- shared/common-tools.md
- shared/mcp/tavily.md
---
# Your Workflow
Workflow instructions here...

Shared workflows that declare an import-schema accept runtime parameters. Use the uses/with form to pass values:

---
on: issues
engine: copilot
imports:
- uses: shared/mcp/serena.md
with:
languages: ["go", "typescript"]
---

uses is an alias for path; with is an alias for inputs.

A workflow file can appear at most once in an import graph. If the same file is imported more than once with identical with values it is silently deduplicated. Importing the same file with different with values is a compile-time error:

import conflict: 'shared/mcp/serena.md' is imported more than once with different 'with' values.
An imported workflow can only be imported once per workflow.
Previous 'with': {"languages":["go"]}
New 'with': {"languages":["typescript"]}

In markdown, use the special {{#import ...}} directive:

---
...
---
# Your Workflow
Workflow instructions here...
{{#import shared/common-tools.md}}

Files without an on field are shared workflow components — validated but not compiled into GitHub Actions, only imported by other workflows. The compiler skips them with an informative message.

Use import-schema to declare a typed parameter contract. Callers pass values via with; the compiler validates them and substitutes them into the shared file’s frontmatter and body before processing.

---
# shared/deploy.md — no 'on:' field, shared component only
import-schema:
region:
type: string
required: true
environment:
type: choice
options: [staging, production]
required: true
count:
type: number
default: 10
languages:
type: array
items:
type: string
required: true
config:
type: object
description: Configuration object
properties:
apiKey:
type: string
required: true
timeout:
type: number
default: 30
mcp-servers:
my-server:
url: "https://example.com/mcp"
allowed: ["*"]
---
Deploy ${{ github.aw.import-inputs.count }} items to ${{ github.aw.import-inputs.region }}.
API key: ${{ github.aw.import-inputs.config.apiKey }}.
Languages: ${{ github.aw.import-inputs.languages }}.
TypeDescriptionExtra fields
stringPlain text value
numberNumeric value
booleantrue/false
choiceOne of a fixed set of stringsoptions: [...]
arrayOrdered list of valuesitems.type (element type)
objectKey/value mapproperties (one level deep)

Each field supports required: true and an optional default value.

Use ${{ github.aw.import-inputs.<key> }} to substitute a top-level value; use dotted notation for object sub-fields (e.g. ${{ github.aw.import-inputs.config.apiKey }}). Substitution applies to both frontmatter and body, so inputs can drive any field such as mcp-servers or runtimes.

---
on: issues
engine: copilot
imports:
- uses: shared/deploy.md
with:
region: us-east-1
environment: staging
count: 5
languages: ["go", "typescript"]
config:
apiKey: my-secret-key
timeout: 60
---

The compiler validates required fields, choice options, array element types, and object properties. Unknown keys are compile-time errors.

Import paths are resolved using one of three modes depending on their format.

Paths that do not start with .github/, /, or an owner/repo/ prefix are resolved relative to the importing workflow’s directory. When compiling with the default --dir value, that directory is .github/workflows/.

---
on: issues
engine: copilot
imports:
- shared/common-tools.md # → .github/workflows/shared/common-tools.md
- ../agents/helper.md # → .github/agents/helper.md (.. goes up from .github/workflows/)
---

Paths starting with .github/ or / are resolved from the repository root. Absolute paths (/) must point inside .github/ or .agents/; any other prefix is rejected at compile time for security.

---
on: pull_request
engine: copilot
imports:
- .github/agents/code-reviewer.md # resolved from repo root
- .github/workflows/shared/app.md # resolved from repo root
---

This form is required when workflows in different directories need to import the same shared file using a stable path, and is the supported way to import files from the .github/agents/ directory.

Paths matching the owner/repo/path@ref format are fetched from GitHub at compile time and cached locally. The @ref suffix pins the import to a tag, branch, or commit SHA.

---
on: issues
engine: copilot
imports:
- acme-org/shared-workflows/shared/reporting.md@v2.1.0 # pinned to a tag
- acme-org/shared-workflows/shared/tools.md@main # track a branch
- acme-org/shared-workflows/shared/helpers.md@abc1234 # locked to a SHA
---

Remote imports are cached in .github/aw/imports/ by commit SHA, enabling offline compilation. See Remote Repository Imports for details.

---
on: issues
engine: copilot
imports:
# 1. Relative path – resolved relative to .github/workflows/
- shared/mcp/tavily.md
# 2. Repo-root-relative – resolved from the repository root
- .github/agents/my-expert-agent.md
# 3. Cross-repo – fetched from GitHub at compile time
- acme-org/shared-workflows/shared/reporting.md@v1.0.0
---
# My Workflow
Use the imported tools, agent, and reporting configuration.

Append #SectionName to any path to import a single section from a markdown file:

imports:
- shared/tools.md#WebSearch

Use the {{#import? ...}} syntax to mark an import as optional, which skips missing files silently instead of failing compilation.

Import shared components from external repositories using the owner/repo/path@ref format:

---
on: issues
engine: copilot
imports:
- acme-org/shared-workflows/mcp/tavily.md@v1.0.0
- acme-org/shared-workflows/tools/github-setup.md@main
---
# Issue Triage Workflow
Analyze incoming issues using imported tools and configurations.

Supported refs: semantic tags (@v1.0.0), branches (@main), or commit SHAs. See Reusing Workflows for installation and update workflows.

Remote imports are cached in .github/aw/imports/ by commit SHA, enabling offline compilation. The cache is git-tracked with .gitattributes for conflict-free merges. Local imports are never cached.

Agent files are markdown documents in .github/agents/ that add specialized instructions to the AI engine. Import them from your repository or from external repositories.

Import agent files from your repository’s .github/agents/ directory:

---
on: pull_request
engine: copilot
imports:
- .github/agents/code-reviewer.md
---

Import agent files from external repositories using the owner/repo/path@ref format:

---
on: pull_request
engine: copilot
imports:
- githubnext/shared-agents/.github/agents/security-reviewer.md@v1.0.0
---
# PR Security Review
Analyze pull requests for security vulnerabilities using the shared security reviewer agent.

Remote agent imports support the same @ref versioning syntax as other remote imports.

  • One agent per workflow: Only one agent file can be imported per workflow (local or remote)
  • Agent path detection: Files in .github/agents/ directories are automatically recognized as agent files
  • Caching: Remote agents are cached in .github/aw/imports/ by commit SHA, enabling offline compilation

Shared workflow files (without on: field) can define:

  • import-schema: - Parameter schema for with validation and input substitution
  • tools: - Tool configurations (bash, web-fetch, github, mcp-*, etc.)
  • mcp-servers: - Model Context Protocol server configurations
  • services: - Docker services for workflow execution
  • safe-outputs: - Safe output handlers and configuration
  • mcp-scripts: - MCP Scripts configurations
  • network: - Network permission specifications
  • permissions: - GitHub Actions permissions (validated, not merged)
  • runtimes: - Runtime version overrides (node, python, go, etc.)
  • secret-masking: - Secret masking steps

Agent files (.github/agents/*.md) can additionally define:

  • name - Agent name
  • description - Agent description

Other fields in imported files generate warnings and are ignored.

Imports are processed using breadth-first traversal: direct imports first, then nested. Earlier imports in the list take precedence; circular imports fail at compile time.

FieldMerge strategy
tools:Deep merge; allowed arrays concatenate and deduplicate. MCP tool conflicts fail except on allowed arrays.
mcp-servers:Imported servers override same-named main servers; first-wins across imports.
network:allowed domains union (deduped, sorted). Main mode and firewall take precedence.
permissions:Validation only — not merged. Main must declare all imported permissions at sufficient levels (writereadnone).
safe-outputs:Each type defined once; main overrides imports. Duplicate types across imports fail.
runtimes:Main overrides imports; imported values fill in unspecified fields.
services:All services merged; duplicate names fail compilation.
steps:Imported steps prepended to main; concatenated in import order.
jobs:Not merged — define only in the main workflow. Use safe-outputs.jobs for importable jobs.
safe-outputs.jobsNames must be unique; duplicates fail. Order determined by needs: dependencies.

Example — tools.bash.allowed merging:

# main.md: [write]
# import: [read, list]
# result: [read, list, write]

Share reusable pre-execution steps — such as token rotation, environment setup, or gate checks — across multiple workflows by defining them in a shared file:

shared/rotate-token.md
---
description: Shared token rotation setup
steps:
- name: Rotate GitHub App token
id: get-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
---

Any workflow that imports this file gets the rotation step prepended before its own steps:

my-workflow.md
---
on: issues
engine: copilot
imports:
- shared/rotate-token.md
permissions:
contents: read
issues: write
steps:
- name: Prepare context
run: echo "context ready"
---
# My Workflow
Process the issue using the rotated token from the imported step.

Steps from imports run before steps defined in the main workflow, in import declaration order.

Define an MCP server configuration once and import it wherever needed:

shared/mcp/tavily.md
---
description: Tavily web search MCP server
mcp-servers:
tavily:
url: "https://mcp.tavily.com/mcp/?tavilyApiKey=${{ secrets.TAVILY_API_KEY }}"
allowed: ["*"]
network:
allowed:
- mcp.tavily.com
---

Import it into any workflow that needs web search:

research.md
---
on: issues
engine: copilot
imports:
- shared/mcp/tavily.md
permissions:
contents: read
issues: write
---
# Research Workflow
Search the web for relevant information and summarize findings in the issue.

Top-level jobs: defined in a shared workflow are merged into the importing workflow’s compiled lock file. The job execution order is determined by needs entries — a shared job can run before or after other jobs in the final workflow:

shared/build.md
---
description: Shared build job that compiles artifacts for the agent to inspect
jobs:
build:
runs-on: ubuntu-latest
needs: [activation]
outputs:
artifact_name: ${{ steps.build.outputs.artifact_name }}
steps:
- uses: actions/checkout@v6
- name: Build
id: build
run: |
npm ci && npm run build
echo "artifact_name=build-output" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
steps:
- uses: actions/download-artifact@v4
with:
name: ${{ needs.build.outputs.artifact_name }}
path: /tmp/build-output
---

Import it so the build job runs before the agent and its artifacts are available as pre-steps:

my-workflow.md
---
on: pull_request
engine: copilot
imports:
- shared/build.md
permissions:
contents: read
pull-requests: write
---
# Code Review Workflow
Review the build output in /tmp/build-output and suggest improvements.

In the compiled lock file the build job appears alongside activation and agent jobs, ordered according to each job’s needs declarations.

Jobs defined under safe-outputs: can be shared across workflows. These jobs become callable MCP tools that the AI agent can invoke during execution:

shared/notify.md
---
description: Shared notification job
safe-outputs:
notify-slack:
description: "Post a message to Slack"
runs-on: ubuntu-latest
output: "Notification sent"
inputs:
message:
description: "Message to post"
required: true
type: string
steps:
- name: Post to Slack
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
run: |
curl -s -X POST "$SLACK_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{\"text\":\"${{ inputs.message }}\"}"
---

Import and use it in multiple workflows:

my-workflow.md
---
on: issues
engine: copilot
imports:
- shared/notify.md
permissions:
contents: read
issues: write
---
# My Workflow
Process the issue. When done, use notify-slack to send a summary notification.
  • Circular imports: Detected at compile time.
  • Missing files: Use {{#import? file.md}} for optional imports; required imports fail if missing.
  • Conflicts: Duplicate safe-output types across imports fail — define in main workflow to override.
  • Permissions: Insufficient permissions fail with detailed error messages.

Remote imports are cached by commit SHA in .github/aw/imports/. Keep import chains shallow and consolidate related imports; every compilation records imports in the lock file manifest.

Using Imports in Repository Rulesets (inlined-imports: true)

Section titled “Using Imports in Repository Rulesets (inlined-imports: true)”

When a workflow is configured as a required status check in a repository ruleset, it runs in a restricted context that does not have access to other files in the repository. Shared files imported with the imports: field are loaded at runtime from the repository checkout, but this checkout is not available in the ruleset context, producing an error such as:

ERR_SYSTEM: Runtime import file not found: workflows/shared/file.md

Set inlined-imports: true to bundle all imported content directly into the compiled .lock.yml at compile time, so no file system access is needed at runtime:

---
on: pull_request
engine: copilot
inlined-imports: true
imports:
- shared/common-tools.md
- shared/security-setup.md
---
# My Workflow
Workflow instructions here.

After adding inlined-imports: true, recompile the workflow:

Terminal window
gh aw compile my-workflow