ollama_chat 0.0.109 → 0.0.110

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: e6c85b95f4c5051b6b0f257dd8f63ee2a3997146c527fd8af251458fee641039
4
- data.tar.gz: 45a66e25a1a92d8cbd86253a991ad28eeb48d1a8ca68c1f8bbb8df90bbbba37f
3
+ metadata.gz: 239476f6dca2ca4fca22dc0290b25bb048572cc89d12ef82c09d54a7ee429068
4
+ data.tar.gz: d7c51cf3d519c593666625bcedf487a1d40893099c251f211cd055a4610cb3db
5
5
  SHA512:
6
- metadata.gz: a401173a7deb626af01ad8c9adf358259a6ffd7ea292644bb1a5a4553df2f70e9c74bb4ff0807d04914a7aa47e8140e81e0bd46745e33900962d8559950ea914
7
- data.tar.gz: 6b04fcce611684e73bbe54cdadb01859fc4219f57960604a64c3037b414e56a02c7389a5bfef2341d0a2aa2a44d8a723761469f9c677ec8784c2c9d9e8b6fae0
6
+ metadata.gz: 24eadaa6eb35470ff3908fbfd7646ad0b685bded30694b7784839aef35a447b6f6926c9cac6a29f4eed55d723065cdc063f21d7c718f3c15dc4905892f860484
7
+ data.tar.gz: 98f4ab4370005f5a9523a7fc54ea62f56e64b411894d8ca40ae85518cae9962f8b9818086d5984f34aec392dde268377b10d33ef103e64ebc9b8abcd17702293
data/CHANGES.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-08-20 v0.0.110
4
+
5
+ ### Added
6
+
7
+ * **App State Support**: Introduced `009_add_app_states_table.rb` migration
8
+ and `app_state.rb` model.
9
+ * **Prompt Synchronization**: Added `/prompt sync` subcommand with unified
10
+ diffs and boot check capabilities.
11
+ * Implemented `AppState` model with `get`/`set` helpers and a `seed` boot
12
+ check using per-prompt `Digest::SHA256` hashes XOR-folded into a
13
+ fingerprint.
14
+ * Boot check prints `~`/`+`/`-` detail report on drift and prompts user
15
+ to acknowledge; declining retains old fingerprint for repeated notice
16
+ on next boot.
17
+ * Added `OC::DIFF_COMMAND` setting (`diff -u --color=always`) to `oc.rb`
18
+ for unified diff display, decoded via `Shellwords.split`.
19
+ * Implemented `prompt_sync` and `show_prompt_diff` methods in
20
+ `prompt_management.rb` to compare DB prompts against shipped defaults,
21
+ show unified diff, offer `OC::DIFF_TOOL` resolution, and clean up
22
+ orphaned default prompts.
23
+ * Wired `/prompt sync` subcommand into `commands.rb` (regexp, completion,
24
+ dispatch).
25
+ * **Test Mode**: Added `OllamaChat.test_mode` flag in `ollama_chat.rb` to
26
+ suppress interactive prompts during specs; set in `spec_helper.rb`.
27
+ * **Documentation**: Added YARD `@!attribute` docs for `test_mode` and
28
+ `test_mode?`.
29
+
30
+ ### Changed
31
+
32
+ * **Web Searching**: Updated `web_searching.rb` to store `search_engine` in
33
+ local `engine` to avoid double method call.
34
+ * **Session Management**: Added call to `session_close` after
35
+ `clean_messages!` succeeds in `lib/ollama_chat/commands.rb`.
36
+ * **Error Messages**: Simplified stale context error message in `PatchFile`
37
+ when a checksum mismatch occurs.
38
+
39
+ ### Fixed
40
+
41
+ * **Spec Expectations**: Updated `web_searching_spec.rb` to replace `allow`
42
+ with `expect` for message expectations.
43
+ * Changed `log` expectations to `.at_least(:once)` since `links.add` also
44
+ triggers `log` internally.
45
+ * Removed unused `before { allow(chat).to receive(:log) }` from the
46
+ `#web` describe block.
47
+ * Added `.at_least(:once)` to `document_policy.selected` as it is called
48
+ in both `if` and `elsif` branches.
49
+ * Changed `fetch_source`, `summarize`, and `import` expectations to
50
+ `.twice` since they are called once per URL (2 URLs in test data).
51
+
52
+ ### Tests
53
+
54
+ * Added 7 new spec examples for `prompt_sync` and `show_prompt_diff` in
55
+ `prompt_management_spec.rb`.
56
+
3
57
  ## 2026-08-18 v0.0.109
4
58
 
5
59
  ### Added
@@ -484,14 +484,15 @@ module OllamaChat::Commands
484
484
 
485
485
  command(
486
486
  name: :prompt,
487
- regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|rename|-e))?(\s+(?:-[ef]|-c\s+(?:\w+|\?)))?(?:\s+([^-].*))?$),
488
- complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset rename ] ],
487
+ regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|rename|sync|-e))?(\s+(?:-[ef]|-c\s+(?:\w+|\?)))?(?:\s+([^-].*))?$),
488
+ complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset rename sync ] ],
489
489
  optional: true,
490
490
  options: '[-c CONTEXT|-e|-f]',
491
491
  help: <<~EOT,
492
492
  📝 Manage prompt templates:
493
- Subcommands: edit, info, add, delete, list,
494
- duplicate, import, export, reset, rename.
493
+ Subcommands: edit, info, add, delete, list,
494
+ duplicate, import, export, reset, rename,
495
+ sync.
495
496
  Options: -c [context]
496
497
  (? for interactive in /prompt),
497
498
  -e (edit next)
@@ -544,6 +545,8 @@ module OllamaChat::Commands
544
545
  STDOUT.puts "No default value found for prompt #{bold{prompt.name}}."
545
546
  end
546
547
  end
548
+ when 'sync'
549
+ prompt_sync(context:)
547
550
  when nil, '-e'
548
551
  if prompt = choose_prompt(
549
552
  prompt: 'Which template shall guide the next response? %s',
@@ -619,6 +622,7 @@ module OllamaChat::Commands
619
622
  )
620
623
  then
621
624
  messages.clean_messages!
625
+ session_close
622
626
  STDOUT.puts "Conversation cleaned."
623
627
  else
624
628
  STDOUT.puts 'Cancelled.'
@@ -0,0 +1,11 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table :app_states do
4
+ primary_key :id
5
+ String :key, null: false, unique: true
6
+ Text :value # JSON-encoded payload
7
+ DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP, null: false
8
+ DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP, null: false
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,122 @@
1
+ require 'digest'
2
+
3
+ # Represents a key/value pair of persistent application state stored in
4
+ # the database.
5
+ #
6
+ # This model provides a generic, JSON-backed store for small bits of
7
+ # runtime state that need to survive across sessions—such as the
8
+ # XOR-SHA256 fingerprint of the shipped default prompts used for boot
9
+ # drift detection.
10
+ class OllamaChat::Database::Models::AppState < Sequel::Model(OllamaChat::DB)
11
+ plugin :timestamps, update_on_create: true
12
+ plugin :serialization, :json, :value
13
+ plugin :validation_helpers
14
+
15
+ # Validates the application state entry.
16
+ #
17
+ # Ensures that the `key` is present and unique.
18
+ def validate
19
+ super
20
+ validates_presence :key
21
+ validates_unique :key
22
+ end
23
+
24
+ # Retrieves the JSON payload stored under the given key.
25
+ #
26
+ # @param key [String, Symbol] the state key to look up
27
+ # @return [Hash, nil] the deserialized JSON payload, or nil if no
28
+ # record exists for the key
29
+ def self.get(key)
30
+ where(key: key.to_s).first&.value
31
+ end
32
+
33
+ # Creates or updates the JSON payload stored under the given key.
34
+ #
35
+ # @param key [String, Symbol] the state key to write
36
+ # @param value [Hash, nil] the JSON-serializable payload to store
37
+ # @return [OllamaChat::Database::Models::AppState] the saved record
38
+ def self.set(key, value)
39
+ if found = where(key: key.to_s).first
40
+ found.update(value:)
41
+ found
42
+ else
43
+ create(key: key.to_s, value:)
44
+ end
45
+ end
46
+
47
+ # Computes per-prompt SHA256 hashes and an XOR fingerprint of all shipped
48
+ # default prompts.
49
+ #
50
+ # At boot, if the stored fingerprint differs from the current one, a detail
51
+ # report (modified / added / removed) is printed to STDERR and the user is
52
+ # prompted to accept the new fingerprint. If declined, the old fingerprint
53
+ # is retained and the notice will reappear on the next boot.
54
+ #
55
+ # This method is invoked automatically by the model seeding loop in
56
+ # `OllamaChat::Database.setup_models`.
57
+ #
58
+ # @param chat [OllamaChat::Chat] the chat instance providing the config
59
+ # and `confirm?`
60
+ def self.seed(chat)
61
+ stored = get(:prompt_defaults_fingerprint)
62
+
63
+ stored_fingerprint = stored&.dig('fingerprint')
64
+
65
+ hashes = {}
66
+ chat.config.prompts.to_h.each do |context, prompts|
67
+ prompts.each do |name, content|
68
+ hashes["#{context}/#{name}"] =
69
+ Digest::SHA256.hexdigest(content.to_s)
70
+ end
71
+ end
72
+
73
+ fingerprint = hashes.values.reduce(0) do |sum, hex|
74
+ sum ^ hex.to_i(16)
75
+ end.to_s(16)
76
+
77
+ return true if fingerprint == stored_fingerprint
78
+
79
+ if stored&.dig('hashes')
80
+ old = stored['hashes']
81
+ added = hashes.keys - old.keys
82
+ removed = old.keys - hashes.keys
83
+ changed = hashes.select { |k, v| old[k] && old[k] != v }.keys
84
+
85
+ STDERR.puts "⚠️ Shipped prompts changed since last boot:"
86
+ changed.each { |k| STDERR.puts " ~ #{k} (modified)" }
87
+ added.each { |k| STDERR.puts " + #{k} (new)" }
88
+ removed.each { |k| STDERR.puts " - #{k} (removed)" }
89
+ elsif !OllamaChat.test_mode?
90
+ STDERR.puts '⚠️ First run — storing prompt fingerprints.'
91
+ set(:prompt_defaults_fingerprint,
92
+ { fingerprint:, hashes:, computed_at: Time.now.iso8601 })
93
+ return true
94
+ end
95
+
96
+ prompt = <<~EOT.chomp << ' '
97
+ Keep local prompts (stop nagging)?
98
+ Consider adopting via /prompt sync or /prompt reset. (y/n) %s
99
+ EOT
100
+ if OllamaChat.test_mode? || chat.confirm?(prompt:, yes: /\Ay/i, timeout: 5)
101
+ set(:prompt_defaults_fingerprint,
102
+ { fingerprint:, hashes:, computed_at: Time.now.iso8601 })
103
+ end
104
+
105
+ true
106
+ end
107
+
108
+ # @!attribute [v] id
109
+ # @return [Integer] The primary key for the state entry.
110
+ #
111
+ # @!attribute [v] key
112
+ # @return [String] The unique state key.
113
+ #
114
+ # @!attribute [v] value
115
+ # @return [Hash, nil] The JSON-serialized state payload.
116
+ #
117
+ # @!attribute [v] created_at
118
+ # @return [Time, nil] The timestamp when the state was created.
119
+ #
120
+ # @!attribute [v] updated_at
121
+ # @return [Time, nil] The timestamp of the last update to the state.
122
+ end
@@ -1,5 +1,6 @@
1
1
  require 'const_conf'
2
2
  require 'pathname'
3
+ require 'shellwords'
3
4
 
4
5
  # Environment configuration module for OllamaChat
5
6
  #
@@ -64,6 +65,14 @@ module OC
64
65
  end
65
66
  end
66
67
 
68
+ DIFF_COMMAND = set do
69
+ description 'Command (string) to generate a unified diff with'
70
+
71
+ required true
72
+ default { 'diff -u --color=always' }
73
+ decode { Shellwords.split(_1) if _1.present? }
74
+ end
75
+
67
76
  DIFF_TOOL = set do
68
77
  description 'Diff tool to apply changes with'
69
78
 
@@ -240,7 +240,6 @@ prompts:
240
240
  How does this implementation handle malformed JSON or missing keys in the
241
241
  middle of a 2GB file without crashing the process?
242
242
 
243
-
244
243
  What is the time complexity of the current insert logic, and can we implement
245
244
  bulk inserts to reduce database round trips?
246
245
 
@@ -1,3 +1,5 @@
1
+ require 'tmpdir'
2
+
1
3
  # Provides administrative and interactive management for
2
4
  # prompt templates stored in the database.
3
5
  #
@@ -382,6 +384,74 @@ module OllamaChat::PromptManagement
382
384
  end
383
385
  end
384
386
 
387
+ # Synchronizes database prompts with their shipped defaults from the
388
+ # configuration.
389
+ #
390
+ # For each default prompt in the given context, compares the database copy
391
+ # against the shipped default. Drifted prompts are displayed as a unified
392
+ # diff (via `OC::DIFF_COMMAND`), and the user is offered an interactive
393
+ # resolution via `OC::DIFF_TOOL`.
394
+ #
395
+ # Orphaned default prompts (present in the database but no longer in the
396
+ # shipped config) are listed and offered for deletion.
397
+ #
398
+ # @param context [String, nil] the prompt context to sync (default: 'prompt')
399
+ # @return [self, nil] self on success, nil if no drift was found
400
+ def prompt_sync(context: nil)
401
+ context ||= 'prompt'
402
+ shipped = config.prompts[context].to_h.stringify_keys_recursive
403
+ db_prompts = each_prompt(context:, default: true).to_a
404
+
405
+ drifted = db_prompts.select do |p|
406
+ shipped.key?(p.name) && shipped[p.name].to_s != p.to_s
407
+ end
408
+
409
+ orphans = db_prompts.select { |p| !shipped.key?(p.name) }
410
+
411
+ if drifted.empty? && orphans.empty?
412
+ STDOUT.puts "All prompts in context #{bold{context}} are in sync. ✨"
413
+ return self
414
+ end
415
+
416
+ if drifted.any?
417
+ STDOUT.puts "\n#{drifted.count} drifted prompt(s) found:\n"
418
+ drifted.each { |p| STDOUT.puts " • #{bold{p.name}} in #{italic{p.context}}" }
419
+ STDOUT.puts
420
+ end
421
+
422
+ confirm?(prompt: '⏎ Press any key to continue (%s). ', timeout: 5)
423
+
424
+ drifted.each do |p|
425
+ show_prompt_diff(p, shipped[p.name], context:)
426
+ end
427
+
428
+ if orphans.any?
429
+ STDOUT.puts "\n#{orphans.count} orphaned prompt(s) "\
430
+ "(no longer in default config):\n"
431
+ orphans.each { |p| STDOUT.puts " • #{bold{p.name}} in #{italic{p.context}}" }
432
+ STDOUT.puts
433
+ end
434
+
435
+ confirm?(prompt: '⏎ Press any key to continue (%s). ', timeout: 5)
436
+
437
+ unless orphans.empty?
438
+ STDOUT.puts
439
+ if confirm?(
440
+ prompt: "🧹 Remove #{orphans.count} orphaned prompt(s)? (y/n) ",
441
+ yes: /\Ay/i
442
+ )
443
+ then
444
+ orphans.each do |p|
445
+ p.destroy
446
+ STDOUT.puts " ✓ Removed #{bold{p.name}}"
447
+ log(:info, "Orphan prompt cleaned up", data: { name: p.name, context: })
448
+ end
449
+ end
450
+ end
451
+
452
+ self
453
+ end
454
+
385
455
  private
386
456
 
387
457
  # Helper to wrap a prompt name with its favourite status for the UI.
@@ -395,6 +465,60 @@ module OllamaChat::PromptManagement
395
465
  SearchUI::Wrapper.new(name, display:)
396
466
  end
397
467
 
468
+ # Writes the database copy and the shipped default to temp files and
469
+ # displays a unified diff via `OC::DIFF_COMMAND`.
470
+ #
471
+ # If the user opts in, launches `OC::DIFF_TOOL` for interactive
472
+ # resolution; the resolved content (file A) is then written back
473
+ # to the database.
474
+ #
475
+ # @param prompt [OllamaChat::Database::Models::Prompt] the DB prompt
476
+ # @param shipped [String] the shipped default content from config
477
+ # @param context [String] the prompt context
478
+ def show_prompt_diff(prompt, shipped, context:)
479
+ STDOUT.puts "\n#{'─' * 60}"
480
+ STDOUT.puts "📝 #{bold{prompt.name}}"
481
+ STDOUT.puts '─' * 60
482
+
483
+ Dir.mktmpdir('prompt_sync') do |dir|
484
+ file_a = File.join(dir, "#{prompt.name}.local")
485
+ file_b = File.join(dir, "#{prompt.name}.default")
486
+ File.write(file_a, prompt.to_s)
487
+ File.write(file_b, shipped.to_s)
488
+
489
+ cmd = OC::DIFF_COMMAND.dup << file_a << file_b
490
+ output = IO.popen(cmd, &:read).chomp
491
+
492
+ if output.empty?
493
+ STDOUT.puts " (no differences detected)"
494
+ else
495
+ STDOUT.puts output
496
+ end
497
+
498
+ STDOUT.puts
499
+ if confirm?(
500
+ prompt: "🔧 Resolve differences for #{bold{prompt.name}}? (y/n) ",
501
+ yes: /\Ay/i
502
+ )
503
+ then
504
+ unless diff_tool = OC::DIFF_TOOL?
505
+ STDERR.puts ' No DIFF_TOOL available.'
506
+ return
507
+ end
508
+ system(*[diff_tool, file_a, file_b].map(&:to_s))
509
+ resolved = File.read(file_a)
510
+ if resolved != prompt.to_s
511
+ write_prompt(prompt.name, resolved, context:)
512
+ STDOUT.puts " ✓ Updated #{bold{prompt.name}}"
513
+ log(:info, "Prompt synced via diff tool",
514
+ data: { name: prompt.name, context: })
515
+ else
516
+ STDOUT.puts " (no changes made)"
517
+ end
518
+ end
519
+ end
520
+ end
521
+
398
522
  # Interactively determines a unique name for a new prompt, ensuring it
399
523
  # does not conflict with existing prompts in the database.
400
524
  #
@@ -132,7 +132,7 @@ class OllamaChat::Tools::PatchFile
132
132
  if args.checksum != current_checksum
133
133
  raise OllamaChat::ToolFunctionArgumentError,
134
134
  "Stale context: File `#{path}` has been modified since your last read. " \
135
- "Expected checksum `#{current_checksum}`, got `#{args.checksum}`."
135
+ "Unexpected checksum `#{args.checksum}`, I cannot patch!"
136
136
  end
137
137
 
138
138
  # We use the content we just read for the patch, as it's verified fresh
@@ -1,6 +1,6 @@
1
1
  module OllamaChat
2
2
  # OllamaChat version
3
- VERSION = '0.0.109'
3
+ VERSION = '0.0.110'
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:
@@ -24,16 +24,17 @@ module OllamaChat::WebSearching
24
24
  def search_web(query, n = nil)
25
25
  n = n.to_i.clamp(1..)
26
26
  query = URI.encode_uri_component(query)
27
- search_command = :"search_web_with_#{search_engine}"
27
+ engine = search_engine
28
+ search_command = :"search_web_with_#{engine}"
28
29
  if respond_to?(search_command, true)
29
30
  send(search_command, query, n).tap do |results|
30
31
  log(:info, "Web search performed", data: {
31
- search_engine:, query:, results: results.size
32
+ search_engine: engine, query:, results: results.size
32
33
  })
33
34
  results.each { |url| links.add(url) }
34
35
  end
35
36
  else
36
- STDOUT.puts "Search engine #{bold{search_engine}} not implemented!"
37
+ STDOUT.puts "Search engine #{bold{engine}} not implemented!"
37
38
  nil
38
39
  end
39
40
  end
data/lib/ollama_chat.rb CHANGED
@@ -20,9 +20,9 @@ module OllamaChat
20
20
  #
21
21
  # The error carries the offending `#path` as an attribute so that
22
22
  # callers can log or display the problematic location.
23
- #
24
- # @attr [String] path The path that caused the error.
25
23
  class InvalidPathError < OllamaChatError
24
+ # @!attribute [r] path
25
+ # @return [String] The path that caused the error.
26
26
  attr_accessor :path
27
27
  end
28
28
 
@@ -74,6 +74,24 @@ module OllamaChat
74
74
  #
75
75
  # @return [Regexp] the pattern for validating collection identifiers
76
76
  COLLECTION_NAME_REGEXP = /[-\w\.]+/
77
+
78
+ class << self
79
+ # @!attribute [w] test_mode
80
+ # @return [Boolean] test_mode Whether test mode is enabled.
81
+ # Suppresses interactive prompts during seeding.
82
+ attr_writer :test_mode
83
+
84
+ # Checks whether test mode is enabled.
85
+ #
86
+ # When true, `AppState.seed` skips the interactive
87
+ # `confirm?` prompt and auto-accepts new prompt fingerprints.
88
+ #
89
+ # @return [Boolean] true if test mode is enabled
90
+ def test_mode?
91
+ !!@test_mode
92
+ end
93
+ end
94
+ self.test_mode = false
77
95
  end
78
96
 
79
97
  require 'ollama'
data/ollama_chat.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama_chat 0.0.109 ruby lib
2
+ # stub: ollama_chat 0.0.110 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama_chat".freeze
6
- s.version = "0.0.109".freeze
6
+ s.version = "0.0.110".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/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/config_handling_spec.rb".freeze, "spec/ollama_chat/conversation_spec.rb".freeze, "spec/ollama_chat/database/models/favourite_spec.rb".freeze, "spec/ollama_chat/favourites_management_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/prompt_handling_spec.rb".freeze, "spec/ollama_chat/prompt_management_spec.rb".freeze, "spec/ollama_chat/rag_handling_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/system_prompt_management_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/migrations/009_add_app_states_table.rb".freeze, "lib/ollama_chat/database/models/app_state.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/migrations/009_add_app_states_table.rb".freeze, "lib/ollama_chat/database/models/app_state.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/config_handling_spec.rb".freeze, "spec/ollama_chat/conversation_spec.rb".freeze, "spec/ollama_chat/database/models/favourite_spec.rb".freeze, "spec/ollama_chat/favourites_management_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/prompt_handling_spec.rb".freeze, "spec/ollama_chat/prompt_management_spec.rb".freeze, "spec/ollama_chat/rag_handling_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/system_prompt_management_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]
@@ -461,4 +461,114 @@ describe OllamaChat::PromptManagement do
461
461
  expect(chat.determine_valid_new_name_for_prompt('to add')).to be_nil
462
462
  end
463
463
  end
464
+
465
+ describe '#prompt_sync' do
466
+ it 'reports all in sync when no drift or orphans' do
467
+ expect(chat.config.prompts).to receive(:[]).with('prompt')
468
+ .and_return({})
469
+ expect(chat).to receive(:each_prompt)
470
+ .with(context: 'prompt', default: true).and_return([])
471
+
472
+ expect(STDOUT).to receive(:puts).with(/in sync/)
473
+ expect(chat.prompt_sync).to eq(chat)
474
+ end
475
+
476
+ it 'detects drifted prompts and calls show_prompt_diff' do
477
+ p = prompt_model.create(context: 'prompt', name: 'zz_drifted',
478
+ metadata: { default: true, content: 'local' })
479
+ expect(chat.config.prompts).to receive(:[]).with('prompt')
480
+ .and_return('zz_drifted' => 'shipped')
481
+ expect(chat).to receive(:each_prompt)
482
+ .with(context: 'prompt', default: true).and_return([p])
483
+
484
+ expect(chat).to receive(:confirm?)
485
+ .with(hash_including(prompt: /Press any key/)).twice.and_return(true)
486
+ expect(chat).to receive(:show_prompt_diff)
487
+ .with(p, 'shipped', context: 'prompt')
488
+ expect(chat.prompt_sync).to eq(chat)
489
+ end
490
+
491
+ it 'detects orphaned prompts and cleans up on confirm' do
492
+ p = prompt_model.create(context: 'prompt', name: 'zz_orphan',
493
+ metadata: { default: true, content: 'x' })
494
+ expect(chat.config.prompts).to receive(:[]).with('prompt')
495
+ .and_return({})
496
+ expect(chat).to receive(:each_prompt)
497
+ .with(context: 'prompt', default: true).and_return([p])
498
+
499
+ expect(chat).to receive(:confirm?)
500
+ .with(hash_including(prompt: /Press any key/)).twice.and_return(true)
501
+ expect(chat).to receive(:confirm?)
502
+ .with(hash_including(prompt: /Remove/)).and_return(true)
503
+ expect { chat.prompt_sync }.to change { prompt_model.count }.by(-1)
504
+ end
505
+
506
+ it 'keeps orphans when user declines cleanup' do
507
+ p = prompt_model.create(context: 'prompt', name: 'zz_orphan',
508
+ metadata: { default: true, content: 'x' })
509
+ expect(chat.config.prompts).to receive(:[]).with('prompt')
510
+ .and_return({})
511
+ expect(chat).to receive(:each_prompt)
512
+ .with(context: 'prompt', default: true).and_return([p])
513
+
514
+ expect(chat).to receive(:confirm?)
515
+ .with(hash_including(prompt: /Press any key/)).twice.and_return(true)
516
+ expect(chat).to receive(:confirm?)
517
+ .with(hash_including(prompt: /Remove/)).and_return(false)
518
+ expect { chat.prompt_sync }.not_to change { prompt_model.count }
519
+ end
520
+
521
+ it 'handles both drifted and orphaned prompts' do
522
+ drifted = prompt_model.create(context: 'prompt', name: 'zz_drifted',
523
+ metadata: { default: true, content: 'local' })
524
+ orphan = prompt_model.create(context: 'prompt', name: 'zz_orphan',
525
+ metadata: { default: true, content: 'x' })
526
+ expect(chat.config.prompts).to receive(:[]).with('prompt')
527
+ .and_return('zz_drifted' => 'shipped')
528
+ expect(chat).to receive(:each_prompt)
529
+ .with(context: 'prompt', default: true).and_return([drifted, orphan])
530
+
531
+ expect(chat).to receive(:confirm?)
532
+ .with(hash_including(prompt: /Press any key/)).twice.and_return(true)
533
+ expect(chat).to receive(:show_prompt_diff)
534
+ .with(drifted, 'shipped', context: 'prompt')
535
+ expect(chat).to receive(:confirm?)
536
+ .with(hash_including(prompt: /Remove/)).and_return(true)
537
+
538
+ expect { chat.prompt_sync }
539
+ .to change { prompt_model.where(name: 'zz_orphan').count }
540
+ .from(1).to(0)
541
+ end
542
+ end
543
+
544
+ describe '#show_prompt_diff' do
545
+ it 'displays diff and skips resolution when declined' do
546
+ p = prompt_model.create(context: 'prompt', name: 'zz_diff_me',
547
+ metadata: { default: true, content: 'local' })
548
+
549
+ expect(chat).to receive(:confirm?).and_return(false)
550
+ expect { chat.show_prompt_diff(p, 'shipped', context: 'prompt') }
551
+ .not_to raise_error
552
+ end
553
+
554
+ it 'updates prompt when resolved content differs' do
555
+ p = prompt_model.create(context: 'prompt', name: 'zz_resolve',
556
+ metadata: { default: true, content: 'before' })
557
+
558
+ expect(chat).to receive(:confirm?).and_return(true)
559
+ expect(OC).to receive(:DIFF_TOOL?).and_return(%w[vimdiff])
560
+ # Simulate vimdiff editing file_a by wrapping system to write to it
561
+ expect(chat).to receive(:system) do |*args|
562
+ file_a = args[-2]
563
+ File.write(file_a, 'after resolution')
564
+ true
565
+ end
566
+
567
+ expect(chat).to receive(:write_prompt)
568
+ .with('zz_resolve', 'after resolution', context: 'prompt')
569
+
570
+ expect { chat.show_prompt_diff(p, 'default', context: 'prompt') }
571
+ .not_to raise_error
572
+ end
573
+ end
464
574
  end
@@ -11,26 +11,26 @@ describe OllamaChat::WebSearching do
11
11
 
12
12
  describe '#search_web' do
13
13
  it 'returns nil and warns for an unimplemented engine' do
14
- allow(chat).to receive(:search_engine).and_return('nonexistent')
14
+ expect(chat).to receive(:search_engine).and_return('nonexistent')
15
15
  expect(STDOUT).to receive(:puts).with(/not implemented/)
16
16
  expect(chat.search_web('ruby')).to be_nil
17
17
  end
18
18
 
19
19
  it 'delegates to the engine method and collects links' do
20
- allow(chat).to receive(:search_engine).and_return('duckduckgo')
20
+ expect(chat).to receive(:search_engine).and_return('duckduckgo')
21
21
  urls = %w[https://a.example https://b.example]
22
22
  expect(chat).to receive(:search_web_with_duckduckgo)
23
23
  .with(/ruby/, 2).and_return(urls)
24
- allow(chat).to receive(:log)
24
+ expect(chat).to receive(:log).at_least(:once)
25
25
  expect(chat.links).to receive(:add).twice
26
26
  expect(chat.search_web('ruby', 2)).to eq(urls)
27
27
  end
28
28
 
29
29
  it 'clamps n below 1 to a minimum of 1' do
30
- allow(chat).to receive(:search_engine).and_return('duckduckgo')
30
+ expect(chat).to receive(:search_engine).and_return('duckduckgo')
31
31
  expect(chat).to receive(:search_web_with_duckduckgo)
32
32
  .with(/ruby/, 1).and_return(%w[https://a.example])
33
- allow(chat).to receive(:log)
33
+ expect(chat).to receive(:log).at_least(:once)
34
34
  chat.search_web('ruby', 0)
35
35
  end
36
36
  end
@@ -38,28 +38,26 @@ describe OllamaChat::WebSearching do
38
38
  describe '#web' do
39
39
  let(:urls) { %w[https://a.example https://b.example] }
40
40
 
41
- before { allow(chat).to receive(:log) }
42
-
43
41
  it 'returns :next when no search results are found' do
44
- allow(chat).to receive(:search_web).and_return(nil)
42
+ expect(chat).to receive(:search_web).and_return(nil)
45
43
  expect(chat.web('1', 'ruby')).to be(:next)
46
44
  end
47
45
 
48
46
  context 'when policy is embedding and embedding is on' do
49
47
  before do
50
- allow(chat.document_policy).to receive(:selected).
51
- and_return('embedding')
52
- allow(chat.instance_variable_get(:@embedding)).
48
+ expect(chat.document_policy).to receive(:selected).
49
+ at_least(:once).and_return('embedding')
50
+ expect(chat.instance_variable_get(:@embedding)).
53
51
  to receive(:on?).and_return(true)
54
52
  end
55
53
 
56
54
  it 'embeds each source and interpolates the web_embed prompt' do
57
- allow(chat).to receive(:prompt).with(:web_embed).
55
+ expect(chat).to receive(:prompt).with(:web_embed).
58
56
  and_return(double('prompt', to_s: 'Search for: %{query}'))
59
- allow(chat).to receive(:search_web).and_return(urls)
60
- allow(chat).to receive(:fetch_source) do |_url, &block|
61
- block&.call(nil)
62
- end
57
+ expect(chat).to receive(:search_web).and_return(urls)
58
+ expect(chat).to receive(:fetch_source).twice do |_url, &block|
59
+ block&.call(nil)
60
+ end
63
61
  expect(chat).to receive(:embed_source).twice
64
62
  result = chat.web('2', 'ruby tutorials')
65
63
  expect(result).to eq('Search for: ruby tutorials')
@@ -68,15 +66,16 @@ describe OllamaChat::WebSearching do
68
66
 
69
67
  context 'when policy is summarizing' do
70
68
  before do
71
- allow(chat.document_policy).to receive(:selected).
72
- and_return('summarizing')
69
+ expect(chat.document_policy).to receive(:selected).
70
+ at_least(:once).and_return('summarizing')
73
71
  end
74
72
 
75
73
  it 'summarizes each URL and interpolates the prompt' do
76
- allow(chat).to receive(:prompt).with(:web_import).
74
+ expect(chat).to receive(:prompt).with(:web_import).
77
75
  and_return(double('prompt', to_s: 'Q: %{query} R: %{results}'))
78
- allow(chat).to receive(:search_web).and_return(urls)
79
- allow(chat).to receive(:summarize).and_return('summary text')
76
+ expect(chat).to receive(:search_web).and_return(urls)
77
+ expect(chat).to receive(:summarize).twice
78
+ .and_return('summary text')
80
79
  result = chat.web('2', 'ruby')
81
80
  expect(result).to eq('Q: ruby R: summary textsummary text')
82
81
  end
@@ -84,15 +83,16 @@ describe OllamaChat::WebSearching do
84
83
 
85
84
  context 'when policy is importing (default)' do
86
85
  before do
87
- allow(chat.document_policy).to receive(:selected).
88
- and_return('importing')
86
+ expect(chat.document_policy).to receive(:selected).
87
+ at_least(:once).and_return('importing')
89
88
  end
90
89
 
91
90
  it 'imports each URL and interpolates the prompt' do
92
- allow(chat).to receive(:prompt).with(:web_summarize).
91
+ expect(chat).to receive(:prompt).with(:web_summarize).
93
92
  and_return(double('prompt', to_s: 'Q: %{query} R: %{results}'))
94
- allow(chat).to receive(:search_web).and_return(urls)
95
- allow(chat).to receive(:import).and_return('imported text')
93
+ expect(chat).to receive(:search_web).and_return(urls)
94
+ expect(chat).to receive(:import).twice
95
+ .and_return('imported text')
96
96
  result = chat.web('2', 'ruby')
97
97
  expect(result).to eq('Q: ruby R: imported textimported text')
98
98
  end
data/spec/spec_helper.rb CHANGED
@@ -213,6 +213,7 @@ RSpec.configure do |config|
213
213
  end
214
214
 
215
215
  config.before do
216
+ OllamaChat.test_mode = true
216
217
  const_conf_as(
217
218
  'OC::OLLAMA::CHAT::HISTORY' => Pathname.pwd.join('tmp', 'history.json'),
218
219
  'OC::OLLAMA::CHAT::LOGFILE' => Pathname.pwd.join('tmp', 'chat.log'),
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.109
4
+ version: 0.0.110
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -497,6 +497,8 @@ extra_rdoc_files:
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
499
  - lib/ollama_chat/database/migrations/008_drop_think_mode_constraint.rb
500
+ - lib/ollama_chat/database/migrations/009_add_app_states_table.rb
501
+ - lib/ollama_chat/database/models/app_state.rb
500
502
  - lib/ollama_chat/database/models/collection.rb
501
503
  - lib/ollama_chat/database/models/favourite.rb
502
504
  - lib/ollama_chat/database/models/model_options.rb
@@ -626,6 +628,8 @@ files:
626
628
  - lib/ollama_chat/database/migrations/006_rename_system_prompt_context_to_system.rb
627
629
  - lib/ollama_chat/database/migrations/007_add_collections_table.rb
628
630
  - lib/ollama_chat/database/migrations/008_drop_think_mode_constraint.rb
631
+ - lib/ollama_chat/database/migrations/009_add_app_states_table.rb
632
+ - lib/ollama_chat/database/models/app_state.rb
629
633
  - lib/ollama_chat/database/models/collection.rb
630
634
  - lib/ollama_chat/database/models/favourite.rb
631
635
  - lib/ollama_chat/database/models/model_options.rb