Skip to content

Commit d06e466

Browse files
Add autosolve actions for automated issue resolution
- autosolve/assess: evaluate tasks for automated resolution suitability using Claude in read-only mode. - autosolve/implement: autonomously implement solutions, validate security, push to fork, and create PRs using Claude. Includes AI security review, token usage tracking, and per-file batched diff analysis. - Prefers roachdev wrapper for Claude CLI when available, falls back to native installer. - Precompiled binary with shared source_hash.sh for staleness checks. Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
1 parent e1cbdd6 commit d06e466

27 files changed

Lines changed: 3719 additions & 1 deletion

.github/workflows/test.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@ jobs:
88
runs-on: ubuntu-latest
99
steps:
1010
- uses: actions/checkout@v5
11-
- run: ./test.sh
11+
- uses: actions/setup-go@v6
12+
with:
13+
go-version-file: autosolve/go.mod
14+
- name: Run shell tests
15+
run: ./test.sh
16+
- name: Run Go tests
17+
run: cd autosolve && go test ./... -count=1

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ Breaking changes are prefixed with "Breaking Change: ".
1010

1111
### Added
1212

13+
- `autosolve/assess` action: evaluate tasks for automated resolution suitability
14+
using Claude in read-only mode.
15+
- `autosolve/implement` action: autonomously implement solutions, validate
16+
security, push to fork, and create PRs using Claude. Includes AI security
17+
review, token usage tracking, and per-file batched diff analysis.
1318
- `get-workflow-ref` action: resolve the ref a caller used to invoke a reusable
1419
workflow by parsing the caller's workflow file — no API calls or extra
1520
permissions needed.

autosolve/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.PHONY: build test clean
2+
3+
# Local dev binary
4+
build:
5+
go build -o autosolve ./cmd/autosolve
6+
7+
test:
8+
go test ./... -count=1
9+
10+
clean:
11+
rm -f autosolve

autosolve/assess/action.yml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: Autosolve Assess
2+
description: Run Claude in read-only mode to assess whether a task is suitable for automated resolution.
3+
4+
inputs:
5+
claude_cli_version:
6+
description: "Claude CLI version to install (e.g. '2.1.79' or 'latest')."
7+
required: false
8+
default: "2.1.79"
9+
prompt:
10+
description: The task to assess. Plain text instructions describing what needs to be done.
11+
required: false
12+
default: ""
13+
skill:
14+
description: Path to a skill/prompt file relative to the repo root.
15+
required: false
16+
default: ""
17+
additional_instructions:
18+
description: Extra context appended after the task prompt but before the assessment footer.
19+
required: false
20+
default: ""
21+
assessment_criteria:
22+
description: Custom criteria for the assessment. If not provided, uses default criteria.
23+
required: false
24+
default: ""
25+
model:
26+
description: Claude model ID.
27+
required: false
28+
default: "claude-opus-4-6"
29+
blocked_paths:
30+
description: Comma-separated path prefixes that cannot be modified (injected into security preamble).
31+
required: false
32+
default: ".github/workflows/"
33+
working_directory:
34+
description: Directory to run in (relative to workspace root). Defaults to workspace root.
35+
required: false
36+
default: "."
37+
38+
outputs:
39+
assessment:
40+
description: PROCEED or SKIP
41+
value: ${{ steps.assess.outputs.assessment }}
42+
summary:
43+
description: Human-readable assessment reasoning.
44+
value: ${{ steps.assess.outputs.summary }}
45+
result:
46+
description: Full Claude result text.
47+
value: ${{ steps.assess.outputs.result }}
48+
49+
runs:
50+
using: "composite"
51+
steps:
52+
- name: Set up Claude CLI
53+
shell: bash
54+
run: |
55+
if command -v roachdev >/dev/null; then
56+
printf '#!/bin/sh\nexec roachdev claude -- "$@"\n' > /usr/local/bin/claude
57+
chmod +x /usr/local/bin/claude
58+
echo "Claude CLI: using roachdev wrapper"
59+
else
60+
curl --fail --silent --show-error --location https://claude.ai/install.sh | bash -s -- "$CLAUDE_CLI_VERSION"
61+
echo "Claude CLI installed: $(claude --version)"
62+
fi
63+
env:
64+
CLAUDE_CLI_VERSION: ${{ inputs.claude_cli_version }}
65+
66+
- name: Set up Go
67+
uses: actions/setup-go@v5
68+
with:
69+
go-version-file: ${{ github.action_path }}/../go.mod
70+
71+
- name: Build autosolve
72+
shell: bash
73+
run: go build -trimpath -o "$RUNNER_TEMP/autosolve" ./cmd/autosolve
74+
working-directory: ${{ github.action_path }}/..
75+
76+
- name: Run assessment
77+
id: assess
78+
shell: bash
79+
working-directory: ${{ inputs.working_directory }}
80+
run: "$RUNNER_TEMP/autosolve" assess
81+
env:
82+
INPUT_PROMPT: ${{ inputs.prompt }}
83+
INPUT_SKILL: ${{ inputs.skill }}
84+
INPUT_ADDITIONAL_INSTRUCTIONS: ${{ inputs.additional_instructions }}
85+
INPUT_ASSESSMENT_CRITERIA: ${{ inputs.assessment_criteria }}
86+
INPUT_MODEL: ${{ inputs.model }}
87+
INPUT_BLOCKED_PATHS: ${{ inputs.blocked_paths }}

autosolve/cmd/autosolve/main.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"os/signal"
8+
9+
"github.com/cockroachdb/actions/autosolve/internal/action"
10+
"github.com/cockroachdb/actions/autosolve/internal/assess"
11+
"github.com/cockroachdb/actions/autosolve/internal/claude"
12+
"github.com/cockroachdb/actions/autosolve/internal/config"
13+
"github.com/cockroachdb/actions/autosolve/internal/git"
14+
"github.com/cockroachdb/actions/autosolve/internal/github"
15+
"github.com/cockroachdb/actions/autosolve/internal/implement"
16+
)
17+
18+
// BuildSHA is set at build time via -ldflags.
19+
var BuildSHA = "dev"
20+
21+
const usage = `Usage: autosolve <command>
22+
23+
Commands:
24+
assess Run assessment phase
25+
implement Run implementation phase
26+
version Print the git SHA this binary was built from
27+
`
28+
29+
func main() {
30+
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
31+
defer cancel()
32+
33+
if len(os.Args) < 2 {
34+
fatalf(usage)
35+
}
36+
37+
var err error
38+
switch os.Args[1] {
39+
case "assess":
40+
err = runAssess(ctx)
41+
case "implement":
42+
err = runImplement(ctx)
43+
case "version":
44+
fmt.Println(BuildSHA)
45+
return
46+
default:
47+
fatalf("unknown command: %s\n\n%s", os.Args[1], usage)
48+
}
49+
50+
if err != nil {
51+
action.LogError(err.Error())
52+
os.Exit(1)
53+
}
54+
}
55+
56+
func fatalf(format string, args ...any) {
57+
fmt.Fprintf(os.Stderr, format+"\n", args...)
58+
os.Exit(1)
59+
}
60+
61+
func runAssess(ctx context.Context) error {
62+
cfg, err := config.LoadAssessConfig()
63+
if err != nil {
64+
return err
65+
}
66+
if err := config.ValidateAuth(); err != nil {
67+
return err
68+
}
69+
tmpDir, err := ensureTmpDir()
70+
if err != nil {
71+
return err
72+
}
73+
return assess.Run(ctx, cfg, &claude.CLIRunner{}, tmpDir)
74+
}
75+
76+
func runImplement(ctx context.Context) error {
77+
cfg, err := config.LoadImplementConfig()
78+
if err != nil {
79+
return err
80+
}
81+
if err := config.ValidateAuth(); err != nil {
82+
return err
83+
}
84+
tmpDir, err := ensureTmpDir()
85+
if err != nil {
86+
return err
87+
}
88+
89+
gitClient := &git.CLIClient{}
90+
defer implement.Cleanup(gitClient)
91+
92+
ghClient := &github.GithubClient{Token: cfg.PRCreateToken}
93+
return implement.Run(ctx, cfg, &claude.CLIRunner{}, ghClient, gitClient, tmpDir)
94+
}
95+
96+
func ensureTmpDir() (string, error) {
97+
dir := os.Getenv("AUTOSOLVE_TMPDIR")
98+
if dir != "" {
99+
return dir, nil
100+
}
101+
dir, err := os.MkdirTemp("", "autosolve_*")
102+
if err != nil {
103+
return "", fmt.Errorf("creating temp dir: %w", err)
104+
}
105+
os.Setenv("AUTOSOLVE_TMPDIR", dir)
106+
return dir, nil
107+
}

autosolve/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/cockroachdb/actions/autosolve
2+
3+
go 1.23.8

autosolve/go.sum

Whitespace-only changes.

0 commit comments

Comments
 (0)