ollama_chat 0.0.116 → 0.0.117
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 +35 -0
- data/lib/ollama_chat/command_concern.rb +38 -9
- data/lib/ollama_chat/commands.rb +236 -232
- data/lib/ollama_chat/session_management.rb +4 -11
- data/lib/ollama_chat/version.rb +1 -1
- data/ollama_chat.gemspec +2 -2
- data/spec/ollama_chat/session_management_spec.rb +1 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbe0a034dee8079ea14e573b0e98624ebaa3311baaf60e1bd666d7f7b0d61dda
|
|
4
|
+
data.tar.gz: 2a2b0c1b00ce5db2fa4b20660d8cda86669f1bac50e62352a07953e0c327d863
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f187ae33f48c198b1aa15d680b5d7dd45e96c8ba7bcb71bf80adf5d0cb049a766d79d57d8bad32175363c490cfe94a94908b1eba4a1ba28a8b0e9896065079ed
|
|
7
|
+
data.tar.gz: 0d61b926d6e7018d5f21d52ed06d36e81ea859dc5f288a0247834def14245813f1306bf444baaf715c8ec37df20ea64d97f03279045e34eb669a29c9facadb23
|
data/CHANGES.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-09-03 v0.0.117
|
|
4
|
+
|
|
5
|
+
* Fixed `/favourite` subcommand completion rendering by replacing separate
|
|
6
|
+
`add`/`delete` and type arrays in `complete` with `.product`, ensuring each
|
|
7
|
+
subcommand renders as an explicit pair (e.g., `add model`, `delete
|
|
8
|
+
persona`) instead of being independently cross-multiplied by `arguments`.
|
|
9
|
+
* Added command categories to help output:
|
|
10
|
+
* Added `category(name)` and `current_category` class methods to
|
|
11
|
+
`command_concern.rb` for tracking the active category during
|
|
12
|
+
registration.
|
|
13
|
+
* Added `category:` keyword to `command` and `Command#initialize` with
|
|
14
|
+
`attr_reader :category`.
|
|
15
|
+
* Reworked `help_message` to sort commands by `[category, name]` and
|
|
16
|
+
insert bold full-width category header rows via `colspan: 4`.
|
|
17
|
+
* Added `extend Term::ANSIColor` to the `included` block for `bold{}`
|
|
18
|
+
access.
|
|
19
|
+
* Replaced `## Section` comments in `commands.rb` with `category
|
|
20
|
+
:Section` calls.
|
|
21
|
+
* Added two new categories: `Models` (`/model`, `/think`) and `Prompts`
|
|
22
|
+
(`/prompt`, `/system`, `/suggest`).
|
|
23
|
+
* Moved session-persisted commands (`/toggle`, `/tools`, `/voice`,
|
|
24
|
+
`/document_policy`, `/context_format`) under `category :Session`.
|
|
25
|
+
* Moved `/change_response` and `/conversation` under `category
|
|
26
|
+
:Conversation`.
|
|
27
|
+
* Reduced `Settings` to app-level commands only (`/config`,
|
|
28
|
+
`/favourite`).
|
|
29
|
+
* Made `report_session` a pure generator:
|
|
30
|
+
* Removed `&block` parameter and `block&.(result)` call from
|
|
31
|
+
`report_session`, eliminating the infobar side-effect that caused
|
|
32
|
+
double output.
|
|
33
|
+
* Simplified `report_conversation` to call `report_session(name:)`
|
|
34
|
+
without a block; display logic (pager or file write) is the sole
|
|
35
|
+
consumer of the returned string.
|
|
36
|
+
* Removed `@param block [Proc]` from `report_session` YARD docs.
|
|
37
|
+
|
|
3
38
|
## 2026-09-03 v0.0.116
|
|
4
39
|
|
|
5
40
|
### Added
|
|
@@ -11,6 +11,20 @@ module OllamaChat::CommandConcern
|
|
|
11
11
|
# `Command` instances.
|
|
12
12
|
attr_accessor :commands
|
|
13
13
|
|
|
14
|
+
# @return [Symbol] the category currently in effect for new commands
|
|
15
|
+
def current_category
|
|
16
|
+
(@category || :Misc).to_sym
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Set the category that subsequent `command` registrations will be
|
|
20
|
+
# assigned to. Commands registered without an explicit `category:`
|
|
21
|
+
# keyword inherit the current category (defaults to `:Misc`).
|
|
22
|
+
#
|
|
23
|
+
# @param name [String, Symbol] the category name
|
|
24
|
+
def category(name)
|
|
25
|
+
@category = name.to_sym
|
|
26
|
+
end
|
|
27
|
+
|
|
14
28
|
# Register a new command.
|
|
15
29
|
#
|
|
16
30
|
# @param name [String, Symbol] The primary name of the command.
|
|
@@ -24,17 +38,19 @@ module OllamaChat::CommandConcern
|
|
|
24
38
|
# @param options [String, nil] A string describing command options
|
|
25
39
|
# for help output.
|
|
26
40
|
# @param help [String, nil] Short help text for the command.
|
|
41
|
+
# @param category [Symbol] Category for grouping in help output
|
|
42
|
+
# (defaults to `current_category`).
|
|
27
43
|
# @yield [context] Block that receives a binding context for
|
|
28
44
|
# execution. The block must be provided.
|
|
29
45
|
#
|
|
30
46
|
# @raise [ArgumentError] if a command with the same name already
|
|
31
47
|
# exists or if no block is given.
|
|
32
|
-
def command(name:, regexp:, complete: nil, optional: false, options: nil, help: nil, &block)
|
|
48
|
+
def command(name:, regexp:, complete: nil, optional: false, options: nil, help: nil, category: current_category, &block)
|
|
33
49
|
name = name.to_sym
|
|
34
50
|
commands.key?(name) and
|
|
35
51
|
raise ArgumentError, "command #{name} already registered!"
|
|
36
|
-
commands[name] =Command.new(
|
|
37
|
-
name:, regexp:, complete:, optional:, options:, help:, &block
|
|
52
|
+
commands[name] = Command.new(
|
|
53
|
+
name:, regexp:, complete:, optional:, options:, help:, category:, &block
|
|
38
54
|
)
|
|
39
55
|
end
|
|
40
56
|
|
|
@@ -52,15 +68,18 @@ module OllamaChat::CommandConcern
|
|
|
52
68
|
#
|
|
53
69
|
# @param pattern [String, Regexp, nil] An optional pattern to filter
|
|
54
70
|
# commands by their names.
|
|
55
|
-
# @return [Terminal::Table] A table with columns CMD, SUBCMD, OPTS, HELP
|
|
71
|
+
# @return [Terminal::Table] A table with columns CMD, SUBCMD, OPTS, HELP,
|
|
72
|
+
# with bold category header rows inserted between groups.
|
|
56
73
|
def help_message(pattern = nil)
|
|
57
74
|
table = Terminal::Table.new
|
|
58
75
|
table.style = {
|
|
59
76
|
all_separators: true,
|
|
60
77
|
border: :unicode_round,
|
|
61
78
|
}
|
|
62
|
-
table.headings
|
|
63
|
-
commands.
|
|
79
|
+
table.headings = %w[ CMD SUBCMD OPTS HELP ]
|
|
80
|
+
sorted_commands = self.commands.sort_by { [ _2.category, _2.name ] }.map(&:last)
|
|
81
|
+
cat = nil
|
|
82
|
+
sorted_commands.each do |command|
|
|
64
83
|
command.help or next
|
|
65
84
|
if pattern
|
|
66
85
|
command.command_names.any? { _1 =~ pattern } or next
|
|
@@ -72,6 +91,10 @@ module OllamaChat::CommandConcern
|
|
|
72
91
|
[ _1, (?﹡ unless command.optional?) ].compact.join
|
|
73
92
|
}.sort.join(?\n)
|
|
74
93
|
}
|
|
94
|
+
if cat != command.category
|
|
95
|
+
cat = command.category
|
|
96
|
+
table << [ { value: bold{cat}, colspan: 4 } ]
|
|
97
|
+
end
|
|
75
98
|
table << [
|
|
76
99
|
"%s" % command.command_names.map { ?/ + _1 }.join(?\n),
|
|
77
100
|
'%s' % subcommands,
|
|
@@ -91,6 +114,8 @@ module OllamaChat::CommandConcern
|
|
|
91
114
|
delegate :command_completions, to: self
|
|
92
115
|
|
|
93
116
|
delegate :help_message, to: self
|
|
117
|
+
|
|
118
|
+
extend Term::ANSIColor
|
|
94
119
|
end
|
|
95
120
|
|
|
96
121
|
# Represents a registered command in the OllamaChat command DSL.
|
|
@@ -117,13 +142,14 @@ module OllamaChat::CommandConcern
|
|
|
117
142
|
# @param optional [Boolean] Whether the command is optional.
|
|
118
143
|
# @param options [String, nil] Options description.
|
|
119
144
|
# @param help [String] Help text.
|
|
145
|
+
# @param category [Symbol] Category for grouping in help output.
|
|
120
146
|
# @yield [context] Execution block.
|
|
121
147
|
#
|
|
122
148
|
# @raise [ArgumentError] if no block is given.
|
|
123
|
-
def initialize(name:, regexp:, complete: nil, optional: false, options: nil, help:, &block)
|
|
149
|
+
def initialize(name:, regexp:, complete: nil, optional: false, options: nil, help:, category:, &block)
|
|
124
150
|
block or raise ArgumentError, 'require &block'
|
|
125
|
-
@name, @regexp, @optional, @options, @help, @block =
|
|
126
|
-
name, regexp, optional, options, help, block
|
|
151
|
+
@name, @regexp, @optional, @options, @help, @category, @block =
|
|
152
|
+
name, regexp, optional, options, help, category, block
|
|
127
153
|
@complete = Array(complete || name.to_s).map { Array(_1) }
|
|
128
154
|
end
|
|
129
155
|
|
|
@@ -133,6 +159,9 @@ module OllamaChat::CommandConcern
|
|
|
133
159
|
# @return [String] Help text for the command.
|
|
134
160
|
attr_reader :help
|
|
135
161
|
|
|
162
|
+
# @return [Symbol] Category for grouping in help output.
|
|
163
|
+
attr_reader :category
|
|
164
|
+
|
|
136
165
|
# @return [String, nil] Options description.
|
|
137
166
|
attr_reader :options
|
|
138
167
|
|
data/lib/ollama_chat/commands.rb
CHANGED
|
@@ -22,7 +22,7 @@ require 'ollama_chat/command_concern'
|
|
|
22
22
|
module OllamaChat::Commands
|
|
23
23
|
include OllamaChat::CommandConcern
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
category :Clipboard
|
|
26
26
|
|
|
27
27
|
command(
|
|
28
28
|
name: :copy,
|
|
@@ -58,7 +58,7 @@ module OllamaChat::Commands
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
category :Settings
|
|
62
62
|
|
|
63
63
|
command(
|
|
64
64
|
name: :config,
|
|
@@ -85,14 +85,67 @@ module OllamaChat::Commands
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
command(
|
|
88
|
-
name: :
|
|
89
|
-
regexp: %r(^/
|
|
90
|
-
complete: %w[
|
|
88
|
+
name: :favourite,
|
|
89
|
+
regexp: %r(^/favourite(?:\s+(add|delete))?(?:\s+(model|prompt|system|persona|suggest))$),
|
|
90
|
+
complete: [ 'favourite', %w[ add delete ].product(%w[ model prompt system persona suggest ]) ],
|
|
91
91
|
help: <<~EOT
|
|
92
|
-
|
|
92
|
+
\u2B50\uFE0F Manage favorites (add/delete models,
|
|
93
|
+
prompts, personae)
|
|
93
94
|
EOT
|
|
94
|
-
) do
|
|
95
|
-
|
|
95
|
+
) do |subcommand, type|
|
|
96
|
+
case subcommand
|
|
97
|
+
when 'add'
|
|
98
|
+
add_favourite(type)
|
|
99
|
+
when 'delete'
|
|
100
|
+
delete_favourite(type)
|
|
101
|
+
end
|
|
102
|
+
:next
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
category :Session
|
|
106
|
+
|
|
107
|
+
command(
|
|
108
|
+
name: :session,
|
|
109
|
+
regexp: %r(^/session(?:\s+(change|previous|list|new|duplicate|rename|delete|model options change|model options))?((?:\s+-(?:p\s*\w+))*)(?:\s+([^-].*))?$),
|
|
110
|
+
complete: [ 'session', %w[ change previous list new duplicate rename delete model\ options\ change model\ options ] ],
|
|
111
|
+
optional: true,
|
|
112
|
+
options: "[-p profile]\n[name]",
|
|
113
|
+
help: <<~EOT
|
|
114
|
+
💬 Manage sessions:
|
|
115
|
+
- list/new/delete/rename/duplicate
|
|
116
|
+
- change [name]/previous
|
|
117
|
+
- model options/change
|
|
118
|
+
EOT
|
|
119
|
+
) do |subcommand, opts, name|
|
|
120
|
+
case subcommand
|
|
121
|
+
when nil
|
|
122
|
+
info_session
|
|
123
|
+
when 'list'
|
|
124
|
+
list_sessions
|
|
125
|
+
when 'new'
|
|
126
|
+
set_new_session
|
|
127
|
+
when 'duplicate'
|
|
128
|
+
duplicate_session
|
|
129
|
+
when 'delete'
|
|
130
|
+
delete_session
|
|
131
|
+
when 'rename'
|
|
132
|
+
rename_session
|
|
133
|
+
when 'change'
|
|
134
|
+
change_session(name)
|
|
135
|
+
when 'model options'
|
|
136
|
+
edit_session_model_options
|
|
137
|
+
when 'model options change'
|
|
138
|
+
opts = go_command('p:', opts)
|
|
139
|
+
if profile = opts[?p] || choose_profile_for_model(@model)
|
|
140
|
+
copy_model_options_to_session(profile:)
|
|
141
|
+
end
|
|
142
|
+
when 'previous'
|
|
143
|
+
if prev = previous_session
|
|
144
|
+
change_session(prev.id)
|
|
145
|
+
else
|
|
146
|
+
STDOUT.puts "No previous session defined."
|
|
147
|
+
end
|
|
148
|
+
end
|
|
96
149
|
:next
|
|
97
150
|
end
|
|
98
151
|
|
|
@@ -132,123 +185,6 @@ module OllamaChat::Commands
|
|
|
132
185
|
:next
|
|
133
186
|
end
|
|
134
187
|
|
|
135
|
-
command(
|
|
136
|
-
name: :favourite,
|
|
137
|
-
regexp: %r(^/favourite(?:\s+(add|delete))?(?:\s+(model|prompt|system|persona|suggest))$),
|
|
138
|
-
complete: [ 'favourite', %w[ add delete ], %w[ model prompt system persona suggest ] ],
|
|
139
|
-
help: <<~EOT
|
|
140
|
-
\u2B50\uFE0F Manage favorites (add/delete models,
|
|
141
|
-
prompts, personae)
|
|
142
|
-
EOT
|
|
143
|
-
) do |subcommand, type|
|
|
144
|
-
case subcommand
|
|
145
|
-
when 'add'
|
|
146
|
-
add_favourite(type)
|
|
147
|
-
when 'delete'
|
|
148
|
-
delete_favourite(type)
|
|
149
|
-
end
|
|
150
|
-
:next
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
command(
|
|
154
|
-
name: :model,
|
|
155
|
-
regexp: %r(^/model(?:\s+(change|options(?: copy| delete| export| import)?|options from session|options to session))?((?:\s+(?:-m|-p\s+[\S]+))*)$),
|
|
156
|
-
complete: [ 'model', %w[ change options options\ copy options\ delete options\ export options\ import options\ from\ session options\ to\ session ] ],
|
|
157
|
-
options: '[-m|-p pattern]',
|
|
158
|
-
help: <<~EOT
|
|
159
|
-
🤖 Manage AI models & profiles:
|
|
160
|
-
- change: Switch active model
|
|
161
|
-
- options: Edit saved profile config
|
|
162
|
-
- options copy: Copy profile from another model
|
|
163
|
-
- options delete: Delete a saved profile
|
|
164
|
-
- options export: Export ALL model profiles to JSON
|
|
165
|
-
- options import: Selectively import profiles from JSON
|
|
166
|
-
- options from session: Save live → Saved
|
|
167
|
-
- options to session: Apply Saved → Live
|
|
168
|
-
-m interactively choose a model
|
|
169
|
-
-p PATTERN narrow file search for import
|
|
170
|
-
EOT
|
|
171
|
-
) do |subcommand, opts|
|
|
172
|
-
if subcommand == 'change'
|
|
173
|
-
begin
|
|
174
|
-
model = choose_model('', @model)
|
|
175
|
-
profile = choose_profile_for_model(model) || 'default'
|
|
176
|
-
use_model(model, profile:)
|
|
177
|
-
rescue OllamaChat::UnknownModelError => e
|
|
178
|
-
msg = "Caught #{e.class}: #{e}"
|
|
179
|
-
log(:error, msg, data: { command: 'model', profile: }, warn: true)
|
|
180
|
-
end
|
|
181
|
-
next :next
|
|
182
|
-
end
|
|
183
|
-
opts = go_command('mp:', opts)
|
|
184
|
-
model = opts[?m] ? choose_model('', @model) : @model
|
|
185
|
-
case subcommand
|
|
186
|
-
when 'options'
|
|
187
|
-
profile = choose_profile_for_model(model, allow_new: true) or next :next
|
|
188
|
-
edit_model_options(model, profile:)
|
|
189
|
-
when 'options copy'
|
|
190
|
-
copy_model_options_profile(model)
|
|
191
|
-
when 'options delete'
|
|
192
|
-
delete_model_options_profile(model)
|
|
193
|
-
when 'options from session'
|
|
194
|
-
profile = choose_profile_for_model(model) || 'default'
|
|
195
|
-
copy_model_options_from_session(model, profile:)
|
|
196
|
-
when 'options to session'
|
|
197
|
-
profile = choose_profile_for_model(model) || 'default'
|
|
198
|
-
copy_model_options_to_session(model, profile:)
|
|
199
|
-
when 'options export'
|
|
200
|
-
export_model_options
|
|
201
|
-
when 'options import'
|
|
202
|
-
pattern = opts[?p] || '**/*.json'
|
|
203
|
-
filename = choose_filename(pattern) or next :next
|
|
204
|
-
import_model_options(filename)
|
|
205
|
-
end
|
|
206
|
-
:next
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
command(
|
|
210
|
-
name: :system,
|
|
211
|
-
regexp: %r(^/system(?:\s+(change))?$),
|
|
212
|
-
complete: [ 'system', %w[ change ] ],
|
|
213
|
-
optional: true,
|
|
214
|
-
help: <<~EOT,
|
|
215
|
-
🧬 Manage active system prompt (change)
|
|
216
|
-
Note: Use '/prompt -c system' for managing
|
|
217
|
-
(add, delete, edit, list, etc.) system templates.
|
|
218
|
-
EOT
|
|
219
|
-
) do |subcommand, filename|
|
|
220
|
-
case subcommand
|
|
221
|
-
when 'change'
|
|
222
|
-
change_system_prompt(@messages.system_name)
|
|
223
|
-
@messages.show_system_prompt
|
|
224
|
-
when nil
|
|
225
|
-
@messages.show_system_prompt
|
|
226
|
-
end
|
|
227
|
-
:next
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
command(
|
|
231
|
-
name: :think,
|
|
232
|
-
regexp: %r(^/think$),
|
|
233
|
-
help: <<~EOT
|
|
234
|
-
🧠 Configure model thinking mode
|
|
235
|
-
EOT
|
|
236
|
-
) do
|
|
237
|
-
think_mode.choose
|
|
238
|
-
:next
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
command(
|
|
242
|
-
name: :context_format,
|
|
243
|
-
regexp: %r(^/context_format$),
|
|
244
|
-
help: <<~EOT,
|
|
245
|
-
📐 Set context format (JSON|TOON)
|
|
246
|
-
EOT
|
|
247
|
-
) do
|
|
248
|
-
context_format.choose
|
|
249
|
-
:next
|
|
250
|
-
end
|
|
251
|
-
|
|
252
188
|
command(
|
|
253
189
|
name: :tools,
|
|
254
190
|
regexp: %r(^/tools(?:\s+(on|off|enable|disable))?),
|
|
@@ -287,54 +223,31 @@ module OllamaChat::Commands
|
|
|
287
223
|
:next
|
|
288
224
|
end
|
|
289
225
|
|
|
290
|
-
## Session
|
|
291
|
-
|
|
292
226
|
command(
|
|
293
|
-
name: :
|
|
294
|
-
regexp: %r(^/
|
|
295
|
-
complete:
|
|
296
|
-
optional: true,
|
|
297
|
-
options: "[-p profile]\n[name]",
|
|
227
|
+
name: :document_policy,
|
|
228
|
+
regexp: %r(^/document policy$),
|
|
229
|
+
complete: %w[ document policy ],
|
|
298
230
|
help: <<~EOT
|
|
299
|
-
|
|
300
|
-
- list/new/delete/rename/duplicate
|
|
301
|
-
- change [name]/previous
|
|
302
|
-
- model options/change
|
|
231
|
+
📜 Select a scanning policy for documents
|
|
303
232
|
EOT
|
|
304
|
-
) do
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
rename_session
|
|
318
|
-
when 'change'
|
|
319
|
-
change_session(name)
|
|
320
|
-
when 'model options'
|
|
321
|
-
edit_session_model_options
|
|
322
|
-
when 'model options change'
|
|
323
|
-
opts = go_command('p:', opts)
|
|
324
|
-
if profile = opts[?p] || choose_profile_for_model(@model)
|
|
325
|
-
copy_model_options_to_session(profile:)
|
|
326
|
-
end
|
|
327
|
-
when 'previous'
|
|
328
|
-
if prev = previous_session
|
|
329
|
-
change_session(prev.id)
|
|
330
|
-
else
|
|
331
|
-
STDOUT.puts "No previous session defined."
|
|
332
|
-
end
|
|
333
|
-
end
|
|
233
|
+
) do
|
|
234
|
+
document_policy.choose
|
|
235
|
+
:next
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
command(
|
|
239
|
+
name: :context_format,
|
|
240
|
+
regexp: %r(^/context_format$),
|
|
241
|
+
help: <<~EOT,
|
|
242
|
+
📐 Set context format (JSON|TOON)
|
|
243
|
+
EOT
|
|
244
|
+
) do
|
|
245
|
+
context_format.choose
|
|
334
246
|
:next
|
|
335
247
|
end
|
|
336
248
|
|
|
337
|
-
|
|
249
|
+
|
|
250
|
+
category :Conversation
|
|
338
251
|
|
|
339
252
|
command(
|
|
340
253
|
name: :list,
|
|
@@ -447,6 +360,77 @@ module OllamaChat::Commands
|
|
|
447
360
|
content
|
|
448
361
|
end
|
|
449
362
|
|
|
363
|
+
command(
|
|
364
|
+
name: :change_response,
|
|
365
|
+
regexp: %r(^/change response$),
|
|
366
|
+
complete: %w[ change response ],
|
|
367
|
+
help: <<~EOT,
|
|
368
|
+
✏️ Edit last AI response in editor
|
|
369
|
+
EOT
|
|
370
|
+
) do
|
|
371
|
+
change_response
|
|
372
|
+
:next
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
command(
|
|
376
|
+
name: :conversation,
|
|
377
|
+
regexp: %r(^/conversation\s+(clean|compact|save|load|summarize|report)((?:\s+-(?:[sf]|c))*)(?:\s+([^-].*\.jsonl?))?$),
|
|
378
|
+
complete: [ 'conversation', %w[ save load clean compact summarize report ] ],
|
|
379
|
+
options: '[-s|-f|-c] [FILENAME]',
|
|
380
|
+
help: <<~EOT
|
|
381
|
+
💾 Manage conversation content:
|
|
382
|
+
- save/load: Export/import as .json or .jsonl
|
|
383
|
+
- clean: Remove tool content, images, thinking
|
|
384
|
+
- compact: Summarize old messages, keep recent
|
|
385
|
+
- summarize: Per-message narrative (-s sentence, -f save)
|
|
386
|
+
- report: Generate a session report document
|
|
387
|
+
EOT
|
|
388
|
+
) do |subcommand,opts,path|
|
|
389
|
+
if %w[ save load ].include?(subcommand) && path.blank?
|
|
390
|
+
STDERR.puts "Require a path as argument to save/load!"
|
|
391
|
+
next :next
|
|
392
|
+
end
|
|
393
|
+
case subcommand
|
|
394
|
+
when 'save'
|
|
395
|
+
opts = go_command('c', opts.to_s)
|
|
396
|
+
save_conversation(path, clean: opts[?c])
|
|
397
|
+
when 'load'
|
|
398
|
+
load_conversation(path)
|
|
399
|
+
repair_group_uuids
|
|
400
|
+
when 'clean'
|
|
401
|
+
if confirm?(
|
|
402
|
+
prompt: '🔔 Clean tool content, images, and thinking from conversation? (y/n) ',
|
|
403
|
+
yes: /\Ay/i
|
|
404
|
+
)
|
|
405
|
+
then
|
|
406
|
+
messages.clean_messages!
|
|
407
|
+
session_sync
|
|
408
|
+
STDOUT.puts "Conversation cleaned."
|
|
409
|
+
else
|
|
410
|
+
STDOUT.puts 'Cancelled.'
|
|
411
|
+
end
|
|
412
|
+
when 'compact'
|
|
413
|
+
if confirm?(
|
|
414
|
+
prompt: '🔔 Compact conversation? Old messages will be summarized. (y/n) ',
|
|
415
|
+
yes: /\Ay/i
|
|
416
|
+
)
|
|
417
|
+
then
|
|
418
|
+
compact_with_retry
|
|
419
|
+
else
|
|
420
|
+
STDOUT.puts 'Cancelled.'
|
|
421
|
+
end
|
|
422
|
+
when 'summarize'
|
|
423
|
+
opts = go_command('sf', opts)
|
|
424
|
+
summarize_conversation(save: opts[?f], sentence: opts[?s])
|
|
425
|
+
when 'report'
|
|
426
|
+
opts = go_command('f', opts)
|
|
427
|
+
report_conversation(save: opts[?f])
|
|
428
|
+
end
|
|
429
|
+
:next
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
category :Prompts
|
|
433
|
+
|
|
450
434
|
command(
|
|
451
435
|
name: :prompt,
|
|
452
436
|
regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset|rename|sync|-e))?(\s+(?:-[ef]|-c\s+(?:\w+|\?)))?(?:\s+([^-].*))?$),
|
|
@@ -528,6 +512,27 @@ module OllamaChat::Commands
|
|
|
528
512
|
:next
|
|
529
513
|
end
|
|
530
514
|
|
|
515
|
+
command(
|
|
516
|
+
name: :system,
|
|
517
|
+
regexp: %r(^/system(?:\s+(change))?$),
|
|
518
|
+
complete: [ 'system', %w[ change ] ],
|
|
519
|
+
optional: true,
|
|
520
|
+
help: <<~EOT,
|
|
521
|
+
🧬 Manage active system prompt (change)
|
|
522
|
+
Note: Use '/prompt -c system' for managing
|
|
523
|
+
(add, delete, edit, list, etc.) system templates.
|
|
524
|
+
EOT
|
|
525
|
+
) do |subcommand, filename|
|
|
526
|
+
case subcommand
|
|
527
|
+
when 'change'
|
|
528
|
+
change_system_prompt(@messages.system_name)
|
|
529
|
+
@messages.show_system_prompt
|
|
530
|
+
when nil
|
|
531
|
+
@messages.show_system_prompt
|
|
532
|
+
end
|
|
533
|
+
:next
|
|
534
|
+
end
|
|
535
|
+
|
|
531
536
|
command(
|
|
532
537
|
name: :suggest,
|
|
533
538
|
regexp: %r(^/suggest(\s+(?:-e))?$),
|
|
@@ -545,76 +550,76 @@ module OllamaChat::Commands
|
|
|
545
550
|
:next
|
|
546
551
|
end
|
|
547
552
|
|
|
548
|
-
|
|
549
|
-
name: :change_response,
|
|
550
|
-
regexp: %r(^/change response$),
|
|
551
|
-
complete: %w[ change response ],
|
|
552
|
-
help: <<~EOT,
|
|
553
|
-
✏️ Edit last AI response in editor
|
|
554
|
-
EOT
|
|
555
|
-
) do
|
|
556
|
-
change_response
|
|
557
|
-
:next
|
|
558
|
-
end
|
|
553
|
+
category :Models
|
|
559
554
|
|
|
560
555
|
command(
|
|
561
|
-
name: :
|
|
562
|
-
regexp: %r(^/
|
|
563
|
-
complete: [ '
|
|
564
|
-
options: '[-
|
|
556
|
+
name: :model,
|
|
557
|
+
regexp: %r(^/model(?:\s+(change|options(?: copy| delete| export| import)?|options from session|options to session))?((?:\s+(?:-m|-p\s+[\S]+))*)$),
|
|
558
|
+
complete: [ 'model', %w[ change options options\ copy options\ delete options\ export options\ import options\ from\ session options\ to\ session ] ],
|
|
559
|
+
options: '[-m|-p pattern]',
|
|
565
560
|
help: <<~EOT
|
|
566
|
-
|
|
567
|
-
-
|
|
568
|
-
-
|
|
569
|
-
-
|
|
570
|
-
-
|
|
571
|
-
-
|
|
561
|
+
🤖 Manage AI models & profiles:
|
|
562
|
+
- change: Switch active model
|
|
563
|
+
- options: Edit saved profile config
|
|
564
|
+
- options copy: Copy profile from another model
|
|
565
|
+
- options delete: Delete a saved profile
|
|
566
|
+
- options export: Export ALL model profiles to JSON
|
|
567
|
+
- options import: Selectively import profiles from JSON
|
|
568
|
+
- options from session: Save live → Saved
|
|
569
|
+
- options to session: Apply Saved → Live
|
|
570
|
+
-m interactively choose a model
|
|
571
|
+
-p PATTERN narrow file search for import
|
|
572
572
|
EOT
|
|
573
|
-
) do |subcommand,opts
|
|
574
|
-
if
|
|
575
|
-
|
|
573
|
+
) do |subcommand, opts|
|
|
574
|
+
if subcommand == 'change'
|
|
575
|
+
begin
|
|
576
|
+
model = choose_model('', @model)
|
|
577
|
+
profile = choose_profile_for_model(model) || 'default'
|
|
578
|
+
use_model(model, profile:)
|
|
579
|
+
rescue OllamaChat::UnknownModelError => e
|
|
580
|
+
msg = "Caught #{e.class}: #{e}"
|
|
581
|
+
log(:error, msg, data: { command: 'model', profile: }, warn: true)
|
|
582
|
+
end
|
|
576
583
|
next :next
|
|
577
584
|
end
|
|
585
|
+
opts = go_command('mp:', opts)
|
|
586
|
+
model = opts[?m] ? choose_model('', @model) : @model
|
|
578
587
|
case subcommand
|
|
579
|
-
when '
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
when '
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
if confirm?(
|
|
599
|
-
prompt: '🔔 Compact conversation? Old messages will be summarized. (y/n) ',
|
|
600
|
-
yes: /\Ay/i
|
|
601
|
-
)
|
|
602
|
-
then
|
|
603
|
-
compact_with_retry
|
|
604
|
-
else
|
|
605
|
-
STDOUT.puts 'Cancelled.'
|
|
606
|
-
end
|
|
607
|
-
when 'summarize'
|
|
608
|
-
opts = go_command('sf', opts)
|
|
609
|
-
summarize_conversation(save: opts[?f], sentence: opts[?s])
|
|
610
|
-
when 'report'
|
|
611
|
-
opts = go_command('f', opts)
|
|
612
|
-
report_conversation(save: opts[?f])
|
|
588
|
+
when 'options'
|
|
589
|
+
profile = choose_profile_for_model(model, allow_new: true) or next :next
|
|
590
|
+
edit_model_options(model, profile:)
|
|
591
|
+
when 'options copy'
|
|
592
|
+
copy_model_options_profile(model)
|
|
593
|
+
when 'options delete'
|
|
594
|
+
delete_model_options_profile(model)
|
|
595
|
+
when 'options from session'
|
|
596
|
+
profile = choose_profile_for_model(model) || 'default'
|
|
597
|
+
copy_model_options_from_session(model, profile:)
|
|
598
|
+
when 'options to session'
|
|
599
|
+
profile = choose_profile_for_model(model) || 'default'
|
|
600
|
+
copy_model_options_to_session(model, profile:)
|
|
601
|
+
when 'options export'
|
|
602
|
+
export_model_options
|
|
603
|
+
when 'options import'
|
|
604
|
+
pattern = opts[?p] || '**/*.json'
|
|
605
|
+
filename = choose_filename(pattern) or next :next
|
|
606
|
+
import_model_options(filename)
|
|
613
607
|
end
|
|
614
608
|
:next
|
|
615
609
|
end
|
|
616
610
|
|
|
617
|
-
|
|
611
|
+
command(
|
|
612
|
+
name: :think,
|
|
613
|
+
regexp: %r(^/think$),
|
|
614
|
+
help: <<~EOT
|
|
615
|
+
🧠 Configure model thinking mode
|
|
616
|
+
EOT
|
|
617
|
+
) do
|
|
618
|
+
think_mode.choose
|
|
619
|
+
:next
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
category :Collection
|
|
618
623
|
|
|
619
624
|
command(
|
|
620
625
|
name: :collection,
|
|
@@ -666,7 +671,7 @@ module OllamaChat::Commands
|
|
|
666
671
|
:next
|
|
667
672
|
end
|
|
668
673
|
|
|
669
|
-
|
|
674
|
+
category :Persona
|
|
670
675
|
|
|
671
676
|
command(
|
|
672
677
|
name: :persona,
|
|
@@ -788,7 +793,7 @@ module OllamaChat::Commands
|
|
|
788
793
|
end
|
|
789
794
|
end
|
|
790
795
|
|
|
791
|
-
|
|
796
|
+
category :Input
|
|
792
797
|
|
|
793
798
|
command(
|
|
794
799
|
name: :compose,
|
|
@@ -930,7 +935,7 @@ module OllamaChat::Commands
|
|
|
930
935
|
end
|
|
931
936
|
end
|
|
932
937
|
|
|
933
|
-
|
|
938
|
+
category :Output
|
|
934
939
|
|
|
935
940
|
command(
|
|
936
941
|
name: :pipe,
|
|
@@ -977,7 +982,7 @@ module OllamaChat::Commands
|
|
|
977
982
|
:next
|
|
978
983
|
end
|
|
979
984
|
|
|
980
|
-
|
|
985
|
+
category :Actions
|
|
981
986
|
|
|
982
987
|
command(
|
|
983
988
|
name: :reconnect,
|
|
@@ -1003,7 +1008,7 @@ module OllamaChat::Commands
|
|
|
1003
1008
|
quit_app
|
|
1004
1009
|
end
|
|
1005
1010
|
|
|
1006
|
-
|
|
1011
|
+
category :Information
|
|
1007
1012
|
|
|
1008
1013
|
command(
|
|
1009
1014
|
name: :info,
|
|
@@ -1072,5 +1077,4 @@ module OllamaChat::Commands
|
|
|
1072
1077
|
STDOUT.puts "Type /quit to quit."
|
|
1073
1078
|
:next
|
|
1074
1079
|
end
|
|
1075
|
-
|
|
1076
1080
|
end
|
|
@@ -425,9 +425,8 @@ module OllamaChat::SessionManagement
|
|
|
425
425
|
# stays bounded even after multiple compaction rounds.
|
|
426
426
|
#
|
|
427
427
|
# @param name [String, nil] specific template name (skips chooser)
|
|
428
|
-
# @param block [Proc] a block to handle the report content
|
|
429
428
|
# @return [String, nil] the report or nil if empty
|
|
430
|
-
def report_session(name: nil
|
|
429
|
+
def report_session(name: nil)
|
|
431
430
|
content = messages.compacted_messages.inject('') do |c, message|
|
|
432
431
|
message.content.present? or next c
|
|
433
432
|
sender = sender_name_displayed(message)
|
|
@@ -445,9 +444,7 @@ module OllamaChat::SessionManagement
|
|
|
445
444
|
end
|
|
446
445
|
template or return
|
|
447
446
|
|
|
448
|
-
|
|
449
|
-
block&.(result)
|
|
450
|
-
result
|
|
447
|
+
generate(prompt: template.to_s % { content: })
|
|
451
448
|
end
|
|
452
449
|
|
|
453
450
|
# Generates a report and displays or saves the result.
|
|
@@ -458,9 +455,7 @@ module OllamaChat::SessionManagement
|
|
|
458
455
|
if save
|
|
459
456
|
filename = ask_for_filename?(action: 'for report') or return
|
|
460
457
|
should_overwrite?(filename) or return
|
|
461
|
-
result = report_session(name:)
|
|
462
|
-
infobar.puts kramdown_ansi_parse(content)
|
|
463
|
-
end
|
|
458
|
+
result = report_session(name:)
|
|
464
459
|
if result.full?
|
|
465
460
|
filename.write(result)
|
|
466
461
|
STDOUT.puts "File successfully written."
|
|
@@ -468,9 +463,7 @@ module OllamaChat::SessionManagement
|
|
|
468
463
|
STDERR.puts "Nothing to report!"
|
|
469
464
|
end
|
|
470
465
|
else
|
|
471
|
-
result = report_session(name:)
|
|
472
|
-
infobar.puts kramdown_ansi_parse(content) << ?\n
|
|
473
|
-
end
|
|
466
|
+
result = report_session(name:)
|
|
474
467
|
if result.full?
|
|
475
468
|
use_pager do |output|
|
|
476
469
|
output.puts kramdown_ansi_parse(result)
|
data/lib/ollama_chat/version.rb
CHANGED
data/ollama_chat.gemspec
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: ollama_chat 0.0.
|
|
2
|
+
# stub: ollama_chat 0.0.117 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "ollama_chat".freeze
|
|
6
|
-
s.version = "0.0.
|
|
6
|
+
s.version = "0.0.117".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]
|
|
@@ -352,10 +352,8 @@ describe OllamaChat::SessionManagement do
|
|
|
352
352
|
expect(chat).to receive(:choose_prompt).and_return(double(to_s: '%{content}'))
|
|
353
353
|
expect(chat).to receive(:generate).and_return('Generated report')
|
|
354
354
|
|
|
355
|
-
|
|
356
|
-
result = chat.report_session { |c| results << c }
|
|
355
|
+
result = chat.report_session
|
|
357
356
|
expect(result).to eq('Generated report')
|
|
358
|
-
expect(results).to eq(['Generated report'])
|
|
359
357
|
end
|
|
360
358
|
|
|
361
359
|
it 'returns nil when no messages have content' do
|