internator 0.1.5 → 0.1.7
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 +4 -4
- data/README.md +21 -8
- data/lib/internator/cli.rb +102 -34
- data/lib/internator/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f6853d7e223f60aa6d4ea55bde5b3c5f3ba13b7f349b2f3029efd25c97b4ba6
|
4
|
+
data.tar.gz: 442f68165403712347f11e182aa5c460762568d3f50d7efbb347acf4e783c04c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 909efaf0ae3e29c473356f4a07e815a615488c32b138d3c06e502f82bfd748726a24f9c7151452825153305aeab739819429967612fddfd477e78701f713e0d7
|
7
|
+
data.tar.gz: bcc2c29ef1b5c197d45219c2ed4bea00beb520d40fe60c3f8b1b82a22be1ded73b937719620ad1fb45086874451767c395d843bd4ad43f45a34864b38e3c0f18
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Internator is a Ruby-based CLI tool that automates iterative pull request improv
|
|
7
7
|
## Requirements
|
8
8
|
|
9
9
|
- Ruby (>= 2.5).
|
10
|
-
- [Codex CLI](https://github.com/openai/codex) installed.
|
10
|
+
- [Codex CLI](https://github.com/openai/codex) installed (>= 0.3.0).
|
11
11
|
- Environment variable `OPENAI_API_KEY` set to your OpenAI API key.
|
12
12
|
|
13
13
|
## Installation
|
@@ -17,15 +17,15 @@ gem install internator
|
|
17
17
|
```
|
18
18
|
|
19
19
|
## Usage
|
20
|
-
|
21
|
-
Push to Github your new empty branch and run the `internator` command:
|
20
|
+
Create your new empty branch and run the `internator` command:
|
22
21
|
|
23
22
|
```bash
|
24
|
-
internator "<PR Objectives>" [delay_mins]
|
23
|
+
internator "<PR Objectives>" [delay_mins] [parent_branch]
|
25
24
|
```
|
26
25
|
|
27
|
-
|
28
|
-
|
26
|
+
- `<PR Objectives>`: Description of what the pull request should achieve.
|
27
|
+
- `[delay_mins]`: (Optional) Minutes to wait between commits (default: 0).
|
28
|
+
- `[parent_branch]`: (Optional) Branch name to diff against (default: detected repository default branch).
|
29
29
|
|
30
30
|
Example:
|
31
31
|
```bash
|
@@ -33,9 +33,22 @@ internator "Refactor authentication flow and add tests" 10
|
|
33
33
|
```
|
34
34
|
For more detailed usage tips, see the [Usage Tips wiki page](https://github.com/AlexLarra/internator/wiki/Usage-tips).
|
35
35
|
|
36
|
-
|
36
|
+
## Configuration
|
37
|
+
|
38
|
+
Internator reads custom instructions from a YAML file at `~/.internator_config.yml`. The file must define an `instructions` key whose value is the instruction text. For example:
|
39
|
+
|
40
|
+
```yaml
|
41
|
+
# ~/.internator_config.yml
|
42
|
+
instructions: |
|
43
|
+
1. Do not overuse code comments; if the method name says it all, comments are not necessary.
|
44
|
+
2. Please treat files as if Vim were saving them with `set binary` and `set noeol`, i.e. do not add a final newline at the end of the file.
|
45
|
+
```
|
46
|
+
|
47
|
+
When present, Internator will use these instructions instead of the built-in defaults.
|
48
|
+
|
49
|
+
## Contributing
|
37
50
|
|
38
|
-
|
51
|
+
Feel free to open issues or submit pull requests.
|
39
52
|
|
40
53
|
## License
|
41
54
|
|
data/lib/internator/cli.rb
CHANGED
@@ -2,10 +2,54 @@ require "net/http"
|
|
2
2
|
require "uri"
|
3
3
|
require "json"
|
4
4
|
require "tempfile"
|
5
|
+
require "yaml"
|
5
6
|
|
6
7
|
module Internator
|
7
8
|
# Command-line interface for the Internator gem
|
8
9
|
class CLI
|
10
|
+
# Configuration file for custom options (YAML format).
|
11
|
+
CONFIG_FILE = File.expand_path('~/.internator_config.yml')
|
12
|
+
|
13
|
+
def self.config
|
14
|
+
@config ||= begin
|
15
|
+
if File.exist?(CONFIG_FILE)
|
16
|
+
YAML.load_file(CONFIG_FILE)
|
17
|
+
else
|
18
|
+
{}
|
19
|
+
end
|
20
|
+
rescue => e
|
21
|
+
warn "⚠️ Could not parse config file #{CONFIG_FILE}: #{e.message}"
|
22
|
+
{}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Load custom instructions from config or fall back to built-in defaults
|
27
|
+
def self.instructions
|
28
|
+
main = <<~MAIN_INSTRUCTIONS
|
29
|
+
1. If there are changes in the PR, first check if it has already been completed; if so, do nothing.
|
30
|
+
2. Make ONLY one incremental change.
|
31
|
+
3. Prioritize completing main objectives.
|
32
|
+
MAIN_INSTRUCTIONS
|
33
|
+
|
34
|
+
# Load custom instructions from ~/.internator_config.yml or fall back to built-in defaults
|
35
|
+
secondary =
|
36
|
+
if config.is_a?(Hash) && config['instructions'].is_a?(String)
|
37
|
+
config['instructions'].strip
|
38
|
+
else
|
39
|
+
<<~SECONDARY_INSTRUCTIONS
|
40
|
+
1. Do not overuse code comments; if the method name says it all, comments are not necessary.
|
41
|
+
2. Please treat files as if Vim were saving them with `set binary` and `set noeol`, i.e. do not add a final newline at the end of the file.
|
42
|
+
SECONDARY_INSTRUCTIONS
|
43
|
+
end
|
44
|
+
|
45
|
+
<<~INSTRUCTIONS.chomp
|
46
|
+
Main instructions:
|
47
|
+
#{main}
|
48
|
+
Secondary instructions:
|
49
|
+
#{secondary}
|
50
|
+
INSTRUCTIONS
|
51
|
+
end
|
52
|
+
|
9
53
|
def self.run(args = ARGV)
|
10
54
|
unless system("which codex > /dev/null 2>&1")
|
11
55
|
abort "❌ 'codex' CLI is not installed or not in PATH. Please install it from https://github.com/openai/codex"
|
@@ -15,16 +59,42 @@ module Internator
|
|
15
59
|
abort "❌ OPENAI_API_KEY not set. Please set the environment variable."
|
16
60
|
end
|
17
61
|
|
18
|
-
|
19
|
-
|
62
|
+
# Parse arguments: objectives, optional delay (minutes), optional parent_branch
|
63
|
+
if args.empty? || args.size > 3
|
64
|
+
abort "❌ Usage: internator \"<PR Objectives>\" [delay_mins] [parent_branch]"
|
20
65
|
end
|
21
66
|
|
22
67
|
objectives = args[0]
|
23
|
-
delay_mins =
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
68
|
+
delay_mins = 0
|
69
|
+
parent_branch = nil
|
70
|
+
case args.size
|
71
|
+
when 2
|
72
|
+
# single extra arg: integer delay or parent branch
|
73
|
+
begin
|
74
|
+
delay_mins = Integer(args[1])
|
75
|
+
rescue ArgumentError
|
76
|
+
parent_branch = args[1]
|
77
|
+
end
|
78
|
+
when 3
|
79
|
+
delay_mins = Integer(args[1]) rescue abort("❌ Invalid delay_mins: must be an integer")
|
80
|
+
parent_branch = args[2]
|
81
|
+
end
|
82
|
+
|
83
|
+
remote, default_base = git_detect_default_base&.split("/", 2)
|
84
|
+
branch = git_current_branch
|
85
|
+
|
86
|
+
abort "❌ Git remote is not detected." unless remote
|
87
|
+
abort "❌ Git default branch is not detected." unless default_base
|
88
|
+
|
89
|
+
if branch == default_base
|
90
|
+
abort "❌ You are on the default branch '#{default_base}'. Please create a new branch before running Internator."
|
91
|
+
end
|
92
|
+
|
93
|
+
if parent_branch && !system("git rev-parse --verify --quiet #{parent_branch} > /dev/null 2>&1")
|
94
|
+
abort "❌ Specified parent branch '#{parent_branch}' does not exist."
|
95
|
+
end
|
96
|
+
|
97
|
+
git_upstream(remote, branch)
|
28
98
|
|
29
99
|
iteration = 1
|
30
100
|
Signal.trap("INT") do
|
@@ -35,15 +105,14 @@ module Internator
|
|
35
105
|
begin
|
36
106
|
loop do
|
37
107
|
puts "\n🌀 Iteration ##{iteration} - #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}"
|
38
|
-
|
108
|
+
|
109
|
+
exit_code = codex_cycle(objectives, iteration, remote, default_base, branch, parent_branch)
|
39
110
|
if exit_code != 0
|
40
|
-
|
41
|
-
break
|
111
|
+
abort "🚨 Codex process exited with code #{exit_code}. Stopping."
|
42
112
|
end
|
43
113
|
|
44
114
|
if `git status --porcelain`.strip.empty?
|
45
|
-
|
46
|
-
break
|
115
|
+
abort "🎉 Objectives completed; no new changes. Exiting loop..."
|
47
116
|
end
|
48
117
|
|
49
118
|
auto_commit
|
@@ -59,7 +128,7 @@ module Internator
|
|
59
128
|
end
|
60
129
|
|
61
130
|
# Detect the repository's default branch across remotes (e.g., main, master, develop)
|
62
|
-
def self.
|
131
|
+
def self.git_detect_default_base
|
63
132
|
remotes = `git remote`.split("\n").reject(&:empty?)
|
64
133
|
remotes.unshift('origin') unless remotes.include?('origin')
|
65
134
|
remotes.each do |remote|
|
@@ -75,23 +144,27 @@ module Internator
|
|
75
144
|
nil
|
76
145
|
end
|
77
146
|
|
78
|
-
|
79
|
-
|
80
|
-
|
147
|
+
def self.git_current_branch
|
148
|
+
`git rev-parse --abbrev-ref HEAD`.strip
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.git_upstream(remote, branch)
|
81
152
|
upstream = `git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null`.strip
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
base = detect_default_base || upstream
|
89
|
-
elsif !upstream.empty?
|
90
|
-
base = upstream
|
91
|
-
else
|
92
|
-
base = detect_default_base || 'master'
|
153
|
+
|
154
|
+
if upstream.empty?
|
155
|
+
# As upstream is not configured, push the current branch and set upstream to remote
|
156
|
+
puts "🔄 No upstream configured for branch '#{branch}'. Sending to #{remote}..."
|
157
|
+
system("git push -u #{remote} #{branch}")
|
158
|
+
upstream = `git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null`.strip
|
93
159
|
end
|
94
|
-
|
160
|
+
|
161
|
+
upstream
|
162
|
+
end
|
163
|
+
|
164
|
+
# Executes one Codex iteration by diffing against the parent or default branch
|
165
|
+
def self.codex_cycle(objectives, iteration, remote, default_base, branch, parent_branch = nil)
|
166
|
+
# Determine base branch: user-specified parent or detected default
|
167
|
+
base = parent_branch || default_base
|
95
168
|
current_diff = `git diff #{base} 2>/dev/null`
|
96
169
|
current_diff = "No initial changes" if current_diff.strip.empty?
|
97
170
|
prompt = <<~PROMPT
|
@@ -99,12 +172,7 @@ module Internator
|
|
99
172
|
Iteration: #{iteration}
|
100
173
|
Current Pull Request: #{current_diff}
|
101
174
|
|
102
|
-
|
103
|
-
1. If there are changes in the PR, first check if it has already been completed; if so, do nothing.
|
104
|
-
2. Make ONLY one incremental change.
|
105
|
-
3. Prioritize completing main objectives.
|
106
|
-
4. Do not overuse code comments; if the method name says it all, comments are not necessary.
|
107
|
-
5. Please treat files as if Vim were saving them with `set binary` and `set noeol`, i.e. do not add a final newline at the end of the file.
|
175
|
+
#{instructions}
|
108
176
|
PROMPT
|
109
177
|
|
110
178
|
CodexService.new(prompt).call
|
data/lib/internator/version.rb
CHANGED