diff --git a/tests/test_help_docs.py b/tests/test_help_docs.py index 879d198..c9a8637 100644 --- a/tests/test_help_docs.py +++ b/tests/test_help_docs.py @@ -12,7 +12,7 @@ def test_run_help_prints_docstring_for_known_command(monkeypatch, capsys): "path": "trushell/commands/settings.py", "function": "run_settings", } - } + }, ) # Provide a minimal module object with the expected function and diff --git a/trushell/cli.py b/trushell/cli.py index 0eb1674..2f8e37c 100644 --- a/trushell/cli.py +++ b/trushell/cli.py @@ -29,9 +29,6 @@ def app_with_lower() -> None: if len(sys.argv) > 1: argv_copy = sys.argv.copy() if argv_copy[1].lower() not in {"--help", "-h", "version"}: - # Normalize the command name to lowercase for case-insensitive - # invocation, but preserve the case of subsequent arguments - # (e.g., filenames) which may be case-sensitive. first = argv_copy[1].lower() rest = argv_copy[2:] raw = " ".join([first] + rest) @@ -195,7 +192,7 @@ def _handle_todo_command(command: str) -> bool: if add_match: from trushell.commands.tasks import add_task - add_task(f"{add_match.group(1)} {add_match.group(2)}") + add_task(f'"{add_match.group(1)}" "{add_match.group(2)}"') return True update_match = re.match(r'updatetask\s+(\d+)\s+"([^"]+)"\s+"([^"]+)"', command) diff --git a/trushell/commands/tasks.py b/trushell/commands/tasks.py index 7a1688c..889b42e 100644 --- a/trushell/commands/tasks.py +++ b/trushell/commands/tasks.py @@ -2,67 +2,92 @@ from typing import Callable +import typer + from trushell.core.database import complete_todo, delete_todo, get_all_todos, insert_todo, update_todo from trushell.core.models import Todo def add_task(args: str) -> None: - """Add a new task to the todo list. The full remainder is treated as the task.""" + """Add a new task to the todo list. + + Supports two formats: + addtask "task text" "category" — explicit quotes + addtask task text here — entire text is the task + """ if not args.strip(): - print("Usage: task add ") + typer.secho('⚠️ Missing arguments. Syntax: addtask "task-name" "category"', fg=typer.colors.YELLOW) return - task_text = args.strip() - todo = Todo(task=task_text, category="General") + # Only parse as "task" "category" when the user explicitly used quotes. + # Otherwise treat the entire args as the task text. + import shlex + stripped = args.strip() + if stripped.startswith('"') or stripped.startswith("'"): + try: + parts = shlex.split(stripped) + except ValueError: + parts = None + if parts and len(parts) >= 2: + task_text = parts[0] + category = parts[1] + else: + task_text = stripped.strip('"').strip("'") + category = "General" + else: + task_text = stripped + category = "General" + + todo = Todo(task=task_text, category=category) insert_todo(todo) - print("Task added.") + typer.secho("✅ Task added.", fg=typer.colors.GREEN) def show_tasks(_: str) -> None: """Display the current todo list.""" tasks = get_all_todos() if not tasks: - print("No tasks found.") + typer.echo("No tasks found.") return for index, task in enumerate(tasks, start=1): status = "✅" if task.status == 2 else "❌" - print(f"{index}. {task.task} [{task.category}] {status}") + typer.echo(f"{index}. {task.task} [{task.category}] {status}") def complete_task(args: str) -> None: """Mark a todo item as complete.""" if not args.strip() or not args.strip().isdigit(): - print("Usage: task done ") + typer.secho("⚠️ Usage: task done ", fg=typer.colors.YELLOW) return index = int(args.strip()) - 1 try: complete_todo(index) - print("Task completed.") + typer.secho("✅ Task completed.", fg=typer.colors.GREEN) except Exception as error: - print(f"Task error: {error}") + typer.secho(f"❌ Task error: {error}", fg=typer.colors.RED) def remove_task(args: str) -> None: """Remove a task by its numeric position.""" if not args.strip() or not args.strip().isdigit(): - print("Usage: task remove ") + typer.secho("⚠️ Usage: task remove ", fg=typer.colors.YELLOW) return index = int(args.strip()) - 1 try: delete_todo(index) - print("Task removed.") + typer.secho("✅ Task removed.", fg=typer.colors.GREEN) except Exception as error: - print(f"Task error: {error}") + typer.secho(f"❌ Task error: {error}", fg=typer.colors.RED) def update_task(args: str) -> None: """Update an existing task's text and/or category.""" parts = args.split(maxsplit=2) if len(parts) < 2 or not parts[0].isdigit(): - print('Usage: task update "" [""]') + typer.secho('⚠️ Usage: task update "" [""]', fg=typer.colors.YELLOW) return index = int(parts[0]) - 1 @@ -71,9 +96,9 @@ def update_task(args: str) -> None: try: update_todo(index, task_text, category) - print("Task updated.") + typer.secho("✅ Task updated.", fg=typer.colors.GREEN) except Exception as error: - print(f"Task error: {error}") + typer.secho(f"❌ Task error: {error}", fg=typer.colors.RED) def list_tasks(_: str) -> None: @@ -94,7 +119,7 @@ def run_task_command(args: str) -> None: } if not args.strip(): - print("Usage: task [options]") + typer.secho("⚠️ Usage: task [options]", fg=typer.colors.YELLOW) return parts = args.split(maxsplit=1) @@ -106,6 +131,6 @@ def run_task_command(args: str) -> None: try: handler(subargs) except Exception as error: - print(f"Task error: {error}") + typer.secho(f"❌ Task error: {error}", fg=typer.colors.RED) else: - print(f"Unknown task subcommand: {subcmd}") + typer.secho(f"❓ Unknown task subcommand: {subcmd}", fg=typer.colors.YELLOW)