Conversation
There was a problem hiding this comment.
Pull request overview
This pull request refactors the slash command system by extracting command implementations from a single tux.py file into individual modules within a new codeai/commands/ package. This improves code organization and maintainability.
Changes:
- Extracted
SlashCommanddataclass and command registration logic fromtux.pytocodeai/commands/__init__.py - Created 19 individual command modules, one per slash command, following a consistent structure
- Added a new
/suggestionscommand that allows users to select from agent-provided suggestions - Renamed Makefile targets from
codeai-openteams-demotocodeai-demofor clarity
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| codeai/tux.py | Removed SlashCommand class and _register_commands method; imports from commands package |
| codeai/commands/init.py | Defines SlashCommand and build_commands factory function for command registration |
| codeai/commands/about.py | Easter egg command for "About Datalayer" animation |
| codeai/commands/agents.py | Lists available agents with detailed information |
| codeai/commands/browser.py | Opens the Agent chat UI in browser |
| codeai/commands/clear.py | Clears conversation history |
| codeai/commands/cls.py | Clears the terminal screen |
| codeai/commands/codemode_toggle.py | Toggles codemode on/off |
| codeai/commands/context.py | Visualizes current context usage |
| codeai/commands/context_export.py | Exports context to CSV file |
| codeai/commands/exit.py | Exits the application |
| codeai/commands/gif.py | Black hole spinning animation (Easter egg) |
| codeai/commands/help.py | Shows available commands |
| codeai/commands/jupyter.py | Opens Jupyter server in browser |
| codeai/commands/mcp_servers.py | Lists MCP servers and their status |
| codeai/commands/rain.py | Matrix rain animation (Easter egg) |
| codeai/commands/skills.py | Lists available skills |
| codeai/commands/status.py | Shows Code AI status |
| codeai/commands/suggestions.py | New command to list and select agent suggestions |
| codeai/commands/tools.py | Lists available tools |
| codeai/commands/tools_last.py | Shows tool call details from last response |
| Makefile | Renamed demo targets for clarity |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| NAME = "about" | ||
| ALIASES: list[str] = [] | ||
| DESCRIPTION = "About Datalayer" | ||
| SHORTCUT = "escape l" |
There was a problem hiding this comment.
Keyboard shortcut conflict: Both /about (line 17 in about.py) and /tools-last (line 18 in tools_last.py) are assigned the same shortcut "escape l". This causes only one command to be reachable via the keyboard shortcut. The shortcut registration logic in tux.py (line 314-319) uses a dictionary where shortcuts are keys, so the second command with the same shortcut will overwrite the first. Consider changing one of these shortcuts to a different key combination.
| SHORTCUT = "escape l" | |
| SHORTCUT = "escape a" |
| async def execute(tux: "CodeAITux") -> Optional[str]: | ||
| """Fetch suggestions from the running agent spec, display them numbered, | ||
| and let the user choose one to use as the next prompt. | ||
|
|
||
| Returns: | ||
| The chosen suggestion text, or None if cancelled / no suggestions. | ||
| """ | ||
| from ..tux import STYLE_PRIMARY, STYLE_ACCENT, STYLE_MUTED, STYLE_WARNING | ||
| from ..banner import GREEN_MEDIUM, GREEN_LIGHT, GRAY, RESET | ||
|
|
||
| # Fetch the agent spec which contains the suggestions list | ||
| suggestions: list[str] = [] | ||
| try: | ||
| async with httpx.AsyncClient() as client: | ||
| url = f"{tux.server_url}/api/v1/configure/agents/{tux.agent_id}/spec" | ||
| response = await client.get(url, timeout=10.0) | ||
| response.raise_for_status() | ||
| data = response.json() | ||
| suggestions = data.get("suggestions", []) | ||
| except Exception as e: | ||
| tux.console.print(f"[red]Error fetching suggestions: {e}[/red]") | ||
| return None | ||
|
|
||
| if not suggestions: | ||
| tux.console.print() | ||
| tux.console.print("● No suggestions available for this agent.", style=STYLE_MUTED) | ||
| tux.console.print() | ||
| return None | ||
|
|
||
| # Display numbered suggestions | ||
| tux.console.print() | ||
| tux.console.print(f"● Suggestions ({len(suggestions)}):", style=STYLE_PRIMARY) | ||
| tux.console.print() | ||
|
|
||
| for i, suggestion in enumerate(suggestions, 1): | ||
| tux.console.print(f" {i}. {suggestion}", style=STYLE_ACCENT) | ||
|
|
||
| tux.console.print() | ||
|
|
||
| # Prompt user to choose | ||
| while True: | ||
| try: | ||
| choice = input( | ||
| f"{GREEN_MEDIUM}Choose a suggestion [1-{len(suggestions)}] " | ||
| f"(Enter to cancel): {RESET}" | ||
| ).strip() | ||
|
|
||
| if not choice: | ||
| tux.console.print(" Cancelled.", style=STYLE_MUTED) | ||
| return None | ||
|
|
||
| idx = int(choice) - 1 | ||
| if 0 <= idx < len(suggestions): | ||
| selected = suggestions[idx] | ||
| tux.console.print() | ||
| tux.console.print(f" {GREEN_LIGHT}Prompting:{RESET} {selected}", style=STYLE_ACCENT) | ||
| tux.console.print() | ||
| return selected | ||
| else: | ||
| tux.console.print( | ||
| f" Please enter a number between 1 and {len(suggestions)}.", | ||
| style=STYLE_MUTED, | ||
| ) | ||
| except ValueError: | ||
| tux.console.print( | ||
| f" Please enter a number between 1 and {len(suggestions)}.", | ||
| style=STYLE_MUTED, | ||
| ) | ||
| except (KeyboardInterrupt, EOFError): | ||
| tux.console.print() | ||
| tux.console.print(" Cancelled.", style=STYLE_MUTED) | ||
| return None |
There was a problem hiding this comment.
The suggestions command returns a string that should be used as the next prompt (e.g., line 79 returns the selected suggestion), but the handler's return value is not captured or used in handle_command (line 823 in tux.py). This means the suggestions command won't work as intended. The handle_command method should capture the return value from cmd.handler() and if it's a non-None string, it should be automatically sent as a message to the agent.
| choice = input( | ||
| f"{GREEN_MEDIUM}Choose a suggestion [1-{len(suggestions)}] " | ||
| f"(Enter to cancel): {RESET}" | ||
| ).strip() |
There was a problem hiding this comment.
Using synchronous input() in an async function can block the event loop. Consider using prompt_toolkit's asynchronous input methods (like prompt_async) instead, which would be consistent with how user input is handled elsewhere in the codebase (e.g., tux.py line 352).
No description provided.