ace-task 0.31.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/nav/protocols/skill-sources/ace-task.yml +19 -0
- data/.ace-defaults/nav/protocols/wfi-sources/ace-task.yml +19 -0
- data/.ace-defaults/task/config.yml +25 -0
- data/CHANGELOG.md +518 -0
- data/README.md +52 -0
- data/Rakefile +12 -0
- data/exe/ace-task +22 -0
- data/handbook/guides/task-definition.g.md +156 -0
- data/handbook/skills/as-bug-analyze/SKILL.md +26 -0
- data/handbook/skills/as-bug-fix/SKILL.md +27 -0
- data/handbook/skills/as-task-document-unplanned/SKILL.md +27 -0
- data/handbook/skills/as-task-draft/SKILL.md +24 -0
- data/handbook/skills/as-task-finder/SKILL.md +27 -0
- data/handbook/skills/as-task-plan/SKILL.md +30 -0
- data/handbook/skills/as-task-review/SKILL.md +25 -0
- data/handbook/skills/as-task-review-questions/SKILL.md +25 -0
- data/handbook/skills/as-task-update/SKILL.md +21 -0
- data/handbook/skills/as-task-work/SKILL.md +41 -0
- data/handbook/templates/task/draft.template.md +166 -0
- data/handbook/templates/task/file-modification-checklist.template.md +26 -0
- data/handbook/templates/task/technical-approach.template.md +26 -0
- data/handbook/workflow-instructions/bug/analyze.wf.md +458 -0
- data/handbook/workflow-instructions/bug/fix.wf.md +512 -0
- data/handbook/workflow-instructions/task/document-unplanned.wf.md +222 -0
- data/handbook/workflow-instructions/task/draft.wf.md +552 -0
- data/handbook/workflow-instructions/task/finder.wf.md +22 -0
- data/handbook/workflow-instructions/task/plan.wf.md +489 -0
- data/handbook/workflow-instructions/task/review-plan.wf.md +144 -0
- data/handbook/workflow-instructions/task/review-questions.wf.md +411 -0
- data/handbook/workflow-instructions/task/review-work.wf.md +146 -0
- data/handbook/workflow-instructions/task/review.wf.md +351 -0
- data/handbook/workflow-instructions/task/update.wf.md +118 -0
- data/handbook/workflow-instructions/task/work.wf.md +106 -0
- data/lib/ace/task/atoms/task_file_pattern.rb +68 -0
- data/lib/ace/task/atoms/task_frontmatter_defaults.rb +46 -0
- data/lib/ace/task/atoms/task_id_formatter.rb +62 -0
- data/lib/ace/task/atoms/task_validation_rules.rb +51 -0
- data/lib/ace/task/cli/commands/create.rb +105 -0
- data/lib/ace/task/cli/commands/doctor.rb +206 -0
- data/lib/ace/task/cli/commands/list.rb +73 -0
- data/lib/ace/task/cli/commands/plan.rb +119 -0
- data/lib/ace/task/cli/commands/show.rb +58 -0
- data/lib/ace/task/cli/commands/status.rb +77 -0
- data/lib/ace/task/cli/commands/update.rb +183 -0
- data/lib/ace/task/cli.rb +83 -0
- data/lib/ace/task/models/task.rb +46 -0
- data/lib/ace/task/molecules/path_utils.rb +20 -0
- data/lib/ace/task/molecules/subtask_creator.rb +130 -0
- data/lib/ace/task/molecules/task_config_loader.rb +92 -0
- data/lib/ace/task/molecules/task_creator.rb +115 -0
- data/lib/ace/task/molecules/task_display_formatter.rb +221 -0
- data/lib/ace/task/molecules/task_doctor_fixer.rb +510 -0
- data/lib/ace/task/molecules/task_doctor_reporter.rb +264 -0
- data/lib/ace/task/molecules/task_frontmatter_validator.rb +138 -0
- data/lib/ace/task/molecules/task_loader.rb +119 -0
- data/lib/ace/task/molecules/task_plan_cache.rb +190 -0
- data/lib/ace/task/molecules/task_plan_generator.rb +141 -0
- data/lib/ace/task/molecules/task_plan_prompt_builder.rb +91 -0
- data/lib/ace/task/molecules/task_reparenter.rb +247 -0
- data/lib/ace/task/molecules/task_resolver.rb +115 -0
- data/lib/ace/task/molecules/task_scanner.rb +129 -0
- data/lib/ace/task/molecules/task_structure_validator.rb +154 -0
- data/lib/ace/task/organisms/task_doctor.rb +199 -0
- data/lib/ace/task/organisms/task_manager.rb +353 -0
- data/lib/ace/task/version.rb +7 -0
- data/lib/ace/task.rb +37 -0
- metadata +197 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../molecules/task_config_loader"
|
|
4
|
+
require_relative "../molecules/task_scanner"
|
|
5
|
+
require_relative "../molecules/task_resolver"
|
|
6
|
+
require_relative "../molecules/task_loader"
|
|
7
|
+
require_relative "../molecules/task_creator"
|
|
8
|
+
require_relative "../molecules/subtask_creator"
|
|
9
|
+
require_relative "../molecules/task_reparenter"
|
|
10
|
+
require_relative "../atoms/task_validation_rules"
|
|
11
|
+
|
|
12
|
+
module Ace
|
|
13
|
+
module Task
|
|
14
|
+
module Organisms
|
|
15
|
+
# Orchestrates all task CRUD operations.
|
|
16
|
+
# Entry point for task management with config-driven root directory.
|
|
17
|
+
class TaskManager
|
|
18
|
+
attr_reader :last_list_total, :last_folder_counts
|
|
19
|
+
|
|
20
|
+
# @param root_dir [String, nil] Override root directory for tasks
|
|
21
|
+
# @param config [Hash, nil] Override configuration
|
|
22
|
+
def initialize(root_dir: nil, config: nil)
|
|
23
|
+
@config = config || load_config
|
|
24
|
+
@root_dir = root_dir || resolve_root_dir
|
|
25
|
+
@last_update_note = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
attr_reader :last_update_note
|
|
29
|
+
|
|
30
|
+
# Create a new task.
|
|
31
|
+
# @param title [String] Task title
|
|
32
|
+
# @param status [String, nil] Initial status
|
|
33
|
+
# @param priority [String, nil] Priority level
|
|
34
|
+
# @param tags [Array<String>] Tags
|
|
35
|
+
# @param dependencies [Array<String>] Dependency task IDs
|
|
36
|
+
# @param use_llm_slug [Boolean] Whether to attempt LLM slug generation
|
|
37
|
+
# @return [Models::Task] Created task
|
|
38
|
+
def create(title, status: nil, priority: nil, tags: [], dependencies: [], use_llm_slug: false, estimate: nil)
|
|
39
|
+
ensure_root_dir
|
|
40
|
+
creator = Molecules::TaskCreator.new(root_dir: @root_dir, config: @config)
|
|
41
|
+
creator.create(title, status: status, priority: priority, tags: tags,
|
|
42
|
+
dependencies: dependencies, use_llm_slug: use_llm_slug, estimate: estimate)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Show (load) a single task by reference, including subtasks.
|
|
46
|
+
# @param ref [String] Full ID, shortcut, or subtask reference
|
|
47
|
+
# @return [Models::Task, nil] Loaded task or nil if not found
|
|
48
|
+
def show(ref)
|
|
49
|
+
scan_result = resolve_scan_result(ref)
|
|
50
|
+
return nil unless scan_result
|
|
51
|
+
|
|
52
|
+
loader = Molecules::TaskLoader.new
|
|
53
|
+
loader.load(scan_result.dir_path,
|
|
54
|
+
id: scan_result.id,
|
|
55
|
+
special_folder: scan_result.special_folder)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# List tasks with optional filtering and sorting.
|
|
59
|
+
# @param status [String, nil] Filter by status
|
|
60
|
+
# @param in_folder [String, nil] Filter by special folder (default: "next" = root items only)
|
|
61
|
+
# @param tags [Array<String>] Filter by tags (any match)
|
|
62
|
+
# @param filters [Array<String>, nil] Generic filter strings
|
|
63
|
+
# @param sort [String] Sort order: "smart", "id", "priority", "created" (default: "smart")
|
|
64
|
+
# @return [Array<Models::Task>] List of tasks
|
|
65
|
+
def list(status: nil, in_folder: "next", tags: [], filters: nil, sort: "smart")
|
|
66
|
+
scanner = Molecules::TaskScanner.new(@root_dir)
|
|
67
|
+
scan_results = scanner.scan_in_folder(in_folder)
|
|
68
|
+
@last_list_total = scanner.last_scan_total
|
|
69
|
+
@last_folder_counts = scanner.last_folder_counts
|
|
70
|
+
|
|
71
|
+
loader = Molecules::TaskLoader.new
|
|
72
|
+
tasks = scan_results.filter_map do |sr|
|
|
73
|
+
loader.load(sr.dir_path, id: sr.id, special_folder: sr.special_folder)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Apply legacy filters
|
|
77
|
+
tasks = tasks.select { |t| t.status == status } if status
|
|
78
|
+
tasks = filter_by_tags(tasks, tags) if tags.any?
|
|
79
|
+
|
|
80
|
+
# Apply generic --filter specs
|
|
81
|
+
if filters && !filters.empty?
|
|
82
|
+
filter_specs = Ace::Support::Items::Atoms::FilterParser.parse(filters)
|
|
83
|
+
tasks = Ace::Support::Items::Molecules::FilterApplier.apply(
|
|
84
|
+
tasks, filter_specs, value_accessor: method(:task_value_accessor)
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
apply_sort(tasks, sort)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Update a task's frontmatter fields and optionally move to a folder.
|
|
92
|
+
# @param ref [String] Task reference
|
|
93
|
+
# @param set [Hash] Fields to set (supports dot-notation for nested keys)
|
|
94
|
+
# @param add [Hash] Fields to add to arrays
|
|
95
|
+
# @param remove [Hash] Fields to remove from arrays
|
|
96
|
+
# @param move_to [String, nil] Target folder to move to (archive, maybe, anytime, next/root//)
|
|
97
|
+
# @param move_as_child_of [String, nil] Reparent: parent ref, "none" (promote), "self" (orchestrator)
|
|
98
|
+
# @return [Models::Task, nil] Updated task or nil if not found
|
|
99
|
+
def update(ref, set: {}, add: {}, remove: {}, move_to: nil, move_as_child_of: nil)
|
|
100
|
+
@last_update_note = nil
|
|
101
|
+
scan_result = resolve_scan_result(ref)
|
|
102
|
+
return nil unless scan_result
|
|
103
|
+
|
|
104
|
+
loader = Molecules::TaskLoader.new
|
|
105
|
+
task = loader.load(scan_result.dir_path,
|
|
106
|
+
id: scan_result.id,
|
|
107
|
+
special_folder: scan_result.special_folder)
|
|
108
|
+
return nil unless task
|
|
109
|
+
|
|
110
|
+
# Apply field updates if any
|
|
111
|
+
has_field_updates = [set, add, remove].any? { |h| h && !h.empty? }
|
|
112
|
+
if has_field_updates
|
|
113
|
+
Ace::Support::Items::Molecules::FieldUpdater.update(
|
|
114
|
+
task.file_path, set: set, add: add, remove: remove
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Apply move if requested
|
|
119
|
+
current_path = task.path
|
|
120
|
+
current_special = task.special_folder
|
|
121
|
+
current_id = task.id
|
|
122
|
+
if move_to
|
|
123
|
+
if archive_move_for_subtask?(task, move_to)
|
|
124
|
+
result = handle_subtask_archive_move(task, loader)
|
|
125
|
+
current_path = result[:path]
|
|
126
|
+
current_special = result[:special_folder]
|
|
127
|
+
current_id = result[:id]
|
|
128
|
+
else
|
|
129
|
+
mover = Ace::Support::Items::Molecules::FolderMover.new(@root_dir)
|
|
130
|
+
new_path = if Ace::Support::Items::Atoms::SpecialFolderDetector.move_to_root?(move_to)
|
|
131
|
+
mover.move_to_root(task)
|
|
132
|
+
else
|
|
133
|
+
archive_date = parse_archive_date(task)
|
|
134
|
+
mover.move(task, to: move_to, date: archive_date)
|
|
135
|
+
end
|
|
136
|
+
current_path = new_path
|
|
137
|
+
current_special = Ace::Support::Items::Atoms::SpecialFolderDetector.detect_in_path(
|
|
138
|
+
new_path, root: @root_dir
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Reparent if requested (mutually exclusive with move_to)
|
|
144
|
+
if move_as_child_of
|
|
145
|
+
reparenter = Molecules::TaskReparenter.new(root_dir: @root_dir, config: @config)
|
|
146
|
+
resolve_fn = ->(r) { show(r) }
|
|
147
|
+
# Reload task from current path before reparenting (may have been field-updated)
|
|
148
|
+
task_for_reparent = loader.load(current_path, id: task.id, special_folder: current_special)
|
|
149
|
+
return reparenter.reparent(task_for_reparent, target: move_as_child_of, resolve_ref: resolve_fn)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Auto-archive hook: if a subtask status was set to terminal,
|
|
153
|
+
# check if all siblings are terminal and auto-move parent to archive
|
|
154
|
+
if set && set.key?("status")
|
|
155
|
+
check_auto_archive(task, set["status"], loader)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Reload and return updated task
|
|
159
|
+
loader.load(current_path, id: current_id, special_folder: current_special)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Create a subtask within a parent task.
|
|
163
|
+
# @param parent_ref [String] Parent task reference
|
|
164
|
+
# @param title [String] Subtask title
|
|
165
|
+
# @param status [String, nil] Initial status
|
|
166
|
+
# @param priority [String, nil] Priority level
|
|
167
|
+
# @param tags [Array<String>] Tags
|
|
168
|
+
# @return [Models::Task, nil] Created subtask or nil if parent not found
|
|
169
|
+
def create_subtask(parent_ref, title, status: nil, priority: nil, tags: [], estimate: nil)
|
|
170
|
+
parent = show(parent_ref)
|
|
171
|
+
return nil unless parent
|
|
172
|
+
|
|
173
|
+
subtask_creator = Molecules::SubtaskCreator.new(config: @config)
|
|
174
|
+
subtask_creator.create(parent, title, status: status, priority: priority, tags: tags, estimate: estimate)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Get the root directory.
|
|
178
|
+
# @return [String] Absolute path to tasks root
|
|
179
|
+
attr_reader :root_dir
|
|
180
|
+
|
|
181
|
+
private
|
|
182
|
+
|
|
183
|
+
def load_config
|
|
184
|
+
Molecules::TaskConfigLoader.load
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def resolve_root_dir
|
|
188
|
+
Molecules::TaskConfigLoader.root_dir(@config)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def ensure_root_dir
|
|
192
|
+
require "fileutils"
|
|
193
|
+
FileUtils.mkdir_p(@root_dir) unless Dir.exist?(@root_dir)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def resolve_scan_result(ref)
|
|
197
|
+
scanner = Molecules::TaskScanner.new(@root_dir)
|
|
198
|
+
scan_results = scanner.scan
|
|
199
|
+
resolver = Molecules::TaskResolver.new(scan_results)
|
|
200
|
+
resolver.resolve(ref)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def apply_sort(tasks, sort_mode)
|
|
204
|
+
case sort_mode
|
|
205
|
+
when "smart"
|
|
206
|
+
smart_sort(tasks)
|
|
207
|
+
when "id"
|
|
208
|
+
tasks.sort_by(&:id)
|
|
209
|
+
when "priority"
|
|
210
|
+
priority_order = {"critical" => 0, "high" => 1, "medium" => 2, "low" => 3}
|
|
211
|
+
tasks.sort_by { |t| priority_order[t.priority] || 99 }
|
|
212
|
+
when "created"
|
|
213
|
+
tasks.sort_by { |t| t.created_at || Time.at(0) }
|
|
214
|
+
else
|
|
215
|
+
tasks
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def smart_sort(tasks)
|
|
220
|
+
Ace::Support::Items::Molecules::SmartSorter.sort(
|
|
221
|
+
tasks,
|
|
222
|
+
score_fn: method(:compute_task_score),
|
|
223
|
+
pin_accessor: ->(t) { t.metadata&.dig("position") }
|
|
224
|
+
)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def compute_task_score(task)
|
|
228
|
+
weight = Ace::Support::Items::Atoms::SortScoreCalculator.priority_weight(task.priority)
|
|
229
|
+
age = if task.created_at
|
|
230
|
+
[(Time.now - task.created_at) / 86_400.0, 0].max
|
|
231
|
+
else
|
|
232
|
+
0
|
|
233
|
+
end
|
|
234
|
+
Ace::Support::Items::Atoms::SortScoreCalculator.compute(
|
|
235
|
+
priority_weight: weight,
|
|
236
|
+
age_days: age,
|
|
237
|
+
status: task.status
|
|
238
|
+
)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def filter_by_tags(tasks, tags)
|
|
242
|
+
return tasks if tags.empty?
|
|
243
|
+
|
|
244
|
+
tasks.select do |task|
|
|
245
|
+
tags.any? { |tag| task.tags.include?(tag) }
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Auto-archive: if a subtask reaches terminal status and all siblings
|
|
250
|
+
# in the parent directory are also terminal, move the parent to archive.
|
|
251
|
+
def check_auto_archive(task, new_status, loader)
|
|
252
|
+
terminal = Ace::Support::Items::Atoms::FolderCompletionDetector::TERMINAL_STATUSES
|
|
253
|
+
return unless terminal.include?(new_status.to_s.downcase)
|
|
254
|
+
|
|
255
|
+
# Only applies to subtasks (task dir is nested inside a parent dir)
|
|
256
|
+
parent_dir = File.dirname(task.path)
|
|
257
|
+
return if File.expand_path(parent_dir) == File.expand_path(@root_dir)
|
|
258
|
+
|
|
259
|
+
# Check if all specs in the parent dir (recursive for subtask subdirs) are terminal
|
|
260
|
+
return unless Ace::Support::Items::Atoms::FolderCompletionDetector.all_terminal?(
|
|
261
|
+
parent_dir, recursive: true
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Auto-move the parent folder to archive
|
|
265
|
+
parent_stub = Struct.new(:path).new(parent_dir)
|
|
266
|
+
mover = Ace::Support::Items::Molecules::FolderMover.new(@root_dir)
|
|
267
|
+
mover.move(parent_stub, to: "archive")
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def parse_archive_date(task)
|
|
271
|
+
raw = task.metadata["completed_at"] || task.metadata["created_at"]
|
|
272
|
+
return nil unless raw
|
|
273
|
+
|
|
274
|
+
case raw
|
|
275
|
+
when Time then raw
|
|
276
|
+
when DateTime then raw.to_time
|
|
277
|
+
else begin
|
|
278
|
+
Time.parse(raw.to_s)
|
|
279
|
+
rescue
|
|
280
|
+
nil
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def archive_move_for_subtask?(task, move_to)
|
|
286
|
+
normalized = Ace::Support::Items::Atoms::SpecialFolderDetector.normalize(move_to)
|
|
287
|
+
task.subtask? && normalized == "_archive"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def handle_subtask_archive_move(task, loader)
|
|
291
|
+
parent = show(task.parent_id)
|
|
292
|
+
unless parent
|
|
293
|
+
@last_update_note = "Subtask #{task.id} was not archived because parent task '#{task.parent_id}' was not found."
|
|
294
|
+
return {
|
|
295
|
+
path: task.path,
|
|
296
|
+
special_folder: task.special_folder,
|
|
297
|
+
id: task.id
|
|
298
|
+
}
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
parent_with_subtasks = loader.load(parent.path, id: parent.id, special_folder: parent.special_folder)
|
|
302
|
+
subtasks = parent_with_subtasks&.subtasks || []
|
|
303
|
+
all_terminal = subtasks.any? &&
|
|
304
|
+
subtasks.all? { |st| Atoms::TaskValidationRules.terminal_status?(st.status.to_s.downcase) }
|
|
305
|
+
|
|
306
|
+
unless all_terminal
|
|
307
|
+
@last_update_note = "Subtask #{task.id} was not archived because sibling subtasks are not all terminal."
|
|
308
|
+
return {
|
|
309
|
+
path: task.path,
|
|
310
|
+
special_folder: task.special_folder,
|
|
311
|
+
id: task.id
|
|
312
|
+
}
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
unless Atoms::TaskValidationRules.terminal_status?(parent.status.to_s.downcase)
|
|
316
|
+
Ace::Support::Items::Molecules::FieldUpdater.update(
|
|
317
|
+
parent.file_path, set: {"status" => "done"}
|
|
318
|
+
)
|
|
319
|
+
parent = loader.load(parent.path, id: parent.id, special_folder: parent.special_folder)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
mover = Ace::Support::Items::Molecules::FolderMover.new(@root_dir)
|
|
323
|
+
new_parent_path = mover.move(parent, to: "archive", date: parse_archive_date(parent))
|
|
324
|
+
new_special_folder = Ace::Support::Items::Atoms::SpecialFolderDetector.detect_in_path(
|
|
325
|
+
new_parent_path, root: @root_dir
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
@last_update_note = "Archived parent task #{parent.id} because all subtasks are terminal."
|
|
329
|
+
{
|
|
330
|
+
path: File.join(new_parent_path, File.basename(task.path)),
|
|
331
|
+
special_folder: new_special_folder,
|
|
332
|
+
id: task.id
|
|
333
|
+
}
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Value accessor for FilterApplier
|
|
337
|
+
def task_value_accessor(item, key)
|
|
338
|
+
case key
|
|
339
|
+
when "status" then item.status
|
|
340
|
+
when "title" then item.title
|
|
341
|
+
when "tags" then item.tags
|
|
342
|
+
when "id" then item.id
|
|
343
|
+
when "priority" then item.priority
|
|
344
|
+
when "estimate" then item.estimate
|
|
345
|
+
when "special_folder" then item.special_folder
|
|
346
|
+
else
|
|
347
|
+
item.metadata[key] || item.metadata[key.to_sym] if item.respond_to?(:metadata) && item.metadata
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
data/lib/ace/task.rb
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "task/version"
|
|
4
|
+
|
|
5
|
+
# Dependencies
|
|
6
|
+
require "ace/support/items"
|
|
7
|
+
require "ace/b36ts"
|
|
8
|
+
|
|
9
|
+
# Atoms
|
|
10
|
+
require_relative "task/atoms/task_id_formatter"
|
|
11
|
+
require_relative "task/atoms/task_file_pattern"
|
|
12
|
+
require_relative "task/atoms/task_frontmatter_defaults"
|
|
13
|
+
|
|
14
|
+
# Models
|
|
15
|
+
require_relative "task/models/task"
|
|
16
|
+
|
|
17
|
+
# Molecules
|
|
18
|
+
require_relative "task/molecules/task_config_loader"
|
|
19
|
+
require_relative "task/molecules/task_scanner"
|
|
20
|
+
require_relative "task/molecules/task_resolver"
|
|
21
|
+
require_relative "task/molecules/task_loader"
|
|
22
|
+
require_relative "task/molecules/task_creator"
|
|
23
|
+
require_relative "task/molecules/subtask_creator"
|
|
24
|
+
require_relative "task/molecules/task_display_formatter"
|
|
25
|
+
require_relative "task/molecules/path_utils"
|
|
26
|
+
require_relative "task/molecules/task_plan_cache"
|
|
27
|
+
require_relative "task/molecules/task_plan_generator"
|
|
28
|
+
|
|
29
|
+
# Organisms
|
|
30
|
+
require_relative "task/organisms/task_manager"
|
|
31
|
+
|
|
32
|
+
module Ace
|
|
33
|
+
# Task provides B36TS-based task management with type-marked IDs.
|
|
34
|
+
# Tasks use the format xxx.t.yyy where xxx and yyy are 3-char b36ts components.
|
|
35
|
+
module Task
|
|
36
|
+
end
|
|
37
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ace-task
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.31.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Michal Czyz
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ace-support-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.25'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.25'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ace-support-fs
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.2'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.2'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: ace-support-items
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.3'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.3'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: ace-support-markdown
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.2'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.2'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: ace-b36ts
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0.7'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0.7'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: ace-support-cli
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0.3'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0.3'
|
|
96
|
+
description: Behavioral spec-based task management with compact IDs, subtask trees,
|
|
97
|
+
AI-powered implementation plans, and automated health checks. Same CLI for developers
|
|
98
|
+
and coding agents.
|
|
99
|
+
email:
|
|
100
|
+
- mc@cs3b.com
|
|
101
|
+
executables:
|
|
102
|
+
- ace-task
|
|
103
|
+
extensions: []
|
|
104
|
+
extra_rdoc_files: []
|
|
105
|
+
files:
|
|
106
|
+
- ".ace-defaults/nav/protocols/skill-sources/ace-task.yml"
|
|
107
|
+
- ".ace-defaults/nav/protocols/wfi-sources/ace-task.yml"
|
|
108
|
+
- ".ace-defaults/task/config.yml"
|
|
109
|
+
- CHANGELOG.md
|
|
110
|
+
- README.md
|
|
111
|
+
- Rakefile
|
|
112
|
+
- exe/ace-task
|
|
113
|
+
- handbook/guides/task-definition.g.md
|
|
114
|
+
- handbook/skills/as-bug-analyze/SKILL.md
|
|
115
|
+
- handbook/skills/as-bug-fix/SKILL.md
|
|
116
|
+
- handbook/skills/as-task-document-unplanned/SKILL.md
|
|
117
|
+
- handbook/skills/as-task-draft/SKILL.md
|
|
118
|
+
- handbook/skills/as-task-finder/SKILL.md
|
|
119
|
+
- handbook/skills/as-task-plan/SKILL.md
|
|
120
|
+
- handbook/skills/as-task-review-questions/SKILL.md
|
|
121
|
+
- handbook/skills/as-task-review/SKILL.md
|
|
122
|
+
- handbook/skills/as-task-update/SKILL.md
|
|
123
|
+
- handbook/skills/as-task-work/SKILL.md
|
|
124
|
+
- handbook/templates/task/draft.template.md
|
|
125
|
+
- handbook/templates/task/file-modification-checklist.template.md
|
|
126
|
+
- handbook/templates/task/technical-approach.template.md
|
|
127
|
+
- handbook/workflow-instructions/bug/analyze.wf.md
|
|
128
|
+
- handbook/workflow-instructions/bug/fix.wf.md
|
|
129
|
+
- handbook/workflow-instructions/task/document-unplanned.wf.md
|
|
130
|
+
- handbook/workflow-instructions/task/draft.wf.md
|
|
131
|
+
- handbook/workflow-instructions/task/finder.wf.md
|
|
132
|
+
- handbook/workflow-instructions/task/plan.wf.md
|
|
133
|
+
- handbook/workflow-instructions/task/review-plan.wf.md
|
|
134
|
+
- handbook/workflow-instructions/task/review-questions.wf.md
|
|
135
|
+
- handbook/workflow-instructions/task/review-work.wf.md
|
|
136
|
+
- handbook/workflow-instructions/task/review.wf.md
|
|
137
|
+
- handbook/workflow-instructions/task/update.wf.md
|
|
138
|
+
- handbook/workflow-instructions/task/work.wf.md
|
|
139
|
+
- lib/ace/task.rb
|
|
140
|
+
- lib/ace/task/atoms/task_file_pattern.rb
|
|
141
|
+
- lib/ace/task/atoms/task_frontmatter_defaults.rb
|
|
142
|
+
- lib/ace/task/atoms/task_id_formatter.rb
|
|
143
|
+
- lib/ace/task/atoms/task_validation_rules.rb
|
|
144
|
+
- lib/ace/task/cli.rb
|
|
145
|
+
- lib/ace/task/cli/commands/create.rb
|
|
146
|
+
- lib/ace/task/cli/commands/doctor.rb
|
|
147
|
+
- lib/ace/task/cli/commands/list.rb
|
|
148
|
+
- lib/ace/task/cli/commands/plan.rb
|
|
149
|
+
- lib/ace/task/cli/commands/show.rb
|
|
150
|
+
- lib/ace/task/cli/commands/status.rb
|
|
151
|
+
- lib/ace/task/cli/commands/update.rb
|
|
152
|
+
- lib/ace/task/models/task.rb
|
|
153
|
+
- lib/ace/task/molecules/path_utils.rb
|
|
154
|
+
- lib/ace/task/molecules/subtask_creator.rb
|
|
155
|
+
- lib/ace/task/molecules/task_config_loader.rb
|
|
156
|
+
- lib/ace/task/molecules/task_creator.rb
|
|
157
|
+
- lib/ace/task/molecules/task_display_formatter.rb
|
|
158
|
+
- lib/ace/task/molecules/task_doctor_fixer.rb
|
|
159
|
+
- lib/ace/task/molecules/task_doctor_reporter.rb
|
|
160
|
+
- lib/ace/task/molecules/task_frontmatter_validator.rb
|
|
161
|
+
- lib/ace/task/molecules/task_loader.rb
|
|
162
|
+
- lib/ace/task/molecules/task_plan_cache.rb
|
|
163
|
+
- lib/ace/task/molecules/task_plan_generator.rb
|
|
164
|
+
- lib/ace/task/molecules/task_plan_prompt_builder.rb
|
|
165
|
+
- lib/ace/task/molecules/task_reparenter.rb
|
|
166
|
+
- lib/ace/task/molecules/task_resolver.rb
|
|
167
|
+
- lib/ace/task/molecules/task_scanner.rb
|
|
168
|
+
- lib/ace/task/molecules/task_structure_validator.rb
|
|
169
|
+
- lib/ace/task/organisms/task_doctor.rb
|
|
170
|
+
- lib/ace/task/organisms/task_manager.rb
|
|
171
|
+
- lib/ace/task/version.rb
|
|
172
|
+
homepage: https://github.com/cs3b/ace
|
|
173
|
+
licenses:
|
|
174
|
+
- MIT
|
|
175
|
+
metadata:
|
|
176
|
+
allowed_push_host: https://rubygems.org
|
|
177
|
+
homepage_uri: https://github.com/cs3b/ace
|
|
178
|
+
source_code_uri: https://github.com/cs3b/ace/tree/main/ace-task/
|
|
179
|
+
changelog_uri: https://github.com/cs3b/ace/blob/main/ace-task/CHANGELOG.md
|
|
180
|
+
rdoc_options: []
|
|
181
|
+
require_paths:
|
|
182
|
+
- lib
|
|
183
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - ">="
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: 3.2.0
|
|
188
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
|
+
requirements:
|
|
190
|
+
- - ">="
|
|
191
|
+
- !ruby/object:Gem::Version
|
|
192
|
+
version: '0'
|
|
193
|
+
requirements: []
|
|
194
|
+
rubygems_version: 3.6.9
|
|
195
|
+
specification_version: 4
|
|
196
|
+
summary: Draft, organize, and tackle tasks — for you and your agents
|
|
197
|
+
test_files: []
|