scout-ai 1.2.5 → 2.0.0

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.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +111 -31
  3. data/README.md +171 -320
  4. data/Rakefile +17 -1
  5. data/VERSION +1 -1
  6. data/doc/Improvements.md +325 -0
  7. data/doc/StartHere.md +110 -0
  8. data/doc/developer/Architecture.md +126 -0
  9. data/doc/developer/Backends.md +199 -0
  10. data/doc/developer/ChatLifecycle.md +183 -0
  11. data/doc/developer/DelegationInternals.md +295 -0
  12. data/doc/developer/DesignPrinciples.md +245 -0
  13. data/doc/developer/PromptProcessing.md +292 -0
  14. data/doc/developer/Provenance.md +317 -0
  15. data/doc/user/BuildingAgents.md +345 -0
  16. data/doc/user/Cookbook.md +333 -0
  17. data/doc/user/CoreConcepts.md +181 -0
  18. data/doc/user/Delegation.md +191 -0
  19. data/doc/user/GettingStarted.md +159 -0
  20. data/doc/user/ManagingContext.md +163 -0
  21. data/doc/user/MultiAgentWorkflows.md +256 -0
  22. data/doc/user/Python.md +159 -0
  23. data/doc/user/RunningInference.md +200 -0
  24. data/doc/user/ToolCalling.md +193 -0
  25. data/doc/user/WritingChats.md +197 -0
  26. data/lib/scout/llm/agent/chat.rb +62 -12
  27. data/lib/scout/llm/agent/delegate.rb +274 -65
  28. data/lib/scout/llm/agent/iterate.rb +2 -2
  29. data/lib/scout/llm/agent/save.rb +273 -0
  30. data/lib/scout/llm/agent/workflow.rb +164 -0
  31. data/lib/scout/llm/agent.rb +86 -61
  32. data/lib/scout/llm/ask.rb +36 -6
  33. data/lib/scout/llm/backends/anthropic.rb +9 -1
  34. data/lib/scout/llm/backends/bedrock.rb +15 -3
  35. data/lib/scout/llm/backends/default.rb +129 -89
  36. data/lib/scout/llm/backends/glm.rb +58 -0
  37. data/lib/scout/llm/backends/huggingface.rb +13 -1
  38. data/lib/scout/llm/backends/ollama.rb +13 -0
  39. data/lib/scout/llm/backends/openwebui.rb +8 -3
  40. data/lib/scout/llm/chat/agent_meta.rb +264 -0
  41. data/lib/scout/llm/chat/annotation.rb +37 -14
  42. data/lib/scout/llm/chat/parse.rb +28 -6
  43. data/lib/scout/llm/chat/persist.rb +25 -0
  44. data/lib/scout/llm/chat/process/clear.rb +19 -1
  45. data/lib/scout/llm/chat/process/files.rb +16 -1
  46. data/lib/scout/llm/chat/process/meta.rb +422 -32
  47. data/lib/scout/llm/chat/process/options.rb +21 -1
  48. data/lib/scout/llm/chat/process/tools.rb +49 -12
  49. data/lib/scout/llm/chat/process.rb +4 -0
  50. data/lib/scout/llm/chat/prompt/shorten_tools.rb +125 -0
  51. data/lib/scout/llm/chat/prompt/shorten_tools_epoch.rb +365 -0
  52. data/lib/scout/llm/chat/prompt.rb +48 -0
  53. data/lib/scout/llm/chat/provenance.rb +775 -0
  54. data/lib/scout/llm/chat/tool_calls.rb +76 -0
  55. data/lib/scout/llm/chat.rb +18 -2
  56. data/lib/scout/llm/embed.rb +8 -3
  57. data/lib/scout/llm/image.rb +86 -0
  58. data/lib/scout/llm/rag.rb +3 -3
  59. data/lib/scout/llm/tools/call.rb +159 -10
  60. data/lib/scout/llm/tools/knowledge_base.rb +1 -1
  61. data/lib/scout/llm/tools/workflow.rb +13 -5
  62. data/lib/scout-ai.rb +1 -0
  63. data/research/ChatAnalyst-required-changes.md +167 -0
  64. data/research/agent-delegation-analysis.md +810 -0
  65. data/research/agent-meta-provenance-integration-plan.md +622 -0
  66. data/research/agent-workflow-analysis.md +1120 -0
  67. data/research/backends-analysis.md +836 -0
  68. data/research/chat-core-analysis.md +946 -0
  69. data/research/chatanalyst-provenance/00-baseline.md +30 -0
  70. data/research/chatanalyst-provenance/01-repo-map.md +60 -0
  71. data/research/chatanalyst-provenance/02-event-reconstruction.md +55 -0
  72. data/research/chatanalyst-provenance/03-duplication-evidence.md +45 -0
  73. data/research/chatanalyst-provenance/04-tooling-root-cause.md +57 -0
  74. data/research/chatanalyst-provenance/05-fix-plan.md +46 -0
  75. data/research/chatanalyst-provenance/07-critic-review.md +25 -0
  76. data/research/chatanalyst-provenance/final-report.md +45 -0
  77. data/research/chatanalyst-provenance/resumption.md +37 -0
  78. data/research/coding-philosophy-analysis.md +928 -0
  79. data/research/commands-analysis.md +947 -0
  80. data/research/multi-agent-patterns-analysis.md +853 -0
  81. data/research/prompt-strategies-analysis.md +630 -0
  82. data/research/prov-verbosity-fix-notes.md +77 -0
  83. data/research/provenance-analysis.md +469 -0
  84. data/research/provenance-navigation-design.md +640 -0
  85. data/research/synthesis-report.md +487 -0
  86. data/research/tools-system-analysis.md +779 -0
  87. data/scout-ai.gemspec +97 -13
  88. data/scout_commands/agent/ask +13 -3
  89. data/scout_commands/agent/kb +2 -0
  90. data/scout_commands/llm/ask +11 -4
  91. data/scout_commands/llm/md +76 -0
  92. data/scout_commands/llm/prov +602 -0
  93. data/scout_commands/llm/word +71 -0
  94. data/share/word/reference.docx +0 -0
  95. data/test/etc/AI/mock.yaml +11 -0
  96. data/test/fixtures/backends/anthropic.json +19 -0
  97. data/test/fixtures/backends/anthropic_tool_use.json +24 -0
  98. data/test/fixtures/backends/bedrock.json +8 -0
  99. data/test/fixtures/backends/bedrock_embedding.json +3 -0
  100. data/test/fixtures/backends/bedrock_tool_use.json +17 -0
  101. data/test/fixtures/backends/ollama.json +16 -0
  102. data/test/fixtures/backends/ollama_tool_call.json +27 -0
  103. data/test/fixtures/backends/openai_chat.json +21 -0
  104. data/test/fixtures/backends/openai_chat_tool_call.json +31 -0
  105. data/test/fixtures/backends/responses.json +33 -0
  106. data/test/fixtures/backends/responses_tool_call.json +28 -0
  107. data/test/integration/README.md +32 -0
  108. data/test/integration/scout/llm/backends/test_endpoints.rb +34 -0
  109. data/test/integration/scout/llm/backends/test_openwebui.rb +61 -0
  110. data/test/integration/scout/llm/backends/test_relay.rb +52 -0
  111. data/test/integration/scout/llm/test_infrastructure.rb +74 -0
  112. data/test/{scout → integration/scout}/llm/test_mcp.rb +1 -1
  113. data/test/integration/scout/llm/tools/test_mcp.rb +42 -0
  114. data/test/integration/scout/model/test_base.rb +91 -0
  115. data/test/scout/llm/agent/test_chat.rb +8 -2
  116. data/test/scout/llm/agent/test_save.rb +413 -0
  117. data/test/scout/llm/agent/test_workflow.rb +110 -0
  118. data/test/scout/llm/backends/test_anthropic.rb +93 -10
  119. data/test/scout/llm/backends/test_bedrock.rb +118 -2
  120. data/test/scout/llm/backends/test_ollama.rb +70 -20
  121. data/test/scout/llm/backends/test_openwebui.rb +42 -40
  122. data/test/scout/llm/backends/test_relay.rb +4 -2
  123. data/test/scout/llm/chat/agent_meta_fixtures.rb +131 -0
  124. data/test/scout/llm/chat/process/test_meta.rb +518 -0
  125. data/test/scout/llm/chat/process/test_normalize_usage.rb +183 -0
  126. data/test/scout/llm/chat/test_agent_meta.rb +357 -0
  127. data/test/scout/llm/chat/test_agent_meta_provenance.rb +467 -0
  128. data/test/scout/llm/chat/test_agent_meta_tokens.rb +594 -0
  129. data/test/scout/llm/chat/test_parse.rb +70 -15
  130. data/test/scout/llm/chat/test_prov_cli.rb +274 -0
  131. data/test/scout/llm/chat/test_provenance.rb +240 -0
  132. data/test/scout/llm/chat/test_tool_calls.rb +38 -0
  133. data/test/scout/llm/test_agent.rb +13 -36
  134. data/test/scout/llm/test_ask.rb +75 -52
  135. data/test/scout/llm/test_chat.rb +107 -13
  136. data/test/scout/llm/test_embed.rb +48 -0
  137. data/test/scout/llm/test_rag.rb +23 -16
  138. data/test/scout/llm/test_tools.rb +12 -1
  139. data/test/scout/llm/tools/test_knowledge_base.rb +0 -1
  140. data/test/scout/llm/tools/test_mcp.rb +5 -3
  141. data/test/scout/llm/tools/test_workflow.rb +23 -2
  142. data/test/scout/model/python/huggingface/causal/test_next_token.rb +11 -5
  143. data/test/scout/model/python/huggingface/test_causal.rb +9 -3
  144. data/test/scout/model/python/huggingface/test_classification.rb +11 -2
  145. data/test/scout/model/python/test_torch.rb +2 -0
  146. data/test/scout/model/python/torch/test_helpers.rb +4 -0
  147. data/test/scout/model/test_base.rb +4 -2
  148. data/test/support/availability.rb +231 -0
  149. data/test/support/fake_clients.rb +138 -0
  150. data/test/support/fixtures.rb +21 -0
  151. data/test/support/infrastructure_probes.rb +136 -0
  152. data/test/support/mock_backend.rb +215 -0
  153. data/test/test_helper.rb +32 -2
  154. metadata +96 -12
  155. data/doc/Agent.md +0 -354
  156. data/doc/Chat.md +0 -481
  157. data/doc/LLM.md +0 -356
  158. data/doc/PythonAgentTasks.md +0 -333
  159. data/doc/RAG.md +0 -129
  160. data/doc/USER_GUIDE.md +0 -572
  161. data/scout_commands/documenter +0 -148
  162. data/test/scout/llm/backends/test_openai.rb +0 -192
  163. data/test/scout/llm/backends/test_responses.rb +0 -238
  164. data/test/scout/llm/test_parse.rb +0 -98
data/scout-ai.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: scout-ai 1.2.5 ruby lib
5
+ # stub: scout-ai 2.0.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "scout-ai".freeze
9
- s.version = "1.2.5".freeze
9
+ s.version = "2.0.0".freeze
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
@@ -29,22 +29,39 @@ Gem::Specification.new do |s|
29
29
  "Rakefile",
30
30
  "VERSION",
31
31
  "bin/scout-ai",
32
- "doc/Agent.md",
33
- "doc/Chat.md",
34
- "doc/LLM.md",
32
+ "doc/Improvements.md",
35
33
  "doc/Model.md",
36
- "doc/PythonAgentTasks.md",
37
- "doc/RAG.md",
38
- "doc/USER_GUIDE.md",
34
+ "doc/StartHere.md",
35
+ "doc/developer/Architecture.md",
36
+ "doc/developer/Backends.md",
37
+ "doc/developer/ChatLifecycle.md",
38
+ "doc/developer/DelegationInternals.md",
39
+ "doc/developer/DesignPrinciples.md",
40
+ "doc/developer/PromptProcessing.md",
41
+ "doc/developer/Provenance.md",
42
+ "doc/user/BuildingAgents.md",
43
+ "doc/user/Cookbook.md",
44
+ "doc/user/CoreConcepts.md",
45
+ "doc/user/Delegation.md",
46
+ "doc/user/GettingStarted.md",
47
+ "doc/user/ManagingContext.md",
48
+ "doc/user/MultiAgentWorkflows.md",
49
+ "doc/user/Python.md",
50
+ "doc/user/RunningInference.md",
51
+ "doc/user/ToolCalling.md",
52
+ "doc/user/WritingChats.md",
39
53
  "lib/scout-ai.rb",
40
54
  "lib/scout/llm/agent.rb",
41
55
  "lib/scout/llm/agent/chat.rb",
42
56
  "lib/scout/llm/agent/delegate.rb",
43
57
  "lib/scout/llm/agent/iterate.rb",
58
+ "lib/scout/llm/agent/save.rb",
59
+ "lib/scout/llm/agent/workflow.rb",
44
60
  "lib/scout/llm/ask.rb",
45
61
  "lib/scout/llm/backends/anthropic.rb",
46
62
  "lib/scout/llm/backends/bedrock.rb",
47
63
  "lib/scout/llm/backends/default.rb",
64
+ "lib/scout/llm/backends/glm.rb",
48
65
  "lib/scout/llm/backends/huggingface.rb",
49
66
  "lib/scout/llm/backends/ollama.rb",
50
67
  "lib/scout/llm/backends/openai.rb",
@@ -53,15 +70,23 @@ Gem::Specification.new do |s|
53
70
  "lib/scout/llm/backends/responses.rb",
54
71
  "lib/scout/llm/backends/vllm.rb",
55
72
  "lib/scout/llm/chat.rb",
73
+ "lib/scout/llm/chat/agent_meta.rb",
56
74
  "lib/scout/llm/chat/annotation.rb",
57
75
  "lib/scout/llm/chat/parse.rb",
76
+ "lib/scout/llm/chat/persist.rb",
58
77
  "lib/scout/llm/chat/process.rb",
59
78
  "lib/scout/llm/chat/process/clear.rb",
60
79
  "lib/scout/llm/chat/process/files.rb",
61
80
  "lib/scout/llm/chat/process/meta.rb",
62
81
  "lib/scout/llm/chat/process/options.rb",
63
82
  "lib/scout/llm/chat/process/tools.rb",
83
+ "lib/scout/llm/chat/prompt.rb",
84
+ "lib/scout/llm/chat/prompt/shorten_tools.rb",
85
+ "lib/scout/llm/chat/prompt/shorten_tools_epoch.rb",
86
+ "lib/scout/llm/chat/provenance.rb",
87
+ "lib/scout/llm/chat/tool_calls.rb",
64
88
  "lib/scout/llm/embed.rb",
89
+ "lib/scout/llm/image.rb",
65
90
  "lib/scout/llm/mcp.rb",
66
91
  "lib/scout/llm/rag.rb",
67
92
  "lib/scout/llm/tools.rb",
@@ -106,42 +131,96 @@ Gem::Specification.new do |s|
106
131
  "python/tests/test_chat_agent.py",
107
132
  "python/tests/test_huggingface_eval.py",
108
133
  "python/tests/test_runner.py",
134
+ "research/ChatAnalyst-required-changes.md",
135
+ "research/agent-delegation-analysis.md",
136
+ "research/agent-meta-provenance-integration-plan.md",
137
+ "research/agent-workflow-analysis.md",
138
+ "research/backends-analysis.md",
139
+ "research/chat-core-analysis.md",
140
+ "research/chatanalyst-provenance/00-baseline.md",
141
+ "research/chatanalyst-provenance/01-repo-map.md",
142
+ "research/chatanalyst-provenance/02-event-reconstruction.md",
143
+ "research/chatanalyst-provenance/03-duplication-evidence.md",
144
+ "research/chatanalyst-provenance/04-tooling-root-cause.md",
145
+ "research/chatanalyst-provenance/05-fix-plan.md",
146
+ "research/chatanalyst-provenance/07-critic-review.md",
147
+ "research/chatanalyst-provenance/final-report.md",
148
+ "research/chatanalyst-provenance/resumption.md",
149
+ "research/coding-philosophy-analysis.md",
150
+ "research/commands-analysis.md",
151
+ "research/multi-agent-patterns-analysis.md",
152
+ "research/prompt-strategies-analysis.md",
153
+ "research/prov-verbosity-fix-notes.md",
154
+ "research/provenance-analysis.md",
155
+ "research/provenance-navigation-design.md",
156
+ "research/synthesis-report.md",
157
+ "research/tools-system-analysis.md",
109
158
  "scout-ai.gemspec",
110
159
  "scout_commands/agent/ask",
111
160
  "scout_commands/agent/find",
112
161
  "scout_commands/agent/kb",
113
- "scout_commands/documenter",
114
162
  "scout_commands/llm/ask",
115
163
  "scout_commands/llm/json",
164
+ "scout_commands/llm/md",
116
165
  "scout_commands/llm/process",
117
166
  "scout_commands/llm/process_queries",
167
+ "scout_commands/llm/prov",
118
168
  "scout_commands/llm/server",
119
169
  "scout_commands/llm/template",
170
+ "scout_commands/llm/word",
120
171
  "scout_commands/workflow/mcp",
121
172
  "share/server/chat.html",
122
173
  "share/server/chat.js",
174
+ "share/word/reference.docx",
123
175
  "test/data/cat.jpg",
124
176
  "test/data/person/brothers",
125
177
  "test/data/person/identifiers",
126
178
  "test/data/person/marriages",
127
179
  "test/data/person/parents",
180
+ "test/etc/AI/mock.yaml",
181
+ "test/fixtures/backends/anthropic.json",
182
+ "test/fixtures/backends/anthropic_tool_use.json",
183
+ "test/fixtures/backends/bedrock.json",
184
+ "test/fixtures/backends/bedrock_embedding.json",
185
+ "test/fixtures/backends/bedrock_tool_use.json",
186
+ "test/fixtures/backends/ollama.json",
187
+ "test/fixtures/backends/ollama_tool_call.json",
188
+ "test/fixtures/backends/openai_chat.json",
189
+ "test/fixtures/backends/openai_chat_tool_call.json",
190
+ "test/fixtures/backends/responses.json",
191
+ "test/fixtures/backends/responses_tool_call.json",
192
+ "test/integration/README.md",
193
+ "test/integration/scout/llm/backends/test_endpoints.rb",
194
+ "test/integration/scout/llm/backends/test_openwebui.rb",
195
+ "test/integration/scout/llm/backends/test_relay.rb",
196
+ "test/integration/scout/llm/test_infrastructure.rb",
197
+ "test/integration/scout/llm/test_mcp.rb",
198
+ "test/integration/scout/llm/tools/test_mcp.rb",
199
+ "test/integration/scout/model/test_base.rb",
128
200
  "test/scout/llm/agent/test_chat.rb",
201
+ "test/scout/llm/agent/test_save.rb",
202
+ "test/scout/llm/agent/test_workflow.rb",
129
203
  "test/scout/llm/backends/test_anthropic.rb",
130
204
  "test/scout/llm/backends/test_bedrock.rb",
131
205
  "test/scout/llm/backends/test_huggingface.rb",
132
206
  "test/scout/llm/backends/test_ollama.rb",
133
- "test/scout/llm/backends/test_openai.rb",
134
207
  "test/scout/llm/backends/test_openwebui.rb",
135
208
  "test/scout/llm/backends/test_relay.rb",
136
- "test/scout/llm/backends/test_responses.rb",
209
+ "test/scout/llm/chat/agent_meta_fixtures.rb",
210
+ "test/scout/llm/chat/process/test_meta.rb",
211
+ "test/scout/llm/chat/process/test_normalize_usage.rb",
212
+ "test/scout/llm/chat/test_agent_meta.rb",
213
+ "test/scout/llm/chat/test_agent_meta_provenance.rb",
214
+ "test/scout/llm/chat/test_agent_meta_tokens.rb",
137
215
  "test/scout/llm/chat/test_parse.rb",
138
216
  "test/scout/llm/chat/test_process.rb",
217
+ "test/scout/llm/chat/test_prov_cli.rb",
218
+ "test/scout/llm/chat/test_provenance.rb",
219
+ "test/scout/llm/chat/test_tool_calls.rb",
139
220
  "test/scout/llm/test_agent.rb",
140
221
  "test/scout/llm/test_ask.rb",
141
222
  "test/scout/llm/test_chat.rb",
142
223
  "test/scout/llm/test_embed.rb",
143
- "test/scout/llm/test_mcp.rb",
144
- "test/scout/llm/test_parse.rb",
145
224
  "test/scout/llm/test_rag.rb",
146
225
  "test/scout/llm/test_tools.rb",
147
226
  "test/scout/llm/test_utils.rb",
@@ -161,6 +240,11 @@ Gem::Specification.new do |s|
161
240
  "test/scout/network/test_entity.rb",
162
241
  "test/scout/network/test_knowledge_base.rb",
163
242
  "test/scout/network/test_paths.rb",
243
+ "test/support/availability.rb",
244
+ "test/support/fake_clients.rb",
245
+ "test/support/fixtures.rb",
246
+ "test/support/infrastructure_probes.rb",
247
+ "test/support/mock_backend.rb",
164
248
  "test/test_helper.rb"
165
249
  ]
166
250
  s.homepage = "http://github.com/mikisvaz/scout-ai".freeze
@@ -85,7 +85,8 @@ if chat
85
85
  conversation.message :tool, workflow if workflow
86
86
 
87
87
  convo_options = LLM.options conversation
88
- agent.start(Chat.setup(conversation)) if conversation.any?
88
+ agent.start
89
+ agent.follow(Chat.setup(conversation)) if conversation.any?
89
90
  agent.current_chat.concat LLM.chat(question) if question && ! question.empty?
90
91
  imports.each{|import| agent.import import } if imports
91
92
 
@@ -94,7 +95,15 @@ if chat
94
95
  exit 0
95
96
  end
96
97
 
97
- new = agent.ask(agent.current_chat, convo_options.merge(options.merge(return_messages: true, endpoint: endpoint, model: model)))
98
+ # ScoutCoder: named-file rule, same as AgentWorkflow#log_agent: the agent
99
+ # chat is named after the agent ('agent.chat' by default, but
100
+ # worker.chat/critic.chat when the CLI is invoked for a named agent), and
101
+ # the society tree is the sibling <name>.society (see
102
+ # LLM::Agent.society_dir_for). Must be set before the first chat call so
103
+ # the Agent#chat ensure-hook and the restart snapshot see it.
104
+ agent.save_file = chat + ".files/#{agent_name || 'agent'}.chat"
105
+ #new = agent.ask(agent.current_chat, convo_options.merge(options.merge(return_messages: true)))
106
+ new = agent.chat(convo_options.merge(options.merge(return_messages: true)))
98
107
  conversation += LLM.chat(question) if question
99
108
  conversation += new
100
109
 
@@ -102,6 +111,7 @@ if chat
102
111
  f.puts LLM.print([{role: :user, content: question}]) unless question.empty?
103
112
  f.puts LLM.print(new)
104
113
  end
114
+ raise "No messages returned" if LLM.purge(new).empty?
105
115
  puts LLM.purge(new).last[:content]
106
116
  elsif inline
107
117
 
@@ -112,7 +122,7 @@ elsif inline
112
122
  pre, question, post =
113
123
  file.partition(/^\s*#\s*ask:(?:.*?)(?=^\s*[^\s#])/smu)
114
124
 
115
- break if post.empty?
125
+ break if question.empty?
116
126
 
117
127
  new_file << pre
118
128
  new_file << question
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'scout-ai'
4
+
3
5
  agent = ARGV.shift
4
6
 
5
7
  agent_dir = Scout.var.Agent[agent]
@@ -75,7 +75,7 @@ elsif file
75
75
  end
76
76
 
77
77
  if chat
78
- conversation = Open.exist?(chat)? LLM.chat(chat) : Chat.setup([])
78
+ conversation = Open.exist?(chat)? Chat.load(chat) : Chat.setup([])
79
79
  imports.each{|import| conversation.import import } if imports
80
80
  convo_options = LLM.options conversation
81
81
  conversation = question.empty? ? conversation : conversation + LLM.chat(question)
@@ -88,7 +88,14 @@ if chat
88
88
  exit 0
89
89
  end
90
90
 
91
- new = LLM.ask(conversation, convo_options.merge(options.merge(return_messages: true)))
91
+ # ScoutCoder: named-file rule (AgentWorkflow#log_agent parity): the agent
92
+ # chat of this CLI conversation lands at <chat>.files/agent.chat, with the
93
+ # society tree at <chat>.files/agent.society (LLM::Agent.society_dir_for).
94
+ # This CLI has no agent option, so the name is always 'agent'; the
95
+ # `agent_save_file` option forwarded to LLM.ask is this very value and is
96
+ # honored verbatim by lib/scout/llm/ask.rb.
97
+ agent_save_file = chat + '.files/agent.chat'
98
+ new = LLM.ask(conversation, convo_options.merge(options.merge(return_messages: true, agent_save_file: agent_save_file)))
92
99
  Open.open(chat, mode: 'a') do |f|
93
100
  f.puts LLM.print([{role: :user, content: question}]) unless question.empty?
94
101
  f.puts LLM.print(new)
@@ -111,10 +118,10 @@ elsif inline
111
118
  chat = [
112
119
  {role: :system, content: "Write a succint reply with no commentary and no formatting."},
113
120
  {role: :user, content: "Find the following question as a comment in the file give a response to be placed inline: #{question}"},
114
- LLM.tag('file', file, inline)
121
+ Chat.tag('file', file, inline)
115
122
  ]
116
123
 
117
- chat = Chat.chat(chat)
124
+ chat = LLM.chat(chat)
118
125
 
119
126
  imports.each{|import| chat.import import } if imports
120
127
 
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'scout'
5
+
6
+ cmd = $previous_commands ?
7
+ "scout #{$previous_commands.any? ? "#{$previous_commands * ' '} " : ''}#{File.basename(__FILE__)}" :
8
+ $PROGRAM_NAME
9
+
10
+ options = SOPT.setup <<~EOF
11
+
12
+ Format a chat in Markdown
13
+
14
+ $ #{cmd} [<options>] <chat> <markdown>
15
+
16
+ -h--help Print this help
17
+ -l--last Format only the last interaction
18
+ EOF
19
+ if options[:help]
20
+ if defined? scout_usage
21
+ scout_usage
22
+ else
23
+ puts SOPT.doc
24
+ end
25
+ exit 0
26
+ end
27
+
28
+ help, last = IndiferentHash.process_options options, :help, :last
29
+
30
+ chat, output, _ = ARGV
31
+
32
+ raise MissingParameterException, :chat if chat.nil?
33
+
34
+ messages = Chat.parse Open.read(chat)
35
+
36
+ if last
37
+ messages = messages.select{|m| %w(user assistant).include? m[:role].to_s }[-2..-1]
38
+ end
39
+
40
+ out = []
41
+ messages.each do |message|
42
+ IndiferentHash.setup(message)
43
+ case message['role']
44
+ when 'user'
45
+ out.push <<-EOF
46
+
47
+ ---
48
+ # 👤 User
49
+ ---
50
+
51
+ #{message['content']}
52
+
53
+ EOF
54
+ when 'assistant'
55
+ out.push <<-EOF
56
+
57
+ ---
58
+ # 🤖 Assistant
59
+ ---
60
+
61
+ #{message['content']}
62
+
63
+ EOF
64
+ else
65
+ next
66
+ end
67
+ end
68
+
69
+ markdown = out * "\n"
70
+
71
+ if output
72
+ Open.write(output, markdown)
73
+ puts output
74
+ else
75
+ puts markdown
76
+ end