Skip to content

Conversation

@KRRT7
Copy link
Collaborator

@KRRT7 KRRT7 commented Dec 21, 2025

PR Type

Enhancement, Other


Description

  • Replace Inquirer with Textual prompts

  • Add themed prompt wrapper module

  • Update CLI flows to new API

  • Adjust dependencies and mypy config


Diagram Walkthrough

flowchart LR
  Inquirer["inquirer-based prompts"] -- replaced by --> ThemedPrompts["themed_prompts (inquirer-textual)"]
  ThemedPrompts -- used in --> CmdInit["cmd_init.py flows"]
  PyProject["pyproject.toml deps"] -- add/remove --> InquirerTextual["inquirer-textual"], RemoveInquirer["remove inquirer"]
  MypyCfg["mypy overrides"] -- cleanup --> RemoveInquirer
Loading

File Walkthrough

Relevant files
Enhancement
cli_common.py
Remove legacy inquirer helpers from CLI common                     

codeflash/cli_cmds/cli_common.py

  • Remove inquirer utilities and wrapping helpers
  • Keep only exit helper and console usage
+0/-82   
cmd_init.py
Port init workflow to inquirer-textual prompts                     

codeflash/cli_cmds/cmd_init.py

  • Replace inquirer prompts with themed_prompts APIs
  • Add Choice usage for formatter selection
  • Add path existence checks for text inputs
  • Remove custom inquirer theme and wrappers
+74/-156
themed_prompts.py
Add themed prompt wrapper around inquirer-textual               

codeflash/cli_cmds/themed_prompts.py

  • Introduce CodeflashThemedApp with custom CSS/theme
  • Provide select, confirm, text, checkbox helpers
  • Wrap inquirer-textual widgets with inline app
+94/-0   
Dependencies
pyproject.toml
Update dependencies and mypy config for new prompts           

pyproject.toml

  • Remove inquirer dependency
  • Add inquirer-textual dependency
  • Update mypy ignore list to drop inquirer
+2/-2     

@KRRT7 KRRT7 requested a review from misrasaurabh1 December 21, 2025 05:15
@github-actions github-actions bot changed the title no more inquirer no more inquirer Dec 21, 2025
@github-actions
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Type Handling

In multiple prompt flows, the returned value may be a Choice or a raw string. Ensure consistent handling and typing (especially when accessing .value or .value.data) to avoid runtime errors if the prompt returns unexpected types.

formatter_choices = [
    Choice("⚫ black", data="black"),
    Choice("⚡ ruff", data="ruff"),
    Choice("🔧 other", data="other"),
    Choice("❌ don't use a formatter", data="don't use a formatter"),
]

result = prompts.select("Which code formatter do you use?", choices=formatter_choices, default=formatter_choices[0])
if result.command is None:
    apologize_and_exit()
formatter = result.value.data if isinstance(result.value, Choice) else result.value

git_remote = ""
Exit Flow Consistency

Several prompts treat result.command is None as a cancel and immediately exit. Verify this matches desired UX in all places (e.g., module/tests selection, GH Actions setup) and won’t surprise users by exiting instead of re-prompting.

default_choice = project_name if project_name in module_subdir_options else module_subdir_options[0]
result = prompts.select(
    "Which Python module do you want me to optimize?", choices=module_subdir_options, default=default_choice
)
if result.command is None:
    apologize_and_exit()
module_root_answer = result.value
if module_root_answer == curdir_option:
API Stability

The wrapper relies on inquirer_textual internal widget classes and return structure. Confirm these APIs and the .run(inline=True) return shape (command/value) are stable across versions to prevent breakages.

def select(  # noqa: ANN201
    message: str, choices: list[str | Choice], default: str | Choice | None = None
):  # type: ignore[no-untyped-def]
    widget = InquirerSelect(message, choices, default, mandatory=True)
    app: CodeflashThemedApp = CodeflashThemedApp(widget, shortcuts=None, show_footer=False)
    return app.run(inline=True)


def confirm(message: str, *, default: bool = False):  # noqa: ANN201  # type: ignore[no-untyped-def]
    widget = InquirerConfirm(message, default=default, mandatory=True)
    app: CodeflashThemedApp = CodeflashThemedApp(widget, shortcuts=None, show_footer=False)
    return app.run(inline=True)


def text(message: str):  # noqa: ANN201  # type: ignore[no-untyped-def]
    widget = InquirerText(message)
    app: CodeflashThemedApp = CodeflashThemedApp(widget, shortcuts=None, show_footer=False)
    return app.run(inline=True)


def checkbox(  # noqa: ANN201
    message: str, choices: list[str | Choice], enabled: list[str | Choice] | None = None
):  # type: ignore[no-untyped-def]
    widget = InquirerCheckbox(message, choices, enabled)
    app: CodeflashThemedApp = CodeflashThemedApp(widget, shortcuts=None, show_footer=False)
    return app.run(inline=True)

@github-actions
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Use boolean confirm instead of select

Use a proper boolean confirm prompt to avoid fragile string comparisons and
localization issues. Replace the select with prompts.confirm and branch on its
boolean value.

codeflash/cli_cmds/cmd_init.py [573-577]

-result = prompts.select("Create pyproject.toml in the current directory?", choices=["Yes", "No"], default="Yes")
-if result.command is None or result.value == "No":
+result = prompts.confirm("Create pyproject.toml in the current directory?", default=True)
+if result.command is None or result.value is False:
     apologize_and_exit()
 create_empty_pyproject_toml(pyproject_toml_path)
Suggestion importance[1-10]: 7

__

Why: Replacing a Yes/No select with a boolean confirm reduces string-comparison fragility and improves UX; the improved_code aligns with the PR’s new prompt framework (prompts.confirm) and keeps behavior consistent.

Medium
Normalize select default and value handling

Ensure the default passed to select matches the widget’s expected type. Pass the
underlying value (label or Choice) consistently and avoid mixing plain strings and
Choice objects to prevent selection mismatch.

codeflash/cli_cmds/cmd_init.py [473-484]

 formatter_choices = [
     Choice("⚫ black", data="black"),
     Choice("⚡ ruff", data="ruff"),
     Choice("🔧 other", data="other"),
     Choice("❌ don't use a formatter", data="don't use a formatter"),
 ]
 
-result = prompts.select("Which code formatter do you use?", choices=formatter_choices, default=formatter_choices[0])
+default_choice = formatter_choices[0]
+result = prompts.select("Which code formatter do you use?", choices=formatter_choices, default=default_choice)
 if result.command is None:
     apologize_and_exit()
-formatter = result.value.data if isinstance(result.value, Choice) else result.value
+formatter = result.value.data
Suggestion importance[1-10]: 4

__

Why: Passing a Choice as default is already done and supported; forcing formatter = result.value.data may be slightly cleaner but the current isinstance guard is safe, making the improvement minor.

Low
General
Validate non-empty choices

Guard against empty choices to prevent runtime errors inside the widget and provide
a clear failure path. Validate input early and raise a helpful error message.

codeflash/cli_cmds/themed_prompts.py [69-75]

 def select(  # noqa: ANN201
     message: str, choices: list[str | Choice], default: str | Choice | None = None
 ):  # type: ignore[no-untyped-def]
+    if not choices:
+        raise ValueError("select() requires at least one choice")
     widget = InquirerSelect(message, choices, default, mandatory=True)
     app: CodeflashThemedApp = CodeflashThemedApp(widget, shortcuts=None, show_footer=False)
     return app.run(inline=True)
Suggestion importance[1-10]: 6

__

Why: Adding an early check for empty choices prevents runtime errors and clarifies failures; it's a small but useful robustness improvement that accurately updates the provided code block.

Low

@KRRT7 KRRT7 changed the title no more inquirer no more inquirer or clicker Dec 21, 2025
@codeflash-ai
Copy link
Contributor

codeflash-ai bot commented Dec 22, 2025

⚡️ Codeflash found optimizations for this PR

📄 55% (0.55x) speedup for RelativePathValidator.validate in codeflash/cli_cmds/validators.py

⏱️ Runtime : 3.22 milliseconds 2.08 milliseconds (best of 59 runs)

A dependent PR with the suggested changes has been created. Please review:

If you approve, it will be merged into this PR (branch inquirer).

Static Badge

@claude
Copy link

claude bot commented Dec 23, 2025

PR Review: Replace inquirer with inquirer-textual

Thanks for this substantial refactoring! This modernizes the CLI experience and improves code organization. Overall, this is a great improvement, but I've identified some security issues and areas for enhancement.


🔴 Critical Issues (Must Fix Before Merge)

1. API Key Exposure in Error Messages

File: codeflash/cli_cmds/validators.py:59

The APIKeyValidator shows invalid API keys in full in error messages. This could leak keys to logs, terminal history, or screenshots.

Fix: Never display user-provided secrets in error messages.

2. Path Traversal Vulnerability

File: codeflash/cli_cmds/validators.py:22-27

The PathExistsValidator doesn't prevent malicious paths like ../../../../etc/passwd. Users could probe filesystem structure outside the project directory.

Fix: Add explicit path traversal checks to ensure paths are relative and don't contain parent directory references.

3. Empty Git Remotes Edge Case

File: codeflash/cli_cmds/cmd_init.py (~line 170)

When len(git_remotes) == 0, the code sets git_remote = "", which may cause issues downstream. Handle the no-remotes case explicitly.


🟡 High Priority Issues

4. Type Safety: Missing Return Type Annotations

File: codeflash/cli_cmds/themed_prompts.py (lines 91-182)

All functions have disabled type checking (noqa: ANN201, type: ignore). This reduces IDE support and type safety.

5. Lost Stack Traces in Error Handling

File: codeflash/cli_cmds/cli.py:255

Changed from logger.exception() to logger.error(), losing stack traces for debugging.

6. Overly Broad Exception Handling

File: codeflash/cli_cmds/validators.py:184

Using bare 'except Exception' could hide security-relevant or unexpected errors.


🟢 Medium Priority Issues

  1. Insufficient Test Coverage - Missing tests for collect_config(), process_module_root_answer(), cancellation scenarios
  2. Hard-coded Retry Count - github_utils.py:94 should use a constant
  3. Dependency Version Pinning - Add upper bound to inquirer-textual
  4. Circular Import Workaround - themed_prompts.py imports inside functions

✅ Strengths

  1. Clean Separation of Concerns - validators, prompts, and business logic well-separated
  2. Better UX - single-page forms show all questions at once
  3. Comprehensive Validators - 7 validators with good error messages
  4. Modern TUI Experience - inquirer-textual provides better visual feedback
  5. Reduced Dependencies - 2 libraries → 1
  6. Immutable Data Structures - ConfigAnswers uses frozen=True
  7. Graceful Error Handling - new helper functions centralize error handling

📊 Overall Assessment

This is a substantial improvement that modernizes the CLI with better organization and UX. The refactoring is well-executed, but security issues must be addressed before merging.

Recommendation: Approve with required changes

Before Merging:

  1. Fix API key exposure
  2. Add path traversal checks
  3. Handle empty git remotes case

Follow-up (can be separate PR):

  1. Add return type annotations
  2. Improve test coverage
  3. Pin inquirer-textual version with upper bound

Great work on this refactoring! The new architecture is much cleaner and more maintainable. 🚀

@claude
Copy link

claude bot commented Dec 23, 2025

Code Review: PR #980 - Replace inquirer with inquirer-textual

Thanks for this refactoring! The migration to inquirer-textual improves the CLI experience. However, I've identified several issues that should be addressed.


🔴 Critical Issues - Must Fix

1. Missing Return Type Annotations (themed_prompts.py)

All functions suppress return type warnings with # noqa: ANN201. This defeats type safety:

# Current (problematic):
def select(message: str, choices: list[str | Choice], ...):  # noqa: ANN201

# Should be:
def select(message: str, choices: list[str | Choice], ...) -> InquirerResult:

Impact: Makes API unclear to users, prevents type checking from catching bugs.


2. Unsafe Exception Handling (validators.py:85)

except Exception:  # noqa: S110
    pass

Issue: Silently swallows ALL exceptions including KeyboardInterrupt, SystemExit.
Fix: Catch specific exceptions:

except (OSError, ValueError, RuntimeError) as e:
    logger.debug(f"Path resolution failed: {e}")

3. Path Separator Bug (cmd_init.py:107)

create_for_me_option = f"🆕 Create a new tests{os.pathsep} directory for me!"

Bug: Uses os.pathsep (';' on Windows, ':' on Unix) instead of os.sep for directory separator.
Fix: f"🆕 Create a new tests{os.sep} directory for me!"


4. KeyboardInterrupt Not Propagated (github_utils.py:105)

except (KeyboardInterrupt, EOFError):
    console.print()  # Clean line for next prompt

Bug: Catches Ctrl+C but doesn't exit or re-raise. User can't cancel the operation.
Fix: Add raise or call apologize_and_exit()


5. Fragile Result Unpacking (cmd_init.py:161-165)

if has_multiple_remotes:
    module_root_answer, tests_root_answer, formatter_answer, enable_telemetry, git_remote = result.value
else:
    module_root_answer, tests_root_answer, formatter_answer, enable_telemetry = result.value

Risk: No validation that result.value has correct number of elements. Will raise ValueError if structure changes.
Fix: Use explicit indexing or a dataclass with named fields.


🟡 Important Issues - Should Fix

6. Type Checker Workarounds

Multiple places have unreachable code for type checker:

if not api_key:
    apologize_and_exit()
    return  # unreachable, satisfies type checker

Fix: Add -> NoReturn type hint to apologize_and_exit():

def apologize_and_exit() -> NoReturn:
    console.rule()
    # ...
    sys.exit(1)

7. Missing Subprocess Timeout (cmd_init.py:989-997)

with subprocess.Popen(command, text=True, cwd=args.module_root, ...) as process:

Issue: No timeout. Could hang indefinitely.
Fix: Add timeout parameter or use communicate(timeout=300)


8. Unsafe Directory Creation (cmd_init.py:428)

tests_root.mkdir()

Issue: Will raise FileExistsError if directory already exists.
Fix: tests_root.mkdir(parents=True, exist_ok=True)


9. Path Operation Without Error Handling (cmd_init.py:175)

tests_root = tests_root.relative_to(curdir)

Issue: Can raise ValueError if path is not relative.
Fix: Wrap in try/except with proper error message.


10. Insufficient Test Coverage

Missing tests:

  • ❌ All of themed_prompts.py (0 tests)
  • ❌ Most validators: PathExistsValidator, APIKeyValidator, NotEqualPathValidator, DirectoryOrCreatableValidator, TomlFileValidator
  • ❌ New install_github_app() function
  • ❌ Error/cancellation paths

Recommendation: Add tests for at least:

def test_api_key_validator_rejects_invalid_prefix()
def test_api_key_validator_allows_empty_for_browser_flow()
def test_path_exists_validator_with_nonexistent_path()
def test_select_or_exit_calls_exit_on_cancel()

🟢 Nice to Have

  1. Extract CSS - The 50+ line CSS string in CodeflashThemedApp makes testing difficult. Consider extracting to a constant.

  2. Add Retry Delays (github_utils.py:88-104) - Immediately retries without backoff could hammer the API.

  3. Reduce API Key Exposure - Keys printed to console (cmd_init.py:202) could be captured in logs. Consider masking more characters.

  4. Break Up Large Functions - init_codeflash() is 200+ lines, install_github_actions() is 110+ lines. These violate single responsibility principle.


✅ Positive Changes

  • Clean separation of validators into dedicated module
  • Removal of overly complex text-wrapping logic
  • Good use of dataclasses for configuration
  • Improved UX with themed prompts

📊 Overall Assessment

Risk Level: Medium

The refactoring successfully removes the inquirer dependency and introduces cleaner architecture. However, the lack of return type annotations, unsafe exception handling, and the os.pathsep bug need to be addressed before merge. The missing test coverage is also concerning.

Recommendation: Fix critical issues #1-5, add basic test coverage, then merge. Nice-to-haves can be addressed in follow-up PRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants