mirror of
https://github.com/TheR1D/shell_gpt.git
synced 2026-07-03 14:10:18 +02:00
Non-interactive mode for --shell and integration changes (#455)
* Added --interaction that works with --shell option. * Changed shell integrations to use new --no-interaction option. * Moved shell integrations into dedicated file integration.py. * Changed --install-integration logic to install integrations without downloading sh scripts. * Removed validation for PROMPT argument, empty string by default. * Fixing an issue when sgpt is being called from non-interactive shell environments. * Fixed and optimised Dockerfile. * README.md improvements, and new feature examples. * New funny demo video.
This commit is contained in:
+2
-10
@@ -1,18 +1,10 @@
|
||||
FROM python:3-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE 1
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV PIP_ROOT_USER_ACTION ignore
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
|
||||
RUN pip install --no-cache --upgrade pip \
|
||||
&& pip install --no-cache /app \
|
||||
&& addgroup --system app && adduser --system --group --home /home/app app \
|
||||
&& mkdir -p /tmp/shell_gpt \
|
||||
&& chown -R app:app /tmp/shell_gpt
|
||||
|
||||
USER app
|
||||
RUN apt-get update && apt-get install -y gcc
|
||||
RUN pip install --no-cache /app && mkdir -p /tmp/shell_gpt
|
||||
|
||||
VOLUME /tmp/shell_gpt
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ShellGPT
|
||||
A command-line productivity tool powered by AI large language models (LLM). This command-line tool offers streamlined generation of **shell commands, code snippets, documentation**, eliminating the need for external resources (like Google search). Supports Linux, macOS, Windows and compatible with all major Shells like PowerShell, CMD, Bash, Zsh, etc.
|
||||
|
||||
https://github.com/TheR1D/shell_gpt/assets/16740832/721ddb19-97e7-428f-a0ee-107d027ddd59
|
||||
https://github.com/TheR1D/shell_gpt/assets/16740832/9197283c-db6a-4b46-bfea-3eb776dd9093
|
||||
|
||||
## Installation
|
||||
```shell
|
||||
@@ -35,6 +35,19 @@ Error Detected: Memory allocation failed at line 12.
|
||||
Possible Solution: Consider increasing memory allocation or optimizing application memory usage.
|
||||
```
|
||||
|
||||
You can also use all kind of redirection operators to pass input:
|
||||
```shell
|
||||
sgpt "summarise" < document.txt
|
||||
# -> The document discusses the impact...
|
||||
sgpt << EOF
|
||||
What is the best way to lear Golang.
|
||||
Provide simple hello world example.
|
||||
EOF
|
||||
# -> The best way to learn Golang...
|
||||
sgpt <<< "What is the best way to learn shell redirects?"
|
||||
# -> The best way to learn shell redirects is through...
|
||||
```
|
||||
|
||||
|
||||
### Shell commands
|
||||
Have you ever found yourself forgetting common shell commands, such as `find`, and needing to look up the syntax online? With `--shell` or shortcut `-s` option, you can quickly generate and execute the commands you need right in the terminal.
|
||||
@@ -65,14 +78,14 @@ sgpt -s "start nginx container, mount ./index.html"
|
||||
# -> [E]xecute, [D]escribe, [A]bort: e
|
||||
```
|
||||
|
||||
We can still use pipes to pass input to `sgpt` and get generate shell commands:
|
||||
We can still use pipes to pass input to `sgpt` and generate shell commands:
|
||||
```shell
|
||||
cat data.json | sgpt -s "POST localhost with json"
|
||||
sgpt -s "POST localhost with" < data.json
|
||||
# -> curl -X POST -H "Content-Type: application/json" -d '{"a": 1, "b": 2}' http://localhost
|
||||
# -> [E]xecute, [D]escribe, [A]bort: e
|
||||
```
|
||||
|
||||
Applying additional shell magic in our prompt, in this example passing file names to ffmpeg:
|
||||
Applying additional shell magic in our prompt, in this example passing file names to `ffmpeg`:
|
||||
```shell
|
||||
ls
|
||||
# -> 1.mp4 2.mp4 3.mp4
|
||||
@@ -81,9 +94,14 @@ sgpt -s "ffmpeg combine $(ls -m) into one video file without audio."
|
||||
# -> [E]xecute, [D]escribe, [A]bort: e
|
||||
```
|
||||
|
||||
If you would like to pass generated shell command using pipe, you can use `--no-interaction` option. This will disable interactive mode and will print generated command to stdout. In this example we are using `pbcopy` to copy generated command to clipboard:
|
||||
```shell
|
||||
sgpt -s "find all json files in current folder" --no-interaction | pbcopy
|
||||
```
|
||||
|
||||
|
||||
### Shell integration
|
||||
Shell integration enables the use of ShellGPT with hotkeys in your terminal, supported by both Bash and ZSH shells. This feature puts `sgpt` completions directly into terminal buffer (input line), allowing for immediate editing of suggested commands.
|
||||
This is a **very handy feature**, which allows you to use `sgpt` shell completions directly in your terminal, without the need to type `sgpt` with prompt and arguments. Shell integration enables the use of ShellGPT with hotkeys in your terminal, supported by both Bash and ZSH shells. This feature puts `sgpt` completions directly into terminal buffer (input line), allowing for immediate editing of suggested commands.
|
||||
|
||||
https://github.com/TheR1D/shell_gpt/assets/16740832/bead0dab-0dd9-436d-88b7-6abfb2c556c1
|
||||
|
||||
@@ -248,6 +266,21 @@ Entering REPL mode, press Ctrl+C to exit.
|
||||
It is a Python script that uses the random module to generate and print a random integer.
|
||||
```
|
||||
|
||||
You can also enter REPL mode with initial prompt by passing it as an argument or stdin or even both:
|
||||
```shell
|
||||
sgpt --repl temp < my_app.py
|
||||
```
|
||||
```text
|
||||
Entering REPL mode, press Ctrl+C to exit.
|
||||
──────────────────────────────────── Input ────────────────────────────────────
|
||||
name = input("What is your name?")
|
||||
print(f"Hello {name}")
|
||||
───────────────────────────────────────────────────────────────────────────────
|
||||
>>> What is this code about?
|
||||
The snippet of code you've provided is written in Python. It prompts the user...
|
||||
>>> Follow up questions...
|
||||
```
|
||||
|
||||
### Function calling
|
||||
[Function calls](https://platform.openai.com/docs/guides/function-calling) is a powerful feature OpenAI provides. It allows LLM to execute functions in your system, which can be used to accomplish a variety of tasks. To install [default functions](https://github.com/TheR1D/shell_gpt/tree/main/sgpt/default_functions/) run:
|
||||
```shell
|
||||
@@ -392,6 +425,7 @@ Possible options for `CODE_THEME`: https://pygments.org/styles/
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭─ Assistance Options ─────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ --shell -s Generate and execute shell commands. │
|
||||
│ --interaction --no-interaction Interactive mode for --shell option. [default: interaction] │
|
||||
│ --describe-shell -d Describe a shell command. │
|
||||
│ --code -c Generate only code. │
|
||||
│ --functions --no-functions Allow function calls. [default: functions] │
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = "1.1.0"
|
||||
__version__ = "1.2.0"
|
||||
|
||||
+21
-13
@@ -1,10 +1,11 @@
|
||||
# To allow users to use arrow keys in the REPL.
|
||||
import os
|
||||
|
||||
# To allow users to use arrow keys in the REPL.
|
||||
import readline # noqa: F401
|
||||
import sys
|
||||
|
||||
import typer
|
||||
from click import BadArgumentUsage, MissingParameter
|
||||
from click import BadArgumentUsage
|
||||
from click.types import Choice
|
||||
|
||||
from sgpt.config import cfg
|
||||
@@ -24,7 +25,7 @@ from sgpt.utils import (
|
||||
|
||||
def main(
|
||||
prompt: str = typer.Argument(
|
||||
None,
|
||||
"",
|
||||
show_default=False,
|
||||
help="The prompt to generate completions for.",
|
||||
),
|
||||
@@ -51,6 +52,11 @@ def main(
|
||||
help="Generate and execute shell commands.",
|
||||
rich_help_panel="Assistance Options",
|
||||
),
|
||||
interaction: bool = typer.Option(
|
||||
True,
|
||||
help="Interactive mode for --shell option.",
|
||||
rich_help_panel="Assistance Options",
|
||||
),
|
||||
describe_shell: bool = typer.Option(
|
||||
False,
|
||||
"--describe-shell",
|
||||
@@ -156,20 +162,22 @@ def main(
|
||||
# but rest of the stdin to be used as a inputs. For example:
|
||||
# echo "hello\n__sgpt__eof__\nThis is input" | sgpt --repl temp
|
||||
# In this case, "hello" will be used as a init prompt, and
|
||||
# "This is input" will be used as a input to the REPL.
|
||||
# "This is input" will be used as "interactive" input to the REPL.
|
||||
# This is useful to test REPL with some initial context.
|
||||
for line in sys.stdin:
|
||||
if "__sgpt__eof__" in line:
|
||||
break
|
||||
stdin += line
|
||||
prompt = f"{stdin}\n\n{prompt}" if prompt else stdin
|
||||
# Switch to stdin for interactive input.
|
||||
if os.name == "posix":
|
||||
sys.stdin = open("/dev/tty", "r")
|
||||
elif os.name == "nt":
|
||||
sys.stdin = open("CON", "r")
|
||||
|
||||
if not prompt and not editor and not repl:
|
||||
raise MissingParameter(param_hint="PROMPT", param_type="string")
|
||||
try:
|
||||
# Switch to stdin for interactive input.
|
||||
if os.name == "posix":
|
||||
sys.stdin = open("/dev/tty", "r")
|
||||
elif os.name == "nt":
|
||||
sys.stdin = open("CON", "r")
|
||||
except OSError:
|
||||
# Non-interactive shell.
|
||||
pass
|
||||
|
||||
if sum((shell, describe_shell, code)) > 1:
|
||||
raise BadArgumentUsage(
|
||||
@@ -225,7 +233,7 @@ def main(
|
||||
functions=function_schemas,
|
||||
)
|
||||
|
||||
while shell:
|
||||
while shell and interaction:
|
||||
option = typer.prompt(
|
||||
text="[E]xecute, [D]escribe, [A]bort",
|
||||
type=Choice(("e", "d", "a", "y"), case_sensitive=False),
|
||||
|
||||
@@ -35,7 +35,6 @@ class Handler:
|
||||
with Live(
|
||||
Markdown(markup="", code_theme=self.theme_name),
|
||||
console=Console(),
|
||||
refresh_per_second=8,
|
||||
) as live:
|
||||
if self.disable_stream:
|
||||
live.update(
|
||||
@@ -45,7 +44,7 @@ class Handler:
|
||||
for word in self.get_completion(messages=messages, **kwargs):
|
||||
full_completion += word
|
||||
live.update(
|
||||
Markdown(full_completion, code_theme=self.theme_name),
|
||||
Markdown(markup=full_completion, code_theme=self.theme_name),
|
||||
refresh=not self.disable_stream,
|
||||
)
|
||||
return full_completion
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
bash_integration = """
|
||||
# Shell-GPT integration BASH v0.2
|
||||
_sgpt_bash() {
|
||||
if [[ -n "$READLINE_LINE" ]]; then
|
||||
READLINE_LINE=$(sgpt --shell <<< "$READLINE_LINE" --no-interaction)
|
||||
READLINE_POINT=${#READLINE_LINE}
|
||||
fi
|
||||
}
|
||||
bind -x '"\\C-l": _sgpt_bash'
|
||||
# Shell-GPT integration BASH v0.2
|
||||
"""
|
||||
|
||||
zsh_integration = """
|
||||
# Shell-GPT integration ZSH v0.2
|
||||
_sgpt_zsh() {
|
||||
if [[ -n "$BUFFER" ]]; then
|
||||
_sgpt_prev_cmd=$BUFFER
|
||||
BUFFER+="⌛"
|
||||
zle -I && zle redisplay
|
||||
BUFFER=$(sgpt --shell <<< "$_sgpt_prev_cmd" --no-interaction)
|
||||
zle end-of-line
|
||||
fi
|
||||
}
|
||||
zle -N _sgpt_zsh
|
||||
bindkey ^l _sgpt_zsh
|
||||
# Shell-GPT integration ZSH v0.2
|
||||
"""
|
||||
+16
-7
@@ -5,9 +5,10 @@ from tempfile import NamedTemporaryFile
|
||||
from typing import Any, Callable
|
||||
|
||||
import typer
|
||||
from click import BadParameter
|
||||
from click import BadParameter, UsageError
|
||||
|
||||
from sgpt.__version__ import __version__
|
||||
from sgpt.integration import bash_integration, zsh_integration
|
||||
|
||||
|
||||
def get_edited_prompt() -> str:
|
||||
@@ -65,17 +66,25 @@ def option_callback(func: Callable) -> Callable: # type: ignore
|
||||
@option_callback
|
||||
def install_shell_integration(*_args: Any) -> None:
|
||||
"""
|
||||
Installs shell integration. Currently only supports Linux.
|
||||
Installs shell integration. Currently only supports ZSH and Bash.
|
||||
Allows user to get shell completions in terminal by using hotkey.
|
||||
Allows user to edit shell command right away in terminal.
|
||||
Replaces current "buffer" of the shell with the completion.
|
||||
"""
|
||||
# TODO: Add support for Windows.
|
||||
# TODO: Implement updates.
|
||||
if platform.system() == "Windows":
|
||||
typer.echo("Windows is not supported yet.")
|
||||
shell = os.getenv("SHELL", "")
|
||||
if shell == "/bin/zsh":
|
||||
typer.echo("Installing ZSH integration...")
|
||||
with open(os.path.expanduser("~/.zshrc"), "a", encoding="utf-8") as file:
|
||||
file.write(zsh_integration)
|
||||
elif shell == "/bin/bash":
|
||||
typer.echo("Installing Bash integration...")
|
||||
with open(os.path.expanduser("~/.bashrc"), "a", encoding="utf-8") as file:
|
||||
file.write(bash_integration)
|
||||
else:
|
||||
url = "https://raw.githubusercontent.com/TheR1D/shell_gpt/shell-integrations/install.sh"
|
||||
os.system(f'sh -c "$(curl -fsSL {url})"')
|
||||
raise UsageError("ShellGPT integrations only available for ZSH and Bash.")
|
||||
|
||||
typer.echo("Done! Restart your shell to apply changes.")
|
||||
|
||||
|
||||
@option_callback
|
||||
|
||||
@@ -159,3 +159,21 @@ def test_shell_and_describe_shell(completion):
|
||||
completion.assert_not_called()
|
||||
assert result.exit_code == 2
|
||||
assert "Error" in result.stdout
|
||||
|
||||
|
||||
@patch("openai.resources.chat.Completions.create")
|
||||
def test_shell_no_interaction(completion):
|
||||
completion.return_value = comp_chunks("git commit -m test")
|
||||
role = SystemRole.get(DefaultRoles.SHELL.value)
|
||||
|
||||
args = {
|
||||
"prompt": "make a commit using git",
|
||||
"--shell": True,
|
||||
"--no-interaction": True,
|
||||
}
|
||||
result = runner.invoke(app, cmd_args(**args))
|
||||
|
||||
completion.assert_called_once_with(**comp_args(role, args["prompt"]))
|
||||
assert result.exit_code == 0
|
||||
assert "git commit" in result.stdout
|
||||
assert "[E]xecute" not in result.stdout
|
||||
|
||||
Reference in New Issue
Block a user