Skip to content
Open
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
17 changes: 17 additions & 0 deletions .github/workflows/po-agent-resume.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: PO Agent Resume
on:
workflow_run:
workflows: ["CI"]
types: [completed]

permissions:
contents: read
pull-requests: read
actions: write

jobs:
resume:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./resume
51 changes: 51 additions & 0 deletions .github/workflows/po-agent.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: PO Agent
on:
issue_comment:
types: [created]
repository_dispatch:
types: [po-agent-resume]

permissions:
contents: write
pull-requests: write
issues: write
actions: write

jobs:
agent:
if: >
(github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '@po-agent') &&
(github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR'))
|| github.event_name == 'repository_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 60
concurrency:
group: po-agent-${{ github.event.issue.number || github.event.client_payload.pr_number || github.run_id }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v4

- name: Start Copilot API proxy
run: |
npm install -g copilot-api@latest
copilot-api start --github-token "$COPILOT_GITHUB_TOKEN" &
for i in $(seq 1 30); do
if curl -sf http://localhost:4141 > /dev/null 2>&1; then
echo "Copilot API proxy running (attempt $i)"
exit 0
fi
sleep 2
done
echo "Proxy failed to start after 60s"
exit 1
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_API_GITHUB_TOKEN }}

- uses: ./
with:
anthropic_api_key: "dummy"
anthropic_base_url: "http://localhost:4141"
max_budget_usd: "3.0"
63 changes: 63 additions & 0 deletions .po-agent/skills/test-skills/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
name: test-skills
description: Run the skill validation framework against all built-in and consumer skills.
disable-model-invocation: true
---

# Test Skills

Run this skill to validate that all skill definitions are correctly structured.

## Step 1: Validate Built-in Skills

```bash
bash scripts/validate-all-skills.sh skills/
```

All 11 built-in skills (including `rfs-proposal`) must pass with 0 errors.

## Step 2: Validate Consumer Skills (if any)

```bash
if [ -d .po-agent/skills ]; then
bash scripts/validate-all-skills.sh .po-agent/skills/
else
echo "No consumer skills found"
fi
```

## Step 3: Check Skill Routing Table

Verify that every skill in `skills/` has a corresponding entry in `prompts/core.md`:

```bash
for skill_dir in skills/*/; do
skill_name=$(basename "$skill_dir")
if grep -q "/$skill_name" prompts/core.md; then
echo "βœ… $skill_name β€” listed in core.md"
else
echo "⚠️ $skill_name β€” NOT in core.md routing table"
fi
done
```

## Step 4: Check for Orphaned Skill References

Verify that every skill referenced in `prompts/core.md` has an actual directory:

```bash
grep -oP '/[a-z][-a-z]+' prompts/core.md | sort -u | while read -r skill_ref; do
skill_name="${skill_ref#/}"
if [ -d "skills/$skill_name" ] || [ -d ".po-agent/skills/$skill_name" ]; then
echo "βœ… $skill_ref β€” exists"
else
echo "❌ $skill_ref β€” referenced but not found"
fi
done
```

## When to Use

- After adding, modifying, or removing a skill
- After modifying `prompts/core.md`
- Before releasing a new version
64 changes: 64 additions & 0 deletions .po-agent/skills/validate-action-yml/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
name: validate-action-yml
description: Validate the action.yml composite action after modifications. Checks YAML syntax, inputs/outputs, step structure.
disable-model-invocation: true
---

# Validate action.yml

Run this skill after modifying `action.yml` to ensure the file is structurally valid.

## Step 1: YAML Syntax Check

```bash
python3 -c "import yaml; yaml.safe_load(open('action.yml'))" && echo 'βœ… YAML valid' || echo '❌ YAML invalid'
```

## Step 2: Structure Validation

```bash
python3 -c "
import yaml
with open('action.yml') as f:
data = yaml.safe_load(f)

# Check required top-level keys
assert 'name' in data, 'Missing: name'
assert 'inputs' in data, 'Missing: inputs'
assert 'outputs' in data, 'Missing: outputs'
assert 'runs' in data, 'Missing: runs'
assert data['runs']['using'] == 'composite', 'Not a composite action'

# Check required inputs
required_inputs = ['anthropic_api_key']
for inp in required_inputs:
assert inp in data['inputs'], f'Missing required input: {inp}'

# Check outputs
assert 'response' in data['outputs'], 'Missing output: response'
assert 'cost_usd' in data['outputs'], 'Missing output: cost_usd'

# Count steps
steps = data['runs']['steps']
print(f'βœ… All checks passed ({len(steps)} steps, {len(data[\"inputs\"])} inputs)')
"
```

## Step 3: Check for Common Issues

- Ensure all `jq` calls have `2>/dev/null` or `|| true` for error handling
- Ensure all `grep -oP` calls have `|| true` to handle no-match exit code 1
- Ensure `GITHUB_OUTPUT` writes don't contain unescaped newlines in values
- Ensure all step IDs referenced in `steps.*.outputs.*` actually exist

## Step 4: Verify Skills Still Pass

```bash
bash scripts/validate-all-skills.sh
```

## When to Use

- After ANY edit to `action.yml`
- After merging upstream changes
- Before committing action.yml changes
101 changes: 101 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# PO Agent β€” AI-Powered Product Owner

## Project Overview

PO Agent is a **GitHub Actions composite action** that wraps Claude Code CLI to act as an AI Product Owner and Senior Developer. It manages the full lifecycle: requirements, investigation, implementation, CI, review, verification, and delivery.

## Architecture

```
action.yml ← Main composite action (all steps in one job)
prompts/
core.md ← Agent persona + skill routing table
source-github.md ← GitHub PR trigger behavior
source-github-issue.md← GitHub Issue trigger behavior
source-slack.md ← Slack trigger behavior
source-resume.md ← Auto-resume trigger behavior
source-azure-devops.md← Azure DevOps trigger behavior
rfs-detection.md ← Skill gap detection prompt
skills/ ← Built-in skills (SKILL.md files)
scripts/ ← Shell scripts linked to PATH at runtime
resume/action.yml ← Resume sub-action (triggers on CI completion)
webhooks/ ← Event relay functions (Slack, AzDo)
docs/ ← Architecture, quickstart, writing skills, RFS docs
```

## Key Files

- **`action.yml`** (~1500 lines): The core of the project. Contains ALL steps:
1. Source detection (github/github-issue/slack/resume/azure-devops/webhook/manual)
2. Secret export
3. Acknowledge receipt (reaction/comment per source)
4. Install Claude Code CLI
5. Setup Node.js + Playwright
6. Register MCP servers (GitHub, Playwright, optionally AzDo, Notion)
7. Link skills + scripts
8. Fetch context (PR details, issue details, AzDo work items, Slack threads)
9. Compose prompt + run Claude Code
10. Save persistent context + transcript
11. Upload artifacts
12. Post response to source
13. Job summary + cancellation handling

- **`resume/action.yml`**: Watches for CI completion via `workflow_run` event, checks labels, dispatches `po-agent-resume` repository_dispatch event.

## Inputs

| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `anthropic_api_key` | Yes | β€” | API key (or dummy if using proxy) |
| `max_budget_usd` | No | `5.0` | Max spend per session |
| `model` | No | `claude-sonnet-4-20250514` | Default model |
| `anthropic_base_url` | No | β€” | Custom API base URL (proxy support) |
| `timeout_minutes` | No | `0` | Timeout awareness |
| `dry_run` | No | `false` | Investigation only, no code changes |
| `investigation_model` | No | β€” | Model for read-only tasks |
| `implementation_model` | No | β€” | Model for code-writing tasks |
| `trigger_word` | No | `@po-agent` | Keyword to invoke agent |
| `secrets` | No | β€” | KEY=VALUE pairs as env vars |

## Skills System

Skills are markdown files (`SKILL.md`) with frontmatter, linked into `.claude/skills/` at runtime.

- **Built-in skills** (10 + rfs-proposal): `skills/` directory
- **Consumer overrides**: `.po-agent/skills/<name>/` overrides built-in skill of same name
- **Consumer extensions**: `.po-agent/skills/<name>/` for project-specific skills

### Validation

```bash
bash scripts/validate-skill.sh skills/bug-fix-workflow/
bash scripts/validate-all-skills.sh
```

## Label System

Labels on PRs control the auto-resume flow:
- `po-agent:waiting-ci` β€” waiting for CI
- `po-agent:waiting-human` β€” waiting for human input (`<!-- WAITING_FOR_HUMAN -->`)
- `po-agent:in-progress` β€” agent is running
- `po-agent:attempt-N` β€” tracks resume attempts
- `po-agent:failed` β€” max attempts exceeded

## RFS (Request for Skills)

The agent auto-detects skill gaps and files structured GitHub Issues with `rfs` + `rfs:proposed` labels. See `docs/rfs.md` for the full lifecycle.

## Testing

- Validate YAML: `python3 -c "import yaml; yaml.safe_load(open('action.yml'))"`
- Validate skills: `bash scripts/validate-all-skills.sh`
- Test in a consumer repo: point workflow at `Adrian62D/po-agent@main`

## Development Guidelines

- All shell code is in `action.yml` steps β€” there's no separate build step
- Use `|| true` after grep/commands that may return non-zero in `set -e` contexts
- Test YAML validity after every edit to action.yml
- MCP servers are registered at runtime, not bundled
- Prompts are composed from `core.md` + `source-*.md` + context + user message
- The `stream-json` output format is parsed for `"type":"result"` lines
Loading