Class: Agentilda::CLI::Run

Inherits:
Base
  • Object
show all
Defined in:
lib/agentilda/cli/run/run.rb

Overview

agentilda run — drive the specialist agents until the tree settles.

Constant Summary

Constants included from UI

UI::MAX_WIDTH, UI::METER_WIDTH, UI::MIN_WIDTH, UI::NO_FAILURE, UI::NO_FIELDS, UI::NO_TIMEOUT, UI::PROGRESS_THRESHOLD, UI::TIMER_WARNING, UI::TIMER_WIDTH

Instance Method Summary collapse

Methods inherited from Base

inherited

Methods included from UI

abbreviate, activity_for, animate?, box, box_height, color?, concurrently, countdown, default_jobs, display_width, elapsed, #error, fit, #info, line, log, logging_activity, meter, monotonic, once, paint, #paint, pastel, popup, report_line, reset!, said, #say, solo_spinner, spinning, stepping, #success, threaded, tty?, #warn, width

Instance Method Details

#call(**options) ⇒ void

This method returns an undefined value.

Option precedence, quietest to loudest: the built-in default, then ~/.local/config/agentilda.json under its "run" key, then the flag actually typed. The config never supplies --commit: an agent run that writes is something a person asks for each time.

Parameters:

  • options (Hash)


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/agentilda/cli/run/run.rb', line 53

def call(**options)
  config = begin
    Agentilda::Config.for(:run)
  rescue Agentilda::Error => e
    refuse(e.message, 66)
  end

  if options[:prompt] && !options[:agent]
    refuse("--prompt only works with --agent: it speaks to one agent, and without " \
           "that restriction every agent in the round would hear it.", 64)
  end

  tree = tree_for(options)
  root = options[:root] || File.dirname(tree.dir)
  agents = Agentilda::Agents.new
  agents = filtered(agents, options[:agent]) if options[:agent]
  agents = skipped(agents, options[:skip], options[:agent]) if options[:skip]
  plans = options[:plan] ? scoped(tree, options[:plan]) : nil
  assignable!(agents, tree, plans, options[:agent]) if options[:agent]

  isolation = options.fetch(:isolation, "worktree").to_sym
  jobs = (options[:jobs] || config[:jobs] || UI.default_jobs).to_i
  timeout = (options[:timeout] || config[:timeout] || 900).to_i
  chain = if options[:agent]
    false
  elsif options.key?(:chain)
    options[:chain]
  else
    config.fetch(:chain, true)
  end

  if isolation == :worktree && !Worktree.new(root:).repository?
    refuse("#{root} is not a git repository, so plans cannot be isolated.\n\n" \
           "Run with --isolation shared to work in one tree, serially.", 66)
  end

  # Set before the loop starts, not after: `UI.animate?` (and therefore
  # whether a round prints anything at all) is read the whole time the
  # loop runs, not just when `report` prints its closing summary.
  quiet?(options)
  UI.log_path = options[:log] || config[:log] ||
    File.join(Dir.tmpdir, "agentilda-#{File.basename(root)}.log")
  info("Progress: #{UI.log_path}") unless quiet?(options)
  credentials_warning if commit?(options) && !quiet?(options)

  Control.reset!
  keyboard = Keyboard.listen
  UI.line("keys: h for help — w wrap up · n write out and stop · q quit") if keyboard && !quiet?(options)

  runner = Runner.new(
    tree:, agents:, isolation:, jobs:, plans:, chain:,
    worktree: (Worktree.new(root:) if isolation == :worktree),
    max_rounds: (options[:rounds] || config[:rounds] || 10).to_i,
    executor: Executor.new(root:, timeout:, dry_run: !commit?(options),
      instructions: options[:prompt], model: options[:model],
      max_tokens: (options[:max_tokens] || config[:max_tokens])&.to_i,
      interactive: !keyboard.nil? && commit?(options)), dry_run: !commit?(options),
    publisher: publisher_for(root, isolation, options)
  )

  started = UI.monotonic
  rounds = begin
    runner.call
  ensure
    keyboard&.stop
  end
  report(runner, rounds, options, seconds: UI.monotonic - started)
  exit(failures(rounds).empty? ? 0 : 1)
end