Commit a24fa4d

mo khan <mo@mokhan.ca>
2026-09-07 13:10:27
refactor: adapt to elelem core's Output/Input split
agent.terminal is gone; core now exposes agent.output (say/oops) and agent.input (ask/interactive?). Permissions#check's terminal: kwarg renamed to input: since it only ever used ask/interactive?. Claude-Session: https://claude.ai/code/session_01DUuj4amvRrPxDnHPHBkvz5
1 parent bd9d591
lib/elelem/tools/compact.rb
@@ -6,7 +6,7 @@ Elelem.configure do |config|
       response = agent.turn("Summarize: accomplishments, state, next steps. Brief.")
       agent.conversation.clear!
       agent.conversation.add(role: "user", content: "Context: #{response}")
-      agent.terminal.say "  → compacted"
+      agent.output.say "  → compacted"
     end
   end
 end
lib/elelem/tools/confirm.rb
@@ -5,7 +5,7 @@ Elelem.configure do |config|
     permissions = Elelem::Tools::Permissions.new
 
     agent.toolbox.before do |args, tool_name:|
-      permissions.check(tool_name, args, terminal: agent.terminal)
+      permissions.check(tool_name, args, input: agent.input)
     end
   end
 end
lib/elelem/tools/fork.rb
@@ -3,7 +3,7 @@
 module Elelem
   module Tools
     def self.fork(agent, system_prompt:)
-      Elelem::Agent.new(agent.client, toolbox: agent.toolbox, terminal: agent.terminal, system_prompt: system_prompt)
+      Elelem::Agent.new(agent.client, toolbox: agent.toolbox, output: agent.output, input: agent.input, system_prompt: system_prompt)
     end
   end
 end
lib/elelem/tools/git.rb
@@ -11,7 +11,7 @@ Elelem.configure do |config|
     end
 
     agent.toolbox.after("git") do |_, result|
-      agent.terminal.say "  ! #{result[:error]}" if result[:error]
+      agent.output.say "  ! #{result[:error]}" if result[:error]
     end
   end
 end
lib/elelem/tools/interview.rb
@@ -9,8 +9,8 @@ Elelem.configure do |config|
       },
       required: ["question"]
     ) do |args|
-      agent.terminal.say(agent.terminal.markdown(args["question"]))
-      { answer: agent.terminal.ask("> ") }
+      agent.output.say(args["question"], as: :markdown)
+      { answer: agent.input.ask("> ") }
     end
   end
 end
lib/elelem/tools/permissions.rb
@@ -13,12 +13,12 @@ module Elelem
         @rules = rules
       end
 
-      def check(tool_name, args, terminal:)
+      def check(tool_name, args, input:)
         policy = @rules[tool_name.to_sym] || :ask
         case policy
         when :allow then true
         when :deny then raise "Permission denied: #{tool_name}"
-        when :ask then prompt(tool_name, args, terminal)
+        when :ask then prompt(tool_name, args, input)
         end
       end
 
@@ -32,10 +32,10 @@ module Elelem
         {}
       end
 
-      def prompt(tool_name, args, terminal)
-        return true unless terminal.interactive?
+      def prompt(tool_name, args, input)
+        return true unless input.interactive?
 
-        answer = terminal.ask("  Allow? [Y/n] > ")&.downcase
+        answer = input.ask("  Allow? [Y/n] > ")&.downcase
         raise "User denied permission: #{tool_name}" if answer == "n"
 
         true
lib/elelem/tools/verify.rb
@@ -35,7 +35,7 @@ Elelem.configure do |config|
     ) do |args|
       path = args["path"]
       Verifiers.for(path).inject({ verified: [] }) do |memo, cmd|
-        agent.terminal.say agent.toolbox.header("execute", { "command" => cmd })
+        agent.output.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
 
spec/elelem/tools/permissions_spec.rb
@@ -3,45 +3,45 @@
 RSpec.describe Elelem::Tools::Permissions do
   subject { described_class.new }
 
-  let(:terminal) { double(ask: nil, interactive?: false) }
+  let(:input) { double(ask: nil, interactive?: false) }
 
   describe "#check" do
     context "with default allow policies" do
       it "allows read without prompting" do
-        expect(subject.check("read", {}, terminal: terminal)).to be true
-        expect(terminal).not_to have_received(:ask)
+        expect(subject.check("read", {}, input: input)).to be true
+        expect(input).not_to have_received(:ask)
       end
     end
 
     context "with deny policy" do
       subject { described_class.new(rules: { write: :deny }) }
 
-      it { expect { subject.check("write", {}, terminal: terminal) }.to raise_error(/Permission denied/) }
+      it { expect { subject.check("write", {}, input: input) }.to raise_error(/Permission denied/) }
     end
 
-    context "with ask policy on a non-interactive terminal" do
+    context "with ask policy on a non-interactive input" do
       it "returns true without prompting" do
-        expect(subject.check("execute", {}, terminal: terminal)).to be true
-        expect(terminal).not_to have_received(:ask)
+        expect(subject.check("execute", {}, input: input)).to be true
+        expect(input).not_to have_received(:ask)
       end
     end
 
-    context "with ask policy on an interactive terminal" do
-      let(:terminal) { double(ask: answer, interactive?: true) }
+    context "with ask policy on an interactive input" do
+      let(:input) { double(ask: answer, interactive?: true) }
 
       context "when approved" do
         let(:answer) { "y" }
 
         it "prompts and returns true" do
-          expect(subject.check("execute", {}, terminal: terminal)).to be true
-          expect(terminal).to have_received(:ask)
+          expect(subject.check("execute", {}, input: input)).to be true
+          expect(input).to have_received(:ask)
         end
       end
 
       context "when denied" do
         let(:answer) { "n" }
 
-        it { expect { subject.check("execute", {}, terminal: terminal) }.to raise_error(/User denied permission/) }
+        it { expect { subject.check("execute", {}, input: input) }.to raise_error(/User denied permission/) }
       end
     end
   end