Skip to content

feat(pair): huh forms for prompts (TTY-gated, legacy fallback)#21

Merged
ironystock merged 1 commit into
mainfrom
feat/cli-huh-pair-prompts
Apr 29, 2026
Merged

feat(pair): huh forms for prompts (TTY-gated, legacy fallback)#21
ironystock merged 1 commit into
mainfrom
feat/cli-huh-pair-prompts

Conversation

@ironystock
Copy link
Copy Markdown
Contributor

@ironystock ironystock commented Apr 28, 2026

Summary

Final phase of the Charmbracelet polish sprint (Phase 4 of `~/.claude/plans/peppy-forging-dahl.md`). Three pair-flow prompts now render through `huh` in real terminals; the legacy bufio implementations are preserved verbatim and still fire when stdout isn't a TTY.

Prompt TTY path Non-TTY path
`promptForServerURL` `huh.NewSelect` + `huh.NewInput` on "Other" `promptForServerURLLegacy` (numbered menu)
`promptForDefaultNamespace` `huh.NewSelect` with explicit skip option `promptForDefaultNamespaceLegacy` (numeric index)
`confirmFileMerge` (F24) `huh.NewConfirm` ("Yes, merge" / "No, skip") `confirmFileMergeLegacy` (`[Y/n]` bufio)

Gating logic

Each new wrapper:

```go
if !isTTY(out) {
return *Legacy(in, out, ...)
}
// ...huh form...
```

`isTTY` (added in `render.go` for Phase 3) returns false for any writer that isn't a `*os.File` — including the `*bytes.Buffer` and `io.Discard` that `pair_flow_test.go` uses. Result: every existing pair test exercises the legacy path and passes unchanged.

Why keep the legacy functions

Three reasons, all carry-forward from CLAUDE.md:

  1. Tests + scripted use need them. Gating preserves both UX modes. Removing the legacy path would mean either rewriting the test suite against a `pty` harness (overkill) or losing test coverage entirely (worse).
  2. The `pair_flow_test.go::TestPair_F24_*` tests are load-bearing and pin behavior the F24 issue depended on (Brad's dogfooding scenario where `.mcp.json` got clobbered silently).
  3. They're tiny (~25 lines each) and have stable behavior.

Verification

  • `go build ./...` clean.
  • `go test ./...` green. All 12 `TestPair_*` cases pass:
    • 4 F24 sub-tests (preflight, accept-on-yes, no-prompt-on-missing, default-yes-on-empty)
    • 4 server URL prompt sub-tests (default, choice 1, choice 2, pasted URL)
    • Namespace prompt + skip-on-empty
    • Hostname/sentinel client_name, server-returned name, persistence, write-per-config-target
  • Manual: `./bin/rememberize.exe pair ` in a real terminal will now show huh's arrow-key picker for server URL + namespace + the F24 confirm — same flow, prettier UI.
  • Manual non-TTY: `echo "" | ./bin/rememberize.exe pair ` falls through to the legacy bufio path (provable with `script`/`tee`/etc.).

Sprint complete

This closes the original 5-phase plan:

  • ✅ Phase 0 — refactor split (#7)
  • ✅ Phase 1 — leveled logger + flags (#10 part 1)
  • ✅ Phase 2 — fang wrap (#10 part 2)
  • ✅ Phase 3 — lipgloss tables (#18)
  • ✅ Phase 4 — this PR
  • ⏭ Phase 5 — tag `v0.1.0` (yours per AGENTS.md early-weeks rule)

Plus side-quests landed along the way: CI hardening (#11), branch protection ruleset, Blacksmith migration (#16), 4 dependabot bumps (#12-15), API key preflight (#19), styled install.sh (#20).

Test plan

  • CI green across the 6 required checks.
  • Manual smoke after merge in a real terminal: `rememberize pair ` shows the huh-styled flow.
  • If anything looks off in huh (cell padding, color contrast in your terminal theme), tweak in a follow-up before tagging.

🤖 Generated with Claude Code


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

Final phase of the Charmbracelet polish sprint. Three pair-flow
prompts now render through huh in real terminals; the legacy
bufio implementations are preserved verbatim and still fire when
stdout is not a TTY.

  promptForServerURL    -> huh.NewSelect + huh.NewInput on "Other"
  promptForDefaultNamespace -> huh.NewSelect with explicit skip option
  confirmFileMerge (F24) -> huh.NewConfirm("Yes, merge" / "No, skip")

Each new wrapper:

  if !isTTY(out) { return *Legacy(in, out, ...) }
  // ...huh form...

isTTY (added in render.go for Phase 3) returns false for any writer
that isn't a *os.File — including the *bytes.Buffer and io.Discard
that pair_flow_test.go uses. Result: the entire existing test suite
(TestPair_F24_*, TestPair_PromptsForServerWhenConfigEmpty,
TestPair_NamespacePromptSetsDefault, etc.) exercises the legacy path
and passes unchanged.

The legacy functions are intentionally not deleted. Three reasons:
- Tests + scripted use need them; gating preserves both UX modes.
- Removing them would mean either rewriting the tests against a pty
  harness (overkill) or losing test coverage entirely (worse).
- They're tiny (~25 lines each) and have stable behavior.

Closes the original 5-phase plan. Phases 0-3 already on main; this
is Phase 4. Phase 5 is the v0.1.0 tag — separate step, owned by Brad
per AGENTS.md early-weeks rule.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ironystock ironystock merged commit 6ee7ee8 into main Apr 29, 2026
12 of 13 checks passed
@ironystock ironystock deleted the feat/cli-huh-pair-prompts branch April 29, 2026 05:34
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