Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,16 @@ You'll see an interactive prompt. Here are the available commands:

| Command | Description | Example |
|---------|-------------|---------|
| `add <task>` | Add a new task | `add Buy groceries` |
| `add <task> [--priority high\|medium\|low] [--category <name>]` | Add a new task with optional priority and category | `add Buy milk --priority high --category shopping` |
| `tasks` | Display all tasks | `tasks` |
| `complete <number>` | Mark a task as complete | `complete 1` |
| `priority <number> <level>` | Change task priority (high, medium, low) | `priority 1 high` |
| `category <number> <category>` | Set or update task category | `category 1 work` |
| `list <category>` | Filter and view tasks in a category | `list work` |
| `categories` | Display all categories and active task counts | `categories` |
| `sort` | Sort tasks by priority level | `sort` |
| `summary` | View active priority summary | `summary` |
| `tools` | List available tool functions | `tools` |
| `ai <question>` | Ask AI a question | `ai How should I prioritize my tasks?` |
| `exit` | Exit the application | `exit` |

Expand Down
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
pythonpath = . src
136 changes: 127 additions & 9 deletions src/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ def __init__(self):
self.tasks = []
self.client = OpenAI(api_key=OPENAI_API_KEY)

def add_task(self, task, priority="medium"):
def add_task(self, task, priority="medium", category=None, tags=None):
"""
Add a new task with priority level.
Add a new task with priority level and optional category/tags.
Priority can be: 'high', 'medium', 'low'
"""
priority = (priority or "medium").lower()
Expand All @@ -22,18 +22,32 @@ def add_task(self, task, priority="medium"):
if not task:
return "Error: Task title cannot be empty."

clean_category = category.strip().lower() if category and category.strip() else None
tag_list = []
if tags:
if isinstance(tags, str):
tag_list = [t.strip().lower() for t in tags.split(",") if t.strip()]
elif isinstance(tags, (list, tuple, set)):
tag_list = [str(t).strip().lower() for t in tags if str(t).strip()]
if clean_category and clean_category not in tag_list:
tag_list.insert(0, clean_category)

task_dict = {
"title": task,
"priority": priority,
"category": clean_category,
"tags": tag_list,
"completed": False
}

self.tasks.append(task_dict)
if clean_category:
return f"Task added: {task} (Priority: {priority.capitalize()}, Category: {clean_category})"
return f"Task added: {task} (Priority: {priority.capitalize()})"

def view_tasks(self, show_completed=False):
def view_tasks(self, show_completed=False, category=None):
"""
View all tasks with their priority levels.
View all tasks with their priority levels and optional category filter.
"""
if not self.tasks:
return "No tasks available."
Expand All @@ -42,7 +56,12 @@ def view_tasks(self, show_completed=False):
if not show_completed:
tasks_to_show = [t for t in self.tasks if not t.get('completed', False)]

if not tasks_to_show:
if category:
cat_lower = category.strip().lower()
tasks_to_show = [t for t in tasks_to_show if t.get('category') == cat_lower or cat_lower in t.get('tags', [])]
if not tasks_to_show:
return f"No tasks found in category '{category}'."
elif not tasks_to_show:
return "All tasks completed."

priority_labels = {
Expand All @@ -57,13 +76,112 @@ def view_tasks(self, show_completed=False):
output_lines = []
for i, task in enumerate(sorted_tasks, 1):
title = task['title']
priority = task.get('priority', 'medium')
priority_display = priority_labels.get(priority, '[MEDIUM]')
status = "[DONE]" if task.get('completed') else "[ ]"
output_lines.append(f"{i}. {status} {priority_display} - {title}")
if category:
cat_display = f"[{task.get('category') or category.strip().lower()}]"
done_suffix = " [DONE]" if task.get('completed') else ""
output_lines.append(f"{i}. {cat_display} {title}{done_suffix}")
else:
priority = task.get('priority', 'medium')
priority_display = priority_labels.get(priority, '[MEDIUM]')
cat = task.get('category')
cat_display = f"[{cat}] " if cat else ""
status = "[DONE]" if task.get('completed') else "[ ]"
output_lines.append(f"{i}. {status} {priority_display} {cat_display}- {title}")

return "\n".join(output_lines)

def set_category(self, task_number, category):
"""
Set or update category for an existing task.
"""
if task_number < 1 or task_number > len(self.tasks):
return "Error: Invalid task number."

category = (category or "").strip()
if not category:
return "Error: Category cannot be empty."

task = self.tasks[task_number - 1]
old_category = task.get('category')
cat_lower = category.lower()
task['category'] = cat_lower

if 'tags' not in task or not task['tags']:
task['tags'] = [cat_lower]
elif cat_lower not in task['tags']:
task['tags'].append(cat_lower)

if old_category:
return f"Category updated for '{task['title']}': {old_category} -> {cat_lower}"
return f"Category set for '{task['title']}': {cat_lower}"

def add_tags(self, task_number, tags):
"""
Add one or more tags to an existing task.
"""
if task_number < 1 or task_number > len(self.tasks):
return "Error: Invalid task number."

if not tags:
return "Error: Tags cannot be empty."

if isinstance(tags, str):
new_tags = [t.strip().lower() for t in tags.split(",") if t.strip()]
else:
new_tags = [str(t).strip().lower() for t in tags if str(t).strip()]

if not new_tags:
return "Error: Tags cannot be empty."

task = self.tasks[task_number - 1]
if 'tags' not in task:
task['tags'] = []

added = []
for t in new_tags:
if t not in task['tags']:
task['tags'].append(t)
added.append(t)

if not task.get('category') and task['tags']:
task['category'] = task['tags'][0]

return f"Tags added to '{task['title']}': {', '.join(added)}"

def filter_tasks_by_category(self, category, show_completed=False):
"""
Filter tasks by category or tag.
"""
if not category or not str(category).strip():
return "Error: Category cannot be empty."
return self.view_tasks(show_completed=show_completed, category=str(category).strip())

def get_categories(self):
"""
Get all unique categories across tasks.
"""
categories = set()
for task in self.tasks:
if task.get('category'):
categories.add(task['category'])
for tag in task.get('tags', []):
categories.add(tag)
return sorted(list(categories))

def list_categories(self):
"""
Display all categories with active task counts.
"""
categories = self.get_categories()
if not categories:
return "No categories available."

lines = ["Available Categories:"]
for cat in categories:
count = sum(1 for t in self.tasks if (t.get('category') == cat or cat in t.get('tags', [])) and not t.get('completed', False))
lines.append(f"- [{cat}]: {count} active task(s)")
return "\n".join(lines)

def complete_task(self, task_number):
"""
Mark a task as completed.
Expand Down
67 changes: 54 additions & 13 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,26 @@
print("TaskPilot AI - Task Management System")
print("=" * 50)
print("\nCommands:")
print(" add <task> [--priority high|medium|low] - Add a new task")
print(" tasks - View all tasks")
print(" complete <number> - Complete a task")
print(" priority <number> <high|medium|low> - Change task priority")
print(" sort - Sort tasks by priority")
print(" summary - Show priority summary")
print(" tools - Show available tools")
print(" ai <your question> - Ask TaskPilot AI")
print(" exit - Exit the application")
print(" add <task> [--priority high|medium|low] [--category <name>] [--tags tag1,tag2] - Add a new task")
print(" tasks - View all tasks")
print(" complete <number> - Complete a task")
print(" priority <number> <high|medium|low> - Change task priority")
print(" category <number> <category> - Set task category")
print(" list <category> - Filter tasks by category")
print(" categories - Show all categories")
print(" sort - Sort tasks by priority")
print(" summary - Show priority summary")
print(" tools - Show available tools")
print(" ai <your question> - Ask TaskPilot AI")
print(" exit - Exit the application")
print("\n" + "-" * 50)

while True:
command = input("\nYou: ")

if not command.strip():
continue

if command.lower() == "exit":
print("Goodbye!")
break
Expand All @@ -28,32 +34,40 @@
parts = command[4:].split()
task_parts = []
priority = "medium"
category = None
tags = []

i = 0
while i < len(parts):
if parts[i] == "--priority" and i + 1 < len(parts):
priority = parts[i + 1].lower()
i += 2
elif parts[i] == "--category" and i + 1 < len(parts):
category = parts[i + 1].lower()
i += 2
elif parts[i] == "--tags" and i + 1 < len(parts):
tags = [t.strip() for t in parts[i + 1].split(",") if t.strip()]
i += 2
else:
task_parts.append(parts[i])
i += 1

task = " ".join(task_parts)
print(agent.add_task(task, priority))
print(agent.add_task(task, priority, category=category, tags=tags))

elif command.lower() == "tasks":
print("\nYour Tasks:")
print("-" * 50)
print(agent.view_tasks())

elif command.lower().startswith("complete "):
elif command.lower().startswith("complete ") or command.lower() == "complete":
try:
task_number = int(command.split()[1])
print(agent.complete_task(task_number))
except (IndexError, ValueError):
print("Error: Please enter a valid task number. Example: complete 1")

elif command.lower().startswith("priority "):
elif command.lower().startswith("priority ") or command.lower() == "priority":
parts = command.split()
if len(parts) >= 3:
try:
Expand All @@ -65,6 +79,33 @@
else:
print("Error: Please use: priority <task_number> <level>")

elif command.lower().startswith("category ") or command.lower() == "category":
parts = command.split()
if len(parts) >= 3:
try:
task_number = int(parts[1])
new_category = parts[2].lower()
print(agent.set_category(task_number, new_category))
except ValueError:
print("Error: Please use: category <task_number> <category>")
else:
print("Error: Please use: category <task_number> <category>")

elif command.lower() == "list":
print("Error: Please use: list <category>")

elif command.lower().startswith("list "):
category = command[5:].strip()
if not category:
print("Error: Please use: list <category>")
else:
print(f"\nTasks in [{category}]:")
print("-" * 50)
print(agent.filter_tasks_by_category(category))

elif command.lower() == "categories":
print("\n" + agent.list_categories())

elif command.lower() == "sort":
print(agent.sort_tasks())
print("\nSorted Tasks:")
Expand All @@ -88,4 +129,4 @@
print(agent.ask_ai(message))

else:
print("Unknown command. Try: add, tasks, complete, priority, sort, summary, tools, ai, or exit")
print("Unknown command. Try: add, tasks, complete, priority, category, list, categories, sort, summary, tools, ai, or exit")
9 changes: 6 additions & 3 deletions src/tools.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
def get_available_tools():
return {
"add_task": "Add a new task with optional priority (high/medium/low)",
"view_tasks": "View all tasks with priority levels",
"add_task": "Add a new task with optional priority (high/medium/low) and category/tags",
"view_tasks": "View all tasks with priority levels and categories",
"complete_task": "Mark a task as completed",
"set_priority": "Change priority of an existing task",
"sort_tasks": "Sort tasks by priority (High to Low)",
"get_priority_summary": "Get a summary of task priorities"
"get_priority_summary": "Get a summary of task priorities",
"set_category": "Set or update category for an existing task",
"filter_tasks_by_category": "Filter tasks by category or tag",
"list_categories": "Show all categories with active task counts"
}
3 changes: 3 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import os

os.environ.setdefault("OPENAI_API_KEY", "mock-test-key")
Loading