Commit 3c793a0
Changed files (11)
lib
elelem
lib/elelem/builtins/builtins.rb
@@ -8,88 +8,6 @@ Elelem::Plugins.register(:builtins) do |agent|
agent.terminal.say " → context cleared"
end
- agent.commands.register("context", description: "Show conversation context") do |args|
- messages = agent.context
-
- case args
- when nil, ""
- messages.each_with_index do |msg, i|
- role = msg[:role]
- preview = msg[:content].to_s.lines.first&.strip&.slice(0, 60) || ""
- preview += "..." if msg[:content].to_s.length > 60
- agent.terminal.say " #{i + 1}. #{role}: #{preview}"
- end
- when "json"
- agent.terminal.say JSON.pretty_generate(messages)
- when /^\d+$/
- index = args.to_i - 1
- if index >= 0 && index < messages.length
- content = messages[index][:content].to_s
- agent.terminal.say(agent.terminal.markdown(content))
- else
- agent.terminal.say " Invalid index: #{args}"
- end
- else
- agent.terminal.say " Usage: /context [json|<number>]"
- end
- end
-
- strip_ansi = ->(text) do
- text
- .gsub(/^Script started.*?\n/, "")
- .gsub(/\nScript done.*$/, "")
- .gsub(/\e\].*?(?:\a|\e\\)/, "")
- .gsub(/\e\[[0-9;?]*[A-Za-z]/, "")
- .gsub(/\e[PX^_].*?\e\\/, "")
- .gsub(/\e./, "")
- .gsub(/[\b]/, "")
- .gsub(/\r/, "")
- end
-
- agent.commands.register("shell", description: "Start interactive shell") do
- transcript = Tempfile.create do |file|
- system("script", "-q", file.path, chdir: Dir.pwd)
- strip_ansi.call(File.read(file.path))
- end
- agent.conversation.add(role: "user", content: transcript) unless transcript.strip.empty?
- end
-
- agent.commands.register("init", description: "Generate AGENTS.md") do
- system_prompt = <<~PROMPT
- AGENTS.md generator. Analyze codebase and write AGENTS.md to project root.
-
- # AGENTS.md Spec (https://agents.md/)
- A file providing context and instructions for AI coding agents.
-
- ## Recommended Sections
- - Commands: build, test, lint commands
- - Code Style: conventions, patterns
- - Architecture: key components and flow
- - Testing: how to run tests
-
- ## Process
- 1. Read README.md if present
- 2. Identify language (Gemfile, package.json, go.mod)
- 3. Find test scripts (bin/test, npm test)
- 4. Check linter configs
- 5. Write concise AGENTS.md
-
- Keep it minimal. No fluff.
- PROMPT
-
- agent.fork(system_prompt: system_prompt).turn("Generate AGENTS.md for this project")
- end
-
- agent.commands.register("reload", description: "Reload plugins and source") do
- lib_dir = File.expand_path("../..", __dir__)
- original_verbose, $VERBOSE = $VERBOSE, nil
- Dir["#{lib_dir}/**/*.rb"].sort.each { |f| load(f) }
- $VERBOSE = original_verbose
- agent.toolbox = Elelem::Toolbox.new
- agent.commands = Elelem::Commands.new
- Elelem::Plugins.reload!(agent)
- end
-
agent.commands.register("help", description: "Show available commands") do
agent.terminal.say agent.commands.names.join(" ")
end
lib/elelem/builtins/context.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+Elelem::Plugins.register(:context) do |agent|
+ agent.commands.register("context", description: "Show conversation context") do |args|
+ messages = agent.context
+
+ case args
+ when nil, ""
+ messages.each_with_index do |msg, i|
+ role = msg[:role]
+ preview = msg[:content].to_s.lines.first&.strip&.slice(0, 60) || ""
+ preview += "..." if msg[:content].to_s.length > 60
+ agent.terminal.say " #{i + 1}. #{role}: #{preview}"
+ end
+ when "json"
+ agent.terminal.say JSON.pretty_generate(messages)
+ when /^\d+$/
+ index = args.to_i - 1
+ if index >= 0 && index < messages.length
+ content = messages[index][:content].to_s
+ agent.terminal.say(agent.terminal.markdown(content))
+ else
+ agent.terminal.say " Invalid index: #{args}"
+ end
+ else
+ agent.terminal.say " Usage: /context [json|<number>]"
+ end
+ end
+end
lib/elelem/builtins/eval.rb
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-Elelem::Plugins.register(:eval) do |agent|
- description = <<~'DESC'
- Evaluate Ruby code. Available API:
-
- name = "search"
- agent.toolbox.add(name, description: "Search using rg", params: { query: { type: "string" } }, required: ["query"], aliases: []) do |args|
- agent.toolbox.run("execute", { "command" => "rg --json -nI -F #{args["query"]}" })
- end
- DESC
-
- agent.toolbox.add("eval",
- description: description,
- params: { ruby: { type: "string" } },
- required: ["ruby"]
- ) do |args|
- { result: binding.eval(args["ruby"]) }
- end
-end
lib/elelem/builtins/git.rb
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-Elelem::Plugins.register(:git) do |agent|
- allowed = %w[status diff log show branch checkout add reset stash].freeze
-
- agent.toolbox.add("git",
- description: "Run git command",
- params: { command: { type: "string" }, args: { type: "array" } },
- required: ["command"]
- ) do |a|
- cmd = a["command"]
- next { error: "not allowed: #{cmd}" } unless allowed.include?(cmd)
-
- agent.toolbox.exec("git", cmd, *(a["args"] || []))
- end
-
- agent.toolbox.after("git") do |_, result|
- agent.terminal.say " ! #{result[:error]}" if result[:error]
- end
-end
lib/elelem/builtins/glob.rb
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-Elelem::Plugins.register(:glob) do |agent|
- agent.toolbox.add("glob",
- description: "Find files matching pattern",
- params: { pattern: { type: "string" }, path: { type: "string" } },
- required: ["pattern"]
- ) do |a|
- path = a["path"] || "."
- result = agent.toolbox.exec("fd", "--glob", a["pattern"], path)
- result[:ok] ? result : agent.toolbox.exec("find", path, "-name", a["pattern"])
- end
-end
lib/elelem/builtins/grep.rb
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-Elelem::Plugins.register(:grep) do |agent|
- agent.toolbox.add("grep",
- description: "Search file contents",
- params: { pattern: { type: "string" }, path: { type: "string" }, glob: { type: "string" } },
- required: ["pattern"]
- ) do |a|
- path = a["path"] || "."
- glob = a["glob"]
- rg_args = ["rg", "-n", a["pattern"], path]
- rg_args += ["-g", glob] if glob
- result = agent.toolbox.exec(*rg_args)
- next result if result[:ok]
-
- grep_args = ["grep", "-rn"]
- grep_args += ["--include", glob] if glob
- grep_args += [a["pattern"], path]
- agent.toolbox.exec(*grep_args)
- end
-end
lib/elelem/builtins/interview.rb
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-Elelem::Plugins.register(:interview) do |agent|
- agent.toolbox.add("interview",
- description: "Ask the user a question and wait for their response",
- params: {
- question: { type: "string", description: "The question to ask the user" },
- },
- required: ["question"]
- ) do |args|
- agent.terminal.say(agent.terminal.markdown(args["question"]))
- { answer: agent.terminal.ask("> ") }
- end
-end
lib/elelem/builtins/list.rb
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-Elelem::Plugins.register(:list) do |agent|
- agent.toolbox.add("list",
- description: "List directory contents",
- params: { path: { type: "string" }, recursive: { type: "boolean" } },
- required: [],
- aliases: ["ls"]
- ) do |a|
- path = a["path"] || "."
- flags = a["recursive"] ? "-laR" : "-la"
- agent.toolbox.exec("ls", flags, path)
- end
-end
lib/elelem/builtins/mode.rb
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-Elelem::Plugins.register(:mode) do |agent|
- agent.commands.register("mode",
- description: "Switch system prompt mode",
- completions: -> { Elelem::SystemPrompt.available_modes }
- ) do |args|
- name = args&.strip
- if name.nil? || name.empty?
- current = agent.system_prompt.mode
- modes = Elelem::SystemPrompt.available_modes.map { |m| m == current ? "*#{m}" : m }
- agent.terminal.say modes.join(" ")
- else
- agent.system_prompt.switch(name)
- agent.terminal.say "mode: #{name}"
- end
- end
-
- agent.commands.register("compact", description: "Compress context") do
- response = agent.turn("Summarize: accomplishments, state, next steps. Brief.")
- agent.conversation.clear!
- agent.conversation.add(role: "user", content: "Context: #{response}")
- agent.terminal.say " → compacted"
- end
-end
lib/elelem/builtins/task.rb
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-Elelem::Plugins.register(:task) do |agent|
- agent.toolbox.add("task",
- description: "Delegate subtask to focused agent (complex searches, multi-file analysis)",
- params: { prompt: { type: "string" } },
- required: ["prompt"]
- ) do |a|
- sub = Elelem::Agent.new(agent.client, toolbox: agent.toolbox, terminal: agent.terminal,
- system_prompt: "Research agent. Search, analyze, report. Be concise.")
- sub.turn(a["prompt"])
- { result: sub.conversation.last[:content] }
- end
-end
lib/elelem/builtins/verify.rb
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-module Elelem
- module Verifiers
- SYNTAX = {
- ".rb" => "ruby -c %{path}",
- ".erb" => "erb -x %{path} | ruby -c",
- ".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 |agent|
- agent.toolbox.add("verify",
- description: "Verify file syntax and run tests",
- params: { path: { type: "string" } },
- required: ["path"]
- ) do |a|
- path = a["path"]
- Verifiers.for(path).inject({verified: []}) do |memo, cmd|
- agent.terminal.say agent.toolbox.header("execute", { "command" => cmd })
- v = agent.toolbox.run("execute", { "command" => cmd })
- break v.merge(path: path, command: cmd) if v[:exit_status] != 0
-
- memo[:verified] << cmd
- memo
- end
- end
- end
-end