ace-task 0.37.5 → 0.38.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 +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/ace/task/cli/commands/list.rb +3 -2
- data/lib/ace/task/molecules/dependency_aware_smart_sorter.rb +185 -0
- data/lib/ace/task/molecules/task_display_formatter.rb +6 -4
- data/lib/ace/task/molecules/task_scanner.rb +2 -1
- data/lib/ace/task/organisms/task_manager.rb +33 -4
- data/lib/ace/task/version.rb +1 -1
- data/lib/ace/task.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd08e12e78dbfc24fe3e60bc184f1053ca85a77f2e1b9eb3bd8f4390131cbe90
|
|
4
|
+
data.tar.gz: 51805310a020a9426cbf89c3cd079344c8960ecdb97cce13a75e717c679b3989
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94dc2fedb1dcc5133570aec5c7086c0c2cb43b84e84860fd75f27e29978c65511744cb81573e73f6203ac86982e208165b9f6901331f4e72ae9cbf8e8cde47bf
|
|
7
|
+
data.tar.gz: 62f5b092a0ed387ca9830ffa4b7d02c63bd9ce7084c3e6c389fe757ba6ad296122e25b8bae89bced72377f665c48c3266e89ed1f4ffae307e9008a3358b8482f
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.38.0] - 2026-09-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Dependency-aware `--sort=smart` for `ace-task list`: ready tasks (dependencies done or archived, or the task itself done/archived) list first under the existing priority/position ordering; tasks with unmet dependencies follow in a topological tail ordered by fewest dependency hops to readiness; dependency cycles render last with a `↺cycle` marker instead of hiding. Missing dependency references stay unmet and never crash listing; `id`, `priority`, and `created` sorts are unchanged.
|
|
14
|
+
|
|
15
|
+
## [0.37.6] - 2026-09-27
|
|
16
|
+
|
|
17
|
+
### Technical
|
|
18
|
+
- Stub the GitHub sync adapter during pending replay in tests.
|
|
19
|
+
|
|
10
20
|
## [0.37.5] - 2026-09-26
|
|
11
21
|
|
|
12
22
|
### Added
|
|
@@ -36,7 +36,7 @@ module Ace
|
|
|
36
36
|
option :in, type: :string, aliases: %w[-i], desc: "Filter by folder (next=root only [default], all=everything, maybe, archive)"
|
|
37
37
|
option :root, type: :string, aliases: %w[-r], desc: "Override root path (subpath within tasks root)"
|
|
38
38
|
option :filter, type: :array, aliases: %w[-f], desc: "Filter by key:value (repeatable, supports key:a|b and key:!value)"
|
|
39
|
-
option :sort, type: :string, aliases: %w[-S], desc: "Sort order: smart (default), id, priority, created"
|
|
39
|
+
option :sort, type: :string, aliases: %w[-S], desc: "Sort order: smart (default, dependency-aware), id, priority, created"
|
|
40
40
|
|
|
41
41
|
option :quiet, type: :boolean, aliases: %w[-q], desc: "Suppress non-essential output"
|
|
42
42
|
option :verbose, type: :boolean, aliases: %w[-v], desc: "Show verbose output"
|
|
@@ -63,7 +63,8 @@ module Ace
|
|
|
63
63
|
|
|
64
64
|
puts Ace::Task::Molecules::TaskDisplayFormatter.format_list(
|
|
65
65
|
tasks, total_count: manager.last_list_total,
|
|
66
|
-
global_folder_stats: manager.last_folder_counts
|
|
66
|
+
global_folder_stats: manager.last_folder_counts,
|
|
67
|
+
cycle_ids: manager.last_list_cycle_ids
|
|
67
68
|
)
|
|
68
69
|
end
|
|
69
70
|
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ace/support/items"
|
|
4
|
+
|
|
5
|
+
module Ace
|
|
6
|
+
module Task
|
|
7
|
+
module Molecules
|
|
8
|
+
# Dependency-aware ordering for the smart list sort.
|
|
9
|
+
#
|
|
10
|
+
# Ready tasks (every dependency satisfied, or the task itself done or
|
|
11
|
+
# archived) come first under the existing smart comparator. Tasks with
|
|
12
|
+
# unmet dependencies follow in a topological tail ordered by dependency
|
|
13
|
+
# depth (fewest hops to readiness first, with the smart comparator as
|
|
14
|
+
# tie-breaker). Tasks caught in a dependency cycle render last so cycles
|
|
15
|
+
# stay visible instead of hiding or looping.
|
|
16
|
+
class DependencyAwareSmartSorter
|
|
17
|
+
# Status that satisfies a dependency.
|
|
18
|
+
SATISFIED_STATUSES = %w[done].freeze
|
|
19
|
+
# Folder that satisfies a dependency regardless of status (archived).
|
|
20
|
+
ARCHIVE_FOLDER = "_archive"
|
|
21
|
+
|
|
22
|
+
# Ordered listing plus the ids of cyclic tasks (for list rendering).
|
|
23
|
+
Result = Struct.new(:tasks, :cycle_ids)
|
|
24
|
+
|
|
25
|
+
# @param tasks [Array<Models::Task>] Tasks in the current listing
|
|
26
|
+
# @param score_fn [Proc] ->(task) { Float } existing smart score
|
|
27
|
+
# @param pin_accessor [Proc] ->(task) { position, nil } pinned position
|
|
28
|
+
# @param external_statuses [Hash{String => Array(String, String)}]
|
|
29
|
+
# [status, special_folder] for dependency references outside the
|
|
30
|
+
# listing (e.g. archived); absent entries count as unmet.
|
|
31
|
+
# @return [Result]
|
|
32
|
+
def self.sort(tasks, score_fn:, pin_accessor:, external_statuses: {})
|
|
33
|
+
return Result.new([], []) if tasks.nil? || tasks.empty?
|
|
34
|
+
|
|
35
|
+
index = {}
|
|
36
|
+
tasks.each { |task| index[task.id] = task }
|
|
37
|
+
|
|
38
|
+
unmet_deps = {}
|
|
39
|
+
tasks.each do |task|
|
|
40
|
+
deps = normalize_dependencies(task.dependencies)
|
|
41
|
+
unmet_deps[task.id] = deps.reject { |dep| satisfied?(dep, index, external_statuses) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
ready = []
|
|
45
|
+
unmet_ids = {}
|
|
46
|
+
tasks.each do |task|
|
|
47
|
+
if unmet_deps[task.id].empty? || satisfied_state?(task.status, task.special_folder)
|
|
48
|
+
ready << task
|
|
49
|
+
else
|
|
50
|
+
unmet_ids[task.id] = true
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
cycle_ids = cyclic_ids(index, unmet_deps)
|
|
55
|
+
cycle_lookup = cycle_ids.each_with_object({}) { |id, lookup| lookup[id] = true }
|
|
56
|
+
cycle_ids.each { |id| unmet_ids.delete(id) }
|
|
57
|
+
|
|
58
|
+
depths = depth_map(index, unmet_deps, unmet_ids)
|
|
59
|
+
|
|
60
|
+
ordered = order_group(ready, score_fn, pin_accessor)
|
|
61
|
+
unmet_ids.keys.group_by { |id| depths[id] }.sort.each do |_depth, ids|
|
|
62
|
+
ordered.concat(order_group(ids.map { |id| index[id] }, score_fn, pin_accessor))
|
|
63
|
+
end
|
|
64
|
+
cyclic_tasks = tasks.select { |task| cycle_lookup[task.id] }
|
|
65
|
+
ordered.concat(order_group(cyclic_tasks, score_fn, pin_accessor))
|
|
66
|
+
|
|
67
|
+
Result.new(ordered, cycle_ids)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class << self
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def order_group(group, score_fn, pin_accessor)
|
|
74
|
+
return [] if group.empty?
|
|
75
|
+
|
|
76
|
+
Ace::Support::Items::Molecules::SmartSorter.sort(
|
|
77
|
+
group, score_fn: score_fn, pin_accessor: pin_accessor
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def normalize_dependencies(raw)
|
|
82
|
+
Array(raw).compact.map(&:to_s)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# A dependency is satisfied when the referenced task is done or
|
|
86
|
+
# archived. References outside the listing consult external_statuses;
|
|
87
|
+
# unknown references stay unmet so a task cannot appear ready.
|
|
88
|
+
def satisfied?(dep_id, index, external_statuses)
|
|
89
|
+
task = index[dep_id]
|
|
90
|
+
return satisfied_state?(task.status, task.special_folder) if task
|
|
91
|
+
|
|
92
|
+
external = external_statuses[dep_id]
|
|
93
|
+
return satisfied_state?(external[0], external[1]) if external
|
|
94
|
+
|
|
95
|
+
false
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def satisfied_state?(status, special_folder)
|
|
99
|
+
SATISFIED_STATUSES.include?(status.to_s.downcase) || special_folder == ARCHIVE_FOLDER
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Cyclic ids: members of any strongly connected component of the
|
|
103
|
+
# unmet-dependency graph larger than one, plus self-dependencies.
|
|
104
|
+
def cyclic_ids(index, unmet_deps)
|
|
105
|
+
edges = {}
|
|
106
|
+
index.each_key do |id|
|
|
107
|
+
edges[id] = unmet_deps[id].select { |dep| index.key?(dep) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
cyclic = []
|
|
111
|
+
strongly_connected_cyclic_ids(edges, cyclic)
|
|
112
|
+
cyclic
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Tarjan's algorithm over unmet edges (recursion depth is bounded by
|
|
116
|
+
# the dependency chain length, which is small for task graphs).
|
|
117
|
+
def strongly_connected_cyclic_ids(edges, cyclic)
|
|
118
|
+
counter = 0
|
|
119
|
+
indices = {}
|
|
120
|
+
lowlinks = {}
|
|
121
|
+
stack = []
|
|
122
|
+
on_stack = {}
|
|
123
|
+
|
|
124
|
+
visit = nil
|
|
125
|
+
visit = lambda do |vertex|
|
|
126
|
+
indices[vertex] = lowlinks[vertex] = counter
|
|
127
|
+
counter += 1
|
|
128
|
+
stack.push(vertex)
|
|
129
|
+
on_stack[vertex] = true
|
|
130
|
+
|
|
131
|
+
edges[vertex].each do |neighbor|
|
|
132
|
+
if indices.key?(neighbor)
|
|
133
|
+
lowlinks[vertex] = [lowlinks[vertex], indices[neighbor]].min if on_stack[neighbor]
|
|
134
|
+
else
|
|
135
|
+
visit.call(neighbor)
|
|
136
|
+
lowlinks[vertex] = [lowlinks[vertex], lowlinks[neighbor]].min
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if lowlinks[vertex] == indices[vertex]
|
|
141
|
+
component = []
|
|
142
|
+
loop do
|
|
143
|
+
member = stack.pop
|
|
144
|
+
on_stack[member] = false
|
|
145
|
+
component << member
|
|
146
|
+
break if member == vertex
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
cyclic.concat(component) if component.length > 1 || component.any? { |id| edges[id].include?(id) }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
edges.each_key do |vertex|
|
|
154
|
+
visit.call(vertex) unless indices.key?(vertex)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Hop distance to the ready/satisfied boundary for unmet acyclic
|
|
159
|
+
# tasks. Missing, ready, and cyclic dependencies contribute zero, so
|
|
160
|
+
# traversal stays finite and acyclic by construction.
|
|
161
|
+
def depth_map(index, unmet_deps, unmet_ids)
|
|
162
|
+
depths = {}
|
|
163
|
+
unmet_ids.each_key do |id|
|
|
164
|
+
compute_depth(id, index, unmet_deps, unmet_ids, depths)
|
|
165
|
+
end
|
|
166
|
+
depths
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def compute_depth(id, index, unmet_deps, unmet_ids, depths)
|
|
170
|
+
return depths[id] if depths.key?(id)
|
|
171
|
+
|
|
172
|
+
deepest = unmet_deps[id].map do |dep|
|
|
173
|
+
if unmet_ids[dep]
|
|
174
|
+
compute_depth(dep, index, unmet_deps, unmet_ids, depths)
|
|
175
|
+
else
|
|
176
|
+
0
|
|
177
|
+
end
|
|
178
|
+
end.max || 0
|
|
179
|
+
depths[id] = 1 + deepest
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -113,8 +113,9 @@ module Ace
|
|
|
113
113
|
# @param tasks [Array<Models::Task>] Tasks to format
|
|
114
114
|
# @param total_count [Integer, nil] Total items before folder filtering
|
|
115
115
|
# @param global_folder_stats [Hash, nil] Folder name → count hash from full scan
|
|
116
|
+
# @param cycle_ids [Array<String>] Dependency-cycle task ids to mark
|
|
116
117
|
# @return [String] Formatted list output
|
|
117
|
-
def self.format_list(tasks, total_count: nil, global_folder_stats: nil)
|
|
118
|
+
def self.format_list(tasks, total_count: nil, global_folder_stats: nil, cycle_ids: [])
|
|
118
119
|
stats_line = format_stats_line(
|
|
119
120
|
tasks,
|
|
120
121
|
total_count: total_count,
|
|
@@ -124,7 +125,7 @@ module Ace
|
|
|
124
125
|
body = if tasks.empty?
|
|
125
126
|
"No tasks found."
|
|
126
127
|
else
|
|
127
|
-
tasks.map { |task| format_list_item(task) }.join("\n")
|
|
128
|
+
tasks.map { |task| format_list_item(task, cycle_ids: cycle_ids) }.join("\n")
|
|
128
129
|
end
|
|
129
130
|
|
|
130
131
|
"#{body}\n\n#{stats_line}"
|
|
@@ -203,7 +204,7 @@ module Ace
|
|
|
203
204
|
private_class_method :format_up_next_section, :format_recently_done_section
|
|
204
205
|
|
|
205
206
|
# Format a single task as a compact list item.
|
|
206
|
-
def self.format_list_item(task)
|
|
207
|
+
def self.format_list_item(task, cycle_ids: [])
|
|
207
208
|
c = Ace::Support::Items::Atoms::AnsiColors
|
|
208
209
|
status_sym = colored_status_sym(task.status)
|
|
209
210
|
priority_sym = PRIORITY_LABELS[task.priority] || ""
|
|
@@ -219,8 +220,9 @@ module Ace
|
|
|
219
220
|
subtask_str = task.has_subtasks? ? c.colorize(" \u203a#{task.subtasks.length}", c::DIM) : ""
|
|
220
221
|
tags_str = (task.tags && task.tags.any?) ? c.colorize(" [#{task.tags.join(", ")}]", c::DIM) : ""
|
|
221
222
|
folder_str = task.special_folder ? c.colorize(" (#{task.special_folder})", c::DIM) : ""
|
|
223
|
+
cycle_str = cycle_ids.include?(task.id) ? c.colorize(" \u21BAcycle", c::DIM) : ""
|
|
222
224
|
|
|
223
|
-
"#{status_sym} #{priority_prefix}#{id_str} #{task.title}#{subtask_str}#{tags_str}#{folder_str}"
|
|
225
|
+
"#{status_sym} #{priority_prefix}#{id_str} #{task.title}#{subtask_str}#{tags_str}#{folder_str}#{cycle_str}"
|
|
224
226
|
end
|
|
225
227
|
|
|
226
228
|
private_class_method :format_list_item
|
|
@@ -9,7 +9,7 @@ module Ace
|
|
|
9
9
|
# Uses a custom id_extractor that matches xxx.t.yyy-slug folder names.
|
|
10
10
|
# Excludes subtask folders from primary scan results.
|
|
11
11
|
class TaskScanner
|
|
12
|
-
attr_reader :last_scan_total, :last_folder_counts
|
|
12
|
+
attr_reader :last_scan_total, :last_folder_counts, :last_scan_results
|
|
13
13
|
|
|
14
14
|
# ID extractor for task-format folders: "8pp.t.q7w-fix-login"
|
|
15
15
|
TASK_ID_EXTRACTOR = ->(folder_name) {
|
|
@@ -45,6 +45,7 @@ module Ace
|
|
|
45
45
|
def scan_in_folder(folder)
|
|
46
46
|
results = scan
|
|
47
47
|
@last_scan_total = results.size
|
|
48
|
+
@last_scan_results = results
|
|
48
49
|
@last_folder_counts = results.group_by(&:special_folder).transform_values(&:size)
|
|
49
50
|
return results if folder.nil?
|
|
50
51
|
|
|
@@ -19,7 +19,7 @@ module Ace
|
|
|
19
19
|
CREATE_RETRY_LIMIT = 3
|
|
20
20
|
class CreateRetriesExhaustedError < StandardError; end
|
|
21
21
|
|
|
22
|
-
attr_reader :last_list_total, :last_folder_counts
|
|
22
|
+
attr_reader :last_list_total, :last_folder_counts, :last_list_cycle_ids
|
|
23
23
|
|
|
24
24
|
# @param root_dir [String, nil] Override root directory for tasks
|
|
25
25
|
# @param config [Hash, nil] Override configuration
|
|
@@ -27,6 +27,8 @@ module Ace
|
|
|
27
27
|
@config = config || load_config
|
|
28
28
|
@root_dir = root_dir || resolve_root_dir
|
|
29
29
|
@last_update_note = nil
|
|
30
|
+
@last_list_cycle_ids = []
|
|
31
|
+
@last_scan_results = []
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
attr_reader :last_update_note
|
|
@@ -94,13 +96,15 @@ module Ace
|
|
|
94
96
|
# @param in_folder [String, nil] Filter by special folder (default: "next" = root items only)
|
|
95
97
|
# @param tags [Array<String>] Filter by tags (any match)
|
|
96
98
|
# @param filters [Array<String>, nil] Generic filter strings
|
|
97
|
-
# @param sort [String] Sort order: "smart", "id", "priority", "created"
|
|
99
|
+
# @param sort [String] Sort order: "smart" (default, dependency-aware), "id", "priority", "created"
|
|
98
100
|
# @return [Array<Models::Task>] List of tasks
|
|
99
101
|
def list(status: nil, in_folder: "next", tags: [], filters: nil, sort: "smart")
|
|
100
102
|
scanner = Molecules::TaskScanner.new(@root_dir)
|
|
101
103
|
scan_results = scanner.scan_in_folder(in_folder)
|
|
102
104
|
@last_list_total = scanner.last_scan_total
|
|
103
105
|
@last_folder_counts = scanner.last_folder_counts
|
|
106
|
+
@last_scan_results = scanner.last_scan_results
|
|
107
|
+
@last_list_cycle_ids = []
|
|
104
108
|
|
|
105
109
|
loader = Molecules::TaskLoader.new
|
|
106
110
|
tasks = scan_results.filter_map do |sr|
|
|
@@ -293,11 +297,36 @@ module Ace
|
|
|
293
297
|
end
|
|
294
298
|
|
|
295
299
|
def smart_sort(tasks)
|
|
296
|
-
|
|
300
|
+
result = Molecules::DependencyAwareSmartSorter.sort(
|
|
297
301
|
tasks,
|
|
298
302
|
score_fn: method(:compute_task_score),
|
|
299
|
-
pin_accessor: ->(t) { t.metadata&.dig("position") }
|
|
303
|
+
pin_accessor: ->(t) { t.metadata&.dig("position") },
|
|
304
|
+
external_statuses: external_dep_statuses(tasks)
|
|
300
305
|
)
|
|
306
|
+
@last_list_cycle_ids = result.cycle_ids
|
|
307
|
+
result.tasks
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Load statuses for dependency references pointing outside the current
|
|
311
|
+
# listing (e.g. archived tasks), so smart sort can classify them as
|
|
312
|
+
# satisfied or unmet.
|
|
313
|
+
def external_dep_statuses(tasks)
|
|
314
|
+
listed_ids = {}
|
|
315
|
+
tasks.each { |t| listed_ids[t.id] = true }
|
|
316
|
+
external_ids = tasks.flat_map { |t| Array(t.dependencies) }.uniq - listed_ids.keys
|
|
317
|
+
return {} if external_ids.empty?
|
|
318
|
+
|
|
319
|
+
by_id = {}
|
|
320
|
+
@last_scan_results.each { |sr| by_id[sr.id] = sr }
|
|
321
|
+
|
|
322
|
+
loader = Molecules::TaskLoader.new
|
|
323
|
+
external_ids.each_with_object({}) do |dep_id, statuses|
|
|
324
|
+
scan_result = by_id[dep_id]
|
|
325
|
+
next unless scan_result
|
|
326
|
+
|
|
327
|
+
dep = loader.load(scan_result.dir_path, id: scan_result.id, special_folder: scan_result.special_folder)
|
|
328
|
+
statuses[dep_id] = [dep.status, dep.special_folder] if dep
|
|
329
|
+
end
|
|
301
330
|
end
|
|
302
331
|
|
|
303
332
|
def compute_task_score(task)
|
data/lib/ace/task/version.rb
CHANGED
data/lib/ace/task.rb
CHANGED
|
@@ -24,6 +24,7 @@ require_relative "task/molecules/task_loader"
|
|
|
24
24
|
require_relative "task/molecules/task_creator"
|
|
25
25
|
require_relative "task/molecules/subtask_creator"
|
|
26
26
|
require_relative "task/molecules/task_display_formatter"
|
|
27
|
+
require_relative "task/molecules/dependency_aware_smart_sorter"
|
|
27
28
|
require_relative "task/molecules/path_utils"
|
|
28
29
|
require_relative "task/molecules/github_issue_sync_adapter"
|
|
29
30
|
require_relative "task/molecules/task_plan_cache"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-task
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.38.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-09-
|
|
10
|
+
date: 2026-09-28 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-core
|
|
@@ -179,6 +179,7 @@ files:
|
|
|
179
179
|
- lib/ace/task/cli/commands/status.rb
|
|
180
180
|
- lib/ace/task/cli/commands/update.rb
|
|
181
181
|
- lib/ace/task/models/task.rb
|
|
182
|
+
- lib/ace/task/molecules/dependency_aware_smart_sorter.rb
|
|
182
183
|
- lib/ace/task/molecules/github_issue_sync_adapter.rb
|
|
183
184
|
- lib/ace/task/molecules/path_utils.rb
|
|
184
185
|
- lib/ace/task/molecules/subtask_creator.rb
|