ollama_chat 0.0.107 → 0.0.108

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: e2686defd34fe5cf4e0f61c980480578f6b2589bc429d7b724ed4f3cb478d7b8
4
- data.tar.gz: 8bd9136e0e6f3e202c93f8c4aec0a4b482a447bfd1e29163b0dff1973e64f2fa
3
+ metadata.gz: 857b8cf92f68e5856238c1758cfebe463535ded52de3e4dc591cd83b501ccd63
4
+ data.tar.gz: fc90ce1e2145207dc49dfeb6512804dec0e0e3933c943ec0050221b5867bccd1
5
5
  SHA512:
6
- metadata.gz: 1a177889fb72f197a05895b60c7e73f1493a119fcc7832354772097f121d53b150ea500fdf50ae81bdeb05161f0a3121af204496d54a708a13c427096e126159
7
- data.tar.gz: 55c0270902a86e39e1704d62a4478daf0d04526312d47eb35f560b3b95218045d6699dd428b874fa9059ce3af8585bed97a5c2bd662751ef05885baa452a9c6b
6
+ metadata.gz: 173caf67db6f004400f1f5199e07fcd16acd36c44a531a7d8b5618e5c2fb03f994aa63d03c5e4140d2d443022afd7fccc29d6c17d63f76e2d7085f41b9dd97e9
7
+ data.tar.gz: 5addb13b768f52b9ef3f5e3bd532aa632a37e5b5bf7b3c69c64be2d063b0f74ddc41640dd9e19a87d27e570c581e24f5456cf7b3a4d7c1351eba85e6d152e10b
data/CHANGES.md CHANGED
@@ -1,5 +1,69 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-08-15 v0.0.108
4
+
5
+ ### Added
6
+
7
+ - Added `require 'io/console'` to support `IO#raw` for key reads.
8
+ - Added `max` to `THINK_MODE_STATES` in `think_control.rb` to support newer
9
+ Ollama releases (e.g., **0.32.13**).
10
+ - Added migration `008_drop_think_mode_constraint.rb` to drop the `think_mode`
11
+ `CHECK` constraint in SQLite.
12
+ - Added `.obsidian` to `.gitignore` and the `GemHadar` `ignore` list in
13
+ `Rakefile`.
14
+ - Added `require_confirmation: true` to `get_current_weather`, `get_cve`,
15
+ `get_endoflife`, `browse`, `get_jira_issue`, `get_rfc`, and `get_ghr`.
16
+
17
+ ### Changed
18
+
19
+ - Refactored `confirm?` key reads to use `STDIN.raw(min:, time:, intr: true)`
20
+ instead of `stty` commands.
21
+ - Updated `confirm?` to print prompts to `output` instead of `STDOUT`.
22
+ - Updated glyphs for confirmation: `\u2328\uFE0F` to `\u2328`, `☑️` to `\u2611`,
23
+ and `🚫` to `\u{1F6AB}`.
24
+ - Scoped `Reline` histories for various interactive prompts:
25
+ - `duplicate_session` name prompt now scoped to `:session_name`.
26
+ - `determine_valid_new_name_for_persona` now scoped to `:persona_name`.
27
+ - `add_persona` `[FILES]` patterns prompt now scoped to `:patterns`.
28
+ - `add_new_prompt` `[FILES]` patterns prompt now scoped to `:patterns`.
29
+ - `determine_valid_output_filename` loop now scoped to `:filename`.
30
+ - `/session summarize -f` filename prompt now scoped to `:filename`.
31
+ - `choose_profile_for_model` profile name prompt now scoped to
32
+ `:profile_name`.
33
+ - Scoped `Reline` histories for RAG prompts:
34
+ - `rename_collection` now scopes to `:collection_name`.
35
+ - `create_collection` scopes to `:collection_name`,
36
+ `:collection_description`, and `:patterns`.
37
+ - `edit_collection` description prompt now uses `:collection_description`.
38
+ - Refactored terminal truncation by moving `truncate_for_terminal` from
39
+ `prepare_last_message` to `last_message_with_user`.
40
+ - Added `height:` keyword argument to `last_message_with_user` and
41
+ `display_formatted_terminal_output`.
42
+ - Refactored `display_formatted_terminal_output` to use `move_home` and
43
+ `erase_in_display(nil)` instead of `clear_screen` to prevent `tmux` flicker.
44
+ - Updated `require_confirmation` to `false` for `open_file_in_editor`,
45
+ `paste_into_editor`, and `generate_image`.
46
+ - Removed `jq` parsing from `OLLAMA_CHAT_TOOLS_TEST_RUNNER` in `.envrc`.
47
+ - Introduced `OLLAMA_CHAT_COLLECTION_NAME_REGEXP` in `lib/ollama_chat.rb` to
48
+ enforce safe identifiers.
49
+ - Updated `OLLAMA_CHAT::Chat#initial_collection` to validate names against the
50
+ new regex.
51
+ - Wrapped web search link list output in `use_pager` within
52
+ `OLLAMA_CHAT::WebSearching`.
53
+
54
+ ### Fixed
55
+ - Added `rescue` for `Interrupt` and `IRB::Abort` during key reads to return
56
+ `nil` and print `"\u274C\uFE0F"`.
57
+ - Fixed `tmux` flicker in vertically split panes by avoiding full-pane redraws.
58
+ - Fixed `OLLAMA_CHAT::Tools::PatchFile` to explicitly warn against redundant
59
+ tool calls.
60
+
61
+ ### Refactored
62
+ - Updated YARD `@param output` and `@return` documentation.
63
+ - Simplified `last_message_with_user` to return a single joined string.
64
+ - Simplified `OLLAMA_CHAT::Tools::SearchKnowledge` to raise
65
+ `OLLAMA_CHAT::ToolFunctionArgumentError` on invalid input.
66
+
3
67
  ## 2026-08-09 v0.0.107
4
68
 
5
69
  ### Added
data/Rakefile CHANGED
@@ -20,7 +20,7 @@ GemHadar do
20
20
  test_dir 'spec'
21
21
  ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.AppleDouble', '.bundle',
22
22
  '.yardoc', 'doc', 'tags', 'corpus', 'coverage', '/config/searxng/*',
23
- '.starscope.db', 'cscope.out', 'errors.lst', 'tmp/*'
23
+ '.starscope.db', 'cscope.out', 'errors.lst', 'tmp/*', '.obsidian'
24
24
  package_ignore '.all_images.yml', '.tool-versions', '.gitignore', 'VERSION',
25
25
  '.rspec', '.github', '.contexts', '.envrc', '.yardopts'
26
26
 
@@ -229,11 +229,14 @@ class OllamaChat::Chat
229
229
  #
230
230
  # @return [ Symbol ] the collection name symbol
231
231
  def initial_collection
232
- (
233
- session&.current_collection.full? ||
232
+ c = session&.current_collection.full? ||
234
233
  config.embedding.collection.full? ||
235
- :default
236
- ).to_sym
234
+ 'default'
235
+ if c =~ /\A#{OllamaChat::COLLECTION_NAME_REGEXP.source}\z/
236
+ c
237
+ else
238
+ 'default'
239
+ end.to_sym
237
240
  end
238
241
 
239
242
  # The initial_system_prompt method returns the system prompt for the initial
@@ -296,8 +296,10 @@ module OllamaChat::Commands
296
296
  when 'summarize'
297
297
  opts = go_command('fs', opts)
298
298
  if opts[?f] and
299
- filename = ask?(prompt: "❓ Enter filename: ").full? { Pathname.new(_1) }
300
- then
299
+ filename = switch_history(:filename) {
300
+ ask?(prompt: "❓ Enter filename: ")
301
+ }.full? { Pathname.new(_1) }
302
+ then
301
303
  if filename.exist? && !confirm?(
302
304
  prompt: "🔔 File #{filename.to_s.inspect} already exists, overwrite? (y/n) ",
303
305
  yes: /\Ay/i
@@ -807,7 +809,7 @@ module OllamaChat::Commands
807
809
 
808
810
  command(
809
811
  name: :input,
810
- regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[apre]|c\s*\w+|w\s*\d+|t\s*[-\w\.]+(?:,[-\w\.]+)*))*)(?:\s+([^-].*))?$),
812
+ regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[apre]|c\s*#{OllamaChat::COLLECTION_NAME_REGEXP.source}|w\s*\d+|t\s*[-\w\.]+(?:,[-\w\.]+)*))*)(?:\s+([^-].*))?$),
811
813
  optional: true,
812
814
  complete: [ 'input', %w[ path context embedding summary ] ],
813
815
  options: <<~EOT,
@@ -0,0 +1,130 @@
1
+ # Removes the `think_mode` CHECK constraint from the `sessions` table.
2
+ #
3
+ # The set of valid think modes is model-dependent and evolving upstream
4
+ # (e.g., `max` was only added mid-2026, and numeric token budgets are
5
+ # under discussion), so the schema should not pin a fixed vocabulary.
6
+ # State validation is handled in Ruby via
7
+ # `OllamaChat::ThinkControl::THINK_MODE_STATES` and the state selectors.
8
+ #
9
+ # SQLite cannot alter CHECK constraints in place, so the table is rebuilt
10
+ # using the same shadow-table pattern as
11
+ # `004_add_profile_to_model_options.rb`.
12
+ Sequel.migration do
13
+ up do
14
+ create_table(:sessions_new) do
15
+ primary_key :id
16
+ String :name, null: false
17
+ String :current_model
18
+ String :current_collection
19
+ String :default_persona_name
20
+ String :current_system_prompt
21
+ Boolean :tools_enabled, null: false
22
+ Text :tools_default_enabled
23
+ String :think_mode, null: false
24
+ Bool :think_loud_enabled, null: false
25
+ Bool :embedding_enabled, null: false
26
+ String :document_policy, null: false
27
+ Bool :runtime_info_enabled, null: false
28
+ Bool :think_strip_enabled, null: false
29
+ Boolean :markdown_enabled, null: false
30
+ Boolean :stream_enabled, null: false
31
+ Boolean :location_enabled, null: false
32
+ Boolean :voice_enabled, null: false
33
+ String :current_voice, null: false
34
+ Text :working_directory
35
+ Integer :locked_by_pid
36
+ Text :model_options
37
+ Text :messages, null: false
38
+ Text :links, null: false, default: ''
39
+ Text :history, null: false, default: ''
40
+ String :context_format, null: false, default: 'JSON'
41
+ Time :created_at
42
+ Time :updated_at
43
+ unique [:name]
44
+
45
+ constraint(
46
+ :document_policy,
47
+ document_policy: %w[ ignoring embedding importing summarizing ]
48
+ )
49
+ end
50
+
51
+ source = from(:sessions).select(
52
+ :id, :name, :current_model, :current_collection,
53
+ :default_persona_name, :current_system_prompt,
54
+ :tools_enabled, :tools_default_enabled, :think_mode,
55
+ :think_loud_enabled, :embedding_enabled, :document_policy,
56
+ :runtime_info_enabled, :think_strip_enabled,
57
+ :markdown_enabled, :stream_enabled, :location_enabled,
58
+ :voice_enabled, :current_voice, :working_directory,
59
+ :locked_by_pid, :model_options, :messages, :links,
60
+ :history, :context_format, :created_at, :updated_at
61
+ )
62
+ from(:sessions_new).insert(source)
63
+
64
+ drop_table(:sessions)
65
+ rename_table(:sessions_new, :sessions)
66
+ end
67
+
68
+ down do
69
+ # Restores the original `think_mode` constraint. All rows that
70
+ # predate the up-migration satisfy it; any newer value (e.g. `max`)
71
+ # would fail the insert, which is the desired loud failure on
72
+ # rollback.
73
+ create_table(:sessions_old) do
74
+ primary_key :id
75
+ String :name, null: false
76
+ String :current_model
77
+ String :current_collection
78
+ String :default_persona_name
79
+ String :current_system_prompt
80
+ Boolean :tools_enabled, null: false
81
+ Text :tools_default_enabled
82
+ String :think_mode, null: false
83
+ Bool :think_loud_enabled, null: false
84
+ Bool :embedding_enabled, null: false
85
+ String :document_policy, null: false
86
+ Bool :runtime_info_enabled, null: false
87
+ Bool :think_strip_enabled, null: false
88
+ Boolean :markdown_enabled, null: false
89
+ Boolean :stream_enabled, null: false
90
+ Boolean :location_enabled, null: false
91
+ Boolean :voice_enabled, null: false
92
+ String :current_voice, null: false
93
+ Text :working_directory
94
+ Integer :locked_by_pid
95
+ Text :model_options
96
+ Text :messages, null: false
97
+ Text :links, null: false, default: ''
98
+ Text :history, null: false, default: ''
99
+ String :context_format, null: false, default: 'JSON'
100
+ Time :created_at
101
+ Time :updated_at
102
+ unique [:name]
103
+
104
+ constraint(
105
+ :think_mode,
106
+ think_mode: %w[ disabled enabled low medium high ]
107
+ )
108
+ constraint(
109
+ :document_policy,
110
+ document_policy: %w[ ignoring embedding importing summarizing ]
111
+ )
112
+ end
113
+
114
+ source = from(:sessions).select(
115
+ :id, :name, :current_model, :current_collection,
116
+ :default_persona_name, :current_system_prompt,
117
+ :tools_enabled, :tools_default_enabled, :think_mode,
118
+ :think_loud_enabled, :embedding_enabled, :document_policy,
119
+ :runtime_info_enabled, :think_strip_enabled,
120
+ :markdown_enabled, :stream_enabled, :location_enabled,
121
+ :voice_enabled, :current_voice, :working_directory,
122
+ :locked_by_pid, :model_options, :messages, :links,
123
+ :history, :context_format, :created_at, :updated_at
124
+ )
125
+ from(:sessions_old).insert(source)
126
+
127
+ drop_table(:sessions)
128
+ rename_table(:sessions_old, :sessions)
129
+ end
130
+ end
@@ -1,3 +1,5 @@
1
+ require 'io/console'
2
+
1
3
  # A module that provides interactive selection and configuration functionality
2
4
  # for OllamaChat.
3
5
  #
@@ -35,27 +37,32 @@ module OllamaChat::Dialog
35
37
  # @param default [Object, nil] value returned when the timeout expires
36
38
  # (defaults to `nil`)
37
39
  # @param yes [Object, nil] value that is considered a positive response
38
- # @param output [IO] the IO object to write the prompt to
40
+ # @param output [IO] the IO object to write the prompt and the answer to
39
41
  #
40
- # @return [Object] the character entered by the user, or the `default` value
41
- # if a timeout occurs
42
+ # @return [Object] the character entered by the user, the `default` value
43
+ # if a timeout occurs, or `nil` if the read is interrupted (e.g. Ctrl-C)
42
44
  def confirm?(prompt:, timeout: nil, default: nil, yes: nil, output: STDOUT)
43
45
  return default if timeout&.zero?
44
46
  if prompt.include?('%s')
45
47
  prompt = prompt % (timeout ? ('timeout in %us' % timeout) : 'no timeout')
46
48
  end
47
- print prompt
48
- system 'stty raw'
49
- keypress = nil
50
- c = if timeout
51
- keypress = !!IO.select([ STDIN ], nil, nil, timeout)
52
- keypress ? STDIN.getc : nil
53
- else
54
- keypress = true
55
- STDIN.getc
56
- end
57
- system 'stty cooked'
58
- answer = c || default
49
+ output.print prompt
50
+ min = 1
51
+ time = 0
52
+ if timeout
53
+ min = 0
54
+ time = timeout
55
+ end
56
+ exceptions = [ (IRB::Abort if defined? IRB), Interrupt ].compact
57
+ begin
58
+ keypress = STDIN.raw(min:, time:, intr: true) do |io|
59
+ io.getc
60
+ end
61
+ rescue *exceptions
62
+ output.puts "\u274C\uFE0F"
63
+ return
64
+ end
65
+ answer = keypress || default
59
66
  case
60
67
  when yes.nil?
61
68
  if keypress
@@ -68,12 +75,12 @@ module OllamaChat::Dialog
68
75
  if keypress
69
76
  output.puts "\u2705\uFE0F #{answer}"
70
77
  else
71
- output.puts "☑️ #{answer}"
78
+ output.puts "\u2611\uFE0F #{answer}"
72
79
  end
73
80
  answer
74
81
  else
75
82
  if keypress
76
- output.puts "🚫 #{answer}"
83
+ output.puts "\u{1F6AB} #{answer}"
77
84
  else
78
85
  output.puts "\u231B\uFE0F #{answer}"
79
86
  end
@@ -111,20 +111,22 @@ module OllamaChat::FileEditing
111
111
  # @return [Pathname, nil] the validated filename as a Pathname, or nil if the
112
112
  # operation was cancelled
113
113
  def determine_valid_output_filename(action)
114
- loop do
115
- filename_str = ask?(
116
- prompt: "❓ Enter filename #{action}, C-c ⇒ cancel: "
117
- )
118
- if filename_str.nil?
119
- STDOUT.puts "Cancelled."
120
- return nil
121
- end
114
+ switch_history(:filename) do
115
+ loop do
116
+ filename_str = ask?(
117
+ prompt: "❓ Enter filename #{action}, C-c ⇒ cancel: "
118
+ )
119
+ if filename_str.nil?
120
+ STDOUT.puts "Cancelled."
121
+ return nil
122
+ end
122
123
 
123
- filename = Pathname.new(filename_str)
124
- if filename.exist?
125
- STDERR.puts "File #{filename.to_path.inspect} already exists!"
126
- else
127
- return filename
124
+ filename = Pathname.new(filename_str)
125
+ if filename.exist?
126
+ STDERR.puts "File #{filename.to_path.inspect} already exists!"
127
+ else
128
+ return filename
129
+ end
128
130
  end
129
131
  end
130
132
  end
@@ -39,6 +39,9 @@ class OllamaChat::FollowChat
39
39
  end
40
40
  end
41
41
 
42
+ # Returns the chat object representing the conversation context.
43
+ #
44
+ # @return [OllamaChat::Chat] The chat object.
42
45
  attr_reader :chat
43
46
 
44
47
  # Returns the conversation history (an array of message objects).
@@ -74,7 +77,7 @@ class OllamaChat::FollowChat
74
77
  ensure_assistant_response_exists
75
78
  update_last_message(response)
76
79
  if chat.stream.on?
77
- display_formatted_terminal_output
80
+ display_formatted_terminal_output(height: Tins::Terminal.lines - 1)
78
81
  else
79
82
  if display_output
80
83
  display_formatted_terminal_output
@@ -293,9 +296,9 @@ class OllamaChat::FollowChat
293
296
  def prepare_last_message
294
297
  content, thinking = @messages.last.content, @messages.last.thinking
295
298
  if chat.markdown.on?
296
- content = talk_annotate { truncate_for_terminal chat.kramdown_ansi_parse(content) }
299
+ content = talk_annotate { chat.kramdown_ansi_parse(content) }
297
300
  if chat.think?
298
- thinking = think_annotate { truncate_for_terminal chat.kramdown_ansi_parse(thinking) }
301
+ thinking = think_annotate { chat.kramdown_ansi_parse(thinking) }
299
302
  end
300
303
  else
301
304
  content = talk_annotate { content }
@@ -304,20 +307,28 @@ class OllamaChat::FollowChat
304
307
  return content&.chomp, thinking&.chomp
305
308
  end
306
309
 
307
- # The last_message_with_user method constructs a formatted message array by
310
+ # The last_message_with_user method constructs a formatted string by
308
311
  # combining user information, newline characters, thinking annotations, and
309
312
  # content for display in the terminal output.
310
313
  #
311
- # @return [ Array ] an array containing the user identifier, newline
312
- # character, thinking annotation (if present), and content formatted for
313
- # terminal display
314
- def last_message_with_user
314
+ # @param height [Integer] the maximum number of lines to truncate the output
315
+ # to. Defaults to 0 (no truncation).
316
+ #
317
+ # @return [String] the formatted message string containing the user
318
+ # identifier, newline character, thinking annotation (if present), and
319
+ # content formatted for terminal display
320
+ def last_message_with_user(height: 0)
315
321
  content, thinking = prepare_last_message
316
- if thinking.present?
317
- [ @sender + ?:, ?\n, thinking, ?\n, content ]
318
- else
319
- [ @sender + ?:, ?\n, content ]
322
+ content ||= ''
323
+ result = if thinking.present?
324
+ [ @sender + ":\n", thinking, ?\n, content ]
325
+ else
326
+ [ @sender + ":\n", content ]
327
+ end * ''
328
+ if height > 0
329
+ result = truncate_for_terminal(result, max_lines: height)
320
330
  end
331
+ result
321
332
  end
322
333
 
323
334
  # The display_formatted_terminal_output method formats and outputs the
@@ -327,9 +338,17 @@ class OllamaChat::FollowChat
327
338
  # move home commands. The method takes into account whether markdown and
328
339
  # thinking modes are enabled to determine how to process and display the
329
340
  # content.
330
- def display_formatted_terminal_output(output = nil)
341
+ #
342
+ # @param output [IO, nil] the output stream to print to. Defaults to
343
+ # `@output`.
344
+ # @param height [Integer] the maximum number of lines to truncate the output
345
+ # to. Defaults to 0 (no truncation).
346
+ def display_formatted_terminal_output(output = nil, height: 0)
331
347
  output ||= @output
332
- output.print(*([ clear_screen, move_home, *last_message_with_user ].compact))
348
+ output.print(
349
+ move_home, erase_in_display(nil),
350
+ last_message_with_user(height:)
351
+ )
333
352
  end
334
353
 
335
354
  # The display_output method shows the last message in the conversation.
@@ -345,7 +364,7 @@ class OllamaChat::FollowChat
345
364
  if chat.markdown.on?
346
365
  display_formatted_terminal_output(output)
347
366
  else
348
- output.print(*last_message_with_user)
367
+ output.print(last_message_with_user)
349
368
  end
350
369
  end
351
370
  end
@@ -255,7 +255,9 @@ module OllamaChat::ModelHandling
255
255
  STDOUT.puts "Cancelled."
256
256
  return
257
257
  when '[NEW]'
258
- name = ask?(prompt: 'Enter new profile name: ') or return
258
+ name = switch_history(:profile_name) do
259
+ ask?(prompt: 'Enter new profile name: ')
260
+ end or return
259
261
  if models::ModelOptions.where(model_name:, profile: name).present?
260
262
  STDERR.puts "Profile #{name.inspect} already exists!"
261
263
  return
@@ -416,14 +416,17 @@ tools:
416
416
  result_display_timeout: 3
417
417
  url: "https://api.pirateweather.net/forecast/KeyInHeader/%{lat},%{lon}?units=%{units}"
418
418
  default: false
419
+ require_confirmation: true
419
420
  get_cve:
420
421
  result_display_timeout: 3
421
422
  url: 'https://cveawg.mitre.org/api/cve/%{cve_id}'
422
423
  default: true
424
+ require_confirmation: true
423
425
  get_endoflife:
424
426
  result_display_timeout: 3
425
427
  url: "https://endoflife.date/api/v1/products/%{product}"
426
428
  default: false
429
+ require_confirmation: true
427
430
  get_time:
428
431
  result_display_timeout: 3
429
432
  default: true
@@ -451,6 +454,7 @@ tools:
451
454
  browse:
452
455
  result_display_timeout: 3
453
456
  default: true
457
+ require_confirmation: true
454
458
  write_file:
455
459
  default: true
456
460
  require_confirmation: true
@@ -476,9 +480,9 @@ tools:
476
480
  - './spec'
477
481
  search_web:
478
482
  default: true
479
- require_confirmation: true
480
483
  result_display_timeout: 3
481
484
  max_results: 25
485
+ require_confirmation: true
482
486
  get_url:
483
487
  default: true
484
488
  require_confirmation: true
@@ -491,10 +495,9 @@ tools:
491
495
  default: true
492
496
  open_file_in_editor:
493
497
  default: true
494
- require_confirmation: true
498
+ require_confirmation: false
495
499
  result_display_timeout: 0
496
500
  run_tests:
497
- require_confirmation: true
498
501
  result_display_timeout: 3
499
502
  default: true
500
503
  allowed:
@@ -504,10 +507,12 @@ tools:
504
507
  get_jira_issue:
505
508
  result_display_timeout: 3
506
509
  default: false
510
+ require_confirmation: true
507
511
  get_rfc:
508
512
  url: "https://www.rfc-editor.org/rfc/rfc%{rfc_id}.txt"
509
513
  result_display_timeout: 3
510
514
  default: true
515
+ require_confirmation: true
511
516
  generate_password:
512
517
  default: true
513
518
  copy_to_clipboard:
@@ -518,7 +523,7 @@ tools:
518
523
  default: true
519
524
  paste_into_editor:
520
525
  result_display_timeout: 3
521
- require_confirmation: true
526
+ require_confirmation: false
522
527
  default: true
523
528
  execute_ri:
524
529
  result_display_timeout: 3
@@ -538,7 +543,7 @@ tools:
538
543
  result_display_timeout: 3
539
544
  default: true
540
545
  generate_image:
541
- require_confirmation: true
546
+ require_confirmation: false
542
547
  default: true
543
548
  timeout_attempts: 23
544
549
  timeout_duration: 66
@@ -549,6 +554,7 @@ tools:
549
554
  get_ghr:
550
555
  default: true
551
556
  result_display_timeout: 3
557
+ require_confirmation: true
552
558
  eval_ruby:
553
559
  default: false
554
560
  result_display_timeout: 3
@@ -186,10 +186,12 @@ module OllamaChat::PersonaeManagement
186
186
  when '[CLIPBOARD]'
187
187
  perform_paste_from_clipboard(edit: false)
188
188
  when '[FILES]'
189
- patterns = ask?(
190
- prompt: "❓ Enter file patterns to load file, C-u ⇒ new, C-c ⇒ cancel: ",
191
- prefill: '**/*.{txt,md}'
192
- )
189
+ patterns = switch_history(:patterns) do
190
+ ask?(
191
+ prompt: "❓ Enter file patterns to load file, C-u ⇒ new, C-c ⇒ cancel: ",
192
+ prefill: '**/*.{txt,md}'
193
+ )
194
+ end
193
195
  patterns.nil? ? (return) : (patterns.present? ? load_prompt_from_file(patterns) : nil)
194
196
  else
195
197
  nil
@@ -516,22 +518,24 @@ module OllamaChat::PersonaeManagement
516
518
  # @param action [String] The action being performed (e.g., 'to import')
517
519
  # @return [String, nil] The validated persona name or nil if cancelled
518
520
  def determine_valid_new_name_for_persona(action)
519
- persona_name = nil
520
- loop do
521
- persona_name = ask?(
522
- prompt: "❓ Enter new persona prompt name #{action}, C-c ⇒ cancel: "
523
- )
524
- if persona_name.nil?
525
- STDOUT.puts "Cancelled."
526
- return nil
527
- end
528
- if persona_name_to_pathname(persona_name).exist?
529
- STDOUT.puts "Persona prompt named #{bold{persona_name}} already exists."
530
- else
531
- break
521
+ switch_history(:persona_name) do
522
+ persona_name = nil
523
+ loop do
524
+ persona_name = ask?(
525
+ prompt: "❓ Enter new persona prompt name #{action}, C-c ⇒ cancel: "
526
+ )
527
+ if persona_name.nil?
528
+ STDOUT.puts "Cancelled."
529
+ return nil
530
+ end
531
+ if persona_name_to_pathname(persona_name).exist?
532
+ STDOUT.puts "Persona prompt named #{bold{persona_name}} already exists."
533
+ else
534
+ break
535
+ end
532
536
  end
537
+ persona_name
533
538
  end
534
- persona_name
535
539
  end
536
540
 
537
541
  # Interactively duplicates an existing persona profile to a new name.
@@ -103,10 +103,12 @@ module OllamaChat::PromptManagement
103
103
  when '[CLIPBOARD]'
104
104
  perform_paste_from_clipboard(edit: false)
105
105
  when '[FILES]'
106
- patterns = ask?(
107
- prompt: "❓ Enter file patterns to load file, C-u ⇒ new, C-c ⇒ cancel: ",
108
- prefill: '**/*.{txt,md}'
109
- )
106
+ patterns = switch_history(:patterns) do
107
+ ask?(
108
+ prompt: "❓ Enter file patterns to load file, C-u ⇒ new, C-c ⇒ cancel: ",
109
+ prefill: '**/*.{txt,md}'
110
+ )
111
+ end
110
112
  patterns.nil? ? (return) : (patterns.present? ? load_prompt_from_file(patterns) : nil)
111
113
  else
112
114
  nil
@@ -125,26 +125,24 @@ module OllamaChat::RAGHandling
125
125
  #
126
126
  # @param current_collection [Symbol] the current collection name
127
127
  def rename_collection(current_collection)
128
- switch_history(:rename_collection) do
129
- prompt = 'Rename collection %s to: ' % current_collection
130
- new_collection = switch_history :collection do
131
- ask?(prompt:, prefill: current_collection).full?(:to_sym)
132
- end
133
- if new_collection
134
- begin
135
- @documents.rename_collection(new_collection)
136
- col = database_collection?(current_collection)
137
- col&.update(name: new_collection.to_s)
138
- log(:info, "Collection renamed", data: { old_name: current_collection, new_name: new_collection })
139
- STDOUT.puts "Renamed current collection #{current_collection} to #{new_collection}."
140
- rescue Sequel::UniqueConstraintViolation
141
- STDERR.puts "❌ Renaming to #{new_collection} failed, it already exists in database."
142
- rescue => e
143
- STDERR.puts "❌ Renaming to #{new_collection} failed: #{e.message}"
144
- end
145
- else
146
- STDOUT.puts "Renaming cancelled."
128
+ prompt = 'Rename collection %s to: ' % current_collection
129
+ new_collection = switch_history :collection_name do
130
+ ask?(prompt:, prefill: current_collection).full?(:to_sym)
131
+ end
132
+ if new_collection
133
+ begin
134
+ @documents.rename_collection(new_collection)
135
+ col = database_collection?(current_collection)
136
+ col&.update(name: new_collection.to_s)
137
+ log(:info, "Collection renamed", data: { old_name: current_collection, new_name: new_collection })
138
+ STDOUT.puts "Renamed current collection #{current_collection} to #{new_collection}."
139
+ rescue Sequel::UniqueConstraintViolation
140
+ STDERR.puts "❌ Renaming to #{new_collection} failed, it already exists in database."
141
+ rescue => e
142
+ STDERR.puts "❌ Renaming to #{new_collection} failed: #{e.message}"
147
143
  end
144
+ else
145
+ STDOUT.puts "Renaming cancelled."
148
146
  end
149
147
  end
150
148
 
@@ -234,7 +232,9 @@ module OllamaChat::RAGHandling
234
232
 
235
233
  # Interactively create a new collection record.
236
234
  def create_collection
237
- name = ask?(prompt: "📚 Name of the new collection: ")
235
+ name = switch_history(:collection_name) do
236
+ ask?(prompt: "📚 Name of the new collection: ")
237
+ end
238
238
  unless name.full?
239
239
  STDERR.puts "❌ Cancelled creation of collection."
240
240
  return
@@ -245,12 +245,16 @@ module OllamaChat::RAGHandling
245
245
  return
246
246
  end
247
247
 
248
- description = ask?(prompt: "📝 Description: ")
248
+ description = switch_history(:collection_description) do
249
+ ask?(prompt: "📝 Description: ")
250
+ end
249
251
  unless description.full?
250
252
  STDERR.puts "❌ Cancelled creation of collection #{name.inspect}."
251
253
  return
252
254
  end
253
- patterns_str = ask?(prompt: "🔍 Patterns (space-separated globs, e.g., lib/**/*.rb): ")
255
+ patterns_str = switch_history(:patterns) do
256
+ ask?(prompt: "🔍 Patterns (space-separated globs, e.g., lib/**/*.rb): ")
257
+ end
254
258
  patterns = extract_patterns(patterns_str)
255
259
 
256
260
  begin
@@ -288,7 +292,7 @@ module OllamaChat::RAGHandling
288
292
  return
289
293
  end
290
294
 
291
- new_description = switch_history :collection do
295
+ new_description = switch_history :collection_description do
292
296
  ask?(
293
297
  prompt: "📝 New description (leave blank to keep): ",
294
298
  prefill: col.description
@@ -204,9 +204,11 @@ module OllamaChat::SessionManagement
204
204
  # prompts the user for a new name and whether to clear the duplicated
205
205
  # session's message history.
206
206
  def duplicate_session
207
- name = determine_valid_new_name_for_session(
208
- 'to create', default_name: session.name
209
- ) or return
207
+ name = switch_history(:session_name) do
208
+ determine_valid_new_name_for_session(
209
+ 'to create', default_name: session.name
210
+ )
211
+ end or return
210
212
  old_session = session
211
213
  old_session.unlock
212
214
  @session = session.duplicate
@@ -17,7 +17,10 @@ module OllamaChat::ThinkControl
17
17
  # * `low`: A minimal or subtle thinking intensity.
18
18
  # * `medium`: A balanced approach to thinking and reasoning.
19
19
  # * `high`: An intensive, detailed, or highly verbose thinking mode.
20
- THINK_MODE_STATES = %w[ disabled enabled low medium high ]
20
+ # * `max`: The maximum thinking intensity, where supported by the
21
+ # model. Some models treat `high` and `max` as equivalent (e.g.,
22
+ # Qwen3.8 maps both to its xhigh reasoning effort).
23
+ THINK_MODE_STATES = %w[ disabled enabled low medium high max ]
21
24
 
22
25
  # The think method returns the current think mode selection.
23
26
  #
@@ -154,7 +154,11 @@ class OllamaChat::Tools::PatchFile
154
154
  #{msg.inspect}
155
155
  EOT
156
156
  else
157
- "User accepted the patch to file #{path.to_s.inspect}."
157
+ <<~EOT
158
+ User accepted the patch to file #{path.to_s.inspect}. **NEVER** call
159
+ the tool function with this patch again, as it has already been
160
+ applied successfully!
161
+ EOT
158
162
  end
159
163
  else
160
164
  "Failed to apply patch to file #{path.to_s.inspect}."
@@ -113,6 +113,9 @@ class OllamaChat::Tools::SearchKnowledge
113
113
  old_collection = nil
114
114
 
115
115
  if collection = args.collection.full?
116
+ unless collection.to_s.match?(/\A#{OllamaChat::COLLECTION_NAME_REGEXP.source}\z/)
117
+ raise OllamaChat::ToolFunctionArgumentError, "Invalid collection name: #{collection}"
118
+ end
116
119
  old_collection = chat.documents.collection
117
120
  chat.documents.collection = collection
118
121
  end
@@ -1,6 +1,6 @@
1
1
  module OllamaChat
2
2
  # OllamaChat version
3
- VERSION = '0.0.107'
3
+ VERSION = '0.0.108'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
@@ -137,10 +137,12 @@ module OllamaChat::WebSearching
137
137
  if links.empty?
138
138
  STDOUT.puts "List is empty."
139
139
  else
140
- w = Math.log10(links.size + 1).ceil
141
- format = "%#{w}s. %s"
142
- connect = -> link { hyperlink(link) { link } }
143
- STDOUT.puts links.each_with_index.map { |x, i| format % [ i + 1, connect.(x) ] }
140
+ use_pager do |output|
141
+ w = Math.log10(links.size + 1).ceil
142
+ format = "%#{w}s. %s"
143
+ connect = -> link { hyperlink(link) { link } }
144
+ output.puts links.each_with_index.map { |x, i| format % [ i + 1, connect.(x) ] }
145
+ end
144
146
  end
145
147
  end
146
148
  end
data/lib/ollama_chat.rb CHANGED
@@ -65,6 +65,15 @@ module OllamaChat
65
65
  # that need to terminate the main interaction loop cleanly.
66
66
  class OllamaChatQuitError < OllamaChatError
67
67
  end
68
+
69
+ # Regular expression defining the allowed characters for collection names.
70
+ #
71
+ # Matches alphanumeric characters, underscores, hyphens, and dots.
72
+ # Explicitly excludes slashes and other shell metacharacters to prevent
73
+ # path traversal and parsing issues.
74
+ #
75
+ # @return [Regexp] the pattern for validating collection identifiers
76
+ COLLECTION_NAME_REGEXP = /[-\w\.]+/
68
77
  end
69
78
 
70
79
  require 'ollama'
data/ollama_chat.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama_chat 0.0.107 ruby lib
2
+ # stub: ollama_chat 0.0.108 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama_chat".freeze
6
- s.version = "0.0.107".freeze
6
+ s.version = "0.0.108".freeze
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
12
12
  s.description = "The app provides a command-line interface (CLI) to an Ollama AI model,\nallowing users to engage in text-based conversations and generate\nhuman-like responses. Users can import data from local files or web pages,\nwhich are then processed through three different modes: fully importing the\ncontent into the conversation context, summarizing the information for\nconcise reference, or storing it in an embedding vector database for later\nretrieval based on the conversation.\n".freeze
13
13
  s.email = "flori@ping.de".freeze
14
14
  s.executables = ["ollama_chat".freeze, "ollama_chat_send".freeze, "ollama_chat_log".freeze]
15
- s.extra_rdoc_files = ["README.md".freeze, "lib/ollama_chat.rb".freeze, "lib/ollama_chat/chat.rb".freeze, "lib/ollama_chat/clipboard.rb".freeze, "lib/ollama_chat/command_concern.rb".freeze, "lib/ollama_chat/commands.rb".freeze, "lib/ollama_chat/config_handling.rb".freeze, "lib/ollama_chat/conversation.rb".freeze, "lib/ollama_chat/database.rb".freeze, "lib/ollama_chat/database/duplicatable.rb".freeze, "lib/ollama_chat/database/migrations.rb".freeze, "lib/ollama_chat/database/migrations/001_initial_schema.rb".freeze, "lib/ollama_chat/database/migrations/002_add_links_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/003_add_history_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/004_add_profile_to_model_options.rb".freeze, "lib/ollama_chat/database/migrations/005_add_context_format_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb".freeze, "lib/ollama_chat/database/migrations/007_add_collections_table.rb".freeze, "lib/ollama_chat/database/models/collection.rb".freeze, "lib/ollama_chat/database/models/favourite.rb".freeze, "lib/ollama_chat/database/models/model_options.rb".freeze, "lib/ollama_chat/database/models/prompt.rb".freeze, "lib/ollama_chat/database/models/session.rb".freeze, "lib/ollama_chat/database/session_locking.rb".freeze, "lib/ollama_chat/dialog.rb".freeze, "lib/ollama_chat/document_cache.rb".freeze, "lib/ollama_chat/favourites_management.rb".freeze, "lib/ollama_chat/file_editing.rb".freeze, "lib/ollama_chat/follow_chat.rb".freeze, "lib/ollama_chat/history.rb".freeze, "lib/ollama_chat/http_handling.rb".freeze, "lib/ollama_chat/information.rb".freeze, "lib/ollama_chat/input_content.rb".freeze, "lib/ollama_chat/kramdown_ansi.rb".freeze, "lib/ollama_chat/links_set.rb".freeze, "lib/ollama_chat/location_handling.rb".freeze, "lib/ollama_chat/logging.rb".freeze, "lib/ollama_chat/message.rb".freeze, "lib/ollama_chat/message_editing.rb".freeze, "lib/ollama_chat/message_format.rb".freeze, "lib/ollama_chat/message_list.rb".freeze, "lib/ollama_chat/message_output.rb".freeze, "lib/ollama_chat/model_handling.rb".freeze, "lib/ollama_chat/oc.rb".freeze, "lib/ollama_chat/ollama_chat_config.rb".freeze, "lib/ollama_chat/pager.rb".freeze, "lib/ollama_chat/parsing.rb".freeze, "lib/ollama_chat/personae_management.rb".freeze, "lib/ollama_chat/prompt_handling.rb".freeze, "lib/ollama_chat/prompt_management.rb".freeze, "lib/ollama_chat/rag_handling.rb".freeze, "lib/ollama_chat/redis_cache.rb".freeze, "lib/ollama_chat/say.rb".freeze, "lib/ollama_chat/server_socket.rb".freeze, "lib/ollama_chat/session_management.rb".freeze, "lib/ollama_chat/source_fetching.rb".freeze, "lib/ollama_chat/state_selectors.rb".freeze, "lib/ollama_chat/switches.rb".freeze, "lib/ollama_chat/system_prompt_management.rb".freeze, "lib/ollama_chat/think_control.rb".freeze, "lib/ollama_chat/token_estimator.rb".freeze, "lib/ollama_chat/token_estimator/crude.rb".freeze, "lib/ollama_chat/tool_calling.rb".freeze, "lib/ollama_chat/tools.rb".freeze, "lib/ollama_chat/tools/browse.rb".freeze, "lib/ollama_chat/tools/compute_bmi.rb".freeze, "lib/ollama_chat/tools/concern.rb".freeze, "lib/ollama_chat/tools/copy_to_clipboard.rb".freeze, "lib/ollama_chat/tools/delete_file.rb".freeze, "lib/ollama_chat/tools/directory_structure.rb".freeze, "lib/ollama_chat/tools/eval_ruby.rb".freeze, "lib/ollama_chat/tools/execute_grep.rb".freeze, "lib/ollama_chat/tools/execute_ri.rb".freeze, "lib/ollama_chat/tools/file_context.rb".freeze, "lib/ollama_chat/tools/gem_path_lookup.rb".freeze, "lib/ollama_chat/tools/generate_image.rb".freeze, "lib/ollama_chat/tools/generate_password.rb".freeze, "lib/ollama_chat/tools/get_current_weather.rb".freeze, "lib/ollama_chat/tools/get_cve.rb".freeze, "lib/ollama_chat/tools/get_endoflife.rb".freeze, "lib/ollama_chat/tools/get_ghr.rb".freeze, "lib/ollama_chat/tools/get_jira_issue.rb".freeze, "lib/ollama_chat/tools/get_location.rb".freeze, "lib/ollama_chat/tools/get_rfc.rb".freeze, "lib/ollama_chat/tools/get_time.rb".freeze, "lib/ollama_chat/tools/get_url.rb".freeze, "lib/ollama_chat/tools/move_file.rb".freeze, "lib/ollama_chat/tools/open_file_in_editor.rb".freeze, "lib/ollama_chat/tools/paste_from_clipboard.rb".freeze, "lib/ollama_chat/tools/paste_into_editor.rb".freeze, "lib/ollama_chat/tools/patch_file.rb".freeze, "lib/ollama_chat/tools/read_file.rb".freeze, "lib/ollama_chat/tools/resolve_tag.rb".freeze, "lib/ollama_chat/tools/roll_dice.rb".freeze, "lib/ollama_chat/tools/run_tests.rb".freeze, "lib/ollama_chat/tools/search_knowledge.rb".freeze, "lib/ollama_chat/tools/search_web.rb".freeze, "lib/ollama_chat/tools/write_file.rb".freeze, "lib/ollama_chat/utils.rb".freeze, "lib/ollama_chat/utils/analyze_directory.rb".freeze, "lib/ollama_chat/utils/backup.rb".freeze, "lib/ollama_chat/utils/cache_fetcher.rb".freeze, "lib/ollama_chat/utils/chooser.rb".freeze, "lib/ollama_chat/utils/fetcher.rb".freeze, "lib/ollama_chat/utils/json_jsonl_io.rb".freeze, "lib/ollama_chat/utils/log_viewer.rb".freeze, "lib/ollama_chat/utils/path_completer.rb".freeze, "lib/ollama_chat/utils/path_validator.rb".freeze, "lib/ollama_chat/utils/png_metadata_extractor.rb".freeze, "lib/ollama_chat/utils/tag_resolver.rb".freeze, "lib/ollama_chat/utils/utf8_converter.rb".freeze, "lib/ollama_chat/utils/value_formatter.rb".freeze, "lib/ollama_chat/uuid_v7.rb".freeze, "lib/ollama_chat/version.rb".freeze, "lib/ollama_chat/vim.rb".freeze, "lib/ollama_chat/web_searching.rb".freeze]
16
- s.files = [".utilsrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_chat_log".freeze, "bin/ollama_chat_send".freeze, "config/searxng/settings.yml".freeze, "config/sherlock.md".freeze, "docker-compose.yml".freeze, "lib/ollama_chat.rb".freeze, "lib/ollama_chat/chat.rb".freeze, "lib/ollama_chat/clipboard.rb".freeze, "lib/ollama_chat/command_concern.rb".freeze, "lib/ollama_chat/commands.rb".freeze, "lib/ollama_chat/config_handling.rb".freeze, "lib/ollama_chat/conversation.rb".freeze, "lib/ollama_chat/database.rb".freeze, "lib/ollama_chat/database/duplicatable.rb".freeze, "lib/ollama_chat/database/migrations.rb".freeze, "lib/ollama_chat/database/migrations/001_initial_schema.rb".freeze, "lib/ollama_chat/database/migrations/002_add_links_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/003_add_history_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/004_add_profile_to_model_options.rb".freeze, "lib/ollama_chat/database/migrations/005_add_context_format_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb".freeze, "lib/ollama_chat/database/migrations/007_add_collections_table.rb".freeze, "lib/ollama_chat/database/models/collection.rb".freeze, "lib/ollama_chat/database/models/favourite.rb".freeze, "lib/ollama_chat/database/models/model_options.rb".freeze, "lib/ollama_chat/database/models/prompt.rb".freeze, "lib/ollama_chat/database/models/session.rb".freeze, "lib/ollama_chat/database/session_locking.rb".freeze, "lib/ollama_chat/dialog.rb".freeze, "lib/ollama_chat/document_cache.rb".freeze, "lib/ollama_chat/favourites_management.rb".freeze, "lib/ollama_chat/file_editing.rb".freeze, "lib/ollama_chat/follow_chat.rb".freeze, "lib/ollama_chat/history.rb".freeze, "lib/ollama_chat/http_handling.rb".freeze, "lib/ollama_chat/information.rb".freeze, "lib/ollama_chat/input_content.rb".freeze, "lib/ollama_chat/kramdown_ansi.rb".freeze, "lib/ollama_chat/links_set.rb".freeze, "lib/ollama_chat/location_handling.rb".freeze, "lib/ollama_chat/logging.rb".freeze, "lib/ollama_chat/message.rb".freeze, "lib/ollama_chat/message_editing.rb".freeze, "lib/ollama_chat/message_format.rb".freeze, "lib/ollama_chat/message_list.rb".freeze, "lib/ollama_chat/message_output.rb".freeze, "lib/ollama_chat/model_handling.rb".freeze, "lib/ollama_chat/oc.rb".freeze, "lib/ollama_chat/ollama_chat_config.rb".freeze, "lib/ollama_chat/ollama_chat_config/default_config.yml".freeze, "lib/ollama_chat/pager.rb".freeze, "lib/ollama_chat/parsing.rb".freeze, "lib/ollama_chat/personae_management.rb".freeze, "lib/ollama_chat/prompt_handling.rb".freeze, "lib/ollama_chat/prompt_management.rb".freeze, "lib/ollama_chat/rag_handling.rb".freeze, "lib/ollama_chat/redis_cache.rb".freeze, "lib/ollama_chat/say.rb".freeze, "lib/ollama_chat/server_socket.rb".freeze, "lib/ollama_chat/session_management.rb".freeze, "lib/ollama_chat/source_fetching.rb".freeze, "lib/ollama_chat/state_selectors.rb".freeze, "lib/ollama_chat/switches.rb".freeze, "lib/ollama_chat/system_prompt_management.rb".freeze, "lib/ollama_chat/think_control.rb".freeze, "lib/ollama_chat/token_estimator.rb".freeze, "lib/ollama_chat/token_estimator/crude.rb".freeze, "lib/ollama_chat/tool_calling.rb".freeze, "lib/ollama_chat/tools.rb".freeze, "lib/ollama_chat/tools/browse.rb".freeze, "lib/ollama_chat/tools/compute_bmi.rb".freeze, "lib/ollama_chat/tools/concern.rb".freeze, "lib/ollama_chat/tools/copy_to_clipboard.rb".freeze, "lib/ollama_chat/tools/delete_file.rb".freeze, "lib/ollama_chat/tools/directory_structure.rb".freeze, "lib/ollama_chat/tools/eval_ruby.rb".freeze, "lib/ollama_chat/tools/execute_grep.rb".freeze, "lib/ollama_chat/tools/execute_ri.rb".freeze, "lib/ollama_chat/tools/file_context.rb".freeze, "lib/ollama_chat/tools/gem_path_lookup.rb".freeze, "lib/ollama_chat/tools/generate_image.rb".freeze, "lib/ollama_chat/tools/generate_password.rb".freeze, "lib/ollama_chat/tools/get_current_weather.rb".freeze, "lib/ollama_chat/tools/get_cve.rb".freeze, "lib/ollama_chat/tools/get_endoflife.rb".freeze, "lib/ollama_chat/tools/get_ghr.rb".freeze, "lib/ollama_chat/tools/get_jira_issue.rb".freeze, "lib/ollama_chat/tools/get_location.rb".freeze, "lib/ollama_chat/tools/get_rfc.rb".freeze, "lib/ollama_chat/tools/get_time.rb".freeze, "lib/ollama_chat/tools/get_url.rb".freeze, "lib/ollama_chat/tools/move_file.rb".freeze, "lib/ollama_chat/tools/open_file_in_editor.rb".freeze, "lib/ollama_chat/tools/paste_from_clipboard.rb".freeze, "lib/ollama_chat/tools/paste_into_editor.rb".freeze, "lib/ollama_chat/tools/patch_file.rb".freeze, "lib/ollama_chat/tools/read_file.rb".freeze, "lib/ollama_chat/tools/resolve_tag.rb".freeze, "lib/ollama_chat/tools/roll_dice.rb".freeze, "lib/ollama_chat/tools/run_tests.rb".freeze, "lib/ollama_chat/tools/search_knowledge.rb".freeze, "lib/ollama_chat/tools/search_web.rb".freeze, "lib/ollama_chat/tools/write_file.rb".freeze, "lib/ollama_chat/utils.rb".freeze, "lib/ollama_chat/utils/analyze_directory.rb".freeze, "lib/ollama_chat/utils/backup.rb".freeze, "lib/ollama_chat/utils/cache_fetcher.rb".freeze, "lib/ollama_chat/utils/chooser.rb".freeze, "lib/ollama_chat/utils/fetcher.rb".freeze, "lib/ollama_chat/utils/json_jsonl_io.rb".freeze, "lib/ollama_chat/utils/log_viewer.rb".freeze, "lib/ollama_chat/utils/path_completer.rb".freeze, "lib/ollama_chat/utils/path_validator.rb".freeze, "lib/ollama_chat/utils/png_metadata_extractor.rb".freeze, "lib/ollama_chat/utils/tag_resolver.rb".freeze, "lib/ollama_chat/utils/utf8_converter.rb".freeze, "lib/ollama_chat/utils/value_formatter.rb".freeze, "lib/ollama_chat/uuid_v7.rb".freeze, "lib/ollama_chat/version.rb".freeze, "lib/ollama_chat/vim.rb".freeze, "lib/ollama_chat/web_searching.rb".freeze, "ollama_chat.gemspec".freeze, "redis/redis.conf".freeze, "spec/assets/api_show.json".freeze, "spec/assets/api_tags.json".freeze, "spec/assets/api_version.json".freeze, "spec/assets/conversation.json".freeze, "spec/assets/conversation.jsonl".freeze, "spec/assets/deep/deeper/empty.txt".freeze, "spec/assets/deep/deeper/not-empty.txt".freeze, "spec/assets/deep/empty.txt".freeze, "spec/assets/duckduckgo.html".freeze, "spec/assets/example.atom".freeze, "spec/assets/example.csv".freeze, "spec/assets/example.epub".freeze, "spec/assets/example.html".freeze, "spec/assets/example.pdf".freeze, "spec/assets/example.ps".freeze, "spec/assets/example.rb".freeze, "spec/assets/example.rss".freeze, "spec/assets/example.xml".freeze, "spec/assets/example_with_quote.html".freeze, "spec/assets/fluffy.png".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/miyu.png".freeze, "spec/assets/pirateweather.json".freeze, "spec/assets/prompt.txt".freeze, "spec/assets/searxng.json".freeze, "spec/ollama_chat/chat_spec.rb".freeze, "spec/ollama_chat/clipboard_spec.rb".freeze, "spec/ollama_chat/commands_spec.rb".freeze, "spec/ollama_chat/database/models/favourite_spec.rb".freeze, "spec/ollama_chat/file_editing_spec.rb".freeze, "spec/ollama_chat/follow_chat_spec.rb".freeze, "spec/ollama_chat/history_spec.rb".freeze, "spec/ollama_chat/information_spec.rb".freeze, "spec/ollama_chat/input_content_spec.rb".freeze, "spec/ollama_chat/kramdown_ansi_spec.rb".freeze, "spec/ollama_chat/message_editing_spec.rb".freeze, "spec/ollama_chat/message_list_spec.rb".freeze, "spec/ollama_chat/message_output_spec.rb".freeze, "spec/ollama_chat/model_handling_spec.rb".freeze, "spec/ollama_chat/parsing_spec.rb".freeze, "spec/ollama_chat/redis_cache_spec.rb".freeze, "spec/ollama_chat/server_socket_spec.rb".freeze, "spec/ollama_chat/session_management_spec.rb".freeze, "spec/ollama_chat/source_fetching_spec.rb".freeze, "spec/ollama_chat/state_selectors_spec.rb".freeze, "spec/ollama_chat/switches_spec.rb".freeze, "spec/ollama_chat/think_control_spec.rb".freeze, "spec/ollama_chat/token_estimator_spec.rb".freeze, "spec/ollama_chat/tool_calling_spec.rb".freeze, "spec/ollama_chat/tools/browse_spec.rb".freeze, "spec/ollama_chat/tools/compute_bmi_spec.rb".freeze, "spec/ollama_chat/tools/copy_to_clipboard_spec.rb".freeze, "spec/ollama_chat/tools/delete_file_spec.rb".freeze, "spec/ollama_chat/tools/directory_structure_spec.rb".freeze, "spec/ollama_chat/tools/execute_grep_spec.rb".freeze, "spec/ollama_chat/tools/execute_ri_spec.rb".freeze, "spec/ollama_chat/tools/file_context_spec.rb".freeze, "spec/ollama_chat/tools/gem_path_lookup_spec.rb".freeze, "spec/ollama_chat/tools/generate_image_spec.rb".freeze, "spec/ollama_chat/tools/generate_password_spec.rb".freeze, "spec/ollama_chat/tools/get_current_weather_spec.rb".freeze, "spec/ollama_chat/tools/get_cve_spec.rb".freeze, "spec/ollama_chat/tools/get_endoflife_spec.rb".freeze, "spec/ollama_chat/tools/get_ghr_spec.rb".freeze, "spec/ollama_chat/tools/get_jira_issue_spec.rb".freeze, "spec/ollama_chat/tools/get_location_spec.rb".freeze, "spec/ollama_chat/tools/get_rfc_spec.rb".freeze, "spec/ollama_chat/tools/get_time_spec.rb".freeze, "spec/ollama_chat/tools/get_url_spec.rb".freeze, "spec/ollama_chat/tools/move_file_spec.rb".freeze, "spec/ollama_chat/tools/open_file_in_editor_spec.rb".freeze, "spec/ollama_chat/tools/paste_from_clipboard_spec.rb".freeze, "spec/ollama_chat/tools/paste_into_editor_spec.rb".freeze, "spec/ollama_chat/tools/patch_file_spec.rb".freeze, "spec/ollama_chat/tools/read_file_spec.rb".freeze, "spec/ollama_chat/tools/resolve_tag_spec.rb".freeze, "spec/ollama_chat/tools/roll_dice_spec.rb".freeze, "spec/ollama_chat/tools/run_tests_spec.rb".freeze, "spec/ollama_chat/tools/search_knowledge_spec.rb".freeze, "spec/ollama_chat/tools/search_web_spec.rb".freeze, "spec/ollama_chat/tools/write_file_spec.rb".freeze, "spec/ollama_chat/utils/analyze_directory_spec.rb".freeze, "spec/ollama_chat/utils/cache_fetcher_spec.rb".freeze, "spec/ollama_chat/utils/fetcher_spec.rb".freeze, "spec/ollama_chat/utils/log_viewer_spec.rb".freeze, "spec/ollama_chat/utils/path_completer_spec.rb".freeze, "spec/ollama_chat/utils/png_metadata_extractor_spec.rb".freeze, "spec/ollama_chat/vim_spec.rb".freeze, "spec/ollama_chat/web_searching_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
15
+ s.extra_rdoc_files = ["README.md".freeze, "lib/ollama_chat.rb".freeze, "lib/ollama_chat/chat.rb".freeze, "lib/ollama_chat/clipboard.rb".freeze, "lib/ollama_chat/command_concern.rb".freeze, "lib/ollama_chat/commands.rb".freeze, "lib/ollama_chat/config_handling.rb".freeze, "lib/ollama_chat/conversation.rb".freeze, "lib/ollama_chat/database.rb".freeze, "lib/ollama_chat/database/duplicatable.rb".freeze, "lib/ollama_chat/database/migrations.rb".freeze, "lib/ollama_chat/database/migrations/001_initial_schema.rb".freeze, "lib/ollama_chat/database/migrations/002_add_links_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/003_add_history_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/004_add_profile_to_model_options.rb".freeze, "lib/ollama_chat/database/migrations/005_add_context_format_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb".freeze, "lib/ollama_chat/database/migrations/007_add_collections_table.rb".freeze, "lib/ollama_chat/database/migrations/008_drop_think_mode_constraint.rb".freeze, "lib/ollama_chat/database/models/collection.rb".freeze, "lib/ollama_chat/database/models/favourite.rb".freeze, "lib/ollama_chat/database/models/model_options.rb".freeze, "lib/ollama_chat/database/models/prompt.rb".freeze, "lib/ollama_chat/database/models/session.rb".freeze, "lib/ollama_chat/database/session_locking.rb".freeze, "lib/ollama_chat/dialog.rb".freeze, "lib/ollama_chat/document_cache.rb".freeze, "lib/ollama_chat/favourites_management.rb".freeze, "lib/ollama_chat/file_editing.rb".freeze, "lib/ollama_chat/follow_chat.rb".freeze, "lib/ollama_chat/history.rb".freeze, "lib/ollama_chat/http_handling.rb".freeze, "lib/ollama_chat/information.rb".freeze, "lib/ollama_chat/input_content.rb".freeze, "lib/ollama_chat/kramdown_ansi.rb".freeze, "lib/ollama_chat/links_set.rb".freeze, "lib/ollama_chat/location_handling.rb".freeze, "lib/ollama_chat/logging.rb".freeze, "lib/ollama_chat/message.rb".freeze, "lib/ollama_chat/message_editing.rb".freeze, "lib/ollama_chat/message_format.rb".freeze, "lib/ollama_chat/message_list.rb".freeze, "lib/ollama_chat/message_output.rb".freeze, "lib/ollama_chat/model_handling.rb".freeze, "lib/ollama_chat/oc.rb".freeze, "lib/ollama_chat/ollama_chat_config.rb".freeze, "lib/ollama_chat/pager.rb".freeze, "lib/ollama_chat/parsing.rb".freeze, "lib/ollama_chat/personae_management.rb".freeze, "lib/ollama_chat/prompt_handling.rb".freeze, "lib/ollama_chat/prompt_management.rb".freeze, "lib/ollama_chat/rag_handling.rb".freeze, "lib/ollama_chat/redis_cache.rb".freeze, "lib/ollama_chat/say.rb".freeze, "lib/ollama_chat/server_socket.rb".freeze, "lib/ollama_chat/session_management.rb".freeze, "lib/ollama_chat/source_fetching.rb".freeze, "lib/ollama_chat/state_selectors.rb".freeze, "lib/ollama_chat/switches.rb".freeze, "lib/ollama_chat/system_prompt_management.rb".freeze, "lib/ollama_chat/think_control.rb".freeze, "lib/ollama_chat/token_estimator.rb".freeze, "lib/ollama_chat/token_estimator/crude.rb".freeze, "lib/ollama_chat/tool_calling.rb".freeze, "lib/ollama_chat/tools.rb".freeze, "lib/ollama_chat/tools/browse.rb".freeze, "lib/ollama_chat/tools/compute_bmi.rb".freeze, "lib/ollama_chat/tools/concern.rb".freeze, "lib/ollama_chat/tools/copy_to_clipboard.rb".freeze, "lib/ollama_chat/tools/delete_file.rb".freeze, "lib/ollama_chat/tools/directory_structure.rb".freeze, "lib/ollama_chat/tools/eval_ruby.rb".freeze, "lib/ollama_chat/tools/execute_grep.rb".freeze, "lib/ollama_chat/tools/execute_ri.rb".freeze, "lib/ollama_chat/tools/file_context.rb".freeze, "lib/ollama_chat/tools/gem_path_lookup.rb".freeze, "lib/ollama_chat/tools/generate_image.rb".freeze, "lib/ollama_chat/tools/generate_password.rb".freeze, "lib/ollama_chat/tools/get_current_weather.rb".freeze, "lib/ollama_chat/tools/get_cve.rb".freeze, "lib/ollama_chat/tools/get_endoflife.rb".freeze, "lib/ollama_chat/tools/get_ghr.rb".freeze, "lib/ollama_chat/tools/get_jira_issue.rb".freeze, "lib/ollama_chat/tools/get_location.rb".freeze, "lib/ollama_chat/tools/get_rfc.rb".freeze, "lib/ollama_chat/tools/get_time.rb".freeze, "lib/ollama_chat/tools/get_url.rb".freeze, "lib/ollama_chat/tools/move_file.rb".freeze, "lib/ollama_chat/tools/open_file_in_editor.rb".freeze, "lib/ollama_chat/tools/paste_from_clipboard.rb".freeze, "lib/ollama_chat/tools/paste_into_editor.rb".freeze, "lib/ollama_chat/tools/patch_file.rb".freeze, "lib/ollama_chat/tools/read_file.rb".freeze, "lib/ollama_chat/tools/resolve_tag.rb".freeze, "lib/ollama_chat/tools/roll_dice.rb".freeze, "lib/ollama_chat/tools/run_tests.rb".freeze, "lib/ollama_chat/tools/search_knowledge.rb".freeze, "lib/ollama_chat/tools/search_web.rb".freeze, "lib/ollama_chat/tools/write_file.rb".freeze, "lib/ollama_chat/utils.rb".freeze, "lib/ollama_chat/utils/analyze_directory.rb".freeze, "lib/ollama_chat/utils/backup.rb".freeze, "lib/ollama_chat/utils/cache_fetcher.rb".freeze, "lib/ollama_chat/utils/chooser.rb".freeze, "lib/ollama_chat/utils/fetcher.rb".freeze, "lib/ollama_chat/utils/json_jsonl_io.rb".freeze, "lib/ollama_chat/utils/log_viewer.rb".freeze, "lib/ollama_chat/utils/path_completer.rb".freeze, "lib/ollama_chat/utils/path_validator.rb".freeze, "lib/ollama_chat/utils/png_metadata_extractor.rb".freeze, "lib/ollama_chat/utils/tag_resolver.rb".freeze, "lib/ollama_chat/utils/utf8_converter.rb".freeze, "lib/ollama_chat/utils/value_formatter.rb".freeze, "lib/ollama_chat/uuid_v7.rb".freeze, "lib/ollama_chat/version.rb".freeze, "lib/ollama_chat/vim.rb".freeze, "lib/ollama_chat/web_searching.rb".freeze]
16
+ s.files = [".utilsrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_chat_log".freeze, "bin/ollama_chat_send".freeze, "config/searxng/settings.yml".freeze, "config/sherlock.md".freeze, "docker-compose.yml".freeze, "lib/ollama_chat.rb".freeze, "lib/ollama_chat/chat.rb".freeze, "lib/ollama_chat/clipboard.rb".freeze, "lib/ollama_chat/command_concern.rb".freeze, "lib/ollama_chat/commands.rb".freeze, "lib/ollama_chat/config_handling.rb".freeze, "lib/ollama_chat/conversation.rb".freeze, "lib/ollama_chat/database.rb".freeze, "lib/ollama_chat/database/duplicatable.rb".freeze, "lib/ollama_chat/database/migrations.rb".freeze, "lib/ollama_chat/database/migrations/001_initial_schema.rb".freeze, "lib/ollama_chat/database/migrations/002_add_links_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/003_add_history_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/004_add_profile_to_model_options.rb".freeze, "lib/ollama_chat/database/migrations/005_add_context_format_to_sessions.rb".freeze, "lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb".freeze, "lib/ollama_chat/database/migrations/007_add_collections_table.rb".freeze, "lib/ollama_chat/database/migrations/008_drop_think_mode_constraint.rb".freeze, "lib/ollama_chat/database/models/collection.rb".freeze, "lib/ollama_chat/database/models/favourite.rb".freeze, "lib/ollama_chat/database/models/model_options.rb".freeze, "lib/ollama_chat/database/models/prompt.rb".freeze, "lib/ollama_chat/database/models/session.rb".freeze, "lib/ollama_chat/database/session_locking.rb".freeze, "lib/ollama_chat/dialog.rb".freeze, "lib/ollama_chat/document_cache.rb".freeze, "lib/ollama_chat/favourites_management.rb".freeze, "lib/ollama_chat/file_editing.rb".freeze, "lib/ollama_chat/follow_chat.rb".freeze, "lib/ollama_chat/history.rb".freeze, "lib/ollama_chat/http_handling.rb".freeze, "lib/ollama_chat/information.rb".freeze, "lib/ollama_chat/input_content.rb".freeze, "lib/ollama_chat/kramdown_ansi.rb".freeze, "lib/ollama_chat/links_set.rb".freeze, "lib/ollama_chat/location_handling.rb".freeze, "lib/ollama_chat/logging.rb".freeze, "lib/ollama_chat/message.rb".freeze, "lib/ollama_chat/message_editing.rb".freeze, "lib/ollama_chat/message_format.rb".freeze, "lib/ollama_chat/message_list.rb".freeze, "lib/ollama_chat/message_output.rb".freeze, "lib/ollama_chat/model_handling.rb".freeze, "lib/ollama_chat/oc.rb".freeze, "lib/ollama_chat/ollama_chat_config.rb".freeze, "lib/ollama_chat/ollama_chat_config/default_config.yml".freeze, "lib/ollama_chat/pager.rb".freeze, "lib/ollama_chat/parsing.rb".freeze, "lib/ollama_chat/personae_management.rb".freeze, "lib/ollama_chat/prompt_handling.rb".freeze, "lib/ollama_chat/prompt_management.rb".freeze, "lib/ollama_chat/rag_handling.rb".freeze, "lib/ollama_chat/redis_cache.rb".freeze, "lib/ollama_chat/say.rb".freeze, "lib/ollama_chat/server_socket.rb".freeze, "lib/ollama_chat/session_management.rb".freeze, "lib/ollama_chat/source_fetching.rb".freeze, "lib/ollama_chat/state_selectors.rb".freeze, "lib/ollama_chat/switches.rb".freeze, "lib/ollama_chat/system_prompt_management.rb".freeze, "lib/ollama_chat/think_control.rb".freeze, "lib/ollama_chat/token_estimator.rb".freeze, "lib/ollama_chat/token_estimator/crude.rb".freeze, "lib/ollama_chat/tool_calling.rb".freeze, "lib/ollama_chat/tools.rb".freeze, "lib/ollama_chat/tools/browse.rb".freeze, "lib/ollama_chat/tools/compute_bmi.rb".freeze, "lib/ollama_chat/tools/concern.rb".freeze, "lib/ollama_chat/tools/copy_to_clipboard.rb".freeze, "lib/ollama_chat/tools/delete_file.rb".freeze, "lib/ollama_chat/tools/directory_structure.rb".freeze, "lib/ollama_chat/tools/eval_ruby.rb".freeze, "lib/ollama_chat/tools/execute_grep.rb".freeze, "lib/ollama_chat/tools/execute_ri.rb".freeze, "lib/ollama_chat/tools/file_context.rb".freeze, "lib/ollama_chat/tools/gem_path_lookup.rb".freeze, "lib/ollama_chat/tools/generate_image.rb".freeze, "lib/ollama_chat/tools/generate_password.rb".freeze, "lib/ollama_chat/tools/get_current_weather.rb".freeze, "lib/ollama_chat/tools/get_cve.rb".freeze, "lib/ollama_chat/tools/get_endoflife.rb".freeze, "lib/ollama_chat/tools/get_ghr.rb".freeze, "lib/ollama_chat/tools/get_jira_issue.rb".freeze, "lib/ollama_chat/tools/get_location.rb".freeze, "lib/ollama_chat/tools/get_rfc.rb".freeze, "lib/ollama_chat/tools/get_time.rb".freeze, "lib/ollama_chat/tools/get_url.rb".freeze, "lib/ollama_chat/tools/move_file.rb".freeze, "lib/ollama_chat/tools/open_file_in_editor.rb".freeze, "lib/ollama_chat/tools/paste_from_clipboard.rb".freeze, "lib/ollama_chat/tools/paste_into_editor.rb".freeze, "lib/ollama_chat/tools/patch_file.rb".freeze, "lib/ollama_chat/tools/read_file.rb".freeze, "lib/ollama_chat/tools/resolve_tag.rb".freeze, "lib/ollama_chat/tools/roll_dice.rb".freeze, "lib/ollama_chat/tools/run_tests.rb".freeze, "lib/ollama_chat/tools/search_knowledge.rb".freeze, "lib/ollama_chat/tools/search_web.rb".freeze, "lib/ollama_chat/tools/write_file.rb".freeze, "lib/ollama_chat/utils.rb".freeze, "lib/ollama_chat/utils/analyze_directory.rb".freeze, "lib/ollama_chat/utils/backup.rb".freeze, "lib/ollama_chat/utils/cache_fetcher.rb".freeze, "lib/ollama_chat/utils/chooser.rb".freeze, "lib/ollama_chat/utils/fetcher.rb".freeze, "lib/ollama_chat/utils/json_jsonl_io.rb".freeze, "lib/ollama_chat/utils/log_viewer.rb".freeze, "lib/ollama_chat/utils/path_completer.rb".freeze, "lib/ollama_chat/utils/path_validator.rb".freeze, "lib/ollama_chat/utils/png_metadata_extractor.rb".freeze, "lib/ollama_chat/utils/tag_resolver.rb".freeze, "lib/ollama_chat/utils/utf8_converter.rb".freeze, "lib/ollama_chat/utils/value_formatter.rb".freeze, "lib/ollama_chat/uuid_v7.rb".freeze, "lib/ollama_chat/version.rb".freeze, "lib/ollama_chat/vim.rb".freeze, "lib/ollama_chat/web_searching.rb".freeze, "ollama_chat.gemspec".freeze, "redis/redis.conf".freeze, "spec/assets/api_show.json".freeze, "spec/assets/api_tags.json".freeze, "spec/assets/api_version.json".freeze, "spec/assets/conversation.json".freeze, "spec/assets/conversation.jsonl".freeze, "spec/assets/deep/deeper/empty.txt".freeze, "spec/assets/deep/deeper/not-empty.txt".freeze, "spec/assets/deep/empty.txt".freeze, "spec/assets/duckduckgo.html".freeze, "spec/assets/example.atom".freeze, "spec/assets/example.csv".freeze, "spec/assets/example.epub".freeze, "spec/assets/example.html".freeze, "spec/assets/example.pdf".freeze, "spec/assets/example.ps".freeze, "spec/assets/example.rb".freeze, "spec/assets/example.rss".freeze, "spec/assets/example.xml".freeze, "spec/assets/example_with_quote.html".freeze, "spec/assets/fluffy.png".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/miyu.png".freeze, "spec/assets/pirateweather.json".freeze, "spec/assets/prompt.txt".freeze, "spec/assets/searxng.json".freeze, "spec/ollama_chat/chat_spec.rb".freeze, "spec/ollama_chat/clipboard_spec.rb".freeze, "spec/ollama_chat/commands_spec.rb".freeze, "spec/ollama_chat/database/models/favourite_spec.rb".freeze, "spec/ollama_chat/file_editing_spec.rb".freeze, "spec/ollama_chat/follow_chat_spec.rb".freeze, "spec/ollama_chat/history_spec.rb".freeze, "spec/ollama_chat/information_spec.rb".freeze, "spec/ollama_chat/input_content_spec.rb".freeze, "spec/ollama_chat/kramdown_ansi_spec.rb".freeze, "spec/ollama_chat/message_editing_spec.rb".freeze, "spec/ollama_chat/message_list_spec.rb".freeze, "spec/ollama_chat/message_output_spec.rb".freeze, "spec/ollama_chat/model_handling_spec.rb".freeze, "spec/ollama_chat/parsing_spec.rb".freeze, "spec/ollama_chat/redis_cache_spec.rb".freeze, "spec/ollama_chat/server_socket_spec.rb".freeze, "spec/ollama_chat/session_management_spec.rb".freeze, "spec/ollama_chat/source_fetching_spec.rb".freeze, "spec/ollama_chat/state_selectors_spec.rb".freeze, "spec/ollama_chat/switches_spec.rb".freeze, "spec/ollama_chat/think_control_spec.rb".freeze, "spec/ollama_chat/token_estimator_spec.rb".freeze, "spec/ollama_chat/tool_calling_spec.rb".freeze, "spec/ollama_chat/tools/browse_spec.rb".freeze, "spec/ollama_chat/tools/compute_bmi_spec.rb".freeze, "spec/ollama_chat/tools/copy_to_clipboard_spec.rb".freeze, "spec/ollama_chat/tools/delete_file_spec.rb".freeze, "spec/ollama_chat/tools/directory_structure_spec.rb".freeze, "spec/ollama_chat/tools/execute_grep_spec.rb".freeze, "spec/ollama_chat/tools/execute_ri_spec.rb".freeze, "spec/ollama_chat/tools/file_context_spec.rb".freeze, "spec/ollama_chat/tools/gem_path_lookup_spec.rb".freeze, "spec/ollama_chat/tools/generate_image_spec.rb".freeze, "spec/ollama_chat/tools/generate_password_spec.rb".freeze, "spec/ollama_chat/tools/get_current_weather_spec.rb".freeze, "spec/ollama_chat/tools/get_cve_spec.rb".freeze, "spec/ollama_chat/tools/get_endoflife_spec.rb".freeze, "spec/ollama_chat/tools/get_ghr_spec.rb".freeze, "spec/ollama_chat/tools/get_jira_issue_spec.rb".freeze, "spec/ollama_chat/tools/get_location_spec.rb".freeze, "spec/ollama_chat/tools/get_rfc_spec.rb".freeze, "spec/ollama_chat/tools/get_time_spec.rb".freeze, "spec/ollama_chat/tools/get_url_spec.rb".freeze, "spec/ollama_chat/tools/move_file_spec.rb".freeze, "spec/ollama_chat/tools/open_file_in_editor_spec.rb".freeze, "spec/ollama_chat/tools/paste_from_clipboard_spec.rb".freeze, "spec/ollama_chat/tools/paste_into_editor_spec.rb".freeze, "spec/ollama_chat/tools/patch_file_spec.rb".freeze, "spec/ollama_chat/tools/read_file_spec.rb".freeze, "spec/ollama_chat/tools/resolve_tag_spec.rb".freeze, "spec/ollama_chat/tools/roll_dice_spec.rb".freeze, "spec/ollama_chat/tools/run_tests_spec.rb".freeze, "spec/ollama_chat/tools/search_knowledge_spec.rb".freeze, "spec/ollama_chat/tools/search_web_spec.rb".freeze, "spec/ollama_chat/tools/write_file_spec.rb".freeze, "spec/ollama_chat/utils/analyze_directory_spec.rb".freeze, "spec/ollama_chat/utils/cache_fetcher_spec.rb".freeze, "spec/ollama_chat/utils/fetcher_spec.rb".freeze, "spec/ollama_chat/utils/log_viewer_spec.rb".freeze, "spec/ollama_chat/utils/path_completer_spec.rb".freeze, "spec/ollama_chat/utils/png_metadata_extractor_spec.rb".freeze, "spec/ollama_chat/vim_spec.rb".freeze, "spec/ollama_chat/web_searching_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
17
17
  s.homepage = "https://github.com/flori/ollama_chat".freeze
18
18
  s.licenses = ["MIT".freeze]
19
19
  s.rdoc_options = ["--title".freeze, "OllamaChat - A command-line interface (CLI) for interacting with an Ollama AI model.".freeze, "--main".freeze, "README.md".freeze]
@@ -100,4 +100,28 @@ describe OllamaChat::Chat, protect_env: true do
100
100
  expect(chat.version).to eq 0
101
101
  end
102
102
  end
103
+
104
+ describe '#initial_collection' do
105
+ connect_to_ollama_server(instantiate: false)
106
+
107
+ let(:session) { double('Session') }
108
+ let(:config) { double('Config') }
109
+
110
+ before do
111
+ expect(chat).to receive(:session).and_return(session)
112
+ expect(chat).to receive(:config).and_return(config)
113
+ end
114
+
115
+ it 'returns :default for invalid collection names' do
116
+ expect(session).to receive(:current_collection).and_return(nil)
117
+ expect(config).to receive_message_chain(:embedding, :collection).and_return('invalid/collection')
118
+ expect(chat.initial_collection).to eq(:default)
119
+ end
120
+
121
+ it 'returns the collection name for valid names' do
122
+ expect(session).to receive(:current_collection).and_return(nil)
123
+ expect(config).to receive_message_chain(:embedding, :collection).and_return('my.valid-collection')
124
+ expect(chat.initial_collection).to eq('my.valid-collection'.to_sym)
125
+ end
126
+ end
103
127
  end
@@ -37,8 +37,7 @@ describe OllamaChat::FollowChat do
37
37
  message = OllamaChat::Message.new(role: 'assistant', content: 'world')
38
38
  response = double(message:, done: false)
39
39
  expect(output).to receive(:print).with(
40
- "\e[2J", "\e[1;1H", "📨 \e[1m\e[38;5;111massistant\e[0m\e[0m:", "\n",
41
- "world"
40
+ "\e[1;1H", "\e[J", "📨 \e[1m\e[38;5;111massistant\e[0m\e[0m:\nworld"
42
41
  )
43
42
  follow_chat.call(response)
44
43
  response = double(
@@ -226,7 +226,7 @@ describe OllamaChat::SessionManagement do
226
226
  describe '#rename_session' do
227
227
  it 'updates the session name when provided a valid new name' do
228
228
  expect(chat).to receive(:ask?).and_return('renamed_session')
229
- expect(chat).to receive(:switch_history).and_yield
229
+ expect(chat).to receive(:switch_history).with(:session_name).and_yield
230
230
  expect { chat.rename_session }.to change { chat.session.name }.to('renamed_session')
231
231
  end
232
232
  end
@@ -132,6 +132,29 @@ describe OllamaChat::Tools::SearchKnowledge do
132
132
  tool.execute(tool_call, chat:)
133
133
  end
134
134
 
135
+ it 'raises an error for invalid collection names' do
136
+ tool_call = double(
137
+ 'ToolCall',
138
+ function: double(
139
+ name: 'search_knowledge',
140
+ arguments: double(
141
+ query: 'test',
142
+ tags: nil,
143
+ collection: 'invalid/collection',
144
+ min_similarity: nil,
145
+ text_size: nil,
146
+ text_count: nil,
147
+ rerank: false,
148
+ )
149
+ )
150
+ )
151
+
152
+ result = described_class.new.execute(tool_call, chat:)
153
+ json = json_object(result)
154
+ expect(json.error).to eq('OllamaChat::ToolFunctionArgumentError')
155
+ expect(json.message).to match(/Invalid collection name/)
156
+ end
157
+
135
158
  it 'returns an error when query is empty' do
136
159
  tool_call = double(
137
160
  'ToolCall',
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ollama_chat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.107
4
+ version: 0.0.108
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -496,6 +496,7 @@ extra_rdoc_files:
496
496
  - lib/ollama_chat/database/migrations/005_add_context_format_to_sessions.rb
497
497
  - lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb
498
498
  - lib/ollama_chat/database/migrations/007_add_collections_table.rb
499
+ - lib/ollama_chat/database/migrations/008_drop_think_mode_constraint.rb
499
500
  - lib/ollama_chat/database/models/collection.rb
500
501
  - lib/ollama_chat/database/models/favourite.rb
501
502
  - lib/ollama_chat/database/models/model_options.rb
@@ -624,6 +625,7 @@ files:
624
625
  - lib/ollama_chat/database/migrations/005_add_context_format_to_sessions.rb
625
626
  - lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb
626
627
  - lib/ollama_chat/database/migrations/007_add_collections_table.rb
628
+ - lib/ollama_chat/database/migrations/008_drop_think_mode_constraint.rb
627
629
  - lib/ollama_chat/database/models/collection.rb
628
630
  - lib/ollama_chat/database/models/favourite.rb
629
631
  - lib/ollama_chat/database/models/model_options.rb