Documentation Index
Fetch the complete documentation index at: https://mintlify.com/withastro/flue/llms.txt
Use this file to discover all available pages before exploring further.
Run Flue agents inside GitHub Actions workflows. This pattern is for CI agents that run in response to repository events — an issue opened, a PR created, a push to main — without needing a deployed HTTP server.
The key command is flue run:
npx flue run <agent> --target node --id <id> --payload '<json>'
It builds your project, invokes the agent once, streams progress to stderr, prints the final result as JSON to stdout, and exits.
When to use this pattern
- You want an agent to run automatically when a GitHub event fires
- The agent needs access to the runner’s filesystem,
git, gh, or other CLI tools
- You don’t need a persistent HTTP endpoint — just a one-shot invocation per event
For a long-running HTTP server, see Deploy to Node.js.
Project setup
Create the project
mkdir my-flue-project && cd my-flue-project
npm init -y
npm install @flue/runtime valibot
npm install -D @flue/cli
Create a CI agent
CI agents use triggers = {} — no HTTP endpoint is created in the deployed build. The CLI can still invoke them with flue run.// .flue/agents/hello.ts
import type { FlueContext } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import * as v from 'valibot';
export const triggers = {};
export default async function ({ init, payload }: FlueContext) {
const harness = await init({
sandbox: local(),
model: 'anthropic/claude-sonnet-4-6',
});
const session = await harness.session();
const { data } = await session.prompt(
`Say hello to ${payload.name ?? 'the user'} and share an interesting fact.`,
{
result: v.object({
greeting: v.string(),
fact: v.string(),
}),
},
);
return data;
}
local() runs the agent directly against the runner’s filesystem and shell. Anything on $PATH — gh, git, npm — is reachable from the agent’s bash tool. By default only shell-essential env vars (PATH, HOME, locale, etc.) are inherited; pass local({ env: { GH_TOKEN: process.env.GH_TOKEN } }) to expose specific secrets. Test it locally
npx flue run hello --target node --id test-1 \
--payload '{"name": "World"}'
Use flue run during development to iterate fast — no deployment needed.Add the workflow
# .github/workflows/hello.yml
name: Hello Flue
on:
issues:
types: [opened]
jobs:
hello:
runs-on: ubuntu-latest
permissions:
issues: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- name: Run agent
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
npx flue run hello --target node \
--id "hello-${{ github.event.issue.number }}" \
--payload '{"name": "${{ github.event.issue.user.login }}"}'
Add ANTHROPIC_API_KEY as a repository secret at Settings > Secrets and variables > Actions.
Building a real agent
The hello world above shows the basic shape. Here is a more complete example — an issue triage agent that reads issues with gh, assesses severity, and optionally applies a fix.
The agent
// .flue/agents/triage.ts
import { type FlueContext } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import * as v from 'valibot';
export const triggers = {};
export default async function ({ init, payload }: FlueContext) {
const harness = await init({
sandbox: local({
// Explicitly forward the runner's secrets into the agent's shell.
// Anything not listed here (including ANTHROPIC_API_KEY) stays on
// the host and is invisible to the model's bash tool.
env: {
GH_TOKEN: process.env.GH_TOKEN,
},
}),
model: 'anthropic/claude-opus-4-7',
});
const session = await harness.session();
const { data } = await session.skill('triage', {
args: { issueNumber: payload.issueNumber },
result: v.object({
severity: v.picklist(['low', 'medium', 'high', 'critical']),
reproducible: v.boolean(),
summary: v.string(),
fix_applied: v.boolean(),
}),
});
return data;
}
The skill
Skills give the agent a focused instruction set for a specific job. They live in .agents/skills/ and are discovered automatically from process.cwd() when using local().
---
name: triage
description: Triage a GitHub issue — reproduce, assess severity, and optionally fix.
---
Given the issue number in the arguments:
1. Use `gh issue view` to fetch the issue details
2. Read the codebase to understand the relevant area
3. Attempt to reproduce the issue
4. Assess severity and write a summary
5. If the fix is straightforward, apply it and open a PR
Save this as .agents/skills/triage/SKILL.md.
The workflow
# .github/workflows/issue-triage.yml
name: Issue Triage
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
issues: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- name: Run triage agent
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npx flue run triage --target node \
--id "triage-${{ github.event.issue.number }}" \
--payload '{"issueNumber": ${{ github.event.issue.number }}}'
GITHUB_TOKEN is provided automatically by GitHub Actions. Set ANTHROPIC_API_KEY as a repository secret.
Passing GitHub context to your agent
Use --payload to pass event data as JSON:
--payload '{"issueNumber": ${{ github.event.issue.number }}}'
--payload '{"prNumber": ${{ github.event.pull_request.number }}, "sha": "${{ github.sha }}"}'
The payload is available in your agent as payload:
export default async function ({ init, payload }: FlueContext) {
console.log(payload.issueNumber); // 42
}
Typed results and orchestration
Result schemas let you branch on the agent’s output within a single run:
import * as v from 'valibot';
const { data } = await session.skill('triage', {
args: { issueNumber: payload.issueNumber },
result: v.object({
severity: v.picklist(['low', 'medium', 'high', 'critical']),
reproducible: v.boolean(),
summary: v.string(),
}),
});
if (data.severity === 'critical' && data.reproducible) {
await session.skill('auto-fix', {
args: { issueNumber: payload.issueNumber },
result: v.object({ fix_applied: v.boolean(), pr_url: v.optional(v.string()) }),
});
}
return data;
flue run output
flue run writes the agent’s event stream to stderr and the final result to stdout. This means you can pipe the result to jq without capturing logs:
npx flue run triage --target node --id test-1 \
--payload '{"issueNumber": 42}' | jq '.severity'
Secrets
In GitHub Actions, secrets set in env: on the job step are available as process.env in the agent process. Forward them explicitly into local() to make them visible to the agent’s bash tool:
sandbox: local({
env: {
GH_TOKEN: process.env.GH_TOKEN,
NPM_TOKEN: process.env.NPM_TOKEN,
},
})
Secrets not listed in local({ env: ... }) — including your model API key — stay on the host and are never visible to the model’s bash tool.
For tighter isolation, wrap a specific operation as a custom tool instead:
const harness = await init({
tools: [
{
name: 'post_comment',
description: 'Post a comment on a GitHub issue',
parameters: v.object({ body: v.string() }),
execute: async ({ body }) => {
// This code reads process.env directly; the agent never sees GH_TOKEN.
await fetch(`https://api.github.com/repos/owner/repo/issues/${issueNumber}/comments`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.GH_TOKEN}` },
body: JSON.stringify({ body }),
});
return { ok: true };
},
},
],
model: 'anthropic/claude-opus-4-7',
});