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' || (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)) # Minimal default GITHUB_TOKEN permissions 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' ## 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 --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<> $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' 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 --- Write everything using the todo tools. Then investigate and satisfy the request. Only if user requested to you to work explicitely, then use plan agent to plan, todo obsessivley 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