name: goose Issue Solver on: issue_comment: types: [created] workflow_dispatch: inputs: issue_number: description: 'Issue number to solve' required: true type: string env: GOOSE_RECIPE: | version: "1.0.0" title: "Solve GitHub Issue" description: "Solve GitHub issue #${ISSUE_NUMBER}" extensions: - type: builtin name: developer - type: platform name: todo instructions: | Write code like a senior goose contributor. Principles: - Search first: `rg "keyword" -l` to find existing patterns. Copy them exactly. - Deletion is a feature: ask "can I delete code instead of adding?" - Complete changes: update ALL usages when changing a type. No exceptions. - When changing message types, update ALL provider format functions. - When changing server routes, run `just generate-openapi`. - Your context degrades. The TODO is your memory. Update it after each step. Types: - `PathBuf` for paths, never `String` - Enums for finite sets, never string comparisons - `Vec` not `Option>` - empty vec is the "none" case - Don't return `Result` if failure is meaningless - `#[serde(default)]` on new struct fields Patterns: - Early returns, not nested if-else - Extract to utility if code appears 3+ times - Scope locks tightly: extract data, release lock, then I/O - Log and continue for non-critical failures - Error messages must say HOW to fix, not just what's wrong - Use `#[cfg(target_os = "...")]` for platform-specific code Testing: - Bug fix: write failing test FIRST, then fix - Delete tests when deleting features - No tests for trivial code Don't: - Create abstractions unless required - Add backward compat shims - update callers - Write WHAT comments (keep WHY comments) - Add checks the OS will do anyway prompt: | Solve GitHub issue #${ISSUE_NUMBER}: ${ISSUE_TITLE} The issue is saved at /tmp/issue.json Trigger comment: ${TRIGGER_COMMENT} Write this to your TODO immediately and update as you progress: ## Phase 1: Understand - [ ] Read /tmp/issue.json - [ ] Write all requirements (explicit + implicit) to /tmp/requirements.md - [ ] Read AGENTS.md if it exists ## Phase 2: Research - [ ] `rg "keyword" -l` to find relevant files and similar patterns - [ ] `analyze path="crates/" focus="SymbolName"` to trace type/function usage - [ ] Read 2-3 similar implementations to copy their patterns - [ ] List all files that need changes in TODO ## Phase 3: Plan - [ ] Decide on implementation approach - [ ] If changing >5 files or public APIs, write plan to /tmp/plan.md first - [ ] Update TODO with specific changes to make ## Phase 4: Implement - [ ] Implement minimal fix per /tmp/requirements.md - [ ] Before adding anything, check: is it in the requirements? If not, don't. - [ ] No .github/, no lock files, no secrets ## Phase 5: Verify - [ ] source bin/activate-hermit - [ ] cargo check - [ ] cargo test (affected crates) - [ ] cargo fmt - [ ] cargo clippy --all-targets -- -D warnings - [ ] Fix failures, retry up to 3 times ## Phase 6: Confirm (MANDATORY) - [ ] Reread /tmp/issue.json - [ ] Reread /tmp/requirements.md - [ ] Confirm all requirements met, nothing extra added - [ ] Write a summary of changes to /tmp/issue_summary.txt The Phase 6 "Reread" steps MUST appear in your updated TODO. Skipping them is failure. Only write /tmp/issue_summary.txt if the fix is complete and verified. Run `git add` for any new files required for the fix. Do NOT commit. Leave changes in the working directory for the workflow to handle. permissions: contents: write pull-requests: write issues: write concurrency: group: goose-issue-${{ github.event.issue.number || github.event.inputs.issue_number }} cancel-in-progress: false jobs: solve-issue: if: | github.event_name == 'workflow_dispatch' || (!github.event.issue.pull_request && startsWith(github.event.comment.body, '/goose') && contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) runs-on: ubuntu-latest timeout-minutes: 30 container: image: ghcr.io/block/goose:latest options: --user root env: GOOSE_PROVIDER: ${{ vars.GOOSE_PROVIDER || 'anthropic' }} GOOSE_MODEL: ${{ vars.GOOSE_MODEL || 'claude-opus-4-5' }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} HOME: /tmp/goose-home steps: - name: Acknowledge trigger if: github.event_name == 'issue_comment' run: | curl -sL -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ "https://api.github.com/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \ -d '{"content":"eyes"}' - name: Checkout uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: main fetch-depth: 1 - name: Install tools run: | apt-get update apt-get install -y jq gettext curl build-essential pkg-config libssl-dev curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null apt-get update apt-get install -y gh - name: Get issue details (issue_comment) if: github.event_name == 'issue_comment' env: ISSUE_JSON: ${{ toJSON(github.event.issue) }} run: printenv ISSUE_JSON > /tmp/issue.json - name: Get issue details (workflow_dispatch) if: github.event_name == 'workflow_dispatch' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: gh api /repos/${{ github.repository }}/issues/${{ github.event.inputs.issue_number }} > /tmp/issue.json - name: Set issue outputs id: issue run: | echo "number=$(jq -r '.number' /tmp/issue.json)" >> $GITHUB_OUTPUT echo "title<> $GITHUB_OUTPUT jq -r '.title' /tmp/issue.json >> $GITHUB_OUTPUT echo "TITLE_EOF" >> $GITHUB_OUTPUT - name: Run goose id: goose env: ISSUE_NUMBER: ${{ steps.issue.outputs.number }} ISSUE_TITLE: ${{ steps.issue.outputs.title }} TRIGGER_COMMENT: ${{ github.event.comment.body || 'Triggered via workflow_dispatch' }} run: | mkdir -p $HOME/.local/share/goose/sessions mkdir -p $HOME/.config/goose git config --global --add safe.directory "$GITHUB_WORKSPACE" echo "$GOOSE_RECIPE" | envsubst '$ISSUE_NUMBER $ISSUE_TITLE $TRIGGER_COMMENT' > /tmp/recipe.yaml goose run --recipe /tmp/recipe.yaml if [ -n "$(git status --porcelain)" ] && [ -f /tmp/issue_summary.txt ]; then echo "has_changes=true" >> $GITHUB_OUTPUT echo "summary<> $GITHUB_OUTPUT cat /tmp/issue_summary.txt >> $GITHUB_OUTPUT echo "SUMMARY_EOF" >> $GITHUB_OUTPUT else echo "has_changes=false" >> $GITHUB_OUTPUT fi - name: Verify no workflow changes if: steps.goose.outputs.has_changes == 'true' run: | if git diff --name-only | grep -q "^\.github/"; then echo "::error::Changes to .github/ are not allowed" git checkout -- .github/ fi - name: Create Pull Request if: steps.goose.outputs.has_changes == 'true' uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "fix: ${{ steps.issue.outputs.title }}" title: "fix: ${{ steps.issue.outputs.title }}" branch: goose/issue-${{ steps.issue.outputs.number }} delete-branch: true draft: true labels: goose-generated body: | Closes #${{ steps.issue.outputs.number }} ## Summary ${{ steps.goose.outputs.summary || 'See commits for details.' }} --- *Generated by goose Issue Solver*