RFD 0004: Smart Interactive Mode via Flag Detection¶
Summary¶
This RFD documents the decision to make CLI interactive prompts detect when users provide command-line flags and automatically skip prompts, rather than requiring an explicit --non-interactive flag. This pattern provides better UX for both interactive users and scripted automation.
Problem Statement¶
Users reported that running commands like samuel init --template full test-project would hang indefinitely, only proceeding after pressing Ctrl+C. The CLI would prompt for confirmation even when all required values were provided via flags.
Observed behavior:
$ samuel init --template full my-project
? Do you want to proceed? (Y/n) # Hangs here despite --template being provided
Expected behavior:
$ samuel init --template full my-project
Initializing project with 'full' template... # Proceeds automatically
Root Cause Analysis¶
Three interactive prompts were triggering even when CLI flags were provided:
- Confirmation prompt - Always triggered regardless of flags
- Language selection prompt - Triggered when template wasn't "full"
- Framework selection prompt - Same issue
Additionally, promptui.Prompt with IsConfirm: true had quirky behavior where Enter on empty input returned ErrAbort instead of using the default value.
Background¶
CLI Interaction Modes¶
CLI tools typically support two modes:
- Interactive mode: Prompts user for input, used in terminals
- Non-interactive mode: Uses defaults or flags, used in scripts/CI
Common Patterns¶
Pattern 1: Explicit flag
Pattern 2: TTY detection
# Automatically detects if stdin is a terminal
if [ -t 0 ]; then
# Interactive
else
# Non-interactive
fi
Pattern 3: Flag detection (chosen)
Options Considered¶
Option A: Add Explicit --non-interactive Flag¶
Require users to add --non-interactive or -y flag to skip prompts.
Pros:
- Explicit and clear
- Common pattern (apt, npm, etc.)
- Simple implementation
Cons:
- Extra flag to remember
- Verbose for scripted use
- Users who provide all flags still need to add one more
Effort: Low
Option B: Track CLI-Provided Flags and Skip Prompts (Chosen)¶
Automatically detect when users provide CLI flags and skip related prompts.
Pros:
- Better UX - if user provided flags, they know what they want
- Works for both interactive and scripted use
- No extra flags to remember
- More intelligent behavior
Cons:
- Slightly more complex logic
- Implicit behavior (some may prefer explicit)
Effort: Low-Medium
Proposal¶
Chosen: Option B - Smart Flag Detection
Implementation¶
Track whether the user provided any CLI flags:
// Track if user provided CLI flags (skip prompts if so)
cliProvided := templateFlag != "" ||
len(languageFlags) > 0 ||
len(frameworkFlags) > 0
// Working variable for template name (may be set interactively)
templateName := templateFlag
Use !cliProvided as a condition for all interactive prompts:
// Only prompt if no CLI flags were provided
if !cliProvided {
// Show interactive language selection
selectedLangs, err = ui.SelectLanguages(availableLanguages)
}
// Only ask for confirmation if no CLI flags were provided
if !cliProvided {
confirmed, err := ui.Confirm("Proceed with initialization?", true)
}
promptui Quirk Fix¶
The promptui.Prompt with IsConfirm: true has a quirk where pressing Enter on empty input returns ErrAbort instead of using the default. Fixed by not using IsConfirm:
// Before (buggy)
prompt := promptui.Prompt{
Label: label,
IsConfirm: true, // Enter returns ErrAbort
}
// After (fixed)
prompt := promptui.Prompt{
Label: label + " (Y/n)",
Default: "y", // Default shown in prompt
}
result, _ := prompt.Run()
return strings.ToLower(result) == "y" || result == ""
Implementation Considerations¶
Affected Code¶
internal/commands/init.go- AddcliProvidedtrackinginternal/ui/prompts.go- FixConfirmfunction
Behavior Matrix¶
| Scenario | Behavior |
|---|---|
| No flags provided | Full interactive mode |
--template provided | Skip template prompt, still ask for languages |
--template + --languages | Skip all prompts, proceed directly |
| Any flag provided | Skip confirmation prompt |
Testing¶
# All should work without hanging
samuel init --template minimal test-dir
samuel init --template starter test-dir
samuel init --template full test-dir
samuel init --languages go,rust test-dir
# Interactive mode still works
samuel init test-dir # Prompts for all options
Security Considerations¶
No security implications. This is a UX improvement only.
Compatibility¶
- Breaking changes: None (behavior change is improvement)
- Migration path: N/A
- Backwards compatibility: Existing scripts that use
--templatewill now work better
Consequences¶
Positive¶
- Users can use
samuel init --template full <dir>without prompts - Interactive mode still works fully when no flags provided
- Ctrl+C properly cancels operations
- Enter on confirmation now correctly uses default value
- Better CI/script experience
Negative¶
- Users who wanted to provide partial flags and still be prompted won't get prompts
- Implicit behavior may surprise some users (though it matches expectations)
Pattern Established¶
This pattern can be reused for other commands that mix interactive and non-interactive modes:
// Pattern: Track CLI-provided flags
cliProvided := flag1 != "" || flag2 != "" || len(flags3) > 0
// Only prompt if user didn't provide flags
if !cliProvided {
// Interactive prompt
}
References¶
- Decision documented:
.claude/memory/2026-01-14-cli-interactive-mode-fix.md - promptui: github.com/manifoldco/promptui
- Related pattern:
.claude/patterns.md(CLI Flag Detection Pattern)