todo-agent 0.2.9__py3-none-any.whl → 0.3.1__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.
- todo_agent/_version.py +2 -2
- todo_agent/core/todo_manager.py +33 -1
- todo_agent/infrastructure/inference.py +4 -3
- todo_agent/infrastructure/openrouter_client.py +5 -5
- todo_agent/infrastructure/prompts/system_prompt.txt +387 -401
- todo_agent/infrastructure/todo_shell.py +27 -13
- todo_agent/interface/cli.py +2 -2
- todo_agent/interface/tools.py +71 -118
- {todo_agent-0.2.9.dist-info → todo_agent-0.3.1.dist-info}/METADATA +1 -1
- {todo_agent-0.2.9.dist-info → todo_agent-0.3.1.dist-info}/RECORD +14 -14
- {todo_agent-0.2.9.dist-info → todo_agent-0.3.1.dist-info}/WHEEL +0 -0
- {todo_agent-0.2.9.dist-info → todo_agent-0.3.1.dist-info}/entry_points.txt +0 -0
- {todo_agent-0.2.9.dist-info → todo_agent-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {todo_agent-0.2.9.dist-info → todo_agent-0.3.1.dist-info}/top_level.txt +0 -0
todo_agent/_version.py
CHANGED
@@ -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.
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
31
|
+
__version__ = version = '0.3.1'
|
32
|
+
__version_tuple__ = version_tuple = (0, 3, 1)
|
33
33
|
|
34
34
|
__commit_id__ = commit_id = None
|
todo_agent/core/todo_manager.py
CHANGED
@@ -20,6 +20,7 @@ class TodoManager:
|
|
20
20
|
context: Optional[str] = None,
|
21
21
|
due: Optional[str] = None,
|
22
22
|
recurring: Optional[str] = None,
|
23
|
+
duration: Optional[str] = None,
|
23
24
|
) -> str:
|
24
25
|
"""Add new task with explicit project/context parameters."""
|
25
26
|
# Validate and sanitize inputs
|
@@ -82,6 +83,32 @@ class TodoManager:
|
|
82
83
|
f"Invalid interval '{parts[2]}'. Must be a positive integer."
|
83
84
|
)
|
84
85
|
|
86
|
+
if duration is not None:
|
87
|
+
# Validate duration format (e.g., "30m", "2h", "1d")
|
88
|
+
if not duration or not isinstance(duration, str):
|
89
|
+
raise ValueError("Duration must be a non-empty string.")
|
90
|
+
|
91
|
+
# Check if duration ends with a valid unit
|
92
|
+
if not any(duration.endswith(unit) for unit in ["m", "h", "d"]):
|
93
|
+
raise ValueError(
|
94
|
+
f"Invalid duration format '{duration}'. Must end with m (minutes), h (hours), or d (days)."
|
95
|
+
)
|
96
|
+
|
97
|
+
# Extract the numeric part and validate it
|
98
|
+
value = duration[:-1]
|
99
|
+
if not value:
|
100
|
+
raise ValueError("Duration value cannot be empty.")
|
101
|
+
|
102
|
+
try:
|
103
|
+
# Check if the value is a valid positive number
|
104
|
+
numeric_value = float(value)
|
105
|
+
if numeric_value <= 0:
|
106
|
+
raise ValueError("Duration value must be positive.")
|
107
|
+
except ValueError:
|
108
|
+
raise ValueError(
|
109
|
+
f"Invalid duration value '{value}'. Must be a positive number."
|
110
|
+
)
|
111
|
+
|
85
112
|
# Build the full task description with priority, project, and context
|
86
113
|
full_description = description
|
87
114
|
|
@@ -100,6 +127,9 @@ class TodoManager:
|
|
100
127
|
if recurring:
|
101
128
|
full_description = f"{full_description} {recurring}"
|
102
129
|
|
130
|
+
if duration:
|
131
|
+
full_description = f"{full_description} duration:{duration}"
|
132
|
+
|
103
133
|
self.todo_shell.add(full_description)
|
104
134
|
return f"Added task: {full_description}"
|
105
135
|
|
@@ -373,4 +403,6 @@ class TodoManager:
|
|
373
403
|
def get_current_datetime(self, **kwargs: Any) -> str:
|
374
404
|
"""Get the current date and time."""
|
375
405
|
now = datetime.now()
|
376
|
-
|
406
|
+
week_number = now.isocalendar()[1]
|
407
|
+
timezone = now.astimezone().tzinfo
|
408
|
+
return f"Current date and time: {now.strftime('%Y-%m-%d %H:%M:%S')} {timezone} ({now.strftime('%A, %B %d, %Y at %I:%M %p')}) - Week {week_number}"
|
@@ -4,6 +4,7 @@ LLM inference engine for todo.sh agent.
|
|
4
4
|
|
5
5
|
import os
|
6
6
|
import time
|
7
|
+
from datetime import datetime
|
7
8
|
from typing import Any, Dict, Optional
|
8
9
|
|
9
10
|
try:
|
@@ -71,9 +72,9 @@ class Inference:
|
|
71
72
|
tools_section = self._generate_tools_section()
|
72
73
|
|
73
74
|
# Get current datetime for interpolation
|
74
|
-
|
75
|
-
|
76
|
-
current_datetime =
|
75
|
+
now = datetime.now()
|
76
|
+
timezone_info = time.tzname[time.daylight]
|
77
|
+
current_datetime = f"{now.strftime('%Y-%m-%d %H:%M:%S')} {timezone_info}"
|
77
78
|
|
78
79
|
# Get calendar output
|
79
80
|
from .calendar_utils import get_calendar_output
|
@@ -76,24 +76,24 @@ class OpenRouterClient(LLMClient):
|
|
76
76
|
choices = response.get("choices", [])
|
77
77
|
if not choices:
|
78
78
|
return
|
79
|
-
|
79
|
+
|
80
80
|
choice = choices[0]
|
81
81
|
message = choice.get("message", {})
|
82
|
-
|
82
|
+
|
83
83
|
# Always log reasoning and content if present
|
84
84
|
reasoning = message.get("reasoning", "")
|
85
85
|
if reasoning:
|
86
86
|
self.logger.info(f"LLM reasoning: {reasoning}")
|
87
|
-
|
87
|
+
|
88
88
|
content = message.get("content", "")
|
89
89
|
if content:
|
90
90
|
self.logger.info(f"LLM content: {content}")
|
91
|
-
|
91
|
+
|
92
92
|
# Handle tool calls
|
93
93
|
tool_calls = message.get("tool_calls", [])
|
94
94
|
if tool_calls:
|
95
95
|
self.logger.info(f"Response contains {len(tool_calls)} tool calls")
|
96
|
-
|
96
|
+
|
97
97
|
# Log each tool call
|
98
98
|
for i, tool_call in enumerate(tool_calls, 1):
|
99
99
|
tool_name = tool_call.get("function", {}).get("name", "unknown")
|