Class: Agentilda::Agents

Inherits:
Object
  • Object
show all
Defined in:
lib/agentilda/agent.rb

Overview

Loads and indexes the agent definitions.

Constant Summary collapse

DEFAULT_DIR =

Where definitions live, unless told otherwise.

File.expand_path("../../agents", __dir__)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir: DEFAULT_DIR, roster: nil) ⇒ Agents

Returns a new instance of Agents.

Parameters:

  • dir (String) (defaults to: DEFAULT_DIR)
  • roster (Array<Agentilda::Agent>, nil) (defaults to: nil)

    a pre-selected list, used by #only and #without to derive a narrower roster; nil (the default) loads every definition in dir



47
48
49
50
# File 'lib/agentilda/agent.rb', line 47

def initialize(dir: DEFAULT_DIR, roster: nil)
  @dir = File.expand_path(dir)
  @all = roster
end

Instance Attribute Details

#dirString (readonly)

Returns:

  • (String)


53
54
55
# File 'lib/agentilda/agent.rb', line 53

def dir
  @dir
end

Instance Method Details

#allArray<Agentilda::Agent>

Returns in name order.

Returns:



56
57
58
# File 'lib/agentilda/agent.rb', line 56

def all
  @all ||= Dir.glob(File.join(dir, "*.md")).sort.filter_map { |path| parse(path) }
end

#find(name) ⇒ Agentilda::Agent?

Parameters:

  • name (String)

Returns:



62
# File 'lib/agentilda/agent.rb', line 62

def find(name) = all.find { |a| a.name == name.to_s }

#for_status(status) ⇒ Array<Agentilda::Agent>

Every agent that will act on a plan in this state, in definition order. A read-only agent is never offered work by the loop — it has nothing to advance, so including it would make every round look productive.

Parameters:

Returns:



111
# File 'lib/agentilda/agent.rb', line 111

def for_status(status) = all.select { |a| a.handles?(status) && !a.read_only? }

#match(query) ⇒ Array<Agentilda::Agent>

Every agent the query could mean. An exact name wins outright; failing that the query matches as a prefix, and failing that anywhere in the name, so leah finds leah-researcher and review finds hansolo-reviewer. A directory or a trailing .md is stripped first, because tab completion hands those in.

Parameters:

  • query (String)

Returns:



94
95
96
97
98
99
100
101
102
103
# File 'lib/agentilda/agent.rb', line 94

def match(query)
  wanted = File.basename(query.to_s, ".md")
  exact = all.select { |a| a.name == wanted }
  return exact unless exact.empty?

  prefixed = all.select { |a| a.name.start_with?(wanted) }
  return prefixed unless prefixed.empty?

  all.select { |a| a.name.include?(wanted) }
end

#only(*names) ⇒ Agentilda::Agents

A roster holding only the agents named — what run --agent hands the loop, so a restriction typed at the command line restricts assignments and not merely chaining.

Parameters:

  • names (Array<String>)

Returns:



70
71
72
73
# File 'lib/agentilda/agent.rb', line 70

def only(*names)
  wanted = names.flatten.map(&:to_s)
  self.class.new(dir:, roster: all.select { |a| wanted.include?(a.name) })
end

#without(*names) ⇒ Agentilda::Agents

A roster without the agents named — what run --skip hands the loop. A plan sitting in a skipped agent's state is simply never assigned, the same way a state no agent handles is stepped around.

Parameters:

  • names (Array<String>)

Returns:



81
82
83
84
# File 'lib/agentilda/agent.rb', line 81

def without(*names)
  unwanted = names.flatten.map(&:to_s)
  self.class.new(dir:, roster: all.reject { |a| unwanted.include?(a.name) })
end