Commit bd9d591
Changed files (14)
lib
lib/elelem/tools/compact.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: true
-Elelem::Plugins.register(:compact) do |agent|
- 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"
+Elelem.configure do |config|
+ config.setup(:compact) do |agent|
+ 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
end
lib/elelem/tools/confirm.rb
@@ -1,9 +1,11 @@
# frozen_string_literal: true
-Elelem::Plugins.register(:confirm) do |agent|
- permissions = Elelem::Tools::Permissions.new
+Elelem.configure do |config|
+ config.setup(:confirm) do |agent|
+ permissions = Elelem::Tools::Permissions.new
- agent.toolbox.before do |args, tool_name:|
- permissions.check(tool_name, args, terminal: agent.terminal)
+ agent.toolbox.before do |args, tool_name:|
+ permissions.check(tool_name, args, terminal: agent.terminal)
+ end
end
end
lib/elelem/tools/edit.rb
@@ -1,15 +1,17 @@
# frozen_string_literal: true
-Elelem::Plugins.register(:edit) do |agent|
- agent.toolbox.add("edit",
- description: "Replace first occurrence of text in file",
- params: { path: { type: "string" }, old: { type: "string" }, new: { type: "string" } },
- required: ["path", "old", "new"]
- ) do |args|
- path = Pathname.new(args["path"]).expand_path
- content = path.read
- agent.toolbox
- .run("write", { "path" => args["path"], "content" => content.sub(args["old"], args["new"]) })
- .merge(replaced: args["old"], with: args["new"])
+Elelem.configure do |config|
+ config.setup(:edit) do |agent|
+ agent.toolbox.add("edit",
+ description: "Replace first occurrence of text in file",
+ params: { path: { type: "string" }, old: { type: "string" }, new: { type: "string" } },
+ required: ["path", "old", "new"]
+ ) do |args|
+ path = Pathname.new(args["path"]).expand_path
+ content = path.read
+ agent.toolbox
+ .run("write", { "path" => args["path"], "content" => content.sub(args["old"], args["new"]) })
+ .merge(replaced: args["old"], with: args["new"])
+ end
end
end
lib/elelem/tools/eval.rb
@@ -1,20 +1,22 @@
# frozen_string_literal: true
-Elelem::Plugins.register(:eval) do |agent|
- description = <<~'DESC'
- Evaluate Ruby code. Available API:
+Elelem.configure do |config|
+ config.setup(: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
+ 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"]) }
+ agent.toolbox.add("eval",
+ description: description,
+ params: { ruby: { type: "string" } },
+ required: ["ruby"]
+ ) do |args|
+ { result: binding.eval(args["ruby"]) }
+ end
end
end
lib/elelem/tools/git.rb
@@ -1,15 +1,17 @@
# frozen_string_literal: true
-Elelem::Plugins.register(:git) do |agent|
- agent.toolbox.add("git",
- description: "Run git command",
- params: { command: { type: "string" }, args: { type: "array", items: { type: "string" } } },
- required: ["command"]
- ) do |args|
- agent.toolbox.exec("git", args["command"], *(args["args"] || []))
- end
+Elelem.configure do |config|
+ config.setup(:git) do |agent|
+ agent.toolbox.add("git",
+ description: "Run git command",
+ params: { command: { type: "string" }, args: { type: "array", items: { type: "string" } } },
+ required: ["command"]
+ ) do |args|
+ agent.toolbox.exec("git", args["command"], *(args["args"] || []))
+ end
- agent.toolbox.after("git") do |_, result|
- agent.terminal.say " ! #{result[:error]}" if result[:error]
+ agent.toolbox.after("git") do |_, result|
+ agent.terminal.say " ! #{result[:error]}" if result[:error]
+ end
end
end
lib/elelem/tools/glob.rb
@@ -1,13 +1,15 @@
# 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 |args|
- path = args["path"].to_s.empty? ? "." : args["path"]
- result = agent.toolbox.exec("fd", "--glob", args["pattern"], path)
- result[:ok] ? result : agent.toolbox.exec("find", path, "-name", args["pattern"])
+Elelem.configure do |config|
+ config.setup(:glob) do |agent|
+ agent.toolbox.add("glob",
+ description: "Find files matching pattern",
+ params: { pattern: { type: "string" }, path: { type: "string" } },
+ required: ["pattern"]
+ ) do |args|
+ path = args["path"].to_s.empty? ? "." : args["path"]
+ result = agent.toolbox.exec("fd", "--glob", args["pattern"], path)
+ result[:ok] ? result : agent.toolbox.exec("find", path, "-name", args["pattern"])
+ end
end
end
lib/elelem/tools/grep.rb
@@ -1,22 +1,24 @@
# 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 |args|
- path = args["path"].to_s.empty? ? "." : args["path"]
- glob = args["glob"]
+Elelem.configure do |config|
+ config.setup(:grep) do |agent|
+ agent.toolbox.add("grep",
+ description: "Search file contents",
+ params: { pattern: { type: "string" }, path: { type: "string" }, glob: { type: "string" } },
+ required: ["pattern"]
+ ) do |args|
+ path = args["path"].to_s.empty? ? "." : args["path"]
+ glob = args["glob"]
- rg_args = ["rg", "-n", args["pattern"], path]
- rg_args += ["-g", glob] if glob
- result = agent.toolbox.exec(*rg_args)
- next result if result[:ok]
+ rg_args = ["rg", "-n", args["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 += [args["pattern"], path]
- agent.toolbox.exec(*grep_args)
+ grep_args = ["grep", "-rn"]
+ grep_args += ["--include", glob] if glob
+ grep_args += [args["pattern"], path]
+ agent.toolbox.exec(*grep_args)
+ end
end
end
lib/elelem/tools/init.rb
@@ -1,29 +1,31 @@
# frozen_string_literal: true
-Elelem::Plugins.register(:init) do |agent|
- agent.commands.register("init", description: "Generate AGENTS.md") do
- system_prompt = <<~PROMPT
- AGENTS.md generator. Analyze codebase and write AGENTS.md to project root.
+Elelem.configure do |config|
+ config.setup(:init) do |agent|
+ 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.
+ # 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
+ ## 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
+ ## 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
+ Keep it minimal. No fluff.
+ PROMPT
- Elelem::Tools.fork(agent, system_prompt: system_prompt).turn("Generate AGENTS.md for this project")
+ Elelem::Tools.fork(agent, system_prompt: system_prompt).turn("Generate AGENTS.md for this project")
+ end
end
end
lib/elelem/tools/interview.rb
@@ -1,14 +1,16 @@
# 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("> ") }
+Elelem.configure do |config|
+ config.setup(: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
end
lib/elelem/tools/list.rb
@@ -1,14 +1,16 @@
# 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 |args|
- path = args["path"] && !args["path"].empty? ? args["path"] : "."
- flags = args["recursive"] ? "-laR" : "-la"
- agent.toolbox.exec("ls", flags, path)
+Elelem.configure do |config|
+ config.setup(:list) do |agent|
+ agent.toolbox.add("list",
+ description: "List directory contents",
+ params: { path: { type: "string" }, recursive: { type: "boolean" } },
+ required: [],
+ aliases: ["ls"]
+ ) do |args|
+ path = args["path"] && !args["path"].empty? ? args["path"] : "."
+ flags = args["recursive"] ? "-laR" : "-la"
+ agent.toolbox.exec("ls", flags, path)
+ end
end
end
lib/elelem/tools/shell.rb
@@ -1,23 +1,25 @@
# frozen_string_literal: true
-Elelem::Plugins.register(:shell) do |agent|
- 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
+Elelem.configure do |config|
+ config.setup(:shell) do |agent|
+ 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))
+ 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.conversation.add(role: "user", content: transcript) unless transcript.strip.empty?
end
end
lib/elelem/tools/task.rb
@@ -1,13 +1,15 @@
# 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 |args|
- sub = Elelem::Tools.fork(agent, system_prompt: "Research agent. Search, analyze, report. Be concise.")
- sub.turn(args["prompt"])
- { result: sub.conversation.last[:content] }
+Elelem.configure do |config|
+ config.setup(: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 |args|
+ sub = Elelem::Tools.fork(agent, system_prompt: "Research agent. Search, analyze, report. Be concise.")
+ sub.turn(args["prompt"])
+ { result: sub.conversation.last[:content] }
+ end
end
end
lib/elelem/tools/verify.rb
@@ -1,45 +1,47 @@
# frozen_string_literal: true
-Elelem::Plugins.register(:verify) do |agent|
- class 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
+class 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
+ 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
+ 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
+ def self.test_runner
+ %w[bin/test script/test].find { |s| File.executable?(s) }
end
+end
- agent.toolbox.add("verify",
- description: "Verify file syntax and run tests",
- params: { path: { type: "string" } },
- required: ["path"]
- ) do |args|
- path = args["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
+Elelem.configure do |config|
+ config.setup(:verify) do |agent|
+ agent.toolbox.add("verify",
+ description: "Verify file syntax and run tests",
+ params: { path: { type: "string" } },
+ required: ["path"]
+ ) do |args|
+ path = args["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
+ memo[:verified] << cmd
+ memo
+ end
end
end
end
Gemfile.lock
@@ -2,38 +2,30 @@ PATH
remote: .
specs:
elelem-tools (0.1.1)
- elelem (~> 0.10)
+ elelem (~> 0.11)
pathname (~> 0.5)
tempfile (~> 0.3)
GEM
remote: https://rubygems.org/
specs:
- base64 (0.3.0)
bigdecimal (4.1.2)
- date (3.5.1)
diff-lcs (1.6.2)
- digest (3.2.1)
- elelem (0.10.0)
- base64 (~> 0.1)
- date (~> 3.0)
- digest (~> 3.0)
+ elelem (0.11.0)
erb (~> 6.0)
- fileutils (~> 1.0)
- json (~> 2.0)
- json_schemer (~> 2.0)
- logger (~> 1.0)
- net-hippie (~> 1.0)
- open3 (~> 0.1)
- optparse (~> 0.1)
- pathname (~> 0.1)
- reline (~> 0.6)
- securerandom (~> 0.1)
+ fileutils (~> 1.8)
+ io-console (~> 0.9)
+ json (~> 2.21)
+ json_schemer (~> 2.5)
+ logger (~> 1.7)
+ open3 (~> 0.2)
+ optparse (~> 0.8)
+ pathname (~> 0.5)
+ reline (~> 0.7)
shellwords (~> 0.2)
- stringio (~> 3.0)
+ stringio (~> 3.2)
tempfile (~> 0.3)
uri (~> 1.0)
- webrick (~> 1.9)
erb (6.0.7)
fileutils (1.8.0)
hana (1.3.7)
@@ -50,20 +42,7 @@ GEM
regexp_parser (~> 2.0)
simpleidn (~> 0.2)
logger (1.7.0)
- monitor (0.2.0)
- net-hippie (1.5.1)
- base64 (~> 0.1)
- json (~> 2.0)
- logger (~> 1.0)
- monitor (~> 0.1)
- net-http (~> 0.1)
- openssl (~> 4.0)
- resolv (~> 0.1)
- timeout (~> 0.1)
- net-http (0.9.1)
- uri (>= 0.11.1)
open3 (0.2.1)
- openssl (4.0.2)
optparse (0.8.1)
pathname (0.5.0)
pp (0.6.4)
@@ -83,7 +62,6 @@ GEM
regexp_parser (2.12.0)
reline (0.7.0)
io-console (~> 0.5)
- resolv (0.7.2)
rspec (3.13.2)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
@@ -97,15 +75,12 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.7)
- securerandom (0.4.1)
shellwords (0.2.2)
simpleidn (0.3.0)
stringio (3.2.0)
tempfile (0.3.1)
- timeout (0.6.1)
tsort (0.2.0)
uri (1.1.1)
- webrick (1.9.2)
PLATFORMS
ruby
@@ -118,13 +93,10 @@ DEPENDENCIES
rspec (~> 3.0)
CHECKSUMS
- base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
bundler (4.0.20) sha256=7978a8ac648767f5e635bc522445b79e80a52b907a39a36c2d8085ed6bc762ae
- date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
- digest (3.2.1) sha256=ab3312b4e272d7d5dc41c564c86a25861a1f34ac5153374199a0b74861395947
- elelem (0.10.0) sha256=de67f3a28351640da471e6e80e7a1d79e83e293cbc502a9e98c3c609cad2e237
+ elelem (0.11.0)
elelem-tools (0.1.1)
erb (6.0.7) sha256=c5ca6dc25b0ef974a44dc8f59fe847577122483b1968a38dec305c60bf91ee92
fileutils (1.8.0) sha256=8c6b1df54e2540bdb2f39258f08af78853aa70bad52b4d394bbc6424593c6e02
@@ -134,11 +106,7 @@ CHECKSUMS
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
- monitor (0.2.0) sha256=18698584f161ca6611d73130663990ca7dc439d9d88095226e683035d66f1f0f
- net-hippie (1.5.1) sha256=76ecfc7d8df7866c3ed4f2e25fe963d87537451a1fb0d318b58eca161127fbcc
- net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
open3 (0.2.1) sha256=8e2d7d2113526351201438c1aa35c8139f0141c9e8913baa007c898973bf3952
- openssl (4.0.2) sha256=1037ad2868ae58df9ad917891c0c0f9815a1172f6846d4bcdd508e4c2ee747c2
optparse (0.8.1) sha256=42bea10d53907ccff4f080a69991441d611fbf8733b60ed1ce9ee365ce03bd1a
pathname (0.5.0) sha256=d5a331784f6e1f2fefb31c2ff0b8855aabfb661d807284ead9fa47b883d81623
pp (0.6.4) sha256=dfcb0fce700c41456265922884f9fe195d7fbb0674a3578e6c0f69588e82b570
@@ -149,21 +117,17 @@ CHECKSUMS
rdoc (8.0.0) sha256=03bf8c08a9639658855a0cfd77c0abca8325c227693f7f33f82957811348c469
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
- resolv (0.7.2) sha256=626d044d975ab2daac759bf898416f1b51e2cb8dcd6727c2b5b5b28b97ead2e1
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
- securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
shellwords (0.2.2) sha256=b8695a791de2f71472de5abdc3f4332f6535a4177f55d8f99e7e44266cd32f94
simpleidn (0.3.0) sha256=12ca730bed2f3db04d11e9bfd1bca3e11fb37f55b21eb2e9793fb5814bf54d03
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
tempfile (0.3.1) sha256=0bb53ab646744e505eb3102147e22ae130a626a15563e882428c1ec973fed76a
- timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
- webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131
BUNDLED WITH
4.0.20