Commit 67c5a22
Changed files (6)
lib
elelem
lib/elelem/plugins/verify.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Elelem
+ module Verifiers
+ SYNTAX = {
+ ".rb" => "ruby -c %{path}",
+ ".py" => "python -m py_compile %{path}",
+ ".go" => "go vet %{path}",
+ ".rs" => "cargo check --quiet",
+ ".ts" => "npx tsc --noEmit %{path}",
+ ".js" => "node --check %{path}",
+ }.freeze
+
+ def self.for(path)
+ return [] unless path
+
+ cmds = []
+ ext = File.extname(path)
+ cmds << (SYNTAX[ext] % { path: path }) if SYNTAX[ext]
+ cmds << test_runner
+ cmds.compact
+ end
+
+ def self.test_runner
+ %w[bin/test script/test].find { |s| File.executable?(s) }
+ end
+ end
+
+ Plugins.register(:verify) do |toolbox|
+ toolbox.after("write") do |_, result|
+ next if result[:error]
+
+ result[:verify] = {}
+ Verifiers.for(result[:path]).each do |cmd|
+ $stdout.puts "\n → verify: #{cmd}"
+ v = Elelem.sh("bash", args: ["-c", cmd]) { |x| $stdout.print(x) }
+ result[:verify][cmd] = v
+ break if v[:exit_status] != 0
+ end
+ end
+ end
+end
lib/elelem/agent.rb
@@ -81,10 +81,8 @@ module Elelem
def process(tool_call)
name, args = tool_call[:name], tool_call[:arguments]
- Elelem.emit(:tool_call, name: name, args: args)
terminal.say toolbox.header(name, args)
toolbox.run(name.to_s, args).tap do |result|
- Elelem.emit(:tool_result, name: name, args: args, result: result)
terminal.say toolbox.format_result(name, result)
end
end
lib/elelem/plugins.rb
@@ -2,9 +2,17 @@
module Elelem
module Plugins
- LOAD_PATHS = [".elelem/plugins", "~/.elelem/plugins"].freeze
+ LOAD_PATHS = [
+ File.expand_path("plugins", __dir__),
+ "~/.elelem/plugins",
+ ".elelem/plugins"
+ ].freeze
- def self.load!
+ def self.init
+ FileUtils.mkdir_p(File.expand_path(LOAD_PATHS.last))
+ end
+
+ def self.setup!(toolbox)
LOAD_PATHS.each do |path|
dir = File.expand_path(path)
next unless File.directory?(dir)
@@ -15,6 +23,7 @@ module Elelem
warn "elelem: failed to load plugin #{file}: #{e.message}"
end
end
+ registry.each_value { |plugin| plugin.call(toolbox) }
end
def self.register(name, &block)
lib/elelem/toolbox.rb
@@ -42,7 +42,6 @@ module Elelem
def initialize(tools = TOOLS.dup)
@tools = tools
@hooks = { before: Hash.new { |h, k| h[k] = [] }, after: Hash.new { |h, k| h[k] = [] } }
- setup_default_hooks
end
def add(name, tool)
@@ -122,26 +121,5 @@ module Elelem
parts << (result[:error] ? " ! #{text.lines.first&.strip}" : text)
end
-
- def test_commands_for(path)
- commands = []
- commands << "ruby -c #{path}" if path&.end_with?(".rb")
- commands << %w[script/test bin/test].find { |s| File.executable?(s) }
- commands.compact
- end
-
- def setup_default_hooks
- after("write") do |args, result|
- next if result[:error]
-
- result[:verify] = {}
- test_commands_for(result[:path]).each do |cmd|
- $stdout.puts "\n → verify: #{cmd}"
- v = Elelem.sh("bash", args: ["-c", cmd]) { |x| $stdout.print(x) }
- result[:verify][cmd] = v
- break if v[:exit_status] != 0
- end
- end
- end
end
end
lib/elelem.rb
@@ -36,12 +36,12 @@ module Elelem
end
def self.start(client, toolbox: Toolbox.new)
- Plugins.load!
+ Plugins.setup!(toolbox)
Agent.new(client, toolbox).repl
end
def self.ask(client, prompt, toolbox: Toolbox.new)
- Plugins.load!
+ Plugins.setup!(toolbox)
agent = Agent.new(client, toolbox, terminal: Terminal.new(quiet: true))
agent.turn(prompt)
agent.history.last[:content]
elelem.gemspec
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
"lib/elelem/events.rb",
"lib/elelem/mcp.rb",
"lib/elelem/plugins.rb",
+ "lib/elelem/plugins/verify.rb",
"lib/elelem/terminal.rb",
"lib/elelem/toolbox.rb",
"lib/elelem/version.rb",