wralph 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +275 -0
- data/bin/wralph +60 -0
- data/lib/wralph/adapters/agents/claude_code.rb +19 -0
- data/lib/wralph/adapters/agents.rb +9 -0
- data/lib/wralph/adapters/cis/circle_ci.rb +199 -0
- data/lib/wralph/adapters/cis.rb +9 -0
- data/lib/wralph/adapters/objective_repositories/github_issues.rb +37 -0
- data/lib/wralph/adapters/objective_repositories.rb +9 -0
- data/lib/wralph/config.rb +89 -0
- data/lib/wralph/fixtures/config.yaml +18 -0
- data/lib/wralph/fixtures/secrets.yaml +5 -0
- data/lib/wralph/interfaces/agent.rb +13 -0
- data/lib/wralph/interfaces/ci.rb +108 -0
- data/lib/wralph/interfaces/objective_repository.rb +81 -0
- data/lib/wralph/interfaces/print.rb +32 -0
- data/lib/wralph/interfaces/repo.rb +75 -0
- data/lib/wralph/interfaces/shell.rb +60 -0
- data/lib/wralph/run/execute_plan.rb +185 -0
- data/lib/wralph/run/feedback.rb +116 -0
- data/lib/wralph/run/init.rb +98 -0
- data/lib/wralph/run/iterate_ci.rb +120 -0
- data/lib/wralph/run/plan.rb +107 -0
- data/lib/wralph/run/remove.rb +43 -0
- data/lib/wralph/version.rb +5 -0
- data/lib/wralph.rb +19 -0
- metadata +123 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'reline'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
# Fallback for older Ruby versions - reline was added in Ruby 2.7
|
|
7
|
+
# Use basic $stdin.gets for multiline input
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require_relative '../interfaces/repo'
|
|
11
|
+
require_relative '../interfaces/print'
|
|
12
|
+
require_relative '../interfaces/shell'
|
|
13
|
+
require_relative '../interfaces/agent'
|
|
14
|
+
require_relative 'init'
|
|
15
|
+
require_relative 'iterate_ci'
|
|
16
|
+
|
|
17
|
+
module Wralph
|
|
18
|
+
module Run
|
|
19
|
+
module Feedback
|
|
20
|
+
def self.run(issue_number)
|
|
21
|
+
Init.ensure_initialized!
|
|
22
|
+
|
|
23
|
+
branch_name = "issue-#{issue_number}".freeze
|
|
24
|
+
plan_file = Interfaces::Repo.plan_file(issue_number)
|
|
25
|
+
|
|
26
|
+
stdout, = Interfaces::Shell.run_command('git branch --show-current')
|
|
27
|
+
current_branch = stdout.strip
|
|
28
|
+
Interfaces::Shell.switch_into_worktree(branch_name, create_if_not_exists: false)
|
|
29
|
+
|
|
30
|
+
# Check that the Pull Request is open
|
|
31
|
+
stdout, = Interfaces::Shell.run_command("gh pr view #{branch_name} --json state -q .state")
|
|
32
|
+
if stdout.strip != 'OPEN'
|
|
33
|
+
Interfaces::Print.error "Pull Request #{issue_number} is not open. Please open it and try again."
|
|
34
|
+
exit 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Get input from user on changes to make to the Pull Request
|
|
38
|
+
Interfaces::Print.info "Please review the changes to the Pull Request and provide feedback on what changes to make."
|
|
39
|
+
Interfaces::Print.info " - Press Enter for a new line"
|
|
40
|
+
Interfaces::Print.info " - Press Enter three times to submit"
|
|
41
|
+
|
|
42
|
+
if defined?(Reline)
|
|
43
|
+
changes = Reline.readmultiline("> ", true) do |buffer|
|
|
44
|
+
# Submit when the last line is empty (user pressed Enter on empty line)
|
|
45
|
+
# Buffer ends with "\n\n" when user presses Enter on an empty line
|
|
46
|
+
# We need content before the empty line
|
|
47
|
+
next false if buffer.empty? || buffer == "\n"
|
|
48
|
+
|
|
49
|
+
# Check if buffer ends with double newline (empty line entered)
|
|
50
|
+
if buffer.end_with?("\n\n\n")
|
|
51
|
+
# Verify we have at least one non-empty line
|
|
52
|
+
lines = buffer.split("\n")
|
|
53
|
+
lines.any? { |line| !line.strip.empty? }
|
|
54
|
+
else
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
# Remove the trailing empty line if present
|
|
59
|
+
else
|
|
60
|
+
# Fallback for older Ruby versions without reline
|
|
61
|
+
changes = ""
|
|
62
|
+
puts "> "
|
|
63
|
+
loop do
|
|
64
|
+
line = $stdin.gets
|
|
65
|
+
break if line.nil? || line.strip.empty?
|
|
66
|
+
|
|
67
|
+
changes += line
|
|
68
|
+
# Check if we've had two consecutive empty lines
|
|
69
|
+
if changes.end_with?("\n\n")
|
|
70
|
+
changes = changes.chomp
|
|
71
|
+
break
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
changes = changes.chomp
|
|
76
|
+
|
|
77
|
+
# Ask Claude to make the changes
|
|
78
|
+
Interfaces::Print.info "Asking Claude to evaluate your comments to the Pull Request..."
|
|
79
|
+
instructions = <<~INSTRUCTIONS
|
|
80
|
+
Background: You previously created a plan (found in the file #{plan_file}) and executed changes into a Pull Request
|
|
81
|
+
from the current branch (#{branch_name}) to the git origin. You can compare this branch against master
|
|
82
|
+
to see your proposed changes.
|
|
83
|
+
|
|
84
|
+
The user has reviewed your Pull Request and requested the following changes:
|
|
85
|
+
|
|
86
|
+
#{changes}
|
|
87
|
+
|
|
88
|
+
Do as follows:
|
|
89
|
+
1. Review your original plan that you documented in the plan file: `#{plan_file}`
|
|
90
|
+
2. Analyze the code changes that you've made in this branch by comparing it to the master branch
|
|
91
|
+
2. Review the user input
|
|
92
|
+
3. Make the necessary changes address the issues raised by the user
|
|
93
|
+
4. Commit and push the changes to the Pull Request branch
|
|
94
|
+
5. After pushing, output "FIXES_PUSHED" so I know you've completed the fixes
|
|
95
|
+
INSTRUCTIONS
|
|
96
|
+
|
|
97
|
+
# Run claude code with instructions
|
|
98
|
+
claude_output = Interfaces::Agent.run(instructions)
|
|
99
|
+
puts "CLAUDE_OUTPUT: #{claude_output}"
|
|
100
|
+
|
|
101
|
+
# Check if fixes were pushed
|
|
102
|
+
if claude_output.include?('FIXES_PUSHED')
|
|
103
|
+
Interfaces::Print.success 'Fixes have been pushed. Waiting before checking build status again...'
|
|
104
|
+
sleep 60 # Wait a bit for CircleCI to pick up the new commit
|
|
105
|
+
else
|
|
106
|
+
Interfaces::Print.error 'Could not confirm that fixes were pushed. Please check manually.'
|
|
107
|
+
exit 1
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
IterateCI.run(issue_number)
|
|
111
|
+
|
|
112
|
+
Interfaces::Shell.switch_into_worktree(current_branch)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require_relative '../interfaces/repo'
|
|
5
|
+
require_relative '../interfaces/print'
|
|
6
|
+
|
|
7
|
+
module Wralph
|
|
8
|
+
module Run
|
|
9
|
+
module Init
|
|
10
|
+
def self.run
|
|
11
|
+
wralph_dir = Interfaces::Repo.wralph_dir
|
|
12
|
+
plans_dir = Interfaces::Repo.plans_dir
|
|
13
|
+
|
|
14
|
+
if Dir.exist?(wralph_dir)
|
|
15
|
+
Interfaces::Print.warning ".wralph directory already exists at #{wralph_dir}"
|
|
16
|
+
Interfaces::Print.info "Skipping initialization"
|
|
17
|
+
return
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Create .wralph directory
|
|
21
|
+
FileUtils.mkdir_p(wralph_dir)
|
|
22
|
+
Interfaces::Print.success "Created .wralph directory at #{wralph_dir}"
|
|
23
|
+
|
|
24
|
+
# Create plans subdirectory
|
|
25
|
+
FileUtils.mkdir_p(plans_dir)
|
|
26
|
+
Interfaces::Print.success "Created .wralph/plans directory"
|
|
27
|
+
|
|
28
|
+
# Create secrets.yaml template
|
|
29
|
+
secrets_file = Interfaces::Repo.secrets_file
|
|
30
|
+
unless File.exist?(secrets_file)
|
|
31
|
+
secrets_fixture = Interfaces::Repo.fixture_file('secrets.yaml')
|
|
32
|
+
if File.exist?(secrets_fixture)
|
|
33
|
+
FileUtils.cp(secrets_fixture, secrets_file)
|
|
34
|
+
Interfaces::Print.success "Created .wralph/secrets.yaml template"
|
|
35
|
+
else
|
|
36
|
+
Interfaces::Print.warning "Fixture file not found: #{secrets_fixture}"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Create config.yaml template
|
|
41
|
+
config_file = Interfaces::Repo.config_file
|
|
42
|
+
unless File.exist?(config_file)
|
|
43
|
+
config_fixture = Interfaces::Repo.fixture_file('config.yaml')
|
|
44
|
+
if File.exist?(config_fixture)
|
|
45
|
+
FileUtils.cp(config_fixture, config_file)
|
|
46
|
+
Interfaces::Print.success "Created .wralph/config.yaml template"
|
|
47
|
+
else
|
|
48
|
+
Interfaces::Print.warning "Fixture file not found: #{config_fixture}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Ensure .wralph/secrets.yaml is in .gitignore
|
|
53
|
+
update_gitignore
|
|
54
|
+
|
|
55
|
+
Interfaces::Print.success "WRALPH initialized successfully!"
|
|
56
|
+
Interfaces::Print.info "You can now use 'wralph plan <issue_number>' to get started"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.initialized?
|
|
60
|
+
wralph_dir = Interfaces::Repo.wralph_dir
|
|
61
|
+
Dir.exist?(wralph_dir)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.ensure_initialized!
|
|
65
|
+
return if initialized?
|
|
66
|
+
|
|
67
|
+
Interfaces::Print.error "WRALPH has not been initialized in this repository."
|
|
68
|
+
Interfaces::Print.error ""
|
|
69
|
+
Interfaces::Print.error "Please run 'wralph init' first to set up WRALPH."
|
|
70
|
+
exit 1
|
|
71
|
+
rescue SystemExit
|
|
72
|
+
raise
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
Interfaces::Print.error "Error checking initialization: #{e.message}"
|
|
75
|
+
exit 1
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.update_gitignore
|
|
79
|
+
repo_root = Interfaces::Repo.repo_root
|
|
80
|
+
gitignore_path = File.join(repo_root, '.gitignore')
|
|
81
|
+
secrets_ignore_entry = '.wralph/secrets.yaml'
|
|
82
|
+
|
|
83
|
+
# Check if entry already exists
|
|
84
|
+
if File.exist?(gitignore_path)
|
|
85
|
+
content = File.read(gitignore_path)
|
|
86
|
+
return if content.include?(secrets_ignore_entry)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Add entry to .gitignore
|
|
90
|
+
entry = "\n# WRALPH secrets\n#{secrets_ignore_entry}\n"
|
|
91
|
+
File.open(gitignore_path, 'a') do |f|
|
|
92
|
+
f.write(entry)
|
|
93
|
+
end
|
|
94
|
+
Interfaces::Print.success "Added .wralph/secrets.yaml to .gitignore"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require_relative '../interfaces/repo'
|
|
5
|
+
require_relative '../interfaces/print'
|
|
6
|
+
require_relative '../interfaces/shell'
|
|
7
|
+
require_relative '../interfaces/agent'
|
|
8
|
+
require_relative '../interfaces/ci'
|
|
9
|
+
|
|
10
|
+
module Wralph
|
|
11
|
+
module Run
|
|
12
|
+
module IterateCI
|
|
13
|
+
MAX_RETRIES = 10
|
|
14
|
+
|
|
15
|
+
def self.run(issue_number, pr_number = nil)
|
|
16
|
+
branch_name = "issue-#{issue_number}".freeze
|
|
17
|
+
pr_number ||= begin
|
|
18
|
+
stdout, = Interfaces::Shell.run_command("gh pr list --head #{branch_name} --json number -q '.[0].number'")
|
|
19
|
+
pr_num = stdout.strip
|
|
20
|
+
pr_num.empty? || pr_num == 'null' ? nil : pr_num
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
stdout, = Interfaces::Shell.run_command('git branch --show-current')
|
|
24
|
+
current_branch = stdout.strip
|
|
25
|
+
if current_branch != branch_name
|
|
26
|
+
Interfaces::Print.error "You are not on the branch #{branch_name}. Please switch to the branch #{branch_name} and try again."
|
|
27
|
+
exit 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if pr_number.nil?
|
|
31
|
+
Interfaces::Print.error "Could not determine PR number from the branch name."
|
|
32
|
+
exit 1
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
plan_file = Interfaces::Repo.plan_file(issue_number)
|
|
36
|
+
|
|
37
|
+
api_token = Interfaces::Ci.api_token
|
|
38
|
+
if api_token.nil? || api_token.empty?
|
|
39
|
+
Interfaces::Print.error 'ci_api_token is not set in .wralph/secrets.yaml'
|
|
40
|
+
Interfaces::Print.error ''
|
|
41
|
+
Interfaces::Print.error 'Please add your CircleCI API token to .wralph/secrets.yaml:'
|
|
42
|
+
Interfaces::Print.error ' ci_api_token: your-token-here'
|
|
43
|
+
exit 1
|
|
44
|
+
else
|
|
45
|
+
Interfaces::Print.success 'Loaded CI API token from .wralph/secrets.yaml'
|
|
46
|
+
end
|
|
47
|
+
retry_count = 0
|
|
48
|
+
|
|
49
|
+
# Get repository info
|
|
50
|
+
repo_owner, = Interfaces::Shell.run_command('gh repo view --json owner -q .owner.login')
|
|
51
|
+
repo_name, = Interfaces::Shell.run_command('gh repo view --json name -q .name')
|
|
52
|
+
|
|
53
|
+
# Ensure tmp directory exists
|
|
54
|
+
FileUtils.mkdir_p(Interfaces::Repo.tmp_dir)
|
|
55
|
+
|
|
56
|
+
while retry_count < MAX_RETRIES
|
|
57
|
+
Interfaces::Print.info "Iteration #{retry_count + 1}/#{MAX_RETRIES}"
|
|
58
|
+
|
|
59
|
+
# Wait for build to complete
|
|
60
|
+
if Interfaces::Ci.wait_for_build(pr_number, repo_owner, repo_name, api_token)
|
|
61
|
+
Interfaces::Print.success "CircleCI build passed! Issue ##{issue_number} has been successfully solved."
|
|
62
|
+
return true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Build failed, get failure details
|
|
66
|
+
Interfaces::Print.warning 'Build failed. Analyzing failures...'
|
|
67
|
+
failure_details = Interfaces::Ci.build_failures(pr_number, repo_owner, repo_name, api_token) || 'Could not fetch failure details'
|
|
68
|
+
|
|
69
|
+
Interfaces::Print.info 'Failure details:'
|
|
70
|
+
puts failure_details
|
|
71
|
+
|
|
72
|
+
retry_count += 1
|
|
73
|
+
|
|
74
|
+
if retry_count >= MAX_RETRIES
|
|
75
|
+
Interfaces::Print.error "Maximum retry count (#{MAX_RETRIES}) reached. Please fix the issues manually."
|
|
76
|
+
exit 1
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Store the failure details in a new file for each iteration
|
|
80
|
+
filename = Interfaces::Repo.failure_details_file(branch_name, retry_count)
|
|
81
|
+
File.write(filename, failure_details)
|
|
82
|
+
|
|
83
|
+
# Fix the issues
|
|
84
|
+
Interfaces::Print.info "Attempting to fix the issues (attempt #{retry_count}/#{MAX_RETRIES})..."
|
|
85
|
+
|
|
86
|
+
fix_instructions = <<~FIX_INSTRUCTIONS
|
|
87
|
+
The CircleCI build for PR ##{pr_number} has failed. The failure details have been logged into the following file:
|
|
88
|
+
|
|
89
|
+
#{filename}
|
|
90
|
+
|
|
91
|
+
Do as follows:
|
|
92
|
+
1. Review your original plan that you documented in the plan file: `#{plan_file}`
|
|
93
|
+
2. Analyze the failures above
|
|
94
|
+
3. Make the necessary changes to fix the issues
|
|
95
|
+
4. Commit and push the changes to the PR branch
|
|
96
|
+
5. After pushing, output "FIXES_PUSHED" so I know you've completed the fixes
|
|
97
|
+
|
|
98
|
+
The PR branch is: `issue-#{issue_number}`
|
|
99
|
+
FIX_INSTRUCTIONS
|
|
100
|
+
|
|
101
|
+
# Pass fix instructions to claude code
|
|
102
|
+
fix_output = Interfaces::Agent.run(fix_instructions)
|
|
103
|
+
puts "FIX_OUTPUT: #{fix_output}"
|
|
104
|
+
|
|
105
|
+
# Check if fixes were pushed
|
|
106
|
+
if fix_output.include?('FIXES_PUSHED')
|
|
107
|
+
Interfaces::Print.success 'Fixes have been pushed. Waiting before checking build status again...'
|
|
108
|
+
sleep 60 # Wait a bit for CircleCI to pick up the new commit
|
|
109
|
+
else
|
|
110
|
+
Interfaces::Print.error 'Could not confirm that fixes were pushed. Please check manually.'
|
|
111
|
+
exit 1
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
Interfaces::Print.error "Failed to resolve CircleCI build issues after #{MAX_RETRIES} attempts"
|
|
116
|
+
exit 1
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require_relative '../interfaces/repo'
|
|
5
|
+
require_relative '../interfaces/print'
|
|
6
|
+
require_relative '../interfaces/shell'
|
|
7
|
+
require_relative '../interfaces/agent'
|
|
8
|
+
require_relative '../interfaces/objective_repository'
|
|
9
|
+
require_relative 'init'
|
|
10
|
+
require_relative 'execute_plan'
|
|
11
|
+
|
|
12
|
+
module Wralph
|
|
13
|
+
module Run
|
|
14
|
+
module Plan
|
|
15
|
+
def self.run(issue_number)
|
|
16
|
+
Init.ensure_initialized!
|
|
17
|
+
|
|
18
|
+
plan_file = Interfaces::Repo.plan_file(issue_number)
|
|
19
|
+
branch_name = "issue-#{issue_number}".freeze
|
|
20
|
+
|
|
21
|
+
# Ensure plans directory exists
|
|
22
|
+
FileUtils.mkdir_p(Interfaces::Repo.plans_dir)
|
|
23
|
+
|
|
24
|
+
# Check if GitHub CLI is authenticated
|
|
25
|
+
_, _, success = Interfaces::Shell.run_command('gh auth status')
|
|
26
|
+
unless success
|
|
27
|
+
Interfaces::Print.error 'GitHub CLI is not authenticated. Please run \'gh auth login\''
|
|
28
|
+
exit 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Check for uncommitted changes
|
|
32
|
+
_, _, success = Interfaces::Shell.run_command('git diff-index --quiet HEAD --')
|
|
33
|
+
unless success
|
|
34
|
+
Interfaces::Print.warning 'You have uncommitted changes. The script will create a new branch, but consider committing or stashing your changes first.'
|
|
35
|
+
Interfaces::Shell.ask_user_to_continue('Continue anyway? (y/N) ')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Fetch issue details to verify it exists and download it
|
|
39
|
+
Interfaces::Print.info "Fetching GitHub issue ##{issue_number}..."
|
|
40
|
+
begin
|
|
41
|
+
Interfaces::ObjectiveRepository.download!(issue_number)
|
|
42
|
+
rescue StandardError => e
|
|
43
|
+
Interfaces::Print.error "Issue ##{issue_number} not found or not accessible: #{e.message}"
|
|
44
|
+
exit 1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Check if branch already exists (locally or remotely)
|
|
48
|
+
Interfaces::Print.info "Checking if branch '#{branch_name}' already exists..."
|
|
49
|
+
_, _, success = Interfaces::Shell.run_command("git show-ref --verify --quiet refs/heads/#{branch_name}")
|
|
50
|
+
if success
|
|
51
|
+
Interfaces::Print.error "Branch '#{branch_name}' already exists locally. Please delete it first."
|
|
52
|
+
exit 1
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
stdout, = Interfaces::Shell.run_command("git ls-remote --heads origin #{branch_name}")
|
|
56
|
+
if stdout.include?(branch_name)
|
|
57
|
+
Interfaces::Print.error "Branch '#{branch_name}' already exists on remote. Please delete it first."
|
|
58
|
+
exit 1
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
Interfaces::Print.success "Branch '#{branch_name}' does not exist locally or remotely. Proceeding..."
|
|
62
|
+
|
|
63
|
+
# Main workflow
|
|
64
|
+
Interfaces::Print.info "Starting workflow to solve GitHub issue ##{issue_number}"
|
|
65
|
+
|
|
66
|
+
# Step 1: Create initial plan and execute
|
|
67
|
+
Interfaces::Print.info 'Step 1: Creating plan and executing solution...'
|
|
68
|
+
|
|
69
|
+
objective_file = Interfaces::ObjectiveRepository.local_file_path(issue_number)
|
|
70
|
+
instructions_template = <<~INSTRUCTIONS
|
|
71
|
+
I need you to make a plan to solve the objective "#{issue_number}" in the file: `#{objective_file}`. You are not to make any code changes. Instead, here's what I need you to do:
|
|
72
|
+
|
|
73
|
+
1. First, read the objective from the file
|
|
74
|
+
|
|
75
|
+
2. Create a detailed plan for solving the issue. Write your thinking and plan in a markdown file at:
|
|
76
|
+
`#{plan_file}`
|
|
77
|
+
|
|
78
|
+
The plan should include:
|
|
79
|
+
- Analysis of the issue.
|
|
80
|
+
- Approach to solving it.
|
|
81
|
+
- Test cases that should be written to verify the solution.
|
|
82
|
+
- Steps you'll take.
|
|
83
|
+
- Any potential risks or considerations.
|
|
84
|
+
- A list of questions for any clarifications you need to ask the user. If you do not need any clarifications, you can say "No questions needed".
|
|
85
|
+
INSTRUCTIONS
|
|
86
|
+
|
|
87
|
+
# Run claude code with instructions
|
|
88
|
+
Interfaces::Print.info "Running Claude Code to create a plan to solve issue ##{issue_number}..."
|
|
89
|
+
claude_output = Interfaces::Agent.run(instructions_template)
|
|
90
|
+
puts "CLAUDE_OUTPUT: #{claude_output}"
|
|
91
|
+
|
|
92
|
+
# Check if the plan file was created
|
|
93
|
+
unless File.exist?(plan_file)
|
|
94
|
+
Interfaces::Print.error "Plan file '#{plan_file}' was not created. Please check the Claude Code output manually."
|
|
95
|
+
Interfaces::Print.error "Output: #{claude_output}"
|
|
96
|
+
exit 1
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
Interfaces::Print.success "Plan file '#{plan_file}' was created. Please review it, answering any questions Claude has asked."
|
|
100
|
+
Interfaces::Shell.ask_user_to_continue('When you are ready to proceed, answer "y" to continue (y/N) ')
|
|
101
|
+
|
|
102
|
+
# Execute the plan
|
|
103
|
+
ExecutePlan.run(issue_number)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../interfaces/print'
|
|
4
|
+
require_relative '../interfaces/shell'
|
|
5
|
+
require_relative 'init'
|
|
6
|
+
|
|
7
|
+
module Wralph
|
|
8
|
+
module Run
|
|
9
|
+
module Remove
|
|
10
|
+
def self.run(issue_number)
|
|
11
|
+
Init.ensure_initialized!
|
|
12
|
+
|
|
13
|
+
branch_name = "issue-#{issue_number}".freeze
|
|
14
|
+
|
|
15
|
+
# Delete local branch
|
|
16
|
+
_, _, success = Interfaces::Shell.run_command("git branch -D #{branch_name}")
|
|
17
|
+
if success
|
|
18
|
+
Interfaces::Print.info "Deleted branch '#{branch_name}' locally"
|
|
19
|
+
else
|
|
20
|
+
Interfaces::Print.warning "Branch '#{branch_name}' not found locally"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Delete remote branch
|
|
24
|
+
_, _, success = Interfaces::Shell.run_command("git push origin --delete #{branch_name}")
|
|
25
|
+
if success
|
|
26
|
+
Interfaces::Print.info "Deleted branch '#{branch_name}' on remote"
|
|
27
|
+
else
|
|
28
|
+
Interfaces::Print.warning "Branch '#{branch_name}' not found on remote"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Remove worktree
|
|
32
|
+
_, _, success = Interfaces::Shell.run_command("wt remove #{branch_name}")
|
|
33
|
+
if success
|
|
34
|
+
Interfaces::Print.info "Removed worktree for branch '#{branch_name}'"
|
|
35
|
+
else
|
|
36
|
+
Interfaces::Print.warning "Worktree for branch '#{branch_name}' not found"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Interfaces::Print.success "Cleanup completed for issue ##{issue_number}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/wralph.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'wralph/version'
|
|
4
|
+
require_relative 'wralph/config'
|
|
5
|
+
require_relative 'wralph/interfaces/repo'
|
|
6
|
+
require_relative 'wralph/interfaces/print'
|
|
7
|
+
require_relative 'wralph/interfaces/shell'
|
|
8
|
+
require_relative 'wralph/interfaces/agent'
|
|
9
|
+
require_relative 'wralph/interfaces/ci'
|
|
10
|
+
require_relative 'wralph/interfaces/objective_repository'
|
|
11
|
+
require_relative 'wralph/run/init'
|
|
12
|
+
require_relative 'wralph/run/plan'
|
|
13
|
+
require_relative 'wralph/run/execute_plan'
|
|
14
|
+
require_relative 'wralph/run/iterate_ci'
|
|
15
|
+
require_relative 'wralph/run/feedback'
|
|
16
|
+
require_relative 'wralph/run/remove'
|
|
17
|
+
|
|
18
|
+
module Wralph
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: wralph
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nick Knipe
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rake
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rspec
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rubocop
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: webmock
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
description: 'A CLI that wraps a coding agent and CI to autonomously complete software
|
|
69
|
+
objectives using the Ralph Wiggum technique. '
|
|
70
|
+
email:
|
|
71
|
+
- nick@hellodrifter.com
|
|
72
|
+
executables:
|
|
73
|
+
- wralph
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- README.md
|
|
78
|
+
- bin/wralph
|
|
79
|
+
- lib/wralph.rb
|
|
80
|
+
- lib/wralph/adapters/agents.rb
|
|
81
|
+
- lib/wralph/adapters/agents/claude_code.rb
|
|
82
|
+
- lib/wralph/adapters/cis.rb
|
|
83
|
+
- lib/wralph/adapters/cis/circle_ci.rb
|
|
84
|
+
- lib/wralph/adapters/objective_repositories.rb
|
|
85
|
+
- lib/wralph/adapters/objective_repositories/github_issues.rb
|
|
86
|
+
- lib/wralph/config.rb
|
|
87
|
+
- lib/wralph/fixtures/config.yaml
|
|
88
|
+
- lib/wralph/fixtures/secrets.yaml
|
|
89
|
+
- lib/wralph/interfaces/agent.rb
|
|
90
|
+
- lib/wralph/interfaces/ci.rb
|
|
91
|
+
- lib/wralph/interfaces/objective_repository.rb
|
|
92
|
+
- lib/wralph/interfaces/print.rb
|
|
93
|
+
- lib/wralph/interfaces/repo.rb
|
|
94
|
+
- lib/wralph/interfaces/shell.rb
|
|
95
|
+
- lib/wralph/run/execute_plan.rb
|
|
96
|
+
- lib/wralph/run/feedback.rb
|
|
97
|
+
- lib/wralph/run/init.rb
|
|
98
|
+
- lib/wralph/run/iterate_ci.rb
|
|
99
|
+
- lib/wralph/run/plan.rb
|
|
100
|
+
- lib/wralph/run/remove.rb
|
|
101
|
+
- lib/wralph/version.rb
|
|
102
|
+
homepage: https://github.com/niborg/wralph
|
|
103
|
+
licenses:
|
|
104
|
+
- MIT
|
|
105
|
+
metadata: {}
|
|
106
|
+
rdoc_options: []
|
|
107
|
+
require_paths:
|
|
108
|
+
- lib
|
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
110
|
+
requirements:
|
|
111
|
+
- - ">="
|
|
112
|
+
- !ruby/object:Gem::Version
|
|
113
|
+
version: 3.0.0
|
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - ">="
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '0'
|
|
119
|
+
requirements: []
|
|
120
|
+
rubygems_version: 3.6.9
|
|
121
|
+
specification_version: 4
|
|
122
|
+
summary: Human-In-The-Loop AI Factory
|
|
123
|
+
test_files: []
|