ollama_chat 0.0.104 → 0.0.105

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: 3c49eaf9f13a4ed9059d931dca9fdde0c634280496426e0339c0e117de7b8340
4
- data.tar.gz: 5bdcaa702ae84492475c6acfb3bdf9cd559aae3ac3c39ae535bd3aa6312fed03
3
+ metadata.gz: cbf0c7c0df08fa3ef8a15a9c62b7e092d035924c071302a4f37e01776dec2504
4
+ data.tar.gz: '0996355cc3d0324936394a174aaeeee8b67b950d5306cf320fa22a70eef9ea0d'
5
5
  SHA512:
6
- metadata.gz: ec2836dc21c758dd687734e921a8aadd85ca42bb2d827d6f160a690f5870294276f8dd7421699421de7231e71c697e3eace26e31606f6496aeaab8c367c32049
7
- data.tar.gz: 1cc7c1bbd4dc27b28dbc5296bcd4bf7084bc0f493e2a3826cbf2623f9bd3749637729a182d8d89cc2b34c5fac71f7c80b40510f5022d9d64a8b707c6199a1398
6
+ metadata.gz: 879adc039769558c938911909dbfdfc9625d6b4774cd398d8b39b4ebeb7055d7f7eb1fdaa1c26caf79d7fab3330c57c95b1169927e34d6909518e5f30f1c9d74
7
+ data.tar.gz: 7fbe2ad8b0f887cab6d83ece7efe118fd0189e1957fdbd5a40725200729b7e4d93e9c018c1a41cc8fc933dd2cee453a3a7b9090a7e647422314175d567e5496c
data/CHANGES.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-08-04 v0.0.105
4
+
5
+ ### New Features
6
+
7
+ - **Collection Management**: Introduced the
8
+ `OllamaChat::Database::Models::Collection` model and a corresponding `Sequel`
9
+ migration for persistent storage. Added interactive CLI commands under
10
+ `/collection` including `new`, `edit`, `delete`, and `update all`.
11
+ - **Model Profile Copying**: Added an `options copy` subcommand to allow users
12
+ to interactively copy configuration profiles between models, including
13
+ implementation of `copy_model_options_profile`.
14
+ - **Interactive Prompt Contexts**: Implemented `choose_prompt_context` in
15
+ `prompt_management.rb`, allowing the `/prompt` command to dynamically query
16
+ and select contexts via a new `-c ?` flag.
17
+
18
+ ### Enhancements
19
+
20
+ - **Model Options Matching**: Improved profile matching by replacing default
21
+ overwrite prompts with comprehensive checks against all saved `ModelOptions`
22
+ profiles using `ask_and_send(:symbolize_keys_recursive)`. Added pager display
23
+ for available profiles via `use_pager` and the ability to switch profiles via
24
+ `choose_profile_for_model`.
25
+ - **Collection Synchronization**: Enhanced `list_collections` with descriptions
26
+ and paging, and implemented a `Collection.sync` method to backfill database
27
+ records during startup.
28
+ - **Command Refactoring**: Renamed `show_session` to `info_session` within the
29
+ `Commands` module for increased verbosity when no subcommand is provided.
30
+
31
+ ### Bug Fixes & Maintenance
32
+
33
+ - **Documentation**:
34
+ - Clarified that the `checksum` for the `patch_file` tool must be an 8 hex
35
+ digit long CRC32.
36
+ - Added YARD `@!attribute` documentation for `links` and `history` in the
37
+ `Session` model.
38
+ - **Code Cleanup**:
39
+ - Replaced `.exists?` and `.first` dataset checks with `.present?` in
40
+ `model_handling.rb` and `session_management.rb`.
41
+ - Removed deprecated CLI flags `-c`, `-C`, `-D`, `-M`, and `-E` in favor of
42
+ interactive management.
43
+ - Added an `after_destroy` hook to the `Collection` model to purge
44
+ corresponding `Documentrix` vector data.
45
+
3
46
  ## 2026-07-31 v0.0.104
4
47
 
5
48
  ### Changed
data/README.md CHANGED
@@ -83,11 +83,6 @@ Usage: ollama_chat [OPTIONS]
83
83
  -n create a new session
84
84
  -u URL the ollama base url, OLLAMA_URL
85
85
  -m MODEL the ollama model to chat with, OLLAMA_CHAT_MODEL, ?selector
86
- -c CHAT a saved chat conversation to load
87
- -C COLLECTION name of the collection used in this conversation
88
- -D DOCUMENT load document and add to embeddings collection (multiple)
89
- -M use (empty) MemoryCache for this chat session
90
- -E disable embeddings for this chat session
91
86
  -S open a socket to receive input from ollama_chat_send
92
87
  -V display the current version number and quit
93
88
  -h this help
@@ -101,7 +101,7 @@ class OllamaChat::Chat
101
101
  # @raise [RuntimeError] If the Ollama API version is less than 0.9.0, indicating
102
102
  # incompatibility with required API features
103
103
  def initialize(argv: ARGV.dup)
104
- @opts = go 'f:u:m:c:C:D:l:nMESVh', argv
104
+ @opts = go 'f:u:m:l:nSVh', argv
105
105
  @opts[?h] and exit usage
106
106
  @opts[?V] and exit version
107
107
  @ollama_chat_config = OllamaChat::OllamaChatConfig.new(@opts[?f])
@@ -112,14 +112,8 @@ class OllamaChat::Chat
112
112
  setup_switches
113
113
  setup_state_selectors(config)
114
114
  connect_ollama
115
- if conversation_file = @opts[?c]
116
- messages.load_conversation(conversation_file)
117
- else
118
- messages.read_conversation_jsonl(session.messages.to_s)
119
- end
120
- repair_group_uuids
121
- embedding_enabled.set(config.embedding.enabled && !@opts[?E])
122
- @documents = setup_documents
115
+ @documents = setup_documents
116
+ models::Collection.sync(self)
123
117
  @cache = setup_cache
124
118
  @images = []
125
119
  @kramdown_ansi_styles = configure_kramdown_ansi_styles
@@ -221,7 +215,6 @@ class OllamaChat::Chat
221
215
  # @return [ Symbol ] the collection name symbol
222
216
  def initial_collection
223
217
  (
224
- @opts[?C] ||
225
218
  session&.current_collection.full? ||
226
219
  config.embedding.collection.full? ||
227
220
  :default
@@ -639,10 +632,6 @@ class OllamaChat::Chat
639
632
  redis_url: config.redis.documents.url?,
640
633
  debug:
641
634
  )
642
-
643
- document_list = @opts[?D].to_a
644
- add_documents_from_argv(document_list)
645
- @documents
646
635
  else
647
636
  NULL
648
637
  end
@@ -139,12 +139,13 @@ module OllamaChat::Commands
139
139
 
140
140
  command(
141
141
  name: :model,
142
- regexp: %r(^/model(?:\s+(change|options|options from session|options to session))?((?:\s+(?:-m))*)$),
143
- complete: [ 'model', %w[ change options options\ from\ session options\ to\ session ] ],
142
+ regexp: %r(^/model(?:\s+(change|options(?: copy)?|options from session|options to session))?((?:\s+(?:-m))*)$),
143
+ complete: [ 'model', %w[ change options options\ copy options\ from\ session options\ to\ session ] ],
144
144
  help: <<~EOT
145
145
  🤖 Manage AI models & profiles:
146
146
  - change: Switch active model
147
147
  - options: Edit saved profile config
148
+ - options copy: Copy profile from another model
148
149
  - options from session: Save live → Saved
149
150
  - options to session: Apply Saved → Live
150
151
  -m interactively choose a model
@@ -167,6 +168,8 @@ module OllamaChat::Commands
167
168
  when 'options'
168
169
  profile = choose_profile_for_model(model, allow_new: true) or next :next
169
170
  edit_model_options(model, profile:)
171
+ when 'options copy'
172
+ copy_model_options_profile(model)
170
173
  when 'options from session'
171
174
  profile = choose_profile_for_model(model) || 'default'
172
175
  copy_model_options_from_session(model, profile:)
@@ -276,7 +279,7 @@ module OllamaChat::Commands
276
279
  ) do |subcommand, opts, name|
277
280
  case subcommand
278
281
  when nil
279
- show_session
282
+ info_session
280
283
  when 'list'
281
284
  list_sessions
282
285
  when 'new'
@@ -457,7 +460,7 @@ module OllamaChat::Commands
457
460
 
458
461
  command(
459
462
  name: :prompt,
460
- regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|-e))?(\s+(?:-[ef]|-c\s+\w+))?(?:\s+([^-].*))?$),
463
+ regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|-e))?(\s+(?:-[ef]|-c\s+(?:\w+|\?)))?(?:\s+([^-].*))?$),
461
464
  complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset ] ],
462
465
  optional: true,
463
466
  options: '[-c CONTEXT|-e|-f]',
@@ -465,42 +468,61 @@ module OllamaChat::Commands
465
468
  📝 Manage prompt templates:
466
469
  Subcommands: edit, info, add, delete, list,
467
470
  duplicate, import, export, reset.
468
- Options: -c [context], -e (edit next)
471
+ Options: -c [context]
472
+ (? for interactive in /prompt),
473
+ -e (edit next)
469
474
  EOT
470
475
  ) do |subcommand, opts, filename|
471
476
  opts = go_command('fc:', opts)
477
+ context = if subcommand.nil? || subcommand == '-e'
478
+ if opts[?c] == ??
479
+ choose_prompt_context
480
+ else
481
+ opts[?c] || 'prompt'
482
+ end
483
+ else
484
+ opts[?c] || choose_prompt_context
485
+ end
486
+ unless context
487
+ STDOUT.puts 'Cancelled.'
488
+ next :next
489
+ end
490
+
472
491
  case subcommand
473
492
  when 'add'
474
- add_new_prompt(context: opts[?c])
493
+ add_new_prompt(context:)
475
494
  when 'delete'
476
- choose_and_delete_prompt(context: opts[?c], force: opts[?f])
495
+ choose_and_delete_prompt(context:, force: opts[?f])
477
496
  when 'edit'
478
- choose_and_edit_prompt(context: opts[?c])
497
+ choose_and_edit_prompt(context:)
479
498
  when 'list'
480
- list_prompts(context: opts[?c])
499
+ list_prompts(context:)
481
500
  when 'duplicate'
482
- duplicate_prompt(context: opts[?c])
501
+ duplicate_prompt(context:)
483
502
  when 'import'
484
- import_prompt(filename, context: opts[?c])
503
+ import_prompt(filename, context:)
485
504
  when 'export'
486
- export_prompt(context: opts[?c])
505
+ export_prompt(context:)
487
506
  when 'info'
488
- info_prompt(context: opts[?c])
507
+ info_prompt(context:)
489
508
  when 'reset'
490
509
  if prompt = choose_prompt(
491
510
  default: true,
492
- context: opts[?c],
511
+ context:,
493
512
  prompt: 'Which prompt needs to be restored to its origin? %s'
494
513
  )
495
514
  then
496
- if reset_prompt_to_default(prompt.name, context: opts[?c])
515
+ if reset_prompt_to_default(prompt.name, context:)
497
516
  STDOUT.puts "Reset prompt #{bold{prompt.name}} to default."
498
517
  else
499
518
  STDOUT.puts "No default value found for prompt #{bold{prompt.name}}."
500
519
  end
501
520
  end
502
521
  when nil, '-e'
503
- if prompt = choose_prompt(prompt: 'Which template shall guide the next response? %s').full?(&:to_s)
522
+ if prompt = choose_prompt(
523
+ prompt: 'Which template shall guide the next response? %s',
524
+ context:
525
+ ).full?(&:to_s)
504
526
  if subcommand
505
527
  prompt = edit_text(prompt)
506
528
  next prompt
@@ -582,13 +604,17 @@ module OllamaChat::Commands
582
604
 
583
605
  command(
584
606
  name: :collection,
585
- regexp: %r(^/collection(?:\s+(change|clear|list|rename|update))?$),
586
- complete: [ 'collection', %w[ change clear list rename update ] ],
607
+ regexp: %r(^/collection(?:\s+(change|clear|list|rename|update(?: all)?|new|edit|delete))?$),
608
+ complete: [ 'collection', %w[ change clear list rename update update\ all new edit delete ] ],
587
609
  optional: true,
588
610
  help: <<~EOT
589
611
  📚 Manage RAG collections:
590
612
  - change/clear/list/rename
591
613
  - update: Re-index modified docs
614
+ - update all: Re-index all collections
615
+ - new: Interactively create a new collection
616
+ - edit: Interactively update an existing collection
617
+ - delete: Permanently remove a collection
592
618
  - (no subcommand): Show stats
593
619
  EOT
594
620
  ) do |subcommand|
@@ -601,11 +627,25 @@ module OllamaChat::Commands
601
627
  list_collections
602
628
  when 'rename'
603
629
  rename_collection(collection)
630
+ when 'update all'
631
+ results = ''
632
+ all_collections.pluck(:name).each do |collection|
633
+ STDOUT.puts "📝 Updating collection #{collection.inspect}… "
634
+ results << update_collection(collection) << ?\n
635
+ STDOUT.puts "✅ Done."
636
+ end
637
+ results.full? and next results
604
638
  when 'update'
605
- if results = update_collection
639
+ if results = update_collection(collection)
606
640
  disable_content_parsing
607
641
  next results
608
642
  end
643
+ when 'new'
644
+ create_collection
645
+ when 'edit'
646
+ edit_collection
647
+ when 'delete'
648
+ delete_collection
609
649
  when nil
610
650
  collection_stats
611
651
  end
@@ -0,0 +1,12 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table :collections do
4
+ primary_key :id
5
+ String :name, null: false, unique: true
6
+ Text :description, null: false
7
+ Text :patterns # JSON-encoded array of globs
8
+ DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP, null: false
9
+ DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP, null: false
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,81 @@
1
+ # Represents a document collection stored in the database, allowing for
2
+ # dynamic management of documentrix collections.
3
+ #
4
+ # This model stores collections with a name, description, and patterns
5
+ # for automatic file discovery and synchronization.
6
+ class OllamaChat::Database::Models::Collection < Sequel::Model(OllamaChat::DB)
7
+ include OllamaChat::Database::Duplicatable
8
+
9
+ plugin :timestamps, update_on_create: true
10
+ plugin :serialization, :json, :patterns
11
+ plugin :validation_helpers
12
+
13
+ # Validates the collection.
14
+ #
15
+ # Ensures that the `name` is present and unique.
16
+ def validate
17
+ super
18
+ validates_presence :name
19
+ validates_unique :name
20
+ validates_presence :description
21
+ end
22
+
23
+ # Sets the chat instance associated with this collection.
24
+ #
25
+ # This allows the collection to interact with the chat's document
26
+ # store during lifecycle events, such as cleanup upon destruction.
27
+ #
28
+ # @param chat [OllamaChat::Chat] the chat instance
29
+ attr_writer :chat
30
+
31
+ # Hook to clean up the corresponding Documentrix collection when
32
+ # the database record is destroyed.
33
+ #
34
+ # This ensures that we don't leave orphaned vector data in the
35
+ # Documentrix engine when the underlying collection record is removed.
36
+ def after_destroy
37
+ super
38
+ @chat or return
39
+ @chat.switch_collection(name) do
40
+ @chat.documents.clear
41
+ end
42
+ true
43
+ end
44
+
45
+ # Synchronizes the database records with the Documentrix collections.
46
+ #
47
+ # This method iterates through the configured collections in the chat's
48
+ # document store and ensures that every collection has a corresponding
49
+ # record in the database. If a collection exists in Documentrix but not
50
+ # in the database, a new record is created with a default description.
51
+ #
52
+ # @param chat [OllamaChat::Chat] the chat instance providing the configuration
53
+ def self.sync(chat)
54
+ chat.documents.collections.each do |name|
55
+ where(name: name.to_s).first and next
56
+ create(
57
+ name: name.to_s,
58
+ description: "TODO: Describe collection #{name}",
59
+ patterns: []
60
+ )
61
+ end
62
+ end
63
+
64
+ # @!attribute [v] id
65
+ # @return [Integer] The primary key for the collection entry.
66
+ #
67
+ # @!attribute [v] name
68
+ # @return [String] The name of the collection (matches documentrix name).
69
+ #
70
+ # @!attribute [v] description
71
+ # @return [String, nil] A description of the collection.
72
+ #
73
+ # @!attribute [v] patterns
74
+ # @return [Array, nil] An array of glob patterns for file discovery.
75
+ #
76
+ # @!attribute [v] created_at
77
+ # @return [Time, nil] The timestamp when the collection was created.
78
+ #
79
+ # @!attribute [v] updated_at
80
+ # @return [Time, nil] The timestamp of the last update to the collection.
81
+ end
@@ -52,7 +52,7 @@ class OllamaChat::Database::Models::Prompt < Sequel::Model(OllamaChat::DB)
52
52
  # database when the underlying prompt is removed.
53
53
  def after_destroy
54
54
  super
55
- OllamaChat::Database::Models::Favourite.where(context:, name:).destroy
55
+ models::Favourite.where(context:, name:).destroy
56
56
  end
57
57
 
58
58
  # Seeds the prompt table from the provided chat configuration.
@@ -137,6 +137,12 @@ class OllamaChat::Database::Models::Session < Sequel::Model(OllamaChat::DB)
137
137
  # @!attribute [v] messages
138
138
  # @return [String] The full conversation history, stored in JSONL format.
139
139
  #
140
+ # @!attribute [v] links
141
+ # @return [String] A text field storing links associated with the session.
142
+ #
143
+ # @!attribute [v] history
144
+ # @return [String] A text field storing the history for the session.
145
+ #
140
146
  # @!attribute [v] created_at
141
147
  # @return [Time, nil] The timestamp when the session was created.
142
148
  #
@@ -72,6 +72,7 @@ module OllamaChat::Information
72
72
  #
73
73
  # @param output [IO] the output stream to write the message to
74
74
  def collection_stats(output: STDOUT)
75
+ col = database_collection?(collection)
75
76
  length = (Tins::Terminal.cols - 10).clamp(0..)
76
77
  wrapped_tags = Kramdown::ANSI::Width.
77
78
  wrap(@documents.tags.to_a.join(', '), length:).
@@ -79,6 +80,7 @@ module OllamaChat::Information
79
80
  output.puts <<~EOT
80
81
  Current Collection
81
82
  Name: #{bold{collection}}
83
+ Patterns: #{italic{col&.patterns&.join(' ')}}
82
84
  #Embeddings: #{@documents.size}
83
85
  #Tags: #{@documents.tags.size}
84
86
  Tags:
@@ -235,11 +237,6 @@ module OllamaChat::Information
235
237
  -n create a new session
236
238
  -u URL the ollama base url, OLLAMA_URL
237
239
  -m MODEL the ollama chat model, OLLAMA_CHAT_MODEL, ?selector
238
- -c CHAT a saved chat conversation to load
239
- -C COLLECTION name of the collection used in this conversation
240
- -D DOCUMENT load document and add to embeddings collection (multiple)
241
- -M use (empty) MemoryCache for this chat session
242
- -E disable embeddings for this chat session
243
240
  -S open a socket to receive input from ollama_chat_send
244
241
  -V display the current version number and quit
245
242
  -h this help
@@ -297,6 +294,21 @@ module OllamaChat::Information
297
294
  config.infobar.message.to_h
298
295
  end
299
296
 
297
+ # Retrieves a hash of collection names and their descriptions from the
298
+ # database.
299
+ #
300
+ # This is used to provide context to the AI model about available RAG
301
+ # collections.
302
+ #
303
+ # @return [Hash{String => String}] a hash mapping collection names to their
304
+ # descriptions
305
+ def collection_descriptions
306
+ cols = models::Collection.select(:name, :description).order(:name)
307
+ cols.each_with_object({}) do |c, hash|
308
+ hash[c.name] = c.description
309
+ end
310
+ end
311
+
300
312
  # Generates a hash containing static runtime information.
301
313
  #
302
314
  # This method collects session-level constants including the user,
@@ -307,7 +319,7 @@ module OllamaChat::Information
307
319
  def static_runtime_information_values
308
320
  {
309
321
  client: ,
310
- collections: JSON.pretty_generate(config.embedding.collection_descriptions?),
322
+ collections: JSON.pretty_generate(collection_descriptions),
311
323
  current_directory: Pathname.pwd.expand_path.to_path,
312
324
  languages: config.languages * ', ',
313
325
  location: location.on?.full? { location_description } || 'n/a',
@@ -172,6 +172,37 @@ module OllamaChat::ModelHandling
172
172
  STDOUT.puts "Model options #{italic{profile}} of #{bold{model_name}} were copied to session model options."
173
173
  end
174
174
 
175
+ # Interactively copies model options from a source model/profile to the
176
+ # current model/profile. Displays a side-by-side comparison if the
177
+ # destination profile already exists and prompts for override confirmation.
178
+ #
179
+ # @param model [String] the destination model for the copied options
180
+ def copy_model_options_profile(model)
181
+ src_model = choose_model('', model)
182
+ src_profile = choose_profile_for_model(src_model) || 'default'
183
+ dst_model = model
184
+ dst_profile = choose_profile_for_model(dst_model, allow_new: true) || src_profile
185
+
186
+ src_opts = get_stored_model_options(src_model, profile: src_profile).full? or return
187
+
188
+ dst_opts = get_stored_model_options(dst_model, profile: dst_profile)
189
+ if dst_opts.present?
190
+ STDOUT.puts "Profile #{italic{dst_profile}} already exists for #{bold{dst_model}}."
191
+ STDOUT.puts "\n📥 Source (#{src_model}/#{src_profile}):"
192
+ STDOUT.puts JSON.pretty_generate(src_opts)
193
+ STDOUT.puts "\n📤 Destination (#{dst_model}/#{dst_profile}):"
194
+ STDOUT.puts JSON.pretty_generate(dst_opts)
195
+
196
+ unless confirm?(prompt: "⚠️ Override existing profile? (y/n) ", yes: /\Ay/i)
197
+ STDOUT.puts "Cancelled."
198
+ return
199
+ end
200
+ end
201
+
202
+ store_model_options(dst_model, src_opts, profile: dst_profile)
203
+ STDOUT.puts "✅ Copied options from #{italic{src_model}}/#{italic{src_profile}} to #{bold{dst_model}}/#{italic{dst_profile}}."
204
+ end
205
+
175
206
  # Presents an interactive list of stored configuration profiles for the
176
207
  # specified model and prompts the user to select one.
177
208
  #
@@ -196,8 +227,8 @@ module OllamaChat::ModelHandling
196
227
  STDOUT.puts "Cancelled."
197
228
  return
198
229
  when '[NEW]'
199
- name = ask("Enter new profile name: ") or return
200
- if models::ModelOptions.where(model_name:, profile: name).exists?
230
+ name = ask?(prompt: 'Enter new profile name: ') or return
231
+ if models::ModelOptions.where(model_name:, profile: name).present?
201
232
  STDERR.puts "Profile #{name.inspect} already exists!"
202
233
  return
203
234
  end
@@ -320,16 +351,32 @@ module OllamaChat::ModelHandling
320
351
  session.update(model_options: default_model_options)
321
352
  end
322
353
  elsif !keep_options && session_model_options != stored_model_options
323
- STDOUT.puts <<~EOT
324
- ⚠️ Session model options differ from defaults for model #@model!
325
- Session model options:
326
- #{JSON.pretty_generate(session_model_options)}
327
- Default model options:
328
- #{JSON.pretty_generate(stored_model_options)}
329
- EOT
330
- confirm?(
331
- prompt: "❓ Overwrite session model options with defaults? (y/n) ", yes: /\Ay/i
332
- ) and session.update(model_options: stored_model_options)
354
+ all_profiles = models::ModelOptions.where(model_name: @model).
355
+ order(:profile).all
356
+
357
+ matching = all_profiles.any? do |mo|
358
+ mo.options.ask_and_send(:symbolize_keys_recursive) == session_model_options
359
+ end
360
+
361
+ unless matching
362
+ use_pager do |output|
363
+ output.puts "⚠️ Session model options differ from current profile (#{italic{profile}}) and don't match any saved profile!"
364
+ output.puts "\n📋 Available profiles for #{bold{@model}}:"
365
+ all_profiles.each do |mo|
366
+ output.puts "\n📄 #{italic{mo.profile}}:"
367
+ output.puts JSON.pretty_generate(mo.options.ask_and_send(:symbolize_keys_recursive))
368
+ end
369
+ end
370
+
371
+ if confirm?(prompt: "\n❓ Switch to an existing profile? (y/n) ", yes: /\Ay/i)
372
+ chosen = choose_profile_for_model(@model)
373
+ if chosen
374
+ new_opts = get_stored_model_options(@model, profile: chosen)
375
+ session.update(model_options: new_opts)
376
+ STDOUT.puts "Switched to profile #{italic{chosen}}."
377
+ end
378
+ end
379
+ end
333
380
  end
334
381
  end
335
382
 
@@ -21,6 +21,27 @@ module OllamaChat::PromptManagement
21
21
  end
22
22
  end
23
23
 
24
+ # The choose_prompt_context method presents a menu of available prompt
25
+ # contexts for selection. It allows the user to choose between 'prompt',
26
+ # 'system', and 'suggest' contexts.
27
+ #
28
+ # @return [String, nil] the selected context name, or nil if the user
29
+ # cancels the selection.
30
+ def choose_prompt_context
31
+ contexts = models::Prompt.group(:context).order(:context).pluck(:context)
32
+ contexts.unshift('[EXIT]')
33
+ case chosen = choose_entry(
34
+ contexts,
35
+ prompt: '📝 Which prompt context shall we work in? %s'
36
+ )
37
+ when '[EXIT]', nil
38
+ STDOUT.puts "Exiting chooser."
39
+ return
40
+ else
41
+ chosen
42
+ end
43
+ end
44
+
24
45
  # The choose_prompt method presents a menu of available prompts for
25
46
  # selection. It retrieves the list of prompt names from the database, adds an
26
47
  # '[EXIT]' option, and displays them via the Chooser utility.
@@ -5,15 +5,6 @@
5
5
  # changing collections, listing collections, and renaming collections within
6
6
  # the RAG system.
7
7
  module OllamaChat::RAGHandling
8
- private
9
-
10
- # Returns the name of the currently active document collection.
11
- #
12
- # @return [String, Symbol] the name of the current collection
13
- def collection
14
- @documents.collection
15
- end
16
-
17
8
  # Temporarily switches the RAG collection to the specified collection.
18
9
  #
19
10
  # The current collection is stored and restored after the block is executed,
@@ -32,6 +23,24 @@ module OllamaChat::RAGHandling
32
23
  @documents.collection = old_collection
33
24
  end
34
25
 
26
+ private
27
+
28
+ # Looks up a collection in the database by name.
29
+ #
30
+ # @param collection [String, Symbol, #to_s] the collection name to look up
31
+ # @return [OllamaChat::Database::Models::Collection, nil] the found
32
+ # collection, or `nil` if not present in the database
33
+ def database_collection?(collection)
34
+ models::Collection[name: collection.to_s]
35
+ end
36
+
37
+ # Returns the name of the currently active document collection.
38
+ #
39
+ # @return [String, Symbol] the name of the current collection
40
+ def collection
41
+ @documents.collection
42
+ end
43
+
35
44
  # Clears documents from the collection through an interactive user interface.
36
45
  #
37
46
  # This method allows users to selectively clear documents by choosing
@@ -82,18 +91,18 @@ module OllamaChat::RAGHandling
82
91
  #
83
92
  # @param current_collection [ String, nil ] the name of the currently active collection
84
93
  def choose_collection(current_collection)
85
- collections = [ current_collection ] + @documents.collections.to_a
94
+ collections = [ current_collection ] + all_collections.pluck(:name)
86
95
  collections = collections.filter_map(&:to_s).uniq.sort
87
96
  collections.unshift('[EXIT]').unshift('[NEW]')
88
97
  collection = choose_entry(
89
98
  collections,
90
99
  prompt: 'Which archive of knowledge shall we delve into? %s'
91
100
  ) || current_collection
92
- case collection&.to_s
101
+ case collection = collection&.to_s
93
102
  when '[NEW]'
94
- @documents.collection = ask?(
95
- prompt: "❓ Enter name of the new collection: "
96
- )
103
+ if name = create_collection
104
+ @documents.collection = name
105
+ end
97
106
  when nil, '[EXIT]'
98
107
  STDOUT.puts "Exiting chooser."
99
108
  when /./
@@ -118,13 +127,20 @@ module OllamaChat::RAGHandling
118
127
  def rename_collection(current_collection)
119
128
  switch_history(:rename_collection) do
120
129
  prompt = 'Rename collection %s to: ' % current_collection
121
- if new_collection = ask?(prompt:, prefill: current_collection).full?(:to_sym)
130
+ new_collection = switch_history :collection do
131
+ ask?(prompt:, prefill: current_collection).full?(:to_sym)
132
+ end
133
+ if new_collection
122
134
  begin
123
135
  @documents.rename_collection(new_collection)
136
+ col = database_collection?(current_collection)
137
+ col&.update(name: new_collection.to_s)
124
138
  log(:info, "Collection renamed", data: { old_name: current_collection, new_name: new_collection })
125
139
  STDOUT.puts "Renamed current collection #{current_collection} to #{new_collection}."
126
- rescue
127
- STDERR.puts "Renaming to #{new_collection} failed, it already exists."
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}"
128
144
  end
129
145
  else
130
146
  STDOUT.puts "Renaming cancelled."
@@ -132,42 +148,204 @@ module OllamaChat::RAGHandling
132
148
  end
133
149
  end
134
150
 
151
+ # Retrieves all document collections registered in the database.
152
+ #
153
+ # @return [Sequel::Dataset] a dataset of all collections ordered by name.
154
+ def all_collections
155
+ models::Collection.order(:name)
156
+ end
157
+
135
158
  # Displays the list of available collections in the terminal.
136
159
  #
137
160
  # This method retrieves the current collection and the full list of available
138
- # collections from the internal document handler, highlighting the active one.
161
+ # collections and their descriptions from the internal document handler,
162
+ # highlighting the active one.
139
163
  def list_collections
140
- current_collection = collection
141
- STDOUT.puts @documents.collections.
142
- map { |c| current_collection == c ? bold { c } : c }
164
+ current_collection = collection.to_s
165
+ collections = all_collections.select(:name, :description)
166
+ use_pager do |output|
167
+ collections.each { |c|
168
+ collection_name = current_collection == c.name ? bold { c.name } : c.name
169
+ collection_description = c.description
170
+ output.puts '%s: %s' % [ collection_name, collection_description ]
171
+ }
172
+ end
143
173
  end
144
174
 
145
175
  # Updates the documents in the current collection by re-embedding any sources
146
- # that have been modified since they were first added.
176
+ # that have been modified since they were first added, and embedding any new
177
+ # files matching the collection's patterns.
147
178
  #
148
179
  # This method iterates through all records in the active collection and
149
180
  # identifies unique sources. For each modified source, it preserves the
150
181
  # existing tags, removes the stale records, and re-embeds the current
151
- # version of the source.
182
+ # version of the source. It then scans for new files matching the
183
+ # collection's patterns and embeds them.
152
184
  #
153
185
  # @return [String] a newline-separated string of embedding result messages.
154
- def update_collection
155
- results = []
156
- seen = {}
157
- @documents.each_record do |record|
158
- source = @documents.normalize_source(record.source) or next
159
- seen.key?(source) and next
160
- seen[source] = true
161
- unless @documents.source_modified?(source)
162
- infobar.puts "Source #{source.to_s.inspect} is unmodified. => Skipping."
163
- next
186
+ def update_collection(collection)
187
+ switch_collection(collection) do
188
+ unless col = database_collection?(collection)
189
+ STDERR.puts "❌ Collection #{collection.inspect} not found in database."
190
+ return
191
+ end
192
+ results = []
193
+ seen = {}
194
+ @documents.each_record do |record|
195
+ source = @documents.normalize_source(record.source) or next
196
+ seen.key?(source) and next
197
+ seen[source] = true
198
+ unless @documents.source_modified?(source)
199
+ infobar.puts "Source #{source.to_s.inspect} is unmodified. => Skipping."
200
+ next
201
+ end
202
+ tags = record.tags_set
203
+ @documents.source_remove(source)
204
+ r = embed(source, tags:) or next
205
+ results << r
206
+ end
207
+
208
+ if patterns = col.patterns.full?
209
+ all_file_set(patterns).each do |file|
210
+ seen[file.to_s] and next
211
+ seen[file.to_s] = true
212
+ r = embed(file.to_s, tags: []) or next
213
+ results << r
214
+ end
215
+ end
216
+
217
+ log(:info, "Collection updated", data: { collection:, sources_updated: results.size })
218
+ results * "\n"
219
+ end
220
+ end
221
+
222
+ # Extracts and normalizes file patterns from a space-separated string.
223
+ #
224
+ # Splits the input string by whitespace, strips each pattern, and expands
225
+ # them to absolute paths. Returns an empty array if the input is blank.
226
+ #
227
+ # @param patterns_str [String, nil] the space-separated glob patterns
228
+ # @return [Array<String>] an array of expanded absolute path patterns
229
+ def extract_patterns(patterns_str)
230
+ patterns = patterns_str.full? ? patterns_str.split(/\s+/).map(&:strip) : []
231
+ patterns.map { File.expand_path(_1) }
232
+ end
233
+
234
+ # Interactively create a new collection record.
235
+ def create_collection
236
+ name = ask?(prompt: "📚 Name of the new collection: ")
237
+ unless name.full?
238
+ STDERR.puts "❌ Cancelled creation of collection."
239
+ return
240
+ end
241
+
242
+ if database_collection?(name)
243
+ STDERR.puts "❌ Collection #{name.inspect} already exists."
244
+ return
245
+ end
246
+
247
+ description = ask?(prompt: "📝 Description: ")
248
+ unless description.full?
249
+ STDERR.puts "❌ Cancelled creation of collection #{name.inspect}."
250
+ return
251
+ end
252
+ patterns_str = ask?(prompt: "🔍 Patterns (space-separated globs, e.g., lib/**/*.rb): ")
253
+ patterns = extract_patterns(patterns_str)
254
+
255
+ begin
256
+ models::Collection.create(
257
+ name: name.to_s,
258
+ description: description.to_s,
259
+ patterns:
260
+ )
261
+ if patterns.full?
262
+ update_collection(name.to_s)
263
+ end
264
+ STDOUT.puts "✅ Created collection '#{name}'."
265
+ log(:info, "Collection created", data: { name: name, description:, patterns: })
266
+ rescue Sequel::UniqueConstraintViolation
267
+ STDERR.puts "❌ Collection #{name.inspect} already exists."
268
+ rescue Sequel::Error => e
269
+ STDERR.puts "❌ Database error: #{e.message}"
270
+ end
271
+ name.to_s
272
+ end
273
+
274
+ # Interactively update attributes of an existing collection.
275
+ def edit_collection
276
+ collections = models::Collection.order(:name).pluck(:name)
277
+ collections.unshift('[CANCEL]')
278
+ target_name = choose_entry(
279
+ collections,
280
+ prompt: '📚 Which collection to edit? %s'
281
+ )
282
+ return if target_name.nil? || target_name == '[CANCEL]'
283
+
284
+ col = database_collection?(target_name)
285
+ unless col
286
+ STDERR.puts "❌ Collection #{target_name.inspect} not found in database."
287
+ return
288
+ end
289
+
290
+ new_description = switch_history :collection do
291
+ ask?(
292
+ prompt: "📝 New description (leave blank to keep): ",
293
+ prefill: col.description
294
+ )
295
+ end
296
+ patterns = switch_history :patterns do
297
+ patterns_str = ask?(
298
+ prompt: "🔍 New patterns (space-separated, leave blank to keep): ",
299
+ prefill: col.patterns.full?(:join, ' ')
300
+ )
301
+ extract_patterns(patterns_str)
302
+ end
303
+
304
+ col.description = new_description.full? ? new_description.to_s : col.description
305
+ col.patterns = patterns
306
+
307
+ begin
308
+ col.save
309
+ STDOUT.puts "✅ Updated collection '#{col.name}'."
310
+ log(:info, "Collection updated", data: { name: col.name })
311
+ rescue Sequel::Error => e
312
+ STDERR.puts "❌ Database error: #{e.message}"
313
+ end
314
+ end
315
+
316
+ # Permanently remove a collection from DB and purge from Documentrix.
317
+ def delete_collection
318
+ choose_with_state do
319
+ loop do
320
+ collections = models::Collection.order(:name).pluck(:name)
321
+ collections.unshift('[CANCEL]')
322
+ target_name = choose_entry(
323
+ collections,
324
+ prompt: '🗑️ Which collection to delete? %s'
325
+ )
326
+ break if target_name.nil? || target_name == '[CANCEL]'
327
+
328
+ col = database_collection?(target_name)
329
+ unless col
330
+ STDERR.puts "❌ Collection #{target_name.inspect} not found in database."
331
+ next
332
+ end
333
+
334
+ if confirm?(prompt: "⚠️ Are you sure you want to permanently delete #{target_name.inspect}? (y/n) ", yes: /\Ay/i)
335
+ begin
336
+ col.chat = self
337
+ col.destroy
338
+ STDOUT.puts "✅ Deleted collection #{target_name.inspect}."
339
+ log(:info, "Collection deleted", data: { name: target_name })
340
+ rescue Sequel::Error => e
341
+ STDERR.puts "❌ Database error: #{e.message}"
342
+ rescue => e
343
+ STDERR.puts "❌ Error removing from Documentrix: #{e.message}"
344
+ end
345
+ else
346
+ STDOUT.puts "🚫 Deletion cancelled."
347
+ end
164
348
  end
165
- tags = record.tags_set
166
- @documents.source_remove(source)
167
- r = embed(source, tags:) or next
168
- results << r
169
349
  end
170
- log(:info, "Collection updated", data: { collection:, sources_updated: results.size })
171
- results * "\n"
172
350
  end
173
351
  end
@@ -63,7 +63,7 @@ module OllamaChat::SessionManagement
63
63
  # @return [OllamaChat::Database::Models::Session] a new session with default
64
64
  # attributes
65
65
  def new_session
66
- OllamaChat::Database::Models::Session.with_defaults(self)
66
+ models::Session.with_defaults(self)
67
67
  end
68
68
 
69
69
  # Retrieves the preferred session from the database, or creates a new one if
@@ -159,7 +159,7 @@ module OllamaChat::SessionManagement
159
159
  STDOUT.puts "Cancelled."
160
160
  return nil
161
161
  end
162
- if models::Session.where(name: session_name).first
162
+ if models::Session.where(name: session_name).present?
163
163
  STDOUT.puts "Session named #{bold{session_name}} already exists."
164
164
  else
165
165
  break
@@ -239,6 +239,7 @@ module OllamaChat::SessionManagement
239
239
  end
240
240
  session or abort "No session named #{bold{session_name.inspect}} found."
241
241
  if session.lock?
242
+ messages.read_conversation_jsonl(session.messages.to_s)
242
243
  session_apply
243
244
  else
244
245
  raise OllamaChat::OllamaChatError,
@@ -249,6 +250,7 @@ module OllamaChat::SessionManagement
249
250
  def session_apply
250
251
  session.update(working_directory: Dir.pwd)
251
252
  init_history
253
+ repair_group_uuids
252
254
  session
253
255
  end
254
256
 
@@ -321,7 +323,7 @@ module OllamaChat::SessionManagement
321
323
  if name == session.name
322
324
  STDOUT.puts "Keeping the old name #{name.inspect}."
323
325
  elsif name.present?
324
- if exists = models::Session.where(name:).first
326
+ if exists = models::Session.where(name:).present?
325
327
  STDOUT.puts "Session with name #{name.inspect} already exists."
326
328
  else
327
329
  session.update(name:)
@@ -434,13 +436,18 @@ module OllamaChat::SessionManagement
434
436
  session_close
435
437
  previous_session_id = session.id
436
438
  @session = chosen_session
437
- messages.read_conversation_jsonl(session.messages.to_s)
438
- repair_group_uuids
439
- set_current_collection(session.current_collection.full? || :default)
440
- session.current_model.full? { use_model(_1) }
441
- set_default_persona_name(session.default_persona_name.full? || :none)
442
- set_current_system_prompt(session.current_system_prompt.full? || 'default')
443
439
  if session.lock?
440
+ messages.read_conversation_jsonl(session.messages.to_s)
441
+ if current_collection = session.current_collection.full? and
442
+ database_collection?(current_collection)
443
+ then
444
+ set_current_collection(current_collection)
445
+ else
446
+ set_current_collection(:default)
447
+ end
448
+ session.current_model.full? { use_model(_1) }
449
+ set_default_persona_name(session.default_persona_name.full? || :none)
450
+ set_current_system_prompt(session.current_system_prompt.full? || 'default')
444
451
  session_apply
445
452
  log(:info, "Session changed", data: { session_id: session.id, name: session.name, previous_session_id: })
446
453
  info_session
@@ -36,9 +36,10 @@ class OllamaChat::Tools::PatchFile
36
36
  expected content. To save tokens, you can read just the range around
37
37
  your target area using `read_file` with specific start/end lines.
38
38
 
39
- FRESHNESS CHECK: You MUST provide the current `checksum` (CRC32)
40
- of the file as returned by `read_file`. This ensures you are patching
41
- the most recent version of the file and prevents stale context errors.
39
+ FRESHNESS CHECK: You MUST provide the current `checksum` (8 hex
40
+ digits long CRC32) of the file as returned by `read_file`. This
41
+ ensures you are patching the most recent version of the file and
42
+ prevents stale context errors.
42
43
 
43
44
  IMPORTANT: The checksum is only returned by `read_file` when the
44
45
  entire file is read with `line_numbers: true` (no start_line or
@@ -81,7 +82,10 @@ class OllamaChat::Tools::PatchFile
81
82
  ),
82
83
  checksum: Tool::Function::Parameters::Property.new(
83
84
  type: 'string',
84
- description: 'The CRC32 checksum of the file from the latest `read_file` call (8 hex chars).'
85
+ description: <<~EOT
86
+ The CRC32 checksum of the file from the latest `read_file` call
87
+ (8 hex digits long).
88
+ EOT
85
89
  ),
86
90
  },
87
91
  required: %w[path edits checksum]
@@ -1,6 +1,6 @@
1
1
  module OllamaChat
2
2
  # OllamaChat version
3
- VERSION = '0.0.104'
3
+ VERSION = '0.0.105'
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:
data/ollama_chat.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama_chat 0.0.104 ruby lib
2
+ # stub: ollama_chat 0.0.105 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama_chat".freeze
6
- s.version = "0.0.104".freeze
6
+ s.version = "0.0.105".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/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/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/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/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/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/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/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]
@@ -1,10 +1,10 @@
1
1
  describe OllamaChat::Chat, protect_env: true do
2
2
  let :collection do
3
- "test-#{Random.hex}"
3
+ :default
4
4
  end
5
5
 
6
6
  let :argv do
7
- chat_default_config(%w[ -C ] << collection)
7
+ chat_default_config
8
8
  end
9
9
 
10
10
  before do
@@ -26,33 +26,9 @@ describe OllamaChat::Chat, protect_env: true do
26
26
  end
27
27
  end
28
28
 
29
- context 'loading conversations' do
30
- connect_to_ollama_server(instantiate: false)
31
-
32
- let :argv do
33
- chat_default_config(%w[ -C ] << collection << '-c' << asset('conversation.json'))
34
- end
35
-
36
- it 'dispays the last exchange of the converstation' do
37
- expect(chat).to receive(:interact_with_user).and_return 0
38
- expect(STDOUT).to receive(:puts).at_least(1)
39
- chat.start
40
- end
41
- end
42
-
43
29
  describe OllamaChat::DocumentCache do
44
30
  connect_to_ollama_server(instantiate: false)
45
31
 
46
- context 'with MemoryCache' do
47
- let :argv do
48
- chat_default_config(%w[ -M ])
49
- end
50
-
51
- it 'can use MemoryCache' do
52
- expect(chat.documents.cache).to be_a Documentrix::Documents::MemoryCache
53
- end
54
- end
55
-
56
32
  context 'falls back to MemoryCache' do
57
33
  it 'falls back to MemoryCache' do
58
34
  expect_any_instance_of(OllamaChat::Chat).to\
@@ -62,22 +38,6 @@ describe OllamaChat::Chat, protect_env: true do
62
38
  end
63
39
  end
64
40
 
65
- describe Documentrix::Documents do
66
- context 'with documents' do
67
- connect_to_ollama_server(instantiate: false)
68
-
69
- let :argv do
70
- chat_default_config(%w[ -C ] << collection << '-D' << asset('example.html'))
71
- end
72
-
73
- it 'Adds documents passed to app via -D option' do
74
- expect_any_instance_of(OllamaChat::Chat).to receive(:add_documents_from_argv).
75
- with([ asset('example.html') ])
76
- chat
77
- end
78
- end
79
- end
80
-
81
41
  describe OllamaChat::Information do
82
42
  connect_to_ollama_server(instantiate: false)
83
43
 
@@ -92,7 +52,7 @@ describe OllamaChat::Chat, protect_env: true do
92
52
  it 'can display collection_stats' do
93
53
  chat
94
54
  expect(STDOUT).to receive(:puts).with(
95
- /Current Collection\n Name: \e\[1m#{collection}\e\[0m\n #Embeddings: 0\n #Tags: 0\n Tags:/
55
+ /Current Collection\n Name: \e\[1mdefault\e\[0m\n Patterns: \e\[3m\e\[0m\n #Embeddings: 0\n #Tags: 0\n Tags:/
96
56
  )
97
57
  expect(chat.collection_stats).to be_nil
98
58
  end
@@ -1,10 +1,6 @@
1
1
  describe OllamaChat::Commands, protect_env: true do
2
- let :collection do
3
- "test-#{Random.hex}"
4
- end
5
-
6
2
  let :argv do
7
- chat_default_config(%w[ -C ] << collection)
3
+ chat_default_config
8
4
  end
9
5
 
10
6
  before do
@@ -237,6 +233,17 @@ describe OllamaChat::Commands, protect_env: true do
237
233
  expect(chat).to receive(:copy_model_options_to_session).with('codellama', profile: 'default')
238
234
  expect(chat.handle_input("/model options to session -m")).to eq :next
239
235
  end
236
+
237
+ it 'returns :next when input is "/model options copy"' do
238
+ expect(chat).to receive(:copy_model_options_profile).with(nil)
239
+ expect(chat.handle_input("/model options copy")).to eq :next
240
+ end
241
+
242
+ it 'returns :next when input is "/model options copy -m"' do
243
+ expect(chat).to receive(:choose_model).and_return 'codellama'
244
+ expect(chat).to receive(:copy_model_options_profile).with('codellama')
245
+ expect(chat.handle_input("/model options copy -m")).to eq :next
246
+ end
240
247
  end
241
248
 
242
249
  describe '/session model options change' do
@@ -291,11 +298,11 @@ describe OllamaChat::Commands, protect_env: true do
291
298
  expect(chat.handle_input("/collection clear")).to eq :next
292
299
  expect(chat).to receive(:choose_entry)
293
300
  expect(chat).to receive(:info)
294
- expect(STDOUT).to receive(:puts).with(/./)
301
+ expect(STDOUT).to receive(:puts).with(/Using collection/)
295
302
  expect(chat.handle_input("/collection change")).to eq :next
296
- expect(STDOUT).to receive(:puts).with(array_including(:default))
303
+ expect(STDOUT).to receive(:puts).with(/default/)
297
304
  expect(chat.handle_input("/collection list")).to eq :next
298
- expect(chat).to receive(:rename_collection).with(collection.to_sym)
305
+ expect(chat).to receive(:rename_collection).with(:default)
299
306
  expect(chat.handle_input("/collection rename")).to eq :next
300
307
  end
301
308
  end
@@ -309,7 +309,6 @@ describe OllamaChat::SessionManagement do
309
309
  new_s = chat.new_session.tap { |s| s.name = 'change_to_me'; s.save }
310
310
  expect(chat).to receive(:choose_session).and_return(new_s)
311
311
  expect(chat).to receive(:session_close)
312
- expect(chat).to receive(:repair_group_uuids)
313
312
  expect(chat).to receive(:set_current_collection)
314
313
  expect(chat).to receive(:use_model)
315
314
  expect(chat).to receive(:set_default_persona_name)
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.104
4
+ version: 0.0.105
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -495,6 +495,8 @@ extra_rdoc_files:
495
495
  - lib/ollama_chat/database/migrations/004_add_profile_to_model_options.rb
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
+ - lib/ollama_chat/database/migrations/007_add_collections_table.rb
499
+ - lib/ollama_chat/database/models/collection.rb
498
500
  - lib/ollama_chat/database/models/favourite.rb
499
501
  - lib/ollama_chat/database/models/model_options.rb
500
502
  - lib/ollama_chat/database/models/prompt.rb
@@ -620,6 +622,8 @@ files:
620
622
  - lib/ollama_chat/database/migrations/004_add_profile_to_model_options.rb
621
623
  - lib/ollama_chat/database/migrations/005_add_context_format_to_sessions.rb
622
624
  - lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb
625
+ - lib/ollama_chat/database/migrations/007_add_collections_table.rb
626
+ - lib/ollama_chat/database/models/collection.rb
623
627
  - lib/ollama_chat/database/models/favourite.rb
624
628
  - lib/ollama_chat/database/models/model_options.rb
625
629
  - lib/ollama_chat/database/models/prompt.rb