feat: add spinner feedback for long-running operations#277
Merged
Conversation
Add indicatif spinner progress indicators to all commands that perform long-running git/filesystem operations (clone, init, checkout, prune, fetch, sync, carry, branch delete, flow adopt/eject, worktree branch). The spinner runs during core execute() calls on stderr, with step() messages updating the spinner text in real time. When the operation completes, the spinner is cleared and replaced by the existing result summary line. Key implementation details: - Add start_spinner/finish_spinner to Output trait - CliOutput uses indicatif ProgressBar with braille tick characters - Spinner is no-op in quiet mode, non-TTY, or DAFT_TESTING env - Drop impl on CliOutput ensures spinner cleanup on early return - All stdout/stderr writes coordinate with spinner via suspend/println - Route gitoxide experimental notice through Output system instead of direct eprintln (which corrupted spinner display) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The HookRenderer writes directly to stderr, bypassing the Output system's spinner coordination. This caused ghost spinner lines when hooks ran during a spinner-wrapped execute() call. Fix by calling finish_spinner() in CommandBridge::run_hook() before delegating to the hook executor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move finish_spinner() from CommandBridge::run_hook() into the hook executor, right before the hook header is printed. This keeps the spinner alive during hook discovery and trust checks (which don't produce visible output), and only clears it at the last moment before the hook renderer starts writing. If hooks are skipped (disabled, no files found, untrusted), the spinner is never cleared — it stays active for the rest of the operation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add bash/zsh/fish sandbox() shell functions for automatic cd - Update behavior to reflect .envrc regeneration on re-run - Add _lib.sh to file structure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `mise run sandbox:setup` to install a `daft-dev-sandbox` shell function into the user's RC file for automatic cd into sandboxes. Rename the shell function from `sandbox()` to `daft-dev-sandbox()` to avoid conflicts. Stub `compdef` in generated .envrc to suppress zsh completion errors under direnv. Add sandbox:clean to the worktree-pre-remove hook for automatic cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Export DAFT_WORKTREE in the generated .envrc for easy navigation back to the worktree. Add the sandbox directory to PATH and generate a daft-rebuild script that builds daft from the worktree. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add build.rs that embeds the git branch name and short commit hash into the display version for dev builds. `daft --version` now shows "1.0.33 (dev branch abc1234)" while release builds (DAFT_BUILD_RELEASE=1) show a clean "1.0.33". Clap attributes and man pages always use the clean version to avoid churn. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
enable_steady_tick() only schedules future ticks at 80ms intervals. Without an explicit tick() call, fast operations complete before the first draw, making the spinner invisible. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move `daft shell-init` eval from the generated .envrc (where direnv silently drops shell functions) into the daft-dev-sandbox shell function where it actually works. Remove the now-unnecessary compdef stub and shell detection from the sandbox task. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `?` operator inside a block propagates from the enclosing function, not from the block. This meant that if execute() failed, finish_spinner() was never reached, leaving ghost spinner lines on screen. Most visible in `daft go --start` where a failed checkout (BranchNotFound) left the spinner text on screen before falling through to create a new worktree. Fix: capture the Result without `?`, call finish_spinner(), then propagate. Applied consistently across all 13 spinner sites. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the ◉ indicator from after the branch name to the first (annotation) column alongside the > current-worktree marker. Change its color from orange to dark gray. The annotation column width is now dynamic: hidden when no annotations exist, narrow with one type, or wider with both, with a space separating the two slots. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DAFT_TESTING)finish_spinner()is always called before propagating errors via?(applied across all 13 spinner sites)tick()beforeenable_steady_tick(), since the timer only schedules future ticksmise run sandbox,sandbox:setup,sandbox:clean) for per-worktree test environments with auto-cleanup on worktree removaldaft --versionfor dev builds viabuild.rsdaft listoutputTest plan
mise run clippy— zero warningsmise run fmt:check— formatting verifiedmise run test:unit— all 395+ tests pass--quiet, piped output, andDAFT_TESTING=1daft go --start <nonexistent>)🤖 Generated with Claude Code