-
-
Notifications
You must be signed in to change notification settings - Fork 52
[#249] Interactive command suggestions with fuzzy search #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add SuggestionDatabase with 500+ curated package stacks - Add IntentMatcher with fuzzy matching on keywords - Add interactive TUI with scrolling viewport - Integrate suggest() method in CLI - Support 14 intents and 7 languages - No LLM needed - works entirely offline
- Add JSON error handling with informative messages - Specify UTF-8 encoding when opening JSON file - Fix multi-word keyword matching to use word boundaries - Remove unused imports (Text, detect_hardware) - Fix misleading UI hints in non-interactive mode - Fix error message to reference suggestions.json
WalkthroughIntroduces an interactive suggestion system for package discovery with fuzzy matching, intent detection, and hardware-aware filtering. Adds three new modules: SuggestionDatabase (JSON-backed suggestions data layer), IntentMatcher (intent and keyword-based stack ranking), and SuggestionTUI (interactive terminal UI with prompt_toolkit support). Updates CLI to route queries directly to suggestions workflow, removes the parallel parameter from install method signature, and integrates hardware detection for GPU-aware recommendations. Changes
Sequence DiagramsequenceDiagram
participant User
participant CLI as cortex/cli.py<br/>(CortexCLI.suggest)
participant DB as cortex/database.py<br/>(SuggestionDatabase)
participant Matcher as cortex/matcher.py<br/>(IntentMatcher)
participant TUI as cortex/tui.py<br/>(SuggestionTUI)
participant Hardware as Hardware<br/>Detection
User->>CLI: suggest("docker")
activate CLI
CLI->>DB: Load suggestions from JSON
activate DB
DB->>DB: Build indexes (intents, stacks, languages)
DB-->>CLI: SuggestionDatabase ready
deactivate DB
CLI->>TUI: Initialize with DB and Matcher
activate TUI
TUI->>Matcher: Create IntentMatcher(db)
activate Matcher
Matcher->>Matcher: Build keyword index from stacks
Matcher-->>TUI: Matcher ready
deactivate Matcher
TUI->>Hardware: Detect GPU/CPU/RAM
activate Hardware
Hardware-->>TUI: Hardware info
deactivate Hardware
TUI->>Matcher: match("docker", limit=10)
activate Matcher
Matcher->>DB: detect_intent_from_keywords(tokens)
Matcher->>DB: get_stacks_by_intent_and_language(...)
Matcher->>Matcher: Score and rank results
Matcher-->>TUI: Ranked stacks list
deactivate Matcher
Note over TUI: Interactive flow: render table,<br/>keyboard navigation (↑↓),<br/>Enter to select, Tab for preview
TUI->>User: Display suggestions + navigate
User->>TUI: Select stack (Enter)
TUI->>Matcher: get_install_preview(selected_stack)
activate Matcher
Matcher->>DB: get_apt_packages_for_stack(...)
Matcher->>DB: get_pip_packages_for_stack(...)
Matcher-->>TUI: Preview with commands
deactivate Matcher
TUI->>User: Show install preview
User->>TUI: Confirm (Enter)
TUI->>CLI: Return selected item
deactivate TUI
CLI->>CLI: Execute install with selected stack
deactivate CLI
CLI-->>User: Install complete
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
cortex/matcher.py (1)
354-386: Consider adding Args/Returns to the docstring.The method works correctly, but the docstring could be more complete per coding guidelines requiring docstrings for public APIs.
🔎 Suggested docstring improvement
def get_install_preview(self, item_type: str, item_id: str) -> dict: - """Get installation preview for a stack.""" + """Get installation preview for a stack. + + Args: + item_type: Type of item (e.g., "stack"). + item_id: Identifier of the stack. + + Returns: + Dictionary with installation details including packages, + commands, and metadata. Contains "error" key if stack not found. + """cortex/database.py (1)
137-149: Consider using an index forget_intentandget_language.These methods use linear search while
get_stackandget_packageuse indexed lookup. If intent/language counts are small, this is acceptable, but adding indexes would be consistent.cortex/cli.py (1)
166-168: Move imports to module level for consistency.While inline imports work, moving
subprocessandrich.progressimports to the top of the file is more consistent with PEP 8 style.cortex/tui.py (1)
370-383: Consider reusing the database instance in CLI entry point.Line 378 creates a new
PackageDatabase()andFuzzyMatcher()whenrun_suggestionsalready used one. This is minor since it's only for the CLI test path.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
cortex/cli.pycortex/database.pycortex/matcher.pycortex/tui.pydata/suggestions.jsontests/test_cli.pytests/test_cli_extended.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs
Files:
tests/test_cli.pycortex/matcher.pycortex/cli.pycortex/tui.pycortex/database.pytests/test_cli_extended.py
tests/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Maintain >80% test coverage for pull requests
Files:
tests/test_cli.pytests/test_cli_extended.py
🧠 Learnings (1)
📚 Learning: 2025-12-11T12:03:24.071Z
Learnt from: CR
Repo: cortexlinux/cortex PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-11T12:03:24.071Z
Learning: Applies to **/*install*.py : Implement audit logging to ~/.cortex/history.db for all package operations
Applied to files:
cortex/cli.py
🧬 Code graph analysis (4)
tests/test_cli.py (1)
cortex/cli.py (1)
main(721-874)
cortex/matcher.py (1)
cortex/database.py (8)
stacks(121-123)detect_language_from_keywords(219-224)detect_intent_from_keywords(161-217)intents(111-113)get_stack(151-153)get_stacks_by_intent_and_language(236-241)get_stacks_by_language(232-234)get_stacks_by_intent(228-230)
cortex/cli.py (4)
cortex/tui.py (1)
run_suggestions(319-345)cortex/database.py (2)
SuggestionDatabase(11-283)packages(126-128)cortex/matcher.py (2)
IntentMatcher(26-386)get_install_preview(354-386)cortex/validators.py (1)
validate_install_request(117-144)
cortex/tui.py (1)
cortex/matcher.py (2)
match(202-352)get_install_preview(354-386)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Build Package
- GitHub Check: test (3.12)
- GitHub Check: test (3.11)
- GitHub Check: test (3.10)
🔇 Additional comments (39)
cortex/matcher.py (7)
1-24: LGTM! Module docstring and imports are well-structured.The module docstring clearly explains the matching strategy with examples. Imports are appropriate.
26-71: LGTM! Well-documented scoring constants and stop words.The class docstring clearly explains the scoring priority, and constants are appropriately defined at the class level.
73-103: LGTM! Initialization and keyword indexing are well-implemented.The
__init__has proper docstring with Args section, and_build_keyword_indexcorrectly builds a reverse index from keywords to stacks.
105-136: LGTM! Tokenization and context detection are well-implemented.The methods correctly handle query normalization and multi-word keyword matching.
138-168: LGTM! Keyword scoring logic is sensible.The scoring hierarchy (exact > partial > name > description) is appropriate for relevance ranking.
202-352: LGTM! Comprehensive matching logic with multiple strategies.The method correctly implements the documented scoring priority with appropriate hardware-aware filtering and bonus scoring.
389-390: LGTM! Backward compatibility alias is appropriate.The alias allows existing code referencing
FuzzyMatcherto continue working.cortex/database.py (9)
1-10: LGTM! Module docstring and imports are appropriate.The module clearly states its purpose. Imports are minimal and appropriate.
11-38: LGTM! Class and initialization are well-structured.The class docstring clearly explains the database structure, and initialization flow is correct.
40-54: LGTM! Database discovery with sensible fallback paths.The search order from project-relative to system-wide paths is appropriate, with backward compatibility for
packages.json.
56-70: LGTM! Robust JSON loading with helpful error messages.The error handling includes file path, error message, and line number for debugging.
72-106: LGTM! Efficient index building for fast lookups.The indexing strategy enables O(1) lookups for stacks by ID, intent, and language.
108-133: LGTM! Properties are clean and well-documented.
161-217: LGTM! Thoughtful multi-pass intent detection with clear priority.The six-pass approach correctly handles ambiguity between intent keywords and language keywords, prioritizing more specific matches.
226-249: LGTM! Filtering methods are efficient and correct.The intersection logic for
get_stacks_by_intent_and_languagecorrectly uses set operations.
251-287: LGTM! Package methods and backward compatibility aliases are well-designed.The aliases ensure smooth migration from older API usage.
tests/test_cli_extended.py (3)
283-283: LGTM! Test assertion updated to match new API signature.Correctly removes the
parallelparameter from the expected call.
291-291: LGTM! Test assertion updated to match new API signature.
299-299: LGTM! Test assertion updated to match new API signature.tests/test_cli.py (3)
187-187: LGTM! Test assertion updated to match new API signature.
195-195: LGTM! Test assertion updated to match new API signature.
203-203: LGTM! Test assertion updated to match new API signature.cortex/cli.py (8)
7-12: Logging suppression and path modification look reasonable.Suppressing noisy logs and adding parent directory to path for imports is acceptable for CLI tooling.
31-38: LGTM! Graceful degradation for optional suggestion system.The try/except pattern allows the CLI to function even without the suggestion modules.
304-305: LGTM! API signature updated with clear docstring.The
parallelparameter removal is reflected in tests. The docstring accurately describes the new behavior.
665-671: Demo method is a minimal placeholder.The comment indicates existing demo logic should be preserved. Verify this is intentional or if more functionality should be added.
721-755: LGTM! Smart early routing for suggestion queries.The approach of intercepting queries before argparse avoids "unrecognized command" errors for natural language input like
cortex docker.
683-683: LGTM! Helpful quick tip added to help output.
849-849: LGTM! Install call updated to match new signature.
189-201: Add installation history logging to thesuggest()method.The
suggest()method executes installation commands (lines 189-201) but does not record them to the installation history database, unlike theinstall()method. To maintain a complete audit trail of all package operations, instantiateInstallationHistory, extract packages from the commands, and callrecord_installation()after successful execution, similar to how it's done in theinstall()method (lines 313, 353).⛔ Skipped due to learnings
Learnt from: CR Repo: cortexlinux/cortex PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-12-11T12:03:24.071Z Learning: Applies to **/*install*.py : Implement audit logging to ~/.cortex/history.db for all package operationscortex/tui.py (9)
1-17: LGTM! Module setup with appropriate imports.The docstring clearly identifies the module purpose. Imports are minimal and appropriate.
20-33: LGTM! Class initialization is well-structured.The class has a proper docstring with Args section and appropriate type hints.
35-80: LGTM! Search and rendering logic is clean.The methods have proper docstrings and type hints. The rendering correctly handles empty results and selection highlighting.
82-109: LGTM! Preview rendering with appropriate truncation.The method handles the no-selection case gracefully and limits displayed packages to avoid overwhelming the UI.
195-247: LGTM! Scrolling viewport and display logic are well-implemented.The scroll indicators and viewport calculations are correct.
253-287: LGTM! Full details display is comprehensive and clean.
289-316: LGTM! Simple mode provides a clean fallback.The method handles both invalid input and keyboard interrupts gracefully.
319-345: LGTM! Entry point function with proper error handling and documentation.
348-367: LGTM! Scripting-friendly function with error handling.
| def _fuzzy_match(self, query: str, target: str) -> float: | ||
| """ | ||
| Simple fuzzy matching score. | ||
| Returns value between 0 and 1. | ||
| """ | ||
| if not query or not target: | ||
| return 0.0 | ||
|
|
||
| query = query.lower() | ||
| target = target.lower() | ||
|
|
||
| if query == target: | ||
| return 1.0 | ||
|
|
||
| if query in target or target in query: | ||
| return 0.8 | ||
|
|
||
| # Check if query is a prefix | ||
| if target.startswith(query): | ||
| return 0.9 | ||
|
|
||
| # Character overlap score | ||
| query_chars = set(query) | ||
| target_chars = set(target) | ||
| overlap = len(query_chars & target_chars) | ||
| total = len(query_chars | target_chars) | ||
|
|
||
| if total == 0: | ||
| return 0.0 | ||
|
|
||
| return overlap / total * 0.5 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefix matching is unreachable due to condition ordering.
The prefix check at line 188 (target.startswith(query)) will never be reached because a prefix is also a substring, and the substring check at line 184 (query in target) returns 0.8 first. If prefix matching should score higher (0.9), reorder the conditions.
🔎 Proposed fix
if query == target:
return 1.0
+ # Check if query is a prefix (more specific than substring)
+ if target.startswith(query):
+ return 0.9
+
if query in target or target in query:
return 0.8
- # Check if query is a prefix
- if target.startswith(query):
- return 0.9
-
# Character overlap score🤖 Prompt for AI Agents
In cortex/matcher.py around lines 170 to 200, the prefix check
target.startswith(query) is unreachable because the earlier substring check
(query in target or target in query) matches prefixes first and returns 0.8;
move the prefix check above the generic substring check so exact-prefix matches
return 0.9, keeping the exact-equality check at the top and leaving the rest of
the fallback logic unchanged.
| @kb.add("tab") | ||
| def _(event): | ||
| event.app.exit() | ||
| self.show_full_details() | ||
| # Restart the app | ||
| event.app.run() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tab handler may not work as intended after exit.
Calling event.app.exit() followed by event.app.run() within the same key binding handler has undefined behavior. The run() call at line 185 occurs after the app has already exited, so it likely won't restart the interactive session properly.
🔎 Consider restructuring the Tab flow
One approach is to use a loop outside the app or set a flag to indicate "show details and restart":
# In run_interactive, use a loop pattern:
while True:
app.run()
if self.show_details_requested:
self.show_full_details()
self.show_details_requested = False
continue
break🤖 Prompt for AI Agents
In cortex/tui.py around lines 180-185, the Tab key handler currently calls
event.app.exit() and then event.app.run() which is undefined; instead set a flag
(e.g. self.show_details_requested = True) in the handler and only call
event.app.exit(), removing the direct run() call. Then restructure the outer
run_interactive loop to repeatedly call app.run(), check the flag after run
returns, call self.show_full_details() when the flag is set, clear the flag, and
continue the loop to restart the UI; this ensures exiting and restarting are
coordinated outside the key-binding handler.
|
@ShreeJejurikar
2. show installation plan /dry run before confirmation
|
|
closed because it is redundant |
|
Approved checked after discussion |





Related Issue
Closes #249
Summary
Implements an interactive command suggestion system with fuzzy search for Cortex.
Features
Demo
Checklist
pytest tests/)[#249] DescriptionSummary by CodeRabbit
New Features
Removed
Changes
✏️ Tip: You can customize this high-level summary in your review settings.