todo-agent 0.3.2__py3-none-any.whl → 0.3.3__py3-none-any.whl

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.
@@ -32,13 +32,19 @@ class TodoShell:
32
32
  self.todo_dir = os.path.dirname(todo_file_path) or os.getcwd()
33
33
  self.logger = logger
34
34
 
35
- def execute(self, command: List[str], cwd: Optional[str] = None) -> str:
35
+ def execute(
36
+ self,
37
+ command: List[str],
38
+ cwd: Optional[str] = None,
39
+ suppress_color: bool = False,
40
+ ) -> str:
36
41
  """
37
- Execute todo.sh command.
42
+ Execute a todo.sh command and return the output.
38
43
 
39
44
  Args:
40
45
  command: List of command arguments
41
46
  cwd: Working directory (defaults to todo.sh directory)
47
+ suppress_color: If True, strip ANSI color codes from output (for LLM consumption)
42
48
 
43
49
  Returns:
44
50
  Command output as string
@@ -52,6 +58,7 @@ class TodoShell:
52
58
  self.logger.debug(f"=== RAW COMMAND EXECUTION ===")
53
59
  self.logger.debug(f"Raw command: {raw_command}")
54
60
  self.logger.debug(f"Working directory: {cwd or self.todo_dir}")
61
+ self.logger.debug(f"Suppress color: {suppress_color}")
55
62
 
56
63
  try:
57
64
  working_dir = cwd or self.todo_dir
@@ -67,7 +74,20 @@ class TodoShell:
67
74
  self.logger.debug(f"Raw stderr: {result.stderr}")
68
75
  self.logger.debug(f"Return code: {result.returncode}")
69
76
 
70
- return result.stdout.strip()
77
+ output = result.stdout.strip()
78
+
79
+ # Strip ANSI color codes if requested (for LLM consumption)
80
+ if suppress_color:
81
+ from rich.text import Text
82
+
83
+ # Use Rich's Text.from_ansi to parse and then get plain text
84
+ output = Text.from_ansi(output).plain
85
+ if self.logger:
86
+ self.logger.debug(
87
+ f"Stripped ANSI codes from output for LLM consumption"
88
+ )
89
+
90
+ return output
71
91
  except subprocess.CalledProcessError as e:
72
92
  # Log error details
73
93
  if self.logger:
@@ -88,12 +108,14 @@ class TodoShell:
88
108
  """Add new task."""
89
109
  return self.execute(["todo.sh", "add", description])
90
110
 
91
- def list_tasks(self, filter_str: Optional[str] = None) -> str:
111
+ def list_tasks(
112
+ self, filter_str: Optional[str] = None, suppress_color: bool = True
113
+ ) -> str:
92
114
  """List tasks with optional filtering."""
93
115
  command = ["todo.sh", "ls"]
94
116
  if filter_str:
95
117
  command.append(filter_str)
96
- return self.execute(command)
118
+ return self.execute(command, suppress_color=suppress_color)
97
119
 
98
120
  def complete(self, task_number: int) -> str:
99
121
  """Mark task complete."""
@@ -135,20 +157,22 @@ class TodoShell:
135
157
  """Remove task priority."""
136
158
  return self.execute(["todo.sh", "depri", str(task_number)])
137
159
 
138
- def list_projects(self) -> str:
160
+ def list_projects(self, suppress_color: bool = True) -> str:
139
161
  """List projects."""
140
- return self.execute(["todo.sh", "lsp"])
162
+ return self.execute(["todo.sh", "lsp"], suppress_color=suppress_color)
141
163
 
142
- def list_contexts(self) -> str:
164
+ def list_contexts(self, suppress_color: bool = True) -> str:
143
165
  """List contexts."""
144
- return self.execute(["todo.sh", "lsc"])
166
+ return self.execute(["todo.sh", "lsc"], suppress_color=suppress_color)
145
167
 
146
- def list_completed(self, filter_str: Optional[str] = None) -> str:
168
+ def list_completed(
169
+ self, filter_str: Optional[str] = None, suppress_color: bool = True
170
+ ) -> str:
147
171
  """List completed tasks with optional filtering."""
148
172
  command = ["todo.sh", "listfile", "done.txt"]
149
173
  if filter_str:
150
174
  command.append(filter_str)
151
- return self.execute(command)
175
+ return self.execute(command, suppress_color=suppress_color)
152
176
 
153
177
  def archive(self) -> str:
154
178
  """Archive completed tasks."""
@@ -2,8 +2,9 @@
2
2
  Command-line interface for todo.sh LLM agent.
3
3
  """
4
4
 
5
- from typing import Optional
6
5
  import readline
6
+ from typing import Optional
7
+
7
8
  from rich.align import Align
8
9
  from rich.console import Console, Group
9
10
  from rich.live import Live
@@ -23,8 +24,8 @@ try:
23
24
  TableFormatter,
24
25
  TaskFormatter,
25
26
  )
26
- from todo_agent.interface.tools import ToolCallHandler
27
27
  from todo_agent.interface.progress import ToolCallProgress
28
+ from todo_agent.interface.tools import ToolCallHandler
28
29
  except ImportError:
29
30
  from core.todo_manager import TodoManager # type: ignore[no-redef]
30
31
  from infrastructure.config import Config # type: ignore[no-redef]
@@ -38,8 +39,8 @@ except ImportError:
38
39
  TableFormatter,
39
40
  TaskFormatter,
40
41
  )
41
- from interface.tools import ToolCallHandler # type: ignore[no-redef]
42
42
  from interface.progress import ToolCallProgress # type: ignore[no-redef]
43
+ from interface.tools import ToolCallHandler # type: ignore[no-redef]
43
44
 
44
45
 
45
46
  class CLI:
@@ -100,22 +101,23 @@ class CLI:
100
101
  initial_spinner = self._create_thinking_spinner("Thinking...")
101
102
  return Live(initial_spinner, console=self.console, refresh_per_second=10)
102
103
 
103
- def _create_tool_call_spinner(self, progress_description: str,
104
- sequence: int = 0, total_sequences: int = 0) -> Group:
104
+ def _create_tool_call_spinner(
105
+ self, progress_description: str, sequence: int = 0, total_sequences: int = 0
106
+ ) -> Group:
105
107
  """
106
108
  Create a multi-line spinner showing tool call progress.
107
-
109
+
108
110
  Args:
109
111
  progress_description: User-friendly description of what the tool is doing
110
112
  sequence: Current sequence number
111
113
  total_sequences: Total number of sequences
112
-
114
+
113
115
  Returns:
114
116
  Group object with spinner and optional sequence info
115
117
  """
116
118
  # Line 1: Main progress with spinner
117
119
  main_line = Spinner("dots", text=Text(progress_description, style="cyan"))
118
-
120
+
119
121
  # Line 2: Sequence progress (show current sequence even if we don't know total)
120
122
  # if sequence > 0:
121
123
  # if total_sequences > 0:
@@ -123,75 +125,87 @@ class CLI:
123
125
  # else:
124
126
  # sequence_text = Text(f"Sequence {sequence}", style="dim")
125
127
  # return Group(main_line, sequence_text)
126
-
127
- return main_line
128
+
129
+ return Group(main_line)
128
130
 
129
131
  def _create_completion_spinner(self, thinking_time: float) -> Spinner:
130
132
  """
131
133
  Create completion spinner with timing.
132
-
134
+
133
135
  Args:
134
136
  thinking_time: Total thinking time in seconds
135
-
137
+
136
138
  Returns:
137
139
  Spinner object showing completion
138
140
  """
139
- return Spinner("dots", text=Text(f"✅ Complete ({thinking_time:.1f}s)", style="green"))
141
+ return Spinner(
142
+ "dots", text=Text(f"✅ Complete ({thinking_time:.1f}s)", style="green")
143
+ )
140
144
 
141
145
  def _create_cli_progress_callback(self, live_display: Live) -> ToolCallProgress:
142
146
  """
143
147
  Create a CLI-specific progress callback for tool call tracking.
144
-
148
+
145
149
  Args:
146
150
  live_display: The live display to update
147
-
151
+
148
152
  Returns:
149
153
  ToolCallProgress implementation for CLI
150
154
  """
155
+
151
156
  class CLIProgressCallback(ToolCallProgress):
152
157
  def __init__(self, cli: CLI, live: Live):
153
158
  self.cli = cli
154
159
  self.live = live
155
160
  self.current_sequence = 0
156
161
  self.total_sequences = 0
157
-
162
+
158
163
  def on_thinking_start(self) -> None:
159
164
  """Show initial thinking spinner."""
160
- spinner = self.cli._create_thinking_spinner("🤔 Analyzing your request...")
165
+ spinner = self.cli._create_thinking_spinner(
166
+ "🤔 Analyzing your request..."
167
+ )
161
168
  self.live.update(spinner)
162
-
163
- def on_tool_call_start(self, tool_name: str, progress_description: str,
164
- sequence: int, total_sequences: int) -> None:
169
+
170
+ def on_tool_call_start(
171
+ self,
172
+ tool_name: str,
173
+ progress_description: str,
174
+ sequence: int,
175
+ total_sequences: int,
176
+ ) -> None:
165
177
  """Show tool execution progress."""
166
178
  self.current_sequence = sequence
167
179
  self.total_sequences = total_sequences
168
-
180
+
169
181
  # Create multi-line spinner
170
182
  spinner = self.cli._create_tool_call_spinner(
171
183
  progress_description=progress_description,
172
184
  sequence=sequence,
173
- total_sequences=total_sequences
185
+ total_sequences=total_sequences,
174
186
  )
175
187
  self.live.update(spinner)
176
-
177
- def on_tool_call_complete(self, tool_name: str, success: bool, duration: float) -> None:
188
+
189
+ def on_tool_call_complete(
190
+ self, tool_name: str, success: bool, duration: float
191
+ ) -> None:
178
192
  """Tool completion - no action needed."""
179
193
  pass
180
-
194
+
181
195
  def on_sequence_complete(self, sequence: int, total_sequences: int) -> None:
182
196
  """Show sequence completion."""
183
197
  spinner = self.cli._create_tool_call_spinner(
184
198
  progress_description=f"🔄 Sequence {sequence} complete",
185
199
  sequence=sequence,
186
- total_sequences=total_sequences
200
+ total_sequences=total_sequences,
187
201
  )
188
202
  self.live.update(spinner)
189
-
203
+
190
204
  def on_thinking_complete(self, total_time: float) -> None:
191
205
  """Show completion spinner."""
192
206
  spinner = self.cli._create_completion_spinner(total_time)
193
207
  self.live.update(spinner)
194
-
208
+
195
209
  return CLIProgressCallback(self, live_display)
196
210
 
197
211
  def _print_header(self) -> None:
@@ -296,7 +310,8 @@ class CLI:
296
310
  if user_input.lower() == "list":
297
311
  self.logger.debug("User requested task list")
298
312
  try:
299
- output = self.todo_shell.list_tasks()
313
+ # Use suppress_color=False for interactive display to preserve colors
314
+ output = self.todo_shell.list_tasks(suppress_color=False)
300
315
  formatted_output = TaskFormatter.format_task_list(output)
301
316
  task_panel = PanelFormatter.create_task_panel(formatted_output)
302
317
  self.console.print(task_panel)
@@ -311,7 +326,8 @@ class CLI:
311
326
  if user_input.lower() == "done":
312
327
  self.logger.debug("User requested completed task list")
313
328
  try:
314
- output = self.todo_shell.list_completed()
329
+ # Use suppress_color=False for interactive display to preserve colors
330
+ output = self.todo_shell.list_completed(suppress_color=False)
315
331
  formatted_output = TaskFormatter.format_completed_tasks(output)
316
332
  task_panel = PanelFormatter.create_task_panel(
317
333
  formatted_output, title="✅ Completed Tasks"
@@ -336,7 +352,7 @@ class CLI:
336
352
  # Get memory usage
337
353
  # DISABLED FOR NOW
338
354
  memory_usage = self._get_memory_usage()
339
- #memory_usage = None
355
+ # memory_usage = None
340
356
 
341
357
  # Create response panel with memory usage
342
358
  response_panel = PanelFormatter.create_response_panel(
@@ -368,9 +384,11 @@ class CLI:
368
384
  try:
369
385
  # Create progress callback for tool call tracking
370
386
  progress_callback = self._create_cli_progress_callback(live)
371
-
387
+
372
388
  # Process request through inference engine with progress tracking
373
- response, thinking_time = self.inference.process_request(user_input, progress_callback)
389
+ response, thinking_time = self.inference.process_request(
390
+ user_input, progress_callback
391
+ )
374
392
 
375
393
  # Update spinner with completion message and thinking time
376
394
  live.update(
@@ -21,20 +21,23 @@ PROVIDER_ERROR_MESSAGES = {
21
21
  "rate_limit": "I'm a bit overwhelmed right now. Please wait a moment and try again, or type 'clear' to start fresh.",
22
22
  "auth_error": "I can't connect to my AI service. Please check your configuration, or type 'clear' to reset.",
23
23
  "timeout": "The request took too long. Please try again, or type 'clear' to reset our conversation.",
24
- "general_error": "Something went wrong with my AI service. Please try again, or type 'clear' to reset our conversation."
24
+ "general_error": "Something went wrong with my AI service. Please try again, or type 'clear' to reset our conversation.",
25
25
  }
26
26
 
27
+
27
28
  def get_provider_error_message(error_type: str) -> str:
28
29
  """
29
30
  Get user-friendly error message for provider errors.
30
-
31
+
31
32
  Args:
32
33
  error_type: The type of provider error
33
-
34
+
34
35
  Returns:
35
36
  User-friendly error message with recovery suggestion
36
37
  """
37
- return PROVIDER_ERROR_MESSAGES.get(error_type, PROVIDER_ERROR_MESSAGES["general_error"])
38
+ return PROVIDER_ERROR_MESSAGES.get(
39
+ error_type, PROVIDER_ERROR_MESSAGES["general_error"]
40
+ )
38
41
 
39
42
 
40
43
  class TaskFormatter:
@@ -3,34 +3,39 @@ Progress tracking interface for tool call execution.
3
3
  """
4
4
 
5
5
  from abc import ABC, abstractmethod
6
- from typing import Optional
7
6
 
8
7
 
9
8
  class ToolCallProgress(ABC):
10
9
  """Abstract interface for tool call progress tracking."""
11
-
10
+
12
11
  @abstractmethod
13
12
  def on_thinking_start(self) -> None:
14
13
  """Called when LLM starts thinking."""
15
14
  pass
16
-
15
+
17
16
  @abstractmethod
18
- def on_tool_call_start(self, tool_name: str, progress_description: str,
19
- sequence: int, total_sequences: int) -> None:
17
+ def on_tool_call_start(
18
+ self,
19
+ tool_name: str,
20
+ progress_description: str,
21
+ sequence: int,
22
+ total_sequences: int,
23
+ ) -> None:
20
24
  """Called when a tool call starts."""
21
25
  pass
22
-
26
+
23
27
  @abstractmethod
24
- def on_tool_call_complete(self, tool_name: str, success: bool,
25
- duration: float) -> None:
28
+ def on_tool_call_complete(
29
+ self, tool_name: str, success: bool, duration: float
30
+ ) -> None:
26
31
  """Called when a tool call completes (optional - no action needed)."""
27
32
  pass
28
-
33
+
29
34
  @abstractmethod
30
35
  def on_sequence_complete(self, sequence: int, total_sequences: int) -> None:
31
36
  """Called when a tool call sequence completes."""
32
37
  pass
33
-
38
+
34
39
  @abstractmethod
35
40
  def on_thinking_complete(self, total_time: float) -> None:
36
41
  """Called when thinking is complete."""
@@ -39,20 +44,26 @@ class ToolCallProgress(ABC):
39
44
 
40
45
  class NoOpProgress(ToolCallProgress):
41
46
  """No-operation implementation for when progress tracking is not needed."""
42
-
47
+
43
48
  def on_thinking_start(self) -> None:
44
49
  pass
45
-
46
- def on_tool_call_start(self, tool_name: str, progress_description: str,
47
- sequence: int, total_sequences: int) -> None:
50
+
51
+ def on_tool_call_start(
52
+ self,
53
+ tool_name: str,
54
+ progress_description: str,
55
+ sequence: int,
56
+ total_sequences: int,
57
+ ) -> None:
48
58
  pass
49
-
50
- def on_tool_call_complete(self, tool_name: str, success: bool,
51
- duration: float) -> None:
59
+
60
+ def on_tool_call_complete(
61
+ self, tool_name: str, success: bool, duration: float
62
+ ) -> None:
52
63
  pass
53
-
64
+
54
65
  def on_sequence_complete(self, sequence: int, total_sequences: int) -> None:
55
66
  pass
56
-
67
+
57
68
  def on_thinking_complete(self, total_time: float) -> None:
58
69
  pass
@@ -180,7 +180,7 @@ class ToolCallHandler:
180
180
  "required": ["description"],
181
181
  },
182
182
  },
183
- "progress_description": " Creating new task...",
183
+ "progress_description": " Creating new task: {description}...",
184
184
  },
185
185
  {
186
186
  "type": "function",
@@ -204,7 +204,7 @@ class ToolCallHandler:
204
204
  "required": ["task_number"],
205
205
  },
206
206
  },
207
- "progress_description": "🎯 Marking task complete...",
207
+ "progress_description": "🎯 Marking task #{task_number} as complete...",
208
208
  },
209
209
  {
210
210
  "type": "function",
@@ -232,7 +232,7 @@ class ToolCallHandler:
232
232
  "required": ["task_number", "new_description"],
233
233
  },
234
234
  },
235
- "progress_description": "✏️ Updating task description...",
235
+ "progress_description": "✏️ Updating task #{task_number} with new description...",
236
236
  },
237
237
  {
238
238
  "type": "function",
@@ -262,7 +262,7 @@ class ToolCallHandler:
262
262
  "required": ["task_number", "text_to_append"],
263
263
  },
264
264
  },
265
- "progress_description": "📝 Adding notes to task...",
265
+ "progress_description": "📝 Adding notes to task #{task_number}...",
266
266
  },
267
267
  {
268
268
  "type": "function",
@@ -289,7 +289,7 @@ class ToolCallHandler:
289
289
  "required": ["task_number", "text"],
290
290
  },
291
291
  },
292
- "progress_description": "📝 Adding prefix to task...",
292
+ "progress_description": "📝 Adding prefix to task #{task_number}...",
293
293
  },
294
294
  {
295
295
  "type": "function",
@@ -320,7 +320,7 @@ class ToolCallHandler:
320
320
  "required": ["task_number"],
321
321
  },
322
322
  },
323
- "progress_description": "🗑️ Deleting task...",
323
+ "progress_description": "🗑️ Deleting task #{task_number}...",
324
324
  },
325
325
  {
326
326
  "type": "function",
@@ -348,7 +348,7 @@ class ToolCallHandler:
348
348
  "required": ["task_number", "priority"],
349
349
  },
350
350
  },
351
- "progress_description": "🏷️ Setting priority...",
351
+ "progress_description": "🏷️ Setting priority {priority} for task #{task_number}...",
352
352
  },
353
353
  {
354
354
  "type": "function",
@@ -372,7 +372,7 @@ class ToolCallHandler:
372
372
  "required": ["task_number"],
373
373
  },
374
374
  },
375
- "progress_description": "🏷️ Removing priority...",
375
+ "progress_description": "🏷️ Removing priority from task #{task_number}...",
376
376
  },
377
377
  {
378
378
  "type": "function",
@@ -404,7 +404,7 @@ class ToolCallHandler:
404
404
  "required": ["task_number", "due_date"],
405
405
  },
406
406
  },
407
- "progress_description": "📅 Setting due date...",
407
+ "progress_description": "📅 Setting due date {due_date} for task #{task_number}...",
408
408
  },
409
409
  {
410
410
  "type": "function",
@@ -436,7 +436,7 @@ class ToolCallHandler:
436
436
  "required": ["task_number", "context"],
437
437
  },
438
438
  },
439
- "progress_description": "📍 Setting context...",
439
+ "progress_description": "📍 Setting context {context} for task #{task_number}...",
440
440
  },
441
441
  {
442
442
  "type": "function",
@@ -472,7 +472,7 @@ class ToolCallHandler:
472
472
  "required": ["task_number", "projects"],
473
473
  },
474
474
  },
475
- "progress_description": "🏷️ Setting project tags...",
475
+ "progress_description": "🏷️ Setting project tags for task #{task_number}...",
476
476
  },
477
477
  {
478
478
  "type": "function",
@@ -504,7 +504,7 @@ class ToolCallHandler:
504
504
  "required": ["task_number", "destination"],
505
505
  },
506
506
  },
507
- "progress_description": "📦 Moving task...",
507
+ "progress_description": "📦 Moving task #{task_number} to {destination}...",
508
508
  },
509
509
  {
510
510
  "type": "function",
@@ -544,7 +544,7 @@ class ToolCallHandler:
544
544
  "required": ["date_expression"],
545
545
  },
546
546
  },
547
- "progress_description": "📅 Converting date expression...",
547
+ "progress_description": "📅 Converting date expression '{date_expression}'...",
548
548
  },
549
549
  {
550
550
  "type": "function",
@@ -580,7 +580,7 @@ class ToolCallHandler:
580
580
  "required": ["month", "year"],
581
581
  },
582
582
  },
583
- "progress_description": "📅 Generating calendar...",
583
+ "progress_description": "📅 Generating calendar for {month}/{year}...",
584
584
  },
585
585
  {
586
586
  "type": "function",
@@ -619,7 +619,7 @@ class ToolCallHandler:
619
619
  "required": ["description"],
620
620
  },
621
621
  },
622
- "progress_description": "✅ Creating completed task...",
622
+ "progress_description": "✅ Creating completed task: {description}...",
623
623
  },
624
624
  {
625
625
  "type": "function",
@@ -646,7 +646,7 @@ class ToolCallHandler:
646
646
  "required": ["task_number"],
647
647
  },
648
648
  },
649
- "progress_description": "🔄 Restoring completed task...",
649
+ "progress_description": "🔄 Restoring completed task #{task_number}...",
650
650
  },
651
651
  ]
652
652
 
@@ -805,16 +805,16 @@ class ToolCallHandler:
805
805
  """Execute a tool call and return the result."""
806
806
  # Validate tool call structure
807
807
  if not isinstance(tool_call, dict):
808
- return {
808
+ return { # type: ignore[unreachable]
809
809
  "tool_call_id": "unknown",
810
810
  "name": "unknown",
811
811
  "output": "ERROR: Invalid tool call format",
812
812
  "error": True,
813
813
  "error_type": "malformed_tool_call",
814
814
  "error_details": "Tool call is not a dictionary",
815
- "user_message": "I received a malformed request. Please try again, or type 'clear' to reset our conversation."
815
+ "user_message": "I received a malformed request. Please try again, or type 'clear' to reset our conversation.",
816
816
  }
817
-
817
+
818
818
  if "function" not in tool_call:
819
819
  return {
820
820
  "tool_call_id": tool_call.get("id", "unknown"),
@@ -823,9 +823,9 @@ class ToolCallHandler:
823
823
  "error": True,
824
824
  "error_type": "malformed_tool_call",
825
825
  "error_details": "Tool call missing function field",
826
- "user_message": "I received a malformed request. Please try again, or type 'clear' to reset our conversation."
826
+ "user_message": "I received a malformed request. Please try again, or type 'clear' to reset our conversation.",
827
827
  }
828
-
828
+
829
829
  function = tool_call["function"]
830
830
  if not isinstance(function, dict):
831
831
  return {
@@ -835,9 +835,9 @@ class ToolCallHandler:
835
835
  "error": True,
836
836
  "error_type": "malformed_tool_call",
837
837
  "error_details": "Function field is not a dictionary",
838
- "user_message": "I received a malformed request. Please try again, or type 'clear' to reset our conversation."
838
+ "user_message": "I received a malformed request. Please try again, or type 'clear' to reset our conversation.",
839
839
  }
840
-
840
+
841
841
  tool_name = function.get("name")
842
842
  if not tool_name:
843
843
  return {
@@ -847,9 +847,9 @@ class ToolCallHandler:
847
847
  "error": True,
848
848
  "error_type": "malformed_tool_call",
849
849
  "error_details": "Function missing name field",
850
- "user_message": "I received a malformed request. Please try again, or type 'clear' to reset our conversation."
850
+ "user_message": "I received a malformed request. Please try again, or type 'clear' to reset our conversation.",
851
851
  }
852
-
852
+
853
853
  arguments = function.get("arguments", {})
854
854
  tool_call_id = tool_call.get("id", "unknown")
855
855
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: todo-agent
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: A natural language interface for todo.sh task management
5
5
  Author: codeprimate
6
6
  Maintainer: codeprimate
@@ -0,0 +1,30 @@
1
+ todo_agent/__init__.py,sha256=RUowhd14r3tqB_7rl83unGV8oBjra3UOIl7jix-33fk,254
2
+ todo_agent/_version.py,sha256=lemL_4Kl75FgrO6lVuFrrtw6-Dcf9wtXBalKkXuzkO4,704
3
+ todo_agent/main.py,sha256=-ryhMm4c4sz4e4anXI8B-CYnpEh5HIkmnYcnGxcWHDk,1628
4
+ todo_agent/core/__init__.py,sha256=QAZ4it63pXv5-DxtNcuSAmg7ZnCY5ackI5yycvKHr9I,365
5
+ todo_agent/core/conversation_manager.py,sha256=9aAWogswZe9Cs7wKT47RG-cLh1LQ5D9RbhUHJVUyTS8,13549
6
+ todo_agent/core/exceptions.py,sha256=ilVL5hyPHGYQXsLm0pYivMbhbamupG77r8TbiQr2tAU,2034
7
+ todo_agent/core/todo_manager.py,sha256=nLQbri-hhbqtV-zJNpdnBHPiUYd7tAcqpFFLX-xxeW4,19263
8
+ todo_agent/infrastructure/__init__.py,sha256=SGbHXgzq6U1DMgOfWPMsWEK99zjPSF-6gzy7xqc5fsI,284
9
+ todo_agent/infrastructure/calendar_utils.py,sha256=8tOZHN5CNrRHpTQHYzskmsZNJKWuUFjrjyvHQ76AhzU,1837
10
+ todo_agent/infrastructure/config.py,sha256=zyp6qOlg1nN_awphivlgGNBE6fL0Hf66YgvWxR8ldyQ,2117
11
+ todo_agent/infrastructure/inference.py,sha256=tAAv6kou9ucgLaX2f3ZHahjAn2ofB0WQFVUgFVRQ-KM,16193
12
+ todo_agent/infrastructure/llm_client.py,sha256=Knb6yvQt0q8mobnwVH08mGCd8xBHK_g1S66DOdp7cO8,9807
13
+ todo_agent/infrastructure/llm_client_factory.py,sha256=-tktnVOIF7B45WR7AuLoi7MKnEyuM8lgg1jjc4T1FhM,1929
14
+ todo_agent/infrastructure/logger.py,sha256=2ykG_0lyzmEGxDF6ZRl1qiTUGDuFeQgzv4Na6vRmXcM,4110
15
+ todo_agent/infrastructure/ollama_client.py,sha256=V_zAeBjIEzB8PZXyzFeiLMLA5qf3y4WV2_6Vqxn1ujc,5629
16
+ todo_agent/infrastructure/openrouter_client.py,sha256=0pxIKvHItwVijFz8l4loOeCa4HUpMvTYROcwYJh3iyI,6748
17
+ todo_agent/infrastructure/todo_shell.py,sha256=EySpPNZUZ0Dl_-snieKk6TcMx8NHkZvxXz6E-m6RKhI,18201
18
+ todo_agent/infrastructure/token_counter.py,sha256=PCKheOVJbp1s89yhh_i6iKgURMt9mVoYkwjQJCc2xCE,4958
19
+ todo_agent/infrastructure/prompts/system_prompt.txt,sha256=APyGy3sjc2LP4YgcxMuC7_9c4BVYzSEb9R8pHobB5H8,4453
20
+ todo_agent/interface/__init__.py,sha256=vDD3rQu4qDkpvVwGVtkDzE1M4IiSHYzTif4GbYSFWaI,457
21
+ todo_agent/interface/cli.py,sha256=Ikh5AYoOZnNUVXzHjfv2bsE33yK3VhCdzza83HOzFp8,16460
22
+ todo_agent/interface/formatters.py,sha256=a9PW-5DbY8K5QYZjBXFZSdzlCmy263kwBI9nXgP8LXI,20081
23
+ todo_agent/interface/progress.py,sha256=EpPa20Hrnjv_TBIp0tzViVciThqsOvAHuYO4v2rc8eI,1751
24
+ todo_agent/interface/tools.py,sha256=-pMPSNKyb5M5CGie6-ZxDYdl82YAt0v9ie3TCILOfQg,51714
25
+ todo_agent-0.3.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
26
+ todo_agent-0.3.3.dist-info/METADATA,sha256=VceLOqM8POJIJxGuRsAalzTMgnilLCuL7vmdJgbNOlU,10056
27
+ todo_agent-0.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
+ todo_agent-0.3.3.dist-info/entry_points.txt,sha256=4W7LrCib6AXP5IZDwWRht8S5gutLu5oNfTJHGbt4oHs,52
29
+ todo_agent-0.3.3.dist-info/top_level.txt,sha256=a65mlPIhPZHuq2bRIi_sCMAIJsUddvXt171OBF6r6co,11
30
+ todo_agent-0.3.3.dist-info/RECORD,,