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 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.2.9'
32
- __version_tuple__ = version_tuple = (0, 2, 9)
31
+ __version__ = version = '0.3.1'
32
+ __version_tuple__ = version_tuple = (0, 3, 1)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -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
- return f"Current date and time: {now.strftime('%Y-%m-%d %H:%M:%S')} ({now.strftime('%A, %B %d, %Y at %I:%M %p')})"
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
- from datetime import datetime
75
-
76
- current_datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
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")