Skip to content

Feat/hooks tui - #6

Merged
sam0uly merged 10 commits into
mainfrom
feat/hooks-tui
Jul 22, 2026
Merged

Feat/hooks tui#6
sam0uly merged 10 commits into
mainfrom
feat/hooks-tui

Conversation

@sam0uly

@sam0uly sam0uly commented Jul 22, 2026

Copy link
Copy Markdown
Owner

What this does

Adds an interactive hook review screen to spin new. Before any shell
commands run, the user sees every [[pre]] and [[post]] hook the
template will execute and chooses Run / Skip / Cancel.

Why

spin new --yes skipped the trust prompt entirely. spin new without
--yes printed a console prompt but showed zero detail about what hooks
would run. Neither path let users inspect the commands before execution.
This is important for a scaffolder that shells out to arbitrary templates.

How it works

spin new now has two TUI steps instead of one:

  1. Params form — existing Huh form (unchanged)
  2. Hooks review — new Bubble Tea TUI

The hooks TUI shows a list of hooks on the left (inline commands and
script files) with a detail pane on the right. Pressing R opens a
Run / Skip / Cancel modal centered over the view. The user reviews
every command before anything executes.

Key changes

  • cmd/hook_output.go -- shared hookTreeWriter that emits tree-styled
    hook output for both the CLI (os.Stdout) and the TUI (channel).
    Replaces the old hookOutput/tuiHookOutput/chanWriter duplication.
  • cmd/hooks_tui.go -- the Bubble Tea model with list, viewport, modal,
    ANSI-aware text wrapping, and canvas compositing for the centered
    Run/Skip/Cancel dialog.
  • internal/template/pre_hook.go -- new StepStart callback in
    HookOptions so callers can style per-step headers without pulling
    lipgloss into the template engine. Added flushWriter to drain
    buffered output after each hook command finishes.
  • Dead code removed: startSingleRun, runningAll, chanWriter,
    orphaned RunSingleHook and hookAssetDir.
  • Named layout constants replace magic numbers for pane widths.
  • Canvas sizing fix: the modal is composited onto a canvas sized to the
    rendered base, not m.width, so the view pane's right border is never
    clipped.

Testing

  • Model-driven tests for modal state machine (Run / Skip / Cancel)
  • Async hook streaming via pump()
  • Border-clipping regression test
  • Full scaffold with temporary directories and fixture templates

Before / after

Before: spin new prompts "Run hooks? [y/N]" with no detail.

After: a scrollable hook list with Run / Skip / Cancel, live output
streaming, and the ability to inspect every command before execution.

Summary by CodeRabbit

  • New Features
    • Enhanced the project creation flow with a two-step interactive UI that includes a full-screen hooks review/confirmation step when templates define hooks.
    • During creation, you can review hook commands/scripts and then run, skip, or cancel.
    • After successful creation, the CLI now shows a “cd” hint for the new project directory.
  • Bug Fixes
    • Improved streaming hook output with clearer step headers, formatting, and robust concurrent rendering; hook output flush behavior is more reliable.
  • Tests
    • Added/expanded UI and hook logic tests covering hook discovery, selection, modal state, running, skipping, and cancellation.
  • Chores
    • Updated dependency declarations in the build configuration.

sam0uly and others added 5 commits July 16, 2026 01:50
Introduce a two-step interactive flow for `spin new`: a param form
followed by a hooks review screen. The hooks screen lists every
[[pre]]/[[post]] command and _pre/_post script the template will run,
shows the selected hook's content while navigating, and lets the user
preview a single hook (Enter) or run the full scaffold via a centered
Run/Skip modal (R). Hook execution streams into the right pane, the
success summary (created + cd hint) is mirrored there and reprinted
after quit, and the screen restores the terminal on exit via the
alternate screen buffer.

This also adds HookView/CollectHooks and verbose-gated single-hook
streaming in the template package, and restores the non-TUI
RenderToWithPost call in runNew (it had been left commented out, so
non-interactive `spin new` printed success without rendering files).

💘 Generated with Crush

Assisted-by: Crush:hy3-free
Replace the duplicated hookOutput/tuiHookOutput and chanWriter with a
single hookTreeWriter in cmd/hook_output.go. The writer emits prefixed
lines through an injectable emit function — os.Stdout for CLI, a channel
for the TUI. Add a StepStart callback to HookOptions so callers can
style per-step headers without leaking rendering concerns into the
template engine. Also remove the orphaned RunSingleHook and hookAssetDir.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-pro
Remove vestigial single-hook preview (startSingleRun, runningAll,
chanWriter, "enter" keybind) now that the TUI always runs the full
scaffold. Add a 3-choice Run/Skip/Cancel modal, ANSI-aware viewport
wrapping via ansi.Hardwrap, and named layout constants replacing the
magic numbers (42, -3, 20). Size the modal canvas to the rendered base
instead of m.width so the view pane's right border is never clipped.

Also add regression tests for Cancel dismissal, modal border clipping,
and update assertions for the new modal content.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-pro
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 86f24e35-39ea-4730-86e4-289c5cf10e20

📥 Commits

Reviewing files that changed from the base of the PR and between e4e528c and 1b8d6e0.

📒 Files selected for processing (4)
  • cmd/hooks_tui.go
  • cmd/new_tui.go
  • internal/template/hooks_view_test.go
  • internal/template/pre_hook.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/template/hooks_view_test.go
  • cmd/hooks_tui.go
  • internal/template/pre_hook.go
  • cmd/new_tui.go

📝 Walkthrough

Walkthrough

Adds hook discovery and output routing, a full-screen hook review TUI with run/skip/cancel controls, streamed scaffold execution, and integration with the interactive spin new flow.

Changes

Hook review flow

Layer / File(s) Summary
Hook collection contract
internal/template/hooks_view.go, internal/template/hooks_view_test.go
Defines ordered inline and file-backed hook entries, filters empty commands, sorts discovered files, and tests collection behavior.
Hook output routing
internal/template/pre_hook.go, cmd/hook_output.go
Adds configurable hook output and step callbacks, buffered flushing, executable-script selection, and tree-indented output rendering.
Hook review and execution screen
cmd/hooks_tui.go, cmd/hooks_tui_test.go
Adds hook previews, run/skip/cancel modal behavior, streamed scaffold execution, responsive panes, alternate-screen rendering, and UI tests.
Interactive spin new integration
cmd/new_tui.go, cmd/new.go, cmd/new_prompts.go, go.mod
Transitions from parameter collection to hook review, returns resolved values and execution state, avoids duplicate rendering, updates completion hints, removes the prior hook confirmation helpers, and promotes TUI dependencies to direct requirements.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant runNew
  participant newTUIModel
  participant hooksModel
  participant tpl.RenderToWithPost
  participant channelHookOutput
  runNew->>newTUIModel: start interactive template flow
  newTUIModel->>hooksModel: transition after parameter resolution
  hooksModel->>tpl.RenderToWithPost: submit run or skip mode
  tpl.RenderToWithPost->>channelHookOutput: stream hook output lines
  channelHookOutput->>hooksModel: deliver runLineMsg
  tpl.RenderToWithPost->>hooksModel: deliver runDoneMsg
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too vague and generic to convey the main change beyond a hooks TUI feature. Use a specific title like "Add hooks review TUI to spin new" so teammates can understand the primary change at a glance.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/hooks-tui

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/new_tui.go (1)

118-124: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Route quit keys through the active step first. cmd/new_tui.go intercepts esc/q/ctrl+c before m.hooks.update, so the hooks-step modal’s esc close path and hooks-step ctrl+c handling never run in the real TUI flow. Handle these keys in the active step first, or limit the global shortcut to stepForm.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/new_tui.go` around lines 118 - 124, Update the KeyPressMsg handling
around the active step dispatch so esc, q, and ctrl+c are offered to
m.hooks.update before global quit handling. Preserve the global tea.Quit and
tea.Interrupt shortcuts for stepForm, while allowing the hooks-step modal close
and ctrl+c behavior to execute first.
🧹 Nitpick comments (1)
cmd/hooks_tui.go (1)

201-204: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Quadratic output growth on streamed runs. Each runLineMsg appends to m.output and then re-wraps the entire accumulated buffer via wrapForView, so a verbose hook that emits N lines does O(N²) wrapping/allocation work. Consider wrapping only the incoming chunk and appending to the viewport, or capping/truncating retained output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/hooks_tui.go` around lines 201 - 204, Optimize the runLineMsg handling so
streamed output does not re-wrap the entire accumulated m.output on every
message. Update the flow around wrapForView to wrap only the incoming msg.text
and append it incrementally to the viewport, or otherwise cap retained output
while preserving bottom-scrolling behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/new_tui.go`:
- Around line 336-343: Update the TUI execution flow around tea.NewProgram and
p.Run to capture the final Bubble Tea model returned by p.Run, then read
hooks.didRun and resolved from that final model rather than the original m.
Preserve the existing collectResolved fallback using the final model’s params
and name before returning.

In `@internal/template/hooks_view_test.go`:
- Around line 19-26: Handle the errors returned by f.Close and each
f.WriteString call in the test helper, including writes in the pres and posts
loops. Update the helper’s error-handling flow so errcheck passes while
preserving the generated template content and cleanup behavior.

---

Outside diff comments:
In `@cmd/new_tui.go`:
- Around line 118-124: Update the KeyPressMsg handling around the active step
dispatch so esc, q, and ctrl+c are offered to m.hooks.update before global quit
handling. Preserve the global tea.Quit and tea.Interrupt shortcuts for stepForm,
while allowing the hooks-step modal close and ctrl+c behavior to execute first.

---

Nitpick comments:
In `@cmd/hooks_tui.go`:
- Around line 201-204: Optimize the runLineMsg handling so streamed output does
not re-wrap the entire accumulated m.output on every message. Update the flow
around wrapForView to wrap only the incoming msg.text and append it
incrementally to the viewport, or otherwise cap retained output while preserving
bottom-scrolling behavior.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8def50bb-5ee7-4aa6-9a56-238c75474d14

📥 Commits

Reviewing files that changed from the base of the PR and between 0372af4 and 42fae93.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (9)
  • cmd/hook_output.go
  • cmd/hooks_tui.go
  • cmd/hooks_tui_test.go
  • cmd/new.go
  • cmd/new_tui.go
  • go.mod
  • internal/template/hooks_view.go
  • internal/template/hooks_view_test.go
  • internal/template/pre_hook.go

Comment thread cmd/new_tui.go Outdated
Comment thread internal/template/hooks_view_test.go Outdated
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 2 file(s) based on 2 unresolved review comments.

Files modified:

  • cmd/new_tui.go
  • internal/template/hooks_view_test.go

Commit: 0988f1bed9274c3271ea149ced83be4b6edbdae2

The changes have been pushed to the feat/hooks-tui branch.

Time taken: 2m 29s

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/template/pre_hook.go (2)

185-208: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Quote hook script paths before passing them to sh -c. Filenames with spaces or shell metacharacters will be split/interpreted when rendered is executed, so scriptCommand should shell-escape the path and a fixture should cover such a filename.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/template/pre_hook.go` around lines 185 - 208, The scriptCommand
function currently returns unescaped paths, allowing spaces and shell
metacharacters in hook filenames to be split or interpreted when rendered
through sh -c. Update scriptCommand to shell-escape the generated scriptPath for
both executable and sh command forms, and add a fixture covering a filename with
spaces or shell metacharacters.

139-143: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Return Flush errors from hook output. flushWriter drops hookTreeWriter.Flush() failures, so a sink error on the final buffered line can be lost while the hook still returns success. Propagate that error from both call sites.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/template/pre_hook.go` around lines 139 - 143, Update both hook
output flush call sites around c.Run() to return errors from flushWriter instead
of discarding them. Change flushWriter to propagate hookTreeWriter.Flush()
failures, and ensure the pre- and post-hook paths return that error while
preserving the existing command-error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/template/pre_hook.go`:
- Around line 185-208: The scriptCommand function currently returns unescaped
paths, allowing spaces and shell metacharacters in hook filenames to be split or
interpreted when rendered through sh -c. Update scriptCommand to shell-escape
the generated scriptPath for both executable and sh command forms, and add a
fixture covering a filename with spaces or shell metacharacters.
- Around line 139-143: Update both hook output flush call sites around c.Run()
to return errors from flushWriter instead of discarding them. Change flushWriter
to propagate hookTreeWriter.Flush() failures, and ensure the pre- and post-hook
paths return that error while preserving the existing command-error handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c2978d1-4b4c-4eec-b016-3fe8c5ecc471

📥 Commits

Reviewing files that changed from the base of the PR and between 42fae93 and e4e528c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • cmd/hooks_tui_test.go
  • cmd/new_prompts.go
  • go.mod
  • internal/template/pre_hook.go
💤 Files with no reviewable changes (1)
  • cmd/new_prompts.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • go.mod
  • cmd/hooks_tui_test.go

coderabbitai Bot and others added 2 commits July 22, 2026 04:26
Fixed 2 file(s) based on 2 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@sam0uly
sam0uly merged commit 1f5a7bc into main Jul 22, 2026
3 checks passed
@sam0uly
sam0uly deleted the feat/hooks-tui branch July 27, 2026 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant