Commit 67c5a22

mo khan <mo@mokhan.ca>
2026-01-20 20:31:46
refactor: extract default verify plugin
1 parent 89322cb
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",