Skip to content

feat(cli-component): Enhance CLI interface with new components and ESLint configuration#3

Merged
jeancdevx merged 14 commits into
developfrom
feature/cli-component-architecture
Jun 3, 2026
Merged

feat(cli-component): Enhance CLI interface with new components and ESLint configuration#3
jeancdevx merged 14 commits into
developfrom
feature/cli-component-architecture

Conversation

@jeancdevx

@jeancdevx jeancdevx commented Jun 3, 2026

Copy link
Copy Markdown
Owner

This pull request introduces a new interactive command menu to the CLI, along with several UI improvements and supporting utilities. The main changes include adding the command menu component and its supporting logic, updating the input bar to integrate command selection, and enhancing the CLI's overall appearance with a header and status bar. There are also updates to ESLint configuration and type dependencies to support React code.

Command Menu Feature and UI Enhancements:

  • Added a fully functional command menu system, including the CommandMenu component, command definitions, filtering logic, keyboard navigation, and type definitions. This allows users to trigger commands via the input bar with / and navigate/execute them interactively. (packages/cli/src/components/command-menu/commands.tsx, filter-commands.ts, index.tsx, types.ts, use-command-menu.ts) [1] [2] [3] [4] [5]
  • Updated the InputBar component to integrate the command menu, handle command execution, and manage input state, including support for keyboard navigation and command actions. (packages/cli/src/components/input-bar.tsx)
  • Added a Header component with stylized ASCII art and a StatusBar component for improved CLI appearance. (packages/cli/src/components/header.tsx, status-bar.tsx) [1] [2]
  • Introduced border utilities (EmptyBorder, SplitBorder) for consistent UI styling. (packages/cli/src/components/border.tsx)
  • Updated the main CLI entry point to use the new components, set background color, and configure renderer options for a better user experience. (packages/cli/src/index.tsx)

Tooling and Dependency Updates:

  • Added @types/react to devDependencies to support React type checking in the CLI package. (packages/cli/package.json)
  • Updated ESLint configuration to include the eslint-plugin-react-hooks plugin and enabled rules for React hooks best practices. (eslint.config.mjs) [1] [2] [3]

Summary by CodeRabbit

New Features

  • Introduced command menu system with slash commands (/new, /agents, /models, /sessions, /theme, /login, /logout, /upgrade, /usage, /exit)
  • Redesigned CLI interface with header display, command-enabled input bar, and status bar
  • Added keyboard navigation and filtering for quick command access

Chores

  • Enhanced linting configuration for React hooks
  • Updated development dependencies

@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jeancdevx, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 33 minutes. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 788e5944-4ebe-43ea-8339-b62f19512d9b

📥 Commits

Reviewing files that changed from the base of the PR and between eb3bbbf and 3346830.

📒 Files selected for processing (2)
  • packages/cli/src/components/command-menu/use-command-menu.ts
  • packages/cli/src/components/input-bar.tsx
📝 Walkthrough

Walkthrough

This PR introduces a command menu system to the CLI application, enabling users to invoke slash commands (e.g., /new, /agents, /models, /sessions, /theme, /login, /logout, /upgrade, /usage, /exit) directly from the textarea input. The feature includes filtering, keyboard navigation, and integration with the main renderer.

Changes

Command Menu Feature

Layer / File(s) Summary
ESLint Configuration & Dependencies
eslint.config.mjs, packages/cli/package.json
Adds eslint-plugin-react-hooks with rules-of-hooks (error) and exhaustive-deps (warn) rules; adds @types/react dev dependency to CLI package.
Command Types & Definitions
packages/cli/src/components/command-menu/types.ts, packages/cli/src/components/command-menu/commands.tsx
Defines CommandContext with exit() method and Command interface; exports COMMANDS array with slash commands including /exit handler that triggers renderer destruction.
Command Filtering & State Management
packages/cli/src/components/command-menu/filter-commands.ts, packages/cli/src/components/command-menu/use-command-menu.ts
Implements getFilteredCommands() to filter by name prefix; implements useCommandMenu() hook managing text input, menu visibility, selection index, keyboard navigation (Escape, Up/Down with viewport sync), and command resolution.
Command Menu Component
packages/cli/src/components/command-menu/index.tsx
Renders scrollable CommandMenu displaying filtered commands with selection highlighting, row sizing, empty-state message, and mouse callbacks for selection and execution.
Layout & Display Components
packages/cli/src/components/border.tsx, packages/cli/src/components/header.tsx, packages/cli/src/components/status-bar.tsx
Defines EmptyBorder and SplitBorder configurations; adds Header component displaying "Night" and "Code" text; adds StatusBar component showing build info with styled separator.
Input Bar with Command Integration
packages/cli/src/components/input-bar.tsx
Implements InputBar component with textarea, TEXTAREA_KEY_BINDINGS for Enter/Shift+Enter handling, integrated CommandMenu above input, command execution (action callback or value insertion), and StatusBar rendering.
CLI Integration
packages/cli/src/index.tsx
Refactors App component to render Header and InputBar in nested box layouts; configures renderer with targetFps: 60 and exitOnCtrlC: false for command-driven exit.

Sequence Diagrams

sequenceDiagram
  participant User
  participant useCommandMenu
  participant getFilteredCommands
  participant CommandMenu as CommandMenu UI
  User->>useCommandMenu: Type text starting with /
  useCommandMenu->>getFilteredCommands: Extract query from /text
  getFilteredCommands-->>useCommandMenu: Return filtered commands
  useCommandMenu->>useCommandMenu: Show menu, reset selection to 0
  User->>useCommandMenu: Press Up/Down arrow
  useCommandMenu->>useCommandMenu: Update selectedIndex, scroll viewport
  User->>useCommandMenu: Press Enter on command
  useCommandMenu->>useCommandMenu: Resolve command at selectedIndex, close menu
Loading
sequenceDiagram
  participant User
  participant InputBar
  participant CommandMenu
  participant Command as Command Action
  participant Renderer
  User->>InputBar: Press Enter on selected command
  InputBar->>CommandMenu: Resolve selected command
  alt Command has action
    InputBar->>Command: Call action(ctx) with exit callback
    Command->>Renderer: ctx.exit() destroys renderer
  else Command is value
    InputBar->>InputBar: Insert command value + space into textarea
  end
  InputBar->>InputBar: Close command menu
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • jeancdevx/nightcode#1: Establishes the base ESLint configuration that this PR extends with React Hooks rules.

Poem

🐰 A rabbit hops through /commands so bright,
Filtering queries, keyboard control in sight,
Commands now flow from "/" to "exit",
The CLI menu arrives—hooray, let's try it!
React Hooks rules keep the logic tight. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary changes: adding new CLI components (Header, StatusBar, InputBar, CommandMenu) and updating ESLint configuration for React hooks. It is concise and directly reflects the main objectives.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/cli-component-architecture

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
packages/cli/src/components/command-menu/use-command-menu.ts (1)

37-37: ⚡ Quick win

Fix comment typos.

Minor typos in comments:

  • Line 37: "caracter" should be "character"
  • Line 49: "caller, handles" should be "caller handles" (remove comma)
📝 Proposed fixes
-    // jump back to top of list when the user types a new caracter
+    // jump back to top of list when the user types a new character
-  // resolve a command at a specific index (returns the command, caller, handles execution)
+  // resolve a command at a specific index (returns the command; caller handles execution)

Also applies to: 49-49

🤖 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 `@packages/cli/src/components/command-menu/use-command-menu.ts` at line 37, In
useCommandMenu update the inline comments to fix typos: change "caracter" to
"character" in the comment about jumping back to the top of the list, and remove
the stray comma so "caller, handles" becomes "caller handles"; locate these
comments inside the useCommandMenu implementation and edit the comment text
accordingly.
packages/cli/src/components/command-menu/index.tsx (1)

19-26: ⚡ Quick win

Single source of truth for the filtered list.

CommandMenu recomputes getFilteredCommands(query) even though useCommandMenu already derives filteredCommands from the same commandQuery and uses that list to drive selectedIndex, keyboard navigation, and resolveCommand. The two lists are equivalent today only because query === commandQuery; any future change to filtering inputs (or passing a different query) would silently desync the highlighted/executed index from what is rendered. Consider passing the already-computed list down as a prop instead of refiltering here.

🤖 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 `@packages/cli/src/components/command-menu/index.tsx` around lines 19 - 26,
CommandMenu is re-running getFilteredCommands(query) despite useCommandMenu
already providing the canonical filtered list (filteredCommands derived from
commandQuery), which can cause desyncs; update the component to accept the
precomputed filtered list as a prop (e.g. add filteredCommands to
CommandMenuProps) and use that prop instead of calling getFilteredCommands
inside CommandMenu, and ensure callers (the hook or parent that uses
useCommandMenu) pass useCommandMenu.filteredCommands so selectedIndex, keyboard
navigation, and resolveCommand remain in sync with the rendered list.
packages/cli/src/components/input-bar.tsx (1)

100-121: ⚡ Quick win

Move onSubmitRef.current assignment out of the render phase.

React guidance discourages mutating refs during render (except lazy init), since concurrent rendering can make ref mutations unpredictable; update onSubmitRef.current from an effect (or use useEffectEvent on React 19.2+) so the textarea handler always points at the committed/latest closure.

♻️ Move assignment into an effect
-  onSubmitRef.current = () => {
-    if (disabled) return
-
-    if (showCommandMenu) {
-      const command = resolveCommand(selectedIndex)
-      handleCommand(command)
-      return
-    }
-
-    handleSubmit()
-  }
+  useEffect(() => {
+    onSubmitRef.current = () => {
+      if (disabled) return
+
+      if (showCommandMenu) {
+        const command = resolveCommand(selectedIndex)
+        handleCommand(command)
+        return
+      }
+
+      handleSubmit()
+    }
+  }, [disabled, showCommandMenu, selectedIndex, resolveCommand, handleCommand, handleSubmit])
🤖 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 `@packages/cli/src/components/input-bar.tsx` around lines 100 - 121, The
current code sets onSubmitRef.current during render which mutates a ref in the
render phase; move the assignment into an effect so the ref update happens after
commit (or use useEffectEvent on React 19.2+). Specifically, stop assigning
onSubmitRef.current = () => { ... } during render and instead place that whole
assignment inside a useEffect (or create an effect-safe event via
useEffectEvent) so the textarea.onSubmit handler (wired in the existing
useEffect that reads textareRef.current) always calls the latest closure; make
sure the effect captures disabled, showCommandMenu, selectedIndex,
resolveCommand, handleCommand and handleSubmit dependencies or uses refs for
stable access.
🤖 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 `@packages/cli/src/components/command-menu/use-command-menu.ts`:
- Around line 50-53: The resolveCommand function uses the comma operator so
setShowCommandMenu(false) only runs when filteredCommands[index] is truthy;
change it to always close the menu before returning the command. Update
resolveCommand to call setShowCommandMenu(false) unconditionally (e.g., as a
separate statement) and then return filteredCommands[index] (which may be
undefined). Ensure you modify the resolveCommand function (referenced by name)
and keep filteredCommands usage intact.

In `@packages/cli/src/components/input-bar.tsx`:
- Around line 74-80: The call to command.action currently ignores returned
promises, causing unhandled rejections; update the invocation of command.action
so you capture its return value and handle async rejections: call
command.action({ exit: () => renderer.destroy() }), store the result, and if
it's a Promise attach .catch(err => { /* log and surface error to user (e.g.
console.error or render a message in the UI) */ }) (or await it if the
surrounding function can be made async), ensuring renderer.destroy() still runs
when exit() is invoked; keep textarea.insertText when command.action is absent.

In `@packages/cli/src/index.tsx`:
- Around line 26-29: The CLI disables Ctrl+C by setting exitOnCtrlC: false on
createCliRenderer, which prevents the universal interrupt from cleaning up;
either set exitOnCtrlC: true when constructing the renderer (in the
createCliRenderer call) or add a process SIGINT/SIGTERM handler that always
calls renderer.destroy() (ensure the handler references the same renderer
instance created in packages/cli/src/index.tsx and mirrors the cleanup already
invoked by ctx.exit() -> renderer.destroy() in input-bar.tsx).

---

Nitpick comments:
In `@packages/cli/src/components/command-menu/index.tsx`:
- Around line 19-26: CommandMenu is re-running getFilteredCommands(query)
despite useCommandMenu already providing the canonical filtered list
(filteredCommands derived from commandQuery), which can cause desyncs; update
the component to accept the precomputed filtered list as a prop (e.g. add
filteredCommands to CommandMenuProps) and use that prop instead of calling
getFilteredCommands inside CommandMenu, and ensure callers (the hook or parent
that uses useCommandMenu) pass useCommandMenu.filteredCommands so selectedIndex,
keyboard navigation, and resolveCommand remain in sync with the rendered list.

In `@packages/cli/src/components/command-menu/use-command-menu.ts`:
- Line 37: In useCommandMenu update the inline comments to fix typos: change
"caracter" to "character" in the comment about jumping back to the top of the
list, and remove the stray comma so "caller, handles" becomes "caller handles";
locate these comments inside the useCommandMenu implementation and edit the
comment text accordingly.

In `@packages/cli/src/components/input-bar.tsx`:
- Around line 100-121: The current code sets onSubmitRef.current during render
which mutates a ref in the render phase; move the assignment into an effect so
the ref update happens after commit (or use useEffectEvent on React 19.2+).
Specifically, stop assigning onSubmitRef.current = () => { ... } during render
and instead place that whole assignment inside a useEffect (or create an
effect-safe event via useEffectEvent) so the textarea.onSubmit handler (wired in
the existing useEffect that reads textareRef.current) always calls the latest
closure; make sure the effect captures disabled, showCommandMenu, selectedIndex,
resolveCommand, handleCommand and handleSubmit dependencies or uses refs for
stable access.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f21eb59-61de-4fef-905c-34c6c4ddeec4

📥 Commits

Reviewing files that changed from the base of the PR and between e413860 and eb3bbbf.

📒 Files selected for processing (12)
  • eslint.config.mjs
  • packages/cli/package.json
  • packages/cli/src/components/border.tsx
  • packages/cli/src/components/command-menu/commands.tsx
  • packages/cli/src/components/command-menu/filter-commands.ts
  • packages/cli/src/components/command-menu/index.tsx
  • packages/cli/src/components/command-menu/types.ts
  • packages/cli/src/components/command-menu/use-command-menu.ts
  • packages/cli/src/components/header.tsx
  • packages/cli/src/components/input-bar.tsx
  • packages/cli/src/components/status-bar.tsx
  • packages/cli/src/index.tsx

Comment thread packages/cli/src/components/command-menu/use-command-menu.ts Outdated
Comment thread packages/cli/src/components/input-bar.tsx
Comment thread packages/cli/src/index.tsx
@jeancdevx jeancdevx merged commit d4a1aa5 into develop Jun 3, 2026
1 check passed
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