Skip to content

Conversation

@abhipatel12
Copy link
Collaborator

@abhipatel12 abhipatel12 commented Jul 3, 2025

TLDR

This PR implements a fundamental architectural refactor of the Gemini CLI's slash command system. It replaces the monolithic useSlashCommandProcessor with a scalable, extensible, and testable CommandService that manages a nested command tree.

This is the foundational first step in a multi-phase effort to enable user-defined commands and a more powerful automation platform, in a more broad, community driven vision. This PR delivers the core service, the new nested command structure, and migrates the first set of commands (/memory, /help, /clear) to the new system. It also includes a crucial legacy adapter to ensure all non-migrated commands remain fully functional during the transition.

Dive Deeper

The Problem

The previous command system, centered in useSlashCommandProcessor, was a single, massive hook that was difficult to maintain, test, and extend. Commands with sub-options (e.g., /memory show) were handled with brittle switch statements, and there was no path to allow users to define their own commands—a key strategic feature.

The Solution: A Service-Oriented, Nested Architecture

This refactor introduces a clean, modern architecture composed of three key pillars:

  1. The Nested SlashCommand Interface (types.ts): I've adopted a recursive data structure (inspired by @allenhutchison on Feature: Nested Slash Command Completion and Execution #2622 !!) where a command can contain subCommands. This allows us to model command hierarchies naturally (e.g., /memory is a parent to show, add, and refresh).

  2. The CommandService (CommandService.ts): This new, central service is now the single source of truth for all commands.

    • Discovery: It is responsible for loading command definitions. In this PR, it loads our built-in commands from their own modules (e.g., memoryCommand.ts). In the future, it will be extended to load commands from user-defined YAML files or any other format we choose to pursue!
    • Tree Construction: It assembles the flat list of definitions into the unified, nested command tree.
    • Provision: It provides this tree to the UI.

    This service is the central hub responsible for the entire command lifecycle, abstracting the UI from the command source.

    Architectural Flow Goals:

    +-------------------+      +---------------------+      +--------------------+
    |   Built-in cmds   |      | Local Command Files |      | MCP Server Prompts |
    | (TS Objects with  |      |  (.gemini.yml with  |      | (JSON definitions) |
    |  subCommands)     |      |   subCommands)      |      +--------------------+
    +-------------------+      +---------------------+               |
             |                        | (Discovers & Parses)         | (Discovers via API)
             | (Loads)                |                              |
             +------------------------+------------------------------+
                                      |
                          +---------------------------+
                          |      CommandService       |
                          | (Builds unified command   |
                          |           TREE)           |
                          +---------------------------+
                                      |
                                      | (Provides Tree)
                                      v
                        +-----------------------------+
                        |  CLI UI / useSlashProcessor |
                        | (Traverses tree to execute) |
                        +-----------------------------+
    

    This architecture is the key enabler for the phased rollout plan.

  3. The CommandContext Object (types.ts): I've established a single context object that is passed to every command's action. This acts as a dependency injection (DI) container, providing access to all necessary services (config, logger), UI actions (addItem, clear), and session data (stats) in a clean, explicit, and highly testable way. This eliminates the need for prop-drilling and makes individual command logic much easier to unit test in isolation.

The Legacy Adapter: Ensuring a Smooth Transition

To avoid a massive, all-or-nothing PR, I've implemented a legacy adapter pattern within useSlashCommandProcessor.

  • The handleSlashCommand function now first attempts to find and execute a command from the new CommandService tree using a tree-traversal algorithm.
  • If no command is found in the new system, it falls back to the old legacyCommands array, ensuring that commands not yet migrated (like /chat, /stats, /docs) continue to function perfectly.
  • This allows us to migrate the entire command system incrementally without any disruption to the user.

Scope of This PR

  • [✅] Introduction of the CommandService and the CommandContext DI container.
  • [✅] Definition of the new nested SlashCommand interface in types.ts.
  • [✅] Migration of /clear, /help, and the entire /memory command suite to the new modular, tree-based structure.
  • [✅] Refactoring of useSlashCommandProcessor to consume commands from the CommandService and include the legacy adapter.
  • [✅] Significant refactoring of the useCompletion hook, InputPrompt component, and <Help/> component to be fully aware of the nested command structure, providing intelligent, context-aware autocompletion and help display for sub-commands and their arguments.
  • [✅] New, comprehensive unit tests for the CommandService and all migrated commands, leveraging a new createMockCommandContext test utility for easy and consistent test setup.

Future Work (To Be Addressed in Follow-up PRs)

This PR lays the groundwork for the next phases! This are for discussion and I would love to get community feedback towards the direction we want to drive this. Some of these just encompass some ideas.

High-Priority Follow-up

  • Complete Command Migration: Systematically migrate all remaining commands from the legacyCommands array (/chat, /stats, /docs, /theme, /auth, etc.) into their own [commandName]Command.ts files using the new SlashCommand interface.
  • Remove Legacy Adapter: Once all commands are migrated, the legacyCommands array and the fallback logic will be removed entirely, simplifying useSlashCommandProcessor.

Medium-Priority: Custom Commands

  • YAML Parser: Enhance the CommandService to discover and parse user-defined .gemini.yml files from project and user directories.
  • Generic Action Runner: Implement the generic action that will execute prompts defined in YAML, handling argument substitution.

Medium-Priority: MCP Prompts

  • MCP Prompt Discovery: The CommandService will be extended to query connected MCP servers for available prompts using the prompts/list protocol endpoint.
  • Seamless Adaptation: Each discovered MCP Prompt will be automatically adapted and grafted into the command tree as a namespaced command (e.g., a create-ticket prompt from a jira server becomes the /jira:create-ticket command).
  • Unified Invocation: The action for these commands will transparently handle the prompts/get request/response cycle, making the remote command feel completely native to the user.

Low-Priority: UX Polish

  • Command Palette: Implement a Ctrl+K-style command palette to improve discoverability in our increasingly powerful command system.

Reviewer Test Plan

To validate these changes, please pull down the branch and run the Gemini CLI.

  1. Test Migrated Commands:

    • Run /clear. The screen should clear as expected.
    • Run /help. The help dialog should appear, and you should see the nested structure for /memory.
    • Run /memory show, /memory add "test fact", and /memory refresh. All three sub-commands should execute correctly.
  2. Test Nested Autocompletion:

    • Type /mem and press Tab. It should complete to /memory .
    • With /memory in the input, press Tab again. It should cycle through the sub-commands (show, add, refresh).
    • Type /memory sh and press Tab. It should complete to /memory show .
  3. Test Legacy Command Fallback:

    • Run a command that has not been migrated, such as /stats or /docs.
    • Verify that these commands still work exactly as they did before. This confirms the legacy adapter is working correctly.
  4. Review the Code:

    • Pay close attention to packages/cli/src/services/CommandService.ts and packages/cli/src/ui/commands/types.ts as they define the core of the new architecture.
    • Examine packages/cli/src/ui/hooks/slashCommandProcessor.ts to see how the new service is consumed and how the legacy adapter is implemented.
    • Look at packages/cli/src/ui/commands/memoryCommand.ts as a reference for how new, modular, nested commands are defined.

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Linked issues / bugs

@abhipatel12 abhipatel12 requested a review from a team as a code owner July 3, 2025 22:56
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @abhipatel12, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request initiates a major architectural overhaul of the CLI's slash command system, moving towards a more modular, extensible, and testable design. It lays the groundwork for future features like user-defined commands and a more powerful automation platform, while ensuring backward compatibility for existing commands during the transition.

Highlights

  • Architectural Refactor: The core slash command system has been fundamentally refactored. The monolithic useSlashCommandProcessor is being replaced by a new CommandService that manages a scalable, extensible, and testable nested command tree.
  • New Command System Components: Introduced a new CommandService (packages/cli/src/services/CommandService.ts) to centralize command management, a SlashCommand interface (packages/cli/src/ui/commands/types.ts) supporting nested commands, and a CommandContext (packages/cli/src/ui/commands/types.ts) for dependency injection into command actions.
  • Command Migration: The /clear, /help, and the entire /memory command suite (including its show, add, and refresh subcommands) have been migrated to the new modular, tree-based structure.
  • Legacy Compatibility: A crucial legacy adapter has been implemented within useSlashCommandProcessor (packages/cli/src/ui/hooks/slashCommandProcessor.ts). It prioritizes execution of commands from the new CommandService but falls back to the old system for unmigrated commands, ensuring a smooth, incremental transition.
  • Enhanced Autocompletion: The useCompletion hook (packages/cli/src/ui/hooks/useCompletion.ts) and InputPrompt component (packages/cli/src/ui/components/InputPrompt.tsx) have been significantly refactored to provide intelligent, context-aware autocompletion for nested commands and their arguments.
  • Improved Testability: New unit tests have been added for the CommandService and all migrated commands. A createMockCommandContext utility (packages/cli/src/test-utils/mockCommandContext.ts) was introduced to simplify testing of command actions.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a CommandService and nested SlashCommand interface, significantly improving the architecture. The review identified a potential issue in the test utilities related to object mutation.

@github-actions
Copy link

github-actions bot commented Jul 3, 2025

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 71.79% 71.79% 73.59% 76.92%
Core 71.96% 71.96% 75% 81%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   71.79 |    76.92 |   73.59 |   71.79 |                   
 src               |   42.45 |    52.94 |    37.5 |   42.45 |                   
  gemini.tsx       |   17.03 |     12.5 |   16.66 |   17.03 | ...92-225,230-301 
  ...ractiveCli.ts |   90.16 |    65.38 |     100 |   90.16 | ...27,130,153-154 
 src/config        |      82 |    71.07 |   82.14 |      82 |                   
  auth.ts          |   15.38 |      100 |       0 |   15.38 | 11-39             
  config.ts        |   94.71 |    77.14 |    62.5 |   94.71 | ...06-207,269-273 
  extension.ts     |    73.8 |    70.58 |     100 |    73.8 | ...02-106,115-116 
  sandboxConfig.ts |   51.35 |    16.66 |   66.66 |   51.35 | ...43,53-69,74-91 
  settings.ts      |   90.77 |    78.94 |     100 |   90.77 | ...65-266,302-303 
 src/generated     |     100 |      100 |     100 |     100 |                   
  git-commit.ts    |     100 |      100 |     100 |     100 |                   
 src/services      |     100 |      100 |     100 |     100 |                   
  ...andService.ts |     100 |      100 |     100 |     100 |                   
 src/test-utils    |     100 |      100 |     100 |     100 |                   
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
 src/ui            |   60.47 |    55.26 |   68.18 |   60.47 |                   
  App.tsx          |   57.96 |     45.9 |    62.5 |   57.96 | ...71-783,789-818 
  colors.ts        |   86.04 |      100 |   76.92 |   86.04 | 12-13,18-19,42-43 
  constants.ts     |       0 |        0 |       0 |       0 | 1-15              
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/commands   |   99.13 |    94.73 |   83.33 |   99.13 |                   
  clearCommand.ts  |     100 |      100 |     100 |     100 |                   
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/ui/components |      71 |    70.98 |   64.28 |      71 |                   
  AboutBox.tsx     |     100 |       50 |     100 |     100 | 102               
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  AuthDialog.tsx   |   89.74 |      100 |      50 |   89.74 | 44-51             
  ...nProgress.tsx |   15.78 |      100 |       0 |   15.78 | 17-57             
  ...Indicator.tsx |   15.15 |      100 |       0 |   15.15 | 17-47             
  ...lePatcher.tsx |   73.17 |      100 |   66.66 |   73.17 | 34-46             
  ...ryDisplay.tsx |   21.05 |      100 |       0 |   21.05 | 17-35             
  ...ryDisplay.tsx |   97.82 |    95.83 |     100 |   97.82 | 59                
  ...esDisplay.tsx |   10.52 |      100 |       0 |   10.52 | 24-82             
  ...ngsDialog.tsx |    6.76 |      100 |       0 |    6.76 | 26-168            
  Footer.tsx       |   71.42 |    11.11 |     100 |   71.42 | ...,90-97,100-103 
  ...ngSpinner.tsx |      80 |    33.33 |     100 |      80 | 29,31-32          
  Header.tsx       |   96.87 |       60 |     100 |   96.87 | 27                
  Help.tsx         |    3.73 |      100 |       0 |    3.73 | 17-154            
  ...emDisplay.tsx |   68.65 |    57.14 |     100 |   68.65 | ...55-60,79-86,89 
  InputPrompt.tsx  |   79.52 |     62.5 |     100 |   79.52 | ...19,362,389-393 
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  ...geDisplay.tsx |   25.92 |      100 |       0 |   25.92 | 14-36             
  ...tsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   44.44 |      100 |       0 |   44.44 | 12-17             
  ...MoreLines.tsx |      60 |       25 |     100 |      60 | 24-27,33-40       
  StatsDisplay.tsx |   98.39 |    86.66 |     100 |   98.39 | 173-175           
  ...nsDisplay.tsx |   83.05 |    61.53 |     100 |   83.05 | 34-39,42-43,87-89 
  ThemeDialog.tsx  |    82.7 |       20 |      25 |    82.7 | ...,95-99,197-208 
  Tips.tsx         |      16 |      100 |       0 |      16 | 17-45             
  ...tsDisplay.tsx |     100 |     87.5 |     100 |     100 | 30-31             
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
 ...nents/messages |   62.67 |     80.9 |   57.89 |   62.67 |                   
  ...onMessage.tsx |   18.51 |      100 |       0 |   18.51 | 22-49             
  DiffRenderer.tsx |   96.31 |    82.66 |     100 |   96.31 | ...01-202,206,271 
  ErrorMessage.tsx |   22.22 |      100 |       0 |   22.22 | 16-31             
  ...niMessage.tsx |   18.51 |      100 |       0 |   18.51 | 20-43             
  ...geContent.tsx |   19.04 |      100 |       0 |   19.04 | 25-43             
  InfoMessage.tsx  |     100 |      100 |     100 |     100 |                   
  ...onMessage.tsx |   38.91 |     62.5 |   33.33 |   38.91 | ...32-164,200-225 
  ...upMessage.tsx |     9.3 |      100 |       0 |     9.3 | 26-123            
  ToolMessage.tsx  |   87.76 |       80 |     100 |   87.76 | ...,91-95,169-171 
  UserMessage.tsx  |     100 |      100 |     100 |     100 |                   
  ...llMessage.tsx |   36.36 |      100 |       0 |   36.36 | 17-25             
 ...ponents/shared |    74.7 |    71.87 |   85.18 |    74.7 |                   
  MaxSizedBox.tsx  |   78.51 |    79.24 |   88.88 |   78.51 | ...36-438,537-538 
  ...tonSelect.tsx |   88.05 |    92.85 |      60 |   88.05 | ...,68-71,108-109 
  text-buffer.ts   |   72.44 |    67.24 |    92.3 |   72.44 | ...1235-1238,1243 
 src/ui/contexts   |    92.8 |    86.36 |     100 |    92.8 |                   
  ...owContext.tsx |   91.07 |    81.81 |     100 |   91.07 | 46-47,59-61       
  ...onContext.tsx |     100 |      100 |     100 |     100 |                   
  ...ngContext.tsx |   71.42 |       50 |     100 |   71.42 | 17-20             
 src/ui/editors    |   93.87 |    85.71 |   66.66 |   93.87 |                   
  ...ngsManager.ts |   93.87 |    85.71 |   66.66 |   93.87 | 53,67-68          
 src/ui/hooks      |   71.39 |    80.72 |   65.75 |   71.39 |                   
  ...dProcessor.ts |   82.07 |    82.75 |     100 |   82.07 | ...92-395,406-422 
  ...dProcessor.ts |      82 |    74.35 |      80 |      82 | ...15-324,328-329 
  ...dProcessor.ts |   67.17 |    77.12 |   45.45 |   67.17 | ...1199-1204,1207 
  ...uthCommand.ts |    7.14 |      100 |       0 |    7.14 | 17-76             
  ...tIndicator.ts |     100 |      100 |     100 |     100 |                   
  ...ketedPaste.ts |     100 |      100 |     100 |     100 |                   
  useCompletion.ts |   68.62 |    86.17 |      80 |   68.62 | ...14-420,485-488 
  ...leMessages.ts |   96.96 |    88.88 |     100 |   96.96 | 25-26             
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...miniStream.ts |   67.49 |    70.33 |     100 |   67.49 | ...36,638,687-777 
  ...BranchName.ts |   91.66 |    84.61 |     100 |   91.66 | 57-63             
  ...oryManager.ts |   98.41 |    93.33 |     100 |   98.41 | 43                
  ...putHistory.ts |    92.5 |    85.71 |     100 |    92.5 | 62-63,71,93-95    
  useKeypress.ts   |    60.6 |       50 |     100 |    60.6 | ...1,75-76,92-101 
  ...gIndicator.ts |     100 |      100 |     100 |     100 |                   
  useLogger.ts     |      25 |      100 |       0 |      25 | 14-32             
  ...raseCycler.ts |    95.5 |       75 |     100 |    95.5 | ...66-167,185-187 
  ...cySettings.ts |     4.5 |      100 |       0 |     4.5 | 19-135            
  ...lScheduler.ts |   79.01 |    94.87 |     100 |   79.01 | ...00-203,293-303 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...ellHistory.ts |   91.95 |    79.16 |   83.33 |   91.95 | 28-30,41-42,87-88 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-75              
  ...tateAndRef.ts |   95.45 |    66.66 |     100 |   95.45 | 25                
  ...rminalSize.ts |   77.27 |      100 |      50 |   77.27 | 19-23             
  ...emeCommand.ts |   64.28 |     87.5 |     100 |   64.28 | ...,92-93,100-106 
  useTimer.ts      |   88.09 |    85.71 |     100 |   88.09 | 44-45,51-53       
 src/ui/privacy    |   13.77 |      100 |       0 |   13.77 |                   
  ...acyNotice.tsx |    9.58 |      100 |       0 |    9.58 | 20-113            
  ...acyNotice.tsx |    12.9 |      100 |       0 |    12.9 | 15-55             
  ...acyNotice.tsx |   10.81 |      100 |       0 |   10.81 | 15-58             
  ...acyNotice.tsx |   30.76 |      100 |       0 |   30.76 | 19-36,39-41       
 src/ui/themes     |   99.42 |    89.74 |     100 |   99.42 |                   
  ansi-light.ts    |     100 |      100 |     100 |     100 |                   
  ansi.ts          |     100 |      100 |     100 |     100 |                   
  atom-one-dark.ts |     100 |      100 |     100 |     100 |                   
  ayu-light.ts     |     100 |      100 |     100 |     100 |                   
  ayu.ts           |     100 |      100 |     100 |     100 |                   
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  dracula.ts       |     100 |      100 |     100 |     100 |                   
  github-dark.ts   |     100 |      100 |     100 |     100 |                   
  github-light.ts  |     100 |      100 |     100 |     100 |                   
  googlecode.ts    |     100 |      100 |     100 |     100 |                   
  no-color.ts      |     100 |      100 |     100 |     100 |                   
  ...-of-purple.ts |     100 |      100 |     100 |     100 |                   
  theme-manager.ts |   89.77 |    84.21 |     100 |   89.77 | 66,98-103,108-109 
  theme.ts         |   98.44 |       95 |     100 |   98.44 | 304-307           
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |   78.47 |     84.1 |   97.14 |   78.47 |                   
  ...Colorizer.tsx |    80.5 |     87.5 |     100 |    80.5 | 88-89,159-183     
  ...nRenderer.tsx |   57.57 |    45.45 |     100 |   57.57 | ...21-129,131-133 
  ...wnDisplay.tsx |   64.24 |    71.69 |      75 |   64.24 | ...94-322,354-378 
  ...eRenderer.tsx |     100 |    96.29 |     100 |     100 | 121               
  commandUtils.ts  |     100 |      100 |     100 |     100 |                   
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  displayUtils.ts  |     100 |      100 |     100 |     100 |                   
  errorParsing.ts  |     100 |      100 |     100 |     100 |                   
  formatters.ts    |   90.47 |       96 |     100 |   90.47 | 57-60             
  ...nUtilities.ts |   69.84 |    85.71 |     100 |   69.84 | 75-91,100-101     
  textUtils.ts     |   93.93 |    92.85 |     100 |   93.93 | 14-15             
  updateCheck.ts   |     100 |      100 |     100 |     100 |                   
 src/utils         |    8.78 |    77.77 |    62.5 |    8.78 |                   
  cleanup.ts       |   91.66 |       50 |     100 |   91.66 | 18                
  package.ts       |   88.88 |    85.71 |     100 |   88.88 | 33-34             
  readStdin.ts     |    3.44 |      100 |       0 |    3.44 | 7-39              
  sandbox.ts       |       0 |        0 |       0 |       0 | 1-871             
  ...upWarnings.ts |   23.07 |      100 |       0 |   23.07 | 14-40             
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  version.ts       |     100 |       50 |     100 |     100 | 11                
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   71.96 |       81 |      75 |   71.96 |                   
 src               |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/__mocks__/fs  |     100 |      100 |     100 |     100 |                   
  promises.ts      |     100 |      100 |     100 |     100 |                   
 src/code_assist   |   61.96 |    74.32 |      65 |   61.96 |                   
  codeAssist.ts    |   29.41 |      100 |       0 |   29.41 | 12-24             
  converter.ts     |   89.06 |    96.15 |   81.81 |   89.06 | 175-179,198-208   
  oauth2.ts        |    69.6 |    54.28 |   83.33 |    69.6 | ...94-296,300-302 
  server.ts        |    46.4 |       80 |   53.84 |    46.4 | ...60-201,204-206 
  setup.ts         |   10.34 |      100 |       0 |   10.34 | 19-22,30-85       
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/config        |   68.71 |    90.16 |   59.61 |   68.71 |                   
  config.ts        |   68.36 |    90.16 |   59.61 |   68.36 | ...52-457,475-523 
  models.ts        |     100 |      100 |     100 |     100 |                   
 src/core          |   71.25 |    75.74 |   78.02 |   71.25 |                   
  client.ts        |   70.74 |    74.24 |   86.36 |   70.74 | ...15,527-528,531 
  ...tGenerator.ts |    43.9 |     62.5 |      50 |    43.9 | ...00,122,133-136 
  ...lScheduler.ts |   72.07 |    69.33 |   81.25 |   72.07 | ...51-657,674-683 
  geminiChat.ts    |   64.97 |    75.67 |   64.28 |   64.97 | ...61,576,580-588 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  logger.ts        |   82.71 |    78.26 |     100 |   82.71 | ...72-276,285-286 
  modelCheck.ts    |    4.65 |      100 |       0 |    4.65 | 20-68             
  ...olExecutor.ts |     100 |    66.66 |     100 |     100 | 63,93             
  prompts.ts       |   81.05 |    68.18 |      50 |   81.05 | ...60-265,281-338 
  tokenLimits.ts   |      15 |      100 |       0 |      15 | 15-31             
  turn.ts          |   80.67 |    82.14 |     100 |   80.67 | ...13-216,229-230 
 src/services      |   77.71 |    86.11 |   70.58 |   77.71 |                   
  ...eryService.ts |   77.33 |       75 |   85.71 |   77.33 | ...1-62,84,91-103 
  gitService.ts    |   78.02 |      100 |      60 |   78.02 | ...10-114,117-121 
 src/telemetry     |   81.31 |    86.77 |   86.04 |   81.31 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  loggers.ts       |   82.94 |       85 |    87.5 |   82.94 | 160-199           
  metrics.ts       |   60.36 |    95.65 |    62.5 |   60.36 | ...36-158,161-184 
  sdk.ts           |   82.85 |    28.57 |     100 |   82.85 | ...18,126-127,133 
  types.ts         |   82.51 |     97.5 |   84.61 |   82.51 | 83-92,149-172     
  uiTelemetry.ts   |   99.18 |    95.65 |     100 |   99.18 | 119               
 ...learcut-logger |   80.75 |    81.25 |   72.22 |   80.75 |                   
  ...cut-logger.ts |   80.51 |    80.64 |   76.47 |   80.51 | ...02-414,417-419 
  ...tadata-key.ts |    82.6 |      100 |       0 |    82.6 | 143-153           
 src/tools         |    63.8 |    81.35 |   70.07 |    63.8 |                   
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  edit.ts          |   78.15 |    82.19 |   84.61 |   78.15 | ...58-459,463-498 
  glob.ts          |   87.03 |       80 |   85.71 |   87.03 | ...93-294,302-309 
  grep.ts          |   56.81 |     75.8 |   72.72 |   56.81 | ...31-536,541-545 
  ls.ts            |    8.55 |      100 |    12.5 |    8.55 | ...89-194,202-312 
  mcp-client.ts    |   75.65 |    78.33 |      30 |   75.65 | ...73-374,393-394 
  mcp-tool.ts      |   91.75 |    85.71 |     100 |   91.75 | ...36,142,147-148 
  memoryTool.ts    |   97.43 |    84.84 |     100 |   97.43 | 93,95,97-98       
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 90,97             
  read-file.ts     |   98.42 |    96.15 |     100 |   98.42 | 124-125           
  ...many-files.ts |   79.78 |    75.86 |   83.33 |   79.78 | ...67-468,475-476 
  shell.ts         |   31.28 |      100 |   58.33 |   31.28 | ...56-278,281-495 
  tool-registry.ts |   58.04 |    73.46 |   78.57 |   58.04 | ...01-306,312-313 
  tools.ts         |   76.31 |      100 |      40 |   76.31 | ...44-145,154-159 
  web-fetch.ts     |   31.29 |    72.22 |   66.66 |   31.29 | ...89-190,218-356 
  web-search.ts    |   10.92 |      100 |      20 |   10.92 | ...06-107,110-199 
  write-file.ts    |   81.61 |    82.69 |   81.81 |   81.61 | ...26-331,393-423 
 src/utils         |   81.75 |    82.37 |   87.25 |   81.75 |                   
  LruCache.ts      |   70.96 |     62.5 |     100 |   70.96 | 20-22,28,30-34    
  bfsFileSearch.ts |   92.45 |    86.66 |     100 |   92.45 | 53-54,67-68       
  editCorrector.ts |   77.35 |    61.11 |   91.66 |   77.35 | ...70-682,716,730 
  editor.ts        |   97.33 |    93.75 |     100 |   97.33 | 134,196,199-200   
  ...rReporting.ts |   83.52 |    84.61 |     100 |   83.52 | 81-85,106-114     
  errors.ts        |   41.46 |       60 |      75 |   41.46 | 17-21,37-52,56-62 
  fetch.ts         |   34.04 |      100 |       0 |   34.04 | 22-27,31-57       
  fileUtils.ts     |   94.69 |    91.54 |     100 |   94.69 | ...45-249,318-324 
  ...eUtilities.ts |   96.03 |       96 |     100 |   96.03 | 28-29,57-58       
  ...rStructure.ts |      95 |    93.42 |     100 |      95 | ...66-167,344-346 
  ...noreParser.ts |   96.36 |     91.3 |     100 |   96.36 | 70-71             
  gitUtils.ts      |   46.34 |    66.66 |      50 |   46.34 | 24-25,40-41,50-73 
  ...yDiscovery.ts |    80.7 |    75.43 |      75 |    80.7 | ...12-313,316-317 
  ...tProcessor.ts |   86.86 |    88.46 |     100 |   86.86 | 115-124,131-140   
  ...Inspectors.ts |     100 |      100 |     100 |     100 |                   
  ...kerChecker.ts |   83.14 |    82.35 |     100 |   83.14 | ...,94-99,107-113 
  paths.ts         |   62.79 |    73.33 |   57.14 |   62.79 | ...23-133,139-140 
  retry.ts         |   68.85 |    73.91 |     100 |   68.85 | ...91-211,256-271 
  ...aValidator.ts |     100 |      100 |     100 |     100 |                   
  session.ts       |     100 |      100 |     100 |     100 |                   
  testUtils.ts     |   84.44 |    72.72 |   83.33 |   84.44 | 27-28,34-35,70-72 
  user_id.ts       |   63.63 |    33.33 |      80 |   63.63 | ...57,74-75,78-79 
-------------------|---------|----------|---------|---------|-------------------

For detailed HTML reports, please see the 'coverage-reports-20.x' artifact from the main CI run.

@allenhutchison
Copy link
Collaborator

This sounds great and I'm looking forward to reading through the code when I'm back at a computer.

One idea while I was reading your description is that you might want to enable tools to define their own commands from the tool definition. You mentioned /memory in your writeup and I wonder if it would be even cleaner to allow the memory tool to define its commands in the tool definition?

@abhipatel12
Copy link
Collaborator Author

This sounds great and I'm looking forward to reading through the code when I'm back at a computer.

One idea while I was reading your description is that you might want to enable tools to define their own commands from the tool definition. You mentioned /memory in your writeup and I wonder if it would be even cleaner to allow the memory tool to define its commands in the tool definition?

Ohh I like that! I'll have to take a look when I'm back at my computer

Copy link
Collaborator

@NTaylorMullen NTaylorMullen left a comment

Choose a reason for hiding this comment

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

Super exciting changes. There's so much here that's going to make our lives significantly easier with new slash command pieces coming up. Left a good amount of comments around API state. Let me know what you think!

altName: '?',
description: 'for help on gemini-cli',
action: (context, _args) => {
context.utils.onDebugMessage('Opening help.');
Copy link
Collaborator

Choose a reason for hiding this comment

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

I forget, can't we replace all the onDebugMessage bits with console.debug(...)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes we def can!

When I was testing this, the difference is how it appears for the user. If the CLI is started in debug mode, the onDebugMessage surfaces in the footer while the console.debug(...) appears within the console.

I think it depends on which UX we prefer. I added a log statement and then ran /clear. The resulting UI is:

Screenshot 2025-07-04 at 1 19 35 PM

Do you have a UX preference here? I can make the switch pretty easily.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I actually think the onDebugMessage is a bit misleading and not worth. I'd argue we should do console.debug only.

@abhipatel12 abhipatel12 force-pushed the abhipatel12/refactor-slash-processor branch from f4606da to 392f41a Compare July 7, 2025 01:40
@abhipatel12
Copy link
Collaborator Author

@NTaylorMullen Thanks for all the great feedback on that last round! I made some pretty significant shifts here so let me know what you think!

@abhipatel12
Copy link
Collaborator Author

abhipatel12 commented Jul 7, 2025

This sounds great and I'm looking forward to reading through the code when I'm back at a computer.

One idea while I was reading your description is that you might want to enable tools to define their own commands from the tool definition. You mentioned /memory in your writeup and I wonder if it would be even cleaner to allow the memory tool to define its commands in the tool definition?

@allenhutchison I wanted to follow up on this idea! It enables Tool definitions to become the source of truth for their own user-facing slash commands. We should definitely do this since it co-locates related logic, eliminates boilerplate, and creates a single, unified discovery mechanism for commands.

The architecture proposed in this PR supports this idea and I want to propose pursuing this in a fast-follow.

An example implementation would be as follows:

Possible Impl Plan

1. Enhance the Tool Interface:

We can introduce an optional method, getSlashCommands(), to the core Tool interface. This method will allow any tool to return an array of SlashCommand objects that it provides.

export interface Tool {
  // ... existing properties
  getSlashCommands?(context: CommandContext): SlashCommand[];
}

2. CommandService Discovery Mechanism Changes

The CommandService can query the ToolRegistry as a new source for commands. This new discovery step will happen alongside the loading of built-in and possible file-based commands.

async loadCommands(context: CommandContext): Promise<void> {
  // Load built-in commands (e.g., /help, /clear)
  const builtInCommands = await loadBuiltInCommands();

  // Discover commands from tools
  const toolBasedCommands: SlashCommand[] = [];
  for (const tool of this.toolRegistry.getAllTools()) {
    if (tool.getSlashCommands) {
      toolBasedCommands.push(...tool.getSlashCommands(context));
    }
  }

  // 3. FUTURE: Discover commands from .gemini.yml files
  // 4. FUTURE: Discover commands from MCP `prompts/list` endpoint

  // 5. Merge all sources into a single command tree
  this.commandTree = [...builtInCommands, ...toolBasedCommands, ...];
}

3. Proof-of-Concept: /memory

We can start with /memory to prove out this concept. This PR moves /memory using built-in commands and this fast-follow would then directly use the Tool interface.


Let me know what you think!

NTaylorMullen
NTaylorMullen previously approved these changes Jul 7, 2025
Copy link
Collaborator

@NTaylorMullen NTaylorMullen left a comment

Choose a reason for hiding this comment

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

Love the refactor and where you went with it!

altName: '?',
description: 'for help on gemini-cli',
action: (context, _args) => {
context.utils.onDebugMessage('Opening help.');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I actually think the onDebugMessage is a bit misleading and not worth. I'd argue we should do console.debug only.

type: MessageType.INFO,
text: 'Refreshing memory from source files...',
},
Date.now(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we ever not do Date.now()? Wondering if this can be automatic in addItem

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Originally we had two of these addItems, and we consolidated to a single one. Data.now() is being used as the id here. I agree that it's a bit repetitive, but I believe the best change may be to make this id field optional and have a default id that uses Date.now(). But i think that's best addressed in a follow-up PR if that sounds good to you!

export interface CommandContext {
// Core services and configuration
services: {
config: Config | null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What scenario is this null in?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't believe it should ever be. This was the type passed in defensively when I made the context in the event that it could be possible, but I agree it shouldn't really be possible.

Since the solution involves modifying the app's startup logic in gemini.tsx to disable input until the config is loaded, it's a non-trivial architectural change if we want to handle this safely. I added a TODO and to have a fast follow to remove this extra check.

* The return type for a command action that results in a simple message
* being displayed to the user.
*/
export interface MessageActionReturn {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah is this in place of say ui.addItem?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sort of. For multi-step actions, the command takes action through the context.ui.AddItem function. So for example, in /memory we use the context.ui.AddItem in order to provide updates saying "refresh memory", etc.

But when a slash command's job is to produce a single, final message it uses MessageActionReturn.

I added MessageActionReturn for a couple of reasons.

  1. It simplifies certain commands. This is especially true for error handling. We have a common error handling path currently, and making every command implement its own HistoryItem would be more work.
  2. It makes the contract stronger. Now that the return is a union, the slash Command Processor knows exactly what each command is doing and it doesn't have to rely on optional checks.
  • tool --> needs to schedule a tool call
  • message --> needs to display the basic message for the user (maybe it's an error)
  • void --> command handled everything, so don't worry about doing anything.

description?: string;

// The action to run. Optional for parent commands that only group sub-commands.
action?: (
Copy link
Collaborator

Choose a reason for hiding this comment

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

What commands don't have an action? Wondering if this can be required / not nullable

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Great question!

The action property is optional by design to support parent commands. In our nested tree structure (/command subcommand ...args), a command can be a "leaf" that performs an action, or it can be a "branch" that only groups other commands.

Example:
The top-level /memory command only exists to contain its subCommands (show, add, refresh). It doesn't have an action of its own. If a user types just /memory, the system should recognize that the action is undefined and displays a help message listing the available subcommands.

If we made the action property required, it would force us to add empty, placeholder actions to all parent commands, which would be more boilerplate.

@NTaylorMullen
Copy link
Collaborator

My 2cents on exposing slash commands on tools: We could BUT I'm not sure how valuable it'd be. I could see a world in which we automatically translate some tools to slash commands maybe.

Memory might be an exception to the common case here 😄

abhipatel12 and others added 15 commits July 7, 2025 15:51
This commit introduces a new command registry architecture to decouple slash command logic from the `useSlashCommandProcessor` hook.

The key changes are:

- **Command Registry:** A new directory `packages/cli/src/ui/commands` now houses individual command modules and a central registry (`index.ts`).
- **`CommandContext`:** A new `CommandContext` object is introduced to provide dependencies to commands via a single, structured object, breaking the tight coupling with the hooks props and state.
- **Initial Migration:** The `/help` and `/clear` commands have been migrated to this new architecture as proof of concept.
- **Coexistence Model:** The `useSlashCommandProcessor` hook has been adapted to support both the new command registry and the legacy, inline command definitions, allowing for a safe, incremental migration.
- **Testing Strategy:** New, isolated tests have been added for the migrated commands, and the testing strategy for the command processor has been updated to mock the command registry.

This change lays the foundation for migrating all remaining slash commands, which will improve scalability, testability, and maintainability.
This commit introduces the initial proof-of-concept for the CommandService, which is responsible for loading slash commands from various sources.

Key changes:
- Created the  class in .
- Implemented file-based discovery of custom commands from .
- Added  for parsing command definitions.
- Integrated the  into the  hook to dynamically load and register custom commands.
- Added a sample  custom command for testing purposes.

This lays the foundation for a fully extensible command system
This commit refactors the slash command infrastructure to simplify the CommandContext object and create a more explicit contract for command actions. This addresses PR feedback aimed at improving code clarity and making commands more self-contained.

Key changes include:

- **Simplified CommandContext**: The `actions` and `utils` properties have been removed. UI-related functions like `addItem`, `setDebugMessage`, and `clear` are now consolidated directly under the `ui` object. This reduces indirection and makes the context easier to mock for testing.

- **Explicit Command Action Contract**: Slash command actions no longer return a boolean or an implicit object. They now return a discriminated union:
  - `{ type: 'tool', ... }` to schedule a tool call.
  - `{ type: 'message', ... }` to display a simple message to the user.
  The `useSlashCommandProcessor` is updated to handle this new, clearer contract.

- **Refactored Commands**:
  - `/clear` and `/help` are updated to use the new `ui.setDebugMessage`.
  - `/memory` subcommands are refactored to be more self-contained. For instance, `/memory refresh` now orchestrates its own async operation and UI updates for success or failure, calling the new `config.refreshMemory()` method.

- **Core Memory Refresh Logic**: A `refreshMemory()` method was added to the core `Config` class, centralizing the logic for reloading memory content from `GEMINI.md` files.
@abhipatel12 abhipatel12 force-pushed the abhipatel12/refactor-slash-processor branch from f231eae to ac1d4e8 Compare July 7, 2025 20:02
@abhipatel12
Copy link
Collaborator Author

abhipatel12 commented Jul 7, 2025

My 2cents on exposing slash commands on tools: We could BUT I'm not sure how valuable it'd be. I could see a world in which we automatically translate some tools to slash commands maybe.

Memory might be an exception to the common case here 😄

Good point, something to consider if we want go forward with that plan!

Copy link
Collaborator

@NTaylorMullen NTaylorMullen left a comment

Choose a reason for hiding this comment

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

:shipit:

@abhipatel12 abhipatel12 added this pull request to the merge queue Jul 7, 2025
Merged via the queue into main with commit aa10ccb Jul 7, 2025
13 checks passed
@abhipatel12 abhipatel12 deleted the abhipatel12/refactor-slash-processor branch July 7, 2025 20:48
@mattKorwel
Copy link
Collaborator

👏

JunYang-tes pushed a commit to JunYang-tes/gemini-cli.nvim that referenced this pull request Jul 12, 2025
reconsumeralization pushed a commit to reconsumeralization/gemini-cli that referenced this pull request Sep 19, 2025
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.

Feature: Nested Slash Command Completion and Execution

4 participants