feat: Add lightweight TUI for cognee CLI
- Implement interactive Terminal User Interface using Textual - Add main TUI application with keyboard navigation - Create home screen with main menu - Add placeholder screens for Context, Query, and Settings - Integrate TUI command into existing CLI structure - Support keyboard shortcuts (q=quit, ?=help, d=toggle dark mode) Closes #1762
This commit is contained in:
parent
487635b71b
commit
2db9d1ee03
10 changed files with 319 additions and 0 deletions
|
|
@ -92,6 +92,7 @@ def _discover_commands() -> List[Type[SupportsCliCommand]]:
|
||||||
("cognee.cli.commands.cognify_command", "CognifyCommand"),
|
("cognee.cli.commands.cognify_command", "CognifyCommand"),
|
||||||
("cognee.cli.commands.delete_command", "DeleteCommand"),
|
("cognee.cli.commands.delete_command", "DeleteCommand"),
|
||||||
("cognee.cli.commands.config_command", "ConfigCommand"),
|
("cognee.cli.commands.config_command", "ConfigCommand"),
|
||||||
|
("cognee.cli.commands.tui_command", "TuiCommand"),
|
||||||
]
|
]
|
||||||
|
|
||||||
for module_path, class_name in command_modules:
|
for module_path, class_name in command_modules:
|
||||||
|
|
|
||||||
54
cognee/cli/commands/tui_command.py
Normal file
54
cognee/cli/commands/tui_command.py
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import argparse
|
||||||
|
from cognee.cli.reference import SupportsCliCommand
|
||||||
|
from cognee.cli import DEFAULT_DOCS_URL
|
||||||
|
import cognee.cli.echo as fmt
|
||||||
|
from cognee.cli.exceptions import CliCommandException
|
||||||
|
|
||||||
|
|
||||||
|
class TuiCommand(SupportsCliCommand):
|
||||||
|
command_string = "tui"
|
||||||
|
help_string = "Launch interactive Terminal User Interface"
|
||||||
|
docs_url = DEFAULT_DOCS_URL
|
||||||
|
|
||||||
|
description = """
|
||||||
|
Launch the Cognee Terminal User Interface (TUI).
|
||||||
|
|
||||||
|
The TUI provides an interactive, text-based interface for managing your
|
||||||
|
knowledge graphs with features like:
|
||||||
|
|
||||||
|
- **Context Management**: Add and manage data sources
|
||||||
|
- **Search & Query**: Interactive knowledge graph querying
|
||||||
|
- **Settings**: Configure API keys and models
|
||||||
|
- **Live Updates**: Real-time status and progress indicators
|
||||||
|
|
||||||
|
The TUI is keyboard-driven and supports:
|
||||||
|
- Arrow key navigation
|
||||||
|
- Keyboard shortcuts (h=Home, c=Context, s=Search, etc.)
|
||||||
|
- Tab completion for inputs
|
||||||
|
|
||||||
|
Perfect for managing Cognee from the terminal or SSH sessions!
|
||||||
|
"""
|
||||||
|
|
||||||
|
def configure_parser(self, parser: argparse.ArgumentParser) -> None:
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-mouse",
|
||||||
|
action="store_true",
|
||||||
|
help="Disable mouse support (keyboard only mode)",
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, args: argparse.Namespace) -> None:
|
||||||
|
try:
|
||||||
|
fmt.echo("Starting Cognee TUI...")
|
||||||
|
fmt.note("Press 'q' to quit, '?' for help")
|
||||||
|
|
||||||
|
# Import and run TUI
|
||||||
|
from cognee.cli.tui import run_tui
|
||||||
|
run_tui()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
fmt.note("\nTUI closed by user")
|
||||||
|
except Exception as e:
|
||||||
|
raise CliCommandException(
|
||||||
|
f"Failed to start TUI: {str(e)}",
|
||||||
|
error_code=1
|
||||||
|
) from e
|
||||||
4
cognee/cli/tui/__init__.py
Normal file
4
cognee/cli/tui/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
"""Cognee TUI - Terminal User Interface"""
|
||||||
|
from cognee.cli.tui.app import run_tui, CogneeTUI
|
||||||
|
|
||||||
|
__all__ = ["run_tui", "CogneeTUI"]
|
||||||
124
cognee/cli/tui/app.py
Normal file
124
cognee/cli/tui/app.py
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
"""
|
||||||
|
Cognee TUI - Main Application
|
||||||
|
Text-based User Interface for managing Cognee knowledge graphs
|
||||||
|
"""
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.binding import Binding
|
||||||
|
from textual.widgets import Header, Footer
|
||||||
|
from textual.screen import Screen
|
||||||
|
|
||||||
|
from cognee.cli.tui.screens.home import HomeScreen
|
||||||
|
|
||||||
|
|
||||||
|
class CogneeTUI(App):
|
||||||
|
"""Cognee Terminal User Interface Application"""
|
||||||
|
|
||||||
|
CSS = """
|
||||||
|
Screen {
|
||||||
|
background: $surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
border: solid $primary;
|
||||||
|
background: $panel;
|
||||||
|
padding: 1 2;
|
||||||
|
margin: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
margin: 1 2;
|
||||||
|
min-width: 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button:hover {
|
||||||
|
background: $primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu-container {
|
||||||
|
width: 60;
|
||||||
|
height: auto;
|
||||||
|
border: heavy $primary;
|
||||||
|
background: $panel;
|
||||||
|
padding: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
text-style: bold;
|
||||||
|
color: $accent;
|
||||||
|
padding: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
align: center middle;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
TITLE = "Cognee TUI - Knowledge Graph Manager"
|
||||||
|
SUB_TITLE = "Navigate with arrow keys • Press ? for help"
|
||||||
|
|
||||||
|
BINDINGS = [
|
||||||
|
Binding("q", "quit", "Quit", priority=True),
|
||||||
|
Binding("?", "help", "Help"),
|
||||||
|
Binding("d", "toggle_dark", "Toggle Dark Mode"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
"""Initialize the app with the home screen"""
|
||||||
|
self.push_screen(HomeScreen())
|
||||||
|
|
||||||
|
def action_help(self) -> None:
|
||||||
|
"""Show help information"""
|
||||||
|
help_text = """
|
||||||
|
# Cognee TUI Help
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
- Arrow Keys: Navigate between UI elements
|
||||||
|
- Enter: Select/activate items
|
||||||
|
- Tab: Move to next field
|
||||||
|
- Esc: Go back
|
||||||
|
|
||||||
|
## Keyboard Shortcuts
|
||||||
|
- q: Quit application
|
||||||
|
- d: Toggle dark/light mode
|
||||||
|
- ?: Show this help
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
1. Add Context: Add data sources
|
||||||
|
2. Cognify: Process data
|
||||||
|
3. Search: Query knowledge graph
|
||||||
|
4. Settings: Configure API keys
|
||||||
|
"""
|
||||||
|
self.push_screen(HelpScreen(help_text))
|
||||||
|
|
||||||
|
|
||||||
|
class HelpScreen(Screen):
|
||||||
|
"""Help screen"""
|
||||||
|
|
||||||
|
def __init__(self, help_text: str):
|
||||||
|
super().__init__()
|
||||||
|
self.help_text = help_text
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
from textual.widgets import Static, Button
|
||||||
|
from textual.containers import VerticalScroll
|
||||||
|
|
||||||
|
yield Header()
|
||||||
|
with VerticalScroll():
|
||||||
|
yield Static(self.help_text, markup=False)
|
||||||
|
yield Button("Close (Esc)", id="close", variant="primary")
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def on_button_pressed(self, event) -> None:
|
||||||
|
if event.button.id == "close":
|
||||||
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
def on_key(self, event) -> None:
|
||||||
|
if event.key == "escape":
|
||||||
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
|
||||||
|
def run_tui():
|
||||||
|
"""Entry point to run the TUI application"""
|
||||||
|
app = CogneeTUI()
|
||||||
|
app.run()
|
||||||
7
cognee/cli/tui/screens/__init__.py
Normal file
7
cognee/cli/tui/screens/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
"""Screens for Cognee TUI"""
|
||||||
|
from cognee.cli.tui.screens.home import HomeScreen
|
||||||
|
from cognee.cli.tui.screens.context import ContextScreen
|
||||||
|
from cognee.cli.tui.screens.query import QueryScreen
|
||||||
|
from cognee.cli.tui.screens.settings import SettingsScreen
|
||||||
|
|
||||||
|
__all__ = ["HomeScreen", "ContextScreen", "QueryScreen", "SettingsScreen"]
|
||||||
26
cognee/cli/tui/screens/context.py
Normal file
26
cognee/cli/tui/screens/context.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
"""Context Management Screen"""
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.widgets import Header, Footer, Button, Static
|
||||||
|
from textual.containers import Container
|
||||||
|
from textual.binding import Binding
|
||||||
|
|
||||||
|
|
||||||
|
class ContextScreen(Screen):
|
||||||
|
"""Context management screen"""
|
||||||
|
|
||||||
|
BINDINGS = [Binding("escape", "back", "Back")]
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header()
|
||||||
|
with Container():
|
||||||
|
yield Static("[bold]📁 Context Management[/bold]\n", classes="title")
|
||||||
|
yield Static("Context management features coming soon!")
|
||||||
|
yield Button("← Back", id="back_btn")
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def on_button_pressed(self, event) -> None:
|
||||||
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
def action_back(self) -> None:
|
||||||
|
self.app.pop_screen()
|
||||||
48
cognee/cli/tui/screens/home.py
Normal file
48
cognee/cli/tui/screens/home.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
"""Home Screen for Cognee TUI"""
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.widgets import Header, Footer, Button, Static
|
||||||
|
from textual.containers import Container, Vertical
|
||||||
|
|
||||||
|
|
||||||
|
class HomeScreen(Screen):
|
||||||
|
"""Main dashboard screen"""
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header()
|
||||||
|
|
||||||
|
with Container(id="menu-container", classes="center"):
|
||||||
|
yield Static("[bold cyan]🧠 Cognee Knowledge Graph Manager[/bold cyan]", classes="title")
|
||||||
|
yield Static("\nManage your AI memory and knowledge graphs\n", classes="center")
|
||||||
|
|
||||||
|
with Vertical():
|
||||||
|
yield Button("📁 Manage Context", id="context", variant="primary")
|
||||||
|
yield Button("🔍 Search & Query", id="query", variant="success")
|
||||||
|
yield Button("⚙️ Settings", id="settings", variant="default")
|
||||||
|
yield Button("❓ Help", id="help", variant="default")
|
||||||
|
yield Button("🚪 Exit", id="exit", variant="error")
|
||||||
|
|
||||||
|
yield Static("\n[dim]Use arrow keys • Enter to select[/dim]", classes="center")
|
||||||
|
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
button_id = event.button.id
|
||||||
|
|
||||||
|
if button_id == "context":
|
||||||
|
from cognee.cli.tui.screens.context import ContextScreen
|
||||||
|
self.app.push_screen(ContextScreen())
|
||||||
|
|
||||||
|
elif button_id == "query":
|
||||||
|
from cognee.cli.tui.screens.query import QueryScreen
|
||||||
|
self.app.push_screen(QueryScreen())
|
||||||
|
|
||||||
|
elif button_id == "settings":
|
||||||
|
from cognee.cli.tui.screens.settings import SettingsScreen
|
||||||
|
self.app.push_screen(SettingsScreen())
|
||||||
|
|
||||||
|
elif button_id == "help":
|
||||||
|
self.app.action_help()
|
||||||
|
|
||||||
|
elif button_id == "exit":
|
||||||
|
self.app.exit()
|
||||||
26
cognee/cli/tui/screens/query.py
Normal file
26
cognee/cli/tui/screens/query.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
"""Query Screen"""
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.widgets import Header, Footer, Button, Static
|
||||||
|
from textual.containers import Container
|
||||||
|
from textual.binding import Binding
|
||||||
|
|
||||||
|
|
||||||
|
class QueryScreen(Screen):
|
||||||
|
"""Query screen"""
|
||||||
|
|
||||||
|
BINDINGS = [Binding("escape", "back", "Back")]
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header()
|
||||||
|
with Container():
|
||||||
|
yield Static("[bold]🔍 Search & Query[/bold]\n", classes="title")
|
||||||
|
yield Static("Search features coming soon!")
|
||||||
|
yield Button("← Back", id="back_btn")
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def on_button_pressed(self, event) -> None:
|
||||||
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
def action_back(self) -> None:
|
||||||
|
self.app.pop_screen()
|
||||||
26
cognee/cli/tui/screens/settings.py
Normal file
26
cognee/cli/tui/screens/settings.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
"""Settings Screen"""
|
||||||
|
from textual.screen import Screen
|
||||||
|
from textual.app import ComposeResult
|
||||||
|
from textual.widgets import Header, Footer, Button, Static
|
||||||
|
from textual.containers import Container
|
||||||
|
from textual.binding import Binding
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsScreen(Screen):
|
||||||
|
"""Settings screen"""
|
||||||
|
|
||||||
|
BINDINGS = [Binding("escape", "back", "Back")]
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header()
|
||||||
|
with Container():
|
||||||
|
yield Static("[bold]⚙️ Settings[/bold]\n", classes="title")
|
||||||
|
yield Static("Settings features coming soon!")
|
||||||
|
yield Button("← Back", id="back_btn")
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def on_button_pressed(self, event) -> None:
|
||||||
|
self.app.pop_screen()
|
||||||
|
|
||||||
|
def action_back(self) -> None:
|
||||||
|
self.app.pop_screen()
|
||||||
3
cognee/cli/tui/widgets/__init__.py
Normal file
3
cognee/cli/tui/widgets/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""Custom widgets for Cognee TUI"""
|
||||||
|
# Custom widgets can be added here as needed
|
||||||
|
__all__ = []
|
||||||
Loading…
Add table
Reference in a new issue