Skip to content
Merged
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
100 changes: 86 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ the [GitHub Models](https://docs.github.com/en/github-models) default
(`github/openai/gpt-4.1`, no API key required), CI patterns, and reusable
skills / prompts / personas.

The first agent is **PR Review**; more will be added over time.
The current agents are **PR Review** and **Issue Health Check**.

## Agents

| Agent | What it does | How it runs |
| --- | --- | --- |
| **PR Review** (`workflows/pr-review.ts`) | Reviews a pull request against the target repo's conventions and posts a verdict + findings. | A composite GitHub Action other repos call — see [the PR Review agent](#the-pr-review-agent). |
| _…more soon_ | | |
| **Issue Health Check** (`workflows/issue-health.ts`) | Checks newly opened issues for actionability, missing information, privacy concerns, and existing target-repo labels. | A composite GitHub Action other repos call — see [the Issue Health Check agent](#the-issue-health-check-agent). |

Adding one is cheap — see [Adding an agent](#adding-an-agent).

Expand All @@ -23,18 +23,22 @@ Adding one is cheap — see [Adding an agent](#adding-an-agent).
```
agents/ # pnpm workspace root — a fleet of agents
workflows/
pr-review.ts # one file per agent (PR Review is the first)
pr-review.ts # one file per agent
issue-health.ts
skills/
code-review/SKILL.md # skills agents register (code-review is PR Review's)
code-review/SKILL.md # skills agents register
issue-health/SKILL.md
prompts/
*.md # prompt guidance loaded at runtime
personas/
*.ts # subagents an agent can delegate to
AGENTS.md # docs for agents working ON this repo (not the reviewer's persona)
actions/
pr-review/action.yml # composite action consuming repos use
pr-review/action.yml # composite actions consuming repos use
issue-health/action.yml
examples/
consumer-workflow.yml # copy-paste workflow for a consuming repo
consumer-workflow.yml # copy-paste workflows for consuming repos
issue-health-workflow.yml
sample-consumer/.agents/ # example per-repo overrides
app.ts # runtime entry: registers model providers (incl. GitHub Models)
flue.config.ts # default target (node)
Expand All @@ -60,16 +64,17 @@ Every agent here is assembled the same way — PR Review is the worked example:
auto-discovers it at runtime, so the review process keeps project context, but
it is not the reviewer's persona. Per-review context about the code being
reviewed comes from the *target* repo's own `AGENTS.md`.
- **Posting is deterministic, done by the workflow — not the model.** The
`code-review` skill only *analyzes* and returns `{ verdict, summary,
findings }`; `workflows/pr-review.ts` renders that and posts the review via
`gh`. (Smaller models can't be trusted to reliably run the post step.)
- **GitHub mutations are deterministic, done by the workflow — not the model.**
Skills only *analyze* and return structured data; workflows render comments,
post them with `gh`, and apply any labels from deterministic code paths.
(Smaller models can't be trusted to reliably run mutation steps.)
- **MCP tools** can be attached at runtime: `connectMcpServer(url)`, then pass
the connection's `tools` to `init(agent, { tools })`. PR Review connects the
Flipt docs MCP so it can ground reviews in the documentation.

The payload only ever carries *which* PR to review (`prNumber`, optional
`repo`) — never the skills or prompts.
The payload only ever carries *which artifact* to inspect (for example
`prNumber` or `issueNumber`, plus optional `repo`) — never the skills or
prompts.

## Subagents / personas

Expand All @@ -88,8 +93,8 @@ no Anthropic key. On a **free** plan GitHub caps requests at ~8k tokens (too
small for big diffs); a **paid** plan lifts that to production limits, which is
what makes real reviews fit.

Override per run with `REVIEW_MODEL` (locally) or the action's `model` input
(CI):
Override per run with `REVIEW_MODEL` or `ISSUE_HEALTH_MODEL` (locally), or the
action's `model` input (CI):

- another GitHub model (e.g. `github/openai/gpt-4o`),
- `anthropic/claude-sonnet-4-6` (also set `ANTHROPIC_API_KEY`),
Expand Down Expand Up @@ -163,6 +168,73 @@ pnpm exec flue run pr-review --payload '{"prNumber": 123, "repo": "owner/name"}'
`flue run` builds the project, invokes the workflow, and prints the structured
verdict as JSON.

## The Issue Health Check agent

Checks newly opened GitHub issues for actionability before a maintainer picks
up triage. It fetches the live issue, asks the `issue-health` skill for
structured analysis (`issueType`, `verdict`, hidden internal `score`,
`summary`, `missingInfo`, `suggestedLabels`, and `redactionWarning`), then
renders and posts one deterministic health-check/support comment.

### Use it in other repos

Consuming repos opt in with an `issues.opened` workflow that calls the composite
action. See [`actions/issue-health/README.md`](actions/issue-health/README.md)
and [`examples/issue-health-workflow.yml`](examples/issue-health-workflow.yml).

```yaml
# .github/workflows/issue-health.yml in the consuming repo
on:
issues:
types: [opened]
permissions:
contents: read
issues: write # required: the workflow comments and may apply labels
models: read # GitHub Models auth (no API key)
jobs:
issue-health:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: <owner>/agents/actions/issue-health@main
with:
issue-number: ${{ github.event.issue.number }}
```

v1 runs only for newly opened issues. It does not respond to edits or reopens,
create labels, edit issue bodies, close issues, or assign people. Labeling is
existing-only by default: the workflow discovers labels from the target repo at
runtime, filters model suggestions against those names, and applies only labels
that already exist there. Set `label-mode: off` to disable labeling.

The default `comment-mode` is `always`, so every opened issue gets one combined
health-check/support comment. Set `comment-mode: needs-improvement` to comment
only for `needs_info` / `not_actionable` results, or `comment-mode: off` to
skip comments. When comments are enabled, the footer links to
[GitHub Sponsors](https://github.com/sponsors/flipt-io) and
[Flipt Pro](https://docs.flipt.io/v2/pro).

### Per-repo overrides

Issue Health Check uses the same `.agents/` override convention as PR Review.
A consuming repo can add local prompts/skills/personas under `.agents/`, and the
action's `override-mode` controls whether those files `merge` with the central
defaults or `replace` them. Full details are in
[`actions/issue-health/README.md`](actions/issue-health/README.md).

### Run it locally

```bash
cp .env.example .env # GITHUB_MODELS_TOKEN (models:read) + GH_TOKEN for gh
pnpm install
pnpm issue-health -- --payload '{"issueNumber":123,"repo":"owner/name"}'
```

`pnpm issue-health` runs the `flue run issue-health` package script, fetches the
issue from `repo`, posts according to `ISSUE_HEALTH_COMMENT_MODE` (default
`always`), applies labels according to `ISSUE_HEALTH_LABEL_MODE` (default
`existing-only`), and prints the structured result as JSON.

## Adding an agent

Add `workflows/<name>.ts` — it shares everything above: the model providers in
Expand Down
104 changes: 104 additions & 0 deletions actions/issue-health/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Issue Health Check — composite action

Comment on newly opened issues with an actionability check, missing-information
checklist when needed, support links, and optional labels that already exist in
the target repository.

The model only returns structured analysis. The workflow code fetches the issue,
renders the comment, and applies labels deterministically.

## Use it in another repo

Add `.github/workflows/issue-health.yml` (see [`examples/issue-health-workflow.yml`](../../examples/issue-health-workflow.yml)):

```yaml
name: Issue Health Check

on:
issues:
types: [opened]

permissions:
contents: read
issues: write
models: read # default model is github/openai/gpt-4.1

jobs:
issue-health:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # lets the agent read this repo's .agents/ overrides
- uses: flipt-io/agents/actions/issue-health@main
with:
issue-number: ${{ github.event.issue.number }}
```

The defaults post one combined health-check/support comment and apply matching
labels only when those labels already exist in the target repository. The action
never creates labels, edits issue bodies, closes issues, assigns people, or
responds to issue edits/reopens in v1.

## Inputs

| Input | Required | Default | Description |
| --- | --- | --- | --- |
| `issue-number` | yes | — | Issue number to analyze. |
| `repo` | no | current repo | `owner/name` of the issue. |
| `model` | no | `github/openai/gpt-4.1` | Override the issue-health model. |
| `override-mode` | no | `merge` | `merge` or `replace` — how local `.agents/` overrides combine with defaults. |
| `github-token` | no | `github.token` | Token for `gh` and GitHub Models. Needs `issues: write`; the default GitHub model also needs `models: read`. |
| `comment-mode` | no | `always` | `always`, `needs-improvement`, or `off`. |
| `label-mode` | no | `existing-only` | `existing-only` or `off`. `existing-only` filters suggestions against labels already present in the target repo. |

Provider credentials (`ANTHROPIC_API_KEY`, `CLOUDFLARE_API_KEY`,
`CLOUDFLARE_ACCOUNT_ID`, …) are read from the workflow environment — pass them
via `env:` on the `uses:` step or at the job level rather than as action inputs.

## Per-repo overrides (`.agents/`)

A consuming repo can tailor the issue-health analysis by adding an `.agents/`
directory at its root. The action passes that directory to the workflow as
`ISSUE_HEALTH_TARGET_DIR`, and `override-mode` controls how those files combine
with central defaults:

- `merge` (default): central defaults apply first, then local files refine them.
- `replace`: local files replace central files for the kinds the repo provides.

## How it works

The composite action resolves this agents repo, installs dependencies, and runs:

```bash
pnpm exec flue run issue-health --payload '{"issueNumber": 123, "repo": "owner/name"}'
```

It sets the `ISSUE_HEALTH_*` environment variables consumed by
`workflows/issue-health.ts`:

- `ISSUE_HEALTH_AGENT_DIR`
- `ISSUE_HEALTH_TARGET_DIR`
- `ISSUE_HEALTH_OVERRIDE_MODE`
- `ISSUE_HEALTH_MODEL`
- `ISSUE_HEALTH_COMMENT_MODE`
- `ISSUE_HEALTH_LABEL_MODE`

GitHub IO is deterministic workflow code. The issue-health skill receives issue
context and target-repo label names, returns structured analysis, and cannot
post comments or apply labels on its own.

## Models

The default model is `github/openai/gpt-4.1` via GitHub Models, authenticated by
the built-in token when `models: read` is granted.

To use Anthropic or Cloudflare Workers AI, pass provider credentials with `env:`
and set `model`; drop `models: read` if you are not using a `github/*` model.

```yaml
- uses: flipt-io/agents/actions/issue-health@main
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
issue-number: ${{ github.event.issue.number }}
model: anthropic/claude-sonnet-4-6
```
100 changes: 100 additions & 0 deletions actions/issue-health/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: 'Issue Health Check'
description: 'Analyze newly opened GitHub issues for actionability, comment with guidance, and apply only existing target-repo labels.'
author: 'markphelps'

inputs:
issue-number:
description: 'The GitHub issue number to analyze.'
required: true
repo:
description: 'owner/name the issue belongs to. Defaults to the calling repository.'
required: false
default: ${{ github.repository }}
model:
description: 'Override the issue-health model (e.g. github/openai/gpt-4o, anthropic/claude-sonnet-4-6, or cloudflare-workers-ai/@cf/moonshotai/kimi-k2.6). Defaults to the agent default.'
required: false
default: ''
override-mode:
description: 'How a consuming repo .agents/ directory combines with the central defaults: "merge" (defaults + local) or "replace" (local only).'
required: false
default: 'merge'
github-token:
description: 'Token used by gh to read/comment on the issue, apply existing labels, and authenticate GitHub Models. Needs issues: write and models: read for the default github/* model.'
required: false
default: ${{ github.token }}
comment-mode:
description: 'When to post the health comment: "always", "needs-improvement", or "off".'
required: false
default: 'always'
label-mode:
description: 'How to apply labels: "existing-only" to apply only labels already present in the target repo, or "off".'
required: false
default: 'existing-only'

runs:
using: 'composite'
steps:
# `github.action_path` is the checked-out agent repo; the Flue project root
# is two levels up from this action. Resolve it to an absolute path once.
- name: Resolve agent project dir
id: agent
shell: bash
run: echo "dir=$(cd '${{ github.action_path }}/../..' && pwd)" >> "$GITHUB_OUTPUT"

- name: Validate issue-health inputs
shell: bash
env:
INPUT_OVERRIDE_MODE: ${{ inputs.override-mode }}
INPUT_COMMENT_MODE: ${{ inputs.comment-mode }}
INPUT_LABEL_MODE: ${{ inputs.label-mode }}
run: |
case "$INPUT_OVERRIDE_MODE" in merge|replace) ;; *) echo 'override-mode must be merge or replace' >&2; exit 1 ;; esac
case "$INPUT_COMMENT_MODE" in always|needs-improvement|off) ;; *) echo 'comment-mode must be always, needs-improvement, or off' >&2; exit 1 ;; esac
case "$INPUT_LABEL_MODE" in existing-only|off) ;; *) echo 'label-mode must be existing-only or off' >&2; exit 1 ;; esac

- uses: pnpm/action-setup@v4
with:
version: 11

- uses: actions/setup-node@v4
with:
node-version: 22
# No setup-node pnpm cache: when this action is consumed from another
# repo, the lockfile lives under _actions/ (outside the caller's
# workspace), which setup-node's cache globber can't resolve. The
# frozen install below works fine without it.

- name: Install agent dependencies
shell: bash
working-directory: ${{ steps.agent.outputs.dir }}
run: pnpm install --frozen-lockfile

- name: Run Issue Health Check
shell: bash
working-directory: ${{ steps.agent.outputs.dir }}
env:
GH_TOKEN: ${{ inputs.github-token }}
GITHUB_TOKEN: ${{ inputs.github-token }}
# Provider creds (ANTHROPIC_API_KEY, CLOUDFLARE_API_KEY,
# CLOUDFLARE_ACCOUNT_ID, …) are inherited from the caller's workflow
# env — set them with `env:` on the `uses:` step or at job level.
# See actions/issue-health/README.md → Models.
# Central skills/prompts live here; consumer overrides (if any) live in
# <their repo>/.agents. The workflow layers them per override-mode.
ISSUE_HEALTH_AGENT_DIR: ${{ steps.agent.outputs.dir }}
ISSUE_HEALTH_TARGET_DIR: ${{ github.workspace }}
ISSUE_HEALTH_OVERRIDE_MODE: ${{ inputs.override-mode }}
ISSUE_HEALTH_MODEL: ${{ inputs.model }}
ISSUE_HEALTH_COMMENT_MODE: ${{ inputs.comment-mode }}
ISSUE_HEALTH_LABEL_MODE: ${{ inputs.label-mode }}
ISSUE_NUMBER: ${{ inputs.issue-number }}
ISSUE_REPO: ${{ inputs.repo }}
run: |
payload=$(node -e '
const issueNumber = Number(process.env.ISSUE_NUMBER);
if (!Number.isInteger(issueNumber)) {
throw new Error("issue-number must be an integer");
}
process.stdout.write(JSON.stringify({ issueNumber, repo: process.env.ISSUE_REPO }));
')
pnpm exec flue run issue-health --payload "$payload"
Loading
Loading