todo-agent 0.3.3__py3-none-any.whl → 0.3.6__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.
@@ -80,20 +80,20 @@ class Inference:
80
80
  def current_tasks(self) -> str:
81
81
  """
82
82
  Get current tasks from the todo manager.
83
-
83
+
84
84
  Returns:
85
85
  Formatted string of current tasks or error message
86
86
  """
87
87
  try:
88
88
  # Use the todo manager from the tool handler to get current tasks
89
89
  tasks = self.tool_handler.todo_manager.list_tasks(suppress_color=True)
90
-
90
+
91
91
  # If no tasks found, return a clear message
92
92
  if not tasks.strip() or tasks == "No tasks found.":
93
93
  return "No current tasks found."
94
-
94
+
95
95
  return tasks
96
-
96
+
97
97
  except Exception as e:
98
98
  self.logger.warning(f"Failed to get current tasks: {e!s}")
99
99
  return f"Error retrieving current tasks: {e!s}"
@@ -355,7 +355,9 @@ class Inference:
355
355
  self.tool_handler.tools
356
356
  )
357
357
 
358
- def _get_tool_progress_description(self, tool_name: str, tool_call: Dict[str, Any]) -> str:
358
+ def _get_tool_progress_description(
359
+ self, tool_name: str, tool_call: Dict[str, Any]
360
+ ) -> str:
359
361
  """
360
362
  Get user-friendly progress description for a tool with parameter interpolation.
361
363
 
@@ -377,25 +379,30 @@ class Inference:
377
379
 
378
380
  if tool_def and "progress_description" in tool_def:
379
381
  template = tool_def["progress_description"]
380
-
382
+
381
383
  # Extract arguments from tool call
382
384
  arguments = tool_call.get("function", {}).get("arguments", {})
383
385
  if isinstance(arguments, str):
384
386
  import json
387
+
385
388
  try:
386
389
  arguments = json.loads(arguments)
387
390
  except json.JSONDecodeError:
388
391
  arguments = {}
389
-
392
+
390
393
  # Use .format() like the system prompt does
391
394
  try:
392
395
  return template.format(**arguments)
393
396
  except KeyError as e:
394
397
  # If a required parameter is missing, fall back to template
395
- self.logger.warning(f"Missing parameter {e} for progress description of {tool_name}")
398
+ self.logger.warning(
399
+ f"Missing parameter {e} for progress description of {tool_name}"
400
+ )
396
401
  return template
397
402
  except Exception as e:
398
- self.logger.warning(f"Failed to interpolate progress description for {tool_name}: {e}")
403
+ self.logger.warning(
404
+ f"Failed to interpolate progress description for {tool_name}: {e}"
405
+ )
399
406
  return template
400
407
 
401
408
  # Fallback to generic description
@@ -12,48 +12,135 @@ You are an AI interface to the user's todo.sh task management system with direct
12
12
 
13
13
  ## Decision Flow
14
14
  1. **Data Discovery** → `list_tasks()` and `list_completed_tasks()` to fetch current and completed tasks
15
+ 2. **Date Discovery**: → `parse_date()` to fetch the calendar dates of any user-specified days
15
16
  2. **Planning Phase** → Analyze tasks and plan operations in logical order:
16
17
  - Multiple distinct goals may be indicated by the user
17
- - Identify dependencies and blocking relationships
18
+ - "I did X" Search existing tasks first, then complete or create_completed_task()
19
+ - Identify dependencies/parents and blocking relationships
20
+ - Identify the due date of recurring tasks using parse_date()
18
21
  - Determine priority sequence (overdue → due today → due soon → others)
19
22
  - Plan context-specific operations if needed
20
23
  - Map out required tool calls in execution order
21
24
  - Detail the execution plan in the response content
22
25
  3. **Execution Phase** → Execute planned operations in sequence:
23
26
  - Task operations: discover → analyze → execute
24
- - "I did X" → Search existing tasks first, then complete or create_completed_task()
25
- - Context filtering → Use exact matching: `list_tasks("@context")` only returns tasks with that specific context
26
27
  4. **Validation** → Verify all planned operations completed successfully
27
- 5. **Respond**: Generate a conversational, context-aware reply that summarizes the actions taken, explains reasoning (especially for due dates, priorities, or suggestions), and presents results in a natural, engaging tone. Always reference real data and operations performed. If no action was taken, clearly state why. Ensure the response is logically consistent, matches the user's style, and highlights any important next steps or recommendations.
28
+ 5. **Respond**: Generate a conversational, context-aware reply that summarizes the actions taken, explains
29
+ reasoning (especially for due dates, priorities, or suggestions), and presents results in a natural, engaging tone.
30
+ Always reference real data and operations performed. If no action was taken, clearly state why. Ensure the response
31
+ is logically consistent, matches the user's style, and highlights any important next steps or recommendations.
28
32
 
29
33
  ## Todo.txt Format
30
34
  ```
31
- (A) Task description +project @context due:YYYY-MM-DD duration:1h
35
+ (A) Task description +project @context due:YYYY-MM-DD duration:2h parent:12
36
+ (B) Put out trash and recycling +weekly +thursday @home duration:5m
37
+ (C) Standup meeting at 9:00AM +daily +weekdays @office duration:10m
38
+ (D) Another task (weekly on Friday) +project @context duration:10m
39
+ (E) Yet another task (daily) +project @context duration:2h
32
40
  x YYYY-MM-DD Completed task description
33
41
  ```
34
42
 
43
+ **Key Format Rules:**
44
+ - **Recurrence Tags**: Use `+weekly`, `+daily`, `+monthly` for recurrence frequency
45
+ - **Day Specificity**: Recurring tasks use `+monday`, `+tuesday`, `+wednesday`, `+thursday`, `+friday`, `+saturday`, `+sunday` for specific days
46
+ - **Weekday Patterns**: Recurring tasks use `+weekdays` for Monday-Friday, `+weekends` for Saturday-Sunday
47
+ - **Multiple Recurrence**: Combine tags like `+weekly +thursday` or `+daily +weekdays`
48
+ - **Time Specification**: Include time directly in description (e.g., "at 9:00AM", "at 2:00PM")
49
+ - **Duration**: Always specify `duration:Xm` or `duration:Xh` for time estimates
50
+
51
+ Example: add_task(description='Put out trash and recycling', project='weekly', context='home', due='YYYY-MM-DD', duration='5m')
52
+
35
53
  ## Key Intelligence Engines
36
54
 
55
+ ### Recurring Tasks
56
+ - **Daily Tasks**: If the task is indicated as 'daily', it is considered due today, regardless if a due date is specified.
57
+ - **Daily Weekday Tasks**: If the task is indicated as 'daily +weekdays', it is due on the next weekday (Monday-Friday).
58
+ - **Weekly Tasks**: If the task is indicated as 'weekly', it is due on the day of the week mentioned in the task description THIS WEEK.
59
+ - **Weekly Day-Specific Tasks**: If the task contains both '+weekly' and a specific day (e.g., '+thursday'), it is due on that specific day THIS WEEK.
60
+ - **Time-Specific Tasks**: If the task contains a specific time (e.g., 'at 9:00AM'), use parse_date() to determine the next occurrence of that time.
61
+ - **Work in Progress Tasks**: If the task has a 'wip' project tag (work in progress), it is considered due today, regardless if a due date is specified.
62
+ - **Due Date Inference**: Issue parse_date() to determine the due date of recurring tasks, especially for complex patterns like "weekly on Thursday" or "daily on weekdays at 9:00AM".
63
+
64
+ ### Complex Recurrence Pattern Examples
65
+ - `+weekly +thursday` → Due on the next Thursday
66
+ - `+daily +weekdays` → Due on the next weekday (Monday-Friday)
67
+ - `at 9:00AM +daily +weekdays` → Due on the next weekday at 9:00AM
68
+ - `+weekly +friday duration:2h` → Due on the next Friday with 2-hour duration
69
+ - `+monthly +first` → Due on the first day of next month
70
+
37
71
  ### Task Creation Protocol
38
72
  1. Get current + completed tasks to check duplicates
39
73
  2. Infer project/context/duration from description and patterns
40
- 3. Apply completion date intelligence (work tasks by week end, bills 3-5 days early, etc.)
41
- 4. Create with full metadata
42
-
43
- ### Task Completion Protocol
44
- 1. Search semantically in active tasks
45
- 2. Single match → complete immediately
46
- 3. Multiple/fuzzy show options
47
- 4. No match suggest alternatives
74
+ 3. **Parse complex recurrence patterns**: Handle multiple recurrence indicators (e.g., '+weekly +thursday', '+daily +weekdays')
75
+ 4. **Handle time specifications**: Extract and process time-based scheduling (e.g., 'at 9:00AM')
76
+ 5. **Parse parent task relationships**: Identify when user indicates task dependencies or hierarchies
77
+ 6. Apply completion date intelligence (work tasks by week end, bills 3-5 days early, etc.)
78
+ 7. Create with full metadata including proper due date calculation and parent relationships
79
+
80
+ ### Parent Task Relationships
81
+ **Natural Language Indicators for Parent Tasks**:
82
+ - **Explicit Dependencies**: "after [task]", "once [task] is done", "following [task]", "depends on [task]"
83
+ - **Hierarchical Language**: "subtask of [task]", "part of [task]", "under [task]", "child of [task]"
84
+ - **Sequential Language**: "next step after [task]", "continue with [task]", "build on [task]"
85
+ - **Conditional Language**: "if [task] then [new task]", "when [task] is complete, do [new task]"
86
+ - **Update Commands**: "make [task] depend on [task]", "update [task] to depend on [task]", "put [task] under [task]", "move [task] under [task]"
87
+
88
+ **Parent Task Resolution Protocol**:
89
+ 1. **Identify Parent Indicators**: Look for natural language cues indicating task relationships (creation or updates)
90
+ 2. **Find Tasks**: Use `list_tasks()` to locate both the child task and parent task
91
+ 3. **Validate Relationship**: Ensure the parent-child relationship makes logical sense
92
+ 4. **Establish Relationship**:
93
+ - **For new tasks**: Create child task with `parent:XX` reference
94
+ - **For existing tasks**: Use `set_parent()` to update target task with parent relationship
95
+ 5. **Maintain Hierarchy**: Preserve task dependencies in the todo.txt format with `parent:XX`
96
+
97
+ **Parent Task Examples**:
98
+ - **Creating new tasks**: "Add a subtask to review the quarterly report" → Find parent task "Review quarterly report" and create child task with `parent:XX`
99
+ - **Creating new tasks**: "After I finish the presentation, I need to schedule a follow-up meeting" → Find "presentation" task and create "schedule follow-up meeting" with `parent:XX`
100
+ - **Creating new tasks**: "Once the code review is done, I'll deploy to staging" → Find "code review" task and create "deploy to staging" with `parent:XX`
101
+ - **Updating existing tasks**: "Update the listing tasks to depend on the pictures task" → Find "listing tasks" and "pictures task", then use `set_parent()` to update listing tasks
102
+ - **Updating existing tasks**: "Make the deployment depend on the testing task" → Find "deployment" and "testing" tasks, then use `set_parent()` to update deployment
103
+ - **Updating existing tasks**: "Put the documentation under the code review task" → Find "documentation" and "code review" tasks, then use `set_parent()` to update documentation
104
+
105
+ **Parent Task Format**: `(A) Child task description +project @context due:YYYY-MM-DD duration:2h parent:12`
106
+
107
+ ### Task Completion Protocol
108
+ NOTE: use complete_task() for existing non-recurring tasks
109
+ 1. **Discovery**: Use `list_tasks()` to search semantically in active tasks
110
+ 2. **For Non-Recurring Tasks**:
111
+ - The task will NOT have the tag "rec:"
112
+ - Single match → use `complete_task(task_number='XX')` to mark existing active tasks complete
113
+ - Multiple/fuzzy → show options
114
+ - No match → suggest alternatives
115
+ 3. **For Recurring Tasks** (containing +daily, +weekly, +monthly, +weekdays, etc.):
116
+ - The task will have the tag "rec:"
117
+ - **USE** `create_completed_task()` with the original task number as parent_number
118
+ - **PRESERVE** the original recurring task for future occurrences. **IMPORTANT** DO NOT MARK COMPLETE! DO NOT MODIFY!
119
+ - **TOOL CALL**: `create_completed_task(description='Task description', parent_number='XX', completion_date='YYYY-MM-DD', context='context', project='project')`
120
+
121
+
122
+ ### Recurring Task Completion Examples
123
+ NOTE: ONLY USE create_completed_task() for RECURRING TASKS!
124
+ - **User says**: "I put out the trash" → Find `(B) Put out trash and recycling +weekly +thursday @home duration:5m`
125
+ - **Tool Call**: ONLY FOR EXISTING TASKS! `create_completed_task(description='Put out trash and recycling', parent_number='B', completion_date='YYYY-MM-DD', context='home', project='weekly')`
126
+ - **Result**: Original task remains active for next Thursday
127
+ - **User says**: "Done with standup" → Find `Standup meeting at 9:00AM +daily +weekdays @office duration:10m`
128
+ - **Tool Call**: ONLY FOR EXISTING TASKS! `create_completed_task(description='Standup meeting at 9:00AM', parent_number='XX', completion_date='YYYY-MM-DD', context='office', project='daily')`
129
+ - **Result**: Original task remains active for next weekday
48
130
 
49
131
  ### Task Suggestions
50
132
  **Trigger**: User asks, seems stuck, or after completions
51
- **Method**:
133
+ **Method**:
134
+ - Identify tasks within the user's implied temporal scope
135
+ - Consider that today is {current_datetime}, and match any recurring tasks
136
+ - Pay careful attention to due dates and their relation to the current date
137
+ - @office and work tasks are always the highest priority
138
+ - +wip tasks are considered high priority and due today
52
139
  - Balance urgency and priority. Use your best judgment.
53
- - Logical dependencies trend first (tasks that unblock others get priority)
140
+ - Logical and explicit dependencies should be suggested first (tasks that unblock others get priority)
54
141
  - Then urgency (overdue → due today → due soon)
55
- - Pay careful attention to due dates and their relation to the current date {{current_datetime}}
56
- - Mention days of week when dates are mentioned for clarity. Minimize repetition.
142
+ - Be exhaustive in your search and mention everything relevant
143
+ - Always state days of the week
57
144
 
58
145
  ### Context Patterns
59
146
  - `@phone`: calls, appointments
@@ -64,6 +151,23 @@ x YYYY-MM-DD Completed task description
64
151
 
65
152
  ### Project Patterns
66
153
  - Health → `+health`, Work → `+work`, Bills → `+bills`, etc.
154
+ - Recurring tasks:
155
+ - `+daily`: ALWAYS consider due today
156
+ - `+weekly`: ALWAYS consider due on the specific day THIS WEEK
157
+
158
+ ## Notes Protocol
159
+ **When the user wants to create a note**:
160
+ **Triggers**: 'note:', or 'Create a note', or 'progress:', etc
161
+ **Action**: create_completed_task()
162
+ **Method**:
163
+ 1. ALWAYS Identify semantically similar tasks that are the likely parent task
164
+ 2. IF there is LIKELY match, that is the parent and parent_number
165
+ 4. Create a completed task with create_completed_task():
166
+ - **IMPORTANT**: ALWAYS assign the parent_number if a match was found
167
+ - with inferred parent_number, completion_date, context, and project
168
+ - Task description should never include date of completion
169
+ - NO priority
170
+ **Response**: ALWAYS refer to your note actions as creating a note, NOT creating a task
67
171
 
68
172
  ## Critical Rules
69
173
  - **Overdue definition**: A task is overdue IF AND _ONLY IF_ due < {current_datetime}. None is an acceptable answer!
@@ -71,11 +175,14 @@ x YYYY-MM-DD Completed task description
71
175
  - **Task ordering**: Always dependencies first, then urgency
72
176
  - **Data integrity**: Only use real tool data, never fabricate
73
177
  - **Completion date reasoning**: Always explain date suggestions briefly
178
+ - **Parent Tasks**: Task dependencies are explicitly indicated by `parent:XX` tags
179
+ - **Parent Task Detection**: Always identify and establish parent-child relationships when users indicate task dependencies through natural language
74
180
 
75
181
  ## Tool Selection Strategy
76
182
  - Project tags: use `set_project()`
77
183
  - Context tags: use `set_context()`
78
184
  - Due dates: use `set_due_date()`
185
+ - Parent relationships: use `set_parent()`
79
186
  - Discovery: `list_tasks()` once for full context
80
187
  - Completion: `list_tasks()` + `complete_task()`
81
188
  - Addition: `list_tasks()` + `add_task()` with full metadata
@@ -108,6 +108,10 @@ class TodoShell:
108
108
  """Add new task."""
109
109
  return self.execute(["todo.sh", "add", description])
110
110
 
111
+ def addto(self, destination: str, text: str) -> str:
112
+ """Add text to a specific file in the todo.txt directory."""
113
+ return self.execute(["todo.sh", "addto", destination, text])
114
+
111
115
  def list_tasks(
112
116
  self, filter_str: Optional[str] = None, suppress_color: bool = True
113
117
  ) -> str:
@@ -178,6 +182,10 @@ class TodoShell:
178
182
  """Archive completed tasks."""
179
183
  return self.execute(["todo.sh", "-f", "archive"])
180
184
 
185
+ def get_help(self) -> str:
186
+ """Get todo.sh help output."""
187
+ return self.execute(["todo.sh", "help"], suppress_color=False)
188
+
181
189
  def set_due_date(self, task_number: int, due_date: str) -> str:
182
190
  """
183
191
  Set or update due date for a task by intelligently rewriting it.
@@ -276,6 +284,61 @@ class TodoShell:
276
284
  # Replace the task with the new description
277
285
  return self.replace(task_number, new_description)
278
286
 
287
+ def set_parent(self, task_number: int, parent_number: Optional[int]) -> str:
288
+ """
289
+ Set or update parent task number for a task by intelligently rewriting it.
290
+
291
+ Args:
292
+ task_number: The task number to modify
293
+ parent_number: Parent task number, or None to remove parent
294
+
295
+ Returns:
296
+ The updated task description
297
+ """
298
+ # First, get the current task to parse its components
299
+ tasks_output = self.list_tasks()
300
+ task_lines = tasks_output.strip().split("\n")
301
+
302
+ # Find the task by its actual number (not array index)
303
+ current_task = None
304
+ for line in task_lines:
305
+ if line.strip():
306
+ # Extract task number from the beginning of the line (handling ANSI codes)
307
+ extracted_number = self._extract_task_number(line)
308
+ if extracted_number == task_number:
309
+ current_task = line
310
+ break
311
+
312
+ if not current_task:
313
+ raise TodoShellError(f"Task number {task_number} not found")
314
+
315
+ # Parse the current task components
316
+ components = self._parse_task_components(current_task)
317
+
318
+ # Update the parent (None removes it)
319
+ if parent_number is not None:
320
+ if not isinstance(parent_number, int) or parent_number <= 0:
321
+ raise TodoShellError(
322
+ f"Invalid parent_number '{parent_number}'. Must be a positive integer."
323
+ )
324
+ parent_tag = f"parent:{parent_number}"
325
+ # Remove any existing parent tag and add the new one
326
+ components["other_tags"] = [
327
+ tag for tag in components["other_tags"] if not tag.startswith("parent:")
328
+ ]
329
+ components["other_tags"].append(parent_tag)
330
+ else:
331
+ # Remove parent tag
332
+ components["other_tags"] = [
333
+ tag for tag in components["other_tags"] if not tag.startswith("parent:")
334
+ ]
335
+
336
+ # Reconstruct the task
337
+ new_description = self._reconstruct_task(components)
338
+
339
+ # Replace the task with the new description
340
+ return self.replace(task_number, new_description)
341
+
279
342
  def _extract_task_number(self, line: str) -> Optional[int]:
280
343
  """
281
344
  Extract task number from a line that may contain ANSI color codes.
@@ -225,6 +225,23 @@ class CLI:
225
225
  self.console.print(table)
226
226
  self.console.print("Or just type your request naturally!", style="italic green")
227
227
 
228
+ def _print_todo_help(self) -> None:
229
+ """Print todo.sh help information."""
230
+ try:
231
+ # Get todo.sh help output
232
+ help_output = self.todo_shell.get_help()
233
+ formatted_output = TaskFormatter.format_task_list(help_output)
234
+ help_panel = PanelFormatter.create_task_panel(
235
+ formatted_output, title="📋 Todo.sh Help"
236
+ )
237
+ self.console.print(help_panel)
238
+ except Exception as e:
239
+ self.logger.error(f"Error getting todo.sh help: {e!s}")
240
+ error_msg = ResponseFormatter.format_error(
241
+ f"Failed to get todo.sh help: {e!s}"
242
+ )
243
+ self.console.print(error_msg)
244
+
228
245
  def _print_about(self) -> None:
229
246
  """Print about information in a formatted panel."""
230
247
  about_panel = PanelFormatter.create_about_panel()
@@ -280,6 +297,37 @@ class CLI:
280
297
  if not user_input:
281
298
  continue
282
299
 
300
+ # Handle todo.sh passthrough commands (starting with /)
301
+ if user_input.startswith("/"):
302
+ self.logger.debug(
303
+ f"Processing todo.sh passthrough command: {user_input}"
304
+ )
305
+ try:
306
+ # Remove the leading / and execute as todo.sh command
307
+ todo_command = user_input[1:].strip()
308
+ if not todo_command:
309
+ self.console.print(
310
+ ResponseFormatter.format_error("Empty todo.sh command")
311
+ )
312
+ continue
313
+
314
+ # Execute the todo.sh command directly
315
+ output = self.todo_shell.execute(
316
+ ["todo.sh", *todo_command.split()]
317
+ )
318
+ formatted_output = TaskFormatter.format_task_list(output)
319
+ task_panel = PanelFormatter.create_task_panel(
320
+ formatted_output, title="📋 Todo.sh Output"
321
+ )
322
+ self.console.print(task_panel)
323
+ except Exception as e:
324
+ self.logger.error(f"Error executing todo.sh command: {e!s}")
325
+ error_msg = ResponseFormatter.format_error(
326
+ f"Todo.sh command failed: {e!s}"
327
+ )
328
+ self.console.print(error_msg)
329
+ continue
330
+
283
331
  # Handle special commands
284
332
  if user_input.lower() == "clear":
285
333
  self.logger.info("User requested conversation clear")
@@ -302,6 +350,11 @@ class CLI:
302
350
  self._print_help()
303
351
  continue
304
352
 
353
+ if user_input.lower() == "todo-help":
354
+ self.logger.debug("User requested todo.sh help")
355
+ self._print_todo_help()
356
+ continue
357
+
305
358
  if user_input.lower() == "about":
306
359
  self.logger.debug("User requested about information")
307
360
  self._print_about()
@@ -357,6 +357,7 @@ class TableFormatter:
357
357
  ("clear", "Clear conversation history"),
358
358
  ("stats", "Show conversation statistics"),
359
359
  ("help", "Show this help message"),
360
+ ("todo-help", "Show todo.sh help"),
360
361
  ("about", "Show application information"),
361
362
  ("list", "List all tasks (no LLM interaction)"),
362
363
  ("done", "List completed tasks (no LLM interaction)"),
@@ -16,7 +16,7 @@ Task Management Tools:
16
16
  - append_to_task(task_number, text) - Add text to end of existing task
17
17
  - prepend_to_task(task_number, text) - Add text to beginning of existing task
18
18
  - delete_task(task_number, term?) - Delete entire task or remove specific term
19
- - created_completed_task(description, completion_date?, project?, context?) - Create task and immediately mark as completed
19
+ - create_completed_task(description, completion_date?, project?, context?) - Create task and immediately mark as completed
20
20
 
21
21
  Priority Management Tools:
22
22
  - set_priority(task_number, priority) - Set or change task priority (A-Z)
@@ -26,6 +26,7 @@ Task Modification Tools:
26
26
  - set_due_date(task_number, due_date) - Set or update due date for a task by intelligently rewriting it (use empty string to remove due date)
27
27
  - set_context(task_number, context) - Set or update context for a task by intelligently rewriting it (use empty string to remove context)
28
28
  - set_project(task_number, projects) - Set or update projects for a task by intelligently rewriting it (handles array of projects with add/remove operations)
29
+ - set_parent(task_number, parent_number) - Set or update parent task number for a task by intelligently rewriting it (use None to remove parent)
29
30
 
30
31
  Utility Tools:
31
32
  - move_task(task_number, destination, source?) - Move task between files
@@ -176,6 +177,10 @@ class ToolCallHandler:
176
177
  "type": "string",
177
178
  "description": "Optional duration estimate in format: minutes (e.g., '30m'), hours (e.g., '2h'), or days (e.g., '1d'). Use for time planning and task prioritization.",
178
179
  },
180
+ "parent_number": {
181
+ "type": "integer",
182
+ "description": "Optional parent task number (required for subtasks)",
183
+ },
179
184
  },
180
185
  "required": ["description"],
181
186
  },
@@ -254,12 +259,12 @@ class ToolCallHandler:
254
259
  "type": "integer",
255
260
  "description": "The line number of the task to modify (required)",
256
261
  },
257
- "text_to_append": {
262
+ "text": {
258
263
  "type": "string",
259
264
  "description": "Text to add to the end of the task (required)",
260
265
  },
261
266
  },
262
- "required": ["task_number", "text_to_append"],
267
+ "required": ["task_number", "text"],
263
268
  },
264
269
  },
265
270
  "progress_description": "📝 Adding notes to task #{task_number}...",
@@ -474,6 +479,39 @@ class ToolCallHandler:
474
479
  },
475
480
  "progress_description": "🏷️ Setting project tags for task #{task_number}...",
476
481
  },
482
+ {
483
+ "type": "function",
484
+ "function": {
485
+ "name": "set_parent",
486
+ "description": (
487
+ "Set or update the parent task number for an EXISTING task by intelligently rewriting it. "
488
+ "USE CASE: Call this when user wants to add, change, or remove a parent task relationship on an existing task. "
489
+ "NOT FOR: Creating new tasks, completing tasks, or any other task operations. "
490
+ "This preserves all existing task components (priority, projects, contexts, due date, etc.) "
491
+ "while updating or adding the parent relationship. Use None to remove the parent. "
492
+ "PREFERRED METHOD: Use this instead of append_to_task() when adding parent tags (parent:XX). "
493
+ "This tool properly manages parent relationships and prevents formatting issues. "
494
+ "IMPORTANT: Use list_tasks() first "
495
+ "to find the correct task number if user doesn't specify it. "
496
+ "Parent task numbers should be provided as integers (e.g., 12 not 'parent:12')."
497
+ ),
498
+ "parameters": {
499
+ "type": "object",
500
+ "properties": {
501
+ "task_number": {
502
+ "type": "integer",
503
+ "description": "The line number of the task to modify (required)",
504
+ },
505
+ "parent_number": {
506
+ "type": "integer",
507
+ "description": "Parent task number to set, or null to remove parent (required)",
508
+ },
509
+ },
510
+ "required": ["task_number", "parent_number"],
511
+ },
512
+ },
513
+ "progress_description": "🔗 Setting parent task {parent_number} for task #{task_number}...",
514
+ },
477
515
  {
478
516
  "type": "function",
479
517
  "function": {
@@ -585,16 +623,17 @@ class ToolCallHandler:
585
623
  {
586
624
  "type": "function",
587
625
  "function": {
588
- "name": "created_completed_task",
626
+ "name": "create_completed_task",
589
627
  "description": (
590
628
  "Create a task and immediately mark it as completed. "
591
- "USE CASE: Call this when user says they completed something on a specific date (e.g., 'I did the laundry today', 'I finished the report yesterday', 'I cleaned the garage last week') "
629
+ "USE CASE: WHEN NO MATCH IS FOUND! Call this when user says they completed something on a specific date (e.g., 'I did the laundry today', 'I finished the report yesterday', 'I cleaned the garage last week') "
592
630
  "and you have already researched existing tasks to determine no match exists. "
593
- "WORKFLOW: 1) Use list_tasks() to search for existing tasks, 2) Use list_completed_tasks() to verify it's not already done, "
594
- "3) If no match found, call this tool to create and complete the task in one operation. "
631
+ "WORKFLOW: 1) Use list_tasks() to search for existing tasks, "
632
+ "2) Use list_completed_tasks() to verify it's not already done, "
633
+ "3) If a match is found, use complete_task() to mark it complete, "
634
+ "4) If no match found, call this tool to create and complete the task in one operation. "
595
635
  "STRATEGIC CONTEXT: This is a convenience tool for the common pattern of 'I did X on [date]' - "
596
- "it creates a task with the specified completion date and immediately marks it complete. "
597
- "The LLM should handle the research and decision-making about whether to use this tool."
636
+ "when no task match is found, it creates a task with the specified completion date and immediately marks it complete. "
598
637
  ),
599
638
  "parameters": {
600
639
  "type": "object",
@@ -615,6 +654,10 @@ class ToolCallHandler:
615
654
  "type": "string",
616
655
  "description": "Optional context name (without the @ symbol) for new task creation",
617
656
  },
657
+ "parent_number": {
658
+ "type": "integer",
659
+ "description": "Optional parent task number (required for subtasks)",
660
+ },
618
661
  },
619
662
  "required": ["description"],
620
663
  },
@@ -897,11 +940,12 @@ class ToolCallHandler:
897
940
  "set_due_date": self.todo_manager.set_due_date,
898
941
  "set_context": self.todo_manager.set_context,
899
942
  "set_project": self.todo_manager.set_project,
943
+ "set_parent": self.todo_manager.set_parent,
900
944
  "move_task": self.todo_manager.move_task,
901
945
  "archive_tasks": self.todo_manager.archive_tasks,
902
946
  "parse_date": self._parse_date,
903
947
  "get_calendar": self._get_calendar,
904
- "created_completed_task": self.todo_manager.created_completed_task,
948
+ "create_completed_task": self.todo_manager.create_completed_task,
905
949
  "restore_completed_task": self.todo_manager.restore_completed_task,
906
950
  }
907
951
 
todo_agent/main.py CHANGED
@@ -11,9 +11,12 @@ from .interface.cli import CLI
11
11
 
12
12
  def main() -> None:
13
13
  """Main application entry point."""
14
+ from ._version import __version__
15
+
14
16
  parser = argparse.ArgumentParser(
15
- description="Todo.sh LLM Agent - Natural language task management",
17
+ description=f"Todo.sh LLM Agent - Natural language task management (v{__version__})",
16
18
  formatter_class=argparse.RawDescriptionHelpFormatter,
19
+ add_help=False,
17
20
  epilog="""
18
21
  Examples:
19
22
  todo-agent # Interactive mode
@@ -23,6 +26,21 @@ Examples:
23
26
  """,
24
27
  )
25
28
 
29
+ parser.add_argument(
30
+ "--version",
31
+ "-v",
32
+ action="version",
33
+ version=f"%(prog)s {__version__}",
34
+ help="Show version information and exit",
35
+ )
36
+
37
+ parser.add_argument(
38
+ "--help",
39
+ "-h",
40
+ action="help",
41
+ help="Show this help message and exit",
42
+ )
43
+
26
44
  parser.add_argument(
27
45
  "command",
28
46
  nargs="?",
@@ -37,11 +55,28 @@ Examples:
37
55
  if args.command:
38
56
  # Single command mode
39
57
  # Handle special commands that don't need LLM processing
40
- if args.command.lower() in ["help", "about"]:
58
+ if args.command.lower() in ["help", "about", "todo-help"]:
41
59
  if args.command.lower() == "help":
42
60
  cli._print_help()
43
61
  elif args.command.lower() == "about":
44
62
  cli._print_about()
63
+ elif args.command.lower() == "todo-help":
64
+ cli._print_todo_help()
65
+ elif args.command.startswith("/"):
66
+ # Handle todo.sh passthrough commands
67
+ try:
68
+ # Remove the leading / and execute as todo.sh command
69
+ todo_command = args.command[1:].strip()
70
+ if not todo_command:
71
+ print("Error: Empty todo.sh command")
72
+ sys.exit(1)
73
+
74
+ # Execute the todo.sh command directly
75
+ output = cli.todo_shell.execute(["todo.sh", *todo_command.split()])
76
+ print(output)
77
+ except Exception as e:
78
+ print(f"Error: Todo.sh command failed: {e}")
79
+ sys.exit(1)
45
80
  else:
46
81
  # Process through LLM
47
82
  response = cli.run_single_request(args.command)
@@ -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