Project 2: task-nexus

Build a task manager CLI with nested subcommands, config hierarchy, and persistent storage.

Quick Reference

Attribute Value
Difficulty Level 2 (Intermediate)
Time Estimate 1 week
Language Go (Alternatives: Rust, Python)
Prerequisites Project 1 or basic CLI parsing, file I/O
Key Topics Subcommand trees, config precedence, persistence

1. Learning Objectives

By completing this project, you will:

  1. Design a multi-level CLI command tree with shared flags.
  2. Implement deterministic config precedence across flags, env, and files.
  3. Persist data safely across runs and across OSes.
  4. Provide structured output for automation.
  5. Build a tool whose command layout can grow without chaos.

2. Theoretical Foundation

2.1 Core Concepts

  • Command Trees: Tools like git and docker map operations to a hierarchy. This makes discoverability and documentation scale.
  • Config Precedence: A predictable order prevents ambiguity: flags > env > config file > defaults.
  • XDG Base Directory: Defines standard paths for config/data/cache on Unix; Windows has equivalent known folders.
  • Persistence Models: JSON is simple and human-readable; SQLite handles concurrency and querying. Both are valid; the choice affects design.

2.2 Why This Matters

Most real CLIs are ecosystems. A tool with poorly designed subcommands becomes unusable as soon as it grows. This project teaches the structure that lets you add new capabilities without breaking users.

2.3 Historical Context / Background

git established conventions like subcommand help and command tree navigation. Modern CLI frameworks (Cobra, Clap) encode these conventions and help avoid inconsistent UX.

2.4 Common Misconceptions

  • “Flat commands are simpler”: They become unintuitive when features grow.
  • “Config is just a file”: It is a merge of multiple sources with precedence.

3. Project Specification

3.1 What You Will Build

A CLI tool named tasks with:

  • tasks add "Ship MVP" --priority high
  • tasks list --status pending
  • tasks done 3
  • tasks config set sync.url https://example.com

3.2 Functional Requirements

  1. Subcommands: add, list, done, delete, config.
  2. Persistence: Store tasks in a data file under the user data directory.
  3. Output Modes: Human table output and JSON output.
  4. Config Sources: Support config via flags, env, and config file.

3.3 Non-Functional Requirements

  • Portability: Linux, macOS, Windows.
  • Reliability: No data loss on crash during write.
  • Discoverability: --help is clear for root and subcommands.

3.4 Example Usage / Output

$ tasks add "Ship MVP" --priority high
Task added: [1] Ship MVP

3.5 Real World Outcome

You add tasks, list them, and get both human-readable and machine-readable output:

$ tasks list --status pending
ID  Task        Priority  Status  Created
1   Ship MVP    High      Pending  2025-01-03

$ tasks list --output json
[{"id":1,"title":"Ship MVP","priority":"high","status":"pending"}]

This output can be piped into jq or used by scripts without parsing tables.


4. Solution Architecture

4.1 High-Level Design

+-------------+     +-----------------+     +-------------------+
| CLI Parser  | --> | Command Handler | --> | Storage Layer     |
| (Cobra)     |     | (add/list/etc.) |     | (JSON/SQLite)     |
+-------------+     +-----------------+     +-------------------+
          |                   |                       |
          +-------------------+-----------------------+
                      Shared config

4.2 Key Components

Component Responsibility Key Decisions
Root Cmd Global flags, config Output mode, config precedence
Subcommands Implement operations Keep naming stable
Storage Read/write tasks JSON vs SQLite
Formatter Table or JSON One output interface

4.3 Data Structures

type Task struct {
    ID       int
    Title    string
    Priority string
    Status   string
    Created  time.Time
}

type Store struct {
    Version int
    Tasks   []Task
}

4.4 Algorithm Overview

Key Algorithm: Safe persistence

  1. Read store from disk.
  2. Apply mutation (add/done/delete).
  3. Write to temp file.
  4. Rename temp file into place (atomic on most OSes).

Complexity Analysis:

  • Time: O(N) per operation
  • Space: O(N) for file rewrite

5. Implementation Guide

5.1 Development Environment Setup

brew install go
mkdir task-nexus && cd task-nexus
go mod init task-nexus

5.2 Project Structure

task-nexus/
├── cmd/
│   ├── root.go
│   ├── add.go
│   ├── list.go
│   ├── done.go
│   └── config.go
├── internal/
│   ├── store/store.go
│   └── output/format.go
└── README.md

5.3 The Core Question You Are Answering

“How do I design a CLI that can scale from a handful of commands to a full product surface?”

5.4 Concepts You Must Understand First

  1. Command Trees
    • Persistent vs local flags
    • Help text inheritance
  2. Config Precedence
    • Why deterministic override order matters
  3. User Data Locations
    • XDG on Unix, AppData on Windows

5.5 Questions to Guide Your Design

  1. Should tasks live under config or data directories?
  2. How will you version the data schema?
  3. Should JSON output be default in CI?

5.6 Thinking Exercise

Sketch your command tree and list which flags should be global vs local.

5.7 The Interview Questions They Will Ask

  1. Why is a command tree better than flat flags?
  2. How do you prevent data loss during writes?
  3. What is the XDG Base Directory spec?

5.8 Hints in Layers

Hint 1: Start with only add and list.

Hint 2: Implement a Store interface for future backends.

Hint 3: Add --output json after table output works.

Hint 4: Use temp-file rename for safe writes.

5.9 Books That Will Help

Topic Book Chapter
CLI design “Build Awesome Command-Line Applications in Go” Ch. 3-5
Config hierarchy “The Twelve-Factor App” Factor III
File I/O “The Linux Programming Interface” Ch. 4

5.10 Implementation Phases

Phase 1: Foundation (1-2 days)

Goals:

  • Root command + add + list
  • JSON persistence

Checkpoint: Add and list tasks across runs.

Phase 2: Core Functionality (2-3 days)

Goals:

  • Implement done and delete
  • Add config loading

Checkpoint: Config values override defaults.

Phase 3: Polish and Output (1-2 days)

Goals:

  • JSON output mode
  • Improved help and error text

Checkpoint: tasks list --output json works.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Storage JSON vs SQLite JSON first Simple and debuggable
Output –json vs –output –output Extensible
File location config vs data data dir Data is not config

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Unit Tests Store operations add/done/delete
Integration Tests CLI behavior list output
Edge Case Tests Empty store list with zero tasks

6.2 Critical Test Cases

  1. Add task then list shows it.
  2. Mark task done and list shows status change.
  3. Corrupt JSON triggers recovery message.

6.3 Test Data

{"version":1,"tasks":[{"id":1,"title":"Ship MVP","status":"pending"}]}

Expected:

  • tasks list shows one row with ID 1
  • tasks done 1 updates status

7. Common Pitfalls and Debugging

Pitfall Symptom Solution
Wrong storage location Data not persisted Use UserConfigDir/UserDataDir
Data loss on crash Missing tasks Temp-file write + rename
Inconsistent output Hard to parse Keep output stable

8. Extensions and Challenges

8.1 Beginner Extensions

  • Add tasks search <query>
  • Add --priority filtering

8.2 Intermediate Extensions

  • Add tasks export --format csv
  • Add tasks import from JSON

8.3 Advanced Extensions

  • Add tasks sync with remote API
  • Add file locking for concurrent access

9. Real-World Connections

  • CLI front-ends for internal tools
  • Local developer productivity tooling

10. Resources

  • Cobra and Viper docs
  • taskwarrior source for inspiration

11. Self-Assessment Checklist

  • I can explain config precedence
  • I can describe a command tree and why it matters

12. Submission / Completion Criteria

Minimum Viable Completion:

  • add and list work
  • Data persists in user data directory

Full Completion:

  • CRUD operations + config precedence

Excellence (Going Above and Beyond):

  • Export/import + file locking or sync