Class: Ask::Agent::TodoList
- Inherits:
-
Object
- Object
- Ask::Agent::TodoList
- Defined in:
- lib/ask/agent/todo_list.rb
Overview
Session-scoped task list maintained by the model through the TodoWrite tool.
The list is the externalized plan: the model writes it once and checks it against each step, humans see progress live (via the TodoUpdated event), and checkpoints carry it across rollbacks and forks.
Defined Under Namespace
Classes: Entry
Constant Summary collapse
- STATUSES =
%w[pending in_progress completed blocked].freeze
Instance Method Summary collapse
- #add(title, status: "pending") ⇒ Entry
-
#all ⇒ Array<Entry>
Snapshot of the current entries.
- #clear ⇒ void
-
#initialize ⇒ TodoList
constructor
A new instance of TodoList.
-
#restore(data) ⇒ void
Rebuild from a serialized snapshot (rollback, fork, load).
-
#subscribe(&block) ⇒ self
Register a listener called with the full entry list after every change (used to emit TodoUpdated events for live rendering).
-
#to_h ⇒ Hash
Serialized form for persistence (checkpoints).
-
#to_s ⇒ String
Human-readable list.
-
#update(id, status: nil, title: nil) ⇒ Entry
The updated entry.
Constructor Details
#initialize ⇒ TodoList
Returns a new instance of TodoList.
18 19 20 21 22 23 |
# File 'lib/ask/agent/todo_list.rb', line 18 def initialize @entries = [] @next_id = 1 @mutex = Mutex.new @listeners = [] end |
Instance Method Details
#add(title, status: "pending") ⇒ Entry
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/ask/agent/todo_list.rb', line 43 def add(title, status: "pending") raise ArgumentError, "title is required" if title.to_s.strip.empty? entry = @mutex.synchronize do e = Entry.new(id: "todo_#{@next_id}", title: title.to_s, status: validate_status(status || "pending")) @next_id += 1 @entries << e e end notify entry end |
#all ⇒ Array<Entry>
Returns snapshot of the current entries.
26 27 28 |
# File 'lib/ask/agent/todo_list.rb', line 26 def all @mutex.synchronize { @entries.dup } end |
#clear ⇒ void
This method returns an undefined value.
78 79 80 81 82 |
# File 'lib/ask/agent/todo_list.rb', line 78 def clear @mutex.synchronize { @entries.clear } notify nil end |
#restore(data) ⇒ void
This method returns an undefined value.
Rebuild from a serialized snapshot (rollback, fork, load). Fires no events.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/ask/agent/todo_list.rb', line 94 def restore(data) @mutex.synchronize do raw = Array(data&.dig(:entries) || data&.dig("entries") || []) @entries = raw.map do |e| Entry.new( id: (e[:id] || e["id"]).to_s, title: (e[:title] || e["title"]).to_s, status: (e[:status] || e["status"]).to_s ) end @next_id = @entries.size + 1 end nil end |
#subscribe(&block) ⇒ self
Register a listener called with the full entry list after every change (used to emit TodoUpdated events for live rendering).
34 35 36 37 |
# File 'lib/ask/agent/todo_list.rb', line 34 def subscribe(&block) @listeners << block self end |
#to_h ⇒ Hash
Returns serialized form for persistence (checkpoints).
85 86 87 |
# File 'lib/ask/agent/todo_list.rb', line 85 def to_h { entries: all.map(&:to_h) } end |
#to_s ⇒ String
Returns human-readable list.
110 111 112 113 114 115 |
# File 'lib/ask/agent/todo_list.rb', line 110 def to_s entries = all return "(no todos)" if entries.empty? entries.map { |e| "[#{e.status}] #{e.title} (#{e.id})" }.join("\n") end |
#update(id, status: nil, title: nil) ⇒ Entry
Returns the updated entry.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/ask/agent/todo_list.rb', line 61 def update(id, status: nil, title: nil) entry = @mutex.synchronize do index = @entries.index { |e| e.id == id } raise ArgumentError, "no todo with id #{id.inspect}" unless index @entries[index] = Entry.new( id: id, title: title.nil? ? @entries[index].title : title.to_s, status: status.nil? ? @entries[index].status : validate_status(status) ) @entries[index] end notify entry end |