feat(cli): add milestone progress bar to mgw:milestone output#165
Conversation
Adds lib/progress.cjs with renderProgressBar and printMilestoneProgress utilities that display a live '[████████░░] 4/9 issues complete' bar at milestone start and after each issue completes. Uses Catppuccin Macchiato ANSI colors in TTY mode and falls back to plain ASCII in CI/NO_COLOR. Closes #121 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
snipcodeit
left a comment
There was a problem hiding this comment.
Review: PR #165 — milestone progress bar
The lib/progress.cjs module is well-written with good Catppuccin color support and clean TTY detection. Tests cover the core cases. However there are issues in the milestone.md integration and a conflict risk.
🔴 DONE_SO_FAR computed but never used
In the execute_loop update block in commands/milestone.md:
DONE_SO_FAR=$((DONE_COUNT + ${#COMPLETED_ISSUES[@]}))
DONE_SO_FAR is set but then immediately replaced by a node invocation that re-reads doneCount from project state:
const doneCount = issues.filter(i => i.pipeline_stage === 'done' || i.pipeline_stage === 'pr-created').length;
So DONE_SO_FAR is dead code. Either use it as the done count (pass it into the node script via DONE_SO_FAR=$DONE_SO_FAR) or remove the variable. Using the state-derived count (from node) is the right approach since it reflects actual persisted state, not just the in-memory bash counter. Remove the DONE_SO_FAR line to avoid confusion.
🔴 lib/index.cjs conflict with PR #164
Both this PR and PR #164 add a single line to lib/index.cjs at the same location:
// PR #164 adds:
...require('./spinner.cjs'),
// PR #165 adds:
...require('./progress.cjs'),
These will conflict. The merged result must include both. Whoever merges second needs to manually resolve and include both requires. Add a note in the PR description about this known conflict.
🟡 NO_COLOR=1 in tests set after module import
In test/progress.test.cjs:
process.env.NO_COLOR = '1';
const { renderProgressBar, stageIcon } = require('../lib/progress.cjs');
progress.cjs imports output.cjs which reads process.env.NO_COLOR at module load time and assigns it to a module-level constant. If output.cjs was already loaded (cached by Node's module system) before NO_COLOR=1 was set, the constant will already be false and color codes will appear in test output, making string assertions non-deterministic.
To fix: set NO_COLOR=1 before Node starts via the test runner, OR set it at the very top before any requires (which you do — but the issue is if output.cjs was cached from a prior test file). The safest approach is to run progress tests in isolation or use --env-file in the test command.
In practice this is unlikely to cause failures if the test runner loads files fresh, but it's worth documenting.
🟡 Missing stageIcon tests for intermediate stages
test/progress.test.cjs tests stageIcon for done, pr-created, executing, failed, blocked, and unknown. But the MGW pipeline has additional stages (triaged, needs-info, discussing, approved, diagnosing) that all fall to the default case. A test that explicitly covers:
it('maps all intermediate stages to pending icon', () => {
for (const stage of ['triaged', 'needs-info', 'discussing', 'approved', 'diagnosing', 'new']) {
assert.equal(stageIcon(stage).icon, '○');
}
});
...would document the intended behavior and prevent regressions if someone adds a new stage.
✅ Good points
- 14 tests cover all key rendering cases cleanly
- Catppuccin color usage is correct and consistent with project theme
renderProgressBaredge case (0/0) handled gracefully with special formatting- Overshoot test (
done > total) correctly clamps to 100% - The
col()helper is the right abstraction for conditional coloring stageIconexported separately — good for testability
…r intermediate stages - Remove unused DONE_SO_FAR assignment in execute_loop (done count is re-read from state by the node progress renderer) - Add conflict-warning comment in lib/index.cjs above progress.cjs barrel entry, noting PR #120 (spinner.cjs) touches the same file - Add stageIcon test covering needs-info, discussing, approved, and diagnosing — all intermediate pipeline stages that should map to ○ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
lib/progress.cjswithrenderProgressBarandprintMilestoneProgressutilities for displaying live ASCII progress bars during milestone execution[████████░░] 4/9 issues completeformat at milestone start and updates after each issue completes✓done,◆running,✗failed,⊘blocked,○pending) are shown inline with issue numbersCloses #121
Milestone Context
Changes
lib/progress.cjs(new) — Core progress bar module:renderProgressBar({done, total, width}),printMilestoneProgress({done, total, label, issues, currentIssue}),stageIcon(stage); Catppuccin Macchiato palette via 24-bit ANSI; TTY/CI/NO_COLOR detection vialib/output.cjslib/index.cjs— Added...require('./progress.cjs')to barrel exportscommands/milestone.md— Two integration points: (1) initial progress bar printed inload_milestonestep after header; (2) updated bar printed after each issue checkpoint inexecute_loopusing live pipeline stage data fromlib/state.cjstest/progress.test.cjs(new) — 14 tests coveringrenderProgressBaredge cases (0/0, empty, full, partial, overshoot),stageIconmappings for all stage values, and module export shapeTest Plan
npm test— all 124 tests pass (14 new progress tests + 110 existing)node -e "require('./lib/progress.cjs').printMilestoneProgress({done:4,total:9})"in the repo to verify bar renderingNO_COLOR=1or pipe output to verify plain ASCII fallback (Progress: [████░░░░░░] 4/9 issues complete)/mgw:milestoneon a milestone with multiple issues and confirm: initial bar at start, updated bar after each issue completes