# How the StepTracker Class Provides Live Progress Updates in the Spec-Kit CLI

> Learn how the Spec-Kit CLI uses the StepTracker class and Rich Live to display real-time hierarchical progress trees in your terminal.

- Repository: [GitHub/spec-kit](https://github.com/github/spec-kit)
- Tags: internals
- Published: 2026-03-05

---

**The StepTracker class uses Rich's `Live` context manager with a refresh callback mechanism to render real-time hierarchical progress trees in the terminal.**

The `StepTracker` class in the `github/spec-kit` repository powers the command-line interface’s live progress reporting. By combining an internal state machine with Rich’s terminal UI capabilities, it delivers smooth, self-updating step trees without blocking the main execution thread.

## StepTracker Architecture and State Management

The `StepTracker` class is defined in [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py) (lines 304–389). It maintains a list of step dictionaries, each containing `key`, `label`, `status`, and `detail` fields.

**Core state methods include:**

- **`add(key, label)`** – Registers a new step with `pending` status.
- **`start(key)`** – Marks a step as `running`.
- **`complete(key, detail=None)`** – Sets status to `done` and attaches optional completion details.
- **`error(key, message)`** – Records an error state with a message.
- **`skip(key)`** – Marks the step as `skipped`.

Each mutating method triggers `_maybe_refresh()`, which invokes a registered callback only when a live display is active.

## The Refresh Callback Mechanism

Live updates rely on a callback pattern rather than polling. The `StepTracker` exposes **`attach_refresh(cb)`** (line 340) to register a callable that redraws the UI.

When state changes occur, `_maybe_refresh()` executes:

```python
def _maybe_refresh(self):
    if self._refresh_cb:
        try:
            self._refresh_cb()
        except Exception:
            pass

```

This design decouples the tracker from the rendering layer. The CLI attaches a lambda that calls `live.update(tracker.render())`, ensuring the terminal redraws instantly after every state transition without manual intervention.

## Rendering Real-Time Progress Trees

The **`render()`** method (lines 360–389) constructs a Rich `Tree` object. It maps internal status values to colored symbols:

- `pending` → dimmed circle
- `running` → blue spinner arrow
- `done` → green checkmark
- `error` → red cross
- `skipped` → yellow dash

The CLI consumes this tree inside Rich’s `Live` context manager (lines 1460–1470):

```python
tracker = StepTracker("Initialize Specify Project")
with Live(tracker.render(), console=console,
          refresh_per_second=8, transient=True) as live:
    tracker.attach_refresh(lambda: live.update(tracker.render()))
    # ... perform work, updating tracker state ...

```

The `transient=True` parameter ensures the progress tree disappears after completion, leaving a clean terminal.

## Practical Implementation Examples

### Project Initialization Workflow

The `init` command demonstrates the full lifecycle. It instantiates `StepTracker` with the operation title, attaches the refresh callback, then sequences through validation, API calls, and file generation, updating the tracker at each phase.

### Tool Verification Steps

In `check_tool` (lines 544–558), the tracker reports binary availability checks:

```python
tracker.add("git", "Check Git installation")
tracker.start("git")
if shutil.which("git"):
    tracker.complete("git", shutil.which("git"))
else:
    tracker.error("git", "Not found in PATH")

```

### Template Download Operations

The `download_and_extract_template` function (lines 2220–2270) uses granular steps for network and disk operations:

- `fetch` – Resolve release metadata
- `download` – Stream archive bytes
- `extract` – Unzip to target directory
- `cleanup` – Remove temporary files

Each step transitions through `start` → `complete` or `error`, giving users immediate visibility into long-running I/O.

## Summary

- **StepTracker** maintains hierarchical step state in [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py) (lines 304–389).
- **Live updates** are driven by a callback registered via `attach_refresh()`, triggered after every state mutation.
- **Rich integration** occurs through the `render()` method, which builds a colored `Tree` displayed inside a `Live` context manager (lines 1460–1470).
- **Usage patterns** include tool checking (lines 544–558), project initialization, and template downloads (lines 2220–2270).

## Frequently Asked Questions

### What is the StepTracker class in Spec-Kit?

The `StepTracker` class is a state management utility in the Spec-Kit CLI that tracks the lifecycle of discrete operations. It stores step metadata—such as labels, statuses, and completion details—and provides methods to transition steps through pending, running, done, error, and skipped states.

### How does StepTracker integrate with the Rich library?

`StepTracker` integrates with Rich through its `render()` method, which constructs a Rich `Tree` object decorated with status symbols and colors. The CLI wraps this tree in Rich’s `Live` context manager and attaches a refresh callback to `StepTracker`. Whenever a step’s state changes, the callback triggers `live.update()`, causing Rich to redraw the terminal display instantly.

### Can I use StepTracker outside of the Spec-Kit CLI?

While `StepTracker` is designed for the Spec-Kit CLI, the class is self-contained and can be reused in any Python project requiring hierarchical progress tracking. You would need to install the `rich` library separately and provide your own rendering logic or use the built-in `render()` method if you want the same tree-style output.

### Where is the StepTracker class defined in the source code?

The `StepTracker` class is defined in [`src/specify_cli/__init__.py`](https://github.com/github/spec-kit/blob/main/src/specify_cli/__init__.py) between lines 304 and 389. Key integration points that use the class—such as the project initialization command and the `check_tool` function—appear at lines 1460–1470 and 544–558 respectively.