Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,6 @@ node_modules/
gh-aw-test/

pkg/cli/workflows/*.yml

# Workflow imports (cached files only, not the lock file)
.aw/imports/
34 changes: 31 additions & 3 deletions cmd/gh-aw/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,34 @@ Examples:
}

var installCmd = &cobra.Command{
Use: "install <org/repo>[@version]",
Use: "install [workflow-name]",
Short: "Install workflow imports and dependencies",
Long: `Install imports for agentic workflows.

If a workflow name is specified, only imports for that workflow are installed.
Otherwise, all imports from all workflows are installed.

Examples:
` + constants.CLIExtensionPrefix + ` install # Install all imports
` + constants.CLIExtensionPrefix + ` install my-workflow # Install imports for specific workflow`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var workflowName string
if len(args) > 0 {
workflowName = args[0]
}
if err := cli.InstallImports(workflowName, verbose); err != nil {
fmt.Fprintln(os.Stderr, console.FormatError(console.CompilerError{
Type: "error",
Message: fmt.Sprintf("installing imports: %v", err),
}))
os.Exit(1)
}
},
}

var installPackageCmd = &cobra.Command{
Use: "install-package <org/repo>[@version]",
Short: "Install agentic workflows from a GitHub repository",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -311,8 +338,8 @@ func init() {
listCmd.Flags().BoolP("packages", "p", false, "List installed packages instead of available workflows")
listCmd.Flags().BoolP("local", "l", false, "List local packages instead of global packages (requires --packages)")

// Add local flag to install command
installCmd.Flags().BoolP("local", "l", false, "Install packages locally in .aw/packages instead of globally in ~/.aw/packages")
// Add local flag to install-package command
installPackageCmd.Flags().BoolP("local", "l", false, "Install packages locally in .aw/packages instead of globally in ~/.aw/packages")

// Add local flag to uninstall command
uninstallCmd.Flags().BoolP("local", "l", false, "Uninstall packages from local .aw/packages instead of global ~/.aw/packages")
Expand Down Expand Up @@ -341,6 +368,7 @@ func init() {
rootCmd.AddCommand(newCmd)
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(installCmd)
rootCmd.AddCommand(installPackageCmd)
rootCmd.AddCommand(uninstallCmd)
rootCmd.AddCommand(compileCmd)
rootCmd.AddCommand(runCmd)
Expand Down
230 changes: 230 additions & 0 deletions docs/src/content/docs/reference/imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
---
title: Imports
description: Import workflow components from external GitHub repositories
---

The `imports` field allows agentic workflows to import content from external GitHub repositories, enabling code reuse and modular workflow design.

## Basic Usage

Import components from external repositories by specifying the repository, version, and file path in the frontmatter:

```aw
---
on: workflow_dispatch
permissions:
contents: read
engine: claude
imports:
- microsoft/genaiscript v1.5.0 agentics/engine.md
- githubnext/gh-aw main .github/workflows/agentics/shared/tool-refused.md
---

# My Workflow

This workflow imports shared components from external repositories.
```

## Import Format

Each import follows the format: `org/repo version path`

- **org/repo**: GitHub repository in `owner/repository` format
- **version**: Git reference (branch name, tag, or commit SHA)
- **path**: Relative path to the file within the repository

Examples:
- `microsoft/genaiscript v1.5.0 agentics/engine.md` - Import from a specific version tag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment below, let's use

  • microsoft/genaiscript/agentics/engine.md@v1.5.0 - Import from a specific version tag

- `githubnext/gh-aw main .github/workflows/shared/config.md` - Import from main branch
- `example/repo abc123def path/to/file.md` - Import from specific commit

## Installation

Before compiling workflows with imports, install the dependencies:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a step before compiling feels wrong. We're already 1-step when we need to be 0-step. We don't want to be 2-step

Why isn't this part of compiling?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes should be merged


```bash
# Install imports for all workflows
gh aw install

# Install imports for a specific workflow
gh aw install my-workflow
```

The install command:
1. Parses import specifications from workflow files
2. Resolves versions to commit SHAs using git ls-remote
3. Clones repositories at the specified versions
4. Stores imported files in `.aw/imports/`
5. Creates/updates `.aw/imports.lock` with resolved SHAs
6. Creates `.aw/.gitignore` to automatically ignore the `imports/` folder

## Lock File

The install command generates `.aw/imports.lock` which records:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another lock is too complex isn't it? Lock on lock...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup can be merged in the generated lock.yml file

- Resolved commit SHAs for each import
- Timestamp when imports were resolved
- List of transitive files (from @include directives)

Example lock file:
```
# Import lock file generated by gh-aw
# This file records resolved versions and commit SHAs for imports
# Do not edit manually

version: 1

microsoft/genaiscript v1.5.0 agentics/engine.md abc123def456... 2025-01-15T10:30:00Z

githubnext/gh-aw main .github/workflows/agentics/shared/tool-refused.md ce06aa00a9ce... 2025-01-15T10:30:01Z
```

## Compilation

During compilation, imported files are processed:
1. Frontmatter from imports is merged (tools, engine config, etc.)
2. Markdown content is prepended to the workflow
3. The workflow is compiled as if the content was local

```bash
# Compile after installing imports
gh aw compile my-workflow
```

If imports are not installed, compilation fails with:
```
error: import org/repo version path not found in lock file (run 'gh aw install')
```

## Import Resolution

Imports are resolved before @include directives during compilation:
1. Parse imports from frontmatter
2. Read lock file and verify imports exist
3. Load imported files from `.aw/imports/`
4. Merge frontmatter and markdown content
5. Process @include directives
6. Compile final workflow

## Frontmatter Merging

When importing files with frontmatter, configurations are merged:

**Tools**: Tool configurations are combined. If the same tool exists in both files, the imported configuration is used.

```aw
# Imported file
---
tools:
github:
allowed: [get_issue, list_issues]
---
```

```aw
# Main workflow
---
imports:
- org/repo v1.0 tools-config.md
tools:
github:
allowed: [get_pull_request]
---
```

Result: GitHub tools will have both sets of allowed functions.

**Other fields**: Imported values take precedence for non-tools fields.

## Version Control

The `gh aw install` command automatically creates a `.aw/.gitignore` file that ignores the `imports/` folder. This ensures the cached imported files are not committed while the lock file remains trackable.

You can also add `.aw/imports/` to your project's root `.gitignore` for additional protection:

```gitignore
# Workflow imports (cached files only, not the lock file)
.aw/imports/
```

The lock file (`.aw/imports.lock`) should be committed to version control to ensure reproducible builds by pinning exact commit SHAs, similar to `go.sum` or `package-lock.json`.

## Best Practices

1. **Use version tags**: Prefer semantic version tags (v1.0.0) over branch names for stability
2. **Install before compile**: Always run `gh aw install` after adding or updating imports
3. **Commit lock file**: Include `.aw/imports.lock` in version control for reproducibility
4. **Test imports**: Verify imported content compiles successfully
5. **Document dependencies**: Document why each import is needed

## Transitive Dependencies

Imported files can contain @include directives. The install command automatically:
- Discovers transitive @include references
- Validates all referenced files exist
- Records them in the lock file

## Comparison with @include

| Feature | @include | imports |
|---------|----------|---------|
| Source | Local repository | External repositories |
| Resolution | Compile time | Install time |
| Caching | None (always read) | Cached in .aw/imports/ |
| Version control | Implicit (via git) | Explicit (via lock file) |
| Use case | Project-local reuse | Cross-repo sharing |

Use @include for local files, imports for external dependencies.

## Troubleshooting

**Import not found during compile**:
```
error: import org/repo version path not found in lock file
```
Solution: Run `gh aw install` to install imports.

**Version resolution failed**:
```
error: failed to resolve version v1.0.0 to commit SHA
```
Solution: Verify the version/tag/branch exists in the repository.

**File not found in imported repository**:
```
error: imported file not found: path/to/file.md
```
Solution: Check the file path exists in the repository at the specified version.

## Example: Importing Security Notices

```aw
---
on:
issues:
types: [opened]
permissions:
contents: read
issues: write
engine: claude
imports:
- githubnext/gh-aw main .github/workflows/agentics/shared/tool-refused.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In GitHub Actions uses the syntax is simpler, like this:

githubnext/gh-aw/.github/workflows/agentics/shared/tool-refused.md@main

e.g.

steps:
- name: Start the Repo-mind MCP server
  id: repo-mind-server
  uses: githubnext/repo-mind/.github/actions/server@main

We should align with this I think

- githubnext/gh-aw main .github/workflows/agentics/shared/xpia.md
tools:
github:
allowed: [get_issue, add_issue_comment]
---

# Issue Triage Bot

This workflow imports security notices and tool usage guidelines.

Analyze issue #${{ github.event.issue.number }} and provide triage recommendations.
```

```bash
# Install and compile
gh aw install
gh aw compile issue-triage
```

The compiled workflow will include the imported security notices and XPIA protection guidelines.
Loading
Loading