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.
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.3
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-28 00:00:00.000000000 Z
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/common_query_handler.rb
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/task_management.rb
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,2 +0,0 @@
1
- # OpenAI API key
2
- OPENAI_API_KEY=your_openai_api_key_here
@@ -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