Skip to content

Commit

Permalink
Accept prompt from stdin (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheR1D authored Apr 10, 2023
1 parent 8fe43f5 commit 3c5361b
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 34 deletions.
73 changes: 43 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Shell GPT
A command-line productivity tool powered by OpenAI's GPT-3.5 model. As developers, we can leverage ChatGPT capabilities to generate shell commands, code snippets, comments, and documentation, among other things. Forget about cheat sheets and notes, with this tool you can get accurate answers right in your terminal, and you'll probably find yourself reducing your daily Google searches, saving you valuable time and effort.
A command-line productivity tool powered by OpenAI's GPT-3.5 model. As developers, we can leverage AI capabilities to generate shell commands, code snippets, comments, and documentation, among other things. Forget about cheat sheets and notes, with this tool you can get accurate answers right in your terminal, and you'll probably find yourself reducing your daily Google searches, saving you valuable time and effort.

<div align="center">
<img src="https://i.ibb.co/nzPqnVd/sgpt-v0-8.gif" width="800"/>
</div>

## Installation
```shell
pip install shell-gpt==0.8.7
pip install shell-gpt==0.8.8
```
You'll need an OpenAI API key, you can generate one [here](https://beta.openai.com/account/api-keys).

Expand All @@ -22,23 +22,26 @@ sgpt "nginx default config file location"
# -> The default configuration file for Nginx is located at /etc/nginx/nginx.conf.
```
```shell
sgpt "docker show all local images"
# -> You can view all locally available Docker images by running: `docker images`
```
```shell
sgpt "mass of sun"
# -> = 1.99 × 10^30 kg
```
### Conversion
Convert various units and measurements without having to search for the conversion formula or use a separate conversion website. You can convert units such as time, distance, weight, temperature, and more.
```shell
sgpt "1 hour and 30 minutes to seconds"
# -> 5,400 seconds
```
### Summarization and analyzing
ShellGPT accepts prompt from both stdin and command line argument, you choose the most convenient input method for your preferences. Whether you prefer piping input through the terminal or specifying it directly as arguments, `sgpt` got you covered. This versatile feature is particularly useful when you need to pass file content or pipe output from other commands to the GPT models for summarization or analysis. For example, you can easily generate a git commit message based on a diff:
```shell
sgpt "1 kilometer to miles"
# -> 1 kilometer is equal to 0.62137 miles.
git diff | sgpt "Generate git commit message, my changes"
# -> Commit message: Implement Model enum and get_edited_prompt()
```
You can analyze logs from various sources by passing them using stdin or command line arguments, along with a user-friendly prompt. This enables you to quickly identify errors and get suggestions for possible solutions:
```shell
docker logs -n 20 container_name | sgpt "check logs, find errors, provide possible solutions"
# ...
```
This powerful feature simplifies the process of managing and understanding data from different sources, making it easier for you to focus on what really matters: improving your projects and applications.

### Shell commands
Have you ever found yourself forgetting common shell commands, such as `chmod`, and needing to look up the syntax online? With `--shell` or shortcut `-s` option, you can quickly find and execute the commands you need right in the terminal.
```shell
Expand All @@ -57,21 +60,18 @@ The same prompt, when used on Ubuntu, will generate a different suggestion:
sgpt -s "update my system"
# -> sudo apt update && sudo apt upgrade -y
```

Let's try some docker containers:
```shell
sgpt -s "start nginx using docker, forward 443 and 80 port, mount current folder with index.html"
# -> docker run -d -p 443:443 -p 80:80 -v $(pwd):/usr/share/nginx/html nginx
# -> Execute shell command? [y/N]: y
# ...
```
Also, we can provide some parameters name in our prompt, for example, passing output file name to ffmpeg:
We can still use pipes to pass input to `sgpt` and get shell commands as output:
```shell
sgpt -s "slow down video twice using ffmpeg, input video name \"input.mp4\" output video name \"output.mp4\""
# -> ffmpeg -i input.mp4 -filter:v "setpts=2.0*PTS" output.mp4
# -> Execute shell command? [y/N]: y
# ...
```
cat data.json | sgpt -s "curl localhost with provided json"
# -> curl -X POST -H "Content-Type: application/json" -d '{"a": 1, "b": 2, "c": 3}' http://localhost
````
We can apply additional shell magic in our prompt, in this example passing file names to ffmpeg:
```shell
ls
Expand All @@ -81,16 +81,6 @@ sgpt -s "using ffmpeg combine multiple videos into one without audio. Video file
# -> Execute shell command? [y/N]: y
# ...
```
Since ChatGPT can also do summarization and analyzing of input text, we can ask it to generate commit message:
```shell
sgpt "Generate git commit message, my changes: $(git diff)"
# -> Commit message: Implement Model enum and get_edited_prompt() func, add temperature, top_p and editor args for OpenAI request.
```
Or ask it to find error in logs and provide more details:
```shell
sgpt "check these logs, find errors, and explain what the error is about: ${docker logs -n 20 container_name}"
# ...
```
### Generating code
With `--code` parameters we can query only code as output, for example:
```shell
Expand Down Expand Up @@ -119,6 +109,29 @@ python fizz_buzz.py
# Fizz
# ...
```
We can also use pipes to pass input to `sgpt`:
```shell
cat fizz_buzz.py | python -m sgpt --code "Generate comments for each line of my code"
```
```python
# Loop through numbers 1 to 100
for i in range(1, 101):
# Check if number is divisible by both 3 and 5
if i % 3 == 0 and i % 5 == 0:
# Print "FizzBuzz" if number is divisible by both 3 and 5
print("FizzBuzz")
# Check if number is divisible by 3
elif i % 3 == 0:
# Print "Fizz" if number is divisible by 3
print("Fizz")
# Check if number is divisible by 5
elif i % 5 == 0:
# Print "Buzz" if number is divisible by 5
print("Buzz")
# If number is not divisible by 3 or 5, print the number itself
else:
print(i)
```
### Chat
To start a chat session, use the `--chat` option followed by a unique session name and a prompt. You can also use "temp" as a session name to start a temporary chat session.
Expand All @@ -128,7 +141,7 @@ sgpt --chat number "please remember my favorite number: 4"
sgpt --chat number "what would be my favorite number + 4?"
# -> Your favorite number is 4, so if we add 4 to it, the result would be 8.
```
You can also use chat sessions to iteratively improve ChatGPT's suggestions by providing additional clues.
You can also use chat sessions to iteratively improve GPT suggestions by providing additional clues.
```shell
sgpt --chat python_requst --code "make an example request to localhost using Python"
```
Expand All @@ -138,7 +151,7 @@ import requests
response = requests.get('http://localhost')
print(response.text)
```
Asking ChatGPT to add a cache to our request.
Asking AI to add a cache to our request.
```shell
sgpt --chat python_request --code "add caching"
```
Expand Down Expand Up @@ -230,7 +243,7 @@ sgpt "what are the colors of a rainbow"
```
Next time, same exact query will get results from local cache instantly. Note that `sgpt "what are the colors of a rainbow" --temperature 0.5` will make a new request, since we didn't provide `--temperature` (same applies to `--top-probability`) on previous request.
This is just some examples of what we can do using ChatGPT model, I'm sure you will find it useful for your specific use cases.
This is just some examples of what we can do using OpenAI GPT models, I'm sure you will find it useful for your specific use cases.
### Runtime configuration file
You can setup some parameters in runtime configuration file `~/.config/shell_gpt/.sgptrc`:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# pylint: disable=consider-using-with
setup(
name="shell_gpt",
version="0.8.7",
version="0.8.8",
packages=find_packages(),
install_requires=[
"typer~=0.7.0",
Expand Down
12 changes: 10 additions & 2 deletions sgpt/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
API Key is stored locally for easy use in future runs.
"""

import sys

# To allow users to use arrow keys in the REPL.
import readline # pylint: disable=unused-import
Expand Down Expand Up @@ -87,6 +87,11 @@ def main( # pylint: disable=too-many-arguments,too-many-locals
rich_help_panel="Chat Options",
),
) -> None:
stdin_passed = not sys.stdin.isatty()

if stdin_passed and not repl:
prompt = sys.stdin.read() + (prompt or "")

if not prompt and not editor and not repl:
raise MissingParameter(param_hint="PROMPT", param_type="string")

Expand All @@ -96,6 +101,9 @@ def main( # pylint: disable=too-many-arguments,too-many-locals
if chat and repl:
raise BadArgumentUsage("--chat and --repl options cannot be used together.")

if editor and stdin_passed:
raise BadArgumentUsage("--editor option cannot be used with stdin input.")

if editor:
prompt = get_edited_prompt()

Expand Down Expand Up @@ -132,7 +140,7 @@ def main( # pylint: disable=too-many-arguments,too-many-locals
caching=cache,
)

if shell and typer.confirm("Execute shell command?"):
if shell and not stdin_passed and typer.confirm("Execute shell command?"):
run_command(full_completion)


Expand Down
18 changes: 17 additions & 1 deletion tests/integration_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import json
import subprocess
import os

from time import sleep
from pathlib import Path
from unittest import TestCase
Expand Down Expand Up @@ -193,7 +194,9 @@ def test_validation_code_shell(self):
assert result.exit_code == 2
assert "--shell and --code options cannot be used together" in result.stdout

def test_repl_default(self):
def test_repl_default(
self,
):
dict_arguments = {
"prompt": "",
"--repl": "temp",
Expand Down Expand Up @@ -316,3 +319,16 @@ def test_color_output(self):
os.environ["DEFAULT_COLOR"] = "red"
handler = Handler(OpenAIClient("test", "test"))
assert handler.color == "red"

def test_simple_stdin(self):
result = runner.invoke(app, input="What is the capital of Germany?\n")
assert "Berlin" in result.stdout

def test_shell_stdin_with_prompt(self):
dict_arguments = {
"prompt": "Sort by name",
"--shell": True,
}
stdin = "What is in current folder\n"
result = runner.invoke(app, self.get_arguments(**dict_arguments), input=stdin)
assert result.stdout == "ls | sort\n"

0 comments on commit 3c5361b

Please sign in to comment.