openai-please 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +21 -23
- data/lib/please/cli.rb +77 -44
- data/lib/please/context.rb +76 -0
- data/lib/please/openai/codex_service.rb +1 -1
- data/lib/please/request.rb +16 -13
- data/lib/please/version.rb +1 -1
- data/lib/please.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ffc331fe72fcbe5a6feec2f0ed039c7aeb453039e40ae533a05f2ff794447ef5
|
4
|
+
data.tar.gz: 55771e4ff93f8b209cda88778ac2eb6ed8118201c3c8d77d2ef61143f747097e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d40360b108300c1c6e862ffa1ef0ed1339cb5860eb074a0a241f5fd0f631fd802c0f9573ba254fced1a5dfdccc13c71b3cadf39e2aeb05ea51136cfba3e5040
|
7
|
+
data.tar.gz: 0c9978a654d714ff870863bbe5ba0a745073528906999ff0ba6d3513444cb5c31dcf1b34247359e13c76a4bcd92823b2c609b2b11493ccc43277d42d4382a1f7
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
please (0.1.
|
4
|
+
openai-please (0.1.1)
|
5
5
|
ruby-openai (>= 1.3)
|
6
6
|
tty-prompt (>= 0.23)
|
7
7
|
|
@@ -60,7 +60,7 @@ PLATFORMS
|
|
60
60
|
|
61
61
|
DEPENDENCIES
|
62
62
|
minitest (~> 5.0)
|
63
|
-
please!
|
63
|
+
openai-please!
|
64
64
|
rake (~> 13.0)
|
65
65
|
rubocop (~> 1.7)
|
66
66
|
|
data/README.md
CHANGED
@@ -1,39 +1,37 @@
|
|
1
1
|
# Please
|
2
2
|
|
3
|
-
|
3
|
+
**⚠️ Requires an access token for OpenAI Codex, which is currently in private beta**
|
4
4
|
|
5
|
-
|
5
|
+
Convert natural language to bash commands using OpenAI Codex
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
Add this line to your application's Gemfile:
|
7
|
+

|
10
8
|
|
11
|
-
|
12
|
-
gem 'please'
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
9
|
+
## Installation
|
16
10
|
|
17
|
-
$
|
11
|
+
$ gem install openai-please
|
18
12
|
|
19
|
-
|
13
|
+
Ensure your [OpenAI API key](https://help.openai.com/en/articles/5480100-how-do-i-gain-access-to-openai-codex) is stored in an environment variable.
|
20
14
|
|
21
|
-
|
15
|
+
```
|
16
|
+
OPENAI_ACCESS_TOKEN=[YOUR ACCESS TOKEN HERE]
|
17
|
+
```
|
22
18
|
|
23
19
|
## Usage
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
```shell
|
22
|
+
user@host:~$ please find all files larger than 1 mb
|
23
|
+
$ find . -type f -size +1M
|
24
|
+
Run the command? (enter "h" for help) [y,n,e,h]
|
25
|
+
```
|
30
26
|
|
31
|
-
|
27
|
+
You can [e]dit the command before running it by pressing the 'e' key. This uses the editor specified in the $EDITOR variable, defaulting to vi.
|
32
28
|
|
33
|
-
##
|
29
|
+
## Privacy
|
34
30
|
|
35
|
-
|
31
|
+
In addition to the instruction text, the result of each of the following commands is sent to OpenAI Codex to improve the relevance of completions.
|
36
32
|
|
37
|
-
|
33
|
+
- `pwd`
|
34
|
+
- `uname -a`
|
35
|
+
- `ls -a`
|
38
36
|
|
39
|
-
|
37
|
+
See [OpenAI's privacy policy](https://beta.openai.com/policies/privacy-policy) for more information.
|
data/lib/please/cli.rb
CHANGED
@@ -1,67 +1,100 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'English'
|
3
4
|
require 'tty-prompt'
|
4
5
|
require 'optparse'
|
5
6
|
require 'please'
|
6
7
|
require 'tempfile'
|
7
8
|
|
8
|
-
|
9
|
+
begin
|
10
|
+
tty_prompt = TTY::Prompt.new
|
9
11
|
|
10
|
-
options = {
|
12
|
+
options = {
|
13
|
+
show_prompt: false,
|
14
|
+
send_pwd: true,
|
15
|
+
send_uname: true,
|
16
|
+
send_ls: true,
|
17
|
+
}
|
11
18
|
|
12
|
-
USAGE = 'Usage: please [options] <instruction>'
|
19
|
+
USAGE = 'Usage: please [options] <instruction>'
|
13
20
|
|
14
|
-
OptionParser.new do |opts|
|
15
|
-
|
16
|
-
end.parse!
|
21
|
+
OptionParser.new do |opts|
|
22
|
+
opts.banner = USAGE
|
17
23
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
24
|
+
opts.on('--show-prompt', 'Output the prompt that would ordinarily be sent to OpenAI Codex, and then exit') do |v|
|
25
|
+
options[:show_prompt] = v
|
26
|
+
end
|
22
27
|
|
23
|
-
|
28
|
+
opts.on('--[no-]send-pwd', 'Send the result of `pwd` as part of the prompt') do |v|
|
29
|
+
options[:send_pwd] = v
|
30
|
+
end
|
24
31
|
|
25
|
-
|
32
|
+
opts.on('--[no-]send-uname', 'Send the result of `uname -a` as part of the prompt') do |v|
|
33
|
+
options[:send_uname] = v
|
34
|
+
end
|
26
35
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
36
|
+
opts.on('--[no-]send-ls', 'Send the result of `ls -a` as part of the prompt') do |v|
|
37
|
+
options[:send_ls] = v
|
38
|
+
end
|
39
|
+
end.parse!
|
31
40
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
)
|
41
|
+
access_token = ENV.fetch('OPENAI_ACCESS_TOKEN') do
|
42
|
+
tty_prompt.error 'Ensure the OPENAI_ACCESS_TOKEN environment variable is set'
|
43
|
+
exit 1
|
44
|
+
end
|
37
45
|
|
38
|
-
|
46
|
+
codex_service = Please::OpenAI::CodexService.new(access_token: access_token)
|
39
47
|
|
40
|
-
|
41
|
-
print '$ '
|
42
|
-
tty_prompt.ok command
|
48
|
+
instruction = ARGV.join(' ')
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
q.choice key: 'e', name: 'Edit command before running', value: :edit
|
50
|
+
if instruction.empty?
|
51
|
+
tty_prompt.error USAGE
|
52
|
+
exit 1
|
48
53
|
end
|
49
54
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
55
|
+
context = Please::Context.new(options)
|
56
|
+
|
57
|
+
request = Please::Request.new(
|
58
|
+
instruction: instruction,
|
59
|
+
context: context,
|
60
|
+
codex_service: codex_service,
|
61
|
+
)
|
62
|
+
|
63
|
+
if options[:show_prompt]
|
64
|
+
tty_prompt.say request.prompt
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
|
68
|
+
command = request.send
|
69
|
+
|
70
|
+
loop do
|
71
|
+
print '$ '
|
72
|
+
tty_prompt.ok command
|
73
|
+
|
74
|
+
action = tty_prompt.expand('Run the command?') do |q|
|
75
|
+
q.choice key: 'y', name: 'Yes', value: :run
|
76
|
+
q.choice key: 'n', name: 'No', value: :abort
|
77
|
+
q.choice key: 'e', name: 'Edit command before running', value: :edit
|
78
|
+
end
|
79
|
+
|
80
|
+
case action
|
81
|
+
when :run
|
82
|
+
Process.wait spawn(command)
|
83
|
+
exit $CHILD_STATUS.exitstatus
|
84
|
+
when :abort
|
85
|
+
break
|
86
|
+
when :edit
|
87
|
+
Tempfile.open('command') do |file|
|
88
|
+
file << command
|
89
|
+
file.flush
|
90
|
+
|
91
|
+
Process.wait spawn("${EDITOR:-vi} #{file.path}")
|
92
|
+
|
93
|
+
file.rewind
|
94
|
+
command = file.read.chomp
|
95
|
+
end
|
65
96
|
end
|
66
97
|
end
|
98
|
+
rescue Interrupt
|
99
|
+
exit 130
|
67
100
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Please
|
4
|
+
class Context
|
5
|
+
def initialize(options)
|
6
|
+
@examples = default_examples
|
7
|
+
|
8
|
+
if options[:send_pwd]
|
9
|
+
@examples << {
|
10
|
+
instruction: 'Print the current working directory',
|
11
|
+
command: 'pwd',
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
if options[:send_uname]
|
16
|
+
@examples << {
|
17
|
+
instruction: 'Show information about the operating system',
|
18
|
+
command: 'uname -a',
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
if options[:send_ls]
|
23
|
+
@examples << {
|
24
|
+
instruction: 'List all files in the current directory',
|
25
|
+
command: 'ls -a',
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
@examples.map do |example|
|
32
|
+
<<~EXAMPLE.chomp
|
33
|
+
# #{example[:instruction]}
|
34
|
+
$ #{example[:command]}
|
35
|
+
#{example.fetch(:result) { `#{example[:command]}` }}
|
36
|
+
EXAMPLE
|
37
|
+
end.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def default_examples
|
43
|
+
[
|
44
|
+
{
|
45
|
+
instruction: 'Find all files older than 1 week and open each of them in vim',
|
46
|
+
command: 'find . -type f -mtime +7 -exec vim {} \;',
|
47
|
+
result: '',
|
48
|
+
},
|
49
|
+
|
50
|
+
{
|
51
|
+
instruction: 'Download a random dog picture',
|
52
|
+
command: 'python3 -c \'import urllib.request; import json; import subprocess; data = urllib.request.urlopen("https://dog.ceo/api/breeds/image/random").read(); url = json.loads(data)["message"]; subprocess.call(["curl", url, "-o", "dog.jpg"])\'',
|
53
|
+
result: '',
|
54
|
+
},
|
55
|
+
|
56
|
+
{
|
57
|
+
instruction: 'Read from stdin until EOF, and then output the length of the string',
|
58
|
+
command: 'python3 -c \'import sys; print(len(sys.stdin.read().strip()))\'',
|
59
|
+
result: '',
|
60
|
+
},
|
61
|
+
|
62
|
+
{
|
63
|
+
instruction: 'Read a single line from stdin and pipe it to cowsay',
|
64
|
+
command: 'python3 -c \'print(input())\' | cowsay',
|
65
|
+
result: '',
|
66
|
+
},
|
67
|
+
|
68
|
+
{
|
69
|
+
instruction: 'Run the fortune command 5 times',
|
70
|
+
command: 'for i in {1..5}; do fortune; done',
|
71
|
+
result: '',
|
72
|
+
},
|
73
|
+
]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/please/request.rb
CHANGED
@@ -1,22 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Please
|
4
|
-
Request = Struct.new(:
|
4
|
+
Request = Struct.new(:instruction, :codex_service, :context, keyword_init: true) do
|
5
5
|
def send
|
6
|
-
codex_service.completion(
|
7
|
-
|
6
|
+
codex_service.completion(prompt)
|
7
|
+
.strip
|
8
|
+
# Collapse multiline commands into one line
|
9
|
+
.gsub(/\s*\\\n\s*/, ' ')
|
10
|
+
# Remove subsequent lines that do not contain commands
|
11
|
+
.gsub(/\n[^\$][^\n]*$/, '')
|
12
|
+
# Collapse multiple commands into one line
|
13
|
+
.gsub(/\n\$ /, '; ')
|
14
|
+
# Remove multiple consecutive spaces
|
15
|
+
.gsub(/\s+/, ' ')
|
16
|
+
end
|
8
17
|
|
9
|
-
|
10
|
-
|
11
|
-
|
18
|
+
def prompt
|
19
|
+
<<~PROMPT.chomp
|
20
|
+
Write a one-line bash command for each of the following tasks.
|
12
21
|
|
13
|
-
#
|
14
|
-
$ uname -a
|
15
|
-
#{`uname -a`}
|
16
|
-
|
17
|
-
# List all files in the current directory
|
18
|
-
$ ls -a
|
19
|
-
#{`ls -a`}
|
22
|
+
#{context.to_s}
|
20
23
|
|
21
24
|
# #{instruction.gsub(/\n/, " ")}
|
22
25
|
$
|
data/lib/please/version.rb
CHANGED
data/lib/please.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openai-please
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Anderson
|
@@ -61,6 +61,7 @@ files:
|
|
61
61
|
- exe/please
|
62
62
|
- lib/please.rb
|
63
63
|
- lib/please/cli.rb
|
64
|
+
- lib/please/context.rb
|
64
65
|
- lib/please/openai/codex_service.rb
|
65
66
|
- lib/please/request.rb
|
66
67
|
- lib/please/version.rb
|