fix(cli): resolve skill loading indicator line duplication#71
Conversation
The CLI was duplicating skill loading indicator lines for each file being processed due to three issues with Ink rendering: 1. Multiple Static components with absolute positioning caused layout conflicts. Fixed by printing the header before Ink starts and using a single Static component for completed items. 2. Wrapping items in new objects on each render broke Static's reference equality tracking. Fixed by passing items directly without wrapper objects. 3. Rapid consecutive rerender() calls from file update callbacks weren't being batched. Fixed by coalescing updates using setImmediate(). Also added comprehensive documentation explaining these Ink rendering constraints for future maintainers. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| let updatePending = false; | ||
| const updateUI = () => { | ||
| rerender(<SkillRunner skills={[...skillStates]} completedItems={[...completedItems]} />); | ||
| if (updatePending) return; | ||
| updatePending = true; | ||
| setImmediate(() => { | ||
| updatePending = false; | ||
| rerender(<SkillRunner skills={[...skillStates]} completedItems={[...completedItems]} />); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
This PR fixes a bug (skill loading indicator line duplication) but adds no regression test. Per testing guidelines, bug fixes must include a test that would have caught the issue.
warden: testing-guidelines
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| setImmediate(() => { | ||
| updatePending = false; | ||
| rerender(<SkillRunner skills={[...skillStates]} completedItems={[...completedItems]} />); | ||
| }); |
There was a problem hiding this comment.
Rerender may be called after Ink unmount
Low Severity
The batching mechanism using setImmediate() creates a race condition where rerender() may be called after unmount(). When a callback fires just before task completion, it schedules a deferred rerender. The await then resolves and unmount() is called synchronously, but the setImmediate callback runs afterward and attempts to call rerender() on the unmounted Ink instance. The updatePending flag does not account for the unmounted state.


Uh oh!
There was an error while loading. Please reload this page.