Class: Ace::Task::Molecules::TaskCreator

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/task/molecules/task_creator.rb

Overview

Creates new tasks with B36TS-based type-marked IDs. Generates folder structure and spec file with full frontmatter. Optionally uses LLM for slug generation with deterministic fallback.

Defined Under Namespace

Classes: IdCollisionError

Instance Method Summary collapse

Constructor Details

#initialize(root_dir:, config: {}) ⇒ TaskCreator

Returns a new instance of TaskCreator.

Parameters:

  • root_dir (String)

    Root directory for tasks

  • config (Hash) (defaults to: {})

    Configuration hash



20
21
22
23
# File 'lib/ace/task/molecules/task_creator.rb', line 20

def initialize(root_dir:, config: {})
  @root_dir = root_dir
  @config = config
end

Instance Method Details

#create(title, status: nil, priority: nil, tags: [], dependencies: [], time: Time.now.utc, use_llm_slug: false, estimate: nil, github_issue: nil) ⇒ Models::Task

Create a new task

Parameters:

  • title (String)

    Task title

  • status (String) (defaults to: nil)

    Initial status (default: from config or “pending”)

  • priority (String, nil) (defaults to: nil)

    Priority level

  • tags (Array<String>) (defaults to: [])

    Tags

  • dependencies (Array<String>) (defaults to: [])

    Dependency task IDs

  • time (Time) (defaults to: Time.now.utc)

    Creation time (default: now)

  • use_llm_slug (Boolean) (defaults to: false)

    Whether to attempt LLM slug generation

Returns:

Raises:

  • (ArgumentError)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ace/task/molecules/task_creator.rb', line 34

def create(
  title,
  status: nil,
  priority: nil,
  tags: [],
  dependencies: [],
  time: Time.now.utc,
  use_llm_slug: false,
  estimate: nil,
  github_issue: nil
)
  raise ArgumentError, "Title is required" if title.nil? || title.strip.empty?

  # Generate task ID
  item_id = Atoms::TaskIdFormatter.generate(time)
  formatted_id = item_id.formatted_id

  with_id_reservation(formatted_id) do
    raise IdCollisionError, "Task ID collision detected for #{formatted_id}" if task_id_exists?(formatted_id)

    # Generate slugs
    slugs = if use_llm_slug
      generate_llm_slugs(title) || generate_slugs(title)
    else
      generate_slugs(title)
    end
    folder_slug = slugs[:folder]
    file_slug = slugs[:file]

    # Create folder with folder_slug
    folder_name = Atoms::TaskIdFormatter.folder_name(formatted_id, folder_slug)
    task_dir = File.join(@root_dir, folder_name)
    FileUtils.mkdir_p(task_dir)

    begin
      # Build frontmatter with all fields
      effective_status = status || @config.dig("task", "default_status") || "pending"
      frontmatter = Atoms::TaskFrontmatterDefaults.build(
        id: formatted_id,
        status: effective_status,
        priority: priority,
        tags: tags,
        dependencies: dependencies,
        created_at: time,
        estimate: estimate,
        github_issue: github_issue
      )

      # Write spec file with file_slug
      spec_filename = Atoms::TaskIdFormatter.spec_filename(formatted_id, file_slug)
      spec_file = File.join(task_dir, spec_filename)
      content = build_spec_content(frontmatter: frontmatter, title: title)
      File.write(spec_file, content)

      # Load and return the created task
      loader = TaskLoader.new
      loader.load(task_dir, id: formatted_id)
    rescue StandardError
      FileUtils.rm_rf(task_dir) if Dir.exist?(task_dir)
      raise
    end
  end
end