ace-assign 0.37.0 → 0.40.4
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/.ace-defaults/assign/catalog/composition-rules.yml +12 -2
- data/.ace-defaults/assign/catalog/recipes/implement-with-pr.recipe.yml +4 -0
- data/.ace-defaults/assign/catalog/steps/record-demo.step.yml +39 -0
- data/.ace-defaults/assign/presets/work-on-task.yml +37 -0
- data/CHANGELOG.md +132 -0
- data/README.md +3 -2
- data/docs/getting-started.md +16 -5
- data/docs/handbook.md +2 -0
- data/docs/usage.md +32 -10
- data/handbook/skills/as-assign-add-task/SKILL.md +24 -0
- data/handbook/workflow-instructions/assign/add-task.wf.md +146 -0
- data/handbook/workflow-instructions/assign/create.wf.md +15 -4
- data/handbook/workflow-instructions/assign/prepare.wf.md +64 -3
- data/lib/ace/assign/atoms/preset_loader.rb +49 -0
- data/lib/ace/assign/atoms/preset_step_resolver.rb +70 -0
- data/lib/ace/assign/cli/commands/add.rb +269 -65
- data/lib/ace/assign/cli/commands/create.rb +35 -6
- data/lib/ace/assign/cli.rb +3 -0
- data/lib/ace/assign/molecules/preset_inferrer.rb +31 -0
- data/lib/ace/assign/organisms/assignment_executor.rb +335 -11
- data/lib/ace/assign/organisms/task_assignment_creator.rb +176 -0
- data/lib/ace/assign/version.rb +1 -1
- data/lib/ace/assign.rb +1 -0
- metadata +39 -18
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ace/task"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "securerandom"
|
|
6
|
+
require "tempfile"
|
|
7
|
+
require "yaml"
|
|
8
|
+
|
|
9
|
+
require_relative "../atoms/preset_loader"
|
|
10
|
+
require_relative "../atoms/preset_expander"
|
|
11
|
+
|
|
12
|
+
module Ace
|
|
13
|
+
module Assign
|
|
14
|
+
module Organisms
|
|
15
|
+
class TaskAssignmentCreator
|
|
16
|
+
DEFAULT_PRESET = "work-on-task"
|
|
17
|
+
SUBTASK_PATTERN = /^[0-9a-z]{3}\.[a-z]\.[0-9a-z]{3}\.[a-z0-9]$/
|
|
18
|
+
|
|
19
|
+
def initialize(task_manager: nil, executor: nil)
|
|
20
|
+
@task_manager = task_manager || Ace::Task::Organisms::TaskManager.new
|
|
21
|
+
@executor = executor || AssignmentExecutor.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(task_refs:, preset_name: DEFAULT_PRESET, primary_task_ref: nil)
|
|
25
|
+
requested_refs = normalize_requested_refs(task_refs)
|
|
26
|
+
raise Ace::Support::Cli::Error, "--task requires at least one task reference" if requested_refs.empty?
|
|
27
|
+
|
|
28
|
+
preset = Atoms::PresetLoader.load(preset_name)
|
|
29
|
+
resolved_refs, skipped_terminal = resolve_requested_refs(requested_refs)
|
|
30
|
+
|
|
31
|
+
if resolved_refs.empty?
|
|
32
|
+
raise Ace::Support::Cli::Error,
|
|
33
|
+
"All requested tasks are already terminal (done/skipped/cancelled): #{skipped_terminal.join(', ')}. No assignment created."
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
primary_ref = primary_task_ref.to_s.strip
|
|
37
|
+
primary_ref = resolved_refs.first[:ref] if primary_ref.empty?
|
|
38
|
+
|
|
39
|
+
guard_multi_task_preset!(preset_name, preset, resolved_refs.length)
|
|
40
|
+
|
|
41
|
+
expanded_taskrefs = if preset_supports_taskrefs?(preset)
|
|
42
|
+
expand_task_refs_in_order(resolved_refs)
|
|
43
|
+
else
|
|
44
|
+
[primary_ref]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
params = build_parameters(preset, primary_ref, expanded_taskrefs)
|
|
48
|
+
validate_parameters!(preset, params)
|
|
49
|
+
|
|
50
|
+
steps = Atoms::PresetExpander.expand(preset, params)
|
|
51
|
+
job_path = write_job_file(
|
|
52
|
+
session_name: "#{preset_name}-#{primary_ref}",
|
|
53
|
+
description: preset["description"],
|
|
54
|
+
steps: steps
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
result = @executor.start(job_path)
|
|
58
|
+
result.merge(
|
|
59
|
+
skipped_terminal: skipped_terminal,
|
|
60
|
+
primary_ref: primary_ref,
|
|
61
|
+
task_refs: expanded_taskrefs,
|
|
62
|
+
job_path: job_path
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def normalize_requested_refs(task_refs)
|
|
69
|
+
Array(task_refs)
|
|
70
|
+
.flat_map { |entry| entry.to_s.split(",") }
|
|
71
|
+
.map(&:strip)
|
|
72
|
+
.reject(&:empty?)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def resolve_requested_refs(requested_refs)
|
|
76
|
+
resolved = []
|
|
77
|
+
skipped_terminal = []
|
|
78
|
+
|
|
79
|
+
requested_refs.each do |ref|
|
|
80
|
+
task = @task_manager.show(ref.to_s)
|
|
81
|
+
raise Ace::Support::Cli::Error, "Task not found: #{ref}" unless task
|
|
82
|
+
|
|
83
|
+
if task.status.to_s == "draft"
|
|
84
|
+
raise Ace::Support::Cli::Error,
|
|
85
|
+
"Task #{ref} has status 'draft' and has not been reviewed. " \
|
|
86
|
+
"Review the spec first with /as-task-review #{ref} " \
|
|
87
|
+
"(or ace-bundle wfi://task/review), then retry."
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
if Ace::Task::Atoms::TaskValidationRules.terminal_status?(task.status.to_s)
|
|
91
|
+
skipped_terminal << ref.to_s
|
|
92
|
+
next
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
resolved << {ref: ref.to_s, task: task, is_subtask: subtask_ref?(ref)}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
[resolved, skipped_terminal]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def subtask_ref?(ref)
|
|
102
|
+
ref.to_s.match?(SUBTASK_PATTERN)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def preset_supports_taskrefs?(preset)
|
|
106
|
+
(preset["parameters"] || {}).key?("taskrefs")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def guard_multi_task_preset!(preset_name, preset, input_count)
|
|
110
|
+
return unless input_count > 1 && !preset_supports_taskrefs?(preset)
|
|
111
|
+
|
|
112
|
+
raise Ace::Support::Cli::Error,
|
|
113
|
+
"Preset '#{preset_name}' accepts only single taskref. " \
|
|
114
|
+
"Use a preset with `taskrefs` (e.g., --preset work-on-task)."
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def expand_task_refs_in_order(resolved_refs)
|
|
118
|
+
resolved_refs.flat_map do |entry|
|
|
119
|
+
ref = entry[:ref]
|
|
120
|
+
task = entry[:task]
|
|
121
|
+
|
|
122
|
+
if entry[:is_subtask]
|
|
123
|
+
[ref]
|
|
124
|
+
elsif task.respond_to?(:subtasks) && task.subtasks&.any?
|
|
125
|
+
active_subtasks = task.subtasks
|
|
126
|
+
.reject { |st| Ace::Task::Atoms::TaskValidationRules.terminal_status?(st.status.to_s) }
|
|
127
|
+
.map(&:id)
|
|
128
|
+
active_subtasks.empty? ? [ref] : active_subtasks
|
|
129
|
+
else
|
|
130
|
+
[ref]
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def build_parameters(preset, primary_ref, task_refs)
|
|
136
|
+
param_defs = preset["parameters"] || {}
|
|
137
|
+
params = {}
|
|
138
|
+
params["taskref"] = primary_ref if param_defs.key?("taskref")
|
|
139
|
+
params["taskrefs"] = task_refs if param_defs.key?("taskrefs")
|
|
140
|
+
params
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def validate_parameters!(preset, params)
|
|
144
|
+
errors = Atoms::PresetExpander.validate_parameters(preset, params)
|
|
145
|
+
return if errors.empty?
|
|
146
|
+
|
|
147
|
+
raise Ace::Support::Cli::Error, errors.join(", ")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def write_job_file(session_name:, description:, steps:)
|
|
151
|
+
job = {
|
|
152
|
+
"session" => {
|
|
153
|
+
"name" => session_name,
|
|
154
|
+
"description" => description || "Prepared by ace-assign create --task"
|
|
155
|
+
},
|
|
156
|
+
"steps" => steps
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
dir = File.join(Ace::Assign.cache_dir, "jobs")
|
|
160
|
+
FileUtils.mkdir_p(dir)
|
|
161
|
+
path = File.join(dir, "#{session_name}-#{SecureRandom.hex(4)}-job.yml")
|
|
162
|
+
payload = YAML.dump(job)
|
|
163
|
+
|
|
164
|
+
Tempfile.create(["#{session_name}-job", ".yml"], dir) do |tmp|
|
|
165
|
+
tmp.write(payload)
|
|
166
|
+
tmp.flush
|
|
167
|
+
tmp.fsync
|
|
168
|
+
FileUtils.mv(tmp.path, path)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
path
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
data/lib/ace/assign/version.rb
CHANGED
data/lib/ace/assign.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-assign
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.40.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
@@ -15,112 +15,126 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0.
|
|
18
|
+
version: '0.6'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0.
|
|
25
|
+
version: '0.6'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: ace-support-core
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '0.
|
|
32
|
+
version: '0.29'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '0.
|
|
39
|
+
version: '0.29'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: ace-support-config
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '0.
|
|
46
|
+
version: '0.9'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0.
|
|
53
|
+
version: '0.9'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: ace-support-nav
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '0.
|
|
60
|
+
version: '0.25'
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0.
|
|
67
|
+
version: '0.25'
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: ace-b36ts
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
72
|
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '0.
|
|
74
|
+
version: '0.13'
|
|
75
75
|
type: :runtime
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '0.
|
|
81
|
+
version: '0.13'
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
83
|
name: ace-support-markdown
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
86
|
- - "~>"
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: '0.
|
|
88
|
+
version: '0.3'
|
|
89
89
|
type: :runtime
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '0.
|
|
95
|
+
version: '0.3'
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
97
|
name: ace-llm
|
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - "~>"
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0.
|
|
102
|
+
version: '0.30'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0.30'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: ace-task
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0.31'
|
|
103
117
|
type: :runtime
|
|
104
118
|
prerelease: false
|
|
105
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
120
|
requirements:
|
|
107
121
|
- - "~>"
|
|
108
122
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '0.
|
|
123
|
+
version: '0.31'
|
|
110
124
|
- !ruby/object:Gem::Dependency
|
|
111
125
|
name: ace-support-test-helpers
|
|
112
126
|
requirement: !ruby/object:Gem::Requirement
|
|
113
127
|
requirements:
|
|
114
128
|
- - "~>"
|
|
115
129
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '0.
|
|
130
|
+
version: '0.13'
|
|
117
131
|
type: :development
|
|
118
132
|
prerelease: false
|
|
119
133
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
134
|
requirements:
|
|
121
135
|
- - "~>"
|
|
122
136
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '0.
|
|
137
|
+
version: '0.13'
|
|
124
138
|
- !ruby/object:Gem::Dependency
|
|
125
139
|
name: minitest
|
|
126
140
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -179,6 +193,7 @@ files:
|
|
|
179
193
|
- ".ace-defaults/assign/catalog/steps/pre-commit-review.step.yml"
|
|
180
194
|
- ".ace-defaults/assign/catalog/steps/push-to-remote.step.yml"
|
|
181
195
|
- ".ace-defaults/assign/catalog/steps/rebase-with-main.step.yml"
|
|
196
|
+
- ".ace-defaults/assign/catalog/steps/record-demo.step.yml"
|
|
182
197
|
- ".ace-defaults/assign/catalog/steps/reflect-and-refactor.step.yml"
|
|
183
198
|
- ".ace-defaults/assign/catalog/steps/release-minor.step.yml"
|
|
184
199
|
- ".ace-defaults/assign/catalog/steps/release.step.yml"
|
|
@@ -212,6 +227,7 @@ files:
|
|
|
212
227
|
- docs/usage.md
|
|
213
228
|
- exe/ace-assign
|
|
214
229
|
- handbook/guides/fork-context.g.md
|
|
230
|
+
- handbook/skills/as-assign-add-task/SKILL.md
|
|
215
231
|
- handbook/skills/as-assign-compose/SKILL.md
|
|
216
232
|
- handbook/skills/as-assign-create/SKILL.md
|
|
217
233
|
- handbook/skills/as-assign-drive/SKILL.md
|
|
@@ -219,6 +235,7 @@ files:
|
|
|
219
235
|
- handbook/skills/as-assign-recover-fork/SKILL.md
|
|
220
236
|
- handbook/skills/as-assign-run-in-batches/SKILL.md
|
|
221
237
|
- handbook/skills/as-assign-start/SKILL.md
|
|
238
|
+
- handbook/workflow-instructions/assign/add-task.wf.md
|
|
222
239
|
- handbook/workflow-instructions/assign/compose.wf.md
|
|
223
240
|
- handbook/workflow-instructions/assign/create.wf.md
|
|
224
241
|
- handbook/workflow-instructions/assign/drive.wf.md
|
|
@@ -232,6 +249,8 @@ files:
|
|
|
232
249
|
- lib/ace/assign/atoms/composition_rules.rb
|
|
233
250
|
- lib/ace/assign/atoms/number_generator.rb
|
|
234
251
|
- lib/ace/assign/atoms/preset_expander.rb
|
|
252
|
+
- lib/ace/assign/atoms/preset_loader.rb
|
|
253
|
+
- lib/ace/assign/atoms/preset_step_resolver.rb
|
|
235
254
|
- lib/ace/assign/atoms/step_file_parser.rb
|
|
236
255
|
- lib/ace/assign/atoms/step_numbering.rb
|
|
237
256
|
- lib/ace/assign/atoms/step_sorter.rb
|
|
@@ -255,18 +274,20 @@ files:
|
|
|
255
274
|
- lib/ace/assign/molecules/assignment_discoverer.rb
|
|
256
275
|
- lib/ace/assign/molecules/assignment_manager.rb
|
|
257
276
|
- lib/ace/assign/molecules/fork_session_launcher.rb
|
|
277
|
+
- lib/ace/assign/molecules/preset_inferrer.rb
|
|
258
278
|
- lib/ace/assign/molecules/queue_scanner.rb
|
|
259
279
|
- lib/ace/assign/molecules/skill_assign_source_resolver.rb
|
|
260
280
|
- lib/ace/assign/molecules/step_renumberer.rb
|
|
261
281
|
- lib/ace/assign/molecules/step_writer.rb
|
|
262
282
|
- lib/ace/assign/organisms/assignment_executor.rb
|
|
283
|
+
- lib/ace/assign/organisms/task_assignment_creator.rb
|
|
263
284
|
- lib/ace/assign/version.rb
|
|
264
285
|
homepage: https://github.com/cs3b/ace
|
|
265
286
|
licenses:
|
|
266
287
|
- MIT
|
|
267
288
|
metadata:
|
|
268
289
|
homepage_uri: https://github.com/cs3b/ace
|
|
269
|
-
source_code_uri: https://github.com/cs3b/ace
|
|
290
|
+
source_code_uri: https://github.com/cs3b/ace/tree/main/ace-assign/
|
|
270
291
|
changelog_uri: https://github.com/cs3b/ace/blob/main/ace-assign/CHANGELOG.md
|
|
271
292
|
rdoc_options: []
|
|
272
293
|
require_paths:
|