ace-assign 0.37.0
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/.ace-defaults/assign/catalog/composition-rules.yml +211 -0
- data/.ace-defaults/assign/catalog/recipes/batch-tasks.recipe.yml +44 -0
- data/.ace-defaults/assign/catalog/recipes/documentation.recipe.yml +35 -0
- data/.ace-defaults/assign/catalog/recipes/fix-and-review.recipe.yml +32 -0
- data/.ace-defaults/assign/catalog/recipes/implement-simple.recipe.yml +29 -0
- data/.ace-defaults/assign/catalog/recipes/implement-with-pr.recipe.yml +48 -0
- data/.ace-defaults/assign/catalog/recipes/release-only.recipe.yml +34 -0
- data/.ace-defaults/assign/catalog/steps/apply-feedback.step.yml +22 -0
- data/.ace-defaults/assign/catalog/steps/commit.step.yml +22 -0
- data/.ace-defaults/assign/catalog/steps/create-pr.step.yml +28 -0
- data/.ace-defaults/assign/catalog/steps/create-retro.step.yml +22 -0
- data/.ace-defaults/assign/catalog/steps/fix-tests.step.yml +22 -0
- data/.ace-defaults/assign/catalog/steps/lint.step.yml +22 -0
- data/.ace-defaults/assign/catalog/steps/mark-task-done.step.yml +57 -0
- data/.ace-defaults/assign/catalog/steps/onboard-base.step.yml +19 -0
- data/.ace-defaults/assign/catalog/steps/onboard.step.yml +19 -0
- data/.ace-defaults/assign/catalog/steps/plan-task.step.yml +17 -0
- data/.ace-defaults/assign/catalog/steps/pre-commit-review.step.yml +34 -0
- data/.ace-defaults/assign/catalog/steps/push-to-remote.step.yml +28 -0
- data/.ace-defaults/assign/catalog/steps/rebase-with-main.step.yml +28 -0
- data/.ace-defaults/assign/catalog/steps/reflect-and-refactor.step.yml +57 -0
- data/.ace-defaults/assign/catalog/steps/release-minor.step.yml +23 -0
- data/.ace-defaults/assign/catalog/steps/release.step.yml +23 -0
- data/.ace-defaults/assign/catalog/steps/reorganize-commits.step.yml +28 -0
- data/.ace-defaults/assign/catalog/steps/research.step.yml +19 -0
- data/.ace-defaults/assign/catalog/steps/review-pr.step.yml +22 -0
- data/.ace-defaults/assign/catalog/steps/security-audit.step.yml +22 -0
- data/.ace-defaults/assign/catalog/steps/split-subtree-root.step.yml +25 -0
- data/.ace-defaults/assign/catalog/steps/squash-changelog.step.yml +28 -0
- data/.ace-defaults/assign/catalog/steps/task-load.step.yml +29 -0
- data/.ace-defaults/assign/catalog/steps/update-docs.step.yml +38 -0
- data/.ace-defaults/assign/catalog/steps/update-pr-desc.step.yml +28 -0
- data/.ace-defaults/assign/catalog/steps/verify-e2e.step.yml +42 -0
- data/.ace-defaults/assign/catalog/steps/verify-test-suite.step.yml +48 -0
- data/.ace-defaults/assign/catalog/steps/verify-test.step.yml +36 -0
- data/.ace-defaults/assign/catalog/steps/work-on-task.step.yml +23 -0
- data/.ace-defaults/assign/config.yml +48 -0
- data/.ace-defaults/assign/presets/fix-bug.yml +65 -0
- data/.ace-defaults/assign/presets/quick-implement.yml +41 -0
- data/.ace-defaults/assign/presets/release-only.yml +35 -0
- data/.ace-defaults/assign/presets/work-on-docs.yml +41 -0
- data/.ace-defaults/assign/presets/work-on-task.yml +179 -0
- data/.ace-defaults/nav/protocols/skill-sources/ace-assign.yml +19 -0
- data/.ace-defaults/nav/protocols/wfi-sources/ace-assign.yml +19 -0
- data/CHANGELOG.md +1415 -0
- data/README.md +87 -0
- data/Rakefile +16 -0
- data/docs/exit-codes.md +61 -0
- data/docs/getting-started.md +121 -0
- data/docs/handbook.md +40 -0
- data/docs/usage.md +224 -0
- data/exe/ace-assign +16 -0
- data/handbook/guides/fork-context.g.md +231 -0
- data/handbook/skills/as-assign-compose/SKILL.md +24 -0
- data/handbook/skills/as-assign-create/SKILL.md +23 -0
- data/handbook/skills/as-assign-drive/SKILL.md +24 -0
- data/handbook/skills/as-assign-prepare/SKILL.md +23 -0
- data/handbook/skills/as-assign-recover-fork/SKILL.md +22 -0
- data/handbook/skills/as-assign-run-in-batches/SKILL.md +23 -0
- data/handbook/skills/as-assign-start/SKILL.md +25 -0
- data/handbook/workflow-instructions/assign/compose.wf.md +256 -0
- data/handbook/workflow-instructions/assign/create.wf.md +215 -0
- data/handbook/workflow-instructions/assign/drive.wf.md +666 -0
- data/handbook/workflow-instructions/assign/prepare.wf.md +469 -0
- data/handbook/workflow-instructions/assign/recover-fork.wf.md +233 -0
- data/handbook/workflow-instructions/assign/run-in-batches.wf.md +212 -0
- data/handbook/workflow-instructions/assign/start.wf.md +46 -0
- data/lib/ace/assign/atoms/assign_frontmatter_parser.rb +173 -0
- data/lib/ace/assign/atoms/catalog_loader.rb +101 -0
- data/lib/ace/assign/atoms/composition_rules.rb +219 -0
- data/lib/ace/assign/atoms/number_generator.rb +110 -0
- data/lib/ace/assign/atoms/preset_expander.rb +277 -0
- data/lib/ace/assign/atoms/step_file_parser.rb +207 -0
- data/lib/ace/assign/atoms/step_numbering.rb +227 -0
- data/lib/ace/assign/atoms/step_sorter.rb +66 -0
- data/lib/ace/assign/atoms/tree_formatter.rb +106 -0
- data/lib/ace/assign/cli/commands/add.rb +102 -0
- data/lib/ace/assign/cli/commands/assignment_target.rb +55 -0
- data/lib/ace/assign/cli/commands/create.rb +63 -0
- data/lib/ace/assign/cli/commands/fail.rb +43 -0
- data/lib/ace/assign/cli/commands/finish.rb +88 -0
- data/lib/ace/assign/cli/commands/fork_run.rb +229 -0
- data/lib/ace/assign/cli/commands/list.rb +166 -0
- data/lib/ace/assign/cli/commands/retry_cmd.rb +42 -0
- data/lib/ace/assign/cli/commands/select.rb +45 -0
- data/lib/ace/assign/cli/commands/start.rb +40 -0
- data/lib/ace/assign/cli/commands/status.rb +407 -0
- data/lib/ace/assign/cli.rb +144 -0
- data/lib/ace/assign/models/assignment.rb +107 -0
- data/lib/ace/assign/models/assignment_info.rb +66 -0
- data/lib/ace/assign/models/queue_state.rb +326 -0
- data/lib/ace/assign/models/step.rb +197 -0
- data/lib/ace/assign/molecules/assignment_discoverer.rb +57 -0
- data/lib/ace/assign/molecules/assignment_manager.rb +276 -0
- data/lib/ace/assign/molecules/fork_session_launcher.rb +102 -0
- data/lib/ace/assign/molecules/queue_scanner.rb +130 -0
- data/lib/ace/assign/molecules/skill_assign_source_resolver.rb +376 -0
- data/lib/ace/assign/molecules/step_renumberer.rb +227 -0
- data/lib/ace/assign/molecules/step_writer.rb +246 -0
- data/lib/ace/assign/organisms/assignment_executor.rb +1299 -0
- data/lib/ace/assign/version.rb +7 -0
- data/lib/ace/assign.rb +141 -0
- metadata +289 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Ace
|
|
6
|
+
module Assign
|
|
7
|
+
module CLI
|
|
8
|
+
module Commands
|
|
9
|
+
# List all assignments with state information
|
|
10
|
+
#
|
|
11
|
+
# @example List active assignments
|
|
12
|
+
# ace-assign list
|
|
13
|
+
#
|
|
14
|
+
# @example Include completed assignments
|
|
15
|
+
# ace-assign list --all
|
|
16
|
+
#
|
|
17
|
+
# @example Filter by task
|
|
18
|
+
# ace-assign list --task my-task
|
|
19
|
+
#
|
|
20
|
+
# @example JSON output
|
|
21
|
+
# ace-assign list --format json
|
|
22
|
+
class List < Ace::Support::Cli::Command
|
|
23
|
+
include Ace::Support::Cli::Base
|
|
24
|
+
|
|
25
|
+
# Column widths for table display
|
|
26
|
+
COL_ID = 10
|
|
27
|
+
COL_NAME = 25
|
|
28
|
+
COL_STATUS = 12
|
|
29
|
+
COL_PROGRESS = 10
|
|
30
|
+
COL_STEP = 20
|
|
31
|
+
COL_UPDATED = 15
|
|
32
|
+
|
|
33
|
+
# Status display labels
|
|
34
|
+
STATE_LABELS = {
|
|
35
|
+
running: "running",
|
|
36
|
+
paused: "paused",
|
|
37
|
+
completed: "completed",
|
|
38
|
+
failed: "failed",
|
|
39
|
+
empty: "empty"
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
desc "List all assignments"
|
|
43
|
+
|
|
44
|
+
option :all, aliases: ["-a"], type: :boolean, default: false, desc: "Include completed assignments"
|
|
45
|
+
option :task, aliases: ["-t"], desc: "Filter by task reference"
|
|
46
|
+
option :tree, type: :boolean, default: false, desc: "Show assignment hierarchy as tree"
|
|
47
|
+
option :format, aliases: ["-f"], desc: "Output format (table, json)", default: "table"
|
|
48
|
+
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
49
|
+
option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
|
|
50
|
+
|
|
51
|
+
def call(**options)
|
|
52
|
+
discoverer = Molecules::AssignmentDiscoverer.new
|
|
53
|
+
manager = Molecules::AssignmentManager.new
|
|
54
|
+
current_id = manager.current_id
|
|
55
|
+
|
|
56
|
+
all_assignments = discoverer.find_all(include_completed: true)
|
|
57
|
+
assignments = if options[:task]
|
|
58
|
+
all_assignments.select { |ai| ai.assignment.name == options[:task] }
|
|
59
|
+
.then { |filtered| options[:all] ? filtered : filtered.reject(&:completed?) }
|
|
60
|
+
elsif options[:all]
|
|
61
|
+
all_assignments
|
|
62
|
+
else
|
|
63
|
+
all_assignments.reject(&:completed?)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
hidden_completed = options[:all] ? 0 : all_assignments.count(&:completed?)
|
|
67
|
+
|
|
68
|
+
if options[:tree]
|
|
69
|
+
print_tree(assignments)
|
|
70
|
+
elsif options[:format] == "json"
|
|
71
|
+
print_json(assignments, current_id: current_id)
|
|
72
|
+
else
|
|
73
|
+
print_table(assignments, current_id: current_id, hidden_completed: hidden_completed)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def print_tree(assignments)
|
|
80
|
+
puts Atoms::TreeFormatter.format(assignments)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def print_table(assignments, current_id:, hidden_completed:)
|
|
84
|
+
if assignments.empty?
|
|
85
|
+
if hidden_completed > 0
|
|
86
|
+
puts "No active assignments (#{hidden_completed} completed, use --all to show)"
|
|
87
|
+
else
|
|
88
|
+
puts "No assignments found."
|
|
89
|
+
end
|
|
90
|
+
return
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Header
|
|
94
|
+
puts format(
|
|
95
|
+
"%-#{COL_ID}s %-#{COL_NAME}s %-#{COL_STATUS}s %-#{COL_PROGRESS}s %-#{COL_STEP}s %s",
|
|
96
|
+
"ID", "NAME", "STATUS", "PROGRESS", "CURRENT STEP", "UPDATED"
|
|
97
|
+
)
|
|
98
|
+
puts "-" * 95
|
|
99
|
+
|
|
100
|
+
# Rows
|
|
101
|
+
assignments.each do |info|
|
|
102
|
+
marker = (info.id == current_id) ? "*" : " "
|
|
103
|
+
id_display = "#{marker}#{info.id}"
|
|
104
|
+
|
|
105
|
+
name_display = truncate(info.name.to_s, COL_NAME - 1)
|
|
106
|
+
state_display = STATE_LABELS[info.state] || info.state.to_s
|
|
107
|
+
step_display = truncate(info.current_step, COL_STEP - 1)
|
|
108
|
+
updated_display = format_relative_time(info.updated_at)
|
|
109
|
+
|
|
110
|
+
puts format(
|
|
111
|
+
"%-#{COL_ID}s %-#{COL_NAME}s %-#{COL_STATUS}s %-#{COL_PROGRESS}s %-#{COL_STEP}s %s",
|
|
112
|
+
id_display, name_display, state_display, info.progress, step_display, updated_display
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
puts
|
|
117
|
+
total = assignments.size + hidden_completed
|
|
118
|
+
if hidden_completed > 0
|
|
119
|
+
puts "#{assignments.size}/#{total} assignment(s) shown (use --all to include completed)"
|
|
120
|
+
else
|
|
121
|
+
puts "#{assignments.size} assignment(s) found"
|
|
122
|
+
end
|
|
123
|
+
puts "* = current selection" if current_id
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def print_json(assignments, current_id:)
|
|
127
|
+
data = assignments.map do |info|
|
|
128
|
+
{
|
|
129
|
+
id: info.id,
|
|
130
|
+
name: info.name,
|
|
131
|
+
state: info.state.to_s,
|
|
132
|
+
progress: info.progress,
|
|
133
|
+
current_step: info.current_step,
|
|
134
|
+
updated_at: info.updated_at.iso8601,
|
|
135
|
+
is_current: info.id == current_id
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
puts JSON.pretty_generate(data)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def truncate(str, max_length)
|
|
143
|
+
return str if str.length <= max_length
|
|
144
|
+
|
|
145
|
+
str[0..max_length - 4] + "..."
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def format_relative_time(time)
|
|
149
|
+
return "-" unless time
|
|
150
|
+
|
|
151
|
+
diff = Time.now - time
|
|
152
|
+
if diff < 60
|
|
153
|
+
"#{diff.to_i}s ago"
|
|
154
|
+
elsif diff < 3600
|
|
155
|
+
"#{(diff / 60).to_i}m ago"
|
|
156
|
+
elsif diff < 86_400
|
|
157
|
+
"#{(diff / 3600).to_i}h ago"
|
|
158
|
+
else
|
|
159
|
+
"#{(diff / 86_400).to_i}d ago"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Assign
|
|
5
|
+
module CLI
|
|
6
|
+
module Commands
|
|
7
|
+
# Retry a failed step (creates new step linked to original)
|
|
8
|
+
class RetryCmd < Ace::Support::Cli::Command
|
|
9
|
+
include Ace::Support::Cli::Base
|
|
10
|
+
include AssignmentTarget
|
|
11
|
+
|
|
12
|
+
desc "Retry a step by creating a new linked step"
|
|
13
|
+
|
|
14
|
+
argument :step_ref, required: true, desc: "Step number to retry (e.g., 040)"
|
|
15
|
+
option :assignment, desc: "Target specific assignment ID"
|
|
16
|
+
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
17
|
+
option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
|
|
18
|
+
|
|
19
|
+
def call(step_ref:, **options)
|
|
20
|
+
target = resolve_assignment_target(options)
|
|
21
|
+
executor = build_executor_for_target(target)
|
|
22
|
+
result = executor.retry_step(step_ref)
|
|
23
|
+
|
|
24
|
+
unless options[:quiet]
|
|
25
|
+
retry_step = result[:retry]
|
|
26
|
+
original = result[:original]
|
|
27
|
+
|
|
28
|
+
puts "Created: steps/#{File.basename(retry_step.file_path)} (retry of #{original.number})"
|
|
29
|
+
puts "Original #{original.number}-#{original.name} preserved: #{original.status}"
|
|
30
|
+
|
|
31
|
+
if result[:state].current && result[:state].current.number != retry_step.number
|
|
32
|
+
puts "Note: Step #{result[:state].current.number} (#{result[:state].current.name}) must complete first"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Assign
|
|
5
|
+
module CLI
|
|
6
|
+
module Commands
|
|
7
|
+
# Select an assignment as current (sets .current symlink)
|
|
8
|
+
#
|
|
9
|
+
# @example Select by ID
|
|
10
|
+
# ace-assign select abc123
|
|
11
|
+
#
|
|
12
|
+
# @example Clear current selection
|
|
13
|
+
# ace-assign select --clear
|
|
14
|
+
class Select < Ace::Support::Cli::Command
|
|
15
|
+
include Ace::Support::Cli::Base
|
|
16
|
+
|
|
17
|
+
desc "Select an assignment as the current active assignment"
|
|
18
|
+
|
|
19
|
+
argument :id, required: false, desc: "Assignment ID to select"
|
|
20
|
+
option :clear, type: :boolean, default: false, desc: "Clear current selection (revert to .latest)"
|
|
21
|
+
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
22
|
+
option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
|
|
23
|
+
|
|
24
|
+
def call(id: nil, **options)
|
|
25
|
+
manager = Molecules::AssignmentManager.new
|
|
26
|
+
|
|
27
|
+
if options[:clear]
|
|
28
|
+
manager.clear_current
|
|
29
|
+
puts "Cleared current assignment selection (will use most recent)" unless options[:quiet]
|
|
30
|
+
return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
raise Error, "Assignment ID required. Usage: ace-assign select <id> or ace-assign select --clear" unless id
|
|
34
|
+
|
|
35
|
+
assignment = manager.set_current(id)
|
|
36
|
+
|
|
37
|
+
unless options[:quiet]
|
|
38
|
+
puts "Selected assignment: #{assignment.name} (#{assignment.id})"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Assign
|
|
5
|
+
module CLI
|
|
6
|
+
module Commands
|
|
7
|
+
# Start a pending step
|
|
8
|
+
class Start < Ace::Support::Cli::Command
|
|
9
|
+
include Ace::Support::Cli::Base
|
|
10
|
+
include AssignmentTarget
|
|
11
|
+
|
|
12
|
+
desc "Start next workable pending step"
|
|
13
|
+
|
|
14
|
+
argument :step, required: false, desc: "Step number to start (active assignment only)"
|
|
15
|
+
option :assignment, desc: "Target specific assignment ID"
|
|
16
|
+
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
17
|
+
option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
|
|
18
|
+
|
|
19
|
+
def call(step: nil, **options)
|
|
20
|
+
if step && options[:assignment]
|
|
21
|
+
raise Error, "Positional STEP targeting is only supported for active assignment. Use --assignment without STEP for cross-assignment start."
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
target = resolve_assignment_target(options)
|
|
25
|
+
executor = build_executor_for_target(target)
|
|
26
|
+
result = executor.start_step(step_number: step, fork_root: target.scope)
|
|
27
|
+
|
|
28
|
+
return if options[:quiet]
|
|
29
|
+
|
|
30
|
+
started = result[:started]
|
|
31
|
+
puts "Step #{started.number} (#{started.name}) started"
|
|
32
|
+
puts
|
|
33
|
+
puts "Instructions:"
|
|
34
|
+
puts started.instructions
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|