Building a Command-Line To-Do App in Python: Beginner-Friendly and Practical

Why This Project?

One of the best ways to learn coding is by building things that solve actual problems. In this tutorial, I’ll show you how I created a simple command-line to-do list app using Python.

This project helped me strengthen my understanding of:

  • File I/O (input and output)
  • Python data structures like lists and dictionaries
  • Argument parsing via the command-line
  • Realistic Create, Read, Update, Delete (CRUD) functionality

It’s beginner-friendly and entirely practical. By the end, you’ll have your own app and understand how each part works—and you’ll be ready to build on it, or even turn it into a GUI (graphical user interface) later.

You can find the full source code here:
👉 View it on GitHub


📂 Project Files You’ll Need

Create a folder called todo_app and inside it, add:

  • todo.py — The Python file containing your logic
  • tasks.json — The data file to store your tasks

Inside tasks.json, paste this to start:

jsonCopyEdit []

🧱 Step 1: Set Up File Reading and Saving

In todo.py:

pythonCopyEditimport json
import os

TASKS_FILE = "tasks.json"

def load_tasks():
    if not os.path.exists(TASKS_FILE):
        return []
    with open(TASKS_FILE, 'r') as f:
        return json.load(f)

def save_tasks(tasks):
    with open(TASKS_FILE, 'w') as f:
        json.dump(tasks, f, indent=4)

This sets up basic load and save functionality for your task list using JSON.


⚙️ Step 2: Add Command-Line Interface (CLI) Support

Now add this below the previous code:

pythonCopyEdit import argparse

parser = argparse.ArgumentParser(description="Simple CLI To-Do App")
parser.add_argument("command", choices=["add", "list", "remove", "done"], help="Command to run")
parser.add_argument("--task", help="Task description")
parser.add_argument("--index", type=int, help="Task index to remove or mark done")

args = parser.parse_args()

This allows the app to accept commands from your terminal like add, list, done, and remove.


🔁 Step 3: Handle Commands

Below your parser setup, add:

pythonCopyEdit tasks = load_tasks()

if args.command == "add" and args.task:
tasks.append({"task": args.task, "done": False})
save_tasks(tasks)
print(f"Added task: {args.task}")

elif args.command == "list":
if not tasks:
print("No tasks found.")
for i, task in enumerate(tasks):
status = "✅" if task["done"] else "❌"
print(f"{i}. {task['task']} [{status}]")

elif args.command == "remove" and args.index is not None:
if 0 <= args.index < len(tasks):
removed = tasks.pop(args.index)
save_tasks(tasks)
print(f"Removed task: {removed['task']}")
else:
print("Invalid index.")

elif args.command == "done" and args.index is not None:
if 0 <= args.index < len(tasks):
tasks[args.index]["done"] = True
save_tasks(tasks)
print(f"Marked task as done: {tasks[args.index]['task']}")
else:
print("Invalid index.")
else:
print("Invalid usage. Use --help for options.")

💻 Step 4: Running the App

Make sure you’re inside the project folder in your terminal:

bashCopyEdit cd path/to/todo_app

Then run commands like:

bashCopyEdit python3 todo.py add --task "Learn Python"
python3 todo.py list
python3 todo.py done --index 0
python3 todo.py remove --index 0

All tasks are saved in tasks.json.


📌 Bonus: Push It to GitHub

After creating your repo on GitHub:

bashCopyEdit git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/TitusGitari/todo-cli-python.git
git push -u origin main

Now your work is live and sharable.


🔚 Final Thoughts

This is more than a toy app. It reflects how real-world command-line tools work—think server management scripts, DevOps automations, and IT workflows.

Project-based learning like this is how I’m building both competence and confidence in Python and software development.

Follow me on GitHub or stick around the Tutorials section at titusgitari.com to follow along as I build more projects.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *