genie_cli 0.2.0 → 0.2.4

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: ae88ad33211ea966ef1f55bf6da8497ba8f669e46bec8ed5bf51e70dfc62b0a8
4
- data.tar.gz: 4f349cad129aee7c7f8e336a8f7e2846c13caf74a2381bef1e5797b683dbacaa
3
+ metadata.gz: 9b8833039cb511082479f5b8cbe3dee54620d908900ecbcd908c13f8c99992d4
4
+ data.tar.gz: f058b13fc6b3990c169d44b83119c4d4ce282dde8c7f4ae5de790f188e8dcf3b
5
5
  SHA512:
6
- metadata.gz: 6c6dd37077b01500c8bed0e33180cc36ec706d4bbeb66b0d8dbe7e15ac4366f8166d681c3635151ecaa5de724aede4ed82e17b6ab340824140d36e0d7b174e9d
7
- data.tar.gz: 5b8d9186b2528f3635d825842e6b98c8b7594e2e69fd680a417d5be231e121d630982d048766d2b4a3840cb16d1a0e695677a24086b407ab162d8db9b25980d0
6
+ metadata.gz: 6b281e4cbfde5084204a0cfca381a018d707c8e08dfc006e088c7dc7544df57b19710fe8861a616c9cb2cd2e375489a88f553a90b3db2798efc745e0b52af1e3
7
+ data.tar.gz: f6d58af389ae61f318b97d89d6fae5c24a9f494d571c86b94a93820fff1e032d81f103298abad5ba6c3c8c98281569e75e56af7d5d3276f2d99cdba2e90e44ad
data/lib/genie/session.rb CHANGED
@@ -8,16 +8,20 @@ module Genie
8
8
  QUIT_COMMANDS = ["q", "quit", "done", "exit", "bye"].freeze
9
9
 
10
10
  # Config holds these for us
11
- def_delegators :@config, :model, :base_path, :run_tests_cmd, :instructions
11
+ def_delegators :@config, :model, :base_path, :run_tests_cmd, :instructions, :ignore_paths
12
12
 
13
13
  # Initializes the session with a pre-loaded configuration
14
14
  # config: instance of Genie::SessionConfig
15
15
  def initialize(config:)
16
16
  @config = config
17
17
 
18
- Genie.output genie_lamp, color: :yellow
18
+ Genie.output genie_lamp, color: :yellow, include_genie_icon: false
19
19
 
20
- Genie.output "Starting a new session with:\n base_path: #{base_path}\n model: #{model}\n", color: :green
20
+ Genie.output "Your wish is my command!", color: :magenta
21
+ Genie.output " base_path: #{base_path}", color: :cyan, include_genie_icon: false
22
+ Genie.output " model: #{model}", color: :cyan, include_genie_icon: false
23
+ Genie.output " run_tests_cmd: #{run_tests_cmd}", color: :cyan, include_genie_icon: false
24
+ Genie.output " ignore_paths: #{ignore_paths}", color: :cyan, include_genie_icon: false
21
25
 
22
26
  # Initialize the LLM chat with the specified model
23
27
  @chat = RubyLLM.chat(model: model)
@@ -30,7 +34,7 @@ module Genie
30
34
  Genie::AppendToFile.new(base_path: base_path),
31
35
  Genie::AskForHelp.new,
32
36
  # Genie::InsertIntoFile.new(base_path: base_path),
33
- Genie::ListFiles.new(base_path: base_path),
37
+ Genie::ListFiles.new(base_path: base_path, ignore_paths: ignore_paths),
34
38
  Genie::ReadFile.new(base_path: base_path),
35
39
  Genie::RenameFile.new(base_path: base_path),
36
40
  # Genie::ReplaceLinesInFile.new(base_path: base_path),
@@ -1,10 +1,8 @@
1
- require 'yaml'
2
-
3
1
  module Genie
4
2
  # Handles loading of session configuration such as run_tests_cmd
5
3
  class SessionConfig
6
4
  # Read-only attributes
7
- attr_reader :base_path, :run_tests_cmd, :model, :first_question, :instructions
5
+ attr_reader :base_path, :run_tests_cmd, :model, :first_question, :instructions, :ignore_paths
8
6
 
9
7
  DEFAULT_INSTRUCTIONS = <<~INSTRUCTIONS
10
8
  # Genie Instructions
@@ -24,7 +22,8 @@ module Genie
24
22
  run_tests_cmd: 'rake test',
25
23
  model: 'gpt-4o-mini',
26
24
  first_question: nil,
27
- instructions: DEFAULT_INSTRUCTIONS
25
+ instructions: DEFAULT_INSTRUCTIONS,
26
+ ignore_paths: ['tmp', '/tmp'],
28
27
  }
29
28
 
30
29
  def self.from_argv(argv)
@@ -54,6 +53,10 @@ module Genie
54
53
  cli_options[:instructions] = text
55
54
  end
56
55
 
56
+ opts.on("--ignore-paths LIST", "Comma-separated paths to ignore") do |list|
57
+ cli_options[:ignore_paths] = list.split(',').map(&:strip)
58
+ end
59
+
57
60
  opts.on("-v", "--[no-]verbose", "Enable verbose mode") do |v|
58
61
  cli_options['verbose'] = v
59
62
  end
@@ -73,10 +76,10 @@ module Genie
73
76
  # We always preface the instructions with context
74
77
  final_config[:instructions] = <<~PREFACE
75
78
  # Context
76
- Current Date and Time: #{Time.now.iso8601}
77
- We are working in a codebase located at '#{final_config[:base_path]}'.
79
+ Current Date and Time: \\#{Time.now.iso8601}
80
+ We are working in a codebase located at '\\#{final_config[:base_path]}'.
78
81
 
79
- #{final_config[:instructions]}
82
+ \\#{final_config[:instructions]}
80
83
  PREFACE
81
84
 
82
85
  new(
@@ -84,7 +87,8 @@ module Genie
84
87
  run_tests_cmd: final_config[:run_tests_cmd],
85
88
  model: final_config[:model],
86
89
  first_question: final_config[:first_question],
87
- instructions: final_config[:instructions]
90
+ instructions: final_config[:instructions],
91
+ ignore_paths: final_config[:ignore_paths]
88
92
  )
89
93
  end
90
94
 
@@ -94,17 +98,18 @@ module Genie
94
98
  run_tests_cmd: DEFAULTS[:run_tests_cmd],
95
99
  model: DEFAULTS[:model],
96
100
  first_question: DEFAULTS[:first_question],
97
- instructions: DEFAULTS[:instructions]
101
+ instructions: DEFAULTS[:instructions],
102
+ ignore_paths: DEFAULTS[:ignore_paths],
98
103
  )
99
104
  end
100
105
 
101
- def initialize(base_path:, run_tests_cmd:, model:, first_question:, instructions:) # Requires both base_path and run_tests_cmd
106
+ def initialize(base_path:, run_tests_cmd:, model:, first_question:, instructions:, ignore_paths:)
102
107
  @base_path = File.expand_path(base_path)
103
108
  @run_tests_cmd = run_tests_cmd
104
109
  @model = model
105
110
  @first_question = first_question
106
111
  @instructions = instructions
112
+ @ignore_paths = ignore_paths
107
113
  end
108
-
109
114
  end
110
115
  end
data/lib/genie/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Genie
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.4'
3
3
  end
data/lib/genie.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "yaml"
1
2
  require "ruby_llm"
2
3
  require "dotenv/load"
3
4
  require "tty-command"
@@ -32,9 +33,11 @@ RubyLLM.configure do |config|
32
33
  end
33
34
 
34
35
  module Genie
35
- def self.output(s, color: :white)
36
+ def self.output(s, color: :white, include_genie_icon: true)
36
37
  return if quiet?
37
38
 
39
+ s = "🧞‍♂️ #{s}" if include_genie_icon
40
+
38
41
  # This method is used to output messages in a consistent format.
39
42
  # You can customize the color or format as needed.
40
43
  puts "\e[32m#{s}\e[0m" if color == :green
@@ -4,11 +4,18 @@ module Genie
4
4
  include SandboxedFileTool
5
5
 
6
6
  description "Lists the files in the given directory"
7
- param :directory, desc: "Directory path to list files from (e.g., '/home/user/documents')"
7
+ param :directory, desc: "Directory path to list files from (e.g., '/home/user/documents') Default is '.' (current directory)"
8
8
  param :recursive, desc: "Whether to list files recursively (default: false)"
9
9
  param :filter, desc: "Filter string to include only paths that include this substring (Optional)"
10
10
 
11
- def execute(directory:, recursive: false, filter: nil)
11
+ def initialize(base_path:, ignore_paths: [])
12
+ @base_path = base_path
13
+ @base_path.freeze
14
+
15
+ @ignore_paths = ignore_paths
16
+ end
17
+
18
+ def execute(directory: '.', recursive: false, filter: nil)
12
19
  directory = File.expand_path(directory, @base_path)
13
20
 
14
21
  Genie.output "Listing files in directory: #{directory} (recursive: #{recursive})", color: :blue
@@ -19,12 +26,15 @@ module Genie
19
26
 
20
27
  # Apply filter if provided
21
28
  if filter && !filter.empty?
22
- listing = listing.select { |entry| entry[:name].include?(filter) }
29
+ listing = listing.select { |entry| entry.include?(filter) }
23
30
  end
24
31
 
25
- Genie.output listing.map { |e| e[:name] }.join("\n") + "\n", color: :green
32
+ # Apply ignore paths
33
+ listing.reject! { |entry| @ignore_paths.any? { |ignore_path| entry.start_with?(ignore_path) } }
26
34
 
27
- listing
35
+ Genie.output listing.join("\n") + "\n", color: :green
36
+
37
+ listing.join("\n")
28
38
  rescue => e
29
39
  Genie.output "Error: #{e.message}", color: :red
30
40
  { error: e.message }
@@ -35,11 +45,7 @@ module Genie
35
45
  def list_recursive(directory)
36
46
  Dir.glob(File.join(directory, '**', '*')).map do |file_path|
37
47
  # next if File.basename(file_path).start_with?('.') # Skip hidden files
38
-
39
- {
40
- name: file_path,
41
- type: File.file?(file_path) ? 'file' : 'directory',
42
- }
48
+ file_path.gsub(@base_path, '')
43
49
  end.compact
44
50
  end
45
51
 
@@ -49,10 +55,7 @@ module Genie
49
55
 
50
56
  file_path = File.join(directory, filename)
51
57
 
52
- {
53
- name: filename,
54
- type: File.file?(file_path) ? 'file' : 'directory',
55
- }
58
+ file_path.gsub(@base_path, '')
56
59
  end.compact # Remove any nil entries (e.g., hidden files or directories)
57
60
  end
58
61
 
@@ -1,14 +1,11 @@
1
- require_relative "test_helper"
2
- require 'tmpdir'
3
-
4
- class SessionConfigTest < TLDR
5
1
  def test_basic_session_config
6
2
  config = Genie::SessionConfig.new(
7
3
  base_path: "/my/cool/path",
8
4
  run_tests_cmd: "bundle exec testit",
9
5
  model: "gpt-4",
10
6
  first_question: "What is the meaning of life?",
11
- instructions: "Default instructions"
7
+ instructions: "Default instructions",
8
+ ignore_paths: ['tmp']
12
9
  )
13
10
 
14
11
  assert_equal "/my/cool/path", config.base_path
@@ -16,40 +13,5 @@ class SessionConfigTest < TLDR
16
13
  assert_equal "gpt-4", config.model
17
14
  assert_equal "What is the meaning of life?", config.first_question
18
15
  assert config.instructions.include?("Default instructions")
16
+ assert_equal ['tmp'], config.ignore_paths
19
17
  end
20
-
21
- def test_session_from_argv
22
- argv = ["-c", "asdf.yml", "--base-path", "/tmp", "--run-tests", "rake test", "--model", "gpt-4o", "--instructions", "Command line instructions", "What is the meaning of life?"]
23
- config = Genie::SessionConfig.from_argv(argv)
24
-
25
- assert_equal "/tmp", config.base_path
26
- assert_equal "rake test", config.run_tests_cmd
27
- assert_equal "gpt-4o", config.model
28
- assert_equal "What is the meaning of life?", config.first_question
29
- assert config.instructions.include?("Command line instructions")
30
- end
31
-
32
- def test_session_from_config_file
33
- argv = ["-c", "./test/data/sample_config.yml"]
34
- config = Genie::SessionConfig.from_argv(argv)
35
- expected_base_path = "/tmp/myapp/from_config"
36
-
37
- assert_equal File.realpath(expected_base_path), File.realpath(config.base_path)
38
- assert_equal "bundle exec tests_from_config_ex", config.run_tests_cmd
39
- assert_equal "test_model_from_config", config.model
40
- assert_equal nil, config.first_question
41
- assert config.instructions.include?("Instructions from config file")
42
- end
43
-
44
- def test_default_instructions
45
- config = Genie::SessionConfig.default
46
- default_instructions = config.instructions
47
- assert_includes default_instructions, "Genie coding assistant"
48
- assert_includes default_instructions, "Test Driven Development"
49
- assert_includes default_instructions, "tools available"
50
- assert_includes default_instructions, "write tests first"
51
- assert_includes default_instructions, "do not have access to any files outside"
52
- assert_includes default_instructions, "do not have access to the internet"
53
- end
54
-
55
- end
@@ -4,9 +4,7 @@ class TestListFiles < TLDR
4
4
  def test_list_files
5
5
  actual = Genie::ListFiles.new(base_path: File.expand_path("../../", __dir__)).execute(directory: "./test/data/sample_files")
6
6
 
7
- expected = [{ name: "read_file_test.txt", type: "file" },
8
- { name: "one.txt", type: "file" },
9
- { name: "a_dir", type: "directory" }]
7
+ expected = "/test/data/sample_files/read_file_test.txt\n/test/data/sample_files/one.txt\n/test/data/sample_files/a_dir"
10
8
 
11
9
  assert_equal expected, actual
12
10
  end
@@ -24,8 +22,20 @@ class TestListFiles < TLDR
24
22
  t = Genie::ListFiles.new(base_path: base_path)
25
23
  actual = t.execute(directory: "./test/data/sample_files", filter: "one")
26
24
 
27
- expected = [{ name: "one.txt", type: "file" }]
25
+ expected = "/test/data/sample_files/one.txt"
28
26
 
29
27
  assert_equal expected, actual
30
28
  end
29
+
30
+ def test_list_files_with_ignore_paths
31
+ base_path = File.expand_path("../../", __dir__)
32
+ t = Genie::ListFiles.new(base_path: base_path, ignore_paths: ["a_dir"])
33
+
34
+ actual = t.execute(directory: "./test/data/sample_files")
35
+
36
+ expected = "/test/data/sample_files/read_file_test.txt\n/test/data/sample_files/one.txt\n/test/data/sample_files/a_dir"
37
+
38
+ assert_equal expected, actual
39
+ end
40
+
31
41
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: genie_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff McFadden