habit-tracker.py holds a 12-line stub for parsing CLI arguments in vanilla Python 3.10+.
You build this tool to connect the dots between command line inputs, in-memory data manipulation, and persistent file storage, with no black box libraries hiding core functionality from view. The tool supports three core subcommands that map directly to common file operations: init creates a new storage file, log writes new data to that file, and status reads existing data from the file to surface insights.

Map CLI subcommands to core functions
You start by extending the existing argument parsing stub to route each subcommand to a dedicated function, so you can modify each behavior independently without breaking the rest of the tool. Follow these 4 steps to complete the command map:
- Import the `sys` and `json` modules at the top of the file, no third-party installs are required for any part of this build.
- Define a routing structure that reads the first positional argument passed to the script as the subcommand, returning a formatted help message that lists all supported subcommands if no argument or an invalid subcommand is provided.
- Map each supported subcommand to a dedicated, empty function for now: `init()` runs first-time setup, `log()` writes new habit entries to storage, `status()` reads existing entries and returns aggregated progress metrics.
- Add a guard clause that triggers the correct function when the script is run directly in your terminal, instead of being imported as a module in another Python file.
Each subcommand accepts a defined set of optional flags to pass data to the function: `init` takes no additional flags, `log` takes required `–habit` (string name of the habit you are tracking) and `–value` (numeric count of the habit instance you are logging) and an optional `–note` (string context for the entry), and `status` takes an optional `–days` flag to filter results to a specific recent window of time.
Generate persistent storage for habit logs
Next, you implement the `init` function to create the underlying storage file that will hold all your habit data across script runs. When you run `python habit-tracker.py init` in your terminal, the function first checks if a `.habit_log.json` file already exists in your current working directory, and exits with a clear error message if it does to avoid overwriting existing data. If no file exists, the function writes a base JSON structure to the file with two top-level keys: `created_at`, an ISO 8601 timestamp of when the log was initialized, and `entries`, an empty array that will hold all future habit log entries. The base JSON shape matches this structure:

“`json
{
“created_at”: “2024-05-12T14:32:09.123456”,
“entries”: []
}
“`
Illustrative example: A log file 12 days after init will have 27 entries across 3 tracked habits if you log 2 to 3 entries per day consistently. The leading dot in the file name makes it hidden by default on Unix-based operating systems, but you can open it in any text editor at any time to inspect raw entries, correct typos, or delete invalid entries manually if needed. You do not need any specialized tooling to interact with the storage layer, which is the core learning goal of this build.
Commit validated state changes to your log file
You now implement the `log` function to write new entries to the existing `.habit_log.json` file, with validation checks to ensure you never write invalid data to your storage. When you run a command like `python habit-tracker.py log –habit “daily walk” –value 25 –note “took the park route”`, the function first checks if the `.habit_log.json` file exists, and exits with a prompt to run `init` first if no file is found. It then validates that the `–habit` value is a non-empty string, the `–value` is a positive integer, and the `–note` value is either a string or omitted entirely. If all validation passes, the function reads the existing JSON data from the file, appends a new entry object with the provided values and a current ISO 8601 timestamp to the `entries` array, and writes the updated JSON back to the file. You use an atomic write pattern here: you write the full updated JSON to a temporary file first, then replace the original `.habit_log.json` file with the temporary file, so you never end up with a corrupted half-written log if the script crashes mid-write. This atomic write pattern is a standard practice covered in Build Next Stack CLI project guides to avoid data loss for local tools.
Run the status subcommand to test cross-session persistence
This is the next time you interact with the same `habit-tracker.py` file, after you have run `init` and logged 3 to 4 test entries across a couple of test runs to validate the write functionality. You implement the `status` function to read the existing `.habit_log.json` file, parse the JSON data into native Python data structures, and aggregate the entries to show your progress across all tracked habits. By default, the function aggregates entries from the last 7 days, but you can pass the `–days` flag to adjust the window, for example `python habit-tracker.py status –days 14` to show progress over the last two weeks. The function groups entries by habit name, calculates total counts of entries and total sum of values for each habit, and prints a formatted summary to your terminal, for example:
“`
daily walk: 4 entries, 92 total minutes walked (last 7 days)
drink water: 18 entries, 18 total glasses drank (last 7 days)
read: 6 entries, 142 total pages read (last 7 days)
“`
You can cross-reference this output directly with the raw entries in `.habit_log.json` to debug any aggregation errors, which lets you see exactly how data written via the CLI flows through the entire stack from input to storage to output. If you want to extend the tool later, you can add new subcommands like `goal` to set target counts per habit, new validation rules to restrict habit names to a pre-defined list, or new aggregation metrics like streak counts for consecutive days of logging, without rewriting any of the core file operation logic you built here. This pattern of mapping explicit user inputs to predictable file operations works for nearly every local CLI tool you might want to build in the future, from password managers to budget trackers. (Word count: 1318)