gpterm 0.6.5 → 0.7.0
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/config/prompts.yml +2 -0
 - data/lib/app_config.rb +69 -0
 - data/lib/{client.rb → command_generator.rb} +16 -4
 - data/lib/gpterm.rb +62 -160
 - data/lib/input.rb +31 -0
 - data/lib/parse_options.rb +65 -0
 - metadata +5 -3
 - data/lib/config.rb +0 -38
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: a5d7e207cd381258fa26f30cad19add40fef4c0b3931971e9d7c131ec16734fd
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: d3c472cf2ac8f979616f2863310aa9fd8ea083384c85ac6a78eaba5397a15899
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 984be150518b40b63ba73a873d0389801c5ab45069be710c2373baa5fe324c538694a5a040c57218388ea0200a3ec38a6d5c2e04d2db803a9e9d91f543781d0e
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 461c55256fc8f71ac54422c95022d921a172f79ec4ef4e59dd31df2d5a123c3d61fcd3013fb5d597701a37285202596342c8b47d768756fb51c8ef485639a868
         
     | 
    
        data/config/prompts.yml
    CHANGED
    
    | 
         @@ -116,3 +116,5 @@ goal_commands: | 
     | 
|
| 
       116 
116 
     | 
    
         
             
              - $$cannot_compute$$ - You cannot create a VALID response to this prompt. The user will be asked to provide a new prompt.
         
     | 
| 
       117 
117 
     | 
    
         | 
| 
       118 
118 
     | 
    
         
             
              COMMANDS:
         
     | 
| 
      
 119 
     | 
    
         
            +
            refine_commands: |
         
     | 
| 
      
 120 
     | 
    
         
            +
              The last response needs some changes. Please take into account this prompt from the user, then refine the commands you provided in the last response.
         
     | 
    
        data/lib/app_config.rb
    ADDED
    
    | 
         @@ -0,0 +1,69 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'colorize'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'input'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module AppConfig
         
     | 
| 
      
 7 
     | 
    
         
            +
              CONFIG_FILE = File.join(Dir.home, '.gpterm', 'config.yml').freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def self.load
         
     | 
| 
      
 10 
     | 
    
         
            +
                # Check if the directory exists, if not, create it
         
     | 
| 
      
 11 
     | 
    
         
            +
                unless File.directory?(File.dirname(CONFIG_FILE))
         
     | 
| 
      
 12 
     | 
    
         
            +
                  Dir.mkdir(File.dirname(CONFIG_FILE))
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                unless File.exist?(self::CONFIG_FILE)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  puts 'Welcome to gpterm! It looks like this is your first time using this application.'.colorize(:magenta)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  new_config = {}
         
     | 
| 
      
 19 
     | 
    
         
            +
                  puts "Before we get started, we need to configure the application. All the info you provide will be saved in #{self::CONFIG_FILE}.".colorize(:magenta)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  puts "Enter your OpenAI API key's \"SECRET KEY\" value then hit return: ".colorize(:yellow)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  new_config['openapi_key'] = Input.non_empty
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  puts "Your PATH environment variable is: #{ENV['PATH']}".colorize(:magenta)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  puts 'Are you happy for your PATH to be sent to OpenAI to help with command generation? (Y/n then hit return) '.colorize(:yellow)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  input = Input.yes_or_no
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  if input == 'y'
         
     | 
| 
      
 30 
     | 
    
         
            +
                    new_config['send_path'] = true
         
     | 
| 
      
 31 
     | 
    
         
            +
                  else
         
     | 
| 
      
 32 
     | 
    
         
            +
                    new_config['send_path'] = false
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  default_model = 'gpt-4-turbo-preview'
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  puts "The default model is #{default_model}. If you would like to change it please enter the name of your preferred model:".colorize(:yellow)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  new_config['model'] = STDIN.gets.chomp.strip || default_model
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  self.save_config(new_config)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  puts "Configuration saved to #{self::CONFIG_FILE}".colorize(:green)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  new_config
         
     | 
| 
      
 45 
     | 
    
         
            +
                else
         
     | 
| 
      
 46 
     | 
    
         
            +
                  self.load_config_from_file
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              def self.load_config_from_file
         
     | 
| 
      
 51 
     | 
    
         
            +
                YAML.load_file(CONFIG_FILE)
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              def self.save_config(config)
         
     | 
| 
      
 55 
     | 
    
         
            +
                File.write(CONFIG_FILE, config.to_yaml)
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              def self.add_openapi_key(config, openapi_key)
         
     | 
| 
      
 59 
     | 
    
         
            +
                config['openapi_key'] = openapi_key
         
     | 
| 
      
 60 
     | 
    
         
            +
                save_config(config)
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
              def self.add_preset(config, preset_name, preset_prompt)
         
     | 
| 
      
 64 
     | 
    
         
            +
                # This is a YAML file so we need to make sure the presets key exists
         
     | 
| 
      
 65 
     | 
    
         
            +
                config['presets'] ||= {}
         
     | 
| 
      
 66 
     | 
    
         
            +
                config['presets'][preset_name] = preset_prompt
         
     | 
| 
      
 67 
     | 
    
         
            +
                save_config(config)
         
     | 
| 
      
 68 
     | 
    
         
            +
              end
         
     | 
| 
      
 69 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,13 +1,12 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require "openai"
         
     | 
| 
       2 
1 
     | 
    
         
             
            require 'yaml'
         
     | 
| 
       3 
2 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
            class  
     | 
| 
      
 3 
     | 
    
         
            +
            class CommandGenerator
         
     | 
| 
       5 
4 
     | 
    
         
             
              attr_reader :openai_client
         
     | 
| 
       6 
5 
     | 
    
         
             
              attr_reader :config
         
     | 
| 
       7 
6 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
              def initialize(config)
         
     | 
| 
      
 7 
     | 
    
         
            +
              def initialize(config, openai_client)
         
     | 
| 
       9 
8 
     | 
    
         
             
                @config = config
         
     | 
| 
       10 
     | 
    
         
            -
                @openai_client =  
     | 
| 
      
 9 
     | 
    
         
            +
                @openai_client = openai_client
         
     | 
| 
       11 
10 
     | 
    
         
             
                @prompts = YAML.load_file(File.join(__dir__, '..', 'config', 'prompts.yml'))
         
     | 
| 
       12 
11 
     | 
    
         
             
              end
         
     | 
| 
       13 
12 
     | 
    
         | 
| 
         @@ -74,6 +73,19 @@ class Client 
     | 
|
| 
       74 
73 
     | 
    
         
             
                continue_conversation(goal_commands_prompt)
         
     | 
| 
       75 
74 
     | 
    
         
             
              end
         
     | 
| 
       76 
75 
     | 
    
         | 
| 
      
 76 
     | 
    
         
            +
              def refine_last_response(prompt)
         
     | 
| 
      
 77 
     | 
    
         
            +
                refinement_prompt = @prompts["refine_commands"]
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                refinement_prompt += <<~PROMPT
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  #{prompt}
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  COMMANDS:
         
     | 
| 
      
 84 
     | 
    
         
            +
                PROMPT
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                continue_conversation(refinement_prompt)
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
       77 
89 
     | 
    
         
             
              private
         
     | 
| 
       78 
90 
     | 
    
         | 
| 
       79 
91 
     | 
    
         
             
              def continue_conversation(prompt)
         
     | 
    
        data/lib/gpterm.rb
    CHANGED
    
    | 
         @@ -1,9 +1,11 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'optparse'
         
     | 
| 
       2 
1 
     | 
    
         
             
            require 'colorize'
         
     | 
| 
       3 
2 
     | 
    
         
             
            require 'open3'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "openai"
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            require_relative ' 
     | 
| 
       6 
     | 
    
         
            -
            require_relative ' 
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative 'app_config'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative 'command_generator'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative 'parse_options'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative 'input'
         
     | 
| 
       7 
9 
     | 
    
         | 
| 
       8 
10 
     | 
    
         
             
            # The colours work like this:
         
     | 
| 
       9 
11 
     | 
    
         
             
            # - Output from STDOUT or STDERR is default
         
     | 
| 
         @@ -14,9 +16,10 @@ require_relative 'client' 
     | 
|
| 
       14 
16 
     | 
    
         | 
| 
       15 
17 
     | 
    
         
             
            class GPTerm
         
     | 
| 
       16 
18 
     | 
    
         
             
              def initialize
         
     | 
| 
       17 
     | 
    
         
            -
                @config =  
     | 
| 
       18 
     | 
    
         
            -
                @options =  
     | 
| 
       19 
     | 
    
         
            -
                @ 
     | 
| 
      
 19 
     | 
    
         
            +
                @config = AppConfig.load
         
     | 
| 
      
 20 
     | 
    
         
            +
                @options = ParseOptions.call(@config)
         
     | 
| 
      
 21 
     | 
    
         
            +
                @openai_client = OpenAI::Client.new(access_token: @config["openapi_key"])
         
     | 
| 
      
 22 
     | 
    
         
            +
                @command_generator = CommandGenerator.new(@config, @openai_client)
         
     | 
| 
       20 
23 
     | 
    
         
             
              end
         
     | 
| 
       21 
24 
     | 
    
         | 
| 
       22 
25 
     | 
    
         
             
              def run
         
     | 
| 
         @@ -47,93 +50,35 @@ class GPTerm 
     | 
|
| 
       47 
50 
     | 
    
         
             
                exit
         
     | 
| 
       48 
51 
     | 
    
         
             
              end
         
     | 
| 
       49 
52 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
              # Ensures the user enters "y" or "n"
         
     | 
| 
       51 
     | 
    
         
            -
              def get_yes_or_no
         
     | 
| 
       52 
     | 
    
         
            -
                input = STDIN.gets.chomp.downcase
         
     | 
| 
       53 
     | 
    
         
            -
                while ['y', 'n'].include?(input) == false
         
     | 
| 
       54 
     | 
    
         
            -
                  puts 'Please enter "y/Y" or "n/N":'.colorize(:yellow)
         
     | 
| 
       55 
     | 
    
         
            -
                  input = STDIN.gets.chomp.downcase
         
     | 
| 
       56 
     | 
    
         
            -
                end
         
     | 
| 
       57 
     | 
    
         
            -
                input
         
     | 
| 
       58 
     | 
    
         
            -
              end
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
              # Ensures the user enters a non-empty value
         
     | 
| 
       61 
     | 
    
         
            -
              def get_non_empty_input
         
     | 
| 
       62 
     | 
    
         
            -
                input = STDIN.gets.chomp.strip
         
     | 
| 
       63 
     | 
    
         
            -
                while input.length == 0
         
     | 
| 
       64 
     | 
    
         
            -
                  puts 'Please enter a non-empty value:'.colorize(:yellow)
         
     | 
| 
       65 
     | 
    
         
            -
                  input = STDIN.gets.chomp.strip
         
     | 
| 
       66 
     | 
    
         
            -
                end
         
     | 
| 
       67 
     | 
    
         
            -
                input
         
     | 
| 
       68 
     | 
    
         
            -
              end
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
53 
     | 
    
         
             
              def start_conversation(prompt)
         
     | 
| 
       71 
     | 
    
         
            -
                 
     | 
| 
      
 54 
     | 
    
         
            +
                shell_output = gather_information_from_shell(prompt)
         
     | 
| 
       72 
55 
     | 
    
         | 
| 
       73 
     | 
    
         
            -
                 
     | 
| 
       74 
     | 
    
         
            -
                  exit_with_message('Sorry, a command could not be generated for that prompt. Try another.', :red)
         
     | 
| 
       75 
     | 
    
         
            -
                end
         
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
                if message.downcase == '$$no_gathering_needed$$'
         
     | 
| 
       78 
     | 
    
         
            -
                  puts 'No information gathering needed'.colorize(:magenta)
         
     | 
| 
       79 
     | 
    
         
            -
                  output = "No information gathering was needed."
         
     | 
| 
       80 
     | 
    
         
            -
                elsif message.downcase == '$$cannot_compute$$'
         
     | 
| 
       81 
     | 
    
         
            -
                  exit_with_message('Sorry, a command could not be generated for that prompt. Try another.', :red)
         
     | 
| 
       82 
     | 
    
         
            -
                else
         
     | 
| 
       83 
     | 
    
         
            -
                  puts 'Information gathering command:'.colorize(:magenta)
         
     | 
| 
       84 
     | 
    
         
            -
                  puts message.gsub(/^/, "#{"  $".colorize(:blue)} ")
         
     | 
| 
       85 
     | 
    
         
            -
                  puts 'Do you want to execute this command? (Y/n then hit return)'.colorize(:yellow)
         
     | 
| 
       86 
     | 
    
         
            -
                  continue = get_yes_or_no
         
     | 
| 
      
 56 
     | 
    
         
            +
                offer_prompt_response = @command_generator.offer_information_prompt(shell_output, :shell_output_response)
         
     | 
| 
       87 
57 
     | 
    
         | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                    exit
         
     | 
| 
       90 
     | 
    
         
            -
                  end
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                  puts 'Running command...'
         
     | 
| 
       93 
     | 
    
         
            -
                  output = `#{message}`
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                  if @config[:verbose]
         
     | 
| 
       96 
     | 
    
         
            -
                    puts 'Output:'
         
     | 
| 
       97 
     | 
    
         
            -
                    puts output
         
     | 
| 
       98 
     | 
    
         
            -
                  end
         
     | 
| 
       99 
     | 
    
         
            -
                end
         
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
                output = @client.offer_information_prompt(output, :shell_output_response)
         
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
                while output.downcase != '$$no_more_information_needed$$'
         
     | 
| 
      
 58 
     | 
    
         
            +
                while offer_prompt_response.downcase != '$$no_more_information_needed$$'
         
     | 
| 
       104 
59 
     | 
    
         
             
                  puts "You have been asked to provide more information with this command:".colorize(:magenta)
         
     | 
| 
       105 
     | 
    
         
            -
                  puts  
     | 
| 
      
 60 
     | 
    
         
            +
                  puts offer_prompt_response.gsub(/^/, "#{"  >".colorize(:blue)} ")
         
     | 
| 
       106 
61 
     | 
    
         
             
                  puts "What is your response? (Type 'skip' to skip this step and force the final command to be generated)".colorize(:yellow)
         
     | 
| 
       107 
62 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                   
     | 
| 
      
 63 
     | 
    
         
            +
                  user_question_response = Input.non_empty
         
     | 
| 
       109 
64 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
                  if  
     | 
| 
       111 
     | 
    
         
            -
                     
     | 
| 
      
 65 
     | 
    
         
            +
                  if user_question_response.downcase == 'skip'
         
     | 
| 
      
 66 
     | 
    
         
            +
                    offer_prompt_response = '$$no_more_information_needed$$'
         
     | 
| 
      
 67 
     | 
    
         
            +
                    puts 'Thanks, one moment please...'.colorize(:magenta)
         
     | 
| 
       112 
68 
     | 
    
         
             
                  else
         
     | 
| 
       113 
     | 
    
         
            -
                     
     | 
| 
      
 69 
     | 
    
         
            +
                    offer_prompt_response = @command_generator.offer_information_prompt(user_question_response, :question_response)
         
     | 
| 
       114 
70 
     | 
    
         
             
                  end
         
     | 
| 
       115 
71 
     | 
    
         
             
                end
         
     | 
| 
       116 
72 
     | 
    
         | 
| 
       117 
     | 
    
         
            -
                puts 'Requesting the next command...'.colorize(:magenta)
         
     | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
       119 
     | 
    
         
            -
                message = @client.final_prompt(output)
         
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
                if message.downcase == '$$cannot_compute$$'
         
     | 
| 
       122 
     | 
    
         
            -
                  exit_with_message('Sorry, a command could not be generated for that prompt. Try another.', :red)
         
     | 
| 
       123 
     | 
    
         
            -
                end
         
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
                puts 'Generated command to accomplish your goal:'.colorize(:magenta)
         
     | 
| 
       126 
     | 
    
         
            -
                puts message.gsub(/^/, "#{"  $".colorize(:green)} ")
         
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
                puts 'Do you want to execute this command? (Y/n then hit return)'.colorize(:yellow)
         
     | 
| 
      
 73 
     | 
    
         
            +
                puts 'Requesting the next command...'.colorize(:magenta) if @config[:verbose]
         
     | 
| 
       129 
74 
     | 
    
         | 
| 
       130 
     | 
    
         
            -
                 
     | 
| 
      
 75 
     | 
    
         
            +
                # TODO: Some redundant info being passed here in the case of
         
     | 
| 
      
 76 
     | 
    
         
            +
                # the user answering a question
         
     | 
| 
      
 77 
     | 
    
         
            +
                goal_prompt_response = @command_generator.final_prompt(offer_prompt_response)
         
     | 
| 
       131 
78 
     | 
    
         | 
| 
       132 
     | 
    
         
            -
                 
     | 
| 
       133 
     | 
    
         
            -
                  exit
         
     | 
| 
       134 
     | 
    
         
            -
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
                goal_prompt_response = run_refinement_loop(goal_prompt_response)
         
     | 
| 
       135 
80 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                commands =  
     | 
| 
      
 81 
     | 
    
         
            +
                commands = goal_prompt_response.split("\n")
         
     | 
| 
       137 
82 
     | 
    
         | 
| 
       138 
83 
     | 
    
         
             
                commands.each do |command|
         
     | 
| 
       139 
84 
     | 
    
         
             
                  stdout, stderr, exit_status = execute_shell_command(command)
         
     | 
| 
         @@ -149,101 +94,58 @@ class GPTerm 
     | 
|
| 
       149 
94 
     | 
    
         
             
                end
         
     | 
| 
       150 
95 
     | 
    
         
             
              end
         
     | 
| 
       151 
96 
     | 
    
         | 
| 
       152 
     | 
    
         
            -
              def  
     | 
| 
       153 
     | 
    
         
            -
                 
     | 
| 
       154 
     | 
    
         
            -
                  puts 'Welcome to gpterm! It looks like this is your first time using this application.'.colorize(:magenta)
         
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
                  new_config = {}
         
     | 
| 
       157 
     | 
    
         
            -
                  puts "Before we get started, we need to configure the application. All the info you provide will be saved in #{AppConfig::CONFIG_FILE}.".colorize(:magenta)
         
     | 
| 
      
 97 
     | 
    
         
            +
              def gather_information_from_shell(prompt)
         
     | 
| 
      
 98 
     | 
    
         
            +
                info_prompt_response = @command_generator.first_prompt(prompt)
         
     | 
| 
       158 
99 
     | 
    
         | 
| 
       159 
     | 
    
         
            -
             
     | 
| 
       160 
     | 
    
         
            -
                   
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
                   
     | 
| 
      
 100 
     | 
    
         
            +
                if info_prompt_response.downcase == '$$no_gathering_needed$$'
         
     | 
| 
      
 101 
     | 
    
         
            +
                  puts 'No information gathering needed'.colorize(:magenta) if @config[:verbose]
         
     | 
| 
      
 102 
     | 
    
         
            +
                  shell_output = nil
         
     | 
| 
      
 103 
     | 
    
         
            +
                else
         
     | 
| 
      
 104 
     | 
    
         
            +
                  info_prompt_response = run_refinement_loop(info_prompt_response, 'gather more information for your request')
         
     | 
| 
       164 
105 
     | 
    
         | 
| 
       165 
     | 
    
         
            -
                   
     | 
| 
      
 106 
     | 
    
         
            +
                  puts 'Running command...' if @config[:verbose]
         
     | 
| 
      
 107 
     | 
    
         
            +
                  shell_output = `#{info_prompt_response}`
         
     | 
| 
       166 
108 
     | 
    
         | 
| 
       167 
     | 
    
         
            -
                  if  
     | 
| 
       168 
     | 
    
         
            -
                     
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
       170 
     | 
    
         
            -
                    new_config['send_path'] = false
         
     | 
| 
      
 109 
     | 
    
         
            +
                  if @config[:verbose]
         
     | 
| 
      
 110 
     | 
    
         
            +
                    puts 'Shell output:'
         
     | 
| 
      
 111 
     | 
    
         
            +
                    puts shell_output
         
     | 
| 
       171 
112 
     | 
    
         
             
                  end
         
     | 
| 
       172 
113 
     | 
    
         | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
                shell_output
         
     | 
| 
      
 116 
     | 
    
         
            +
              end
         
     | 
| 
       174 
117 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
      
 118 
     | 
    
         
            +
              def run_refinement_loop(original_response, purpose = 'accomplish your goal')
         
     | 
| 
      
 119 
     | 
    
         
            +
                if original_response.downcase == '$$cannot_compute$$'
         
     | 
| 
      
 120 
     | 
    
         
            +
                  exit_with_message('Sorry, a command could not be generated for that prompt. Try another.', :red)
         
     | 
| 
      
 121 
     | 
    
         
            +
                end
         
     | 
| 
       177 
122 
     | 
    
         | 
| 
       178 
     | 
    
         
            -
             
     | 
| 
      
 123 
     | 
    
         
            +
                refined_response = original_response.dup
         
     | 
| 
       179 
124 
     | 
    
         | 
| 
       180 
     | 
    
         
            -
             
     | 
| 
      
 125 
     | 
    
         
            +
                puts "The following command(s) were generated to #{purpose}:".colorize(:magenta)
         
     | 
| 
      
 126 
     | 
    
         
            +
                puts refined_response.gsub(/^/, "#{"  $".colorize(:green)} ")
         
     | 
| 
      
 127 
     | 
    
         
            +
                puts 'Do you want to execute this/these command(s), or refine them with another prompt? (Y/n/r then hit return)'.colorize(:yellow)
         
     | 
| 
      
 128 
     | 
    
         
            +
                continue = Input.yes_no_or_refine
         
     | 
| 
       181 
129 
     | 
    
         | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
                   
     | 
| 
       185 
     | 
    
         
            -
             
     | 
| 
       186 
     | 
    
         
            -
              end
         
     | 
| 
      
 130 
     | 
    
         
            +
                while continue.downcase == 'r'
         
     | 
| 
      
 131 
     | 
    
         
            +
                  puts 'Please enter a new prompt:'.colorize(:yellow)
         
     | 
| 
      
 132 
     | 
    
         
            +
                  new_prompt = Input.non_empty
         
     | 
| 
      
 133 
     | 
    
         
            +
                  refined_response = @command_generator.refine_last_response(new_prompt)
         
     | 
| 
       187 
134 
     | 
    
         | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       189 
     | 
    
         
            -
             
     | 
| 
       190 
     | 
    
         
            -
                subcommands = {
         
     | 
| 
       191 
     | 
    
         
            -
                  'preset' => {
         
     | 
| 
       192 
     | 
    
         
            -
                    option_parser: OptionParser.new do |opts|
         
     | 
| 
       193 
     | 
    
         
            -
                      opts.banner = "gpterm preset <name> <prompt>"
         
     | 
| 
       194 
     | 
    
         
            -
                    end,
         
     | 
| 
       195 
     | 
    
         
            -
                    argument_parser: ->(args) {
         
     | 
| 
       196 
     | 
    
         
            -
                      if args.length < 2
         
     | 
| 
       197 
     | 
    
         
            -
                        options[:prompt] = @config['presets'][args[0]]
         
     | 
| 
       198 
     | 
    
         
            -
                      else
         
     | 
| 
       199 
     | 
    
         
            -
                        options[:preset_prompt] = [args[0], args[1]]
         
     | 
| 
       200 
     | 
    
         
            -
                      end
         
     | 
| 
       201 
     | 
    
         
            -
                    }
         
     | 
| 
       202 
     | 
    
         
            -
                  },
         
     | 
| 
       203 
     | 
    
         
            -
                  'config' => {
         
     | 
| 
       204 
     | 
    
         
            -
                    option_parser: OptionParser.new do |opts|
         
     | 
| 
       205 
     | 
    
         
            -
                      opts.banner = "gpterm config [--openapi_key <value>|--send_path <true|false>]"
         
     | 
| 
       206 
     | 
    
         
            -
                      opts.on("--openapi_key VALUE", "Set the OpenAI API key") do |v|
         
     | 
| 
       207 
     | 
    
         
            -
                        AppConfig.add_openapi_key(@config, v)
         
     | 
| 
       208 
     | 
    
         
            -
                        exit_with_message("OpenAI API key saved")
         
     | 
| 
       209 
     | 
    
         
            -
                      end
         
     | 
| 
       210 
     | 
    
         
            -
                      opts.on("--send_path", "Send the PATH environment variable to OpenAI") do
         
     | 
| 
       211 
     | 
    
         
            -
                        @config['send_path'] = true
         
     | 
| 
       212 
     | 
    
         
            -
                        AppConfig.save_config(@config)
         
     | 
| 
       213 
     | 
    
         
            -
                        exit_with_message("Your PATH environment variable will be sent to OpenAI to help with command generation")
         
     | 
| 
       214 
     | 
    
         
            -
                      end
         
     | 
| 
       215 
     | 
    
         
            -
                    end
         
     | 
| 
       216 
     | 
    
         
            -
                  }
         
     | 
| 
       217 
     | 
    
         
            -
                }
         
     | 
| 
       218 
     | 
    
         
            -
             
     | 
| 
       219 
     | 
    
         
            -
                main = OptionParser.new do |opts|
         
     | 
| 
       220 
     | 
    
         
            -
                  opts.banner = "Usage:"
         
     | 
| 
       221 
     | 
    
         
            -
                  opts.banner += "\n\ngpterm <prompt> [options] [subcommand [options]]"
         
     | 
| 
       222 
     | 
    
         
            -
                  opts.banner += "\n\nSubcommands:"
         
     | 
| 
       223 
     | 
    
         
            -
                  subcommands.each do |name, subcommand|
         
     | 
| 
       224 
     | 
    
         
            -
                    opts.banner += "\n  #{name} - #{subcommand[:option_parser].banner}"
         
     | 
| 
       225 
     | 
    
         
            -
                  end
         
     | 
| 
       226 
     | 
    
         
            -
                  opts.banner += "\n\nOptions:"
         
     | 
| 
       227 
     | 
    
         
            -
                  opts.on("-v", "--verbose", "Run verbosely") do |v|
         
     | 
| 
       228 
     | 
    
         
            -
                    options[:verbose] = true
         
     | 
| 
      
 135 
     | 
    
         
            +
                  if refined_response.downcase == '$$cannot_compute$$'
         
     | 
| 
      
 136 
     | 
    
         
            +
                    exit_with_message('Sorry, a command could not be generated for that prompt. Try another.', :red)
         
     | 
| 
       229 
137 
     | 
    
         
             
                  end
         
     | 
| 
       230 
     | 
    
         
            -
                end
         
     | 
| 
       231 
138 
     | 
    
         | 
| 
       232 
     | 
    
         
            -
             
     | 
| 
      
 139 
     | 
    
         
            +
                  puts "The following command(s) were generated to #{purpose}:".colorize(:magenta)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  puts refined_response.gsub(/^/, "#{"  $".colorize(:blue)} ")
         
     | 
| 
      
 141 
     | 
    
         
            +
                  puts 'Do you want to execute this/these command(s), or refine them with another prompt? (Y/n/r then hit return)'.colorize(:yellow)
         
     | 
| 
      
 142 
     | 
    
         
            +
                  continue = Input.yes_no_or_refine
         
     | 
| 
      
 143 
     | 
    
         
            +
                end
         
     | 
| 
       233 
144 
     | 
    
         | 
| 
       234 
     | 
    
         
            -
                 
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
       236 
     | 
    
         
            -
                  subcommands[command][:option_parser].parse!
         
     | 
| 
       237 
     | 
    
         
            -
                  subcommands[command][:argument_parser].call(ARGV) if subcommands[command][:argument_parser]
         
     | 
| 
       238 
     | 
    
         
            -
                elsif command == 'help'
         
     | 
| 
       239 
     | 
    
         
            -
                  exit_with_message(main)
         
     | 
| 
       240 
     | 
    
         
            -
                elsif command
         
     | 
| 
       241 
     | 
    
         
            -
                  options[:prompt] = command
         
     | 
| 
       242 
     | 
    
         
            -
                else
         
     | 
| 
       243 
     | 
    
         
            -
                  puts 'Enter a prompt to generate text from:'.colorize(:yellow)
         
     | 
| 
       244 
     | 
    
         
            -
                  options[:prompt] = get_non_empty_input
         
     | 
| 
      
 145 
     | 
    
         
            +
                unless continue.downcase == 'y'
         
     | 
| 
      
 146 
     | 
    
         
            +
                  exit
         
     | 
| 
       245 
147 
     | 
    
         
             
                end
         
     | 
| 
       246 
148 
     | 
    
         | 
| 
       247 
     | 
    
         
            -
                 
     | 
| 
      
 149 
     | 
    
         
            +
                refined_response
         
     | 
| 
       248 
150 
     | 
    
         
             
              end
         
     | 
| 
       249 
151 
     | 
    
         
             
            end
         
     | 
    
        data/lib/input.rb
    ADDED
    
    | 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Input
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Ensures the user enters a non-empty value
         
     | 
| 
      
 3 
     | 
    
         
            +
              def self.non_empty
         
     | 
| 
      
 4 
     | 
    
         
            +
                input = STDIN.gets.chomp.strip
         
     | 
| 
      
 5 
     | 
    
         
            +
                while input.length == 0
         
     | 
| 
      
 6 
     | 
    
         
            +
                  puts 'Please enter a non-empty value:'.colorize(:yellow)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  input = STDIN.gets.chomp.strip
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
                input
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              # Ensures the user enters "y" or "n"
         
     | 
| 
      
 13 
     | 
    
         
            +
              def self.yes_or_no
         
     | 
| 
      
 14 
     | 
    
         
            +
                input = STDIN.gets.chomp.downcase
         
     | 
| 
      
 15 
     | 
    
         
            +
                while ['y', 'n'].include?(input) == false
         
     | 
| 
      
 16 
     | 
    
         
            +
                  puts 'Please enter "y/Y" or "n/N":'.colorize(:yellow)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  input = STDIN.gets.chomp.downcase
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
                input
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              # Ensures the user enters "y" or "n"
         
     | 
| 
      
 23 
     | 
    
         
            +
              def self.yes_no_or_refine
         
     | 
| 
      
 24 
     | 
    
         
            +
                input = STDIN.gets.chomp.downcase
         
     | 
| 
      
 25 
     | 
    
         
            +
                while ['y', 'n', 'r'].include?(input) == false
         
     | 
| 
      
 26 
     | 
    
         
            +
                  puts 'Please enter "y/Y", "n/N" or "r/R":'.colorize(:yellow)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  input = STDIN.gets.chomp.downcase
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
                input
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class ParseOptions
         
     | 
| 
      
 4 
     | 
    
         
            +
              def self.call(config)
         
     | 
| 
      
 5 
     | 
    
         
            +
                options = {}
         
     | 
| 
      
 6 
     | 
    
         
            +
                subcommands = {
         
     | 
| 
      
 7 
     | 
    
         
            +
                  'preset' => {
         
     | 
| 
      
 8 
     | 
    
         
            +
                    option_parser: OptionParser.new do |opts|
         
     | 
| 
      
 9 
     | 
    
         
            +
                      opts.banner = "gpterm preset <name> <prompt>"
         
     | 
| 
      
 10 
     | 
    
         
            +
                    end,
         
     | 
| 
      
 11 
     | 
    
         
            +
                    argument_parser: ->(args) {
         
     | 
| 
      
 12 
     | 
    
         
            +
                      if args.length < 2
         
     | 
| 
      
 13 
     | 
    
         
            +
                        options[:prompt] = config['presets'][args[0]]
         
     | 
| 
      
 14 
     | 
    
         
            +
                      else
         
     | 
| 
      
 15 
     | 
    
         
            +
                        options[:preset_prompt] = [args[0], args[1]]
         
     | 
| 
      
 16 
     | 
    
         
            +
                      end
         
     | 
| 
      
 17 
     | 
    
         
            +
                    }
         
     | 
| 
      
 18 
     | 
    
         
            +
                  },
         
     | 
| 
      
 19 
     | 
    
         
            +
                  'config' => {
         
     | 
| 
      
 20 
     | 
    
         
            +
                    option_parser: OptionParser.new do |opts|
         
     | 
| 
      
 21 
     | 
    
         
            +
                      opts.banner = "gpterm config [--openapi_key <value>|--send_path <true|false>]"
         
     | 
| 
      
 22 
     | 
    
         
            +
                      opts.on("--openapi_key VALUE", "Set the OpenAI API key") do |v|
         
     | 
| 
      
 23 
     | 
    
         
            +
                        AppConfig.add_openapi_key(config, v)
         
     | 
| 
      
 24 
     | 
    
         
            +
                        exit_with_message("OpenAI API key saved")
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
                      opts.on("--send_path", "Send the PATH environment variable to OpenAI") do
         
     | 
| 
      
 27 
     | 
    
         
            +
                        config['send_path'] = true
         
     | 
| 
      
 28 
     | 
    
         
            +
                        AppConfig.save_config(config)
         
     | 
| 
      
 29 
     | 
    
         
            +
                        exit_with_message("Your PATH environment variable will be sent to OpenAI to help with command generation")
         
     | 
| 
      
 30 
     | 
    
         
            +
                      end
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  }
         
     | 
| 
      
 33 
     | 
    
         
            +
                }
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                main = OptionParser.new do |opts|
         
     | 
| 
      
 36 
     | 
    
         
            +
                  opts.banner = "Usage:"
         
     | 
| 
      
 37 
     | 
    
         
            +
                  opts.banner += "\n\ngpterm <prompt> [options] [subcommand [options]]"
         
     | 
| 
      
 38 
     | 
    
         
            +
                  opts.banner += "\n\nSubcommands:"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  subcommands.each do |name, subcommand|
         
     | 
| 
      
 40 
     | 
    
         
            +
                    opts.banner += "\n  #{name} - #{subcommand[:option_parser].banner}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  opts.banner += "\n\nOptions:"
         
     | 
| 
      
 43 
     | 
    
         
            +
                  opts.on("-v", "--verbose", "Run verbosely") do |v|
         
     | 
| 
      
 44 
     | 
    
         
            +
                    options[:verbose] = true
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                command = ARGV.shift
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                main.order!
         
     | 
| 
      
 51 
     | 
    
         
            +
                if subcommands.key?(command)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  subcommands[command][:option_parser].parse!
         
     | 
| 
      
 53 
     | 
    
         
            +
                  subcommands[command][:argument_parser].call(ARGV) if subcommands[command][:argument_parser]
         
     | 
| 
      
 54 
     | 
    
         
            +
                elsif command == 'help'
         
     | 
| 
      
 55 
     | 
    
         
            +
                  exit_with_message(main)
         
     | 
| 
      
 56 
     | 
    
         
            +
                elsif command
         
     | 
| 
      
 57 
     | 
    
         
            +
                  options[:prompt] = command
         
     | 
| 
      
 58 
     | 
    
         
            +
                else
         
     | 
| 
      
 59 
     | 
    
         
            +
                  puts 'Enter a prompt to generate text from:'.colorize(:yellow)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  options[:prompt] = Input.non_empty
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                options
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: gpterm
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.7.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Dan Hough
         
     | 
| 
         @@ -51,9 +51,11 @@ files: 
     | 
|
| 
       51 
51 
     | 
    
         
             
            - README.md
         
     | 
| 
       52 
52 
     | 
    
         
             
            - bin/gpterm
         
     | 
| 
       53 
53 
     | 
    
         
             
            - config/prompts.yml
         
     | 
| 
       54 
     | 
    
         
            -
            - lib/ 
     | 
| 
       55 
     | 
    
         
            -
            - lib/ 
     | 
| 
      
 54 
     | 
    
         
            +
            - lib/app_config.rb
         
     | 
| 
      
 55 
     | 
    
         
            +
            - lib/command_generator.rb
         
     | 
| 
       56 
56 
     | 
    
         
             
            - lib/gpterm.rb
         
     | 
| 
      
 57 
     | 
    
         
            +
            - lib/input.rb
         
     | 
| 
      
 58 
     | 
    
         
            +
            - lib/parse_options.rb
         
     | 
| 
       57 
59 
     | 
    
         
             
            homepage: https://github.com/basicallydan/gpterm
         
     | 
| 
       58 
60 
     | 
    
         
             
            licenses:
         
     | 
| 
       59 
61 
     | 
    
         
             
            - MIT
         
     | 
    
        data/lib/config.rb
    DELETED
    
    | 
         @@ -1,38 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'yaml'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            module AppConfig
         
     | 
| 
       4 
     | 
    
         
            -
              CONFIG_FILE = File.join(Dir.home, '.gpterm', 'config.yml').freeze
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
              # Check if the directory exists, if not, create it
         
     | 
| 
       7 
     | 
    
         
            -
              unless File.directory?(File.dirname(CONFIG_FILE))
         
     | 
| 
       8 
     | 
    
         
            -
                Dir.mkdir(File.dirname(CONFIG_FILE))
         
     | 
| 
       9 
     | 
    
         
            -
              end
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
              def self.load_config
         
     | 
| 
       12 
     | 
    
         
            -
                YAML.load_file(CONFIG_FILE)
         
     | 
| 
       13 
     | 
    
         
            -
              rescue Errno::ENOENT
         
     | 
| 
       14 
     | 
    
         
            -
                default_config
         
     | 
| 
       15 
     | 
    
         
            -
              end
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
              def self.save_config(config)
         
     | 
| 
       18 
     | 
    
         
            -
                File.write(CONFIG_FILE, config.to_yaml)
         
     | 
| 
       19 
     | 
    
         
            -
              end
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
              def self.add_openapi_key(config, openapi_key)
         
     | 
| 
       22 
     | 
    
         
            -
                config['openapi_key'] = openapi_key
         
     | 
| 
       23 
     | 
    
         
            -
                save_config(config)
         
     | 
| 
       24 
     | 
    
         
            -
              end
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
              def self.add_preset(config, preset_name, preset_prompt)
         
     | 
| 
       27 
     | 
    
         
            -
                # This is a YAML file so we need to make sure the presets key exists
         
     | 
| 
       28 
     | 
    
         
            -
                config['presets'] ||= {}
         
     | 
| 
       29 
     | 
    
         
            -
                config['presets'][preset_name] = preset_prompt
         
     | 
| 
       30 
     | 
    
         
            -
                save_config(config)
         
     | 
| 
       31 
     | 
    
         
            -
              end
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
              def self.default_config
         
     | 
| 
       34 
     | 
    
         
            -
                {
         
     | 
| 
       35 
     | 
    
         
            -
                  'openapi_key' => ''
         
     | 
| 
       36 
     | 
    
         
            -
                }
         
     | 
| 
       37 
     | 
    
         
            -
              end
         
     | 
| 
       38 
     | 
    
         
            -
            end
         
     |