elelem 0.8.0 → 0.9.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +71 -3
- data/README.md +31 -46
- data/exe/elelem +78 -3
- data/lib/elelem/agent.rb +135 -228
- data/lib/elelem/mcp.rb +96 -0
- data/lib/elelem/net/claude.rb +200 -0
- data/lib/elelem/net/ollama.rb +78 -0
- data/lib/elelem/net/openai.rb +86 -0
- data/lib/elelem/net.rb +16 -0
- data/lib/elelem/plugins/confirm.rb +12 -0
- data/lib/elelem/plugins/edit.rb +15 -0
- data/lib/elelem/plugins/eval.rb +20 -0
- data/lib/elelem/plugins/execute.rb +18 -0
- data/lib/elelem/plugins/mcp.rb +14 -0
- data/lib/elelem/plugins/read.rb +21 -0
- data/lib/elelem/plugins/verify.rb +47 -0
- data/lib/elelem/plugins/write.rb +23 -0
- data/lib/elelem/plugins.rb +43 -0
- data/lib/elelem/system_prompt.rb +65 -0
- data/lib/elelem/templates/system_prompt.erb +53 -0
- data/lib/elelem/terminal.rb +55 -65
- data/lib/elelem/tool.rb +30 -32
- data/lib/elelem/toolbox.rb +36 -94
- data/lib/elelem/version.rb +1 -1
- data/lib/elelem.rb +28 -36
- metadata +39 -61
- data/lib/elelem/application.rb +0 -45
- data/lib/elelem/conversation.rb +0 -78
- data/lib/elelem/git_context.rb +0 -79
- data/lib/elelem/system_prompt.erb +0 -16
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: elelem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- mo khan
|
|
@@ -10,33 +10,19 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
13
|
+
name: date
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0
|
|
18
|
+
version: '3.0'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: cli-ui
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '2.0'
|
|
33
|
-
type: :runtime
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '2.0'
|
|
25
|
+
version: '3.0'
|
|
40
26
|
- !ruby/object:Gem::Dependency
|
|
41
27
|
name: erb
|
|
42
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,33 +66,19 @@ dependencies:
|
|
|
80
66
|
- !ruby/object:Gem::Version
|
|
81
67
|
version: '2.0'
|
|
82
68
|
- !ruby/object:Gem::Dependency
|
|
83
|
-
name:
|
|
84
|
-
requirement: !ruby/object:Gem::Requirement
|
|
85
|
-
requirements:
|
|
86
|
-
- - "~>"
|
|
87
|
-
- !ruby/object:Gem::Version
|
|
88
|
-
version: '6.0'
|
|
89
|
-
type: :runtime
|
|
90
|
-
prerelease: false
|
|
91
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
-
requirements:
|
|
93
|
-
- - "~>"
|
|
94
|
-
- !ruby/object:Gem::Version
|
|
95
|
-
version: '6.0'
|
|
96
|
-
- !ruby/object:Gem::Dependency
|
|
97
|
-
name: logger
|
|
69
|
+
name: json_schemer
|
|
98
70
|
requirement: !ruby/object:Gem::Requirement
|
|
99
71
|
requirements:
|
|
100
72
|
- - "~>"
|
|
101
73
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '
|
|
74
|
+
version: '2.0'
|
|
103
75
|
type: :runtime
|
|
104
76
|
prerelease: false
|
|
105
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
78
|
requirements:
|
|
107
79
|
- - "~>"
|
|
108
80
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '
|
|
81
|
+
version: '2.0'
|
|
110
82
|
- !ruby/object:Gem::Dependency
|
|
111
83
|
name: net-hippie
|
|
112
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -122,27 +94,21 @@ dependencies:
|
|
|
122
94
|
- !ruby/object:Gem::Version
|
|
123
95
|
version: '1.0'
|
|
124
96
|
- !ruby/object:Gem::Dependency
|
|
125
|
-
name:
|
|
97
|
+
name: open3
|
|
126
98
|
requirement: !ruby/object:Gem::Requirement
|
|
127
99
|
requirements:
|
|
128
100
|
- - "~>"
|
|
129
101
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '0.
|
|
131
|
-
- - ">="
|
|
132
|
-
- !ruby/object:Gem::Version
|
|
133
|
-
version: 0.5.0
|
|
102
|
+
version: '0.1'
|
|
134
103
|
type: :runtime
|
|
135
104
|
prerelease: false
|
|
136
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
137
106
|
requirements:
|
|
138
107
|
- - "~>"
|
|
139
108
|
- !ruby/object:Gem::Version
|
|
140
|
-
version: '0.
|
|
141
|
-
- - ">="
|
|
142
|
-
- !ruby/object:Gem::Version
|
|
143
|
-
version: 0.5.0
|
|
109
|
+
version: '0.1'
|
|
144
110
|
- !ruby/object:Gem::Dependency
|
|
145
|
-
name:
|
|
111
|
+
name: optparse
|
|
146
112
|
requirement: !ruby/object:Gem::Requirement
|
|
147
113
|
requirements:
|
|
148
114
|
- - "~>"
|
|
@@ -184,47 +150,47 @@ dependencies:
|
|
|
184
150
|
- !ruby/object:Gem::Version
|
|
185
151
|
version: '0.6'
|
|
186
152
|
- !ruby/object:Gem::Dependency
|
|
187
|
-
name:
|
|
153
|
+
name: stringio
|
|
188
154
|
requirement: !ruby/object:Gem::Requirement
|
|
189
155
|
requirements:
|
|
190
156
|
- - "~>"
|
|
191
157
|
- !ruby/object:Gem::Version
|
|
192
|
-
version: '
|
|
158
|
+
version: '3.0'
|
|
193
159
|
type: :runtime
|
|
194
160
|
prerelease: false
|
|
195
161
|
version_requirements: !ruby/object:Gem::Requirement
|
|
196
162
|
requirements:
|
|
197
163
|
- - "~>"
|
|
198
164
|
- !ruby/object:Gem::Version
|
|
199
|
-
version: '
|
|
165
|
+
version: '3.0'
|
|
200
166
|
- !ruby/object:Gem::Dependency
|
|
201
|
-
name:
|
|
167
|
+
name: tempfile
|
|
202
168
|
requirement: !ruby/object:Gem::Requirement
|
|
203
169
|
requirements:
|
|
204
170
|
- - "~>"
|
|
205
171
|
- !ruby/object:Gem::Version
|
|
206
|
-
version: '
|
|
172
|
+
version: '0.3'
|
|
207
173
|
type: :runtime
|
|
208
174
|
prerelease: false
|
|
209
175
|
version_requirements: !ruby/object:Gem::Requirement
|
|
210
176
|
requirements:
|
|
211
177
|
- - "~>"
|
|
212
178
|
- !ruby/object:Gem::Version
|
|
213
|
-
version: '
|
|
179
|
+
version: '0.3'
|
|
214
180
|
- !ruby/object:Gem::Dependency
|
|
215
|
-
name:
|
|
181
|
+
name: uri
|
|
216
182
|
requirement: !ruby/object:Gem::Requirement
|
|
217
183
|
requirements:
|
|
218
184
|
- - "~>"
|
|
219
185
|
- !ruby/object:Gem::Version
|
|
220
|
-
version: '0
|
|
186
|
+
version: '1.0'
|
|
221
187
|
type: :runtime
|
|
222
188
|
prerelease: false
|
|
223
189
|
version_requirements: !ruby/object:Gem::Requirement
|
|
224
190
|
requirements:
|
|
225
191
|
- - "~>"
|
|
226
192
|
- !ruby/object:Gem::Version
|
|
227
|
-
version: '0
|
|
193
|
+
version: '1.0'
|
|
228
194
|
description: A minimal coding agent supporting Ollama, Anthropic, OpenAI, and VertexAI.
|
|
229
195
|
email:
|
|
230
196
|
- mo@mokhan.ca
|
|
@@ -240,10 +206,22 @@ files:
|
|
|
240
206
|
- exe/elelem
|
|
241
207
|
- lib/elelem.rb
|
|
242
208
|
- lib/elelem/agent.rb
|
|
243
|
-
- lib/elelem/
|
|
244
|
-
- lib/elelem/
|
|
245
|
-
- lib/elelem/
|
|
246
|
-
- lib/elelem/
|
|
209
|
+
- lib/elelem/mcp.rb
|
|
210
|
+
- lib/elelem/net.rb
|
|
211
|
+
- lib/elelem/net/claude.rb
|
|
212
|
+
- lib/elelem/net/ollama.rb
|
|
213
|
+
- lib/elelem/net/openai.rb
|
|
214
|
+
- lib/elelem/plugins.rb
|
|
215
|
+
- lib/elelem/plugins/confirm.rb
|
|
216
|
+
- lib/elelem/plugins/edit.rb
|
|
217
|
+
- lib/elelem/plugins/eval.rb
|
|
218
|
+
- lib/elelem/plugins/execute.rb
|
|
219
|
+
- lib/elelem/plugins/mcp.rb
|
|
220
|
+
- lib/elelem/plugins/read.rb
|
|
221
|
+
- lib/elelem/plugins/verify.rb
|
|
222
|
+
- lib/elelem/plugins/write.rb
|
|
223
|
+
- lib/elelem/system_prompt.rb
|
|
224
|
+
- lib/elelem/templates/system_prompt.erb
|
|
247
225
|
- lib/elelem/terminal.rb
|
|
248
226
|
- lib/elelem/tool.rb
|
|
249
227
|
- lib/elelem/toolbox.rb
|
|
@@ -263,14 +241,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
263
241
|
requirements:
|
|
264
242
|
- - ">="
|
|
265
243
|
- !ruby/object:Gem::Version
|
|
266
|
-
version:
|
|
244
|
+
version: 4.0.0
|
|
267
245
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
246
|
requirements:
|
|
269
247
|
- - ">="
|
|
270
248
|
- !ruby/object:Gem::Version
|
|
271
|
-
version:
|
|
249
|
+
version: 4.0.0
|
|
272
250
|
requirements: []
|
|
273
|
-
rubygems_version:
|
|
251
|
+
rubygems_version: 4.0.4
|
|
274
252
|
specification_version: 4
|
|
275
253
|
summary: A minimal coding agent for LLMs.
|
|
276
254
|
test_files: []
|
data/lib/elelem/application.rb
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Elelem
|
|
4
|
-
class Application < Thor
|
|
5
|
-
PROVIDERS = %w[ollama anthropic openai vertex-ai].freeze
|
|
6
|
-
|
|
7
|
-
desc "chat", "Start the REPL"
|
|
8
|
-
method_option :provider,
|
|
9
|
-
aliases: "-p",
|
|
10
|
-
type: :string,
|
|
11
|
-
desc: "LLM provider (#{PROVIDERS.join(', ')})",
|
|
12
|
-
default: ENV.fetch("ELELEM_PROVIDER", "ollama")
|
|
13
|
-
method_option :model,
|
|
14
|
-
aliases: "-m",
|
|
15
|
-
type: :string,
|
|
16
|
-
desc: "Model name (uses provider default if not specified)"
|
|
17
|
-
def chat(*)
|
|
18
|
-
provider = options[:provider]
|
|
19
|
-
model = options[:model]
|
|
20
|
-
say "Agent (#{provider})", :green
|
|
21
|
-
agent = Agent.new(provider, model, Toolbox.new)
|
|
22
|
-
agent.repl
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
desc "files", "Generate CXML of the files"
|
|
26
|
-
def files
|
|
27
|
-
puts '<documents>'
|
|
28
|
-
$stdin.read.split("\n").map(&:strip).reject(&:empty?).each_with_index do |file, i|
|
|
29
|
-
next unless File.file?(file)
|
|
30
|
-
|
|
31
|
-
puts " <document index=\"#{i + 1}\">"
|
|
32
|
-
puts " <source><![CDATA[#{file}]]></source>"
|
|
33
|
-
puts " <document_content><![CDATA[#{File.read(file)}]]></document_content>"
|
|
34
|
-
puts " </document>"
|
|
35
|
-
end
|
|
36
|
-
puts '</documents>'
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
desc "version", "The version of this CLI"
|
|
40
|
-
def version
|
|
41
|
-
say "v#{Elelem::VERSION}"
|
|
42
|
-
end
|
|
43
|
-
map %w[--version -v] => :version
|
|
44
|
-
end
|
|
45
|
-
end
|
data/lib/elelem/conversation.rb
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Elelem
|
|
4
|
-
class Conversation
|
|
5
|
-
ROLES = %i[system assistant user tool].freeze
|
|
6
|
-
|
|
7
|
-
def initialize(items = default_context)
|
|
8
|
-
@items = items
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def history_for(permissions)
|
|
12
|
-
history = @items.dup
|
|
13
|
-
history[0] = { role: "system", content: system_prompt_for(permissions) }
|
|
14
|
-
history
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def add(role: :user, content: "")
|
|
18
|
-
role = role.to_sym
|
|
19
|
-
raise "unknown role: #{role}" unless ROLES.include?(role)
|
|
20
|
-
return if content.nil? || content.empty?
|
|
21
|
-
|
|
22
|
-
if @items.last && @items.last[:role] == role
|
|
23
|
-
@items.last[:content] += content
|
|
24
|
-
else
|
|
25
|
-
@items.push({ role: role, content: normalize(content) })
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def clear
|
|
30
|
-
@items = default_context
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def dump(permissions)
|
|
34
|
-
JSON.pretty_generate(history_for(permissions))
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
def default_context(prompt = system_prompt_for([]))
|
|
40
|
-
[{ role: "system", content: prompt }]
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def system_prompt_for(permissions)
|
|
44
|
-
base = system_prompt
|
|
45
|
-
|
|
46
|
-
case permissions.sort
|
|
47
|
-
when [:read]
|
|
48
|
-
"#{base}\n\nYou may read files on the system."
|
|
49
|
-
when [:write]
|
|
50
|
-
"#{base}\n\nYou may write files on the system."
|
|
51
|
-
when [:execute]
|
|
52
|
-
"#{base}\n\nYou may execute shell commands on the system."
|
|
53
|
-
when [:read, :write]
|
|
54
|
-
"#{base}\n\nYou may read and write files on the system."
|
|
55
|
-
when [:execute, :read]
|
|
56
|
-
"#{base}\n\nYou may execute shell commands and read files on the system."
|
|
57
|
-
when [:execute, :write]
|
|
58
|
-
"#{base}\n\nYou may execute shell commands and write files on the system."
|
|
59
|
-
when [:execute, :read, :write]
|
|
60
|
-
"#{base}\n\nYou may read files, write files and execute shell commands on the system."
|
|
61
|
-
else
|
|
62
|
-
base
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def system_prompt
|
|
67
|
-
ERB.new(Pathname.new(__dir__).join("system_prompt.erb").read).result(binding)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def normalize(content)
|
|
71
|
-
if content.is_a?(Array)
|
|
72
|
-
content.join(", ")
|
|
73
|
-
else
|
|
74
|
-
content.to_s
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
data/lib/elelem/git_context.rb
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Elelem
|
|
4
|
-
class GitContext
|
|
5
|
-
MAX_DIFF_LINES = 100
|
|
6
|
-
|
|
7
|
-
def initialize(shell = Elelem.shell)
|
|
8
|
-
@shell = shell
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def to_s
|
|
12
|
-
return "" unless git_repo?
|
|
13
|
-
|
|
14
|
-
parts = []
|
|
15
|
-
parts << "Branch: #{branch}" if branch
|
|
16
|
-
parts << status_section if status.any?
|
|
17
|
-
parts << diff_section if staged_diff.any? || unstaged_diff.any?
|
|
18
|
-
parts << recent_commits_section if recent_commits.any?
|
|
19
|
-
parts.join("\n\n")
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def git_repo?
|
|
25
|
-
@shell.execute("git", args: ["rev-parse", "--git-dir"])["exit_status"].zero?
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def branch
|
|
29
|
-
@branch ||= @shell.execute("git", args: ["branch", "--show-current"])["stdout"].strip.then { |b| b.empty? ? nil : b }
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def status
|
|
33
|
-
@status ||= @shell.execute("git", args: ["status", "--porcelain"])["stdout"].lines.map(&:chomp)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def staged_diff
|
|
37
|
-
@staged_diff ||= @shell.execute("git", args: ["diff", "--cached", "--stat"])["stdout"].lines
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def unstaged_diff
|
|
41
|
-
@unstaged_diff ||= @shell.execute("git", args: ["diff", "--stat"])["stdout"].lines
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def recent_commits
|
|
45
|
-
@recent_commits ||= @shell.execute("git", args: ["log", "--oneline", "-5"])["stdout"].lines.map(&:strip)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def status_section
|
|
49
|
-
modified = status.select { |l| l[0] == "M" || l[1] == "M" }.map { |l| l[3..] }
|
|
50
|
-
added = status.select { |l| l[0] == "A" || l.start_with?("??") }.map { |l| l[3..] }
|
|
51
|
-
deleted = status.select { |l| l[0] == "D" || l[1] == "D" }.map { |l| l[3..] }
|
|
52
|
-
|
|
53
|
-
lines = []
|
|
54
|
-
lines << "Modified: #{modified.join(', ')}" if modified.any?
|
|
55
|
-
lines << "Added: #{added.join(', ')}" if added.any?
|
|
56
|
-
lines << "Deleted: #{deleted.join(', ')}" if deleted.any?
|
|
57
|
-
lines.any? ? "Working tree:\n#{lines.join("\n")}" : nil
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def diff_section
|
|
61
|
-
lines = []
|
|
62
|
-
lines << "Staged:\n#{truncate(staged_diff)}" if staged_diff.any?
|
|
63
|
-
lines << "Unstaged:\n#{truncate(unstaged_diff)}" if unstaged_diff.any?
|
|
64
|
-
lines.join("\n\n")
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def recent_commits_section
|
|
68
|
-
"Recent commits:\n#{recent_commits.join("\n")}"
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def truncate(lines)
|
|
72
|
-
if lines.size > MAX_DIFF_LINES
|
|
73
|
-
lines.first(MAX_DIFF_LINES).join + "\n... (#{lines.size - MAX_DIFF_LINES} more lines)"
|
|
74
|
-
else
|
|
75
|
-
lines.join
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
You are a trusted terminal agent. You act on behalf of the user - executing tasks directly through bash, files, and git. Be capable, be direct, be done.
|
|
2
|
-
|
|
3
|
-
## Principles
|
|
4
|
-
|
|
5
|
-
- Act, don't explain. Execute the task.
|
|
6
|
-
- Read before write. Understand existing code first.
|
|
7
|
-
- Small focused changes. One thing at a time.
|
|
8
|
-
- Verify your work. Run tests, check output.
|
|
9
|
-
|
|
10
|
-
## System
|
|
11
|
-
|
|
12
|
-
<%= `uname -s`.strip %> · <%= ENV['PWD'] %>
|
|
13
|
-
|
|
14
|
-
## Git State
|
|
15
|
-
|
|
16
|
-
<%= Elelem::GitContext.new.to_s %>
|