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
40 changes: 40 additions & 0 deletions examples/05-pr-review-spawner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# PR Review Spawner

Automatically review open pull requests using an AI agent. The spawner
discovers PRs with a specific label and creates a Task for each one. The
agent reads the diff, checks for common issues, and posts a review comment.

## How It Works

1. A developer opens a PR and adds the `needs-ai-review` label.
2. TaskSpawner discovers the labeled PR and creates a Task.
3. The agent clones the repo, reads the PR diff and description, and posts
a review via the `gh` CLI.
4. After the review, the agent removes the trigger label and adds
`ai-reviewed` so it is not picked up again.

## Resources

| File | Description |
|------|-------------|
| `workspace.yaml` | Git repo the agent clones |
| `credentials-secret.yaml` | Agent credentials (OAuth token) |
| `github-token-secret.yaml` | GitHub token for cloning and `gh` CLI |
| `agentconfig.yaml` | Review instructions and skill |
| `taskspawner.yaml` | TaskSpawner watching for labeled PRs |

## Setup

1. Replace all `# TODO:` placeholders in the YAML files.
2. Create the GitHub labels `needs-ai-review` and `ai-reviewed` in your repo.
3. Apply all resources:

```bash
kubectl apply -f examples/05-pr-review-spawner/
```

4. Label a PR with `needs-ai-review` and watch the agent pick it up:

```bash
axon get tasks -w
```
31 changes: 31 additions & 0 deletions examples/05-pr-review-spawner/agentconfig.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: axon.io/v1alpha1
kind: AgentConfig
metadata:
name: pr-reviewer-config
spec:
agentsMD: |
You are a code reviewer. Your job is to review pull requests thoroughly
and leave constructive, actionable feedback.

Guidelines:
- Focus on correctness, security, performance, and maintainability.
- Point out specific lines when suggesting changes.
- Acknowledge good patterns, not just problems.
- If the PR looks good, approve it with a short summary of what you checked.
- Do not nitpick style issues that a linter would catch.
plugins:
- name: reviewer
skills:
- name: review-pr
content: |
---
name: review-pr
description: Review a pull request and post feedback
---
To review a PR:
1. Fetch the PR branch: `gh pr checkout <number>`
2. Read the diff: `git diff main...HEAD`
3. Read the PR description: `gh pr view <number>`
4. Read any existing review comments: `gh api repos/{owner}/{repo}/pulls/<number>/comments`
5. Post your review: `gh pr review <number> --comment --body "<review>"`
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 12, 2026

Choose a reason for hiding this comment

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

P2: Using --body "<review>" is fragile β€” code reviews routinely contain double quotes, backticks, and other shell metacharacters that will break this command. Prefer --body-file to avoid shell quoting issues.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At examples/05-pr-review-spawner/agentconfig.yaml, line 30:

<comment>Using `--body "<review>"` is fragile β€” code reviews routinely contain double quotes, backticks, and other shell metacharacters that will break this command. Prefer `--body-file` to avoid shell quoting issues.</comment>

<file context>
@@ -0,0 +1,31 @@
+            2. Read the diff: `git diff main...HEAD`
+            3. Read the PR description: `gh pr view <number>`
+            4. Read any existing review comments: `gh api repos/{owner}/{repo}/pulls/<number>/comments`
+            5. Post your review: `gh pr review <number> --comment --body "<review>"`
+            6. Swap the labels: `gh pr edit <number> --remove-label needs-ai-review --add-label ai-reviewed`
</file context>
Fix with Cubic

6. Swap the labels: `gh pr edit <number> --remove-label needs-ai-review --add-label ai-reviewed`
7 changes: 7 additions & 0 deletions examples/05-pr-review-spawner/credentials-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: claude-credentials
type: Opaque
stringData:
oauthToken: "" # TODO: replace with your Claude OAuth token
7 changes: 7 additions & 0 deletions examples/05-pr-review-spawner/github-token-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: github-token
type: Opaque
stringData:
GITHUB_TOKEN: "" # TODO: replace with a GitHub token that has repo scope
39 changes: 39 additions & 0 deletions examples/05-pr-review-spawner/taskspawner.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: axon.io/v1alpha1
kind: TaskSpawner
metadata:
name: pr-reviewer
spec:
when:
githubIssues:
types:
- pulls
labels:
- needs-ai-review
excludeLabels:
- ai-reviewed
state: open
taskTemplate:
type: claude-code
workspaceRef:
name: review-workspace
agentConfigRef:
name: pr-reviewer-config
credentials:
type: oauth
secretRef:
name: claude-credentials
promptTemplate: |
Review PR #{{.Number}}: {{.Title}}

Description:
{{.Body}}

Steps:
1. Check out the PR branch and read the full diff against main.
2. /review-pr {{.Number}}

After reviewing, remove the "needs-ai-review" label and add "ai-reviewed":
gh pr edit {{.Number}} --remove-label needs-ai-review --add-label ai-reviewed
ttlSecondsAfterFinished: 3600
pollInterval: 5m
maxConcurrency: 3
9 changes: 9 additions & 0 deletions examples/05-pr-review-spawner/workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: axon.io/v1alpha1
kind: Workspace
metadata:
name: review-workspace
spec:
repo: https://github.com/your-org/your-repo.git # TODO: replace with your repo
ref: main
secretRef:
name: github-token
53 changes: 53 additions & 0 deletions examples/06-multi-repo-migration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Multi-Repo Migration

Apply a repetitive change across multiple repositories using parallel agent
Tasks. This pattern is useful for fleet-wide refactoring, dependency bumps,
or API migration across microservices.

## How It Works

Each repository gets its own Workspace resource. A single TaskSpawner on
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 12, 2026

Choose a reason for hiding this comment

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

P2: The "How It Works" section opens with "A single TaskSpawner" which contradicts the actual pattern (one TaskSpawner per repo) clarified two paragraphs later. This will confuse readers. Consider rewriting the opening to accurately describe the architecture from the start.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At examples/06-multi-repo-migration/README.md, line 9:

<comment>The "How It Works" section opens with "A single TaskSpawner" which contradicts the actual pattern (one TaskSpawner per repo) clarified two paragraphs later. This will confuse readers. Consider rewriting the opening to accurately describe the architecture from the start.</comment>

<file context>
@@ -0,0 +1,53 @@
+
+## How It Works
+
+Each repository gets its own Workspace resource. A single TaskSpawner on
+a cron schedule creates one Task per Workspace. Axon handles the
+parallelism β€” all agents run concurrently in isolated Pods.
</file context>
Fix with Cubic

a cron schedule creates one Task per Workspace. Axon handles the
parallelism β€” all agents run concurrently in isolated Pods.

This example shows three repos, but the pattern scales to any number.
Add more Workspace resources and corresponding TaskSpawner entries.

Since TaskSpawner currently supports one Workspace per spawner, this
example uses one TaskSpawner per repository. A shared AgentConfig ensures
consistent behavior across all agents.

## Resources

| File | Description |
|------|-------------|
| `workspaces.yaml` | Workspace resources for each target repo |
| `credentials-secret.yaml` | Agent credentials (shared) |
| `github-token-secret.yaml` | GitHub token for cloning and pushing |
| `agentconfig.yaml` | Shared migration instructions |
| `taskspawners.yaml` | One TaskSpawner per repo, all on the same cron |

## Setup

1. Replace all `# TODO:` placeholders.
2. Add or remove Workspace/TaskSpawner pairs to match your repo list.
3. Apply:

```bash
kubectl apply -f examples/06-multi-repo-migration/
```

4. Monitor progress:

```bash
axon get tasks -w
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 12, 2026

Choose a reason for hiding this comment

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

P2: Inconsistent CLI usage: this example uses axon get tasks -w but examples 01–04 and the parent examples/README.md all use kubectl get tasks -w. Use kubectl for consistency, or update all examples together.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At examples/06-multi-repo-migration/README.md, line 43:

<comment>Inconsistent CLI usage: this example uses `axon get tasks -w` but examples 01–04 and the parent `examples/README.md` all use `kubectl get tasks -w`. Use `kubectl` for consistency, or update all examples together.</comment>

<file context>
@@ -0,0 +1,53 @@
+4. Monitor progress:
+
+```bash
+axon get tasks -w
+```
+
</file context>
Fix with Cubic

```

## Tips

- Set `maxConcurrency: 1` on each TaskSpawner to ensure only one migration
attempt per repo at a time.
- Use `ttlSecondsAfterFinished: 0` if you want immediate cleanup after
each run.
- The cron schedule `"0 9 * * 1"` runs once a week (Monday 9 AM UTC).
Adjust to run once (`"0 9 12 2 *"`) for a one-time migration.
15 changes: 15 additions & 0 deletions examples/06-multi-repo-migration/agentconfig.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: axon.io/v1alpha1
kind: AgentConfig
metadata:
name: migration-config
spec:
agentsMD: |
You are performing a fleet-wide migration. Follow the migration
instructions exactly and do not make unrelated changes. If the
migration does not apply to this repo (e.g., the dependency is not
used), exit without creating a PR.

After making changes:
1. Run the project's test suite to verify nothing is broken.
2. Create a PR with a clear title prefixed with "[Migration]".
3. Include a summary of what changed and why in the PR description.
7 changes: 7 additions & 0 deletions examples/06-multi-repo-migration/credentials-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: claude-credentials
type: Opaque
stringData:
oauthToken: "" # TODO: replace with your Claude OAuth token
7 changes: 7 additions & 0 deletions examples/06-multi-repo-migration/github-token-secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: github-token
type: Opaque
stringData:
GITHUB_TOKEN: "" # TODO: replace with a GitHub token that has repo scope
106 changes: 106 additions & 0 deletions examples/06-multi-repo-migration/taskspawners.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# One TaskSpawner per repository, all sharing the same migration prompt
# and AgentConfig. Add or remove entries to match your repo list.
apiVersion: axon.io/v1alpha1
kind: TaskSpawner
metadata:
name: migrate-service-a
spec:
when:
cron:
schedule: "0 9 12 2 *" # TODO: adjust schedule (this runs Feb 12 at 9 AM UTC)
taskTemplate:
type: claude-code
workspaceRef:
name: service-a
agentConfigRef:
name: migration-config
credentials:
type: oauth
secretRef:
name: claude-credentials
promptTemplate: |
Migrate this repository from the deprecated `log/v1` package to `log/v2`.

Steps:
1. Find all imports of `github.com/your-org/log/v1`. # TODO: replace with actual migration
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 12, 2026

Choose a reason for hiding this comment

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

P2: # TODO: replace with actual migration is inside a YAML block scalar (|), so it will be sent as literal text to the AI agent β€” it is NOT a YAML comment. This is inconsistent with the other # TODO: placeholders in this file (e.g., on schedule: lines) which are real YAML comments. Move this TODO to a YAML comment above the promptTemplate: key so users see it but the agent doesn't.

Prompt for AI agents
Check if this issue is valid β€” if so, understand the root cause and fix it. At examples/06-multi-repo-migration/taskspawners.yaml, line 25:

<comment>`# TODO: replace with actual migration` is inside a YAML block scalar (`|`), so it will be sent as literal text to the AI agent β€” it is NOT a YAML comment. This is inconsistent with the other `# TODO:` placeholders in this file (e.g., on `schedule:` lines) which are real YAML comments. Move this TODO to a YAML comment above the `promptTemplate:` key so users see it but the agent doesn't.</comment>

<file context>
@@ -0,0 +1,106 @@
+      Migrate this repository from the deprecated `log/v1` package to `log/v2`.
+
+      Steps:
+      1. Find all imports of `github.com/your-org/log/v1`. # TODO: replace with actual migration
+      2. Replace them with `github.com/your-org/log/v2`.
+      3. Update any changed API calls (see migration guide below).
</file context>
Fix with Cubic

2. Replace them with `github.com/your-org/log/v2`.
3. Update any changed API calls (see migration guide below).
4. Run tests and fix any compilation errors.
5. Open a PR with the changes.

Migration guide:
- `log.Info(msg)` -> `log.Info(msg, nil)` (second arg is now required attrs)
- `log.WithField(k, v)` -> `log.With(k, v)`
- `log.Fatal(msg)` is removed; use `log.Error(msg, nil); os.Exit(1)`
ttlSecondsAfterFinished: 3600
maxConcurrency: 1
---
apiVersion: axon.io/v1alpha1
kind: TaskSpawner
metadata:
name: migrate-service-b
spec:
when:
cron:
schedule: "0 9 12 2 *" # TODO: adjust schedule
taskTemplate:
type: claude-code
workspaceRef:
name: service-b
agentConfigRef:
name: migration-config
credentials:
type: oauth
secretRef:
name: claude-credentials
promptTemplate: |
Migrate this repository from the deprecated `log/v1` package to `log/v2`.

Steps:
1. Find all imports of `github.com/your-org/log/v1`. # TODO: replace with actual migration
2. Replace them with `github.com/your-org/log/v2`.
3. Update any changed API calls (see migration guide below).
4. Run tests and fix any compilation errors.
5. Open a PR with the changes.

Migration guide:
- `log.Info(msg)` -> `log.Info(msg, nil)` (second arg is now required attrs)
- `log.WithField(k, v)` -> `log.With(k, v)`
- `log.Fatal(msg)` is removed; use `log.Error(msg, nil); os.Exit(1)`
ttlSecondsAfterFinished: 3600
maxConcurrency: 1
---
apiVersion: axon.io/v1alpha1
kind: TaskSpawner
metadata:
name: migrate-service-c
spec:
when:
cron:
schedule: "0 9 12 2 *" # TODO: adjust schedule
taskTemplate:
type: claude-code
workspaceRef:
name: service-c
agentConfigRef:
name: migration-config
credentials:
type: oauth
secretRef:
name: claude-credentials
promptTemplate: |
Migrate this repository from the deprecated `log/v1` package to `log/v2`.

Steps:
1. Find all imports of `github.com/your-org/log/v1`. # TODO: replace with actual migration
2. Replace them with `github.com/your-org/log/v2`.
3. Update any changed API calls (see migration guide below).
4. Run tests and fix any compilation errors.
5. Open a PR with the changes.

Migration guide:
- `log.Info(msg)` -> `log.Info(msg, nil)` (second arg is now required attrs)
- `log.WithField(k, v)` -> `log.With(k, v)`
- `log.Fatal(msg)` is removed; use `log.Error(msg, nil); os.Exit(1)`
ttlSecondsAfterFinished: 3600
maxConcurrency: 1
29 changes: 29 additions & 0 deletions examples/06-multi-repo-migration/workspaces.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: axon.io/v1alpha1
kind: Workspace
metadata:
name: service-a
spec:
repo: https://github.com/your-org/service-a.git # TODO: replace
ref: main
secretRef:
name: github-token
---
apiVersion: axon.io/v1alpha1
kind: Workspace
metadata:
name: service-b
spec:
repo: https://github.com/your-org/service-b.git # TODO: replace
ref: main
secretRef:
name: github-token
---
apiVersion: axon.io/v1alpha1
kind: Workspace
metadata:
name: service-c
spec:
repo: https://github.com/your-org/service-c.git # TODO: replace
ref: main
secretRef:
name: github-token
Loading