Skip to content

Conversation

@chronark
Copy link
Collaborator

to be used in a nice seed util and probably our cli

to be used in a nice seed util and probably our cli
@vercel
Copy link

vercel bot commented Jan 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dashboard Ready Ready Preview, Comment Feb 4, 2026 3:18pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
engineering Ignored Ignored Preview Feb 4, 2026 3:18pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

Adds a new interactive terminal prompt package (pkg/prompt) with String/Int/Float/Select/MultiSelect prompts, Date/Time/DateTime pickers, human-friendly number/date parsing, a Wizard wrapper, comprehensive tests and examples, and Bazel + Go module changes to depend on golang.org/x/term.

Changes

Cohort / File(s) Summary
Dependency Management
MODULE.bazel, go.mod
Added org_golang_x_term to MODULE.bazel and promoted/added golang.org/x/term v0.39.0 as a direct dependency in go.mod.
Core Prompt Implementation
pkg/prompt/prompt.go, pkg/prompt/ansi.go, pkg/prompt/parse.go
New Prompt type, I/O options, String/Int/Float/Select/MultiSelect implementations; ANSI utilities and cursor control; human-readable integer/float parsing with suffixes. Terminal raw-mode handling and escape-sequence parsing included.
Date/Time Features
pkg/prompt/datetime.go, pkg/prompt/datetime_parse.go
Interactive Date, Time, DateTime pickers (calendar and spinner UIs); robust parsing helpers for relative/ISO/US/European dates and multiple time formats; rendering/state helpers and input handling in raw terminal mode.
Wizard Wrapper
pkg/prompt/wizard.go
Wizard type wrapping Prompt for multi-step flows with progress prefix, Skip/Done, and wrapper methods delegating to Prompt and advancing on success.
Documentation
pkg/prompt/doc.go
Package documentation describing prompts, behaviors, terminal expectations, examples, and dependencies.
Build Configuration
pkg/prompt/BUILD.bazel, pkg/prompt/example/*/BUILD.bazel
Bazel BUILD files added for the prompt library and example binaries (date, time, datetime, select, multiselect, wizard, main). Note: pkg/prompt library depends on @org_golang_x_term//:term.
Examples
pkg/prompt/example/*/main.go
Multiple example programs added demonstrating Date, Time, DateTime, Select, MultiSelect, Wizard and a combined example.
Tests
pkg/prompt/*_test.go, pkg/prompt/datetime_parse_test.go, pkg/prompt/parse_test.go
Extensive tests added covering parsing, prompt behaviors (TTY/non-TTY paths), rendering, datetime parsing, wizard progression, and many edge cases. Large test surface added.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User as User
participant Prompt as Prompt (pkg/prompt)
participant Term as Terminal (golang.org/x/term)
participant Render as Renderer (ANSI utils)
User->>Prompt: invoke Date/Time/Select prompt
Prompt->>Term: enable raw mode, hide cursor
Prompt->>Render: initial render (calendar/spinner/menu)
loop user interaction
User->>Term: key press (arrows, enter, tab, ctrl+c)
Term->>Prompt: deliver key/event
Prompt->>Render: update view based on input
Render->>User: redraw ANSI output
end
Prompt->>Term: restore terminal, show cursor
Prompt->>User: return selected value or error

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning PR description is incomplete; it lacks required sections from the template including 'What does this PR do', 'Type of change', 'How should this be tested', and most checklist items are unchecked. Complete the PR description using the provided template: add a detailed summary of the prompt package functionality, mark the appropriate type of change (e.g., 'New feature'), document testing instructions, and address all required checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 26.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive Title is vague and overly generic, using only 'pkg/prompt' without describing the actual functionality or the main purpose of the feature being added. Use a more descriptive title that explains what the prompt package does, e.g., 'feat: add interactive prompt package for terminal UI interactions' or 'feat: add Date, Time, and DateTime picker prompts'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pkg/prompt

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Command failed


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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@pkg/prompt/datetime_parse.go`:
- Around line 150-213: The parser currently ignores seconds in "HH:MM:SS" and
allows invalid 12-hour inputs like "13pm" or "0am"; update the parsing in the
function handling time strings to explicitly handle three-part times (when
strings.Contains(s, ":") and split yields 3 parts) by parsing seconds,
validating seconds in 0–59, converting the hour through convert12To24, and
returning an error on any out-of-range component; additionally, when isAM or
isPM is set (detected by suffix handling for "am"/"pm"/"a"/"p"), enforce 12-hour
source validation so the raw hour before convert12To24 must be in 1–12 (reject 0
or >12) and apply the same stricter checks in the other similar parsing branch
referenced (the block around lines 218-226) so lone-hour parses with AM/PM also
reject invalid 12-hour values.
- Around line 50-76: The parsed month/day/year from the "/" and "." branches are
not validated before calling time.Date (which silently normalizes invalid
dates); after converting with strconv.Atoi in both blocks (the parts ->
month/day/year variables), validate month is 1..12 and day is >=1 and <=
daysInMonth(year, month) (accounting for leap years) and only then call
time.Date; if validation fails, return a parsing error. Add or reuse a small
helper (e.g., daysInMonth(year, month)) and apply this check in both the
slash-parsing and dot-parsing branches so malformed inputs like 13/40/2024 are
rejected instead of normalized.

In `@pkg/prompt/parse.go`:
- Around line 3-7: The integer parsing paths can overflow because you convert or
flip sign before validating bounds: in the decimal-suffix branch
convert/checking `result` to int64 only after potential overflow, and in the
integer-suffix branch the sign-flip check using `base`/`multiplier` is
incomplete (negatives with positive multiplier can overflow undetected). Fix
both by performing bounds-first checks: in the decimal path validate that the
unsigned/unsigned-accumulated value is within int64 limits before casting to
int64 (do not cast `result` early), and in the integer path use
pre-multiplication overflow checks using `base` and `multiplier` (e.g., ensure
`abs(result) <= maxInt/abs(multiplier)` or equivalent) before multiplying or
flipping sign so no operation can trigger undefined overflow; update the
branches that reference `result`, `base`, and `multiplier` accordingly.

In `@pkg/prompt/prompt.go`:
- Around line 158-162: The code builds a slice of map keys from options which
yields non-deterministic ordering; to fix, sort the keys before iterating so
Select and MultiSelect render options in a stable order. In the prompt functions
that build keys (the snippet creating keys := make([]string, 0, len(options))
and appending map keys) add sort.Strings(keys) (import "sort") before using the
slice, and apply the same change to the other occurrence that constructs keys
(the block also referenced for lines 248-251); ensure any logic that maps
defaultKey or indices uses the sorted keys so default selection remains correct.
🧹 Nitpick comments (12)
MODULE.bazel (1)

89-89: Consider moving org_golang_x_term to be with other org_golang_x_* entries.

The org_golang_x_term entry is placed after the linter dependencies section, but other org_golang_x_* packages (org_golang_x_net, org_golang_x_sync, org_golang_x_text, org_golang_x_tools) are grouped together at lines 78-81. For consistency and maintainability, consider moving this entry to that group.

pkg/prompt/ansi.go (1)

64-69: Consider adding a guard for negative inputs to itoa.

The comment correctly notes this is for "small positive integers," but negative input would cause infinite recursion (stack overflow). Since this is internal and only used for cursor counts, it's low risk, but a simple guard would make it defensive.

🛡️ Optional defensive guard
 func itoa(n int) string {
+	if n < 0 {
+		return "0" // or handle error appropriately
+	}
 	if n < 10 {
 		return string(rune('0' + n))
 	}
 	return itoa(n/10) + string(rune('0'+n%10))
 }
pkg/prompt/parse.go (1)

45-118: Prefer fault for structured errors in parsing helpers.
These new helpers return many raw fmt.Errorf/strconv errors; the codebase guideline prefers fault.Wrap()/fault.Public() for consistent error semantics.

As per coding guidelines, consider switching these returns to fault-based errors.

Also applies to: 139-172

pkg/prompt/datetime_parse.go (1)

21-215: Prefer fault for structured errors in date/time parsing.
There are many fmt.Errorf returns; the repo guideline prefers fault.Wrap()/fault.Public() for consistent error handling.

As per coding guidelines, consider switching these returns to fault-based errors.

pkg/prompt/parse_test.go (1)

205-213: Add build tags to skip 32-bit platform tests or document 64-bit-only requirement.

The test values (int64 max/min) exceed 32-bit int bounds and would cause the overflow check to return an error on 32-bit systems, making these tests fail on 386 and ARM architectures. While the function correctly uses platform-dependent maxInt/minInt bounds, the test itself is locked to 64-bit values.

Consider either adding //go:build amd64 || arm64 || (other 64-bit archs) to skip on 32-bit platforms, or document that this package requires 64-bit systems.

pkg/prompt/datetime_parse_test.go (1)

10-18: Stabilize tests that depend on time.Now().

ParseDate/ParseTime/ParseDateTime call time.Now() internally while the tests snapshot time.Now() separately; this can flake at minute/day boundaries. Consider a nowFn test hook in datetime_parse.go or loosen assertions to accept a before/after window.

✅ Example tweak for the “now” time test
-		now := time.Now()
-		hour, minute, err := ParseTime("now")
-		require.NoError(t, err)
-		require.Equal(t, now.Hour(), hour)
-		require.Equal(t, now.Minute(), minute)
+		before := time.Now()
+		hour, minute, err := ParseTime("now")
+		after := time.Now()
+		require.NoError(t, err)
+		require.True(t,
+			(hour == before.Hour() && minute == before.Minute()) ||
+				(hour == after.Hour() && minute == after.Minute()),
+			"parsed time should match before/after snapshot",
+		)

Also applies to: 140-146, 277-315

pkg/prompt/wizard_test.go (1)

260-311: Consider using a helper or option pattern instead of accessing unexported fields.

Direct manipulation of wiz.prompt.in works because tests are in the same package, but it couples tests to internal implementation details. Consider adding a test helper or using the existing WithReader option to configure new prompts for each step.

♻️ Suggested approach
 func TestWizardMultipleSteps(t *testing.T) {
 	t.Run("completes full workflow", func(t *testing.T) {
 		out := &bytes.Buffer{}
+		// Use a combined reader or reset via public API if available
 
 		in1 := strings.NewReader("step1\n")
 		p := New(WithReader(in1), WithWriter(out))
 		wiz := p.Wizard(3)
 
 		r1, err := wiz.String("First")
 		require.NoError(t, err)
 		require.Equal(t, "step1", r1)
 		require.Equal(t, 1, wiz.Current())
 
-		in2 := strings.NewReader("step2\n")
-		wiz.prompt.in = in2
+		// Alternative: Create a custom io.Reader that can be fed sequentially
+		// or accept this as test-only internal access
pkg/prompt/datetime.go (3)

246-305: Consider extracting shared input handling to reduce nesting complexity.

The static analysis tool flagged this block with complexity 30 due to deep nesting. The input handling patterns for date navigation, time adjustment, and focus switching are repeated across Date, Time, and DateTime methods.

Consider extracting the common patterns:

  1. A helper for date navigation (arrow keys, PgUp/PgDn, Home/End)
  2. A helper for time adjustment (hour/minute with focus)
  3. Common key handling for Enter/Ctrl+C

This would reduce duplication and make each picker easier to maintain.


337-386: Opportunity to reduce duplication in calendar rendering.

The calendar grid rendering logic in renderCalendar (lines 347-376) and renderDateTimePicker (lines 437-470) is nearly identical, differing only in the color treatment based on focus state. Consider extracting a shared helper that accepts a focus parameter.

♻️ Suggested helper extraction
// renderCalendarGrid generates the day grid portion of a calendar.
// If dimmed is true, uses dim colors for non-selected days.
func (p *Prompt) renderCalendarGrid(viewMonth, selected time.Time, focused bool) []string {
    // Shared logic for grid generation with color determined by focused flag
}

Also applies to: 414-470


104-107: Consider using a sentinel error for "interrupted" instead of fmt.Errorf.

The "interrupted" error is created with fmt.Errorf in three places. Consider defining a package-level sentinel error for consistent error checking by callers.

♻️ Suggested change
// At package level (e.g., in prompt.go or errors.go)
var ErrInterrupted = errors.New("interrupted")

// Then in each handler:
return time.Time{}, ErrInterrupted

Also applies to: 187-190, 321-324

pkg/prompt/wizard.go (1)

48-73: Consider adding bounds validation to Skip and advance.

Both Skip() and advance() increment current without checking if it exceeds total. While this may be acceptable if callers are expected to track completion, it could lead to confusing progress indicators (e.g., [●●●●●] with 5 dots for a 3-step wizard).

♻️ Suggested bounds check
 func (w *Wizard) Skip() {
+	if w.current < w.total {
 		w.current++
+	}
 }

 func (w *Wizard) advance() {
+	if w.current < w.total {
 		w.current++
+	}
 }
pkg/prompt/prompt.go (1)

221-224: Consider a sentinel error for "interrupted".

Similar to the suggestion for datetime.go, the "interrupted" error is created with fmt.Errorf. A package-level ErrInterrupted would allow callers to check for interrupts with errors.Is().

Also applies to: 331-334

Copy link
Member

@Flo4604 Flo4604 left a comment

Choose a reason for hiding this comment

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

Nice examples

@Flo4604 Flo4604 merged commit 39ce6e8 into main Feb 4, 2026
13 checks passed
@Flo4604 Flo4604 deleted the pkg/prompt branch February 4, 2026 15:21
Flo4604 added a commit that referenced this pull request Feb 5, 2026
* feat: pkg/prompt

to be used in a nice seed util and probably our cli

* [autofix.ci] apply automated fixes

---------

Co-authored-by: Flo <53355483+Flo4604@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
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.

3 participants