todo-agent 0.3.3__tar.gz → 0.3.6__tar.gz

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.
Files changed (63) hide show
  1. {todo_agent-0.3.3 → todo_agent-0.3.6}/PKG-INFO +1 -1
  2. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_core/test_todo_manager.py +24 -21
  3. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/test_todo_shell.py +13 -0
  4. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_interface/test_cli.py +91 -0
  5. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_interface/test_tools.py +6 -6
  6. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/_version.py +3 -3
  7. todo_agent-0.3.6/todo_agent/core/todo_manager.py +607 -0
  8. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/inference.py +16 -9
  9. todo_agent-0.3.6/todo_agent/infrastructure/prompts/system_prompt.txt +198 -0
  10. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/todo_shell.py +63 -0
  11. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/interface/cli.py +53 -0
  12. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/interface/formatters.py +1 -0
  13. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/interface/tools.py +54 -10
  14. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/main.py +37 -2
  15. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent.egg-info/PKG-INFO +1 -1
  16. todo_agent-0.3.3/todo_agent/core/todo_manager.py +0 -479
  17. todo_agent-0.3.3/todo_agent/infrastructure/prompts/system_prompt.txt +0 -91
  18. {todo_agent-0.3.3 → todo_agent-0.3.6}/.gitignore +0 -0
  19. {todo_agent-0.3.3 → todo_agent-0.3.6}/LICENSE +0 -0
  20. {todo_agent-0.3.3 → todo_agent-0.3.6}/MANIFEST.in +0 -0
  21. {todo_agent-0.3.3 → todo_agent-0.3.6}/Makefile +0 -0
  22. {todo_agent-0.3.3 → todo_agent-0.3.6}/README.md +0 -0
  23. {todo_agent-0.3.3 → todo_agent-0.3.6}/docs/publishing.md +0 -0
  24. {todo_agent-0.3.3 → todo_agent-0.3.6}/pyproject.toml +0 -0
  25. {todo_agent-0.3.3 → todo_agent-0.3.6}/requirements-dev.txt +0 -0
  26. {todo_agent-0.3.3 → todo_agent-0.3.6}/requirements.txt +0 -0
  27. {todo_agent-0.3.3 → todo_agent-0.3.6}/setup.cfg +0 -0
  28. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/__init__.py +0 -0
  29. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_core/__init__.py +0 -0
  30. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_core/test_conversation_manager.py +0 -0
  31. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/__init__.py +0 -0
  32. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/test_calendar_utils.py +0 -0
  33. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/test_config.py +0 -0
  34. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/test_inference.py +0 -0
  35. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/test_llm_client_factory.py +0 -0
  36. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/test_ollama_client.py +0 -0
  37. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/test_openrouter_client.py +0 -0
  38. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_infrastructure/test_token_counter.py +0 -0
  39. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_interface/__init__.py +0 -0
  40. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_interface/test_formatters.py +0 -0
  41. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_linting.py +0 -0
  42. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_logger.py +0 -0
  43. {todo_agent-0.3.3 → todo_agent-0.3.6}/tests/test_main.py +0 -0
  44. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/__init__.py +0 -0
  45. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/core/__init__.py +0 -0
  46. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/core/conversation_manager.py +0 -0
  47. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/core/exceptions.py +0 -0
  48. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/__init__.py +0 -0
  49. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/calendar_utils.py +0 -0
  50. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/config.py +0 -0
  51. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/llm_client.py +0 -0
  52. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/llm_client_factory.py +0 -0
  53. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/logger.py +0 -0
  54. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/ollama_client.py +0 -0
  55. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/openrouter_client.py +0 -0
  56. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/infrastructure/token_counter.py +0 -0
  57. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/interface/__init__.py +0 -0
  58. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent/interface/progress.py +0 -0
  59. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent.egg-info/SOURCES.txt +0 -0
  60. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent.egg-info/dependency_links.txt +0 -0
  61. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent.egg-info/entry_points.txt +0 -0
  62. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent.egg-info/requires.txt +0 -0
  63. {todo_agent-0.3.3 → todo_agent-0.3.6}/todo_agent.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: todo-agent
3
- Version: 0.3.3
3
+ Version: 0.3.6
4
4
  Summary: A natural language interface for todo.sh task management
5
5
  Author: codeprimate
6
6
  Maintainer: codeprimate
@@ -577,14 +577,14 @@ class TestTodoManager(unittest.TestCase):
577
577
  self.assertIn("2025-01-15 10:30:00", result)
578
578
  self.assertIn("Week 3", result)
579
579
 
580
- def test_created_completed_task_basic(self):
580
+ def test_create_completed_task_basic(self):
581
581
  """Test creating and immediately completing a task."""
582
582
  # Mock the todo_shell methods
583
583
  self.todo_shell.add.return_value = "Task added"
584
584
  self.todo_shell.list_tasks.return_value = "1 Test task\n2 Another task"
585
585
  self.todo_shell.complete.return_value = "Task completed"
586
586
 
587
- result = self.todo_manager.created_completed_task("Test task")
587
+ result = self.todo_manager.create_completed_task("Test task")
588
588
 
589
589
  # Verify the task was added
590
590
  self.todo_shell.add.assert_called_once_with("Test task")
@@ -593,13 +593,13 @@ class TestTodoManager(unittest.TestCase):
593
593
  # Verify the result message
594
594
  self.assertIn("Created and completed task: Test task", result)
595
595
 
596
- def test_created_completed_task_with_project_and_context(self):
596
+ def test_create_completed_task_with_project_and_context(self):
597
597
  """Test creating and completing a task with project and context."""
598
598
  self.todo_shell.add.return_value = "Task added"
599
599
  self.todo_shell.list_tasks.return_value = "1 Test task\n2 Another task"
600
600
  self.todo_shell.complete.return_value = "Task completed"
601
601
 
602
- result = self.todo_manager.created_completed_task(
602
+ result = self.todo_manager.create_completed_task(
603
603
  "Test task", project="work", context="office"
604
604
  )
605
605
 
@@ -609,35 +609,38 @@ class TestTodoManager(unittest.TestCase):
609
609
  self.todo_shell.complete.assert_called_once_with(1)
610
610
  self.assertIn("+work @office", result)
611
611
 
612
- def test_created_completed_task_with_custom_date(self):
612
+ def test_create_completed_task_with_custom_date(self):
613
613
  """Test creating and completing a task with a custom completion date."""
614
- self.todo_shell.add.return_value = "Task added"
615
- self.todo_shell.list_tasks.return_value = "1 Test task"
616
- self.todo_shell.complete.return_value = "Task completed"
614
+ self.todo_shell.addto.return_value = "Task added to done.txt"
617
615
 
618
- result = self.todo_manager.created_completed_task(
616
+ result = self.todo_manager.create_completed_task(
619
617
  "Test task", completion_date="2025-01-10"
620
618
  )
621
619
 
622
- # Verify the task was completed (should find task number 1 since it contains "Test task")
623
- self.todo_shell.complete.assert_called_once_with(1)
620
+ # Verify the task was added directly to done.txt with the custom date
621
+ self.todo_shell.addto.assert_called_once_with(
622
+ "done.txt", "x 2025-01-10 Test task"
623
+ )
624
+ # Verify add and complete were NOT called when using custom date
625
+ self.todo_shell.add.assert_not_called()
626
+ self.todo_shell.complete.assert_not_called()
624
627
  self.assertIn("completed on 2025-01-10", result)
625
628
 
626
- def test_created_completed_task_with_invalid_date(self):
629
+ def test_create_completed_task_with_invalid_date(self):
627
630
  """Test that invalid completion date raises ValueError."""
628
631
  with self.assertRaises(ValueError) as context:
629
- self.todo_manager.created_completed_task(
632
+ self.todo_manager.create_completed_task(
630
633
  "Test task", completion_date="invalid-date"
631
634
  )
632
635
  self.assertIn("Invalid completion date format", str(context.exception))
633
636
 
634
- def test_created_completed_task_sanitizes_project_and_context(self):
637
+ def test_create_completed_task_sanitizes_project_and_context(self):
635
638
  """Test that project and context with existing symbols are properly sanitized."""
636
639
  self.todo_shell.add.return_value = "Task added"
637
640
  self.todo_shell.list_tasks.return_value = "1 Test task"
638
641
  self.todo_shell.complete.return_value = "Task completed"
639
642
 
640
- result = self.todo_manager.created_completed_task(
643
+ result = self.todo_manager.create_completed_task(
641
644
  "Test task", project="+work", context="@office"
642
645
  )
643
646
 
@@ -645,25 +648,25 @@ class TestTodoManager(unittest.TestCase):
645
648
  self.todo_shell.add.assert_called_once_with("Test task +work @office")
646
649
  self.assertIn("+work @office", result)
647
650
 
648
- def test_created_completed_task_with_empty_project_after_sanitization(self):
651
+ def test_create_completed_task_with_empty_project_after_sanitization(self):
649
652
  """Test that empty project after sanitization raises ValueError."""
650
653
  with self.assertRaises(ValueError) as context:
651
- self.todo_manager.created_completed_task("Test task", project="+")
654
+ self.todo_manager.create_completed_task("Test task", project="+")
652
655
  self.assertIn("Project name cannot be empty", str(context.exception))
653
656
 
654
- def test_created_completed_task_with_empty_context_after_sanitization(self):
657
+ def test_create_completed_task_with_empty_context_after_sanitization(self):
655
658
  """Test that empty context after sanitization raises ValueError."""
656
659
  with self.assertRaises(ValueError) as context:
657
- self.todo_manager.created_completed_task("Test task", context="@")
660
+ self.todo_manager.create_completed_task("Test task", context="@")
658
661
  self.assertIn("Context name cannot be empty", str(context.exception))
659
662
 
660
- def test_created_completed_task_no_tasks_after_addition(self):
663
+ def test_create_completed_task_no_tasks_after_addition(self):
661
664
  """Test that RuntimeError is raised when no tasks are found after addition."""
662
665
  self.todo_shell.add.return_value = "Task added"
663
666
  self.todo_shell.list_tasks.return_value = "" # No tasks found
664
667
 
665
668
  with self.assertRaises(RuntimeError) as context:
666
- self.todo_manager.created_completed_task("Test task")
669
+ self.todo_manager.create_completed_task("Test task")
667
670
  self.assertIn("Failed to add task", str(context.exception))
668
671
 
669
672
 
@@ -810,3 +810,16 @@ class TestTodoShell:
810
810
  # Should not duplicate +work, only add +new
811
811
  mock_replace.assert_called_once_with(1, "Test task +work +new @context")
812
812
  assert result == "Task updated"
813
+
814
+ def test_get_help_constructs_correct_command(self):
815
+ """Test getting help constructs the correct todo.sh command."""
816
+ with patch.object(
817
+ self.todo_shell, "execute", return_value="Todo.sh help output"
818
+ ) as mock_execute:
819
+ result = self.todo_shell.get_help()
820
+
821
+ # Verify the correct command was constructed
822
+ mock_execute.assert_called_once_with(
823
+ ["todo.sh", "help"], suppress_color=False
824
+ )
825
+ assert result == "Todo.sh help output"
@@ -377,6 +377,97 @@ class TestCLI:
377
377
  expected_line in str(call) for call in mock_print.call_args_list
378
378
  )
379
379
 
380
+ def test_todo_passthrough_command_success(self):
381
+ """Test successful todo.sh passthrough command execution."""
382
+ with patch("builtins.input", return_value="/add test task"), patch("sys.exit"):
383
+ # Mock the todo_shell execute method
384
+ mock_todo_shell = Mock()
385
+ mock_todo_shell.execute.return_value = "1 test task"
386
+
387
+ # Create CLI instance with mocked dependencies
388
+ with patch("todo_agent.interface.cli.Config"), patch(
389
+ "todo_agent.interface.cli.TodoShell", return_value=mock_todo_shell
390
+ ), patch("todo_agent.interface.cli.TodoManager"), patch(
391
+ "todo_agent.interface.cli.ToolCallHandler"
392
+ ), patch("todo_agent.interface.cli.Inference"), patch(
393
+ "todo_agent.interface.cli.Logger"
394
+ ), patch("todo_agent.interface.cli.Console") as mock_console:
395
+ CLI()
396
+
397
+ # Mock the console methods
398
+ mock_console.return_value.input.return_value = "/add test task"
399
+ mock_console.return_value.print = Mock()
400
+
401
+ # Test the passthrough logic directly
402
+ user_input = "/add test task"
403
+ if user_input.startswith("/"):
404
+ todo_command = user_input[1:].strip()
405
+ output = mock_todo_shell.execute(["todo.sh", *todo_command.split()])
406
+
407
+ # Verify the command was executed correctly
408
+ mock_todo_shell.execute.assert_called_once_with(
409
+ ["todo.sh", "add", "test", "task"]
410
+ )
411
+ assert output == "1 test task"
412
+
413
+ def test_todo_passthrough_command_empty(self):
414
+ """Test todo.sh passthrough with empty command."""
415
+ with patch("builtins.input", return_value="/"), patch("sys.exit"):
416
+ # Mock the todo_shell execute method
417
+ mock_todo_shell = Mock()
418
+
419
+ # Create CLI instance with mocked dependencies
420
+ with patch("todo_agent.interface.cli.Config"), patch(
421
+ "todo_agent.interface.cli.TodoShell", return_value=mock_todo_shell
422
+ ), patch("todo_agent.interface.cli.TodoManager"), patch(
423
+ "todo_agent.interface.cli.ToolCallHandler"
424
+ ), patch("todo_agent.interface.cli.Inference"), patch(
425
+ "todo_agent.interface.cli.Logger"
426
+ ), patch("todo_agent.interface.cli.Console") as mock_console:
427
+ CLI()
428
+
429
+ # Mock the console methods
430
+ mock_console.return_value.input.return_value = "/"
431
+ mock_console.return_value.print = Mock()
432
+
433
+ # Test the passthrough logic with empty command
434
+ user_input = "/"
435
+ if user_input.startswith("/"):
436
+ todo_command = user_input[1:].strip()
437
+ if not todo_command:
438
+ # Should handle empty command gracefully
439
+ assert todo_command == ""
440
+
441
+ def test_todo_help_command(self):
442
+ """Test todo-help command execution."""
443
+ with patch("builtins.input", return_value="todo-help"), patch("sys.exit"):
444
+ # Mock the todo_shell get_help method
445
+ mock_todo_shell = Mock()
446
+ mock_todo_shell.get_help.return_value = "Todo.sh help output"
447
+
448
+ # Create CLI instance with mocked dependencies
449
+ with patch("todo_agent.interface.cli.Config"), patch(
450
+ "todo_agent.interface.cli.TodoShell", return_value=mock_todo_shell
451
+ ), patch("todo_agent.interface.cli.TodoManager"), patch(
452
+ "todo_agent.interface.cli.ToolCallHandler"
453
+ ), patch("todo_agent.interface.cli.Inference"), patch(
454
+ "todo_agent.interface.cli.Logger"
455
+ ), patch("todo_agent.interface.cli.Console") as mock_console:
456
+ CLI()
457
+
458
+ # Mock the console methods
459
+ mock_console.return_value.input.return_value = "todo-help"
460
+ mock_console.return_value.print = Mock()
461
+
462
+ # Test the todo-help command
463
+ user_input = "todo-help"
464
+ if user_input.lower() == "todo-help":
465
+ help_output = mock_todo_shell.get_help()
466
+
467
+ # Verify the help was retrieved
468
+ mock_todo_shell.get_help.assert_called_once()
469
+ assert help_output == "Todo.sh help output"
470
+
380
471
  def test_empty_input_handling(self):
381
472
  """Test that empty input is handled gracefully."""
382
473
  # This would be tested in the main run loop
@@ -108,16 +108,16 @@ class TestToolErrorHandling:
108
108
  assert "Permission denied" in result["user_message"]
109
109
  assert "check file permissions" in result["user_message"]
110
110
 
111
- def test_created_completed_task_tool_execution(self):
112
- """Test that the created_completed_task tool can be executed successfully."""
111
+ def test_create_completed_task_tool_execution(self):
112
+ """Test that the create_completed_task tool can be executed successfully."""
113
113
  # Mock the todo_manager method
114
- self.mock_todo_manager.created_completed_task.return_value = (
114
+ self.mock_todo_manager.create_completed_task.return_value = (
115
115
  "Created and completed task: Test task (completed on 2025-01-15)"
116
116
  )
117
117
 
118
118
  tool_call = {
119
119
  "function": {
120
- "name": "created_completed_task",
120
+ "name": "create_completed_task",
121
121
  "arguments": '{"description": "Test task", "completion_date": "2025-01-15"}',
122
122
  },
123
123
  "id": "test_id",
@@ -128,10 +128,10 @@ class TestToolErrorHandling:
128
128
  assert result["error"] is False
129
129
  assert "Created and completed task: Test task" in result["output"]
130
130
  assert result["tool_call_id"] == "test_id"
131
- assert result["name"] == "created_completed_task"
131
+ assert result["name"] == "create_completed_task"
132
132
 
133
133
  # Verify the method was called with correct arguments
134
- self.mock_todo_manager.created_completed_task.assert_called_once_with(
134
+ self.mock_todo_manager.create_completed_task.assert_called_once_with(
135
135
  description="Test task", completion_date="2025-01-15"
136
136
  )
137
137
 
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.3.3'
32
- __version_tuple__ = version_tuple = (0, 3, 3)
31
+ __version__ = version = '0.3.6'
32
+ __version_tuple__ = version_tuple = (0, 3, 6)
33
33
 
34
- __commit_id__ = commit_id = 'g4d82778ea'
34
+ __commit_id__ = commit_id = 'g30b41eb2b'