Comparing changes

v0.4.0 v0.4.1
8 commits 12 files changed
.github/workflow/ci.yml
@@ -0,0 +1,21 @@
+name: CI
+on:
+  push:
+    branches: [ "main" ]
+  pull_request:
+    branches: [ "main" ]
+permissions:
+  contents: read
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        ruby-version: ['3.4']
+    steps:
+    - uses: actions/checkout@v4
+    - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
+      with:
+        ruby-version: ${{ matrix.ruby-version }}
+        bundler-cache: true # runs 'bundle install' and caches installed gems automatically
+    - run: sh bin/test
lib/elelem/agent.rb
@@ -65,20 +65,12 @@ module Elelem
       HELP
     end
 
-    def format_tool_call(name, args)
-      case name
-      when "execute"
-        cmd = args["cmd"]
-        cmd_args = args["args"] || []
-        cmd_args.empty? ? cmd : "#{cmd} #{cmd_args.join(' ')}"
-      when "grep" then "grep(#{args["query"]})"
-      when "list" then "list(#{args["path"] || "."})"
-      when "patch" then "patch(#{args["diff"]&.lines&.count || 0} lines)"
-      when "read" then "read(#{args["path"]})"
-      when "write" then "write(#{args["path"]})"
-      else
-        "#{name}(#{args.to_s[0...50]})"
-      end
+    def format_tool_call_result(result)
+      return result["stdout"] if result["stdout"]
+      return result["stderr"] if result["stderr"]
+      return result[:error] if result[:error]
+
+      ""
     end
 
     def execute_turn(messages, tools:)
@@ -110,8 +102,9 @@ module Elelem
             name = call.dig("function", "name")
             args = call.dig("function", "arguments")
 
-            puts "Tool> #{format_tool_call(name, args)}"
+            puts "Tool> #{name}(#{args})"
             result = toolbox.run_tool(name, args)
+            puts format_tool_call_result(result)
             turn_context << { role: "tool", content: JSON.dump(result) }
           end
 
lib/elelem/application.rb
@@ -13,7 +13,6 @@ module Elelem
                   type: :string,
                   desc: "Ollama model",
                   default: ENV.fetch("OLLAMA_MODEL", "gpt-oss")
-
     def chat(*)
       client = Net::Llm::Ollama.new(
         host: options[:host],
@@ -24,6 +23,20 @@ module Elelem
       agent.repl
     end
 
+    desc "files", "Generate CXML of the files"
+    def files
+      puts '<documents>'
+      $stdin.read.split("\n").map(&:strip).reject(&:empty?).each_with_index do |file, i|
+        next unless File.file?(file)
+
+        puts "  <document index=\"#{i + 1}\">"
+        puts "    <source><![CDATA[#{file}]]></source>"
+        puts "    <document_content><![CDATA[#{File.read(file)}]]></document_content>"
+        puts "  </document>"
+      end
+      puts '</documents>'
+    end
+
     desc "version", "The version of this CLI"
     def version
       say "v#{Elelem::VERSION}"
lib/elelem/system_prompt.erb
@@ -1,4 +1,4 @@
-You are a reasoning coding and system agent.
+You are a reasoning coding and system agent working from: <%= Dir.pwd %>.
 
 - Less is more
 - No code comments
lib/elelem/tool.rb
@@ -17,8 +17,7 @@ module Elelem
     end
 
     def valid?(args)
-      # TODO:: Use JSON Schema Validator
-      true
+      JSON::Validator.validate(@schema.dig(:function, :parameters), args)
     end
 
     def to_h
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", "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"]) do |args|
+    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|
       Elelem.shell.execute(
         args["cmd"],
         args: args["args"] || [],
lib/elelem/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 module Elelem
-  VERSION = "0.4.0"
+  VERSION = "0.4.1"
 end
.gitlab-ci.yml
@@ -1,12 +0,0 @@
-default:
-  image: ruby:3.4.4
-
-  before_script:
-    - apt-get update && apt-get install -y clang
-    - gem update --system '3.7.2'
-    - gem install bundler -v 2.7.2
-    - bundle install
-
-test:
-  script:
-    - ./bin/test
CHANGELOG.md
@@ -1,5 +1,10 @@
 ## [Unreleased]
 
+## [0.4.1] - 2025-11-26
+
+### Added
+- Updated version to 0.4.1
+
 ## [0.4.0] - 2025-11-10
 
 ### Added
@@ -116,3 +121,17 @@
 ## [0.1.0] - 2025-08-08
 
 - 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.
elelem.gemspec
@@ -10,14 +10,14 @@ Gem::Specification.new do |spec|
 
   spec.summary = "A REPL for Ollama."
   spec.description = "A REPL for Ollama."
-  spec.homepage = "https://gitlab.com/mokhax/elelem"
+  spec.homepage = "https://github.com/xlgmokha/elelem"
   spec.license = "MIT"
   spec.required_ruby_version = ">= 3.4.0"
   spec.required_rubygems_version = ">= 3.3.11"
   spec.metadata["allowed_push_host"] = "https://rubygems.org"
   spec.metadata["homepage_uri"] = spec.homepage
-  spec.metadata["source_code_uri"] = "https://gitlab.com/mokhax/elelem"
-  spec.metadata["changelog_uri"] = "https://gitlab.com/mokhax/elelem/-/blob/main/CHANGELOG.md"
+  spec.metadata["source_code_uri"] = "https://github.com/xlgmokha/elelem"
+  spec.metadata["changelog_uri"] = "https://github.com/xlgmokha/elelem/blob/main/CHANGELOG.md"
 
   # Specify which files should be added to the gem when it is released.
   # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
Gemfile.lock
@@ -1,7 +1,7 @@
 PATH
   remote: .
   specs:
-    elelem (0.4.0)
+    elelem (0.4.1)
       erb
       fileutils
       json
Rakefile
@@ -5,4 +5,15 @@ require "rspec/core/rake_task"
 
 RSpec::Core::RakeTask.new(:spec)
 
+task :files do
+  IO.popen(%w[git ls-files], chdir: __dir__, err: IO::NULL) do |ls|
+    ls.readlines.each do |f|
+      next if f.start_with?(*%w[bin/ spec/ pkg/ .git .rspec Gemfile Rakefile])
+      next if f.strip.end_with?(*%w[.toml .txt .md])
+
+      puts f
+    end
+  end
+end
+
 task default: %i[spec]