ace-task 0.39.2 → 0.39.3

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.ace-defaults/nav/protocols/skill-sources/ace-task.yml +0 -0
  3. data/.ace-defaults/nav/protocols/wfi-sources/ace-task.yml +0 -0
  4. data/.ace-defaults/task/config.yml +0 -0
  5. data/CHANGELOG.md +7 -0
  6. data/README.md +0 -0
  7. data/Rakefile +0 -0
  8. data/handbook/guides/task-definition.g.md +0 -0
  9. data/handbook/skills/as-bug-analyze/SKILL.md +0 -0
  10. data/handbook/skills/as-bug-fix/SKILL.md +0 -0
  11. data/handbook/skills/as-task-document-unplanned/SKILL.md +0 -0
  12. data/handbook/skills/as-task-draft/SKILL.md +0 -0
  13. data/handbook/skills/as-task-finder/SKILL.md +0 -0
  14. data/handbook/skills/as-task-plan/SKILL.md +0 -0
  15. data/handbook/skills/as-task-review/SKILL.md +0 -0
  16. data/handbook/skills/as-task-review-questions/SKILL.md +0 -0
  17. data/handbook/skills/as-task-update/SKILL.md +0 -0
  18. data/handbook/skills/as-task-work/SKILL.md +0 -0
  19. data/handbook/templates/task/draft.template.md +0 -0
  20. data/handbook/templates/task/file-modification-checklist.template.md +0 -0
  21. data/handbook/templates/task/technical-approach.template.md +0 -0
  22. data/handbook/workflow-instructions/bug/analyze.wf.md +0 -0
  23. data/handbook/workflow-instructions/bug/fix.wf.md +0 -0
  24. data/handbook/workflow-instructions/task/document-unplanned.wf.md +0 -0
  25. data/handbook/workflow-instructions/task/finder.wf.md +0 -0
  26. data/handbook/workflow-instructions/task/plan.wf.md +0 -0
  27. data/handbook/workflow-instructions/task/review-plan.wf.md +0 -0
  28. data/handbook/workflow-instructions/task/review-questions.wf.md +0 -0
  29. data/handbook/workflow-instructions/task/review-work.wf.md +0 -0
  30. data/handbook/workflow-instructions/task/review.wf.md +0 -0
  31. data/handbook/workflow-instructions/task/update.wf.md +0 -0
  32. data/lib/ace/task/atoms/task_file_pattern.rb +0 -0
  33. data/lib/ace/task/atoms/task_id_formatter.rb +0 -0
  34. data/lib/ace/task/atoms/task_validation_rules.rb +0 -0
  35. data/lib/ace/task/cli/commands/create.rb +122 -4
  36. data/lib/ace/task/cli/commands/doctor.rb +0 -0
  37. data/lib/ace/task/cli/commands/plan.rb +0 -0
  38. data/lib/ace/task/cli/commands/show.rb +0 -0
  39. data/lib/ace/task/cli/commands/status.rb +0 -0
  40. data/lib/ace/task/models/task.rb +0 -0
  41. data/lib/ace/task/molecules/path_utils.rb +0 -0
  42. data/lib/ace/task/molecules/task_config_loader.rb +0 -0
  43. data/lib/ace/task/molecules/task_doctor_reporter.rb +0 -0
  44. data/lib/ace/task/molecules/task_loader.rb +0 -0
  45. data/lib/ace/task/molecules/task_plan_cache.rb +0 -0
  46. data/lib/ace/task/molecules/task_plan_generator.rb +0 -0
  47. data/lib/ace/task/molecules/task_structure_validator.rb +0 -0
  48. data/lib/ace/task/organisms/task_doctor.rb +0 -0
  49. data/lib/ace/task/version.rb +1 -1
  50. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 425d8beebe8b18f6d816767d85d9f1b0a29c68a470b8c6cf4590629b2028bddd
4
- data.tar.gz: a28fc844b081cc3f9b969e2ab15a674df2dca08bb30fc48cd90b1741d3024663
3
+ metadata.gz: bf3a2b4bdbf7c413363c5f9b81a0f0b4161e6b50e58cce724adcb67ba03fc1c2
4
+ data.tar.gz: b08a3d0f30ad8dfb455c6232bde690e632a9028a603ac37fa289a4768c2f0312
5
5
  SHA512:
6
- metadata.gz: c2be6996b2753ec7ff709cba139a8533e2b746186e7ba3c29b26e51d9361d163e99a022bc5c05a39db372e71ee5a843c64f4f06fbe5b66298e7488b922da27d5
7
- data.tar.gz: b976035a868e0d4d433b956d41520d5b7a2670aff609754bd975797a6bc6a0417ca1e3fe364d429c65dfe9edfc8c4e9609ad20accfb0db4f2743c8b26ec304a5
6
+ metadata.gz: ea63fb03267e6eb84875128ff0ec56becc9fbc0a650a40344cf9a1b11090a61791eed9c97a203ce8a231b132534d442d0238d93cac07162ecd96f522cdd930be
7
+ data.tar.gz: 2be684d6224ec2a57dc9cc4f84eafdee43c14e1b044116e470f12b51dd1cab952267ff9b4a05d96f6e2df5eb4ef1b10d0bd52e32a31e53275a8cdca7e43b0626
File without changes
File without changes
data/CHANGELOG.md CHANGED
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.39.3] - 2026-10-12
11
+
12
+ ### Fixed
13
+
14
+ - Route `ace-task create --in FOLDER` through the task manager's relocation API
15
+ and remove the newly created task artifacts when destination placement fails.
16
+
10
17
  ## [0.39.2] - 2026-10-06
11
18
 
12
19
  ### Fixed
data/README.md CHANGED
File without changes
data/Rakefile CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "ace/support/cli"
4
+ require "fileutils"
4
5
 
5
6
  module Ace
6
7
  module Task
@@ -10,6 +11,9 @@ module Ace
10
11
  class Create < Ace::Support::Cli::Command
11
12
  include Ace::Support::Cli::Base
12
13
 
14
+ # "next" is a move-to-root alias and is validated separately.
15
+ BUILT_IN_DESTINATIONS = %w[maybe anytime archive].freeze
16
+
13
17
  desc <<~DESC.strip
14
18
  Create a new task
15
19
 
@@ -38,7 +42,8 @@ module Ace
38
42
  option :server, type: :string, desc: "Named forge server"
39
43
  option :"default-server", type: :boolean, desc: "Use the configured default forge server"
40
44
  option :"child-of", type: :string, desc: "Parent task reference (creates subtask)"
41
- option :in, type: :string, aliases: %w[-i], desc: "Target folder (e.g. next, maybe)"
45
+ option :in, type: :string, aliases: %w[-i],
46
+ desc: "Top-level target (next, maybe, anytime, archive, or existing custom folder)"
42
47
  option :"dry-run", type: :boolean, aliases: %w[-n], desc: "Preview without writing"
43
48
 
44
49
  option :git_commit, type: :boolean, aliases: %w[--gc], desc: "Auto-commit changes"
@@ -72,6 +77,10 @@ module Ace
72
77
  end
73
78
  end
74
79
 
80
+ manager = Ace::Task::Organisms::TaskManager.new
81
+ validate_destination!(manager, in_folder) if in_folder && !child_of
82
+ placement_directories_before = directories_under(manager.root_dir) if in_folder && !child_of
83
+
75
84
  if dry_run
76
85
  puts "Would create task:"
77
86
  puts " Title: #{title}"
@@ -85,8 +94,6 @@ module Ace
85
94
  return
86
95
  end
87
96
 
88
- manager = Ace::Task::Organisms::TaskManager.new
89
-
90
97
  task = begin
91
98
  if child_of
92
99
  manager.create_subtask(
@@ -119,7 +126,7 @@ module Ace
119
126
 
120
127
  # Move to folder if specified
121
128
  if in_folder && !child_of
122
- task = manager.move(task.id, to: in_folder)
129
+ task = place_created_task(manager, task, in_folder, placement_directories_before)
123
130
  end
124
131
 
125
132
  puts "Created task #{task.id}"
@@ -140,6 +147,117 @@ module Ace
140
147
 
141
148
  private
142
149
 
150
+ def validate_destination!(manager, target_folder)
151
+ detector = Ace::Support::Items::Atoms::SpecialFolderDetector
152
+ return if detector.move_to_root?(target_folder)
153
+
154
+ if detector.virtual_filter?(target_folder)
155
+ raise Ace::Support::Cli::Error,
156
+ "Destination '#{target_folder}' is a virtual list filter, not a physical folder."
157
+ end
158
+
159
+ normalized = detector.normalize(target_folder)
160
+ configured_path = File.join(manager.root_dir, normalized)
161
+ normalized_built_ins = BUILT_IN_DESTINATIONS.map { |name| detector.normalize(name) }
162
+ valid = !target_folder.strip.empty? &&
163
+ !normalized.include?(File::SEPARATOR) &&
164
+ !normalized.include?("..") &&
165
+ (normalized_built_ins.include?(normalized) || Dir.exist?(configured_path)) &&
166
+ destination_within_root?(manager.root_dir, configured_path)
167
+ return if valid
168
+
169
+ choices = (["next"] + BUILT_IN_DESTINATIONS).join(", ")
170
+ raise Ace::Support::Cli::Error,
171
+ "Destination '#{target_folder}' does not exist. " \
172
+ "Use #{choices}, or an existing custom task folder."
173
+ end
174
+
175
+ def destination_within_root?(root, destination)
176
+ return true unless File.exist?(destination)
177
+
178
+ root_path = File.realpath(root)
179
+ destination_path = File.realpath(destination)
180
+ destination_path.start_with?(root_path + File::SEPARATOR)
181
+ rescue Errno::ENOENT, Errno::EACCES
182
+ false
183
+ end
184
+
185
+ def place_created_task(manager, task, target_folder, directories_before)
186
+ placed_task = manager.update(task.id, move_to: target_folder)
187
+ expected_folder = if Ace::Support::Items::Atoms::SpecialFolderDetector.move_to_root?(target_folder)
188
+ nil
189
+ else
190
+ Ace::Support::Items::Atoms::SpecialFolderDetector.normalize(target_folder)
191
+ end
192
+
193
+ unless placed_task && placed_task.special_folder == expected_folder
194
+ actual_folder = if placed_task
195
+ placed_task.special_folder || "root"
196
+ else
197
+ "no task returned"
198
+ end
199
+ # This internal signal is wrapped below with rollback and an actionable CLI error.
200
+ raise "placement returned #{actual_folder.inspect}, expected #{expected_folder || "root"}"
201
+ end
202
+
203
+ placed_task
204
+ rescue Ace::Git::Error
205
+ # update retains linked tasks after post-placement sync failures as
206
+ # recovery records; those are not destination-placement failures.
207
+ raise
208
+ rescue StandardError => e
209
+ begin
210
+ remove_created_task_artifacts(manager, task)
211
+ remove_empty_directories_created_since(manager.root_dir, directories_before)
212
+ rescue StandardError => cleanup_error
213
+ raise Ace::Support::Cli::Error,
214
+ "Failed to create task in '#{target_folder}': #{e.message}. " \
215
+ "Cleanup also failed: #{cleanup_error.message}"
216
+ end
217
+ raise Ace::Support::Cli::Error,
218
+ "Failed to create task in '#{target_folder}': #{e.message}. " \
219
+ "No task was created; fix the destination and retry."
220
+ end
221
+
222
+ def remove_created_task_artifacts(manager, task)
223
+ root = File.expand_path(manager.root_dir)
224
+ candidates = [task.path]
225
+ candidates.concat(
226
+ Dir.glob(File.join(root, "**", "#{task.id}-*")).select { |path| File.directory?(path) }
227
+ )
228
+
229
+ candidates.uniq.each do |path|
230
+ expanded = File.expand_path(path)
231
+ next unless expanded.start_with?(root + File::SEPARATOR)
232
+
233
+ FileUtils.rm_rf(expanded)
234
+ end
235
+
236
+ remaining = Dir.glob(File.join(root, "**", "#{task.id}-*")).select { |path| File.exist?(path) }
237
+ return if remaining.empty?
238
+
239
+ raise Ace::Support::Cli::Error,
240
+ "Failed to remove partial task artifacts after placement failure: #{remaining.join(", ")}"
241
+ end
242
+
243
+ def directories_under(root)
244
+ return [] unless Dir.exist?(root)
245
+
246
+ [File.expand_path(root)] + Dir.glob(File.join(root, "**", "*"))
247
+ .select { |path| File.directory?(path) }
248
+ .map { |path| File.expand_path(path) }
249
+ end
250
+
251
+ def remove_empty_directories_created_since(root, directories_before)
252
+ # Only directories created during this short command transaction
253
+ # are candidates, and non-empty concurrent directories are retained.
254
+ (directories_under(root) - directories_before).sort_by(&:length).reverse_each do |path|
255
+ Dir.rmdir(path)
256
+ rescue Errno::ENOENT, Errno::ENOTEMPTY
257
+ next
258
+ end
259
+ end
260
+
143
261
  def resolve_title(positional_title, flag_title)
144
262
  positional_present = present_string?(positional_title)
145
263
  flag_present = present_string?(flag_title)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Task
5
- VERSION = "0.39.2"
5
+ VERSION = "0.39.3"
6
6
  end
7
7
  end
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.39.2
4
+ version: 0.39.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-08 00:00:00.000000000 Z
10
+ date: 2026-10-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-support-core