ruby_todo 1.0.3 → 1.0.4
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 +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +60 -75
- data/ai_assistant_implementation.md +1 -1
- data/lib/ruby_todo/ai_assistant/command_processor.rb +127 -0
- data/lib/ruby_todo/ai_assistant/openai_integration.rb +178 -183
- data/lib/ruby_todo/ai_assistant/param_extractor.rb +64 -0
- data/lib/ruby_todo/ai_assistant/prompt_builder.rb +95 -0
- data/lib/ruby_todo/ai_assistant/task_creator.rb +161 -0
- data/lib/ruby_todo/cli.rb +128 -469
- data/lib/ruby_todo/commands/ai_assistant.rb +752 -287
- data/lib/ruby_todo/commands/ai_commands.rb +20 -0
- data/lib/ruby_todo/commands/notebook_commands.rb +39 -0
- data/lib/ruby_todo/commands/template_commands.rb +139 -0
- data/lib/ruby_todo/concerns/import_export.rb +138 -0
- data/lib/ruby_todo/concerns/statistics.rb +166 -0
- data/lib/ruby_todo/concerns/task_filters.rb +39 -0
- data/lib/ruby_todo/formatters/display_formatter.rb +80 -0
- data/lib/ruby_todo/models/task.rb +0 -7
- data/lib/ruby_todo/version.rb +1 -1
- data/lib/ruby_todo.rb +9 -0
- metadata +13 -8
- data/.env.template +0 -2
- data/lib/ruby_todo/ai_assistant/common_query_handler.rb +0 -378
- data/lib/ruby_todo/ai_assistant/task_management.rb +0 -331
- data/lib/ruby_todo/ai_assistant/task_search.rb +0 -365
- data/test_ai_assistant.rb +0 -55
- data/test_migration.rb +0 -55
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_todo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremiah Parrack
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -200,7 +200,6 @@ executables:
|
|
200
200
|
extensions: []
|
201
201
|
extra_rdoc_files: []
|
202
202
|
files:
|
203
|
-
- ".env.template"
|
204
203
|
- ".rspec"
|
205
204
|
- ".rubocop.yml"
|
206
205
|
- CHANGELOG.md
|
@@ -216,15 +215,23 @@ files:
|
|
216
215
|
- exe/ruby_todo
|
217
216
|
- implementation_steps.md
|
218
217
|
- lib/ruby_todo.rb
|
219
|
-
- lib/ruby_todo/ai_assistant/
|
218
|
+
- lib/ruby_todo/ai_assistant/command_processor.rb
|
220
219
|
- lib/ruby_todo/ai_assistant/configuration_management.rb
|
221
220
|
- lib/ruby_todo/ai_assistant/openai_integration.rb
|
221
|
+
- lib/ruby_todo/ai_assistant/param_extractor.rb
|
222
|
+
- lib/ruby_todo/ai_assistant/prompt_builder.rb
|
222
223
|
- lib/ruby_todo/ai_assistant/task_creation.rb
|
223
|
-
- lib/ruby_todo/ai_assistant/
|
224
|
-
- lib/ruby_todo/ai_assistant/task_search.rb
|
224
|
+
- lib/ruby_todo/ai_assistant/task_creator.rb
|
225
225
|
- lib/ruby_todo/cli.rb
|
226
226
|
- lib/ruby_todo/commands/ai_assistant.rb
|
227
|
+
- lib/ruby_todo/commands/ai_commands.rb
|
228
|
+
- lib/ruby_todo/commands/notebook_commands.rb
|
229
|
+
- lib/ruby_todo/commands/template_commands.rb
|
230
|
+
- lib/ruby_todo/concerns/import_export.rb
|
231
|
+
- lib/ruby_todo/concerns/statistics.rb
|
232
|
+
- lib/ruby_todo/concerns/task_filters.rb
|
227
233
|
- lib/ruby_todo/database.rb
|
234
|
+
- lib/ruby_todo/formatters/display_formatter.rb
|
228
235
|
- lib/ruby_todo/models/notebook.rb
|
229
236
|
- lib/ruby_todo/models/task.rb
|
230
237
|
- lib/ruby_todo/models/template.rb
|
@@ -232,8 +239,6 @@ files:
|
|
232
239
|
- progress_ai_test.md
|
233
240
|
- protectors_tasks.json
|
234
241
|
- sig/ruby_todo.rbs
|
235
|
-
- test_ai_assistant.rb
|
236
|
-
- test_migration.rb
|
237
242
|
homepage: https://github.com/jeremiahlukus/ruby_todo
|
238
243
|
licenses:
|
239
244
|
- MIT
|
data/.env.template
DELETED
@@ -1,378 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RubyTodo
|
4
|
-
module TaskStatistics
|
5
|
-
private
|
6
|
-
|
7
|
-
def handle_statistics_query(prompt)
|
8
|
-
say "\nHandling statistics query" if options[:verbose]
|
9
|
-
|
10
|
-
if prompt =~ /\b(?:show|display|get)\s+(?:me\s+)?(?:the\s+)?stats\b/i
|
11
|
-
display_task_statistics
|
12
|
-
return true
|
13
|
-
end
|
14
|
-
|
15
|
-
false
|
16
|
-
end
|
17
|
-
|
18
|
-
def display_task_statistics
|
19
|
-
notebooks = RubyTodo::Notebook.all
|
20
|
-
total_stats = { todo: 0, in_progress: 0, done: 0, archived: 0 }
|
21
|
-
|
22
|
-
notebooks.each do |notebook|
|
23
|
-
stats = notebook.task_statistics
|
24
|
-
total_stats.merge!(stats) { |_key, old_val, new_val| old_val + new_val }
|
25
|
-
|
26
|
-
display_notebook_statistics(notebook, stats)
|
27
|
-
end
|
28
|
-
|
29
|
-
display_total_statistics(total_stats)
|
30
|
-
end
|
31
|
-
|
32
|
-
def display_notebook_statistics(notebook, stats)
|
33
|
-
say "\nNotebook: #{notebook.name}".blue
|
34
|
-
say " Todo: #{stats[:todo]}".yellow
|
35
|
-
say " In Progress: #{stats[:in_progress]}".blue
|
36
|
-
say " Done: #{stats[:done]}".green
|
37
|
-
say " Archived: #{stats[:archived]}".gray
|
38
|
-
end
|
39
|
-
|
40
|
-
def display_total_statistics(stats)
|
41
|
-
say "\nTotal Statistics:".blue
|
42
|
-
say " Todo: #{stats[:todo]}".yellow
|
43
|
-
say " In Progress: #{stats[:in_progress]}".blue
|
44
|
-
say " Done: #{stats[:done]}".green
|
45
|
-
say " Archived: #{stats[:archived]}".gray
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
module TaskPriority
|
50
|
-
private
|
51
|
-
|
52
|
-
def handle_priority_query(prompt)
|
53
|
-
say "\nHandling priority query" if options[:verbose]
|
54
|
-
|
55
|
-
if prompt =~ /\b(?:show|display|get|list)\s+(?:me\s+)?(?:the\s+)?(?:high|medium|low)\s*-?\s*priority\b/i
|
56
|
-
display_priority_tasks(prompt)
|
57
|
-
return true
|
58
|
-
end
|
59
|
-
|
60
|
-
false
|
61
|
-
end
|
62
|
-
|
63
|
-
def display_priority_tasks(prompt)
|
64
|
-
priority = extract_priority_level(prompt)
|
65
|
-
tasks = find_priority_tasks(priority)
|
66
|
-
|
67
|
-
if tasks.any?
|
68
|
-
display_tasks_by_priority(tasks, priority)
|
69
|
-
else
|
70
|
-
say "No #{priority} priority tasks found.".yellow
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def extract_priority_level(prompt)
|
75
|
-
if prompt =~ /\b(high|medium|low)\s*-?\s*priority\b/i
|
76
|
-
Regexp.last_match(1).downcase
|
77
|
-
else
|
78
|
-
"high" # Default to high priority
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def find_priority_tasks(priority)
|
83
|
-
tasks = []
|
84
|
-
RubyTodo::Notebook.all.each do |notebook|
|
85
|
-
notebook.tasks.each do |task|
|
86
|
-
next unless task_matches_priority?(task, priority)
|
87
|
-
|
88
|
-
tasks << {
|
89
|
-
task_id: task.id,
|
90
|
-
title: task.title,
|
91
|
-
status: task.status,
|
92
|
-
notebook: notebook.name
|
93
|
-
}
|
94
|
-
end
|
95
|
-
end
|
96
|
-
tasks
|
97
|
-
end
|
98
|
-
|
99
|
-
def task_matches_priority?(task, priority)
|
100
|
-
return false unless task.tags
|
101
|
-
|
102
|
-
case priority
|
103
|
-
when "high"
|
104
|
-
task.tags.downcase.include?("high") || task.tags.downcase.include?("urgent")
|
105
|
-
when "medium"
|
106
|
-
task.tags.downcase.include?("medium") || task.tags.downcase.include?("normal")
|
107
|
-
when "low"
|
108
|
-
task.tags.downcase.include?("low")
|
109
|
-
else
|
110
|
-
false
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def display_tasks_by_priority(tasks, priority)
|
115
|
-
say "\n#{priority.capitalize} Priority Tasks:".blue
|
116
|
-
tasks.each do |task|
|
117
|
-
status_color = case task[:status]
|
118
|
-
when "todo" then :yellow
|
119
|
-
when "in_progress" then :blue
|
120
|
-
when "done" then :green
|
121
|
-
else :white
|
122
|
-
end
|
123
|
-
|
124
|
-
say " [#{task[:notebook]}] Task #{task[:task_id]}: #{task[:title]}".send(status_color)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
module TaskDeadlines
|
130
|
-
private
|
131
|
-
|
132
|
-
def handle_deadline_query(prompt)
|
133
|
-
say "\nHandling deadline query" if options[:verbose]
|
134
|
-
# rubocop:disable Layout/LineLength
|
135
|
-
deadline_pattern = /\b(?:show|display|get|list)\s+(?:me\s+)?(?:the\s+)?(?:upcoming|due|overdue)\s+(?:tasks|deadlines)\b/
|
136
|
-
# rubocop:enable Layout/LineLength
|
137
|
-
if prompt =~ /#{deadline_pattern}/i
|
138
|
-
display_deadline_tasks(prompt)
|
139
|
-
return true
|
140
|
-
end
|
141
|
-
|
142
|
-
false
|
143
|
-
end
|
144
|
-
|
145
|
-
def display_deadline_tasks(prompt)
|
146
|
-
deadline_type = extract_deadline_type(prompt)
|
147
|
-
tasks = find_deadline_tasks(deadline_type)
|
148
|
-
|
149
|
-
if tasks.any?
|
150
|
-
display_tasks_by_deadline(tasks, deadline_type)
|
151
|
-
else
|
152
|
-
say "No #{deadline_type} tasks found.".yellow
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def extract_deadline_type(prompt)
|
157
|
-
if prompt =~ /\b(upcoming|due|overdue)\b/i
|
158
|
-
Regexp.last_match(1).downcase
|
159
|
-
else
|
160
|
-
"upcoming" # Default to upcoming
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def find_deadline_tasks(deadline_type)
|
165
|
-
tasks = []
|
166
|
-
RubyTodo::Notebook.all.each do |notebook|
|
167
|
-
notebook.tasks.each do |task|
|
168
|
-
next unless task_matches_deadline?(task, deadline_type)
|
169
|
-
|
170
|
-
tasks << {
|
171
|
-
task_id: task.id,
|
172
|
-
title: task.title,
|
173
|
-
status: task.status,
|
174
|
-
notebook: notebook.name,
|
175
|
-
deadline: task.deadline
|
176
|
-
}
|
177
|
-
end
|
178
|
-
end
|
179
|
-
tasks
|
180
|
-
end
|
181
|
-
|
182
|
-
def task_matches_deadline?(task, deadline_type)
|
183
|
-
return false unless task.deadline
|
184
|
-
|
185
|
-
case deadline_type
|
186
|
-
when "upcoming"
|
187
|
-
task.deadline > Time.now && task.deadline <= Time.now + (7 * 24 * 60 * 60) # 7 days
|
188
|
-
when "due"
|
189
|
-
task.deadline <= Time.now + (24 * 60 * 60) # 1 day
|
190
|
-
when "overdue"
|
191
|
-
task.deadline < Time.now
|
192
|
-
else
|
193
|
-
false
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def display_tasks_by_deadline(tasks, deadline_type)
|
198
|
-
say "\n#{deadline_type.capitalize} Tasks:".blue
|
199
|
-
tasks.each do |task|
|
200
|
-
deadline_str = task[:deadline].strftime("%Y-%m-%d %H:%M")
|
201
|
-
status_color = case task[:status]
|
202
|
-
when "todo" then :yellow
|
203
|
-
when "in_progress" then :blue
|
204
|
-
when "done" then :green
|
205
|
-
else :white
|
206
|
-
end
|
207
|
-
|
208
|
-
say " [#{task[:notebook]}] Task #{task[:task_id]}: " \
|
209
|
-
"#{task[:title]} (Due: #{deadline_str})".send(status_color)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
module TaskCreation
|
215
|
-
private
|
216
|
-
|
217
|
-
def handle_task_creation(prompt, prompt_lower)
|
218
|
-
say "\n=== Detecting task creation request ===" if options[:verbose]
|
219
|
-
|
220
|
-
title = extract_task_title(prompt)
|
221
|
-
return false unless title
|
222
|
-
|
223
|
-
notebook_name = determine_notebook_name(prompt_lower)
|
224
|
-
return false unless notebook_name
|
225
|
-
|
226
|
-
priority = determine_priority(prompt_lower)
|
227
|
-
|
228
|
-
create_task(notebook_name, title, priority)
|
229
|
-
true
|
230
|
-
end
|
231
|
-
|
232
|
-
def extract_task_title(prompt)
|
233
|
-
# Try to extract title from quotes first
|
234
|
-
title_match = prompt.match(/'([^']+)'|"([^"]+)"/)
|
235
|
-
|
236
|
-
if title_match
|
237
|
-
title_match[1] || title_match[2]
|
238
|
-
else
|
239
|
-
# If no quoted title found, try extracting from the prompt
|
240
|
-
extract_title_from_text(prompt)
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
def extract_title_from_text(prompt)
|
245
|
-
potential_title = prompt
|
246
|
-
phrases_to_remove = [
|
247
|
-
"create a task", "create task", "add a task", "add task",
|
248
|
-
"called", "named", "with", "priority", "high", "medium", "low",
|
249
|
-
"in", "notebook"
|
250
|
-
]
|
251
|
-
|
252
|
-
phrases_to_remove.each do |phrase|
|
253
|
-
potential_title = potential_title.gsub(/#{phrase}/i, " ")
|
254
|
-
end
|
255
|
-
|
256
|
-
result = potential_title.strip
|
257
|
-
result.empty? ? nil : result
|
258
|
-
end
|
259
|
-
|
260
|
-
def determine_notebook_name(prompt_lower)
|
261
|
-
return nil unless Notebook.default_notebook
|
262
|
-
|
263
|
-
notebook_name = Notebook.default_notebook.name
|
264
|
-
|
265
|
-
# Try to extract a specific notebook name from the prompt
|
266
|
-
Notebook.all.each do |notebook|
|
267
|
-
if prompt_lower.include?(notebook.name.downcase)
|
268
|
-
notebook_name = notebook.name
|
269
|
-
break
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
notebook_name
|
274
|
-
end
|
275
|
-
|
276
|
-
def determine_priority(prompt_lower)
|
277
|
-
if prompt_lower.include?("high priority") || prompt_lower.match(/priority.*high/)
|
278
|
-
"high"
|
279
|
-
elsif prompt_lower.include?("medium priority") || prompt_lower.match(/priority.*medium/)
|
280
|
-
"medium"
|
281
|
-
elsif prompt_lower.include?("low priority") || prompt_lower.match(/priority.*low/)
|
282
|
-
"low"
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
def create_task(notebook_name, title, priority)
|
287
|
-
say "\nCreating task in notebook: #{notebook_name}" if options[:verbose]
|
288
|
-
cli_args = ["task:add", notebook_name, title]
|
289
|
-
|
290
|
-
# Add priority if specified
|
291
|
-
cli_args.push("--priority", priority) if priority
|
292
|
-
|
293
|
-
RubyTodo::CLI.start(cli_args)
|
294
|
-
|
295
|
-
# Create a simple explanation
|
296
|
-
priority_text = priority ? " with #{priority} priority" : ""
|
297
|
-
say "\nCreated task '#{title}'#{priority_text} in the #{notebook_name} notebook"
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
module CommonQueryHandler
|
302
|
-
include TaskStatistics
|
303
|
-
include TaskPriority
|
304
|
-
include TaskDeadlines
|
305
|
-
include TaskCreation
|
306
|
-
|
307
|
-
def handle_common_query(prompt)
|
308
|
-
handle_statistics_query(prompt) ||
|
309
|
-
handle_priority_query(prompt) ||
|
310
|
-
handle_deadline_query(prompt)
|
311
|
-
end
|
312
|
-
|
313
|
-
private
|
314
|
-
|
315
|
-
def high_priority_query?(prompt_lower)
|
316
|
-
prompt_lower.include?("high priority") ||
|
317
|
-
(prompt_lower.include?("priority") && prompt_lower.include?("high"))
|
318
|
-
end
|
319
|
-
|
320
|
-
def medium_priority_query?(prompt_lower)
|
321
|
-
prompt_lower.include?("medium priority") ||
|
322
|
-
(prompt_lower.include?("priority") && prompt_lower.include?("medium"))
|
323
|
-
end
|
324
|
-
|
325
|
-
def statistics_query?(prompt_lower)
|
326
|
-
(prompt_lower.include?("statistics") || prompt_lower.include?("stats")) &&
|
327
|
-
(prompt_lower.include?("notebook") || prompt_lower.include?("tasks"))
|
328
|
-
end
|
329
|
-
|
330
|
-
def status_tasks_query?(prompt_lower)
|
331
|
-
statuses = { "todo" => "todo", "in progress" => "in_progress", "done" => "done", "archived" => "archived" }
|
332
|
-
statuses.keys.any? { |status| prompt_lower.include?(status) }
|
333
|
-
end
|
334
|
-
|
335
|
-
def notebook_listing_query?(prompt_lower)
|
336
|
-
prompt_lower.include?("list notebooks") ||
|
337
|
-
prompt_lower.include?("show notebooks") ||
|
338
|
-
prompt_lower.include?("display notebooks")
|
339
|
-
end
|
340
|
-
|
341
|
-
def handle_status_tasks(prompt_lower)
|
342
|
-
say "\n=== Detecting status tasks request ===" if options[:verbose]
|
343
|
-
|
344
|
-
statuses = { "todo" => "todo", "in progress" => "in_progress", "done" => "done", "archived" => "archived" }
|
345
|
-
status = nil
|
346
|
-
|
347
|
-
statuses.each do |name, value|
|
348
|
-
if prompt_lower.include?(name)
|
349
|
-
status = value
|
350
|
-
break
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
return false unless status
|
355
|
-
|
356
|
-
tasks = find_tasks_by_status(status)
|
357
|
-
display_tasks_by_status(tasks, status)
|
358
|
-
true
|
359
|
-
end
|
360
|
-
|
361
|
-
def handle_notebook_listing(_prompt_lower)
|
362
|
-
say "\n=== Detecting notebook listing request ===" if options[:verbose]
|
363
|
-
|
364
|
-
notebooks = Notebook.all
|
365
|
-
if notebooks.empty?
|
366
|
-
say "No notebooks found.".yellow
|
367
|
-
return true
|
368
|
-
end
|
369
|
-
|
370
|
-
say "\nNotebooks:".blue
|
371
|
-
notebooks.each do |notebook|
|
372
|
-
say " #{notebook.name}".green
|
373
|
-
end
|
374
|
-
|
375
|
-
true
|
376
|
-
end
|
377
|
-
end
|
378
|
-
end
|