Build a Python Terminal Task Tracker

Build a terminal task tracker with Python, Bash, Git, and GitHub.

Introduction

30 Second Summary

To-do lists often end up scattered across notes or forgotten in open tabs. A quick tool in your terminal keeps the next task within reach.

In this project, you will build a terminal task tracker with Python. A workflow built on Bash, Git, plus GitHub will make it easy to run or share.

What You'll Build

When you run the finished tracker, your terminal shows a numbered list whose status changes as you complete each task.

By the end of this project, you'll have:

  • An interactive task tracker that accepts tasks throughout one session. You can view numbered entries. You can mark each entry complete.
  • A Bash launcher that starts the tracker with one command.
  • A shareable GitHub repository that displays your source code. It also shows your run instructions plus commit history.
  • Secret Mission: Add a validated Delete option that safely removes a selected task. The remaining tasks will renumber automatically.

Are there any prerequisites?

You'll need a Linux computer plus a GitHub account. The workspace step checks your local tools before you start coding.

Before We Start

First, lock in who your terminal task tracker is for and why they need a simple way to manage tasks during one terminal session.

Prepare Your Linux Workspace

Your task tracker starts as a terminal program on Linux. A quick check of Python 3, Git, and GNU Bash prevents a missing command from interrupting your first run.

You also need a named workspace in Visual Studio Code. Confirming access to GitHub prepares you to publish the finished tracker later.

In this step, get ready to:
  • Verify the required terminal tools.
  • Prepare Visual Studio Code for the project.
  • Create the empty task tracker files.
Verify your command-line tools

A version check asks each command to identify itself. Successful output proves that your terminal can reach the tool.

  • Open your Linux application launcher.
  • Search for Terminal.
  • Select the terminal application from the results.
  • Print the installed versions by running these commands:
python3 --version
git --version
bash --version

What do these commands check?

  • The python3 --version command prints the installed Python version.
  • The git --version command prints the installed Git version.
  • The bash --version command prints information about the installed Bash version.

✔️ All three commands show versions

Your terminal shows version information for all three commands. The command-line tools are ready for the project.

ⓧ python3 is unavailable

Some Linux environments expose Python 3 through the shorter python command.

  • Check the shorter Python command by running:
python --version

What does this command check?

The python --version command prints the version connected to the shorter executable. Output beginning with Python 3 confirms that it can run this project.

  • Use python whenever a later instruction uses python3.
  • Install Python 3 through your Linux distribution's documented package manager if this check also fails.
  • Repeat the Python version check after installation.

Still unable to find Python?

Linux installation steps vary by distribution. Help me identify the correct Python installation path.

ⓧ Git or Bash is unavailable

A missing command means that its tool needs to be installed through your Linux distribution. The correct package manager depends on the distribution you use.

  • Check your Linux distribution's official package manager guidance.
  • Install Git if the Git version check failed.
  • Install GNU Bash if the Bash version check failed.
  • Repeat the failed version check after installation.

Unsure which installation applies?

Package managers differ across Linux distributions. Help me identify the correct installation instructions.

Prepare your editor and account

Your editor keeps every project file inside one visible workspace. Use the official Linux package for your distribution if Visual Studio Code is missing.

✔️ Visual Studio Code opens

  • Open your Linux application launcher.
  • Type Visual Studio Code into the application search.
  • Select Visual Studio Code from the results.

You will see the Visual Studio Code start screen. This confirms that the editor is ready.

ⓧ Visual Studio Code is not installed

Installing an application may trigger an administrator prompt. That prompt authorizes the Visual Studio Code package you selected.

  • Open the official Visual Studio Code Linux setup page.
  • Choose the official package format for your Linux distribution.
  • Download the selected package.
  • Open the downloaded package with your system's software installer.
  • Approve the administrator prompt if it appears.
  • Return to your Linux application launcher.
  • Search for Visual Studio Code.
  • Select Visual Studio Code from the results.

You will see the Visual Studio Code start screen after the installation succeeds.

Having trouble installing the editor?

Confirm that the package format matches your Linux distribution. Help me choose the correct Visual Studio Code package.

Signing in can feel sensitive. Your password stays inside GitHub's sign-in page.

  • Open your Linux application launcher.
  • Search for the web browser you use.
  • Select your browser from the results.
  • Visit GitHub.
  • Complete the sign-in flow for your existing account.
  • Confirm that your account control is visible on the GitHub page.

Your visible account control confirms that GitHub recognizes your session. You will create the repository in a later step.

Create the project workspace

A workspace keeps related files inside one named folder. The Explorer view shows the exact project structure you are building.

  • Switch back to Visual Studio Code.
  • Select File from the menu bar.
  • Select Open Folder....
  • Select your Desktop in the folder dialog.
  • Select New Folder.
  • Enter terminal-task-tracker as the folder name.
  • Select Select Folder.
  • Select Yes, I trust the authors if the Workspace Trust dialog appears.

You will see terminal-task-tracker at the top of the Explorer view. Visual Studio Code now treats this folder as your workspace.

What is a workspace?

A workspace is the folder Visual Studio Code opens as the current project. Its Explorer view keeps your tracker files together.

  • Select New File... in the Explorer view.

A filename field opens inside the Explorer view.

  • Enter myscript.py in the filename field.
  • Press Enter.

You will see the empty myscript.py file inside the workspace.

  • Select New File... in the Explorer view.

The filename field opens again.

  • Enter run.sh in the filename field.
  • Press Enter.

You will see the empty run.sh file beside myscript.py.

  • Select New File... in the Explorer view.

The filename field opens for the final project file.

  • Enter README.md in the filename field.
  • Press Enter.

You will see all three empty files inside terminal-task-tracker.

  • Select View from the menu bar.
  • Select Terminal.

Before the final check, predict whether all three commands will still return version information inside Visual Studio Code.

  • Use the shorter python fallback for the first line only if your earlier Python check required it.
  • Verify the command-line tools from the integrated terminal by running:
python3 --version
git --version
bash --version

What does the final check prove?

The integrated terminal starts inside the open workspace. These results prove that Visual Studio Code can reach the tools you need to build the tracker.

You will see version information for Python 3, Git, and GNU Bash. That check closes the setup loop.

Missing a version inside Visual Studio Code?

Close Visual Studio Code after installing the missing tool. Reopen the editor so its integrated terminal receives the updated command path.

Help me fix a missing command in the integrated terminal.

  • Confirm that terminal-task-tracker appears at the top of the Explorer view.
  • Confirm that the Explorer view lists myscript.py, run.sh, and README.md.

Your Explorer view now shows the exact empty file structure required for the next step.

✔️ Awesome, I've got everything!

Great. Confirm that all three files are empty before moving on.

ⓧ I'd like to double check the full code

Each target file contains zero characters at this checkpoint. Empty files have no code to display in a reference block.

  • Select myscript.py in the Explorer view.
  • Confirm that its editor contains zero characters.
  • Select run.sh in the Explorer view.
  • Confirm that its editor contains zero characters.
  • Select README.md in the Explorer view.
  • Confirm that its editor contains zero characters.

Three empty files exactly match the target artifacts for this step.

Your Linux workspace is ready. Next up, you will build a one-action tracker that accepts its first task.

Build a One-Action Task Tracker

Your verified Linux workspace is ready for its first program. The empty myscript.py file can now become a working Python application.

This first version tests whether keyboard input can drive an if branch. Running it reveals how long one interaction lasts.

In this step, get ready to:
  • Display the tracker title plus a single Add option.
  • Capture a menu choice plus one task title.
  • Run one complete Add interaction in the terminal.
Build the first runnable slice

An empty list holds task titles during the current run. Two printed lines give the user a menu to read.

  • Select myscript.py from the file list on the left in Visual Studio Code.
  • Create the tracker's empty task list plus visible menu by entering this code:
tasks = []

print("Terminal Task Tracker")
print("1. Add task")

What does this code do?

  • The tasks variable starts as an empty list because the session has no tasks yet.
  • The first print() call displays the application title.
  • The second print() call displays the only available menu option.
  • Save myscript.py.
  • Switch back to the terminal from earlier.
  • Show the first menu by running this command:
python3 myscript.py

What does this command do?

The python3 command starts the Python interpreter. The myscript.py argument tells it which script to execute.

You'll see Terminal Task Tracker on one line. You'll see 1. Add task below it.

Your first visible slice is working. The script can now display its menu in the terminal.

Don't see the menu?

Confirm that the terminal is inside the terminal-task-tracker folder. Confirm that you saved myscript.py before running the command.

Check that both menu messages use matching quotation marks. Help me find why my Python menu is not appearing.

Turn the menu into one action

The input() function pauses the script until the user types a response. An if branch checks whether that response selects the Add option.

  • Place your cursor below print("1. Add task") in myscript.py.
  • Add the menu input plus Add behavior by entering this code:
choice = input("Choose an option: ").strip()

if choice == "1":
    title = input("Task title: ").strip()
    tasks.append(title)
    print("Added: {}".format(title))

How does the interaction work?

  • The choice variable holds the menu response after strip() removes surrounding whitespace.
  • The if choice == "1": branch runs only when the user selects Add.
  • The title variable holds the task title entered at the second prompt.
  • The tasks.append(title) line stores that title at the end of the list.
  • The final print() call confirms which title was added.
  • Save myscript.py.

Seeing an indentation problem?

Python uses indentation to identify the lines controlled by the if branch. Make sure the three lines below it each begin with four spaces.

Keep choice = input("Choose an option: ").strip() aligned with the earlier print() calls. Help me correct the indentation in my Add branch.

Run a complete Add interaction

A complete interaction proves that the menu input reaches the Add branch. It also reveals what the script does after confirming the task.

  • Switch back to the terminal from earlier.

Before you run this, do you think the tracker will offer another menu after it confirms your task?

  • Start the tracker by running this command:
python3 myscript.py

What happens when this runs?

Python executes myscript.py from the first line. The process pauses whenever it reaches an input() prompt.

  • Enter 1 at the Choose an option: prompt.
  • Enter Practice Python at the Task title: prompt.

You'll see Added: Practice Python in the terminal. The program then exits.

That immediate exit is the intended shortfall. The tracker needs a loop before one session can handle several actions.

No Added confirmation?

Confirm that you entered 1 at the first prompt. Check that the task input plus confirmation lines remain indented inside the if branch.

Make sure tasks.append(title) appears before the confirmation line. Help me debug why my tracker does not print the Added confirmation.

✔️ Awesome, I've got everything!

Great. Your myscript.py file now accepts one task title plus prints its confirmation.

ⓧ I'd like to double check the full code

The complete myscript.py file should match this reference exactly:

tasks = []

print("Terminal Task Tracker")
print("1. Add task")

choice = input("Choose an option: ").strip()

if choice == "1":
    title = input("Task title: ").strip()
    tasks.append(title)
    print("Added: {}".format(title))

How does the full file fit together?

The file combines an empty task list with a printed menu. Its single branch accepts one title plus confirms the addition.

Your first terminal interaction now works from start to finish. Next, you'll keep the menu running so one session can manage several tasks.

Keep the Tracker Running

Your first Python tracker can collect a task title and confirm it. The program still closes before you can do anything else.

A loop keeps the menu available for several actions in one session. You will add View and Quit choices so you control when the tracker ends.

In this step, get ready to:
  • Build a looping menu with three choices.
  • Display multiple task titles with readable numbers.
  • Verify every menu path.
Replace the one-action flow with a loop

A while True loop repeats every indented line beneath it. The break statement provides a controlled exit when you choose Quit.

  • In the open myscript.py tab, press Cmd+A (macOS) or Ctrl+A (Windows and Linux) to select all existing code.
  • Replace the selected code by pasting the code below:
tasks = []

while True:
    print("\nTerminal Task Tracker")
    print("1. Add task")
    print("2. View tasks")
    print("3. Quit")

    choice = input("Choose an option: ").strip()

    if choice == "1":
        title = input("Task title: ").strip()
        tasks.append(title)
        print("Added: {}".format(title))
    elif choice == "2":
        print("\nTasks")
        for number, task in enumerate(tasks, start=1):
            print("{}. {}".format(number, task))
    elif choice == "3":
        print("Goodbye!")
        break
    else:
        print("Choose 1, 2, or 3.")

What does this code do?

  • The tasks list is created before the loop starts. Every menu cycle uses the same list.
  • The while True loop repeats the menu until the program reaches break.
  • The if, elif, and else branches route each menu choice to its matching behavior.
  • The enumerate(tasks, start=1) call gives each stored title a readable number beginning at 1.
  • Save myscript.py.

Before you run the tracker, predict whether one completed action will close the program.

  • Run the updated tracker in the Visual Studio Code terminal with this command:
python3 myscript.py

What should you see?

You will see the Terminal Task Tracker menu with Add, View, and Quit choices. The program waits at Choose an option: for your first choice.

Does the menu close too soon?

  • Check that the menu print lines are indented beneath while True.
  • Check that every choice branch is indented beneath while True.
  • Make sure break appears only inside the option 3 branch.

Help me find why my Python task tracker exits instead of returning to its menu.

Add and view multiple tasks

The tasks list keeps every title added during the current run. enumerate(tasks, start=1) pairs each title with a number that begins at 1.

  • Type 1 at the menu prompt to choose Add task.
  • Type Practice Python at the task title prompt.
  • Type 1 at the returned menu prompt.
  • Type Review loops at the task title prompt.

Before you choose View, predict which numbers the two titles should receive.

  • Type 2 at the returned menu prompt to choose View tasks.

You will see Practice Python as task 1. You will see Review loops as task 2.

You have the core interaction working. Several tasks now appear without ending the session.

Don't see both tasks?

  • Confirm that you added both titles during the same running session.
  • Check that each Add action printed its Added: confirmation before you chose View.
  • Check that tasks.append(title) remains inside the option 1 branch.

Help me debug why my Python tracker does not display every task I added.

Test unknown input and quit cleanly

An else branch gives an unknown menu choice a clear response. The break statement ends the loop only when option 3 is selected.

Before you test an unknown choice, predict whether the tracker should stop or show its menu again.

  • Type 9 at the menu prompt.

You will see Choose 1, 2, or 3. followed by the menu again.

  • Type 3 at the returned menu prompt to choose Quit.

You will see Goodbye!. Your normal shell prompt returns.

✔️ Awesome, I've got everything!

Your tracker adds several tasks, displays readable numbers, handles unknown choices, and quits when you choose option 3.

ⓧ I'd like to double check the full code

Compare your complete myscript.py file with this reference:

tasks = []

while True:
    print("\nTerminal Task Tracker")
    print("1. Add task")
    print("2. View tasks")
    print("3. Quit")

    choice = input("Choose an option: ").strip()

    if choice == "1":
        title = input("Task title: ").strip()
        tasks.append(title)
        print("Added: {}".format(title))
    elif choice == "2":
        print("\nTasks")
        for number, task in enumerate(tasks, start=1):
            print("{}. {}".format(number, task))
    elif choice == "3":
        print("Goodbye!")
        break
    else:
        print("Choose 1, 2, or 3.")

What should match?

  • The tasks = [] line appears once at the top of the file.
  • The while True loop contains the menu input and every choice branch.
  • The View branch uses enumerate(tasks, start=1) to number stored titles.

Your tracker now stays active for a complete task session. Next, you will give each task a visible completion status.

Organize and Complete Tasks

Your Python tracker now keeps several tasks available during one session. However, each list item only stores a title, so the program cannot track progress.

This step gives every task a completion status. You will also organize the tracker into functions that display tasks, validate numbers, and mark a selected task as complete.

In this step, get ready to:
  • Store each task as a dictionary containing its title and completion status.
  • Create focused functions for adding, displaying, and selecting tasks.
  • Complete a task by number and see its marker change.
Store task status in dictionaries

A task needs two related values: its title and whether it is done. A dictionary keeps those values together under the keys title and done.

  • In the Explorer sidebar, select myscript.py.
  • Find tasks = [] at the top of the file.
  • Insert the following function below the existing tasks list:
def add_task():
    title = input("Task title: ").strip()
    if not title:
        print("Task title cannot be empty.")
        return

    tasks.append({"title": title, "done": False})
    print("Added: {}".format(title))

What does this function do?

  • The add_task() function asks for a title and removes surrounding whitespace.
  • The empty-title check stops a blank task from reaching the list.
  • Each valid task becomes a dictionary whose done value starts as False.
  • Find the branch beginning with if choice == "1":.
  • Delete the three indented lines that currently read the title, append it, and print the confirmation.
  • Replace the deleted lines with one indented call to add_task().
  • Save myscript.py.
  • Run the updated tracker in the terminal with this command:
python3 myscript.py

What does this command do?

This command executes myscript.py with Python 3. The existing menu now sends the Add choice through add_task().

  • Enter 1 at the menu.
  • Enter Practice Python as the task title.
  • Enter 3 to quit.

You will see Added: Practice Python before the menu returns. Good start. Your tracker now creates a task with space for a completion status.

Task title not accepted?

Check that the call to add_task() is indented beneath the Add branch. Confirm that the dictionary uses the exact keys title and done.

Ask for help with the Add branch if the menu closes or reports a syntax problem: Help me check how my existing Add branch should call add_task().

The tracker also needs one place to format every task consistently. The next function turns each done value into a visible status marker.

  • Insert the following function directly above add_task():
def show_tasks():
    print("\nTasks")
    if not tasks:
        print("No tasks yet.")
        return

    for number, task in enumerate(tasks, start=1):
        marker = "x" if task["done"] else " "
        print("{}. [{}] {}".format(number, marker, task["title"]))

How are status markers created?

  • The empty-list check prints a clear message when there are no tasks to display.
  • The enumerate(tasks, start=1) call gives each dictionary a readable number beginning at 1.
  • The marker variable becomes x for a completed task or a space for a pending task.
  • Find the branch beginning with elif choice == "2":.
  • Delete the three indented lines that currently print and number the title strings.
  • Replace the deleted lines with one indented call to show_tasks().
  • Save myscript.py.
  • Run the tracker again with this command:
python3 myscript.py

What are you checking?

This run checks that Add stores dictionaries in the format expected by show_tasks(). It also makes each task's pending status visible.

  • Enter 1 at the menu.
  • Enter Practice Python as the task title.
  • Enter 2 to view the task.
  • Enter 3 to quit.

You will see 1. [ ] Practice Python in the task list. That marker proves the tracker can display the status stored inside a dictionary.

Seeing a dictionary instead of a task?

Confirm that the View branch calls show_tasks(). Remove the old loop that printed each entire task value.

Use this prompt if the status marker still does not appear: Help me find why show_tasks() prints a dictionary instead of a numbered status marker.

Validate the selected task number

Completing a task requires the learner to select an existing list position. The program needs to reject text and missing task numbers before either value can change the list.

  • Find the end of add_task().
  • Insert the following validation function below add_task():
def read_task_number(prompt):
    choice = input(prompt).strip()
    if not choice.isdigit():
        print("Enter a task number.")
        return None

    position = int(choice) - 1
    if position < 0 or position >= len(tasks):
        print("That task number does not exist.")
        return None

    return position

How does number validation work?

  • The isdigit() check rejects input that is not made entirely from digits.
  • The selected number is converted into a zero-based list position by subtracting 1.
  • The range check rejects positions before the first task or beyond the final task.
  • A valid position is returned to whichever action requested it.
  • Save myscript.py.
  • Run the tracker to confirm Python can load the new function:
python3 myscript.py

What does this check prove?

Python reads every function definition before reaching the existing menu loop. Seeing the menu confirms that the new validation function has valid syntax.

  • Enter 3 to quit the tracker.

The menu loads without a syntax error. Your task-number validation is ready for the Complete action.

Menu no longer loading?

Check that read_task_number() begins at the left edge of the file. Confirm that each nested line uses consistent indentation.

Use this prompt to inspect a parsing problem: Help me fix the indentation in my read_task_number() function.

Complete a task and organize startup

The validated position can now identify one task dictionary safely. A completion function updates its done value while leaving the title unchanged.

  • Insert the following function directly below read_task_number():
def complete_task():
    if not tasks:
        print("No tasks to complete.")
        return

    show_tasks()
    position = read_task_number("Task number to complete: ")
    if position is None:
        return

    tasks[position]["done"] = True
    print("Completed: {}".format(tasks[position]["title"]))

What does the completion function do?

  • The first check stops immediately when the list is empty.
  • The function displays the current tasks before asking for a number.
  • A rejected number returns None and ends the action safely.
  • A valid position changes only the selected dictionary's done value to True.
  • Save myscript.py.
  • Confirm that def complete_task(): appears directly above the existing while True loop.

Completion function in the wrong place?

Keep complete_task() outside every other function. Its def line must align with the other function definitions.

Ask for placement help here: Help me place complete_task() correctly between read_task_number() and the menu loop.

The remaining top-level loop can now become main(). The main guard starts that function only when myscript.py runs as the program.

  • Select the existing code from while True: through print("Choose 1, 2, or 3.").
  • Delete the selected loop.
  • Paste the following replacement beneath complete_task():
def main():
    while True:
        print("\nTerminal Task Tracker")
        print("1. Add task")
        print("2. View tasks")
        print("3. Complete task")
        print("4. Quit")

        choice = input("Choose an option: ").strip()

        if choice == "1":
            add_task()
        elif choice == "2":
            show_tasks()
        elif choice == "3":
            complete_task()
        elif choice == "4":
            print("Goodbye!")
            break
        else:
            print("Choose 1, 2, 3, or 4.")


if __name__ == "__main__":
    main()

How is the tracker organized now?

  • The main() function owns the repeating menu and routes each choice to one focused function.
  • Option 3 now runs complete_task().
  • Option 4 prints the goodbye message and ends the loop.
  • The main guard protects startup when another Python file imports this module.
  • Save myscript.py.

Before you run the completed tracker, which status marker do you expect each new task to show?

  • Start the completed tracker with this command:
python3 myscript.py

What does this final run test?

This run exercises the complete function-based flow. You will create task dictionaries, reject an invalid position, update one status, and view the result.

  • Enter 1 at the menu.
  • Enter Practice Python as the first task.
  • Enter 1 again.
  • Enter Review functions as the second task.
  • Enter 2 to view both tasks.

You will see both tasks with [ ] markers. Each dictionary currently has a done value of False.

Before you test an invalid number, do you expect the program to change either task?

  • Enter 3 to choose Complete.
  • Enter 9 as the task number.

You will see That task number does not exist. The menu returns without changing either task.

  • Enter 3 to choose Complete again.
  • Enter 1 as the task number.
  • Enter 2 to view the updated list.
  • Enter 4 to quit.

You will see 1. [x] Practice Python above 2. [ ] Review functions. You now have a tracker that records progress without losing readable task numbers.

Status marker not changing?

Confirm that complete_task() sets tasks[position]["done"] to True. Check that option 3 calls the same function.

Use this prompt if validation works but the marker stays pending: Help me trace why completing task 1 does not change its marker from pending to complete.

✔️ Awesome, I've got everything!

Great. Double check that you saved myscript.py and can change task 1 from [ ] to [x].

ⓧ I'd like to double check the full code

Compare your complete myscript.py file with this version:

tasks = []


def show_tasks():
    print("\nTasks")
    if not tasks:
        print("No tasks yet.")
        return

    for number, task in enumerate(tasks, start=1):
        marker = "x" if task["done"] else " "
        print("{}. [{}] {}".format(number, marker, task["title"]))


def add_task():
    title = input("Task title: ").strip()
    if not title:
        print("Task title cannot be empty.")
        return

    tasks.append({"title": title, "done": False})
    print("Added: {}".format(title))


def read_task_number(prompt):
    choice = input(prompt).strip()
    if not choice.isdigit():
        print("Enter a task number.")
        return None

    position = int(choice) - 1
    if position < 0 or position >= len(tasks):
        print("That task number does not exist.")
        return None

    return position


def complete_task():
    if not tasks:
        print("No tasks to complete.")
        return

    show_tasks()
    position = read_task_number("Task number to complete: ")
    if position is None:
        return

    tasks[position]["done"] = True
    print("Completed: {}".format(tasks[position]["title"]))


def main():
    while True:
        print("\nTerminal Task Tracker")
        print("1. Add task")
        print("2. View tasks")
        print("3. Complete task")
        print("4. Quit")

        choice = input("Choose an option: ").strip()

        if choice == "1":
            add_task()
        elif choice == "2":
            show_tasks()
        elif choice == "3":
            complete_task()
        elif choice == "4":
            print("Goodbye!")
            break
        else:
            print("Choose 1, 2, 3, or 4.")


if __name__ == "__main__":
    main()

Your tracker now organizes its behavior into focused functions and records task completion. Next, you will add a one-command launcher and publish the project.

Launch with Bash and Publish to GitHub

Your function-based tracker now adds tasks, displays status markers, validates input, and completes tasks. Its code still lives only inside the terminal-task-tracker folder in Visual Studio Code.

The tracker also depends on you remembering its Python command. A Bash launcher gives the project one consistent starting command.

Git records the finished files as a project snapshot. GitHub gives that snapshot a shareable home beyond your computer.

In this step, get ready to:
  • Launch the tracker through a Bash script.
  • Document the tracker in README.md.
  • Publish a committed copy of the project to GitHub.
Create the launcher and README

A launcher stores the tracker’s startup command in a reusable file. You can start the program through the same short command every time.

  • Select run.sh in the Explorer sidebar from earlier.
  • Replace the empty contents of run.sh by pasting the launcher below:
python3 myscript.py

What does this launcher do?

The python3 myscript.py command starts the tracker from its existing Python file.

Keeping that command in run.sh gives the project one stable entry point.

  • Save run.sh.

Before you test the launcher, picture the menu you expect the shell command to display.

  • Switch back to the integrated terminal from earlier.
  • Start the tracker through its launcher by running:
bash run.sh

What does this command do?

The bash run.sh command tells Bash to read the launcher file. Bash then executes the Python command stored inside it.

You’ll see the Terminal Task Tracker title followed by the four numbered menu options.

  • Enter 1 at the menu.
  • Enter Publish project as the task title.
  • Enter 3 at the next menu.
  • Enter 1 as the task number.
  • Enter 2 at the next menu.
  • Confirm the task displays with an [x] marker.
  • Enter 4 to quit the tracker.

That launcher now starts a complete task session with one command. You have packaged the tracker’s startup process into a reusable script.

Launcher not starting the tracker?

Confirm that run.sh contains only python3 myscript.py. Make sure both files are inside the same terminal-task-tracker folder.

If the terminal cannot find the Python file, confirm that the integrated terminal is still using the project folder from earlier.

Help me troubleshoot my Bash launcher.

A README explains what a project does before someone reads its source code. It also gives them the exact command needed to run it.

  • Select README.md in the Explorer sidebar.
  • Replace the empty contents of README.md by pasting the documentation below:
# Terminal Task Tracker

A beginner-friendly terminal application built with Python and launched with Bash.

## Features

- Add tasks
- View numbered tasks
- Mark tasks complete
- Handle invalid menu and task-number input

Tasks remain available for the current session and reset when the program closes.

## Run

```bash
bash run.sh
```

How does the README help?

The Features section summarizes the behavior that someone can test in the terminal. The session note makes the in-memory storage limit clear.

The Run section presents bash run.sh as the project’s starting command. Markdown formatting keeps the instructions readable on GitHub.

  • Save README.md.

You’ll see the project title, four features, the session limit, and the Bash run command in the saved file.

✔️ Awesome, I've got everything!

Your three project files now match the finished tracker, launcher, and documentation.

ⓧ I'd like to double check the full code

These references show the exact saved state of every project file.

tasks = []


def show_tasks():
    print("\nTasks")
    if not tasks:
        print("No tasks yet.")
        return

    for number, task in enumerate(tasks, start=1):
        marker = "x" if task["done"] else " "
        print("{}. [{}] {}".format(number, marker, task["title"]))


def add_task():
    title = input("Task title: ").strip()
    if not title:
        print("Task title cannot be empty.")
        return

    tasks.append({"title": title, "done": False})
    print("Added: {}".format(title))


def read_task_number(prompt):
    choice = input(prompt).strip()
    if not choice.isdigit():
        print("Enter a task number.")
        return None

    position = int(choice) - 1
    if position < 0 or position >= len(tasks):
        print("That task number does not exist.")
        return None

    return position


def complete_task():
    if not tasks:
        print("No tasks to complete.")
        return

    show_tasks()
    position = read_task_number("Task number to complete: ")
    if position is None:
        return

    tasks[position]["done"] = True
    print("Completed: {}".format(tasks[position]["title"]))


def main():
    while True:
        print("\nTerminal Task Tracker")
        print("1. Add task")
        print("2. View tasks")
        print("3. Complete task")
        print("4. Quit")

        choice = input("Choose an option: ").strip()

        if choice == "1":
            add_task()
        elif choice == "2":
            show_tasks()
        elif choice == "3":
            complete_task()
        elif choice == "4":
            print("Goodbye!")
            break
        else:
            print("Choose 1, 2, 3, or 4.")


if __name__ == "__main__":
    main()

Why is the tracker included?

The complete myscript.py reference confirms that the launcher points to the finished function-based tracker from the previous step.

python3 myscript.py

How does the launcher connect?

The launcher contains the exact command that starts myscript.py from the project folder.

# Terminal Task Tracker

A beginner-friendly terminal application built with Python and launched with Bash.

## Features

- Add tasks
- View numbered tasks
- Mark tasks complete
- Handle invalid menu and task-number input

Tasks remain available for the current session and reset when the program closes.

## Run

```bash
bash run.sh
```

What does the documentation capture?

The complete README.md reference records the tracker’s features, session behavior, and launcher command.

Record the project with Git

A Git repository tracks snapshots of your project files. Your first commit records the tracker, launcher, and README as one readable milestone.

  • Return to the integrated terminal from earlier.
  • Initialize the terminal-task-tracker folder on the main branch by running this command:
git init -b main

What does this command do?

The git init -b main command turns the current folder into a Git repository. It creates main as the initial branch.

You’ll see Git confirm that it initialized a repository inside the terminal-task-tracker folder.

Git cannot create the main branch?

If Git does not recognize the branch option, your installed Git may be older than the version that supports this command. Update Git through your Linux distribution’s package manager before retrying.

Help me troubleshoot Git initialization.

  • Stage all three project files by running these commands:
git add .
git status

What do these commands do?

  • The git add . command stages the files in the current project for a commit.
  • The git status command reports the current state of the repository.

You’ll see myscript.py, run.sh, and README.md staged for the next commit.

Files missing from Git status?

Confirm that the integrated terminal is using the terminal-task-tracker folder. Check the Explorer sidebar for all three saved files.

Help me find my unstaged project files.

Before you commit, predict which three files Git will include in the first project snapshot.

  • Create the first commit by running:
git commit -m "First commit"

What does this commit record?

The commit saves the staged project state with the message First commit. Future changes can build on this known snapshot.

You’ll see a commit summary that references First commit and the three recorded files.

Git asking for your identity?

Git needs an author name and email before it can create a commit. Follow the configuration guidance printed in your terminal before retrying the commit.

Help me configure my Git author identity.

  • Confirm the commit left no pending file changes by running:
git status

Why check the repository again?

The final status check confirms that the saved files match the committed snapshot. A clean result means the local history is ready to publish.

You’ll see that the repository has no pending file changes on the main branch.

Publish the project to GitHub

A Git remote connects your local repository to a repository hosted elsewhere. You’ll create an empty GitHub repository so your existing local commit can become its starting history.

  • Return to the browser where you are signed in to GitHub.
  • Click New repository.
  • Select your GitHub account from the Owner dropdown.
  • Enter terminal-task-tracker in the repository name field.
  • Select your preferred repository visibility.
  • Leave the README, gitignore, and license initialization options unselected.
  • Click Create repository.

GitHub now shows an empty terminal-task-tracker repository with setup instructions for existing code.

  • Copy the repository URL shown in Quick Setup.
  • Save the copied value as your Quick Setup URL.
  • Switch back to the integrated terminal from earlier.
  • Replace REMOTE-URL below with your Quick Setup URL.
  • Connect the local repository to GitHub by running the completed command:
git remote add origin REMOTE-URL

What is origin?

The command saves the GitHub repository URL under the short name origin. Future push commands can use that name instead of repeating the full URL.

  • Verify the saved remote URL by running:
git remote -v

What does the remote check prove?

The git remote -v command displays the URLs assigned to each remote. It confirms that origin points to your new GitHub repository.

You’ll see the copied GitHub repository URL listed for sending and receiving project history.

Remote URL looks wrong?

Compare the displayed URL with the value you copied from Quick Setup. A missing repository name or account name points to an incomplete copied URL.

Help me check my Git remote.

Your terminal may ask you to complete GitHub authentication during the first push. Follow the displayed sign-in prompt before continuing.

Before you run the push, predict which branch and files GitHub will receive.

  • Publish the local main branch by running:
git push -u origin main

What does the first push do?

The command sends the local main branch to origin. The upstream setting connects future pushes from this branch to the same destination.

  • Return to the GitHub repository page from earlier.
  • Refresh the browser page.
  • Confirm the branch selector shows main.
  • Confirm the file list shows myscript.py, run.sh, and README.md.

You’ll see the tracker source, Bash launcher, and rendered README on the main branch. This proves the local commit reached GitHub successfully.

Push not reaching GitHub?

Complete any authentication prompt shown by Git before retrying. Confirm that git remote -v points to the empty repository you created.

If Git rejects the push, confirm that the GitHub repository was created without pre-populated files.

Help me troubleshoot my first GitHub push.

That completes the local-to-GitHub workflow. Your tracker now starts through Bash and has a readable project history online.

Secret mission

Delete a Task Safely

Deleting a numbered task can change every number that follows it. Your challenge is to reuse the tracker’s existing validation before removing anything. You will finish with a Delete option that handles empty lists and invalid choices safely.

Clean Up Your Resources

Clean Up Your Resources

Choose whether to keep your resources, pause your work, or delete everything. Your local files and GitHub Free repository have no ongoing cost.

Resources you used:

  • The local terminal-task-tracker folder containing myscript.py, run.sh, and README.md.
  • The local Git repository and its commit history inside terminal-task-tracker.
  • The remote GitHub repository named terminal-task-tracker.

Keep everything running

No action needed. Choose this if you want to keep developing the task tracker or use it as the starting point for Part 2.

  • Keep the local terminal-task-tracker folder on your computer.
  • Keep the local Git history for future changes.
  • Keep the remote terminal-task-tracker repository available on GitHub.

Pause - I'll come back to this later

Stop the local session while keeping your project files and repository intact. You can return without rebuilding anything.

  • Enter 5 if the task tracker is still running.
  • Close Visual Studio Code.
  • Close the terminal.

Your local files remain available. Your GitHub repository also stays available.

Delete - I don't want to use this again

Deletion is permanent, so choose this option only when you no longer need the local files or the remote repository.

  • Return to the terminal-task-tracker repository on GitHub.
  • Open the repository settings page.
  • Use the repository deletion control to delete terminal-task-tracker.

The repository should no longer open at its GitHub URL. This confirms that the remote copy is gone.

  • Switch back to the terminal from earlier.
  • Remove the local project folder and its Git history by running these commands:
cd ..
rm -rf terminal-task-tracker
ls

What does this cleanup do?

  • The cd .. command moves the terminal into the folder that contains terminal-task-tracker.
  • The rm -rf terminal-task-tracker command permanently removes the project files and local Git history.
  • The ls command displays the remaining items in the parent folder.

You should no longer see terminal-task-tracker in the output.

Still See the Project Folder?

The removal command must run from the folder that directly contains terminal-task-tracker.

Help me safely remove the local project folder.

  • Close Visual Studio Code after the local folder disappears.

Nice Work!

Nice Work!

Project complete! You built a terminal task tracker that manages a full task session from one menu.

You've learned how to:

  • Build an interactive Python tracker that accepts input through a numbered menu. Its loop keeps tasks available for the current session.
  • Reshape a one-action script into a reusable session with a loop. Organize each behavior into focused functions. Protect task selection with input validation.
  • Start the app with a Bash launcher. Record the project through Git commits. Publish the code to a GitHub repository on main.
  • Complete the Secret Mission by adding safe task deletion through delete_task(). Reuse read_task_number(prompt) to reject bad choices. Rely on show_tasks() to display the remaining tasks with updated numbers.

Ready to quiz yourself?