Skip to main content

Command Palette

Search for a command to run...

Create a Simple Python CLI for Managing Tasks

Step-by-step guide to creating a simple CLI tool in Python

Published
18 min read
Create a Simple Python CLI for Managing Tasks
A

I am a Frontend developer transitioning from IT Administration. Passionate about creating seamless user experiences and continuously improving my skills, I aim to become one of the best in frontend development. Let's connect and grow together!

This is a simple Python application for managing tasks. The project introduces basic concepts that are essential building blocks for any application. So, open your favorite code editor, and let's get started.

Main Menu

Let's start simple by defining the main function, which will be the starting point of the application. Create a dictionary with menu options, then loop through the items to display the values along with their keys.:

def main():
    """Main function"""
    menu = {
        '1': 'Add Task',
        '2': 'List Tasks',
        '3': 'Complete Task',
        '4': 'Delete Task',
        '0': 'Exit'
    }

    print('\n=== Task Manager ===')
    for key, value in menu.items():
        print(f"{key}. {value}")
    print('----------------------')

To test the output, let’s make sure the current script is run directly and not as an imported module:

...

if __name__ == "__main__":
    main()

In your terminal, run the script:

$ python3 app.py

=== Task Manager ===
1. Add Task
2. List Tasks
3. Complete Task
4. Delete Task
0. Exit
----------------------

The options are displayed in a clear format. Great! Now, let's outline the different parts we need to work on to make this application functional:

  • Ask the user to choose an option and record their input.

  • Define functions to handle each user option.

  • Gracefully handle unexpected user entries and implement error checking.

Get User Input

Prompt the user to select an option from among those listed:

choice = input("\nSelect an option: ").strip()

Please note that any input entered will be stored as a string. So, when the user enters 1, for example, it will be captured as "1". The strip function removes any whitespace that the user may have entered accidentally or intentionally..

Let’s now check what choice the user picks. We will use an if…elif…else block to make sure the choice picked matches the menu item:

if choice == '1':
    print("=== Add Task ===")
elif choice == '2':
    print("=== List Tasks ===")
elif choice == '3':
    print('=== Complete Task ===\n')
elif choice == '4':
    print('=== Delete Task ===')
elif choice == '0':
    print('Exiting Task Manager...')
    print('Done')
else:
    print('Invalid option.\n')

To improve the interactivity of this application, we wrap this logic in a while loop which will keep the program running until the user exits:

def main():
    """Main function"""
    menu = {
        '1': 'Add Task',
        '2': 'List Tasks',
        '3': 'Complete Task',
        '4': 'Delete Task',
        '0': 'Exit'
    }

    while True:
        print('\n=== Task Manager ===')
        for key, value in menu.items():
            print(f"{key}. {value}")
        print('----------------------')

        choice = input("\nSelect an option: ").strip()

        if choice == '1':
            print("=== Add Task ===")
        elif choice == '2':
            print("=== List Tasks ===")
        elif choice == '3':
            print('=== Complete Task ===\n')
        elif choice == '4':
            print('=== Delete Task ===')
        elif choice == '0':
            message = 'Exiting Task Manager...'
            print('Done!')
            break
        else:
            print('Invalid option.\n')


if __name__ == "__main__":
    main()

Until the user exits (by selecting "0"), the program will continue to prompt for input. This break mechanism offers an easy way to exit the program. If the user chooses an option not listed, a message will appear to inform them that the choice is invalid.

Define Methods for Handling Tasks

We will utilize classes to manage the tasks created by users and define various methods for manipulating these tasks. Using classes gives more structure, encapsulation, and flexibility as the app grows. We will create two classes: TaskManager to store tasks by ID and Task to define the structure of a task.

Task Class

from datetime import datetime

# Define a Task class
class Task:
    def __init__(self, task_id, title, completed=False, date_added=None, date_completed=None):
        self.task_id = task_id
        self.title = title
        self.completed = completed
        self.date_added = date_added or datetime.now()
        self.date_completed = date_completed

The __init__ method is a special built-in Python method, also called a constructor, that is automatically called when an object is created from a class (in this case, Task). It sets up the initial state of the object. When a new task is created, task_id and title must be provided. The default attributes completed, date_added, and date_completed are automatically set when creating a new task. We need to import the datetime package to get the creation date.

Next, let’s define a method to display the representation of the task. We will use the built-in repr() function.

# Define a Task class
class Task:
    def __init__(self, task_id, title, completed=False, date_added=None, date_completed=None):
        self.task_id = task_id
        self.title = title
        self.completed = completed
        self.date_added = date_added or datetime.now()
        self.date_completed = date_completed

    def __repr__(self):
        status = "[x]" if self.completed else "[ ]"
        date_info = f"Added: {self.date_added.strftime('%Y-%m-%d')}"
        if self.completed:
            date_info += f" | Completed: {self.date_completed.strftime('%Y-%m-%d')}"
        return f"{status} {self.task_id}: {self.title} [{date_info}]"

In the __repr__() function, we determine the status of the task, showing [x] if it is completed, otherwise [ ]. Next, we create a date_info variable to show the date the task was created in the format YYYY-MM-DD. If the task has been marked as completed, we will also include the completed date in the same format.

TaskManager Class

# Define a TaskManager to store tasks by ID
class TaskManager:
    def __init__(self):
        self.tasks = {}
        self.task_id = 1  # to auto-increment

We start by creating an empty dictionary to store the tasks and a task_id attribute to identify each task uniquely.

Add Task

Let’s include the functionality to add tasks in the TaskManager class:

class TaskManager:
    def __init__(self):
        self.tasks = {}
        self.task_id = 1

    def add_task(self, title):
        """Add new task"""
        new_task = Task(self.task_id, title.capitalize())
        self.tasks[self.task_id] = new_task
        self.task_id += 1
        return {'success': f"Task '{title.capitalize()}' added."}

The add_task method accepts a task title and creates a new task object from the Task class. It then adds this new task object to the tasks dictionary, using the task_id identifier as the key. After that, it increments the identifier for the next created task and returns a dictionary that will be used to check the status of the operation.

Back in the main function, when the user selects the option to create a new task, invoke this add_task method:

def main():
    """Main function"""
    tm = TaskManager()  # Create an instance of the task manager class
    menu = {
        '1': 'Add Task',
        '2': 'List Tasks',
        '3': 'Complete Task',
        '4': 'Delete Task',
        '0': 'Exit'
    }

    while True:
        print('\n=== Task Manager ===')
        for key, value in menu.items():
            print(f"{key}. {value}")
        print('----------------------')

        choice = input("\nSelect an option: ").strip()

        if choice == '1':
            print("=== Add Task ===")
            title = input("Enter task title: ").strip()
            status = tm.add_task(title)
            print(status['success'])

The main function starts by creating an instance of the TaskManager class, giving us access to its methods. When the user chooses to add a task, they are asked to enter the task title. This trimmed title is then passed to the add_task method. The resulting status dictionary is stored in the status variable and displayed on the console.

# Sample output

=== Task Manager ===
1. Add Task
2. List Tasks
3. Complete Task
4. Delete Task
0. Exit
----------------------

Select an option: 1

=== Add Task ===
Enter task title: study python
Task 'Study python' added.

List Tasks

Include the list_tasks method in TaskManager:

class TaskManager:
    def __init__(self):...

    def add_task(self, title):...

    def list_tasks(self):
        """List tasks"""
        if len(self.tasks):
            print("Status | ID | Title                          | Date Added | Date Completed |")
            print("---------------------------------------------------------------------------")
            for task in self.tasks.values():
                status = "[x]" if task.completed else "[ ]"
                add_date = task.date_added.strftime('%Y-%m-%d')
                complete_date = task.date_completed.strftime('%Y-%m-%d') if task.date_completed else 'Pending'
                print(f"{status:^6} | {task.task_id:<2} | {task.title:<30} | {add_date} | {complete_date:<14} |")
        else:
            print("No saved tasks.")

Let’s break down what happens here:

  • The list_tasks method checks if there are any tasks in the tasks dictionary. If there are no tasks, the user is informed.

  • The heading is formatted and printed with the right spacing and dividers.

  • We go through each task in the tasks dictionary to process them.

  • Inside the loop, the formatting for status (completed or not), add_dateand complete_date is set.

  • The task details are printed with the right spacing.

# Sample output

=== List Tasks ===
Status | ID | Title                          | Date Added | Date Completed |
----------------------------------------------------------------------------
 [ ]   | 1  | Study python                   | 2025-05-14 | Pending        |
 [ ]   | 2  | Finish up portfolio project    | 2025-05-14 | Pending        |
 [x]   | 3  | Exercise                       | 2025-05-14 | 2025-05-14     |

Two tasks are still pending. One is completed.

Complete Task

To do this, we need to update the completed and date_completed attributes for each task. We will add a method called complete_task in the Task class to access and modify these attributes:

class Task:
    def __init__(self, task_id, title, completed=False, date_added=None, date_completed=None):
        self.task_id = task_id
        self.title = title
        self.completed = completed
        self.date_added = date_added or datetime.now()
        self.date_completed = date_completed

    def complete_task(self):
        """Mark task as completed and set completion time and date"""
        self.completed = True
        self.date_completed = datetime.now()

    def __repr__(self):
        status = "[x]" if self.completed else "[ ]"
        date_info = f"Added: {self.date_added.strftime('%Y-%m-%d')}"
        if self.completed:
            date_info += f" | Completed: {self.date_completed.strftime('%Y-%m-%d')}"
        return f"{status} {self.task_id}: {self.title} [{date_info}]"

This method sets the completed state to True and updates date_completed to the current date and time.

In the TaskManager class, add a method named mark_completed that will accept the task ID and call the complete_task method for that specific task:

class TaskManager:
    def __init__(self):...

    def add_task(self, title):...

    def list_tasks(self):...

    def mark_completed(self, task_id):
        """Mark task as completed"""
        self.tasks[task_id].complete_task()
        return {'success': f"Task {task_id} marked as completed."}

After marking the task as completed, the method returns a dictionary indicating the status. Back in the main function, we manage the user's choice to complete a task:

def main():
    """Main function"""
    tm = TaskManager()
    menu = {...}

    while True:
        print('\n=== Task Manager ===')
        for key, value in menu.items():
            print(f"{key}. {value}")
        print('----------------------')

        choice = input("\nSelect an option: ").strip()

        if choice == '1':...
        elif choice == '2':...
        elif choice == '3':
            print('=== Complete Task ===\n')
            print('Available tasks')
            print('---------------\n')
            tm.list_tasks()

            task_id = input("\nEnter task ID: ").strip()
            status = tm.mark_completed(int(task_id))
            print(status['success'])

The user sees a list of available tasks and is asked to enter the ID of the task they want to complete. The ID is converted to an integer and sent to the mark_completed method. The status dictionary returned is saved in the status variable and shown on the console.

# Sample output

=== Complete Task ===

Available tasks
---------------

Status | ID | Title                          | Date Added | Date Completed |
----------------------------------------------------------------------------
 [ ]   | 1  | Exercise                       | 2025-05-14 | Pending        |

Enter task ID: 1
Task 1 marked as completed.

# List tasks to confirm task completion

=== List Tasks ===
Status | ID | Title                          | Date Added | Date Completed |
----------------------------------------------------------------------------
 [x]   | 1  | Exercise                       | 2025-05-14 | 2025-05-14     |

Delete Task

Finally, we manage the user's choice to delete a task. Add the delete_task method to the TaskManager class. This method will delete the task linked to the task_id and return a dictionary indicating whether the deletion was successful:

class TaskManager:
    def __init__(self):...

    def add_task(self, title):...

    def list_tasks(self):...

    def mark_completed(self, task_id):...

    def delete_task(self, task_id):
        del self.tasks[task_id]
        return {'success': f"Task {task_id} deleted."}

Let’s invoke this method in the main function:

def main():
    """Main function"""
    tm = TaskManager()
    menu = {...}

    while True:
        print('\n=== Task Manager ===')
        for key, value in menu.items():
            print(f"{key}. {value}")
        print('----------------------')

        choice = input("\nSelect an option: ").strip()

        if choice == '1':...
        elif choice == '2':...
        elif choice == '3':...
        elif choice == '4':
            print('=== Delete Task ===')
            print('Available tasks')
            print('---------------\n')
            tm.list_tasks()

            task_id = input("\nEnter task ID: ").strip()
            status = tm.delete_task(int(task_id))
            print(status['success'])
# Sample output

...

=== Add Task ===
Enter task title: exercise
Task 'Exercise' added.

...

=== Delete Task ===
Available tasks
---------------

Status | ID | Title                          | Date Added | Date Completed |
----------------------------------------------------------------------------
 [ ]   | 1  | Exercise                       | 2025-05-14 | Pending        |

Enter task ID: 1
Task 1 deleted.

=== Task Manager ===
1. Add Task
2. List Tasks
3. Complete Task
4. Delete Task
0. Exit
----------------------

Select an option: 2

=== List Tasks ===
No saved tasks.

Additional Features and Error Handling

The basic functionality of this program is complete. However, we can make it more robust by handling errors and enhancing some of its features. A few of the features we’ll include:

  1. Data validation

  2. Check for duplicate entries to ensure no two tasks have the same title.

  3. Confirm the existence of the task to be completed or deleted.

  4. Before completing a task, make sure it hasn't already been completed.

  5. Clear the screen when navigating different menu options to create more space on the console.

Feature 1 - Data validation

Before processing the data, let's ensure it is in the correct format. For each prompt, we will also include the possibility of an early exit.

  • Add task:

      if choice == '1':
          print("=== Add Task ===")
          while True:
              title = input("Enter task title ('q' to exit): ").strip()
    
              if not title:
                  print("Please enter a task title or 'q' to exit.")
                  continue
    
              if title.lower() == 'q':
                  print("Cancelled adding task.")
                  break
    
              if len(title) < 3:
                  print("Title length should be at least 3 characters.")
                  continue
    
              status = tm.add_task(title)
    
              if 'error' in status:
                  print(f"Error: {status['error']}")
                  continue
    
              print(status['success'])
              break
    

    Wrap the logic in a while loop so the user is continuously prompted for correct input. If the user presses Enter without typing a title, or if the title is shorter than 3 characters, inform them of the issue and allow them to try again. The continue keyword returns the program to the start of the while loop. If the user types lowercase q, the program exits the submenu.

    Notice the order of the checks. If the len(title) < 3: check came before title.lower() == 'q':, it would be impossible to exit the submenu because q is always less than 3 characters and would be ignored.

    If an error status is returned from the task manager, the error message is displayed in the console, and the user is asked to enter a task title again. We will address duplicate entry checking in Feature 2.

      # Sample output
    
      === Add Task ===
      Enter task title ('q' to exit):               # No title supplied
      Please enter a task title or 'q' to exit.
      Enter task title ('q' to exit): ae            # Title less that 3 characters
      Title length should be at least 3 characters.
      Enter task title ('q' to exit): q             # Quit condition
      Cancelled adding task.
    
  • Complete task

      elif choice == '3':
          print('=== Complete Task ===\n')
          print('Available tasks')
          print('---------------\n')
          tm.list_tasks()
    
          while True:
              task_id = input("\nEnter task ID ('q' to exit): ").strip()
              if not task_id:
                  print("Please enter a task ID or 'q' to exit.")
                  continue
              if task_id.lower() == 'q':
                  print("Cancelled completing task.")
                  break
              if not task_id.isdigit():
                  print('Task ID should be a number.')
                  continue
    
              status = tm.mark_completed(int(task_id))
              if 'error' in status:
                  print(f"Error: {status['error']}")
                  continue
    
              print(status['success'])
              break
    

    Remember that data is always captured as a string by the input() function. The third check makes sure the data can be converted into an integer. For example, if the user enters ‘x’ as the task ID, status = tm.mark_completed(int(task_id)) would cause an error because ‘x’ cannot be turned into an integer, but ‘4’ can. The rest of the process is similar to adding a task.

  • Delete task

      elif choice == '4':
          print('=== Delete Task ===')
          print('Available tasks')
          print('---------------\n')
          tm.list_tasks()
    
          while True:
              task_id = input("\nEnter task ID ('q' to exit): ").strip()
              if not task_id:
                  print("Please enter a task ID or 'q' to exit.")
                  continue
              if task_id.lower() == 'q':
                  print("Cancelled deleting task.")
                  break
              if not task_id.isdigit():
                  print('Task ID should be a number.')
                  continue
    
              status = tm.delete_task(int(task_id))
              if 'error' in status:
                  print(f"Error: {status['error']}")
                  continue
              print(status['success'])
              break
    

Feature 2 - Check duplicate entries

When adding a new task, we want to ensure there isn't already one with the same title.

class TaskManager:
    def __init__(self):...

    def add_task(self, title):
        # Loop through the saved tasks and compare each
        # task title with the one supplied by the user
        if any(title.lower() == task.title.lower() for task in self.tasks.values()):
            # Return an error status if duplicate found
            return {'error': "A task with this title already exists."}
        new_task = Task(self.task_id, title.capitalize())
        self.tasks[self.task_id] = new_task
        self.task_id += 1
        return {'success': f"Task '{title.capitalize()}' added."}
# Sample output

...

=== List Tasks ===
Status | ID | Title                          | Date Added | Date Completed |
----------------------------------------------------------------------------
 [ ]   | 1  | Exercise                       | 2025-05-14 | Pending        |

...

=== Add Task ===
Enter task title ('q' to exit): EXERCISE
Error: A task with this title already exists.
Enter task title ('q' to exit):

Feature 3 - Confirm task exists before completing or deleting

Notify user user if the task does not exist:

def mark_completed(self, task_id):
    if task_id in self.tasks:  # Check if task exists before completing
        self.tasks[task_id].complete_task()
        return {'success': f"Task {task_id} marked as completed."}
    return {'error': f"Task with id {task_id} not found."}
def delete_task(self, task_id):
    if task_id in self.tasks:  # Confirm task exists before attempting deletion
        del self.tasks[task_id]
        return {'success': f"Task {task_id} deleted."}
    return {'error': f"Task with id {task_id} not found."}
# Sample output

=== Delete Task ===
Available tasks
---------------

Status | ID | Title                          | Date Added | Date Completed |
----------------------------------------------------------------------------
 [ ]   | 1  | Pracite piano                  | 2025-05-15 | Pending        |

Enter task ID ('q' to exit): 2
Error: Task with id 2 not found.

Feature 4 - Confirm task is not completed before completing

def mark_completed(self, task_id):
    if task_id in self.tasks:  # Confirm task first exists
        if self.tasks[task_id].completed:  # Confirm if already completed
            return {'error': f"Task with ID {task_id} already marked as completed"}
        self.tasks[task_id].complete_task()
        return {'success': f"Task {task_id} marked as completed."}
    return {'error': f"Task with id {task_id} not found."}
# Sample output

=== Complete Task ===

Available tasks
---------------

Status | ID | Title                          | Date Added | Date Completed |
----------------------------------------------------------------------------
 [x]   | 1  | Pracite piano                  | 2025-05-15 | 2025-05-16     |

Enter task ID ('q' to exit): 1
Error: Task with ID 1 already marked as completed

Feature 5 - Clear screen

At the top of the file import os module. We need this module to clear the console.

from datetime import datetime
import os  # <= This one

class Task:...

class TaskManager:...

def clear_screen():
    """Clear the console"""
    os.system('cls' if os.name == 'nt' else 'clear')

The clear_screen function checks if the operating system is Windows-based ('nt') to decide whether to use 'cls' (for Windows) or 'clear' for other systems. This ensures the program can run on any platform.

We will use this function after the user selects a menu option:

def main():
    tm = TaskManager()
    menu = {...}

    while True:
        ...
        choice = input("\nSelect an option: ").strip()
        clear_screen()  # Clears screen after menu choice

Future Improvement Considerations

The program works well for managing user tasks. However, there are many opportunities for improvement. Here are a few ideas you can consider:

  • Split into modules - divide the code into separate files, such as task.py for the Task class and task_manager.py for the TaskManager. This approach aids in scalability and testing.

  • Confirm destructive actions - before deleting, ask the user to confirm if they are sure.

  • Sort and filter tasks - let users view only complete/incomplete tasks, or sort by date added to title

  • Add persistence - use JSON to save tasks between sessions

  • Add features - search tasks, include task categories or priorities, let users set due dates and reminders, highlight overdue tasks

  • Testing - start writing unit tests with unittest or pytest. This builds reliability as your code grows

You can also stretch your goals by building a:

  • GUI version using tkinter or PyQt

  • Web app with Flask or Django

  • TUI version with rich or textual

Here is the full program code:

from datetime import datetime
import os

# Define a Task class
class Task:
    def __init__(self, task_id, title, completed=False, date_added=None, date_completed=None):
        self.task_id = task_id
        self.title = title
        self.completed = completed
        self.date_added = date_added or datetime.now()
        self.date_completed = date_completed

    def complete_task(self):
        """Mark task as completed and set completion time and date"""
        self.completed = True
        self.date_completed = datetime.now()

    def __repr__(self):
        status = "[x]" if self.completed else "[ ]"
        date_info = f"Added: {self.date_added.strftime('%Y-%m-%d')}"
        if self.completed:
            date_info += f" | Completed: {self.date_completed.strftime('%Y-%m-%d')}"
        return f"{status} {self.task_id}: {self.title} [{date_info}]"

# Define a TaskManager to store tasks by ID
class TaskManager:
    def __init__(self):
        self.tasks = {}
        self.task_id = 1  # to auto-increment

    def add_task(self, title):
        if any(title.lower() == task.title.lower() for task in self.tasks.values()):
            return {'error': "A task with this title already exists."}
        new_task = Task(self.task_id, title.capitalize())
        self.tasks[self.task_id] = new_task
        self.task_id += 1
        return {'success': f"Task '{title.capitalize()}' added."}

    def list_tasks(self):
        if len(self.tasks):
            print("Status | ID | Title                          | Date Added | Date Completed |")
            print("----------------------------------------------------------------------------")
            for task in self.tasks.values():
                status = "[x]" if task.completed else "[ ]"
                add_date = task.date_added.strftime('%Y-%m-%d')
                complete_date = task.date_completed.strftime('%Y-%m-%d') if task.date_completed else 'Pending'
                print(f"{status:^6} | {task.task_id:<2} | {task.title:<30} | {add_date} | {complete_date:<14} |")
        else:
            print("No saved tasks.")

    def mark_completed(self, task_id):
        if task_id in self.tasks:
            if self.tasks[task_id].completed:
                return {'error': f"Task with ID {task_id} already marked as completed"}
            self.tasks[task_id].complete_task()
            return {'success': f"Task {task_id} marked as completed."}
        return {'error': f"Task with id {task_id} not found."}

    def delete_task(self, task_id):
        if task_id in self.tasks:
            del self.tasks[task_id]
            return {'success': f"Task {task_id} deleted."}
        return {'error': f"Task with id {task_id} not found."}

def clear_screen():
    os.system('cls' if os.name == 'nt' else 'clear')


def main():
    tm = TaskManager()
    menu = {
        '1': 'Add Task',
        '2': 'List Tasks',
        '3': 'Complete Task',
        '4': 'Delete Task',
        '0': 'Exit'
    }

    while True:
        print('\n=== Task Manager ===')
        for key, value in menu.items():
            print(f"{key}. {value}")
        print('----------------------')

        choice = input("\nSelect an option: ").strip()
        clear_screen()

        if choice == '1':
            print("=== Add Task ===")

            while True:
                title = input("Enter task title ('q' to exit): ").strip()

                if not title:
                    print("Please enter a task title or 'q' to exit.")
                    continue

                if title.lower() == 'q':
                    print("Cancelled adding task.")
                    break

                if len(title) < 3:
                    print("Title length should be at least 3 characters.")
                    continue

                status = tm.add_task(title)
                if 'error' in status:
                    print(f"Error: {status['error']}")
                    continue

                print(status['success'])
                break
        elif choice == '2':
            print("=== List Tasks ===")
            tm.list_tasks()
        elif choice == '3':
            print('=== Complete Task ===\n')
            print('Available tasks')
            print('---------------\n')
            tm.list_tasks()

            while True:
                task_id = input("\nEnter task ID ('q' to exit): ").strip()

                if not task_id:
                    print("Please enter a task ID or 'q' to exit.")
                    continue

                if task_id.lower() == 'q':
                    print("Cancelled completing task.")
                    break

                if not task_id.isdigit():
                    print('Task ID should be a number.')
                    continue

                status = tm.mark_completed(int(task_id))
                if 'error' in status:
                    print(f"Error: {status['error']}")
                    continue

                print(status['success'])
                break
        elif choice == '4':
            print('=== Delete Task ===')
            print('Available tasks')
            print('---------------\n')
            tm.list_tasks()

            while True:
                task_id = input("\nEnter task ID ('q' to exit): ").strip()

                if not task_id:
                    print("Please enter a task ID or 'q' to exit.")
                    continue

                if task_id.lower() == 'q':
                    print("Cancelled deleting task.")
                    break

                if not task_id.isdigit():
                    print('Task ID should be a number.')
                    continue

                status = tm.delete_task(int(task_id))
                if 'error' in status:
                    print(f"Error: {status['error']}")
                    continue

                print(status['success'])
                break
        elif choice == '0':
            print('Exiting Task Manager...')
            print('\nDone!')
            break
        else:
            print('Invalid option.\n')


if __name__ == "__main__":
    main()