Skip to content

Commit a564d99

Browse files
authored
feat: New agent workflow that depends less on prompt engineering (#39)
1 parent 9947ca1 commit a564d99

File tree

17 files changed

+621
-228
lines changed

17 files changed

+621
-228
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
The `start-work` binary should gain some new command-line parameters:
2+
3+
1) `--autopush`: besides the current git configuration commands, it should install a
4+
post-commit hook that automatically force pushes the commit to the `target_remote`
5+
(the goal is the push the commit even when the push is not a fast-forward push).
6+
7+
The use of the new flag should be suggested in the GIT_MESSAGE inserted when `get-task`
8+
is launched with `--autopush`. The current instructions for executing git push at the
9+
end of the task can be removed now.
10+
11+
2) `--task-description` and `--branch-name`. Specifying those should result in creating
12+
or modifying an existing task description file, by following similar logic to the
13+
`agent-task` command when not given a branch:
14+
15+
If we are currently not on an agent branch, the command should create a task description
16+
file and commit it immediately just like `agent-task` does, but without creating a new
17+
branch or pushing it to the remote (the `--branch-name` argument will affect only the
18+
name of the task description file).
19+
20+
if, we are already on an agent branch (e.g. `start-work` was already called and it
21+
created a commit with a `Start-Agent-Branch` tag in the commit message), append the
22+
new task description to the existing task file just like `agent-task` does when not
23+
given a branch name. The `--branch-name` parameter will be ignored in this situation.
24+
25+
The use of `start-work` with the new flags will be suggested in the AGENTS.md file
26+
created by `codex-setup` instead of the current manual instructions.
27+
28+
Make sure to refactor the code to not introduce unnecessary code duplication between
29+
`agent-task` and `start-work`
30+
31+
Please add tests for `start-work` that check that the described behavior works.
32+
Before two invocations of `start-work`, there might be multiple commits.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
.direnv/
2+
23
.install-markers/
34
test/logs/
5+
6+
# Debugger temp files
7+
.rdbgrc.breakpoints

.ruby-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.2.1

.vscode/launch.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"type": "rdbg",
6+
"name": "Debug Current Ruby File",
7+
"request": "launch",
8+
"script": "${file}",
9+
"args": [],
10+
"askParameters": false,
11+
"cwd": "${workspaceFolder}",
12+
"env": {},
13+
"useBundler": false,
14+
"rdbgPath": "ruby ${workspaceFolder}/scripts/rdbg-wrapper"
15+
},
16+
{
17+
"type": "rdbg",
18+
"name": "Debug Current Test File",
19+
"request": "launch",
20+
"script": "${file}",
21+
"args": ["-I", "lib", "-I", "test"],
22+
"askParameters": false,
23+
"cwd": "${workspaceFolder}",
24+
"env": {},
25+
"useBundler": false,
26+
"rdbgPath": "ruby ${workspaceFolder}/scripts/rdbg-wrapper"
27+
},
28+
{
29+
"type": "rdbg",
30+
"name": "Debug All Tests",
31+
"request": "launch",
32+
"script": "test/run_tests_shell.rb",
33+
"args": ["-I", "lib", "-I", "test"],
34+
"askParameters": false,
35+
"cwd": "${workspaceFolder}",
36+
"env": {},
37+
"useBundler": false,
38+
"rdbgPath": "ruby ${workspaceFolder}/scripts/rdbg-wrapper"
39+
}
40+
]
41+
}

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
"signatureHelp": true,
3737
"typeHierarchy": true
3838
},
39+
"rdbg.saveBreakpoints": true,
40+
"rdbg.enableDebugging": true,
3941
"files.associations": {
4042
"*.nix": "nix",
4143
"flake.nix": "nix",

.vscode/tasks.json

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "Run Current Test File",
6+
"type": "shell",
7+
"command": "ruby",
8+
"args": ["-I", "lib", "-I", "test", "${file}"],
9+
"group": "test",
10+
"presentation": {
11+
"echo": true,
12+
"reveal": "always",
13+
"focus": false,
14+
"panel": "shared"
15+
},
16+
"options": {
17+
"cwd": "${workspaceFolder}"
18+
}
19+
},
20+
{
21+
"label": "Run All Tests",
22+
"type": "shell",
23+
"command": "ruby",
24+
"args": ["-I", "lib", "-I", "test", "test/run_tests_shell.rb"],
25+
"group": {
26+
"kind": "test",
27+
"isDefault": true
28+
},
29+
"presentation": {
30+
"echo": true,
31+
"reveal": "always",
32+
"focus": false,
33+
"panel": "shared"
34+
},
35+
"options": {
36+
"cwd": "${workspaceFolder}"
37+
}
38+
},
39+
{
40+
"label": "Debug Current Test with Pry",
41+
"type": "shell",
42+
"command": "ruby",
43+
"args": ["-I", "lib", "-I", "test", "-r", "pry", "${file}"],
44+
"group": "test",
45+
"presentation": {
46+
"echo": true,
47+
"reveal": "always",
48+
"focus": true,
49+
"panel": "shared"
50+
},
51+
"options": {
52+
"cwd": "${workspaceFolder}"
53+
},
54+
"problemMatcher": []
55+
},
56+
{
57+
"label": "Simple Test Run (No Debug)",
58+
"type": "shell",
59+
"command": "ruby",
60+
"args": ["-I", "lib", "-I", "test", "${file}"],
61+
"group": "test",
62+
"presentation": {
63+
"echo": true,
64+
"reveal": "always",
65+
"focus": false,
66+
"panel": "shared"
67+
},
68+
"options": {
69+
"cwd": "${workspaceFolder}"
70+
}
71+
}
72+
]
73+
}

codex-setup

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,6 @@
22

33
AGENTS_WORKFLOW_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
44

5-
# BEWARE! The heredoc below is intentially using long lines, becuase otherwise
6-
# Codex gets confused sometimes by the terminal output in its environment that
7-
# splits the lines in unexpected ways (Codex decides the file is truncated).
8-
9-
# Conditionally set the autopush flag based on GITHUB_TOKEN availability
10-
if [ -n "${GITHUB_TOKEN}" ]; then
11-
GET_TASK_CMD="get-task --autopush"
12-
else
13-
GET_TASK_CMD="get-task"
14-
fi
15-
165
# Configure ~/.netrc for Git hosting services
176
NETRC_CONTENT=""
187
if [ -n "${GITHUB_TOKEN}" ]; then
@@ -28,44 +17,28 @@ if [ -n "$NETRC_CONTENT" ]; then
2817
echo "Configuring ~/.netrc for git authentication"
2918
printf "%s" "$NETRC_CONTENT" > "$HOME/.netrc"
3019
chmod 600 "$HOME/.netrc"
20+
GET_TASK_CMD="get-task --autopush"
21+
else
22+
GET_TASK_CMD="get-task"
3123
fi
3224

3325
cat > ../AGENTS.md << EOF
3426
When you a given a task description or developer instructions that
3527
just state "go", execute the shell command \`${GET_TASK_CMD}\` under
3628
the repository root and follow the instructions provided in the output.
3729
38-
When the task description or developer instructions are not "go",
39-
you should follow the given instructions while also adhering to the
40-
workflow described below.
41-
42-
Before starting work, do the following:
43-
44-
1) mkdir -p ".agents/tasks/\$(date -u +%Y)/\$(date -u +%m)"
45-
2) In this directory, create a file with a verbatim copy of the task
46-
description or developer instructions. The file should be named exactly
47-
"\$(date -u +%d-%H%M)-\${TASK_BRANCH_NAME}.txt", where \$TASK_BRANCH_NAME
48-
is an appropriate short branch name that would describe well the task
49-
description or developer instructions.
50-
51-
When you are given follow-up task description/developer instructions,
52-
or a QA review, append a verbatim copy to the same task file. You can
53-
make sure the file is the same by inspecting its contents which should
54-
match the original message.
55-
56-
Use the separator "\\n--- FOLLOW UP TASK ---\\n" to indicate where the
57-
new content has been appended.
30+
When the task description or developer instructions are not "go", before
31+
you do anything else, record a precise copy of them with the following
32+
shell command:
5833
59-
Before starting work on the follow-up task, take note of the current
60-
HEAD commit. I'd like this commit to be preserved. Don't squash it
61-
with other commits at the end of the task.
34+
start-work --branch-name "<TASK_BRANCH_NAME>" --task-description "<message>"
6235
63-
I REPEAT: FOLLOW THE ABOVE WORKFLOW ONLY IF THE TASK DESCRIPTION OR
64-
DEVELOPER INSTRUCTIONS ARE NOT "go".
36+
TASK_BRANCH_NAME should be an appropriate short branch name that describes
37+
well the task description or developer instructions.
6538
66-
In general, when you complete a task or a follow-up task, take a look
67-
at the changes introduced in this specific coding session and write
68-
a detailed summary of them in a single commit for each coding session.
39+
I REPEAT: THE start-work COMMAND SHOULD BE EXECUTED ONLY IF THE TASK
40+
DESCRIPTION OR DEVELOPER INSTRUCTIONS ARE NOT "go" AND IT SHOULD BE
41+
EXECUTED IMMEDIATELY.
6942
7043
EOF
7144

flake.nix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
pkgs.ruby
5151
pkgs.bundler
5252
pkgs.rubocop
53+
pkgs.git
54+
pkgs.fossil
55+
pkgs.mercurial
5356
];
5457
};
5558
});

lib/agent_task/cli.rb

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# frozen_string_literal: true
22

3+
# Command Line Interface Specification
4+
# ====================================
5+
#
6+
# This module implements the core functionality for all agent-workflow commands:
7+
#
8+
# `agent-task` (CLI.start_task) is used to record coding tasks that will be
9+
# developed by AI agents. It has two modes of operation:
10+
#
11+
# 1) Starting a new agent branch (you pass the branch name as argument to agent-task)
12+
# 2) Recording a new task in the current branch (it needs to be a agent branch already)
13+
14+
315
module AgentTask
416
# CLI exposes the main binaries as callable methods so the functionality
517
# can be reused programmatically. The methods here mirror the behavior
@@ -15,7 +27,7 @@ def devshell_names(root)
1527
# This handles all possible Nix syntax variations correctly
1628
begin
1729
# Get the current system first
18-
system_result = `nix eval --impure --raw --expr 'builtins.currentSystem' 2>/dev/null`.strip
30+
system_result = `nix eval --impure --raw --expr 'builtins.currentSystem' 2>#{File::NULL}`.strip
1931
require 'English'
2032
if $CHILD_STATUS.success?
2133
current_system = system_result
@@ -186,7 +198,7 @@ def start_task(args, stdin: $stdin, stdout: $stdout)
186198
unless editor
187199
editors = %w[nano pico micro vim helix vi]
188200
editors.each do |ed|
189-
if system("command -v #{ed} > /dev/null 2>&1")
201+
if system('command', '-v', ed, out: File::NULL, err: File::NULL)
190202
editor = ed
191203
break
192204
end
@@ -204,36 +216,11 @@ def start_task(args, stdin: $stdin, stdout: $stdout)
204216
task_content.gsub!("\r\n", "\n")
205217
abort('Aborted: empty task prompt.') if task_content.strip.empty?
206218

219+
tasks = AgentTasks.new(repo.root)
207220
if start_new_branch
208-
now = Time.now.utc
209-
year = now.year
210-
month = format('%02d', now.month)
211-
day = format('%02d', now.day)
212-
hour = format('%02d', now.hour)
213-
min = format('%02d', now.min)
214-
filename = "#{day}-#{hour}#{min}-#{branch_name}"
215-
tasks_dir = File.join(repo.root, '.agents', 'tasks', year.to_s, month)
216-
FileUtils.mkdir_p(tasks_dir)
217-
task_file = File.join(tasks_dir, filename)
218-
219-
commit_msg = "Start-Agent-Branch: #{branch_name}"
220-
target_remote = repo.default_remote_http_url
221-
commit_msg += "\nTarget-Remote: #{target_remote}" if target_remote
222-
commit_msg += "\nDev-Shell: #{options[:devshell]}" if options[:devshell]
223-
224-
File.binwrite(task_file, task_content)
225-
repo.commit_file(task_file, commit_msg)
221+
tasks.record_initial_task(task_content, branch_name, devshell: options[:devshell])
226222
else
227-
start_commit = repo.latest_agent_branch_commit
228-
abort('Error: Could not locate task start commit') unless start_commit
229-
files = repo.files_in_commit(start_commit)
230-
abort('Error: Task start commit should introduce exactly one file') unless files.length == 1
231-
task_file = File.join(repo.root, files.first)
232-
File.open(task_file, 'a') do |f|
233-
f.write("\n--- FOLLOW UP TASK ---\n")
234-
f.write(task_content)
235-
end
236-
repo.commit_file(task_file, 'Follow-up task')
223+
tasks.append_task(task_content)
237224
end
238225

239226
push = nil
@@ -289,7 +276,7 @@ def run_get_task(args = [])
289276
end
290277

291278
if repos.length == 1 && repos[0][0].nil?
292-
puts repos[0][1].agent_prompt(autopush: options[:autopush])
279+
puts repos[0][1].agent_prompt_with_autopush_setup(autopush: options[:autopush])
293280
return
294281
end
295282

@@ -298,7 +285,7 @@ def run_get_task(args = [])
298285
next if dir.nil?
299286

300287
begin
301-
msg = at.agent_prompt(autopush: options[:autopush])
288+
msg = at.agent_prompt_with_autopush_setup(autopush: options[:autopush])
302289
dir_messages << [dir, msg] if msg && !msg.empty?
303290
rescue StandardError
304291
next
@@ -321,16 +308,39 @@ def run_get_task(args = [])
321308
exit 1
322309
end
323310

324-
def run_start_work(_args = [])
311+
def run_start_work(args = [])
312+
require 'optparse'
325313
require_relative '../agent_tasks'
326314

315+
options = {}
316+
OptionParser.new do |opts|
317+
opts.on('--task-description=DESC', 'Record the given task description') do |val|
318+
options[:task_description] = val
319+
end
320+
opts.on('--branch-name=NAME', 'Name to use for the task description file') do |val|
321+
options[:branch_name] = val
322+
end
323+
end.parse!(args)
324+
327325
repos = discover_repos
328326
if repos.empty?
329327
puts "Error: Could not find repository root from #{Dir.pwd}"
330328
exit 1
331329
end
332330

333-
repos.to_h.each_value(&:prepare_work_environment)
331+
repos.to_h.each_value do |at|
332+
if options[:task_description]
333+
if at.on_task_branch?
334+
at.append_task(options[:task_description])
335+
else
336+
unless options[:branch_name]
337+
raise StandardError, 'Error: --branch-name is required when not on an agent branch'
338+
end
339+
340+
at.record_initial_task(options[:task_description], options[:branch_name])
341+
end
342+
end
343+
end
334344
rescue StandardError => e
335345
puts e.message
336346
exit 1

0 commit comments

Comments
 (0)