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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88db2eab8d2aafb7387f3f13b7da57ec499f85ab25c75b51a2ee342c4a0fa5c1
4
- data.tar.gz: f50f4df10b90b1ab68543a9fb14a6494d47a33bccaf3fa54e829c43fe6215caa
3
+ metadata.gz: ffc331fe72fcbe5a6feec2f0ed039c7aeb453039e40ae533a05f2ff794447ef5
4
+ data.tar.gz: 55771e4ff93f8b209cda88778ac2eb6ed8118201c3c8d77d2ef61143f747097e
5
5
  SHA512:
6
- metadata.gz: 692f1d6e44402e12d6fa59a7733b7b6620323be48f8ef999b367616a917d53262ee8bc1aeb9f4628a583978fea77f653536112f2c2a29b2eaddb915feb28f8d7
7
- data.tar.gz: 675d9326b5e682e7d45c36162ec1fb52ea6487058843a274f8e75eb9cf8505fc5759241987f7f986bcf2bc13570ebbfa81ee1abed16864d024a98a6c77501b47
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.0)
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
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/please`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ **⚠️ Requires an access token for OpenAI Codex, which is currently in private beta**
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Convert natural language to bash commands using OpenAI Codex
6
6
 
7
- ## Installation
8
-
9
- Add this line to your application's Gemfile:
7
+ ![Demo video](https://user-images.githubusercontent.com/4272090/133416481-febce287-1c3b-4a10-ab3e-b7228d403d7a.gif)
10
8
 
11
- ```ruby
12
- gem 'please'
13
- ```
14
-
15
- And then execute:
9
+ ## Installation
16
10
 
17
- $ bundle install
11
+ $ gem install openai-please
18
12
 
19
- Or install it yourself as:
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
- $ gem install please
15
+ ```
16
+ OPENAI_ACCESS_TOKEN=[YOUR ACCESS TOKEN HERE]
17
+ ```
22
18
 
23
19
  ## Usage
24
20
 
25
- TODO: Write usage instructions here
26
-
27
- ## Development
28
-
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
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
- ## Contributing
29
+ ## Privacy
34
30
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/please.
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
- ## License
33
+ - `pwd`
34
+ - `uname -a`
35
+ - `ls -a`
38
36
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
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
- tty_prompt = TTY::Prompt.new
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
- opts.banner = USAGE
16
- end.parse!
21
+ OptionParser.new do |opts|
22
+ opts.banner = USAGE
17
23
 
18
- access_token = ENV.fetch('OPENAI_ACCESS_TOKEN') do
19
- tty_prompt.error 'Ensure the OPENAI_ACCESS_TOKEN environment variable is set'
20
- exit 1
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
- codex_service = Please::OpenAI::CodexService.new(access_token: access_token)
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
- instruction = ARGV.join(' ')
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
- if instruction.empty?
28
- tty_prompt.error USAGE
29
- exit 1
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
- request = Please::Request.new(
33
- options: options,
34
- instruction: instruction,
35
- codex_service: codex_service,
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
- command = request.send
46
+ codex_service = Please::OpenAI::CodexService.new(access_token: access_token)
39
47
 
40
- loop do
41
- print '$ '
42
- tty_prompt.ok command
48
+ instruction = ARGV.join(' ')
43
49
 
44
- action = tty_prompt.expand('Run the command?') do |q|
45
- q.choice key: 'y', name: 'Yes', value: :run
46
- q.choice key: 'n', name: 'No', value: :abort
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
- case action
51
- when :run
52
- Process.wait spawn(command)
53
- exit $?.exitstatus
54
- when :abort
55
- break
56
- when :edit
57
- Tempfile.open('command') do |file|
58
- file << command
59
- file.flush
60
-
61
- Process.wait spawn("${EDITOR:-vi} #{file.path}")
62
-
63
- file.rewind
64
- command = file.read.chomp
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
@@ -27,7 +27,7 @@ module Please
27
27
  top_p: 1,
28
28
  frequency_penalty: 0,
29
29
  presence_penalty: 0,
30
- stop: ["\n"],
30
+ stop: ["\n\n"],
31
31
  }
32
32
  end
33
33
  end
@@ -1,22 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Please
4
- Request = Struct.new(:options, :instruction, :codex_service, keyword_init: true) do
4
+ Request = Struct.new(:instruction, :codex_service, :context, keyword_init: true) do
5
5
  def send
6
- codex_service.completion(<<~PROMPT.chomp).strip
7
- Write a one-line bash command each of the following tasks.
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
- # Print the current working directory
10
- $ pwd
11
- #{`pwd`}
18
+ def prompt
19
+ <<~PROMPT.chomp
20
+ Write a one-line bash command for each of the following tasks.
12
21
 
13
- # Show information about the operating system
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
  $
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Please
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.1'
5
5
  end
data/lib/please.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'please/version'
4
4
  require_relative 'please/request'
5
+ require_relative 'please/context'
5
6
  require_relative 'please/openai/codex_service'
6
7
 
7
8
  module Please
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.0
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