Skip to content

Commit d77a321

Browse files
feat: shell hotkey integration (#297)
Shell hotkey integration - CI passing, reviewed by CodeRabbit
1 parent 09cb6ab commit d77a321

File tree

7 files changed

+161
-43
lines changed

7 files changed

+161
-43
lines changed

.github/workflows/codeql.yml

Lines changed: 0 additions & 43 deletions
This file was deleted.

cortex/cli.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,20 @@ def show_rich_help():
550550
console.print("[dim]Learn more: https://cortexlinux.com/docs[/dim]")
551551

552552

553+
def shell_suggest(text: str) -> int:
554+
"""
555+
Internal helper used by shell hotkey integration.
556+
Prints a single suggested command to stdout.
557+
"""
558+
try:
559+
from cortex.shell_integration import suggest_command
560+
suggestion = suggest_command(text)
561+
if suggestion:
562+
print(suggestion)
563+
return 0
564+
except Exception:
565+
return 1
566+
553567
def main():
554568
parser = argparse.ArgumentParser(
555569
prog='cortex',
@@ -655,5 +669,7 @@ def main():
655669
print(f"❌ Unexpected error: {e}", file=sys.stderr)
656670
return 1
657671

672+
673+
658674
if __name__ == '__main__':
659675
sys.exit(main())

cortex/shell_installer.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import os
2+
from pathlib import Path
3+
4+
BASH_MARKER = "# >>> cortex shell integration >>>"
5+
ZSH_MARKER = "# >>> cortex shell integration >>>"
6+
7+
8+
def _append_if_missing(rc_path: Path, block: str) -> bool:
9+
if rc_path.exists():
10+
content = rc_path.read_text()
11+
if BASH_MARKER in content:
12+
return False
13+
else:
14+
rc_path.touch()
15+
16+
with rc_path.open("a", encoding="utf-8") as f:
17+
f.write("\n" + block + "\n")
18+
19+
return True
20+
21+
22+
def install_shell_integration() -> str:
23+
shell = os.environ.get("SHELL", "")
24+
home = Path.home()
25+
26+
if shell.endswith("bash"):
27+
rc = home / ".bashrc"
28+
script_path = Path(__file__).resolve().parent.parent / "scripts" / "cortex_bash.sh"
29+
block = f"""{BASH_MARKER}
30+
source "{script_path}"
31+
# <<< cortex shell integration <<<
32+
"""
33+
installed = _append_if_missing(rc, block)
34+
return "bash", installed
35+
36+
elif shell.endswith("zsh"):
37+
rc = home / ".zshrc"
38+
script_path = Path(__file__).resolve().parent.parent / "scripts" / "cortex_zsh.zsh"
39+
block = f"""{ZSH_MARKER}
40+
source "{script_path}"
41+
# <<< cortex shell integration <<<
42+
"""
43+
installed = _append_if_missing(rc, block)
44+
return "zsh", installed
45+
46+
else:
47+
raise RuntimeError("Unsupported shell. Only bash and zsh are supported.")
48+
49+

cortex/shell_integration.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""
2+
Shell integration helpers for Cortex.
3+
4+
This module is used by Bash/Zsh hotkey bindings to convert
5+
natural language input into a suggested shell command.
6+
"""
7+
8+
from typing import Optional
9+
10+
11+
def suggest_command(user_input: str) -> Optional[str]:
12+
"""
13+
Generate a shell command suggestion from free-form user input.
14+
15+
Args:
16+
user_input (str): Text currently typed in the shell.
17+
18+
Returns:a
19+
Optional[str]: Suggested shell command or None if no suggestion.
20+
"""
21+
user_input = user_input.strip()
22+
if not user_input:
23+
return None
24+
25+
# Import here to keep shell integration lightweight
26+
try:
27+
from cortex.interpreter import interpret
28+
except Exception:
29+
return None
30+
31+
try:
32+
result = interpret(user_input)
33+
except Exception:
34+
return None
35+
36+
if not result:
37+
return None
38+
39+
# Expected result structure:
40+
# {
41+
# "command": "sudo apt install docker",
42+
# ...
43+
# }
44+
command = result.get("command")
45+
if not isinstance(command, str):
46+
return None
47+
48+
return command.strip()

scripts/cortex_bash.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Cortex Bash shell integration
2+
# Binds Ctrl+L to send current input to Cortex and replace it with a suggestion
3+
4+
_cortex_suggest() {
5+
# READLINE_LINE contains the current command line
6+
local input="$READLINE_LINE"
7+
8+
# Call cortex shell suggestion helper
9+
local suggestion
10+
suggestion="$(cortex _shell_suggest "$input" 2>/dev/null)"
11+
12+
# If we got a suggestion, replace the current line
13+
if [[ -n "$suggestion" ]]; then
14+
READLINE_LINE="$suggestion"
15+
READLINE_POINT=${#READLINE_LINE}
16+
fi
17+
}
18+
19+
# Bind Ctrl+L to cortex suggestion
20+
bind -x '"\C-l": _cortex_suggest'

scripts/cortex_zsh.zsh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Cortex Zsh shell integration
2+
# Binds Ctrl+L to send current input to Cortex and replace it with a suggestion
3+
4+
_cortex_suggest() {
5+
local input="$BUFFER"
6+
local suggestion
7+
8+
suggestion="$(cortex _shell_suggest "$input" 2>/dev/null)"
9+
10+
if [[ -n "$suggestion" ]]; then
11+
BUFFER="$suggestion"
12+
CURSOR=${#BUFFER}
13+
fi
14+
}
15+
16+
zle -N _cortex_suggest
17+
bindkey '^L' _cortex_suggest

tests/test_shell_integration.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from cortex.shell_integration import suggest_command
2+
3+
4+
def test_suggest_command_empty():
5+
assert suggest_command("") is None
6+
7+
8+
def test_suggest_command_text():
9+
# We only check that it does not crash
10+
result = suggest_command("install docker")
11+
assert result is None or isinstance(result, str)

0 commit comments

Comments
 (0)