on main help sign in
fix(noninteractive): always print newline after output

This is particularly important to keep the last line of output from being overwritten by the prompt when output's a TTY.

commit2fd5ab2ebcd5245c7377bb575533c71eb58b12ce authorChristian Rocha <christian@rocha.is> dateSat 1 Nov 2025, 22:07 (1 year ago) parent88c88c6
2 files changed+21−8
internal/app/app.go+15−7 internal/cmd/run.go+6−1
internal/app/app.go+15−7
@@ -101,8 +101,8 @@ func (app *App) Config() *config.Config {
101101 return app.config
102102}
103103
104−// RunNonInteractive handles the execution flow when a prompt is provided via
105−// CLI flag.
104+// RunNonInteractive runs the application in non-interactive mode with the
105+// given prompt, printing to stdout.
106106func (app *App) RunNonInteractive(ctx context.Context, prompt string, quiet bool) error {
107107 slog.Info("Running in non-interactive mode")
108108
@@ -125,7 +125,7 @@ func (app *App) RunNonInteractive(ctx context.Context, prompt string, quiet bool
125125 defer stopSpinner()
126126
127127 const maxPromptLengthForTitle = 100
128− titlePrefix := "Non-interactive: "
128+ const titlePrefix = "Non-interactive: "
129129 var titleSuffix string
130130
131131 if len(prompt) > maxPromptLengthForTitle {
@@ -165,11 +165,19 @@ func (app *App) RunNonInteractive(ctx context.Context, prompt string, quiet bool
165165 messageEvents := app.Messages.Subscribe(ctx)
166166 messageReadBytes := make(map[string]int)
167167
168− defer fmt.Printf(ansi.ResetProgressBar)
168+ defer func() {
169+ _, _ = fmt.Printf(ansi.ResetProgressBar)
170+
171+ // Always print a newline at the end. If output is a TTY this will
172+ // prevent the prompt from overwriting the last line of output.
173+ _, _ = fmt.Print('\n')
174+ }()
175+
169176 for {
170− // HACK: add it again on every iteration so it doesn't get hidden by
171− // the terminal due to inactivity.
172− fmt.Printf(ansi.SetIndeterminateProgressBar)
177+ // HACK: Reinitialize the terminal progress bar on every iteration so
178+ // it doesn't get hidden by the terminal due to inactivity.
179+ _, _ = fmt.Printf(ansi.SetIndeterminateProgressBar)
180+
173181 select {
174182 case result := <-done:
175183 stopSpinner()
internal/cmd/run.go+6−1
@@ -48,7 +48,12 @@ crush run -q "Generate a README for this project"
4848 return fmt.Errorf("no prompt provided")
4949 }
5050
51− // Run non-interactive flow using the App method
51+ // TODO: Make this work when redirected to something other than stdout.
52+ // For example:
53+ // crush run "Do something fancy" > output.txt
54+ // echo "Do something fancy" | crush run > output.txt
55+ //
56+ // TODO: We currently need to press ^c twice to cancel. Fix that.
5257 return app.RunNonInteractive(cmd.Context(), prompt, quiet)
5358 },
5459}