MCPcopy Index your code
hub / github.com/github/spec-kit / StepTracker

Class StepTracker

src/specify_cli/_console.py:41–124  ·  view source on GitHub ↗

Track and render hierarchical steps without emojis, similar to Claude Code tree output. Supports live auto-refresh via an attached refresh callback.

Source from the content-addressed store, hash-verified

39err_console = Console(stderr=True, highlight=False)
40
41class StepTracker:
42 """Track and render hierarchical steps without emojis, similar to Claude Code tree output.
43 Supports live auto-refresh via an attached refresh callback.
44 """
45 def __init__(self, title: str):
46 self.title = title
47 self.steps = [] # list of dicts: {key, label, status, detail}
48 self.status_order = {"pending": 0, "running": 1, "done": 2, "error": 3, "skipped": 4}
49 self._refresh_cb: Callable[[], None] | None = None
50
51 def attach_refresh(self, cb: Callable[[], None]) -> None:
52 self._refresh_cb = cb
53
54 def add(self, key: str, label: str):
55 if key not in [s["key"] for s in self.steps]:
56 self.steps.append({"key": key, "label": label, "status": "pending", "detail": ""})
57 self._maybe_refresh()
58
59 def start(self, key: str, detail: str = ""):
60 self._update(key, status="running", detail=detail)
61
62 def complete(self, key: str, detail: str = ""):
63 self._update(key, status="done", detail=detail)
64
65 def error(self, key: str, detail: str = ""):
66 self._update(key, status="error", detail=detail)
67
68 def skip(self, key: str, detail: str = ""):
69 self._update(key, status="skipped", detail=detail)
70
71 def _update(self, key: str, status: str, detail: str):
72 for s in self.steps:
73 if s["key"] == key:
74 s["status"] = status
75 if detail:
76 s["detail"] = detail
77 self._maybe_refresh()
78 return
79
80 self.steps.append({"key": key, "label": key, "status": status, "detail": detail})
81 self._maybe_refresh()
82
83 def _maybe_refresh(self):
84 if self._refresh_cb:
85 try:
86 self._refresh_cb()
87 except Exception:
88 pass
89
90 def render(self):
91 tree = Tree(f"[cyan]{self.title}[/cyan]", guide_style="grey50")
92 for step in self.steps:
93 label = step["label"]
94 detail_text = step["detail"].strip() if step["detail"] else ""
95
96 status = step["status"]
97 if status == "done":
98 symbol = "[green]●[/green]"

Callers 3

checkFunction · 0.85
initFunction · 0.85

Calls

no outgoing calls

Tested by 1