Commit 703899d

mo khan <mo@mokhan.ca>
2025-11-05 23:30:07
refactor: collapse everything into agent.rb
1 parent 283825a
lib/elelem/agent.rb
@@ -7,16 +7,8 @@ module Elelem
     def initialize(client)
       @conversation = Conversation.new
       @client = client
-
-      exec_tool = build_tool("execute", "Execute shell commands directly. Returns stdout, stderr, and exit code. Commands run in a shell context. Examples: 'date', 'git status', 'ls -la'. Use for: checking system state, running tests, managing services. Tip: Check exit_status in response to determine success.", { cmd: { type: "string" }, args: { type: "array", items: { type: "string" } }, env: { type: "object", additionalProperties: { type: "string" } }, cwd: { type: "string", description: "Working directory for command execution (defaults to current directory if not specified)" }, stdin: { type: "string" } }, ["cmd"])
-      grep_tool = build_tool("grep", "Search all git-tracked files using git grep. Returns file paths with matching line numbers. Use this to discover where code/configuration exists before reading files. Examples: search 'def method_name' to find method definitions. Much faster than reading multiple files.", { query: { type: "string" } }, ["query"])
-      ls_tool = build_tool("list", "List all git-tracked files in the repository, optionally filtered by path. Use this to explore project structure or find files in a directory. Returns relative paths from repo root. Tip: Use this before reading if you need to discover what files exist.", { path: { type: "string" } })
-      patch_tool = build_tool("patch", "Apply a unified diff patch via 'git apply'. Use this for surgical edits to existing files rather than rewriting entire files. Generates proper git diffs. Format: standard unified diff with --- and +++ headers. Tip: More efficient than write for small changes to large files.", { diff: { type: "string" } }, ["diff"])
-      read_tool = build_tool("read", "Read complete contents of a file. Requires exact file path. Use grep or list first if you don't know the path. Best for: understanding existing code, reading config files, reviewing implementation details. Tip: For large files, grep first to confirm relevance.", { path: { type: "string" } }, ["path"])
-      write_tool = build_tool("write", "Write complete file contents (overwrites existing files). Creates parent directories automatically. Best for: creating new files, replacing entire file contents. For small edits to existing files, consider using patch instead.", { path: { type: "string" }, content: { type: "string" } }, ["path", "content"])
-
       @tools = {
-        read: [grep_tool, ls_tool, read_tool],
+        read: [grep_tool, list_tool, read_tool],
         write: [patch_tool, write_tool],
         execute: [exec_tool]
       }
@@ -204,6 +196,65 @@ module Elelem
       { error: error.message, name: name, args: args }
     end
 
+    def exec_tool
+      build_tool(
+        "execute",
+        "Execute shell commands directly. Commands run in a shell context. Examples: 'date', 'git status'.",
+        {
+          cmd: { type: "string" },
+          args: { type: "array", items: { type: "string" } },
+          env: { type: "object", additionalProperties: { type: "string" } },
+          cwd: { type: "string", description: "Working directory (defaults to current)" },
+          stdin: { type: "string" }
+        },
+        ["cmd"]
+      )
+    end
+
+    def grep_tool
+      build_tool(
+        "grep",
+        "Search all git-tracked files using git grep. Returns file paths with matching line numbers.",
+        { query: { type: "string" } },
+        ["query"]
+      )
+    end
+
+    def list_tool
+      build_tool(
+        "list",
+        "List all git-tracked files in the repository, optionally filtered by path.",
+        { path: { type: "string" } }
+      )
+    end
+
+    def patch_tool
+      build_tool(
+        "patch",
+        "Apply a unified diff patch via 'git apply'. Use for surgical edits to existing files.",
+        { diff: { type: "string" } },
+        ["diff"]
+      )
+    end
+
+    def read_tool
+      build_tool(
+        "read",
+        "Read complete contents of a file. Requires exact file path.",
+        { path: { type: "string" } },
+        ["path"]
+      )
+    end
+
+    def write_tool
+      build_tool(
+        "write",
+        "Write complete file contents (overwrites existing files). Creates parent directories automatically.",
+        { path: { type: "string" }, content: { type: "string" } },
+        ["path", "content"]
+      )
+    end
+
     def build_tool(name, description, properties, required = [])
       {
         type: "function",
lib/elelem/system_prompt.erb
@@ -1,1 +1,5 @@
 You are a reasoning coding and system agent.
+
+- Less is more
+- No code comments
+- No trailing whitespace
lib/elelem/tool.rb
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Elelem
-  class Tool
-    attr_reader :name, :description, :parameters
-
-    def initialize(name, description, parameters, &block)
-      @name = name
-      @description = description
-      @parameters = parameters
-      @block = block
-    end
-
-    def valid?(args)
-      JSON::Validator.validate(parameters, args, insert_defaults: true)
-    end
-
-    def call(*args)
-      @block.call(*args)
-    end
-
-    def to_h
-      {
-        type: "function",
-        function: {
-          name: name,
-          description: description,
-          parameters: parameters
-        }
-      }
-    end
-  end
-end
lib/elelem/tools.rb
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module Elelem
-  class Tools
-    def initialize(tools)
-      @tools = tools
-    end
-
-    def add(name, description, parameters, &block)
-      @tools << Tool.new(name, description, parameters, &block)
-    end
-
-    def execute(tool_call)
-      name, args = parse(tool_call)
-
-      tool = tools.find { |tool| tool.name == name }
-      return "Invalid function name: #{name}" if tool.nil?
-      return "Invalid function arguments: #{args}" unless tool.valid?(args)
-
-      tool.call(args)
-    end
-
-    def to_h
-      tools.map(&:to_h)
-    end
-
-    private
-
-    attr_reader :tools
-
-    def parse(tool_call)
-      name = tool_call.dig("function", "name")
-      arguments = tool_call.dig("function", "arguments")
-
-      [name, arguments.is_a?(String) ? JSON.parse(arguments) : arguments]
-    end
-  end
-end
lib/elelem.rb
@@ -16,8 +16,6 @@ require "timeout"
 require_relative "elelem/agent"
 require_relative "elelem/application"
 require_relative "elelem/conversation"
-require_relative "elelem/tool"
-require_relative "elelem/tools"
 require_relative "elelem/version"
 
 Reline.input = $stdin
elelem.gemspec
@@ -38,8 +38,6 @@ Gem::Specification.new do |spec|
     "lib/elelem/application.rb",
     "lib/elelem/conversation.rb",
     "lib/elelem/system_prompt.erb",
-    "lib/elelem/tool.rb",
-    "lib/elelem/tools.rb",
     "lib/elelem/version.rb",
   ]
   spec.bindir = "exe"