ollama_chat 0.0.76 → 0.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +38 -0
- data/Rakefile +1 -1
- data/lib/ollama_chat/chat.rb +2 -16
- data/lib/ollama_chat/information.rb +44 -1
- data/lib/ollama_chat/input_content.rb +1 -1
- data/lib/ollama_chat/model_handling.rb +3 -7
- data/lib/ollama_chat/oc.rb +8 -1
- data/lib/ollama_chat/ollama_chat_config/default_config.yml +23 -10
- data/lib/ollama_chat/personae_management.rb +13 -2
- data/lib/ollama_chat/tools/directory_structure.rb +2 -2
- data/lib/ollama_chat/tools/get_current_weather.rb +2 -2
- data/lib/ollama_chat/tools/{insert_into_editor.rb → paste_into_editor.rb} +12 -12
- data/lib/ollama_chat/tools/patch_file.rb +101 -0
- data/lib/ollama_chat/tools/run_tests.rb +15 -9
- data/lib/ollama_chat/tools.rb +2 -1
- data/lib/ollama_chat/utils/chooser.rb +7 -3
- data/lib/ollama_chat/version.rb +1 -1
- data/ollama_chat.gemspec +6 -6
- data/spec/ollama_chat/chat_spec.rb +2 -3
- data/spec/ollama_chat/clipboard_spec.rb +0 -2
- data/spec/ollama_chat/follow_chat_spec.rb +0 -2
- data/spec/ollama_chat/information_spec.rb +0 -2
- data/spec/ollama_chat/input_content_spec.rb +0 -2
- data/spec/ollama_chat/kramdown_ansi_spec.rb +0 -2
- data/spec/ollama_chat/message_editing_spec.rb +0 -2
- data/spec/ollama_chat/message_list_spec.rb +0 -2
- data/spec/ollama_chat/message_output_spec.rb +0 -2
- data/spec/ollama_chat/model_handling_spec.rb +0 -2
- data/spec/ollama_chat/parsing_spec.rb +0 -3
- data/spec/ollama_chat/redis_cache_spec.rb +0 -2
- data/spec/ollama_chat/server_socket_spec.rb +0 -2
- data/spec/ollama_chat/source_fetching_spec.rb +0 -2
- data/spec/ollama_chat/state_selectors_spec.rb +0 -2
- data/spec/ollama_chat/switches_spec.rb +0 -2
- data/spec/ollama_chat/think_control_spec.rb +0 -2
- data/spec/ollama_chat/tools/browse_spec.rb +0 -2
- data/spec/ollama_chat/tools/copy_to_clipboard_spec.rb +0 -2
- data/spec/ollama_chat/tools/directory_structure_spec.rb +0 -2
- data/spec/ollama_chat/tools/execute_grep_spec.rb +0 -2
- data/spec/ollama_chat/tools/execute_ri_spec.rb +0 -2
- data/spec/ollama_chat/tools/file_context_spec.rb +0 -2
- data/spec/ollama_chat/tools/gem_path_lookup_spec.rb +0 -2
- data/spec/ollama_chat/tools/generate_password_spec.rb +0 -2
- data/spec/ollama_chat/tools/get_current_weather_spec.rb +0 -2
- data/spec/ollama_chat/tools/get_cve_spec.rb +0 -2
- data/spec/ollama_chat/tools/get_endoflife_spec.rb +0 -2
- data/spec/ollama_chat/tools/get_jira_issue_spec.rb +0 -2
- data/spec/ollama_chat/tools/get_location_spec.rb +0 -2
- data/spec/ollama_chat/tools/get_rfc_spec.rb +0 -2
- data/spec/ollama_chat/tools/get_time_spec.rb +0 -2
- data/spec/ollama_chat/tools/get_url_spec.rb +0 -2
- data/spec/ollama_chat/tools/open_file_in_editor_spec.rb +0 -2
- data/spec/ollama_chat/tools/paste_from_clipboard_spec.rb +0 -2
- data/spec/ollama_chat/tools/{insert_into_editor_spec.rb → paste_into_editor_spec.rb} +9 -11
- data/spec/ollama_chat/tools/patch_file_spec.rb +155 -0
- data/spec/ollama_chat/tools/read_file_spec.rb +0 -2
- data/spec/ollama_chat/tools/resolve_tag_spec.rb +0 -2
- data/spec/ollama_chat/tools/run_tests_spec.rb +24 -9
- data/spec/ollama_chat/tools/search_web_spec.rb +0 -2
- data/spec/ollama_chat/tools/write_file_spec.rb +0 -2
- data/spec/ollama_chat/utils/analyze_directory_spec.rb +0 -4
- data/spec/ollama_chat/utils/cache_fetcher_spec.rb +0 -2
- data/spec/ollama_chat/utils/fetcher_spec.rb +0 -2
- data/spec/ollama_chat/utils/file_argument_spec.rb +0 -2
- data/spec/ollama_chat/vim_spec.rb +0 -2
- data/spec/ollama_chat/web_searching_spec.rb +0 -2
- metadata +11 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8e531dd87a2f08ae4d6be00bdf3aef50e29c29405c37ddd439ffa4ad69cf4a22
|
|
4
|
+
data.tar.gz: 2187cd9c3bd2ae6222ec705e0f0d8c8c143f83f51b282e36951b3ada0d8609d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aae64c9b513f65a31568ceb0b73a9ca8acfff96a82a5940ef18fbb568f41346ad3db1c9eb123d98cc969974c7709e840df21e985b7ef4077f948ea0e38db5ab1
|
|
7
|
+
data.tar.gz: db40e853d4f39ad45677da690748b3f85b239cff3faf330acae44a4dc95961551d1f7c2b275e3aecd73c625372f9fc369de0a3decd17e55e1b7f6981772517c2
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-03-11 v0.0.78
|
|
4
|
+
|
|
5
|
+
- Added new `patch_file` tool for applying unified diff patches to files with
|
|
6
|
+
safety checks, including specs and integration into the tool system.
|
|
7
|
+
- Updated `DirectoryStructure` tool description to clarify that `max_depth`
|
|
8
|
+
limits the tree height for better LLM understanding.
|
|
9
|
+
- Bumped `search_ui` gem dependency from **0.0** to **0.1**, refactored model
|
|
10
|
+
handling using `SearchUI::Wrapper.new` and updated chooser logic for safer
|
|
11
|
+
method dispatch.
|
|
12
|
+
- Enhanced `run_tests` tool with a path validation whitelist in
|
|
13
|
+
`default_config.yml`, introducing `OllamaChat::Utils::PathValidator` and
|
|
14
|
+
updating `check_path` method to validate against the allowed list.
|
|
15
|
+
- Simplified runtime information handling by adding new helper methods:
|
|
16
|
+
`runtime_information_values`, `runtime_information`, and updated `chat.rb` to
|
|
17
|
+
use these for displaying structured runtime data.
|
|
18
|
+
- Added default persona name display using the `default_persona_name` helper,
|
|
19
|
+
updating information output and refactoring `reload_default_persona` guard
|
|
20
|
+
logic.
|
|
21
|
+
|
|
22
|
+
## 2026-03-09 v0.0.77
|
|
23
|
+
|
|
24
|
+
- Add runtime Git and time placeholders: added `git_current_branch` and
|
|
25
|
+
`git_remote_origin` to `runtime_info_values` in `lib/ollama_chat/chat.rb`;
|
|
26
|
+
inserted `%{time}` placeholder into prompts section of `default_config.yml`;
|
|
27
|
+
added Git placeholders (`%{git_current_branch}`, `%{git_remote_origin}`)
|
|
28
|
+
under **Git** key; reformatted terminal info under a single **Terminal**
|
|
29
|
+
heading with height and width.
|
|
30
|
+
- Extend weather tool to include six‑day forecast: updated `GetCurrentWeather`
|
|
31
|
+
description to mention a six‑day forecast; functionality unchanged; tool
|
|
32
|
+
still requires no arguments.
|
|
33
|
+
- Rename `insert_into_editor` tool to `paste_into_editor`: updated
|
|
34
|
+
`lib/ollama_chat/tools.rb` to require `paste_into_editor`; renamed and
|
|
35
|
+
updated class from `InsertIntoEditor` to `PasteIntoEditor` in
|
|
36
|
+
`paste_into_editor.rb`, changing its register name, description text, and
|
|
37
|
+
method logic; switched default config key in `default_config.yml` from
|
|
38
|
+
`insert_into_editor` to `paste_into_editor`; refactored spec files; adjusted
|
|
39
|
+
all internal references.
|
|
40
|
+
|
|
3
41
|
## 2026-03-09 v0.0.76
|
|
4
42
|
|
|
5
43
|
- Added `client:` and `current_directory:` keys to `runtime_info_values` in
|
data/Rakefile
CHANGED
|
@@ -52,7 +52,7 @@ GemHadar do
|
|
|
52
52
|
dependency 'kramdown-ansi', '~> 0.3'
|
|
53
53
|
dependency 'complex_config', '~> 0.22', '>= 0.22.2'
|
|
54
54
|
dependency 'tins', '~> 1.52'
|
|
55
|
-
dependency 'search_ui', '~> 0.
|
|
55
|
+
dependency 'search_ui', '~> 0.1'
|
|
56
56
|
dependency 'amatch', '~> 0.4'
|
|
57
57
|
dependency 'pdf-reader', '~> 2.0'
|
|
58
58
|
dependency 'bigdecimal', '~> 3.1'
|
data/lib/ollama_chat/chat.rb
CHANGED
|
@@ -193,7 +193,7 @@ class OllamaChat::Chat
|
|
|
193
193
|
end
|
|
194
194
|
|
|
195
195
|
# The disable_content_parsing method turns off content parsing by setting
|
|
196
|
-
#
|
|
196
|
+
# `@parse_content` to false.
|
|
197
197
|
#
|
|
198
198
|
# This prevents automatic parsing of user input content during chat
|
|
199
199
|
# processing.
|
|
@@ -707,21 +707,7 @@ class OllamaChat::Chat
|
|
|
707
707
|
end
|
|
708
708
|
end
|
|
709
709
|
|
|
710
|
-
if runtime_info.on? && content
|
|
711
|
-
runtime_info_values = {
|
|
712
|
-
languages: config.languages * ', ',
|
|
713
|
-
location: location.on? ? location_description.inspect : 'n/a',
|
|
714
|
-
client: ,
|
|
715
|
-
current_directory: Pathname.pwd.expand_path.to_path.inspect,
|
|
716
|
-
terminal_rows: Tins::Terminal.rows,
|
|
717
|
-
terminal_cols: Tins::Terminal.cols,
|
|
718
|
-
time: Time.now.iso8601,
|
|
719
|
-
voice: voice.on? ? 'enabled' : 'disabled',
|
|
720
|
-
markdown: markdown.on? ? 'enabled' : 'disabled',
|
|
721
|
-
tool_paths_allowed: JSON(tool_paths_allowed),
|
|
722
|
-
}
|
|
723
|
-
content << config.prompts.runtime_info % runtime_info_values
|
|
724
|
-
end
|
|
710
|
+
content << runtime_information if runtime_info.on? && content
|
|
725
711
|
|
|
726
712
|
messages << Ollama::Message.new(role: 'user', content:, images: @images.dup)
|
|
727
713
|
@images.clear
|
|
@@ -112,13 +112,20 @@ module OllamaChat::Information
|
|
|
112
112
|
think_mode.show
|
|
113
113
|
think_loud.show
|
|
114
114
|
location.show
|
|
115
|
-
runtime_info.show
|
|
116
115
|
voice.show
|
|
117
116
|
@voice.on? and @voices.show
|
|
117
|
+
if runtime_info.on?
|
|
118
|
+
STDOUT.puts "Runtime Information:"
|
|
119
|
+
STDOUT.puts runtime_information_values.transform_keys(&:to_s).to_yaml.
|
|
120
|
+
sub(/\A---\s*\n/, '').gsub(/^/, ' ')
|
|
121
|
+
else
|
|
122
|
+
runtime_info.show
|
|
123
|
+
end
|
|
118
124
|
tools_support.show
|
|
119
125
|
STDOUT.puts "Documents database cache is #{@documents.nil? ? 'n/a' : bold{@documents.cache.class}}"
|
|
120
126
|
STDOUT.puts "Document policy for references in user text: #{bold{document_policy}}"
|
|
121
127
|
STDOUT.puts "Currently selected search engine is #{bold(search_engine)}."
|
|
128
|
+
name = default_persona_name and STDOUT.puts "Default persona: #{bold{name}}"
|
|
122
129
|
STDOUT.puts "Conversation length: #{bold(@messages.size.to_s)} message(s)."
|
|
123
130
|
nil
|
|
124
131
|
end
|
|
@@ -231,4 +238,40 @@ module OllamaChat::Information
|
|
|
231
238
|
def server_url
|
|
232
239
|
@server_url ||= ollama.base_url
|
|
233
240
|
end
|
|
241
|
+
|
|
242
|
+
# The runtime_information_values method compiles a set of key runtime details
|
|
243
|
+
# such as languages, location description, default persona name, current git
|
|
244
|
+
# branch and remote origin, client agent string, current directory, terminal
|
|
245
|
+
# dimensions, timestamp, voice and markdown status, and allowed tool paths.
|
|
246
|
+
#
|
|
247
|
+
# @return [Hash] a hash containing runtime information values.
|
|
248
|
+
def runtime_information_values
|
|
249
|
+
{
|
|
250
|
+
languages: config.languages * ', ',
|
|
251
|
+
time: Time.now.iso8601,
|
|
252
|
+
location: location.on?.full? { location_description } || 'n/a',
|
|
253
|
+
default_persona: default_persona_name.full? || 'n/a',
|
|
254
|
+
git_current_branch: `git rev-parse --abbrev-ref HEAD 2>/dev/null`.chomp.full? || 'n/a',
|
|
255
|
+
git_remote_origin: `git remote get-url origin 2>/dev/null`.chomp.full? || 'n/a',
|
|
256
|
+
client: ,
|
|
257
|
+
current_directory: Pathname.pwd.expand_path.to_path,
|
|
258
|
+
terminal_rows: Tins::Terminal.rows,
|
|
259
|
+
terminal_cols: Tins::Terminal.cols,
|
|
260
|
+
voice: voice.on? ? 'enabled' : 'disabled',
|
|
261
|
+
markdown: markdown.on? ? 'enabled' : 'disabled',
|
|
262
|
+
tool_paths_allowed: JSON.pretty_generate(tool_paths_allowed),
|
|
263
|
+
}
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# The runtime_information method generates a formatted string containing
|
|
267
|
+
# various runtime values such as languages, location description, git branch
|
|
268
|
+
# and origin, client agent, current directory, terminal size, time, voice and
|
|
269
|
+
# markdown status, and allowed tool paths.
|
|
270
|
+
# It returns the result of interpolating these values into the configured
|
|
271
|
+
# runtime_info prompt template.
|
|
272
|
+
#
|
|
273
|
+
# @return [String] the formatted runtime information string.
|
|
274
|
+
def runtime_information
|
|
275
|
+
config.prompts.runtime_info % runtime_information_values
|
|
276
|
+
end
|
|
234
277
|
end
|
|
@@ -37,7 +37,7 @@ module OllamaChat::InputContent
|
|
|
37
37
|
# searches for files matching the given pattern, excludes already chosen
|
|
38
38
|
# files, and presents them in an interactive chooser menu.
|
|
39
39
|
#
|
|
40
|
-
# @param
|
|
40
|
+
# @param patterns [ Array<String> ] the glob patterns to search for files
|
|
41
41
|
# @param chosen [ Set ] a set of already chosen filenames to exclude from
|
|
42
42
|
# selection
|
|
43
43
|
#
|
|
@@ -94,14 +94,10 @@ module OllamaChat::ModelHandling
|
|
|
94
94
|
# @return [ Object ] a result object with an overridden to_s method
|
|
95
95
|
# that combines the model name and formatted size
|
|
96
96
|
private def model_with_size(model)
|
|
97
|
-
result = model.name
|
|
98
97
|
formatted_size = Term::ANSIColor.bold {
|
|
99
98
|
Tins::Unit.format(model.size, unit: ?B, prefix: 1024, format: '%.1f %U')
|
|
100
99
|
}
|
|
101
|
-
|
|
102
|
-
define_method(:to_s) { "%s %s" % [ model.name, formatted_size ] }
|
|
103
|
-
end
|
|
104
|
-
result
|
|
100
|
+
SearchUI::Wrapper.new(model.name, display: "#{model.name} #{formatted_size}")
|
|
105
101
|
end
|
|
106
102
|
|
|
107
103
|
# The use_model method selects and sets the model to be used for the chat
|
|
@@ -151,12 +147,12 @@ module OllamaChat::ModelHandling
|
|
|
151
147
|
Regexp.new($1)
|
|
152
148
|
end
|
|
153
149
|
models = ollama.tags.models.sort_by(&:name).map { |m| model_with_size(m) }
|
|
154
|
-
selector and models = models.
|
|
150
|
+
selector and models = models.select { _1.value =~ selector }
|
|
155
151
|
model =
|
|
156
152
|
if models.size == 1
|
|
157
153
|
models.first
|
|
158
154
|
elsif cli_model == ''
|
|
159
|
-
OllamaChat::Utils::Chooser.choose(models) || current_model
|
|
155
|
+
OllamaChat::Utils::Chooser.choose(models)&.value || current_model
|
|
160
156
|
else
|
|
161
157
|
cli_model || current_model
|
|
162
158
|
end
|
data/lib/ollama_chat/oc.rb
CHANGED
|
@@ -140,7 +140,7 @@ module OC
|
|
|
140
140
|
description 'Tool specific configuration settings'
|
|
141
141
|
|
|
142
142
|
# Run Tests tool configuration
|
|
143
|
-
|
|
143
|
+
TEST_RUNNER = set do
|
|
144
144
|
description 'Configured test runner for run_tests tool function'
|
|
145
145
|
default 'rspec'
|
|
146
146
|
required true
|
|
@@ -162,6 +162,13 @@ module OC
|
|
|
162
162
|
decode { Pathname.new(_1).expand_path }
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
+
PATCH_TOOL = set do
|
|
166
|
+
description 'Patch tool to use'
|
|
167
|
+
|
|
168
|
+
default { `which patch`.full?(:chomp) }
|
|
169
|
+
check { value.blank? || File.exist?(value) }
|
|
170
|
+
end
|
|
171
|
+
|
|
165
172
|
module JIRA
|
|
166
173
|
description 'Jira tool configuration'
|
|
167
174
|
|
|
@@ -11,7 +11,7 @@ timeouts:
|
|
|
11
11
|
languages:
|
|
12
12
|
- en-US
|
|
13
13
|
location:
|
|
14
|
-
enabled:
|
|
14
|
+
enabled: true
|
|
15
15
|
name: Berlin
|
|
16
16
|
decimal_degrees: [ 52.514127, 13.475211 ]
|
|
17
17
|
units: SI (International System of Units) # or USCS (United States Customary System)
|
|
@@ -44,15 +44,19 @@ prompts:
|
|
|
44
44
|
There is usually no reason to mention this information to the user unless
|
|
45
45
|
asked about it.
|
|
46
46
|
|
|
47
|
-
- Languages the user prefers: %{languages}
|
|
48
|
-
-
|
|
49
|
-
-
|
|
47
|
+
- Languages the user prefers: %{languages}
|
|
48
|
+
- Current time is %{time}
|
|
49
|
+
- Location: %{location}
|
|
50
|
+
- Current directory is "%{current_directory}"
|
|
51
|
+
- **Git**:
|
|
52
|
+
- Current branch is %{git_current_branch}
|
|
53
|
+
- Current remote origin is %{git_remote_origin}
|
|
50
54
|
- Client is %{client}.
|
|
51
|
-
- Terminal
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
- **Terminal**:
|
|
56
|
+
- The height is %{terminal_rows} rows.
|
|
57
|
+
- The width is %{terminal_cols} columns. **Always** aim to stay within
|
|
58
|
+
80%% of this width in your output of responses, especially when using
|
|
59
|
+
markdown tables.
|
|
56
60
|
- Markdown output is %{markdown}. **Never** output markdown as your
|
|
57
61
|
responses if it is disabled.
|
|
58
62
|
- Voice output is %{voice}. Speak naturally and short if voice
|
|
@@ -214,6 +218,10 @@ tools:
|
|
|
214
218
|
run_tests:
|
|
215
219
|
require_confirmation: true
|
|
216
220
|
default: true
|
|
221
|
+
allowed:
|
|
222
|
+
- "./spec"
|
|
223
|
+
- "./test"
|
|
224
|
+
- "./tests"
|
|
217
225
|
get_jira_issue:
|
|
218
226
|
default: false
|
|
219
227
|
get_rfc:
|
|
@@ -225,10 +233,15 @@ tools:
|
|
|
225
233
|
default: true
|
|
226
234
|
paste_from_clipboard:
|
|
227
235
|
default: true
|
|
228
|
-
|
|
236
|
+
paste_into_editor:
|
|
229
237
|
require_confirmation: true
|
|
230
238
|
default: true
|
|
231
239
|
execute_ri:
|
|
232
240
|
default: true
|
|
233
241
|
resolve_tag:
|
|
234
242
|
default: true
|
|
243
|
+
patch_file:
|
|
244
|
+
default: true
|
|
245
|
+
require_confirmation: true
|
|
246
|
+
allowed:
|
|
247
|
+
- './tmp'
|
|
@@ -55,11 +55,22 @@ module OllamaChat::PersonaeManagement
|
|
|
55
55
|
@default_persona ||= :none
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
# The default_persona_name method returns the name of the currently set
|
|
59
|
+
# default persona by extracting its basename and removing the file extension,
|
|
60
|
+
# unless no persona is set.
|
|
61
|
+
#
|
|
62
|
+
# @return [ String ] the default persona name or nil if none.
|
|
63
|
+
def default_persona_name
|
|
64
|
+
if @default_persona.present? && @default_persona != :none
|
|
65
|
+
@default_persona.basename.sub_ext('').to_s
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
58
69
|
# Reloads the default persona file if one is set and not :none, prompting the
|
|
59
70
|
# user for confirmation before playing the persona file.
|
|
60
71
|
def reload_default_persona
|
|
61
|
-
|
|
62
|
-
prompt = "Reload default persona #{
|
|
72
|
+
name = default_persona_name or return
|
|
73
|
+
prompt = "Reload default persona #{name}? (y/n) "
|
|
63
74
|
if ask?(prompt:) =~ /\Ay/i
|
|
64
75
|
play_persona_file @default_persona
|
|
65
76
|
end
|
|
@@ -26,8 +26,8 @@ class OllamaChat::Tools::DirectoryStructure
|
|
|
26
26
|
name:,
|
|
27
27
|
description: <<~EOT,
|
|
28
28
|
Tree viewer – Returns JSON describing files/folders under path up to
|
|
29
|
-
max_depth. Handy for locating resources or
|
|
30
|
-
layout.
|
|
29
|
+
max_depth (<= height of the tree). Handy for locating resources or
|
|
30
|
+
presenting a project layout.
|
|
31
31
|
EOT
|
|
32
32
|
parameters: Tool::Function::Parameters.new(
|
|
33
33
|
type: 'object',
|
|
@@ -26,8 +26,8 @@ class OllamaChat::Tools::GetCurrentWeather
|
|
|
26
26
|
name:,
|
|
27
27
|
description: <<~EOT,
|
|
28
28
|
Weather fetcher – Retrieves a short report of local meteorological
|
|
29
|
-
conditions based on your preset location coordinates.
|
|
30
|
-
needed.
|
|
29
|
+
conditions based on your preset location coordinates. This includes a
|
|
30
|
+
forecast for the next six days. No arguments needed.
|
|
31
31
|
EOT
|
|
32
32
|
parameters: Tool::Function::Parameters.new(
|
|
33
33
|
type: 'object',
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# A lightweight LLM‑driven tool that
|
|
1
|
+
# A lightweight LLM‑driven tool that pastes a snippet directly into an open
|
|
2
2
|
# Vim session.
|
|
3
|
-
class OllamaChat::Tools::
|
|
3
|
+
class OllamaChat::Tools::PasteIntoEditor
|
|
4
4
|
include OllamaChat::Tools::Concern
|
|
5
5
|
|
|
6
6
|
# Name of the tool used to register it with OllamaChat's tool registry.
|
|
7
|
-
def self.register_name = '
|
|
7
|
+
def self.register_name = 'paste_into_editor'
|
|
8
8
|
|
|
9
9
|
# Returns a `OllamaChat::Tool` instance describing this function‑based tool.
|
|
10
10
|
#
|
|
@@ -15,18 +15,18 @@ class OllamaChat::Tools::InsertIntoEditor
|
|
|
15
15
|
function: Tool::Function.new(
|
|
16
16
|
name:,
|
|
17
17
|
description: <<~EOT,
|
|
18
|
-
Editor helper –
|
|
19
|
-
into the User’s editor
|
|
18
|
+
Editor helper – Pastes a string (or last reply if omitted) straight
|
|
19
|
+
into the User’s running editor.
|
|
20
20
|
If no `text` is supplied, the tool will automatically use the last assistant
|
|
21
|
-
response.
|
|
22
|
-
|
|
21
|
+
response. Do not not call this tool function unless explicitly
|
|
22
|
+
requested by the user.
|
|
23
23
|
EOT
|
|
24
24
|
parameters: Tool::Function::Parameters.new(
|
|
25
25
|
type: 'object',
|
|
26
26
|
properties: {
|
|
27
27
|
text: Tool::Function::Parameters::Property.new(
|
|
28
28
|
type: 'string',
|
|
29
|
-
description: 'Text to
|
|
29
|
+
description: 'Text to paste into the editor (nil = last response)'
|
|
30
30
|
)
|
|
31
31
|
},
|
|
32
32
|
required: []
|
|
@@ -35,22 +35,22 @@ class OllamaChat::Tools::InsertIntoEditor
|
|
|
35
35
|
)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# Executes the tool by
|
|
38
|
+
# Executes the tool by pasting text into Vim.
|
|
39
39
|
#
|
|
40
40
|
# @param [OllamaChat::ToolCall] tool_call The LLM-generated tool call object.
|
|
41
41
|
# @option opts [OllamaChat::Chat] :chat Reference to the current chat instance.
|
|
42
42
|
# @return [String] JSON‑encoded response indicating success or failure.
|
|
43
43
|
def execute(tool_call, **opts)
|
|
44
|
-
text = tool_call.function.arguments.text
|
|
44
|
+
text = tool_call.function.arguments.text.full?
|
|
45
45
|
|
|
46
46
|
chat = opts[:chat]
|
|
47
47
|
chat.perform_insert(text:, content: true)
|
|
48
48
|
|
|
49
49
|
message =
|
|
50
50
|
if text.nil?
|
|
51
|
-
"The last response has been successfully
|
|
51
|
+
"The last response has been successfully pasted into the editor."
|
|
52
52
|
else
|
|
53
|
-
"The provided text has been successfully
|
|
53
|
+
"The provided text has been successfully pasted into the editor."
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
{
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# A tool for applying unified diffs to files.
|
|
2
|
+
#
|
|
3
|
+
# This tool enables the chat client to apply patch content (unified diff format)
|
|
4
|
+
# directly to existing files on the local filesystem. It supports both overwriting
|
|
5
|
+
# and appending modes, with configurable file permissions and safety checks to prevent
|
|
6
|
+
# writing to unauthorized locations.
|
|
7
|
+
class OllamaChat::Tools::PatchFile
|
|
8
|
+
include OllamaChat::Tools::Concern
|
|
9
|
+
include OllamaChat::Utils::PathValidator
|
|
10
|
+
|
|
11
|
+
def self.register_name = 'patch_file'
|
|
12
|
+
|
|
13
|
+
# The tool method creates and returns a tool definition for applying patches
|
|
14
|
+
# to files.
|
|
15
|
+
#
|
|
16
|
+
# @return [Ollama::Tool] a tool definition for patching content in files
|
|
17
|
+
def tool
|
|
18
|
+
Tool.new(
|
|
19
|
+
type: 'function',
|
|
20
|
+
function: Tool::Function.new(
|
|
21
|
+
name:,
|
|
22
|
+
description: <<~EOT,
|
|
23
|
+
Patch applicator – Applies unified diff patches to existing files.
|
|
24
|
+
Path must be allowed; no return value.
|
|
25
|
+
EOT
|
|
26
|
+
parameters: Tool::Function::Parameters.new(
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
path: Tool::Function::Parameters::Property.new(
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'The path to the file to patch (must be within allowed directories)'
|
|
32
|
+
),
|
|
33
|
+
diff_content: Tool::Function::Parameters::Property.new(
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'Unified diff content to apply'
|
|
36
|
+
)
|
|
37
|
+
},
|
|
38
|
+
required: %w[path diff_content]
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The execute method processes a tool call to patch a file.
|
|
45
|
+
#
|
|
46
|
+
# This method handles applying unified diffs (like those from git) to files
|
|
47
|
+
# on the local filesystem. It validates that the target path is within
|
|
48
|
+
# allowed directories and ensures the parent directory exists before writing.
|
|
49
|
+
#
|
|
50
|
+
# @param tool_call [Ollama::Tool::Call] the tool call containing function details
|
|
51
|
+
# @param opts [Hash] additional options
|
|
52
|
+
# @option opts [ComplexConfig::Settings] :config the configuration object
|
|
53
|
+
#
|
|
54
|
+
# @return [String] the result of the patch operation as a JSON string
|
|
55
|
+
# @return [String] a JSON string containing error information if the operation fails
|
|
56
|
+
def execute(tool_call, **opts)
|
|
57
|
+
config = opts[:config]
|
|
58
|
+
args = tool_call.function.arguments
|
|
59
|
+
|
|
60
|
+
diff_content = args.diff_content.full? or
|
|
61
|
+
raise ArgumentError, 'require diff_content to patch with'
|
|
62
|
+
|
|
63
|
+
path = assert_valid_path(args.path, config.tools.functions.patch_file.allowed?)
|
|
64
|
+
|
|
65
|
+
path.exist? or
|
|
66
|
+
raise Errno::ENOENT, 'file to patch does not exist %s' % path.to_s.inspect
|
|
67
|
+
|
|
68
|
+
result = apply_patch(path, diff_content)
|
|
69
|
+
|
|
70
|
+
{
|
|
71
|
+
success: true,
|
|
72
|
+
path: path.to_s,
|
|
73
|
+
message: "Successfully applied patch to #{path}",
|
|
74
|
+
result: ,
|
|
75
|
+
}.to_json
|
|
76
|
+
rescue => e
|
|
77
|
+
{
|
|
78
|
+
error: e.class,
|
|
79
|
+
message: "Failed to apply patch to file: #{e.message}",
|
|
80
|
+
result: ,
|
|
81
|
+
}.to_json
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# Apply the unified diff content to a target file.
|
|
87
|
+
#
|
|
88
|
+
# @param path [Pathname] The file path to patch
|
|
89
|
+
# @param diff_content [String] Unified diff format string
|
|
90
|
+
def apply_patch(path, diff_content)
|
|
91
|
+
cmd = Shellwords.join([ OC::OLLAMA::CHAT::TOOLS::PATCH_TOOL, '-u', '-f', path ])
|
|
92
|
+
cmd << " 2>&1"
|
|
93
|
+
IO.popen(cmd, 'r+') do |output|
|
|
94
|
+
output.puts diff_content
|
|
95
|
+
output.close_write
|
|
96
|
+
return output.read
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
self
|
|
101
|
+
end.register
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# runner (RSpec or Minitest) and streams its output back to the caller.
|
|
7
7
|
class OllamaChat::Tools::RunTests
|
|
8
8
|
include OllamaChat::Tools::Concern
|
|
9
|
+
include OllamaChat::Utils::PathValidator
|
|
9
10
|
|
|
10
11
|
# Register the tool name used by the OllamaChat runtime.
|
|
11
12
|
# @return [String]
|
|
@@ -29,7 +30,7 @@ class OllamaChat::Tools::RunTests
|
|
|
29
30
|
properties: {
|
|
30
31
|
path: Tool::Function::Parameters::Property.new(
|
|
31
32
|
type: 'string',
|
|
32
|
-
description: 'Path to file or directory to run tests for (path
|
|
33
|
+
description: 'Path to file or directory to run tests for (path has to be allowed!)'
|
|
33
34
|
),
|
|
34
35
|
coverage: Tool::Function::Parameters::Property.new(
|
|
35
36
|
type: 'boolean',
|
|
@@ -48,9 +49,10 @@ class OllamaChat::Tools::RunTests
|
|
|
48
49
|
# @param opts [Hash] additional options (currently unused)
|
|
49
50
|
# @return [String] JSON containing ``success``, ``path``, ``output`` and ``status``
|
|
50
51
|
def execute(tool_call, **opts)
|
|
52
|
+
config = opts[:config]
|
|
51
53
|
path = tool_call.function.arguments.path
|
|
52
54
|
coverage = tool_call.function.arguments.coverage || false
|
|
53
|
-
check_path
|
|
55
|
+
check_path(path, config)
|
|
54
56
|
output, success = run_tests(path, coverage)
|
|
55
57
|
{
|
|
56
58
|
success:,
|
|
@@ -64,12 +66,14 @@ class OllamaChat::Tools::RunTests
|
|
|
64
66
|
|
|
65
67
|
private
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
# The check_path method determines the appropriate test directory path based
|
|
70
|
+
# on existing directories and validates it against a whitelist.
|
|
71
|
+
#
|
|
72
|
+
# @param path [ String ] the initial path to be checked
|
|
73
|
+
# @param config [ Object ] configuration object containing tool function settings
|
|
74
|
+
# @return [ String ] the validated and existing path
|
|
75
|
+
def check_path(path, config)
|
|
76
|
+
if path.blank?
|
|
73
77
|
if File.exist?('./spec')
|
|
74
78
|
path = './spec'
|
|
75
79
|
elsif File.exist?('./test')
|
|
@@ -80,6 +84,8 @@ class OllamaChat::Tools::RunTests
|
|
|
80
84
|
raise ArgumentError, 'path could not be determined'
|
|
81
85
|
end
|
|
82
86
|
end
|
|
87
|
+
assert_valid_path(path, config.tools.functions.run_tests.allowed?)
|
|
88
|
+
File.exist?(path) or raise Errno::ENOENT, 'path %s does not exist' % path.inspect
|
|
83
89
|
end
|
|
84
90
|
|
|
85
91
|
# Run the test suite using the configured test runner.
|
|
@@ -103,7 +109,7 @@ class OllamaChat::Tools::RunTests
|
|
|
103
109
|
#
|
|
104
110
|
# @return [String] the command to invoke the test runner
|
|
105
111
|
def test_runner
|
|
106
|
-
OC::OLLAMA::CHAT::TOOLS::
|
|
112
|
+
OC::OLLAMA::CHAT::TOOLS::TEST_RUNNER
|
|
107
113
|
end
|
|
108
114
|
|
|
109
115
|
self.register
|
data/lib/ollama_chat/tools.rb
CHANGED
|
@@ -56,9 +56,10 @@ require 'ollama_chat/tools/get_location'
|
|
|
56
56
|
require 'ollama_chat/tools/get_rfc'
|
|
57
57
|
require 'ollama_chat/tools/get_time'
|
|
58
58
|
require 'ollama_chat/tools/get_url'
|
|
59
|
-
require 'ollama_chat/tools/insert_into_editor'
|
|
60
59
|
require 'ollama_chat/tools/open_file_in_editor'
|
|
61
60
|
require 'ollama_chat/tools/paste_from_clipboard'
|
|
61
|
+
require 'ollama_chat/tools/paste_into_editor'
|
|
62
|
+
require 'ollama_chat/tools/patch_file'
|
|
62
63
|
require 'ollama_chat/tools/read_file'
|
|
63
64
|
require 'ollama_chat/tools/resolve_tag'
|
|
64
65
|
require 'ollama_chat/tools/run_tests'
|
|
@@ -43,14 +43,18 @@ module OllamaChat::Utils::Chooser
|
|
|
43
43
|
prompt:,
|
|
44
44
|
match: -> answer {
|
|
45
45
|
matcher = Amatch::PairDistance.new(answer.downcase)
|
|
46
|
-
matches = entries.map { |n|
|
|
47
|
-
|
|
46
|
+
matches = entries.map { |n|
|
|
47
|
+
[
|
|
48
|
+
n,
|
|
49
|
+
-matcher.similar(n.ask_and_send_or_self(:value).to_s.downcase)
|
|
50
|
+
]
|
|
51
|
+
}.select { |_, s| s < 0 }.sort_by(&:last).map(&:first)
|
|
48
52
|
matches.empty? and matches = entries
|
|
49
53
|
matches.first(Tins::Terminal.lines - 1)
|
|
50
54
|
},
|
|
51
55
|
query: -> _answer, matches, selector {
|
|
52
56
|
matches.each_with_index.map { |m, i|
|
|
53
|
-
i == selector ? "#{blue{?⮕}} #{on_blue{m}}" : " #{m
|
|
57
|
+
i == selector ? "#{blue{?⮕}} #{on_blue{m}}" : " #{m}"
|
|
54
58
|
} * ?\n
|
|
55
59
|
},
|
|
56
60
|
found: -> _answer, matches, selector {
|
data/lib/ollama_chat/version.rb
CHANGED