-
Notifications
You must be signed in to change notification settings - Fork 2
Add autosolve actions for automated issue resolution #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e323423
78d1dcc
b02a463
11b3ba2
3fe1297
a5502d2
31b7e13
599c7a2
c91aab2
45e2c78
226b573
6406103
96bc22e
01b51e1
0fa1d12
09ccb21
7d542de
34ec73c
4bc5058
de8989b
555a3c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| .PHONY: build test clean | ||
|
|
||
| # Local dev binary | ||
| build: | ||
| go build -o autosolve ./cmd/autosolve | ||
|
|
||
| test: | ||
| go test ./... -count=1 | ||
|
|
||
| clean: | ||
| rm -f autosolve |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| name: Autosolve Assess | ||
| description: Run Claude in read-only mode to assess whether a task is suitable for automated resolution. | ||
|
|
||
| inputs: | ||
| claude_cli_version: | ||
| description: "Claude CLI version to install (e.g. '2.1.79' or 'latest')." | ||
| required: false | ||
| default: "2.1.79" | ||
| system_prompt: | ||
| description: > | ||
| Trusted instructions for Claude describing the task to assess. | ||
| Do not embed untrusted user input (e.g., issue titles/bodies) here. | ||
| Pass user-supplied data via environment variables and list them in context_vars. | ||
| required: false | ||
| default: "" | ||
| skill: | ||
| description: Path to a skill/prompt file relative to the repo root. | ||
| required: false | ||
| default: "" | ||
| context_vars: | ||
| description: > | ||
| Comma-separated list of environment variable names to pass through to Claude. | ||
| Use this to provide untrusted user input (e.g., issue titles/bodies) safely. | ||
| Claude is automatically told which variables are available and instructed to | ||
| read them — you do not need to reference them in system_prompt. | ||
| Claude will only have access to these variables plus a baseline set of | ||
| system and authentication variables (PATH, HOME, etc.). | ||
| required: false | ||
| default: "" | ||
| assessment_criteria: | ||
| description: Custom criteria for the assessment. If not provided, uses default criteria. | ||
| required: false | ||
| default: "" | ||
| model: | ||
| description: Claude model ID. | ||
| required: false | ||
| default: "claude-opus-4-6" | ||
| blocked_paths: | ||
| description: > | ||
| Comma-separated path prefixes that cannot be modified. | ||
| .github/ is always blocked and cannot be removed. | ||
| required: false | ||
| default: ".github/workflows/" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this just be |
||
| verbose_logging: | ||
| description: > | ||
| Log full Claude output in collapsible groups in the step log. | ||
| Logs may contain source code snippets, environment variable | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it safe to log env variables? Would they ever contain sensitive info. (Same question about |
||
| values, or other repository content quoted in Claude's responses. | ||
| Security review output is never logged regardless of this setting. | ||
| required: false | ||
| default: "false" | ||
| working_directory: | ||
| description: Directory to run in (relative to workspace root). Defaults to workspace root. | ||
| required: false | ||
| default: "." | ||
|
|
||
| outputs: | ||
| assessment: | ||
| description: PROCEED or SKIP | ||
| value: ${{ steps.assess.outputs.assessment }} | ||
| summary: | ||
| description: Human-readable assessment reasoning. | ||
| value: ${{ steps.assess.outputs.summary }} | ||
| result: | ||
| description: Full Claude result text. | ||
| value: ${{ steps.assess.outputs.result }} | ||
|
|
||
| runs: | ||
| using: "composite" | ||
| steps: | ||
| - name: Set up Claude CLI | ||
| shell: bash | ||
| run: | | ||
| if command -v roachdev >/dev/null; then | ||
| printf '#!/bin/sh\nexec roachdev claude -- "$@"\n' > /usr/local/bin/claude | ||
| chmod +x /usr/local/bin/claude | ||
| echo "Claude CLI: using roachdev wrapper" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: would be nice to log the version used similar to the basic claude equivalent below. (Same for |
||
| else | ||
| curl --fail --silent --show-error --location https://claude.ai/install.sh | bash -s -- "$CLAUDE_CLI_VERSION" | ||
| echo "Claude CLI installed: $(claude --version)" | ||
| fi | ||
| env: | ||
| CLAUDE_CLI_VERSION: ${{ inputs.claude_cli_version }} | ||
|
|
||
| - name: Set up Go | ||
| uses: actions/setup-go@v6 | ||
| with: | ||
| go-version-file: ${{ github.action_path }}/../go.mod | ||
| cache: false | ||
|
|
||
| - name: Build autosolve | ||
| shell: bash | ||
| run: go build -trimpath -o "$RUNNER_TEMP/autosolve" ./cmd/autosolve | ||
| working-directory: ${{ github.action_path }}/.. | ||
|
Comment on lines
+85
to
+94
|
||
|
|
||
| - name: Run assessment | ||
| id: assess | ||
| shell: bash | ||
| working-directory: ${{ inputs.working_directory }} | ||
| run: $RUNNER_TEMP/autosolve assess | ||
| env: | ||
| INPUT_SYSTEM_PROMPT: ${{ inputs.system_prompt }} | ||
| INPUT_SKILL: ${{ inputs.skill }} | ||
| INPUT_CONTEXT_VARS: ${{ inputs.context_vars }} | ||
| INPUT_ASSESSMENT_CRITERIA: ${{ inputs.assessment_criteria }} | ||
| INPUT_MODEL: ${{ inputs.model }} | ||
| INPUT_BLOCKED_PATHS: ${{ inputs.blocked_paths }} | ||
| INPUT_VERBOSE_LOGGING: ${{ inputs.verbose_logging }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "os" | ||
| "os/signal" | ||
|
|
||
| "github.com/cockroachdb/actions/autosolve/internal/action" | ||
| "github.com/cockroachdb/actions/autosolve/internal/assess" | ||
| "github.com/cockroachdb/actions/autosolve/internal/claude" | ||
| "github.com/cockroachdb/actions/autosolve/internal/config" | ||
| "github.com/cockroachdb/actions/autosolve/internal/git" | ||
| "github.com/cockroachdb/actions/autosolve/internal/github" | ||
| "github.com/cockroachdb/actions/autosolve/internal/implement" | ||
| ) | ||
|
|
||
| const usage = `Usage: autosolve <command> | ||
|
|
||
| Commands: | ||
| assess Run assessment phase | ||
| implement Run implementation phase | ||
| ` | ||
|
|
||
| func main() { | ||
| ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) | ||
| defer cancel() | ||
|
|
||
| if len(os.Args) < 2 { | ||
| fatalf(usage) | ||
| } | ||
|
|
||
| var err error | ||
| switch os.Args[1] { | ||
| case "assess": | ||
| err = runAssess(ctx) | ||
| case "implement": | ||
| err = runImplement(ctx) | ||
| default: | ||
| fatalf("unknown command: %s\n\n%s", os.Args[1], usage) | ||
| } | ||
|
|
||
| if err != nil { | ||
| action.LogError(err.Error()) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
|
|
||
| func fatalf(format string, args ...any) { | ||
| fmt.Fprintf(os.Stderr, format+"\n", args...) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| func runAssess(ctx context.Context) error { | ||
| cfg, err := config.LoadAssessConfig() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if err := config.ValidateAuth(); err != nil { | ||
| return err | ||
| } | ||
| tmpDir, err := ensureTmpDir() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| return assess.Run(ctx, cfg, &claude.CLIRunner{}, tmpDir) | ||
| } | ||
|
|
||
| func runImplement(ctx context.Context) error { | ||
| cfg, err := config.LoadImplementConfig() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if err := config.ValidateAuth(); err != nil { | ||
| return err | ||
| } | ||
| tmpDir, err := ensureTmpDir() | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| gitClient := &git.CLIClient{} | ||
| ghClient := &github.GithubClient{Token: cfg.PRCreateToken} | ||
| return implement.Run(ctx, cfg, &claude.CLIRunner{}, ghClient, gitClient, tmpDir) | ||
| } | ||
|
|
||
| func ensureTmpDir() (string, error) { | ||
| dir := os.Getenv("AUTOSOLVE_TMPDIR") | ||
| if dir != "" { | ||
| return dir, nil | ||
| } | ||
| dir, err := os.MkdirTemp("", "autosolve_*") | ||
| if err != nil { | ||
| return "", fmt.Errorf("creating temp dir: %w", err) | ||
| } | ||
| os.Setenv("AUTOSOLVE_TMPDIR", dir) | ||
| return dir, nil | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,3 @@ | ||||||
| module github.com/cockroachdb/actions/autosolve | ||||||
|
|
||||||
| go 1.23.8 | ||||||
|
||||||
| go 1.23.8 | |
| go 1.23 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be moved to the top/unreleased section