Comparing changes

v0.4.1 v0.4.2
12 commits 12 files changed

Commits

1049922 chore: release v0.4.2 mo khan 2025-12-01 23:42:30
18e419e feat: simplify the system prompt mo khan 2025-12-01 18:19:49
66c4e16 feat: improve the system prompt mo khan 2025-12-01 17:28:19
114fe74 feat: return if result is nil mo khan 2025-11-27 22:28:21
14aea18 feat: improve system prompt mo khan 2025-11-27 22:23:51
6e81ccc feat: improve system prompt mo khan 2025-11-27 22:10:45
b3b4c4e test: execute -> bash mo khan 2025-11-27 21:21:21
a7219fb feat: rename exec tool to bash tool mo khan 2025-11-27 18:04:22
6a8a7f2 style: improve Assistant output mo khan 2025-11-27 18:01:53
lib/elelem/agent.rb
@@ -66,6 +66,7 @@ module Elelem
     end
 
     def format_tool_call_result(result)
+      return if result.nil?
       return result["stdout"] if result["stdout"]
       return result["stderr"] if result["stderr"]
       return result[:error] if result[:error]
@@ -80,7 +81,7 @@ module Elelem
         content = ""
         tool_calls = []
 
-        print "Thinking..."
+        print "Assistant> Thinking..."
         client.chat(messages + turn_context, tools) do |chunk|
           msg = chunk["message"]
           if msg
lib/elelem/conversation.rb
@@ -45,19 +45,19 @@ module Elelem
 
       case mode.sort
       when [:read]
-        "#{base}\n\nRead and analyze. Understand before suggesting action."
+        "#{base}\n\nYou may read files on the system."
       when [:write]
-        "#{base}\n\nWrite clean, thoughtful code."
+        "#{base}\n\nYou may write files on the system."
       when [:execute]
-        "#{base}\n\nUse shell commands creatively to understand and manipulate the system."
+        "#{base}\n\nYou may execute shell commands on the system."
       when [:read, :write]
-        "#{base}\n\nFirst understand, then build solutions that integrate well."
+        "#{base}\n\nYou may read and write files on the system."
       when [:execute, :read]
-        "#{base}\n\nUse commands to deeply understand the system."
+        "#{base}\n\nYou may execute shell commands and read files on the system."
       when [:execute, :write]
-        "#{base}\n\nCreate and execute freely. Have fun. Be kind."
+        "#{base}\n\nYou may execute shell commands and write files on the system."
       when [:execute, :read, :write]
-        "#{base}\n\nYou have all tools. Use them wisely."
+        "#{base}\n\nYou may read files, write files and execute shell commands on the system."
       else
         base
       end
lib/elelem/system_prompt.erb
@@ -1,5 +1,15 @@
-You are a reasoning coding and system agent working from: <%= Dir.pwd %>.
+You are a reasoning coding and system agent.
 
-- Less is more
-- No code comments
-- No trailing whitespace
+## System
+
+Operating System: <%= `uname -a` %>
+USER: <%= ENV['USER'] %>
+HOME: <%= ENV['HOME'] %>
+SHELL: <%= ENV['SHELL'] %>
+PATH: <%= ENV['PATH'] %>
+PWD: <%= ENV['PWD'] %>
+LANG: <%= ENV['LANG'] %>
+EDITOR: <%= ENV['EDITOR'] %>
+LOGNAME: <%= ENV['LOGNAME'] %>
+TERM: <%= ENV['TERM'] %>
+MAIL: <%= ENV['MAIL'] %>
lib/elelem/tool.rb
@@ -11,7 +11,9 @@ module Elelem
     end
 
     def call(args)
-      return ArgumentError.new(args) unless valid?(args)
+      unless valid?(args)
+        return { error: "Invalid args for #{@name}", received: args.keys, expected: @schema.dig(:function, :parameters, :required) }
+      end
 
       @block.call(args)
     end
lib/elelem/toolbox.rb
@@ -8,7 +8,7 @@ module Elelem
       full_path.exist? ? { content: full_path.read } : { error: "File not found: #{path}" }
     end
 
-    EXEC_TOOL = Tool.build("execute", "Run shell commands. For git: execute({\"cmd\": \"git\", \"args\": [\"log\", \"--oneline\"]}). Returns stdout/stderr/exit_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"]) do |args|
+    BASH_TOOL = Tool.build("bash", "Run shell commands. For git: bash({\"cmd\": \"git\", \"args\": [\"log\", \"--oneline\"]}). Returns stdout/stderr/exit_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"]) do |args|
       Elelem.shell.execute(
         args["cmd"],
         args: args["args"] || [],
@@ -42,7 +42,7 @@ module Elelem
       @tools_by_name = {}
       @tools = { read: [], write: [], execute: [] }
       add_tool(eval_tool(binding), :execute)
-      add_tool(EXEC_TOOL, :execute)
+      add_tool(BASH_TOOL, :execute)
       add_tool(GREP_TOOL, :read)
       add_tool(LIST_TOOL, :read)
       add_tool(PATCH_TOOL, :write)
lib/elelem/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 module Elelem
-  VERSION = "0.4.1"
+  VERSION = "0.4.2"
 end
spec/elelem/agent_spec.rb
@@ -28,8 +28,8 @@ RSpec.describe Elelem::Agent do
       read_history = conversation.history_for([:read])
       write_history = conversation.history_for([:write])
 
-      expect(read_history[0][:content]).to include("Read and analyze")
-      expect(write_history[0][:content]).to include("Write clean, thoughtful code")
+      expect(read_history[0][:content]).to include("You may read files on the system")
+      expect(write_history[0][:content]).to include("You may write files on the system")
       expect(read_history[0][:content]).not_to eq(write_history[0][:content])
     end
   end
spec/elelem/conversation_spec.rb
@@ -10,43 +10,43 @@ RSpec.describe Elelem::Conversation do
 
         expect(history.length).to eq(1)
         expect(history[0][:role]).to eq("system")
-        expect(history[0][:content]).to include("Read and analyze")
+        expect(history[0][:content]).to include("You may read files on the system")
       end
 
       it "returns history with mode-specific system prompt for write mode" do
         history = conversation.history_for([:write])
 
-        expect(history[0][:content]).to include("Write clean, thoughtful code")
+        expect(history[0][:content]).to include("You may write files on the system")
       end
 
       it "returns history with mode-specific system prompt for execute mode" do
         history = conversation.history_for([:execute])
 
-        expect(history[0][:content]).to include("Use shell commands creatively")
+        expect(history[0][:content]).to include("You may execute shell commands on the system")
       end
 
       it "returns history with mode-specific system prompt for read+write mode" do
         history = conversation.history_for([:read, :write])
 
-        expect(history[0][:content]).to include("First understand, then build solutions")
+        expect(history[0][:content]).to include("You may read and write files on the system")
       end
 
       it "returns history with mode-specific system prompt for read+execute mode" do
         history = conversation.history_for([:read, :execute])
 
-        expect(history[0][:content]).to include("Use commands to deeply understand")
+        expect(history[0][:content]).to include("You may execute shell commands and read files on the system")
       end
 
       it "returns history with mode-specific system prompt for write+execute mode" do
         history = conversation.history_for([:write, :execute])
 
-        expect(history[0][:content]).to include("Create and execute freely")
+        expect(history[0][:content]).to include("You may execute shell commands and write files on the system")
       end
 
       it "returns history with mode-specific system prompt for all tools mode" do
         history = conversation.history_for([:read, :write, :execute])
 
-        expect(history[0][:content]).to include("You have all tools")
+        expect(history[0][:content]).to include("You may read files, write files and execute shell commands on the system")
       end
 
       it "returns base system prompt for unknown mode" do
@@ -182,7 +182,7 @@ RSpec.describe Elelem::Conversation do
       parsed = JSON.parse(json)
       expect(parsed).to be_an(Array)
       expect(parsed.length).to eq(2)
-      expect(parsed[0]["content"]).to include("Read and analyze")
+      expect(parsed[0]["content"]).to include("You may read files on the system")
     end
   end
 end
spec/elelem/toolbox_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Elelem::Toolbox do
 
       tool_names = tools.map { |t| t.dig(:function, :name) }
       expect(tool_names).to include("grep", "list", "read")
-      expect(tool_names).not_to include("write", "patch", "execute")
+      expect(tool_names).not_to include("write", "patch", "bash")
     end
 
     it "returns write tools for write mode" do
@@ -19,7 +19,7 @@ RSpec.describe Elelem::Toolbox do
 
       tool_names = tools.map { |t| t.dig(:function, :name) }
       expect(tool_names).to include("patch", "write")
-      expect(tool_names).not_to include("grep", "execute")
+      expect(tool_names).not_to include("grep", "bash")
     end
 
     it "returns execute tools for execute mode" do
@@ -27,7 +27,7 @@ RSpec.describe Elelem::Toolbox do
       tools = subject.tools_for(mode)
 
       tool_names = tools.map { |t| t.dig(:function, :name) }
-      expect(tool_names).to include("execute")
+      expect(tool_names).to include("bash")
       expect(tool_names).not_to include("grep", "write")
     end
 
@@ -36,7 +36,7 @@ RSpec.describe Elelem::Toolbox do
       tools = subject.tools_for(mode)
 
       tool_names = tools.map { |t| t.dig(:function, :name) }
-      expect(tool_names).to include("grep", "list", "read", "patch", "write", "execute")
+      expect(tool_names).to include("grep", "list", "read", "patch", "write", "bash")
     end
 
     it "returns combined tools for build mode" do
@@ -45,7 +45,7 @@ RSpec.describe Elelem::Toolbox do
 
       tool_names = tools.map { |t| t.dig(:function, :name) }
       expect(tool_names).to include("grep", "read", "write", "patch")
-      expect(tool_names).not_to include("execute")
+      expect(tool_names).not_to include("bash")
     end
   end
 
CHANGELOG.md
@@ -1,9 +1,25 @@
 ## [Unreleased]
 
+## [0.4.2] - 2025-12-01
+
+### Changed
+- Renamed `exec` tool to `bash` for clarity
+- Improved system prompt with iterative refinements
+- Added environment context variables to system prompt
+
 ## [0.4.1] - 2025-11-26
 
 ### Added
-- Updated version to 0.4.1
+- `elelem files` subcommand: generates Claude‑compatible XML file listings.
+- Rake task `files:prompt` to output a ready‑to‑copy list of files for prompts.
+
+### Changed
+- Refactor tool‑call formatting to a more compact JSON payload for better LLM parsing.
+- Updated CI and documentation to use GitHub instead of previous hosting.
+- Runtime validation of command‑line parameters against a JSON schema.
+
+### Fixed
+- Minor documentation and CI workflow adjustments.
 
 ## [0.4.0] - 2025-11-10
 
@@ -122,16 +138,3 @@
 
 - Initial release
 
-## [0.4.2] - 2025-11-27
-
-### Added
-- `elelem files` subcommand: generates Claude‑compatible XML file listings.
-- Rake task `files:prompt` to output a ready‑to‑copy list of files for prompts.
-
-### Changed
-- Refactor tool‑call formatting to a more compact JSON payload for better LLM parsing.
-- Updated CI and documentation to use GitHub instead of previous hosting.
-- Runtime validation of command‑line parameters against a JSON schema.
-
-### Fixed
-- Minor documentation and CI workflow adjustments.
Gemfile.lock
@@ -1,7 +1,7 @@
 PATH
   remote: .
   specs:
-    elelem (0.4.1)
+    elelem (0.4.2)
       erb
       fileutils
       json
README.md
@@ -125,13 +125,13 @@ seven tools, each represented by a JSON schema that the LLM can call.
 
 | Tool      | Purpose                              | Parameters                           |
 | ----      | -------                              | ----------                           |
+| `bash`    | Run shell commands                   | `cmd`, `args`, `env`, `cwd`, `stdin` |
 | `eval`    | Dynamically create new tools         | `code`                               |
 | `grep`    | Search Git‑tracked files             | `query`                              |
 | `list`    | List tracked files                   | `path` (optional)                    |
+| `patch`   | Apply a unified diff via `git apply` | `diff`                               |
 | `read`    | Read file contents                   | `path`                               |
 | `write`   | Overwrite a file                     | `path`, `content`                    |
-| `patch`   | Apply a unified diff via `git apply` | `diff`                               |
-| `execute` | Run shell commands                   | `cmd`, `args`, `env`, `cwd`, `stdin` |
 
 ## Tool Definition