Class: Aidp::Database::Repositories::TaskRepository

Inherits:
Aidp::Database::Repository show all
Defined in:
lib/aidp/database/repositories/task_repository.rb

Overview

Repository for tasks table Replaces tasklist.jsonl

Constant Summary collapse

VALID_STATUSES =
%w[pending in_progress done abandoned].freeze
VALID_PRIORITIES =
%w[high medium low].freeze

Instance Attribute Summary

Attributes inherited from Aidp::Database::Repository

#project_dir, #table_name

Instance Method Summary collapse

Constructor Details

#initialize(project_dir: Dir.pwd) ⇒ TaskRepository

Returns a new instance of TaskRepository.



15
16
17
# File 'lib/aidp/database/repositories/task_repository.rb', line 15

def initialize(project_dir: Dir.pwd)
  super(project_dir: project_dir, table_name: "tasks")
end

Instance Method Details

#all(status: nil, priority: nil, since: nil, tags: nil) ⇒ Array<Hash>

Get all tasks with optional filters

Parameters:

  • status (Symbol, String, nil) (defaults to: nil)

    Filter by status

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

    Filter by priority

  • since (Time, nil) (defaults to: nil)

    Filter by created_at

  • tags (Array<String>, nil) (defaults to: nil)

    Filter by tags (any match)

Returns:

  • (Array<Hash>)

    Matching tasks



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/aidp/database/repositories/task_repository.rb', line 125

def all(status: nil, priority: nil, since: nil, tags: nil)
  conditions = ["project_dir = ?"]
  params = [project_dir]

  if status
    conditions << "status = ?"
    params << status.to_s
  end

  if priority
    conditions << "priority = ?"
    params << priority.to_s
  end

  if since
    conditions << "created_at >= ?"
    params << since.strftime("%Y-%m-%d %H:%M:%S")
  end

  sql = <<~SQL
    SELECT * FROM tasks
    WHERE #{conditions.join(" AND ")}
    ORDER BY created_at DESC
  SQL

  rows = query(sql, params)
  tasks = rows.map { |row| deserialize_task(row) }

  # Filter by tags in Ruby (JSON array matching is complex in SQLite)
  if tags && !tags.empty?
    tag_set = tags.map(&:to_s)
    tasks = tasks.select do |task|
      (Array(task[:tags]) & tag_set).any?
    end
  end

  tasks
end

#countsHash

Get task counts by status

Returns:

  • (Hash)

    Counts by status



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/aidp/database/repositories/task_repository.rb', line 181

def counts
  sql = <<~SQL
    SELECT status, COUNT(*) as count
    FROM tasks
    WHERE project_dir = ?
    GROUP BY status
  SQL

  rows = query(sql, [project_dir])
  counts_by_status = rows.each_with_object({}) do |row, h|
    h[row["status"].to_sym] = row["count"]
  end

  {
    total: counts_by_status.values.sum,
    pending: counts_by_status[:pending] || 0,
    in_progress: counts_by_status[:in_progress] || 0,
    done: counts_by_status[:done] || 0,
    abandoned: counts_by_status[:abandoned] || 0
  }
end

#create(description:, priority: "medium", session: nil, discovered_during: nil, tags: []) ⇒ Hash

Create a new task

Parameters:

  • description (String)

    Task description

  • priority (String, Symbol) (defaults to: "medium")

    Task priority (high, medium, low)

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

    Session identifier

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

    When task was discovered

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

    Task tags

Returns:

  • (Hash)

    Created task



27
28
29
30
31
32
33
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
# File 'lib/aidp/database/repositories/task_repository.rb', line 27

def create(description:, priority: "medium", session: nil, discovered_during: nil, tags: [])
  now = current_timestamp
  id = generate_id

  execute(
    insert_sql([
      :id, :project_dir, :description, :priority, :status,
      :tags, :source, :created_at, :updated_at
    ]),
    [
      id,
      project_dir,
      description.strip,
      priority.to_s,
      "pending",
      serialize_json(Array(tags)),
      serialize_json({session: session, discovered_during: discovered_during}),
      now,
      now
    ]
  )

  Aidp.log_debug("task_repository", "created", id: id, description: description)

  {
    id: id,
    description: description.strip,
    status: :pending,
    priority: priority.to_sym,
    tags: Array(tags),
    session: session,
    discovered_during: discovered_during,
    created_at: now,
    updated_at: now
  }
end

#find(task_id) ⇒ Hash?

Find task by ID

Parameters:

  • task_id (String)

    Task ID

Returns:

  • (Hash, nil)

    Task or nil



108
109
110
111
112
113
114
115
116
# File 'lib/aidp/database/repositories/task_repository.rb', line 108

def find(task_id)
  row = query_one(
    "SELECT * FROM tasks WHERE id = ? AND project_dir = ?",
    [task_id, project_dir]
  )
  return nil unless row

  deserialize_task(row)
end

#in_progressArray<Hash>

Get in-progress tasks

Returns:

  • (Array<Hash>)

    In-progress tasks



174
175
176
# File 'lib/aidp/database/repositories/task_repository.rb', line 174

def in_progress
  all(status: :in_progress)
end

#pendingArray<Hash>

Get pending tasks

Returns:

  • (Array<Hash>)

    Pending tasks



167
168
169
# File 'lib/aidp/database/repositories/task_repository.rb', line 167

def pending
  all(status: :pending)
end

#update_status(task_id, new_status, reason: nil) ⇒ Hash?

Update task status

Parameters:

  • task_id (String)

    Task ID

  • new_status (String, Symbol)

    New status

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

    Reason for status change (for abandoned)

Returns:

  • (Hash, nil)

    Updated task or nil if not found



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
97
98
99
100
101
102
# File 'lib/aidp/database/repositories/task_repository.rb', line 70

def update_status(task_id, new_status, reason: nil)
  task = find(task_id)
  return nil unless task

  now = current_timestamp
  status_str = new_status.to_s

  updates = {status: status_str, updated_at: now}

  case status_str
  when "in_progress"
    updates[:started_at] = now unless task[:started_at]
  when "done"
    updates[:completed_at] = now
  when "abandoned"
    # Store reason in source JSON
    source = task[:source] || {}
    source[:abandoned_reason] = reason
    updates[:source] = serialize_json(source)
  end

  set_clauses = updates.keys.map { |k| "#{k} = ?" }.join(", ")
  values = updates.values + [task_id, project_dir]

  execute(
    "UPDATE tasks SET #{set_clauses} WHERE id = ? AND project_dir = ?",
    values
  )

  Aidp.log_debug("task_repository", "status_updated", id: task_id, status: status_str)

  find(task_id)
end