diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d34785829b5..6e4475fa70c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -245,6 +245,9 @@ jobs:
HOME: ${{ runner.temp }}/lazycodex-published-smoke/home
CODEX_HOME: ${{ runner.temp }}/lazycodex-published-smoke/codex
CODEX_LOCAL_BIN_DIR: ${{ runner.temp }}/lazycodex-published-smoke/bin
+ OMO_CODEX_DISABLE_POSTHOG: "1"
+ OMO_DISABLE_POSTHOG: "1"
+ OMO_SEND_ANONYMOUS_TELEMETRY: "0"
run: |
set -euo pipefail
SMOKE_DIR=$(mktemp -d)
diff --git a/.github/workflows/sisyphus-agent.yml b/.github/workflows/sisyphus-agent.yml
index 3ca82f99151..a33f1ba3b01 100644
--- a/.github/workflows/sisyphus-agent.yml
+++ b/.github/workflows/sisyphus-agent.yml
@@ -1,565 +1,572 @@
-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
- env:
- GITHUB_TOKEN: ${{ secrets.GH_PAT }}
- run: |
- echo "$GITHUB_TOKEN" | 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: "1.3.12"
-
- - name: Cache Bun dependencies
- uses: actions/cache@v5
- 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 --frozen-lockfile
- 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 re-download 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 direct install..."
- bash <(curl -fsSL https://opencode.ai/install)
- fi
- else
- echo "Download corrupted, trying direct install..."
- bash <(curl -fsSL https://opencode.ai/install)
- fi
- fi
- opencode --version
-
- # Run local oh-my-opencode install (uses built dist)
- bun run dist/cli/index.js install --no-tui --claude=max20 --openai=no --gemini=no --copilot=no
-
- # Override plugin to use local file reference
- OPENCODE_JSON=~/.config/opencode/opencode.json
- REPO_PATH=$(pwd)
- jq --arg path "file://$REPO_PATH/packages/omo-opencode/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" '
- .model = "anthropic/claude-opus-4-5" |
- .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-6": {
- "id": "claude-sonnet-4-6-20250929",
- "name": "Sonnet 4.6",
- "limit": { "context": 200000, "output": 64000 }
- },
- "claude-sonnet-4-6-high": {
- "id": "claude-sonnet-4-6-20250929",
- "name": "Sonnet 4.6 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 }
- }
- }
- } |
- .provider["zai-coding-plan"] = {
- "name": "Z.AI Coding Plan",
- "npm": "@ai-sdk/openai-compatible",
- "options": {
- "baseURL": "https://api.z.ai/api/paas/v4"
- },
- "models": {
- "glm-4.7": {
- "id": "glm-4.7",
- "name": "GLM 4.7",
- "limit": { "context": 128000, "output": 16000 }
- },
- "glm-4.6v": {
- "id": "glm-4.6v",
- "name": "GLM 4.6 Vision",
- "limit": { "context": 128000, "output": 16000 }
- }
- }
- } |
- .provider.openai = {
- "name": "OpenAI",
- "npm": "@ai-sdk/openai",
- "models": {
- "gpt-5.5": {
- "id": "gpt-5.5",
- "name": "GPT-5.5",
- "limit": { "context": 128000, "output": 16000 }
- },
- "gpt-5.5-codex": {
- "id": "gpt-5.5-codex",
- "name": "GPT-5.5 Codex",
- "limit": { "context": 128000, "output": 32000 }
- }
- }
- }
- ' "$OPENCODE_JSON" > /tmp/oc.json && mv /tmp/oc.json "$OPENCODE_JSON"
-
- OMO_JSON=~/.config/opencode/oh-my-opencode.json
- PROMPT_APPEND=$(cat << 'PROMPT_EOF'
-
- [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.
-
-
-
- ---
-
-
- [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 --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"
-
- # Add categories configuration for unspecified-low to use GLM 4.7
- jq '.categories["unspecified-low"] = { "model": "zai-coding-plan/glm-4.7" }' "$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 and get title
- ISSUE_DATA=$(gh api "repos/$REPO/issues/${ISSUE_NUM}")
- TITLE=$(echo "$ISSUE_DATA" | jq -r '.title')
- if echo "$ISSUE_DATA" | jq -e '.pull_request' > /dev/null; then
- echo "type=pr"
- echo "number=${ISSUE_NUM}"
- else
- echo "type=issue"
- echo "number=${ISSUE_NUM}"
- fi
- echo "title=${TITLE}"
- fi
-
- echo "comment<> "$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 }}
- REPOSITORY: ${{ github.repository }}
- COMMENT_ID: ${{ steps.context.outputs.comment_id }}
- run: |
- gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \
- -X POST -f content="eyes" || true
-
- - name: Add working label
- if: steps.context.outputs.number != ''
- env:
- GITHUB_TOKEN: ${{ secrets.GH_PAT }}
- REPOSITORY: ${{ github.repository }}
- CONTEXT_TYPE: ${{ steps.context.outputs.type }}
- CONTEXT_NUMBER: ${{ steps.context.outputs.number }}
- run: |
- gh label create "sisyphus: working" \
- --repo "$REPOSITORY" \
- --color "fcf2e1" \
- --description "Sisyphus is currently working on this" \
- --force || true
-
- if [[ "$CONTEXT_TYPE" == "pr" ]]; then
- gh pr edit "$CONTEXT_NUMBER" \
- --repo "$REPOSITORY" \
- --add-label "sisyphus: working" || true
- else
- gh issue edit "$CONTEXT_NUMBER" \
- --repo "$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 }}
- CONTEXT_TITLE: ${{ steps.context.outputs.title }}
- 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
- - Title: TITLE_PLACEHOLDER
- - 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)
-
- ### [CODE RED] MANDATORY CONTEXT READING - ZERO EXCEPTIONS
-
- **YOU MUST READ ALL CONTENT. NOT SOME. NOT MOST. ALL.**
-
- 1. **READ FULL CONVERSATION** - Execute ALL commands below before ANY other action:
- - **Issues**: `gh issue view NUMBER_PLACEHOLDER --comments`
- - **PRs**: Use ALL THREE commands to get COMPLETE context:
- ```bash
- gh pr view NUMBER_PLACEHOLDER --comments
- gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/comments
- gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/reviews
- ```
-
- **WHAT TO EXTRACT FROM THE CONVERSATION:**
- - The ORIGINAL issue/PR description (first message) - this is often the TRUE requirement
- - ALL previous attempts and their outcomes
- - ALL decisions made and their reasoning
- - ALL feedback, criticism, and rejection reasons
- - ANY linked issues, PRs, or external references
- - The EXACT ask from the user who mentioned you
-
- **FAILURE TO READ EVERYTHING = GUARANTEED FAILURE**
- You WILL make wrong assumptions. You WILL repeat past mistakes. You WILL miss critical context.
-
- 2. **CREATE TODOS IMMEDIATELY**: Right after reading, create your todo list using todo tools.
- - First todo: "Summarize issue/PR context and requirements"
- - Break down ALL work into atomic, verifiable steps
- - **GIT WORKFLOW (MANDATORY for implementation tasks)**: ALWAYS include these final todos:
- - "Create new branch from origin/BRANCH_PLACEHOLDER (NEVER push directly to BRANCH_PLACEHOLDER)"
- - "Commit changes"
- - "Create PR to BRANCH_PLACEHOLDER branch"
- - 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//TITLE_PLACEHOLDER/$CONTEXT_TITLE}"
- 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 }}
- REPOSITORY: ${{ github.repository }}
- COMMENT_ID: ${{ steps.context.outputs.comment_id }}
- CONTEXT_NUMBER: ${{ steps.context.outputs.number }}
- CONTEXT_TYPE: ${{ steps.context.outputs.type }}
- run: |
- if [[ -n "$COMMENT_ID" ]]; then
- REACTION_ID=$(gh api "/repos/${REPOSITORY}/issues/comments/${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/${REPOSITORY}/reactions/${REACTION_ID}" || true
- fi
-
- gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \
- -X POST -f content="+1" || true
- fi
-
- if [[ -n "$CONTEXT_NUMBER" ]]; then
- if [[ "$CONTEXT_TYPE" == "pr" ]]; then
- gh pr edit "$CONTEXT_NUMBER" \
- --repo "$REPOSITORY" \
- --remove-label "sisyphus: working" || true
- else
- gh issue edit "$CONTEXT_NUMBER" \
- --repo "$REPOSITORY" \
- --remove-label "sisyphus: working" || true
- fi
- fi
-
- - name: Write job summary
- if: always()
- shell: bash
- env:
- JOB_SUMMARY_TITLE: Sisyphus agent run
- JOB_SUMMARY_STATUS: ${{ job.status }}
- JOB_SUMMARY_DETAILS: |
- - Builds local oh-my-opencode.
- - Configures OpenCode and runs the Sisyphus agent against the issue or PR context.
- - Pushes agent changes when the working branch is safe to push.
- JOB_SUMMARY_NEXT: Check setup/auth first, then the `Run oh-my-opencode` step and pushed branch output.
- run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh
+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
+
+ env:
+ DO_NOT_TRACK: "1"
+ POSTHOG_DISABLED: "1"
+ ANTHROPIC_DISABLE_TELEMETRY: "1"
+ OMO_DISABLE_POSTHOG: "1"
+ OMO_SEND_ANONYMOUS_TELEMETRY: "0"
+
+ 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
+ env:
+ GITHUB_TOKEN: ${{ secrets.GH_PAT }}
+ run: |
+ echo "$GITHUB_TOKEN" | gh auth login --with-token
+ gh auth status || echo "::warning::gh auth status blocked - sandboxed runner (Codex/restricted env); login token already set via --with-token"
+
+ - 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: "1.3.12"
+
+ - name: Cache Bun dependencies
+ uses: actions/cache@v5
+ 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 --frozen-lockfile
+ 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 re-download 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 direct install..."
+ bash <(curl -fsSL https://opencode.ai/install)
+ fi
+ else
+ echo "Download corrupted, trying direct install..."
+ bash <(curl -fsSL https://opencode.ai/install)
+ fi
+ fi
+ opencode --version
+
+ # Run local oh-my-opencode install (uses built dist)
+ bun run dist/cli/index.js install --no-tui --claude=max20 --openai=no --gemini=no --copilot=no
+
+ # Override plugin to use local file reference
+ OPENCODE_JSON=~/.config/opencode/opencode.json
+ REPO_PATH=$(pwd)
+ jq --arg path "file://$REPO_PATH/packages/omo-opencode/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" '
+ .model = "anthropic/claude-opus-4-5" |
+ .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-6": {
+ "id": "claude-sonnet-4-6-20250929",
+ "name": "Sonnet 4.6",
+ "limit": { "context": 200000, "output": 64000 }
+ },
+ "claude-sonnet-4-6-high": {
+ "id": "claude-sonnet-4-6-20250929",
+ "name": "Sonnet 4.6 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 }
+ }
+ }
+ } |
+ .provider["zai-coding-plan"] = {
+ "name": "Z.AI Coding Plan",
+ "npm": "@ai-sdk/openai-compatible",
+ "options": {
+ "baseURL": "https://api.z.ai/api/paas/v4"
+ },
+ "models": {
+ "glm-4.7": {
+ "id": "glm-4.7",
+ "name": "GLM 4.7",
+ "limit": { "context": 128000, "output": 16000 }
+ },
+ "glm-4.6v": {
+ "id": "glm-4.6v",
+ "name": "GLM 4.6 Vision",
+ "limit": { "context": 128000, "output": 16000 }
+ }
+ }
+ } |
+ .provider.openai = {
+ "name": "OpenAI",
+ "npm": "@ai-sdk/openai",
+ "models": {
+ "gpt-5.5": {
+ "id": "gpt-5.5",
+ "name": "GPT-5.5",
+ "limit": { "context": 128000, "output": 16000 }
+ },
+ "gpt-5.5-codex": {
+ "id": "gpt-5.5-codex",
+ "name": "GPT-5.5 Codex",
+ "limit": { "context": 128000, "output": 32000 }
+ }
+ }
+ }
+ ' "$OPENCODE_JSON" > /tmp/oc.json && mv /tmp/oc.json "$OPENCODE_JSON"
+
+ OMO_JSON=~/.config/opencode/oh-my-opencode.json
+ PROMPT_APPEND=$(cat << 'PROMPT_EOF'
+
+ [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.
+
+
+
+ ---
+
+
+ [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 --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"
+
+ # Add categories configuration for unspecified-low to use GLM 4.7
+ jq '.categories["unspecified-low"] = { "model": "zai-coding-plan/glm-4.7" }' "$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 and get title
+ ISSUE_DATA=$(gh api "repos/$REPO/issues/${ISSUE_NUM}")
+ TITLE=$(echo "$ISSUE_DATA" | jq -r '.title')
+ if echo "$ISSUE_DATA" | jq -e '.pull_request' > /dev/null; then
+ echo "type=pr"
+ echo "number=${ISSUE_NUM}"
+ else
+ echo "type=issue"
+ echo "number=${ISSUE_NUM}"
+ fi
+ echo "title=${TITLE}"
+ fi
+
+ echo "comment<> "$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 }}
+ REPOSITORY: ${{ github.repository }}
+ COMMENT_ID: ${{ steps.context.outputs.comment_id }}
+ run: |
+ gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \
+ -X POST -f content="eyes" || true
+
+ - name: Add working label
+ if: steps.context.outputs.number != ''
+ env:
+ GITHUB_TOKEN: ${{ secrets.GH_PAT }}
+ REPOSITORY: ${{ github.repository }}
+ CONTEXT_TYPE: ${{ steps.context.outputs.type }}
+ CONTEXT_NUMBER: ${{ steps.context.outputs.number }}
+ run: |
+ gh label create "sisyphus: working" \
+ --repo "$REPOSITORY" \
+ --color "fcf2e1" \
+ --description "Sisyphus is currently working on this" \
+ --force || true
+
+ if [[ "$CONTEXT_TYPE" == "pr" ]]; then
+ gh pr edit "$CONTEXT_NUMBER" \
+ --repo "$REPOSITORY" \
+ --add-label "sisyphus: working" || true
+ else
+ gh issue edit "$CONTEXT_NUMBER" \
+ --repo "$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 }}
+ CONTEXT_TITLE: ${{ steps.context.outputs.title }}
+ 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
+ - Title: TITLE_PLACEHOLDER
+ - 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)
+
+ ### [CODE RED] MANDATORY CONTEXT READING - ZERO EXCEPTIONS
+
+ **YOU MUST READ ALL CONTENT. NOT SOME. NOT MOST. ALL.**
+
+ 1. **READ FULL CONVERSATION** - Execute ALL commands below before ANY other action:
+ - **Issues**: `gh issue view NUMBER_PLACEHOLDER --comments`
+ - **PRs**: Use ALL THREE commands to get COMPLETE context:
+ ```bash
+ gh pr view NUMBER_PLACEHOLDER --comments
+ gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/comments
+ gh api repos/REPO_PLACEHOLDER/pulls/NUMBER_PLACEHOLDER/reviews
+ ```
+
+ **WHAT TO EXTRACT FROM THE CONVERSATION:**
+ - The ORIGINAL issue/PR description (first message) - this is often the TRUE requirement
+ - ALL previous attempts and their outcomes
+ - ALL decisions made and their reasoning
+ - ALL feedback, criticism, and rejection reasons
+ - ANY linked issues, PRs, or external references
+ - The EXACT ask from the user who mentioned you
+
+ **FAILURE TO READ EVERYTHING = GUARANTEED FAILURE**
+ You WILL make wrong assumptions. You WILL repeat past mistakes. You WILL miss critical context.
+
+ 2. **CREATE TODOS IMMEDIATELY**: Right after reading, create your todo list using todo tools.
+ - First todo: "Summarize issue/PR context and requirements"
+ - Break down ALL work into atomic, verifiable steps
+ - **GIT WORKFLOW (MANDATORY for implementation tasks)**: ALWAYS include these final todos:
+ - "Create new branch from origin/BRANCH_PLACEHOLDER (NEVER push directly to BRANCH_PLACEHOLDER)"
+ - "Commit changes"
+ - "Create PR to BRANCH_PLACEHOLDER branch"
+ - 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//TITLE_PLACEHOLDER/$CONTEXT_TITLE}"
+ 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 }}
+ REPOSITORY: ${{ github.repository }}
+ COMMENT_ID: ${{ steps.context.outputs.comment_id }}
+ CONTEXT_NUMBER: ${{ steps.context.outputs.number }}
+ CONTEXT_TYPE: ${{ steps.context.outputs.type }}
+ run: |
+ if [[ -n "$COMMENT_ID" ]]; then
+ REACTION_ID=$(gh api "/repos/${REPOSITORY}/issues/comments/${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/${REPOSITORY}/reactions/${REACTION_ID}" || true
+ fi
+
+ gh api "/repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \
+ -X POST -f content="+1" || true
+ fi
+
+ if [[ -n "$CONTEXT_NUMBER" ]]; then
+ if [[ "$CONTEXT_TYPE" == "pr" ]]; then
+ gh pr edit "$CONTEXT_NUMBER" \
+ --repo "$REPOSITORY" \
+ --remove-label "sisyphus: working" || true
+ else
+ gh issue edit "$CONTEXT_NUMBER" \
+ --repo "$REPOSITORY" \
+ --remove-label "sisyphus: working" || true
+ fi
+ fi
+
+ - name: Write job summary
+ if: always()
+ shell: bash
+ env:
+ JOB_SUMMARY_TITLE: Sisyphus agent run
+ JOB_SUMMARY_STATUS: ${{ job.status }}
+ JOB_SUMMARY_DETAILS: |
+ - Builds local oh-my-opencode.
+ - Configures OpenCode and runs the Sisyphus agent against the issue or PR context.
+ - Pushes agent changes when the working branch is safe to push.
+ JOB_SUMMARY_NEXT: Check setup/auth first, then the `Run oh-my-opencode` step and pushed branch output.
+ run: GITHUB_STEP_SUMMARY="$GITHUB_STEP_SUMMARY" bash .github/scripts/write-job-summary.sh
diff --git a/packages/team-core/src/team-state-store/locks.ts b/packages/team-core/src/team-state-store/locks.ts
index 796b3586999..2a40747f1d7 100644
--- a/packages/team-core/src/team-state-store/locks.ts
+++ b/packages/team-core/src/team-state-store/locks.ts
@@ -1,5 +1,5 @@
import { randomUUID } from "node:crypto"
-import { open, readFile, rename, rm, unlink } from "node:fs/promises"
+import { access, open, readFile, rename, rm, unlink } from "node:fs/promises"
import { tolerantFsync } from "../tolerant-fsync"
@@ -69,7 +69,17 @@ async function acquireLock(lockPath: string, ownerTag: string, staleAfterMs: num
return
} catch (error) {
const err = error as NodeJS.ErrnoException
- if (err.code !== "EEXIST") throw error
+ // EEXIST = file already exists (POSIX); EPERM on Windows (-4048) = file
+ // locked by another handle during exclusive-create — treat as contention
+ if (err.code !== "EEXIST" && err.code !== "EPERM") throw error
+
+ if (err.code === "EPERM") {
+ // EPERM can also mean a real permission error (unwritable directory).
+ // Distinguish: contention leaves the lock file present; a directory
+ // permission error leaves no file. Throw immediately if absent.
+ const lockExists = await access(lockPath).then(() => true, () => false)
+ if (!lockExists) throw error
+ }
if (await detectStaleLock(lockPath, staleAfterMs)) {
await reapStaleLock(lockPath)
diff --git a/script/ci-job-summary-workflow.test.ts b/script/ci-job-summary-workflow.test.ts
index e01df5443f9..4fb5ca16de7 100644
--- a/script/ci-job-summary-workflow.test.ts
+++ b/script/ci-job-summary-workflow.test.ts
@@ -43,6 +43,7 @@ const workflowExpectations = [
],
},
{ path: ".github/workflows/refresh-model-capabilities.yml", jobs: ["refresh"] },
+ { path: ".github/workflows/network-firewall-guard.yml", jobs: ["guard"] },
{ path: ".github/workflows/sisyphus-agent.yml", jobs: ["agent"] },
{ path: ".github/workflows/web-ci.yml", jobs: ["format-lint-typecheck-build"] },
{ path: ".github/workflows/web-deploy.yml", jobs: ["deploy"] },