- Add explicit `github.event_name == 'issue_comment'` check - Add null coalescing (`|| ''`) for safe property access - Use `>-` folded block scalar for better YAML parsing 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
472 lines
19 KiB
YAML
472 lines
19 KiB
YAML
name: Sisyphus Agent
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
prompt:
|
|
description: "Custom prompt"
|
|
required: false
|
|
# Only issue_comment works for fork PRs (secrets available)
|
|
# pull_request_review/pull_request_review_comment do NOT get secrets for fork PRs
|
|
issue_comment:
|
|
types: [created]
|
|
|
|
jobs:
|
|
agent:
|
|
runs-on: ubuntu-latest
|
|
# @sisyphus-dev-ai mention only (maintainers, exclude self)
|
|
if: >-
|
|
github.event_name == 'workflow_dispatch' ||
|
|
(github.event_name == 'issue_comment' &&
|
|
contains(github.event.comment.body || '', '@sisyphus-dev-ai') &&
|
|
(github.event.comment.user.login || '') != 'sisyphus-dev-ai' &&
|
|
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || ''))
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
steps:
|
|
# Checkout with sisyphus-dev-ai's PAT
|
|
- uses: actions/checkout@v5
|
|
with:
|
|
token: ${{ secrets.GH_PAT }}
|
|
fetch-depth: 0
|
|
|
|
# Git config - commits as sisyphus-dev-ai
|
|
- name: Configure Git as sisyphus-dev-ai
|
|
run: |
|
|
git config user.name "sisyphus-dev-ai"
|
|
git config user.email "sisyphus-dev-ai@users.noreply.github.com"
|
|
|
|
# gh CLI auth as sisyphus-dev-ai
|
|
- name: Authenticate gh CLI as sisyphus-dev-ai
|
|
run: |
|
|
echo "${{ secrets.GH_PAT }}" | gh auth login --with-token
|
|
gh auth status
|
|
|
|
- name: Ensure tmux is available (Linux)
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
set -euo pipefail
|
|
if ! command -v tmux >/dev/null 2>&1; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends tmux
|
|
fi
|
|
tmux -V
|
|
|
|
- name: Setup Bun
|
|
uses: oven-sh/setup-bun@v2
|
|
with:
|
|
bun-version: latest
|
|
|
|
- name: Cache Bun dependencies
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.bun/install/cache
|
|
node_modules
|
|
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-bun-
|
|
|
|
# Build local oh-my-opencode
|
|
- name: Build oh-my-opencode
|
|
run: |
|
|
bun install
|
|
bun run build
|
|
|
|
# Install OpenCode + configure local plugin + auth in single step
|
|
- name: Setup OpenCode with oh-my-opencode
|
|
env:
|
|
OPENCODE_AUTH_JSON: ${{ secrets.OPENCODE_AUTH_JSON }}
|
|
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
run: |
|
|
export PATH="$HOME/.opencode/bin:$PATH"
|
|
|
|
# Install OpenCode (skip if cached)
|
|
if ! command -v opencode &>/dev/null; then
|
|
echo "Installing OpenCode..."
|
|
curl -fsSL https://opencode.ai/install -o /tmp/opencode-install.sh
|
|
|
|
# Try default installer first, fallback to pinned version if it fails
|
|
if file /tmp/opencode-install.sh | grep -q "shell script\|text"; then
|
|
if ! bash /tmp/opencode-install.sh 2>&1; then
|
|
echo "Default installer failed, trying with pinned version..."
|
|
bash /tmp/opencode-install.sh --version 1.0.204
|
|
fi
|
|
else
|
|
echo "Download corrupted, trying direct install with pinned version..."
|
|
bash <(curl -fsSL https://opencode.ai/install) --version 1.0.204
|
|
fi
|
|
fi
|
|
opencode --version
|
|
|
|
# Run local oh-my-opencode install (uses built dist)
|
|
bun run dist/cli/index.js install --no-tui --claude=max20 --chatgpt=no --gemini=no
|
|
|
|
# Override plugin to use local file reference
|
|
OPENCODE_JSON=~/.config/opencode/opencode.json
|
|
REPO_PATH=$(pwd)
|
|
jq --arg path "file://$REPO_PATH/src/index.ts" '
|
|
.plugin = [.plugin[] | select(. != "oh-my-opencode")] + [$path]
|
|
' "$OPENCODE_JSON" > /tmp/oc.json && mv /tmp/oc.json "$OPENCODE_JSON"
|
|
|
|
OPENCODE_JSON=~/.config/opencode/opencode.json
|
|
jq --arg baseURL "$ANTHROPIC_BASE_URL" --arg apiKey "$ANTHROPIC_API_KEY" '
|
|
.provider.anthropic = {
|
|
"name": "Anthropic",
|
|
"npm": "@ai-sdk/anthropic",
|
|
"options": {
|
|
"baseURL": $baseURL,
|
|
"apiKey": $apiKey
|
|
},
|
|
"models": {
|
|
"claude-opus-4-5": {
|
|
"id": "claude-opus-4-5-20251101",
|
|
"name": "Opus 4.5",
|
|
"limit": { "context": 190000, "output": 64000 },
|
|
"options": { "effort": "high" }
|
|
},
|
|
"claude-opus-4-5-high": {
|
|
"id": "claude-opus-4-5-20251101",
|
|
"name": "Opus 4.5 High",
|
|
"limit": { "context": 190000, "output": 128000 },
|
|
"options": { "effort": "high", "thinking": { "type": "enabled", "budgetTokens": 64000 } }
|
|
},
|
|
"claude-sonnet-4-5": {
|
|
"id": "claude-sonnet-4-5-20250929",
|
|
"name": "Sonnet 4.5",
|
|
"limit": { "context": 200000, "output": 64000 }
|
|
},
|
|
"claude-sonnet-4-5-high": {
|
|
"id": "claude-sonnet-4-5-20250929",
|
|
"name": "Sonnet 4.5 High",
|
|
"limit": { "context": 200000, "output": 128000 },
|
|
"options": { "thinking": { "type": "enabled", "budgetTokens": 64000 } }
|
|
},
|
|
"claude-haiku-4-5": {
|
|
"id": "claude-haiku-4-5-20251001",
|
|
"name": "Haiku 4.5",
|
|
"limit": { "context": 200000, "output": 64000 }
|
|
}
|
|
}
|
|
}
|
|
' "$OPENCODE_JSON" > /tmp/oc.json && mv /tmp/oc.json "$OPENCODE_JSON"
|
|
|
|
OMO_JSON=~/.config/opencode/oh-my-opencode.json
|
|
PROMPT_APPEND=$(cat << 'PROMPT_EOF'
|
|
<ultrawork-mode>
|
|
[CODE RED] Maximum precision required. Ultrathink before acting.
|
|
|
|
YOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.
|
|
TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
|
|
|
## AGENT UTILIZATION PRINCIPLES (by capability, not by name)
|
|
- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS for file patterns, internal implementations, project structure
|
|
- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS for API references, examples, external library docs
|
|
- **Planning & Strategy**: For implementation tasks, spawn a dedicated planning agent for work breakdown (not needed for simple questions/investigations)
|
|
- **High-IQ Reasoning**: Leverage specialized agents for architecture decisions, code review, strategic planning
|
|
- **Frontend/UI Tasks**: Delegate to UI-specialized agents for design and implementation
|
|
|
|
## EXECUTION RULES
|
|
- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each.
|
|
- **PARALLEL**: Fire independent agent calls simultaneously via background_task - NEVER wait sequentially.
|
|
- **BACKGROUND FIRST**: Use background_task for exploration/research agents (10+ concurrent if needed).
|
|
- **VERIFY**: Re-read request after completion. Check ALL requirements met before reporting done.
|
|
- **DELEGATE**: Don't do everything yourself - orchestrate specialized agents for their strengths.
|
|
|
|
## WORKFLOW
|
|
1. Analyze the request and identify required capabilities
|
|
2. Spawn exploration/librarian agents via background_task in PARALLEL (10+ if needed)
|
|
3. Always Use Plan agent with gathered context to create detailed work breakdown
|
|
4. Execute with continuous verification against original requirements
|
|
|
|
## TDD (if test infrastructure exists)
|
|
|
|
1. Write spec (requirements)
|
|
2. Write tests (failing)
|
|
3. RED: tests fail
|
|
4. Implement minimal code
|
|
5. GREEN: tests pass
|
|
6. Refactor if needed (must stay green)
|
|
7. Next feature, repeat
|
|
|
|
## ZERO TOLERANCE FAILURES
|
|
- **NO Scope Reduction**: Never make "demo", "skeleton", "simplified", "basic" versions - deliver FULL implementation
|
|
- **NO MockUp Work**: When user asked you to do "port A", you must "port A", fully, 100%. No Extra feature, No reduced feature, no mock data, fully working 100% port.
|
|
- **NO Partial Completion**: Never stop at 60-80% saying "you can extend this..." - finish 100%
|
|
- **NO Assumed Shortcuts**: Never skip requirements you deem "optional" or "can be added later"
|
|
- **NO Premature Stopping**: Never declare done until ALL TODOs are completed and verified
|
|
- **NO TEST DELETION**: Never delete or skip failing tests to make the build pass. Fix the code, not the tests.
|
|
|
|
THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTING POINT.
|
|
|
|
</ultrawork-mode>
|
|
|
|
---
|
|
|
|
|
|
[analyze-mode]
|
|
ANALYSIS MODE. Gather context before diving deep:
|
|
|
|
CONTEXT GATHERING (parallel):
|
|
- 1-2 explore agents (codebase patterns, implementations)
|
|
- 1-2 librarian agents (if external library involved)
|
|
- Direct tools: Grep, AST-grep, LSP for targeted searches
|
|
|
|
IF COMPLEX (architecture, multi-system, debugging after 2+ failures):
|
|
- Consult oracle for strategic guidance
|
|
|
|
SYNTHESIZE findings before proceeding.
|
|
|
|
---
|
|
|
|
## GitHub Actions Environment
|
|
|
|
You are `sisyphus-dev-ai` in GitHub Actions.
|
|
|
|
### CRITICAL: GitHub Comments = Your ONLY Output
|
|
|
|
User CANNOT see console. Post everything via `gh issue comment` or `gh pr comment`.
|
|
|
|
### Comment Formatting (CRITICAL)
|
|
|
|
**ALWAYS use heredoc syntax for comments containing code references, backticks, or multiline content:**
|
|
|
|
```bash
|
|
gh issue comment <number> --body "$(cat <<'EOF'
|
|
Your comment with `backticks` and code references preserved here.
|
|
Multiple lines work perfectly.
|
|
EOF
|
|
)"
|
|
```
|
|
|
|
**NEVER use direct quotes with backticks** (shell will interpret them as command substitution):
|
|
```bash
|
|
# WRONG - backticks disappear:
|
|
gh issue comment 123 --body "text with `code`"
|
|
|
|
# CORRECT - backticks preserved:
|
|
gh issue comment 123 --body "$(cat <<'EOF'
|
|
text with `code`
|
|
EOF
|
|
)"
|
|
```
|
|
|
|
### GitHub Markdown Rules (MUST FOLLOW)
|
|
|
|
**Code blocks MUST have EXACTLY 3 backticks and language identifier:**
|
|
- CORRECT: ` ```bash ` ... ` ``` `
|
|
- WRONG: ` ``` ` (no language), ` ```` ` (4 backticks), ` `` ` (2 backticks)
|
|
|
|
**Every opening ` ``` ` MUST have a closing ` ``` ` on its own line:**
|
|
```
|
|
```bash
|
|
code here
|
|
```
|
|
```
|
|
|
|
**NO trailing backticks or spaces after closing ` ``` `**
|
|
|
|
**For inline code, use SINGLE backticks:** `code` not ```code```
|
|
|
|
**Lists inside code blocks break rendering - avoid them or use plain text**
|
|
|
|
### Rules
|
|
- EVERY response = GitHub comment (use heredoc for proper escaping)
|
|
- Code changes = PR (never push main/master)
|
|
- Setup: bun install first
|
|
- Acknowledge immediately, report when done
|
|
|
|
### Git Config
|
|
- user.name: sisyphus-dev-ai
|
|
- user.email: sisyphus-dev-ai@users.noreply.github.com
|
|
PROMPT_EOF
|
|
)
|
|
jq --arg append "$PROMPT_APPEND" '.agents.Sisyphus.prompt_append = $append' "$OMO_JSON" > /tmp/omo.json && mv /tmp/omo.json "$OMO_JSON"
|
|
|
|
mkdir -p ~/.local/share/opencode
|
|
echo "$OPENCODE_AUTH_JSON" > ~/.local/share/opencode/auth.json
|
|
chmod 600 ~/.local/share/opencode/auth.json
|
|
|
|
cat "$OPENCODE_JSON"
|
|
|
|
# Collect context
|
|
- name: Collect Context
|
|
id: context
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
|
|
COMMENT_ID_VAL: ${{ github.event.comment.id }}
|
|
REPO: ${{ github.repository }}
|
|
run: |
|
|
if [[ "$EVENT_NAME" == "issue_comment" ]]; then
|
|
ISSUE_NUM="$ISSUE_NUMBER"
|
|
AUTHOR="$COMMENT_AUTHOR"
|
|
COMMENT_ID="$COMMENT_ID_VAL"
|
|
|
|
# Check if PR or Issue
|
|
if gh api "repos/$REPO/issues/${ISSUE_NUM}" | jq -e '.pull_request' > /dev/null; then
|
|
echo "type=pr" >> $GITHUB_OUTPUT
|
|
echo "number=${ISSUE_NUM}" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "type=issue" >> $GITHUB_OUTPUT
|
|
echo "number=${ISSUE_NUM}" >> $GITHUB_OUTPUT
|
|
fi
|
|
fi
|
|
|
|
echo "comment<<EOF" >> $GITHUB_OUTPUT
|
|
echo "$COMMENT_BODY" >> $GITHUB_OUTPUT
|
|
echo "EOF" >> $GITHUB_OUTPUT
|
|
echo "author=$AUTHOR" >> $GITHUB_OUTPUT
|
|
echo "comment_id=$COMMENT_ID" >> $GITHUB_OUTPUT
|
|
|
|
# Add :eyes: reaction (as sisyphus-dev-ai)
|
|
- name: Add eyes reaction
|
|
if: steps.context.outputs.comment_id != ''
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
|
run: |
|
|
gh api "/repos/${{ github.repository }}/issues/comments/${{ steps.context.outputs.comment_id }}/reactions" \
|
|
-X POST -f content="eyes" || true
|
|
|
|
- name: Add working label
|
|
if: steps.context.outputs.number != ''
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
|
run: |
|
|
gh label create "sisyphus: working" \
|
|
--repo "${{ github.repository }}" \
|
|
--color "fcf2e1" \
|
|
--description "Sisyphus is currently working on this" \
|
|
--force || true
|
|
|
|
if [[ "${{ steps.context.outputs.type }}" == "pr" ]]; then
|
|
gh pr edit "${{ steps.context.outputs.number }}" \
|
|
--repo "${{ github.repository }}" \
|
|
--add-label "sisyphus: working" || true
|
|
else
|
|
gh issue edit "${{ steps.context.outputs.number }}" \
|
|
--repo "${{ github.repository }}" \
|
|
--add-label "sisyphus: working" || true
|
|
fi
|
|
|
|
- name: Run oh-my-opencode
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
|
USER_COMMENT: ${{ steps.context.outputs.comment }}
|
|
COMMENT_AUTHOR: ${{ steps.context.outputs.author }}
|
|
CONTEXT_TYPE: ${{ steps.context.outputs.type }}
|
|
CONTEXT_NUMBER: ${{ steps.context.outputs.number }}
|
|
REPO_NAME: ${{ github.repository }}
|
|
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
|
run: |
|
|
export PATH="$HOME/.opencode/bin:$PATH"
|
|
|
|
PROMPT=$(cat <<'PROMPT_EOF'
|
|
[analyze-mode]
|
|
ANALYSIS MODE. Gather context before diving deep:
|
|
|
|
CONTEXT GATHERING (parallel):
|
|
- 1-2 explore agents (codebase patterns, implementations)
|
|
- 1-2 librarian agents (if external library involved)
|
|
- Direct tools: Grep, AST-grep, LSP for targeted searches
|
|
|
|
IF COMPLEX (architecture, multi-system, debugging after 2+ failures):
|
|
- Consult oracle for strategic guidance
|
|
|
|
SYNTHESIZE findings before proceeding.
|
|
|
|
---
|
|
|
|
Your username is @sisyphus-dev-ai, mentioned by @AUTHOR_PLACEHOLDER in REPO_PLACEHOLDER.
|
|
|
|
## Context
|
|
- Type: TYPE_PLACEHOLDER
|
|
- Number: #NUMBER_PLACEHOLDER
|
|
- Repository: REPO_PLACEHOLDER
|
|
- Default Branch: BRANCH_PLACEHOLDER
|
|
|
|
## User's Request
|
|
COMMENT_PLACEHOLDER
|
|
|
|
---
|
|
|
|
## CRITICAL: First Steps (MUST DO BEFORE ANYTHING ELSE)
|
|
|
|
1. **READ FULL CONVERSATION**: Before doing ANYTHING, read the ENTIRE conversation history:
|
|
- **Issues**: `gh issue view NUMBER_PLACEHOLDER --comments`
|
|
- **PRs**: Use ALL THREE commands to get complete context:
|
|
- `gh pr view NUMBER_PLACEHOLDER --comments` (issue-style comments)
|
|
- `gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/comments` (inline review comments)
|
|
- `gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/reviews` (review bodies)
|
|
Understand the full context - previous discussions, decisions, and what has already been tried.
|
|
|
|
2. **CREATE TODOS IMMEDIATELY**: Right after reading the conversation, create your todo list using the todo tools. Plan everything before starting any work.
|
|
|
|
---
|
|
|
|
|
|
Plan everything using todo tools.
|
|
Then investigate and satisfy the request. Only if user requested to you to work explicitly, then use plan agent to plan, todo obsessively then create a PR to `BRANCH_PLACEHOLDER` branch.
|
|
When done, report the result to the issue/PR with `gh issue comment NUMBER_PLACEHOLDER` or `gh pr comment NUMBER_PLACEHOLDER`.
|
|
PROMPT_EOF
|
|
)
|
|
|
|
PROMPT="${PROMPT//AUTHOR_PLACEHOLDER/$COMMENT_AUTHOR}"
|
|
PROMPT="${PROMPT//REPO_PLACEHOLDER/$REPO_NAME}"
|
|
PROMPT="${PROMPT//TYPE_PLACEHOLDER/$CONTEXT_TYPE}"
|
|
PROMPT="${PROMPT//NUMBER_PLACEHOLDER/$CONTEXT_NUMBER}"
|
|
PROMPT="${PROMPT//BRANCH_PLACEHOLDER/$DEFAULT_BRANCH}"
|
|
PROMPT="${PROMPT//COMMENT_PLACEHOLDER/$USER_COMMENT}"
|
|
|
|
stdbuf -oL -eL bun run dist/cli/index.js run "$PROMPT"
|
|
|
|
# Push changes (as sisyphus-dev-ai)
|
|
- name: Push changes
|
|
if: always()
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
|
run: |
|
|
if [[ -n "$(git status --porcelain)" ]]; then
|
|
git add -A
|
|
git commit -m "chore: changes by sisyphus-dev-ai" || true
|
|
fi
|
|
|
|
BRANCH=$(git branch --show-current)
|
|
if [[ "$BRANCH" != "main" && "$BRANCH" != "master" ]]; then
|
|
git push origin "$BRANCH" || true
|
|
fi
|
|
|
|
- name: Update reaction and remove label
|
|
if: always()
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
|
run: |
|
|
if [[ -n "${{ steps.context.outputs.comment_id }}" ]]; then
|
|
REACTION_ID=$(gh api "/repos/${{ github.repository }}/issues/comments/${{ steps.context.outputs.comment_id }}/reactions" \
|
|
--jq '.[] | select(.content == "eyes" and .user.login == "sisyphus-dev-ai") | .id' | head -1)
|
|
if [[ -n "$REACTION_ID" ]]; then
|
|
gh api -X DELETE "/repos/${{ github.repository }}/reactions/${REACTION_ID}" || true
|
|
fi
|
|
|
|
gh api "/repos/${{ github.repository }}/issues/comments/${{ steps.context.outputs.comment_id }}/reactions" \
|
|
-X POST -f content="+1" || true
|
|
fi
|
|
|
|
if [[ -n "${{ steps.context.outputs.number }}" ]]; then
|
|
if [[ "${{ steps.context.outputs.type }}" == "pr" ]]; then
|
|
gh pr edit "${{ steps.context.outputs.number }}" \
|
|
--repo "${{ github.repository }}" \
|
|
--remove-label "sisyphus: working" || true
|
|
else
|
|
gh issue edit "${{ steps.context.outputs.number }}" \
|
|
--repo "${{ github.repository }}" \
|
|
--remove-label "sisyphus: working" || true
|
|
fi
|
|
fi
|