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.
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.8.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: cgi
13
+ name: date
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.1'
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.1'
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: json-schema
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: '1.0'
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: '1.0'
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: net-llm
97
+ name: open3
126
98
  requirement: !ruby/object:Gem::Requirement
127
99
  requirements:
128
100
  - - "~>"
129
101
  - !ruby/object:Gem::Version
130
- version: '0.5'
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.5'
141
- - - ">="
142
- - !ruby/object:Gem::Version
143
- version: 0.5.0
109
+ version: '0.1'
144
110
  - !ruby/object:Gem::Dependency
145
- name: open3
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: set
153
+ name: stringio
188
154
  requirement: !ruby/object:Gem::Requirement
189
155
  requirements:
190
156
  - - "~>"
191
157
  - !ruby/object:Gem::Version
192
- version: '1.0'
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: '1.0'
165
+ version: '3.0'
200
166
  - !ruby/object:Gem::Dependency
201
- name: thor
167
+ name: tempfile
202
168
  requirement: !ruby/object:Gem::Requirement
203
169
  requirements:
204
170
  - - "~>"
205
171
  - !ruby/object:Gem::Version
206
- version: '1.0'
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: '1.0'
179
+ version: '0.3'
214
180
  - !ruby/object:Gem::Dependency
215
- name: timeout
181
+ name: uri
216
182
  requirement: !ruby/object:Gem::Requirement
217
183
  requirements:
218
184
  - - "~>"
219
185
  - !ruby/object:Gem::Version
220
- version: '0.1'
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.1'
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/application.rb
244
- - lib/elelem/conversation.rb
245
- - lib/elelem/git_context.rb
246
- - lib/elelem/system_prompt.erb
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: 3.4.0
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: 3.3.11
249
+ version: 4.0.0
272
250
  requirements: []
273
- rubygems_version: 3.7.2
251
+ rubygems_version: 4.0.4
274
252
  specification_version: 4
275
253
  summary: A minimal coding agent for LLMs.
276
254
  test_files: []
@@ -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
@@ -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
@@ -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 %>