brainiac 0.0.14 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '04080c41c24921ed739b0d28ad15ce61b8eb5b5c12935d0cb0b1d72c6b0deeab'
4
- data.tar.gz: bbb1dceb84ecaba924ada9189e8b63320828c035da4a6ff466ba8b47c4c30708
3
+ metadata.gz: 677c29e7ddcf5d9718eda91175193e9ffa448be65fac5e698982009327ee3576
4
+ data.tar.gz: ec7c044b3661f5fdca2f462a305a53ab47c6cfd76299462fa5e3df0f83f28a3a
5
5
  SHA512:
6
- metadata.gz: '0699a2277c78aa2c33e57b6ee4394ac7be8d27bc483a9f3866dd27e644e40fbdea4b85168b3452c6cbbd4fce42da6e282a9609e93541802b2c751f9845c55ea2'
7
- data.tar.gz: adcd7eab6269a33b278499e3b873b072f488f7b06b18378507fba456f883b1c525b7dd8d8ce82b13e6464ab5184007e0887dfa9a82a69910f8f8d0164660ff9b
6
+ metadata.gz: e4f3c2af97935afe5666ae722629f2a6a15af09fef73cc8ef969a11eccbf9c315cf50831e293a381cc6d2f4b2a383bce03a3fb673c4f9294ab17cb11f3656270
7
+ data.tar.gz: f70117f73d55be6914223bca9cbe331e0e9ecdece704349b71eb0c40f35fdcc8da8d2fdcf9cf297b7d9fa7b91b45350a09de12271332fcac7a28cda0d984c91c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- brainiac (0.0.14)
4
+ brainiac (0.0.15)
5
5
  puma (~> 7.2)
6
6
  rackup (~> 2.3)
7
7
  sinatra (~> 4.1)
@@ -84,7 +84,7 @@ DEPENDENCIES
84
84
  CHECKSUMS
85
85
  ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
86
86
  base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
87
- brainiac (0.0.14)
87
+ brainiac (0.0.15)
88
88
  json (2.19.9) sha256=9b9025b7cdddafa38d316eca0b2358488e42d417045c1b90d216a9fefe46b79a
89
89
  language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
90
90
  lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
data/bin/brainiac CHANGED
@@ -120,6 +120,65 @@ rescue StandardError
120
120
  []
121
121
  end
122
122
 
123
+ # Notify the running server to reload configs (triggers agent lifecycle hooks).
124
+ # Silently does nothing if the server isn't running.
125
+ def notify_server_reload
126
+ config = load_config
127
+ server_url = config["server_url"] || "http://localhost:4567"
128
+ uri = URI("#{server_url}/api/reload")
129
+ http = Net::HTTP.new(uri.host, uri.port)
130
+ http.open_timeout = 2
131
+ http.read_timeout = 5
132
+ request = Net::HTTP::Post.new(uri.path)
133
+ http.request(request)
134
+ rescue StandardError
135
+ nil
136
+ end
137
+
138
+ # Run CLI-time plugin hooks for agent lifecycle events.
139
+ # Loads each installed plugin's CLI module and calls the hook method if defined.
140
+ # This runs synchronously and can prompt for interactive input.
141
+ def notify_plugins_agent_created(agent_key, entry)
142
+ plugins_file = File.join(BRAINIAC_DIR, "plugins.json")
143
+ return unless File.exist?(plugins_file)
144
+
145
+ plugins_config = JSON.parse(File.read(plugins_file))
146
+ (plugins_config["plugins"] || []).each do |pentry|
147
+ pname = pentry.is_a?(Hash) ? pentry["name"] : pentry.to_s
148
+ try_require_metadata(pname, pentry)
149
+ try_require_plugin_cli(pname, pentry)
150
+ mod = resolve_plugin_module_for(pname)
151
+ next unless mod.respond_to?(:on_agent_created)
152
+
153
+ mod.on_agent_created(agent_key, entry)
154
+ rescue StandardError => e
155
+ puts " ⚠ Plugin '#{pname}' hook failed: #{e.message}"
156
+ end
157
+ rescue JSON::ParserError
158
+ nil
159
+ end
160
+
161
+ # Run CLI-time plugin hooks for agent removal.
162
+ def notify_plugins_agent_removed(agent_key, display_name)
163
+ plugins_file = File.join(BRAINIAC_DIR, "plugins.json")
164
+ return unless File.exist?(plugins_file)
165
+
166
+ plugins_config = JSON.parse(File.read(plugins_file))
167
+ (plugins_config["plugins"] || []).each do |pentry|
168
+ pname = pentry.is_a?(Hash) ? pentry["name"] : pentry.to_s
169
+ try_require_metadata(pname, pentry)
170
+ try_require_plugin_cli(pname, pentry)
171
+ mod = resolve_plugin_module_for(pname)
172
+ next unless mod.respond_to?(:on_agent_removed)
173
+
174
+ mod.on_agent_removed(agent_key, display_name)
175
+ rescue StandardError => e
176
+ puts " ⚠ Plugin '#{pname}' hook failed: #{e.message}"
177
+ end
178
+ rescue JSON::ParserError
179
+ nil
180
+ end
181
+
123
182
  def wait_for_agents_to_finish
124
183
  sessions = fetch_active_sessions
125
184
  return true if sessions.empty?
@@ -1873,6 +1932,12 @@ when "agent"
1873
1932
  brain_init(name)
1874
1933
  end
1875
1934
 
1935
+ # Let plugins prompt for additional config (e.g. Discord user ID)
1936
+ notify_plugins_agent_created(agent_key, entry)
1937
+
1938
+ # Notify running server to reload and trigger :agent_added hook
1939
+ notify_server_reload
1940
+
1876
1941
  when "remove", "delete", "rm"
1877
1942
  name = ARGV.shift
1878
1943
  unless name
@@ -1894,6 +1959,12 @@ when "agent"
1894
1959
  puts "✓ Removed agent '#{display}' (#{agent_key}) from registry"
1895
1960
  puts " Note: Persona files at ~/.brainiac/brain/persona/#{agent_key}/ were not deleted."
1896
1961
 
1962
+ # Let plugins clean up their config
1963
+ notify_plugins_agent_removed(agent_key, display)
1964
+
1965
+ # Notify running server to reload and trigger :agent_removed hook
1966
+ notify_server_reload
1967
+
1897
1968
  when nil
1898
1969
  puts <<~HELP
1899
1970
  Usage: brainiac agent <name> <command> [args]
@@ -50,8 +50,26 @@ AGENT_REGISTRY = load_agent_registry
50
50
  def reload_agent_registry!(force: false)
51
51
  return unless file_changed?(AGENT_REGISTRY_FILE, force: force)
52
52
 
53
+ old_keys = AGENT_REGISTRY.keys.to_set
53
54
  AGENT_REGISTRY.replace(load_agent_registry)
55
+ new_keys = AGENT_REGISTRY.keys.to_set
54
56
  LOG.info "Reloaded agent registry: #{AGENT_REGISTRY.keys.join(", ")}"
57
+
58
+ # Emit lifecycle hooks for added/removed agents
59
+ added = new_keys - old_keys
60
+ removed = old_keys - new_keys
61
+
62
+ added.each do |key|
63
+ entry = AGENT_REGISTRY[key]
64
+ display_name = entry.is_a?(Hash) ? (entry["display_name"] || key.capitalize) : key.capitalize
65
+ LOG.info "Agent added: #{display_name} (#{key})"
66
+ Brainiac.emit(:agent_added, agent_key: key, display_name: display_name, entry: entry)
67
+ end
68
+
69
+ removed.each do |key|
70
+ LOG.info "Agent removed: #{key}"
71
+ Brainiac.emit(:agent_removed, agent_key: key)
72
+ end
55
73
  end
56
74
 
57
75
  # Get the env hash for an agent. Returns {} if none configured.
@@ -9,6 +9,8 @@
9
9
  #
10
10
  # Events:
11
11
  # :agent_completed — After an agent session finishes (success or failure)
12
+ # :agent_added — When a new agent appears in the registry (after reload)
13
+ # :agent_removed — When an agent is removed from the registry (after reload)
12
14
  # :pr_merged — After a GitHub PR is merged
13
15
  # :pr_opened — After a GitHub PR is opened
14
16
  # :pr_reviewed — After a PR review is submitted
@@ -40,9 +40,11 @@ INTENT_PROMPT_TEMPLATE = <<~PROMPT
40
40
 
41
41
  Rules:
42
42
  - If the message is giving {{AGENT_NAME}} instructions, asking {{AGENT_NAME}} a question, or continuing a conversation WITH {{AGENT_NAME}} → yes
43
+ - If the message starts with or addresses someone else by name (e.g., "Adam, ...", "Hey Sarah", "Effie, ...") → no (it's directed at that person, not {{AGENT_NAME}})
43
44
  - If the message is humans talking to each other and {{AGENT_NAME}} is not being addressed → no
44
45
  - If the message is a simple acknowledgment (like "thanks", "ok", "got it") directed at {{AGENT_NAME}}'s previous work → no
45
46
  - If the message is asking a question to another person or agent → no
47
+ - If the message is responding to or commenting on what someone OTHER than {{AGENT_NAME}} just said → no
46
48
  - If uncertain, lean toward yes (better to respond unnecessarily than miss a request)
47
49
 
48
50
  Respond with ONLY "yes" or "no" — nothing else.
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Brainiac
4
4
  # @return [String] the current gem version
5
- VERSION = "0.0.14"
5
+ VERSION = "0.0.15"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brainiac
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis