Class: TalkToYourApp::Plugins::Rake::Tools::Run

Inherits:
Tool
  • Object
show all
Defined in:
lib/talk_to_your_app/plugins/rake/tools/run.rb

Overview

Runs a single allow-listed rake task and returns its status and output as JSON. The task must be on the plugin’s ‘allowed:` list; anything else is refused without executing. The task runs in a subprocess (`bundle exec rake`), so it cannot inject shell commands and its real exit status is captured.

Defined Under Namespace

Classes: Result

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Tool

argument, arguments, connection, default_arguments, description, dispatch, input_schema_hash, invoke, name, normalize_response, to_mcp_definition, to_mcp_tool

Class Method Details

.app_rootObject



49
50
51
# File 'lib/talk_to_your_app/plugins/rake/tools/run.rb', line 49

def self.app_root
  defined?(Rails) && Rails.respond_to?(:root) ? Rails.root.to_s : Dir.pwd
end

.run_task(task, rake_args) ⇒ Object

Executes ‘bundle exec rake <task>` in the app root. Array argv (no shell) so task/args cannot inject shell commands.



43
44
45
46
47
# File 'lib/talk_to_your_app/plugins/rake/tools/run.rb', line 43

def self.run_task(task, rake_args)
  invocation = rake_args.empty? ? task : "#{task}[#{rake_args.join(",")}]"
  stdout, stderr, status = Open3.capture3("bundle", "exec", "rake", invocation, chdir: app_root)
  Result.new(stdout: stdout, stderr: stderr, exit_code: status.exitstatus || 1)
end

Instance Method Details

#call(args, _ctx) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/talk_to_your_app/plugins/rake/tools/run.rb', line 23

def call(args, _ctx)
  task = args[:task].to_s
  unless TalkToYourApp::Plugins::Rake.allowed?(task)
    return error("Rake task #{task.inspect} is not allowed. Allowed tasks: #{TalkToYourApp::Plugins::Rake.allowed_tasks.inspect}.")
  end

  result = self.class.run_task(task, Array(args[:args]).map(&:to_s))
  json(
    task: task,
    status: result.exit_code.zero? ? "success" : "error",
    exit_code: result.exit_code,
    output: result.stdout.strip,
    error: (result.stderr.strip.empty? ? nil : result.stderr.strip),
  )
rescue StandardError => e
  error("Failed to run rake #{args[:task]}: #{e.class}: #{e.message}")
end