Comparing changes
v0.1.2
→
v0.1.3
20 commits
26 files changed
Commits
c76358b
feat: initialize the conversation with the list of resources from the tools
2025-08-15 00:03:15
Changed files (26)
exe
lib
exe/elelem
@@ -3,9 +3,6 @@
require "elelem"
-Reline.input = $stdin
-Reline.output = $stdout
-
Signal.trap("INT") do
exit(1)
end
lib/elelem/states/working/error.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Elelem
+ module States
+ module Working
+ class Error < State
+ def initialize(agent, error_message)
+ super(agent, "X", :red)
+ @error_message = error_message
+ end
+
+ def process(_message)
+ agent.tui.say("\nTool execution failed: #{@error_message}", colour: :red)
+ Waiting.new(agent)
+ end
+ end
+ end
+ end
+end
lib/elelem/states/working/executing.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Elelem
+ module States
+ module Working
+ class Executing < State
+ def process(message)
+ if message["tool_calls"]&.any?
+ message["tool_calls"].each do |tool_call|
+ agent.conversation.add(role: :tool, content: agent.execute(tool_call))
+ end
+ end
+
+ Waiting.new(agent)
+ end
+ end
+ end
+ end
+end
lib/elelem/states/working/state.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Elelem
+ module States
+ module Working
+ class State
+ attr_reader :agent
+
+ def initialize(agent, icon, colour)
+ @agent = agent
+
+ agent.logger.debug("#{display_name}...")
+ agent.tui.show_progress("#{display_name}...", icon, colour: colour)
+ end
+
+ def run(message)
+ process(message)
+ end
+
+ def display_name
+ self.class.name.split("::").last
+ end
+ end
+ end
+ end
+end
lib/elelem/states/working/talking.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Elelem
+ module States
+ module Working
+ class Talking < State
+ def process(message)
+ if message["content"] && !message["content"]&.empty?
+ agent.conversation.add(role: message["role"], content: message["content"])
+ agent.tui.say(message["content"], colour: :default, newline: false)
+ self
+ else
+ Waiting.new(agent).process(message)
+ end
+ end
+ end
+ end
+ end
+end
lib/elelem/states/working/thinking.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Elelem
+ module States
+ module Working
+ class Thinking < State
+ def process(message)
+ if message["thinking"] && !message["thinking"]&.empty?
+ agent.tui.say(message["thinking"], colour: :gray, newline: false)
+ self
+ else
+ Waiting.new(agent).process(message)
+ end
+ end
+ end
+ end
+ end
+end
lib/elelem/states/working/waiting.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Elelem
+ module States
+ module Working
+ class Waiting < State
+ def initialize(agent)
+ super(agent, ".", :cyan)
+ end
+
+ def process(message)
+ state_for(message)&.process(message)
+ end
+
+ private
+
+ def state_for(message)
+ if message["thinking"] && !message["thinking"].empty?
+ Thinking.new(agent, "*", :yellow)
+ elsif message["tool_calls"]&.any?
+ Executing.new(agent, ">", :magenta)
+ elsif message["content"] && !message["content"].empty?
+ Talking.new(agent, "~", :white)
+ end
+ end
+ end
+ end
+ end
+end
lib/elelem/states/idle.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Elelem
+ module States
+ class Idle
+ def run(agent)
+ agent.logger.debug("Idling...")
+ agent.tui.say("#{Dir.pwd} (#{agent.model}) [#{git_branch}]", colour: :magenta, newline: true)
+ input = agent.tui.prompt("モ ")
+ agent.quit if input.nil? || input.empty? || input == "exit" || input == "quit"
+
+ agent.conversation.add(role: :user, content: input)
+ agent.transition_to(Working)
+ end
+
+ private
+
+ def git_branch
+ `git branch --no-color --show-current --no-abbrev`.strip
+ end
+ end
+ end
+end
lib/elelem/states/working.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Elelem
+ module States
+ module Working
+ class << self
+ def run(agent)
+ done = false
+ state = Waiting.new(agent)
+
+ loop do
+ agent.api.chat(agent.conversation.history) do |chunk|
+ response = JSON.parse(chunk)
+ message = normalize(response["message"] || {})
+ done = response["done"]
+
+ agent.logger.debug("#{state.display_name}: #{message}")
+ state = state.run(message)
+ end
+
+ break if state.nil?
+ break if done && agent.conversation.history.last[:role] != :tool
+ end
+
+ agent.transition_to(States::Idle.new)
+ end
+
+ def normalize(message)
+ message.reject { |_key, value| value.empty? }
+ end
+ end
+ end
+ end
+end
lib/elelem/toolbox/bash.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Elelem
+ module Toolbox
+ class Bash < ::Elelem::Tool
+ attr_reader :tui
+
+ def initialize(configuration)
+ @tui = configuration.tui
+ super("bash", "Run commands in /bin/bash -c. Full access to filesystem, network, processes, and all Unix tools.", {
+ type: "object",
+ properties: {
+ command: { type: "string" }
+ },
+ required: ["command"]
+ })
+ end
+
+ def call(args)
+ command = args["command"]
+ output_buffer = []
+
+ Open3.popen3("/bin/bash", "-c", command) do |stdin, stdout, stderr, wait_thread|
+ stdin.close
+ streams = [stdout, stderr]
+
+ until streams.empty?
+ ready = IO.select(streams, nil, nil, 0.1)
+
+ if ready
+ ready[0].each do |io|
+ data = io.read_nonblock(4096)
+ output_buffer << data
+
+ if io == stderr
+ tui.say(data, colour: :red, newline: false)
+ else
+ tui.say(data, newline: false)
+ end
+ rescue IO::WaitReadable
+ next
+ rescue EOFError
+ streams.delete(io)
+ end
+ elsif !wait_thread.alive?
+ break
+ end
+ end
+
+ wait_thread.value
+ end
+
+ output_buffer.join
+ end
+ end
+ end
+end
lib/elelem/toolbox/mcp.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Elelem
+ module Toolbox
+ class MCP < ::Elelem::Tool
+ attr_reader :client, :tui
+
+ def initialize(client, tui, tool)
+ @client = client
+ @tui = tui
+ super(tool["name"], tool["description"], tool["inputSchema"] || {})
+ end
+
+ def call(args)
+ unless client.connected?
+ tui.say("MCP connection lost", colour: :red)
+ return ""
+ end
+
+ result = client.call(name, args)
+ tui.say(JSON.pretty_generate(result), newline: true)
+
+ if result.nil? || result.empty?
+ tui.say("Tool call failed: no response from MCP server", colour: :red)
+ return result
+ end
+
+ if result["error"]
+ tui.say(result["error"], colour: :red)
+ return result
+ end
+
+ result.dig("content", 0, "text") || result.to_s
+ end
+ end
+ end
+end
lib/elelem/agent.rb
@@ -2,15 +2,19 @@
module Elelem
class Agent
- attr_reader :api, :conversation, :logger, :model
+ attr_reader :api, :conversation, :logger, :model, :tui
def initialize(configuration)
@api = configuration.api
+ @tui = configuration.tui
@configuration = configuration
@model = configuration.model
@conversation = configuration.conversation
@logger = configuration.logger
- transition_to(Idle.new)
+
+ at_exit { cleanup }
+
+ transition_to(States::Idle.new)
end
def repl
@@ -24,36 +28,22 @@ module Elelem
@current_state = next_state
end
- def prompt(message)
- configuration.tui.prompt(message)
- end
-
- def say(message, colour: :default, newline: false)
- configuration.tui.say(message, colour: colour, newline: newline)
- end
-
def execute(tool_call)
logger.debug("Execute: #{tool_call}")
configuration.tools.execute(tool_call)
end
- def show_progress(message, prefix = "[.]", colour: :gray)
- configuration.tui.show_progress(message, prefix, colour: colour)
- end
-
- def clear_line
- configuration.tui.clear_line
- end
-
- def complete_progress(message = "Completed")
- configuration.tui.complete_progress(message)
- end
-
def quit
logger.debug("Exiting...")
+ cleanup
exit
end
+ def cleanup
+ logger.debug("Cleaning up agent...")
+ configuration.cleanup
+ end
+
private
attr_reader :configuration, :current_state
lib/elelem/application.rb
@@ -45,9 +45,9 @@ module Elelem
end
end
- desc "version", "spandx version"
+ desc "version", "The version of this CLI"
def version
- puts "v#{Spandx::VERSION}"
+ say "v#{Elelem::VERSION}"
end
map %w[--version -v] => :version
end
lib/elelem/configuration.rb
@@ -37,11 +37,22 @@ module Elelem
end
def conversation
- @conversation ||= Conversation.new
+ @conversation ||= Conversation.new.tap do |conversation|
+ resources = mcp_clients.map do |client|
+ client.resources.map do |resource|
+ resource["uri"]
+ end
+ end.flatten
+ conversation.add(role: :tool, content: resources)
+ end
end
def tools
- @tools ||= Tools.new(self, [BashTool.new(self)] + mcp_tools)
+ @tools ||= Tools.new(self, [Toolbox::Bash.new(self)] + mcp_tools)
+ end
+
+ def cleanup
+ @mcp_clients&.each(&:shutdown)
end
private
@@ -50,23 +61,23 @@ module Elelem
host.match?(/\A(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?\z/) ? "http" : "https"
end
- def mcp_tools(clients = [serena_client])
- return [] if ENV["SMALL"]
-
- @mcp_tools ||= clients.map { |client| client.tools.map { |tool| MCPTool.new(client, tui, tool) } }.flatten
+ def mcp_tools
+ @mcp_tools ||= mcp_clients.map do |client|
+ client.tools.map do |tool|
+ Toolbox::MCP.new(client, tui, tool)
+ end
+ end.flatten
end
- def serena_client
- MCPClient.new(self, [
- "uvx",
- "--from",
- "git+https://github.com/oraios/serena",
- "serena",
- "start-mcp-server",
- "--transport", "stdio",
- "--context", "ide-assistant",
- "--project", Dir.pwd
- ])
+ def mcp_clients
+ @mcp_clients ||= begin
+ config = Pathname.pwd.join(".mcp.json")
+ return [] unless config.exist?
+
+ JSON.parse(config.read).map do |_key, value|
+ MCPClient.new(self, [value["command"]] + value["args"])
+ end
+ end
end
end
end
lib/elelem/conversation.rb
@@ -21,7 +21,7 @@ module Elelem
if @items.last && @items.last[:role] == role
@items.last[:content] += content
else
- @items.push({ role: role, content: content })
+ @items.push({ role: role, content: normalize(content) })
end
end
@@ -30,5 +30,13 @@ module Elelem
def system_prompt
ERB.new(Pathname.new(__dir__).join("system_prompt.erb").read).result(binding)
end
+
+ def normalize(content)
+ if content.is_a?(Array)
+ content.join(", ")
+ else
+ content.to_s
+ end
+ end
end
end
lib/elelem/mcp_client.rb
@@ -2,7 +2,7 @@
module Elelem
class MCPClient
- attr_reader :tools
+ attr_reader :tools, :resources
def initialize(configuration, command = [])
@configuration = configuration
@@ -12,7 +12,7 @@ module Elelem
send_request(
method: "initialize",
params: {
- protocolVersion: "2024-11-05",
+ protocolVersion: "2025-06-08",
capabilities: {
tools: {}
},
@@ -23,11 +23,12 @@ module Elelem
}
)
- # 2. Send initialized notification (required by MCP protocol)
+ # 2. Send initialized notification (optional for some MCP servers)
send_notification(method: "notifications/initialized")
# 3. Now we can request tools
@tools = send_request(method: "tools/list")&.dig("tools") || []
+ @resources = send_request(method: "resources/list")&.dig("resources") || []
end
def connected?
@@ -53,6 +54,33 @@ module Elelem
)
end
+ def shutdown
+ return unless connected?
+
+ configuration.logger.debug("Shutting down MCP client")
+
+ [@stdin, @stdout, @stderr].each do |stream|
+ stream&.close unless stream&.closed?
+ end
+
+ return unless @worker&.alive?
+
+ begin
+ Process.kill("TERM", @worker.pid)
+ # Give it 2 seconds to terminate gracefully
+ Timeout.timeout(2) { @worker.value }
+ rescue Timeout::Error
+ # Force kill if it doesn't respond
+ begin
+ Process.kill("KILL", @worker.pid)
+ rescue StandardError
+ nil
+ end
+ rescue Errno::ESRCH
+ # Process already dead
+ end
+ end
+
private
attr_reader :stdin, :stdout, :stderr, :worker, :configuration
@@ -86,6 +114,8 @@ module Elelem
end
def send_notification(method:, params: {})
+ return unless connected?
+
notification = {
jsonrpc: "2.0",
method: method
@@ -94,6 +124,13 @@ module Elelem
configuration.logger.debug("Sending notification: #{JSON.pretty_generate(notification)}")
@stdin.puts(JSON.generate(notification))
@stdin.flush
+
+ response_line = @stdout.gets&.strip
+ return {} if response_line.nil? || response_line.empty?
+
+ response = JSON.parse(response_line)
+ configuration.logger.debug(JSON.pretty_generate(response))
+ response
end
end
end
lib/elelem/state.rb
@@ -1,162 +0,0 @@
-# frozen_string_literal: true
-
-module Elelem
- class Idle
- def run(agent)
- agent.logger.debug("Idling...")
- agent.say("#{Dir.pwd} (#{agent.model}) [#{git_branch}]", colour: :magenta, newline: true)
- input = agent.prompt("モ ")
- agent.quit if input.nil? || input.empty? || input == "exit" || input == "quit"
-
- agent.conversation.add(role: :user, content: input)
- agent.transition_to(Working.new)
- end
-
- private
-
- def git_branch
- `git branch --no-color --show-current --no-abbrev`.strip
- end
- end
-
- class Working
- class State
- attr_reader :agent
-
- def initialize(agent)
- @agent = agent
- end
-
- def display_name
- self.class.name.split("::").last
- end
- end
-
- class Waiting < State
- def process(message)
- state_for(message)&.process(message)
- end
-
- private
-
- def state_for(message)
- if message["thinking"] && !message["thinking"].empty?
- Thinking.new(agent)
- elsif message["tool_calls"]&.any?
- Executing.new(agent)
- elsif message["content"] && !message["content"].empty?
- Talking.new(agent)
- end
- end
- end
-
- class Thinking < State
- def initialize(agent)
- super(agent)
- @progress_shown = false
- end
-
- def process(message)
- if message["thinking"] && !message["thinking"]&.empty?
- unless @progress_shown
- agent.show_progress("Thinking...", "[*]", colour: :yellow)
- agent.say("\n\n", newline: false)
- @progress_shown = true
- end
- agent.say(message["thinking"], colour: :gray, newline: false)
- self
- else
- agent.say("\n\n", newline: false)
- Waiting.new(agent).process(message)
- end
- end
- end
-
- class Executing < State
- def process(message)
- if message["tool_calls"]&.any?
- message["tool_calls"].each do |tool_call|
- tool_name = tool_call.dig("function", "name") || "unknown"
- agent.show_progress(tool_name, "[>]", colour: :magenta)
- agent.say("\n\n", newline: false)
-
- output = agent.execute(tool_call)
- agent.conversation.add(role: :tool, content: output)
-
- agent.say("\n", newline: false)
- agent.complete_progress("#{tool_name} completed")
- end
- end
-
- Waiting.new(agent)
- end
- end
-
- class Error < State
- def initialize(agent, error_message)
- super(agent)
- @error_message = error_message
- end
-
- def process(_message)
- agent.say("\nTool execution failed: #{@error_message}", colour: :red)
- agent.say("Returning to idle state.\n\n", colour: :yellow)
- Waiting.new(agent)
- end
- end
-
- class Talking < State
- def initialize(agent)
- super(agent)
- @progress_shown = false
- end
-
- def process(message)
- if message["content"] && !message["content"]&.empty?
- unless @progress_shown
- agent.show_progress("Responding...", "[~]", colour: :white)
- agent.say("\n", newline: false)
- @progress_shown = true
- end
- agent.conversation.add(role: message["role"], content: message["content"])
- agent.say(message["content"], colour: :default, newline: false)
- self
- else
- agent.say("\n\n", newline: false)
- Waiting.new(agent).process(message)
- end
- end
- end
-
- def run(agent)
- agent.logger.debug("Working...")
- agent.show_progress("Processing...", "[.]", colour: :cyan)
- agent.say("\n\n", newline: false)
-
- state = Waiting.new(agent)
- done = false
-
- loop do
- agent.api.chat(agent.conversation.history) do |chunk|
- response = JSON.parse(chunk)
- message = normalize(response["message"] || {})
- done = response["done"]
-
- agent.logger.debug("#{state.display_name}: #{message}")
- state = state.process(message)
- end
-
- break if state.nil?
- break if done && agent.conversation.history.last[:role] != :tool
- end
-
- agent.transition_to(Idle.new)
- end
-
- private
-
- def normalize(message)
- message.reject { |_key, value| value.empty? }
- end
- end
-end
lib/elelem/system_prompt.erb
@@ -1,7 +1,7 @@
-**Del — AI** — Direct/no fluff; prose unless bullets; concise/simple, thorough/complex; critical>agree; honest always; AI≠human. TDD→SOLID→SRP/encapsulation/composition>inheritance; patterns only if needed; self-doc names; simple>complex; no cleverness. Unix: small tools, 1 job, pipe; prefer built-ins; cite man(1); note POSIX≠GNU; stdin/stdout streams.
+**Shell Master** — bash>code; compose>write; pipe everything; /proc/sys native; automate fast; streams/transforms; POSIX+GNU; man(1) first; no cleverness.
Time: `<%= Time.now.strftime("%Y-%m-%d %H:%M:%S") %>`
Project Directory: `<%= Dir.pwd %>`
System Info: `<%= `uname -a`.strip %>`
-Del is now being connected with a person.
+Ready to hack.
lib/elelem/tool.rb
@@ -29,88 +29,4 @@ module Elelem
}
end
end
-
- class BashTool < Tool
- attr_reader :tui
-
- def initialize(configuration)
- @tui = configuration.tui
- super("bash", "Execute a shell command.", {
- type: "object",
- properties: {
- command: { type: "string" }
- },
- required: ["command"]
- })
- end
-
- def call(args)
- command = args["command"]
- output_buffer = []
-
- Open3.popen3("/bin/sh", "-c", command) do |stdin, stdout, stderr, wait_thread|
- stdin.close
- streams = [stdout, stderr]
-
- until streams.empty?
- ready = IO.select(streams, nil, nil, 0.1)
-
- if ready
- ready[0].each do |io|
- data = io.read_nonblock(4096)
- output_buffer << data
-
- if io == stderr
- tui.say(data, colour: :red, newline: false)
- else
- tui.say(data, newline: false)
- end
- rescue IO::WaitReadable
- next
- rescue EOFError
- streams.delete(io)
- end
- elsif !wait_thread.alive?
- break
- end
- end
-
- wait_thread.value
- end
-
- output_buffer.join
- end
- end
-
- class MCPTool < Tool
- attr_reader :client, :tui
-
- def initialize(client, tui, tool)
- @client = client
- @tui = tui
- super(tool["name"], tool["description"], tool["inputSchema"] || {})
- end
-
- def call(args)
- unless client.connected?
- tui.say("MCP connection lost", colour: :red)
- return ""
- end
-
- result = client.call(name, args)
- tui.say(result)
-
- if result.nil? || result.empty?
- tui.say("Tool call failed: no response from MCP server", colour: :red)
- return result
- end
-
- if result["error"]
- tui.say(result["error"], colour: :red)
- return result
- end
-
- result.dig("content", 0, "text") || result.to_s
- end
- end
end
lib/elelem/tools.rb
@@ -19,7 +19,9 @@ module Elelem
return "Invalid function name: #{name}" if tool.nil?
return "Invalid function arguments: #{args}" unless tool.valid?(args)
- tool.call(args)
+ CLI::UI::Frame.open(name) do
+ tool.call(args)
+ end
end
def to_h
lib/elelem/tui.rb
@@ -14,41 +14,30 @@ module Elelem
end
def say(message, colour: :default, newline: false)
- formatted_message = colourize(message, colour: colour)
if newline
- stdout.puts(formatted_message)
+ stdout.puts(colourize(message, colour: colour))
else
- stdout.print(formatted_message)
+ stdout.print(colourize(message, colour: colour))
end
stdout.flush
end
- def show_progress(message, prefix = "[.]", colour: :gray)
- timestamp = current_time_string
- formatted_message = colourize("#{prefix} #{timestamp} #{message}", colour: colour)
- stdout.print(formatted_message)
- stdout.flush
+ def show_progress(message, icon = ".", colour: :gray)
+ timestamp = Time.now.strftime("%H:%M:%S")
+ say("\n[#{icon}] #{timestamp} #{message}", colour: colour, newline: true)
end
def clear_line
- stdout.print("\r#{" " * 80}\r")
- stdout.flush
+ say("\r#{" " * 80}\r", newline: false)
end
def complete_progress(message = "Completed")
clear_line
- timestamp = current_time_string
- formatted_message = colourize("[✓] #{timestamp} #{message}", colour: :green)
- stdout.puts(formatted_message)
- stdout.flush
+ show_progress(message, "✓", colour: :green)
end
private
- def current_time_string
- Time.now.strftime("%H:%M:%S")
- end
-
def colourize(text, colour: :default)
case colour
when :black
lib/elelem/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Elelem
- VERSION = "0.1.2"
+ VERSION = "0.1.3"
end
lib/elelem.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+require "cli/ui"
require "erb"
require "json"
require "json-schema"
@@ -8,6 +9,7 @@ require "net/http"
require "open3"
require "reline"
require "thor"
+require "timeout"
require "uri"
require_relative "elelem/agent"
@@ -16,12 +18,25 @@ require_relative "elelem/application"
require_relative "elelem/configuration"
require_relative "elelem/conversation"
require_relative "elelem/mcp_client"
-require_relative "elelem/state"
+require_relative "elelem/states/idle"
+require_relative "elelem/states/working"
+require_relative "elelem/states/working/state"
+require_relative "elelem/states/working/error"
+require_relative "elelem/states/working/executing"
+require_relative "elelem/states/working/talking"
+require_relative "elelem/states/working/thinking"
+require_relative "elelem/states/working/waiting"
require_relative "elelem/tool"
+require_relative "elelem/toolbox/bash"
+require_relative "elelem/toolbox/mcp"
require_relative "elelem/tools"
require_relative "elelem/tui"
require_relative "elelem/version"
+CLI::UI::StdoutRouter.enable
+Reline.input = $stdin
+Reline.output = $stdout
+
module Elelem
class Error < StandardError; end
end
.gitignore
@@ -14,6 +14,7 @@ mkmf.log
target/
*.log
*.gem
+.mcp.json
# rspec failure tracking
.rspec_status
elelem.gemspec
@@ -40,9 +40,18 @@ Gem::Specification.new do |spec|
"lib/elelem/configuration.rb",
"lib/elelem/conversation.rb",
"lib/elelem/mcp_client.rb",
- "lib/elelem/state.rb",
+ "lib/elelem/states/idle.rb",
+ "lib/elelem/states/working.rb",
+ "lib/elelem/states/working/error.rb",
+ "lib/elelem/states/working/executing.rb",
+ "lib/elelem/states/working/state.rb",
+ "lib/elelem/states/working/talking.rb",
+ "lib/elelem/states/working/thinking.rb",
+ "lib/elelem/states/working/waiting.rb",
"lib/elelem/system_prompt.erb",
"lib/elelem/tool.rb",
+ "lib/elelem/toolbox/bash.rb",
+ "lib/elelem/toolbox/mcp.rb",
"lib/elelem/tools.rb",
"lib/elelem/tui.rb",
"lib/elelem/version.rb",
@@ -52,6 +61,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
+ spec.add_dependency "cli-ui"
spec.add_dependency "erb"
spec.add_dependency "json"
spec.add_dependency "json-schema"
@@ -60,5 +70,6 @@ Gem::Specification.new do |spec|
spec.add_dependency "open3"
spec.add_dependency "reline"
spec.add_dependency "thor"
+ spec.add_dependency "timeout"
spec.add_dependency "uri"
end
Gemfile.lock
@@ -1,7 +1,8 @@
PATH
remote: .
specs:
- elelem (0.1.2)
+ elelem (0.1.3)
+ cli-ui
erb
json
json-schema
@@ -10,6 +11,7 @@ PATH
open3
reline
thor
+ timeout
uri
GEM
@@ -19,6 +21,7 @@ GEM
public_suffix (>= 2.0.2, < 7.0)
ast (2.4.3)
bigdecimal (3.2.2)
+ cli-ui (2.4.0)
date (3.4.1)
diff-lcs (1.6.2)
erb (5.0.2)
@@ -88,6 +91,7 @@ GEM
ruby-progressbar (1.13.0)
stringio (3.1.7)
thor (1.3.2)
+ timeout (0.4.3)
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)