brainiac 0.1.6 → 0.1.8
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 +4 -4
- data/bin/brainiac +4 -2
- data/lib/brainiac/plugins.rb +42 -38
- data/lib/brainiac/prompts.rb +23 -8
- data/lib/brainiac/version.rb +1 -1
- data/templates/cli-providers/kiro.json.example +5 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab0a2720abc8e0ea7c76d7a0c4f8ec6303a744dc8bdf7dfd6cd7d41b5d0671e2
|
|
4
|
+
data.tar.gz: beefb425e88f3418babaf504b5d0a4841c7d7cda03ef189951adb1fd42c747fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 30ed5d2c72453ef0bf624c1bf34df92111bd1e5cab30c492fc483ad9ab241794ff177452b2bbc533e42d39b9199882382eda6930acc89a83f8f3a371020cd166
|
|
7
|
+
data.tar.gz: b5d1b984a1bf7e17e094da496b831f34c822c9368a1c8966b6dc3d30057101c913d69629cf94f9358b6ee196d4a84dd58fb744fe8d652f5280dbf344911efc2f
|
data/bin/brainiac
CHANGED
|
@@ -263,7 +263,10 @@ def wait_for_agents_to_finish
|
|
|
263
263
|
end
|
|
264
264
|
end
|
|
265
265
|
|
|
266
|
-
#
|
|
266
|
+
# stop_server is a command (it stops the server) that happens to return a
|
|
267
|
+
# boolean success flag; renaming it to a predicate (stop_server?) would be
|
|
268
|
+
# semantically wrong, so suppress the predicate-naming cop for this definition.
|
|
269
|
+
# rubocop:disable-next Naming/PredicateMethod
|
|
267
270
|
def stop_server
|
|
268
271
|
pid = find_server_pid
|
|
269
272
|
|
|
@@ -304,7 +307,6 @@ def stop_server
|
|
|
304
307
|
false
|
|
305
308
|
end
|
|
306
309
|
end
|
|
307
|
-
# rubocop:enable Naming/PredicateMethod
|
|
308
310
|
|
|
309
311
|
# Kill a single process: TERM first, KILL if it doesn't exit within 5s.
|
|
310
312
|
def kill_pid(pid, quiet: false)
|
data/lib/brainiac/plugins.rb
CHANGED
|
@@ -40,45 +40,49 @@ end
|
|
|
40
40
|
# Load all installed plugin gems and call their register hooks.
|
|
41
41
|
# Called once during server startup, after core handlers are loaded.
|
|
42
42
|
def load_plugins!(app)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
plugin_module = resolve_plugin_module(name)
|
|
63
|
-
if plugin_module.respond_to?(:register)
|
|
64
|
-
plugin_module.register(app)
|
|
65
|
-
LOG.info "[Plugins] Loaded #{gem_name}"
|
|
66
|
-
else
|
|
67
|
-
LOG.warn "[Plugins] #{gem_name} loaded but no register method found"
|
|
68
|
-
end
|
|
69
|
-
rescue LoadError => e
|
|
70
|
-
LOG.error "[Plugins] Could not load #{gem_name}: #{e.message}"
|
|
71
|
-
if entry.is_a?(Hash) && entry["path"]
|
|
72
|
-
LOG.error "[Plugins] Local path: #{entry["path"]}"
|
|
73
|
-
else
|
|
74
|
-
LOG.error "[Plugins] Is the gem installed? Run: gem install #{gem_name}"
|
|
75
|
-
end
|
|
76
|
-
rescue StandardError => e
|
|
77
|
-
LOG.error "[Plugins] Error registering #{gem_name}: #{e.message}"
|
|
78
|
-
LOG.error "[Plugins] #{e.backtrace.first(3).join("\n ")}"
|
|
79
|
-
end
|
|
43
|
+
installed_plugins.each { |name| load_plugin!(app, name) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Require a single plugin gem and call its register hook.
|
|
47
|
+
# Isolated per-plugin so one failing plugin doesn't abort the rest.
|
|
48
|
+
def load_plugin!(app, name)
|
|
49
|
+
gem_name = "brainiac-#{name}"
|
|
50
|
+
entry = plugin_entry(name)
|
|
51
|
+
|
|
52
|
+
require_plugin_gem(gem_name, entry)
|
|
53
|
+
|
|
54
|
+
plugin_module = resolve_plugin_module(name)
|
|
55
|
+
if plugin_module.respond_to?(:register)
|
|
56
|
+
plugin_module.register(app)
|
|
57
|
+
LOG.info "[Plugins] Loaded #{gem_name}"
|
|
58
|
+
else
|
|
59
|
+
LOG.warn "[Plugins] #{gem_name} loaded but no register method found"
|
|
80
60
|
end
|
|
81
|
-
|
|
61
|
+
rescue LoadError => e
|
|
62
|
+
LOG.error "[Plugins] Could not load #{gem_name}: #{e.message}"
|
|
63
|
+
if entry.is_a?(Hash) && entry["path"]
|
|
64
|
+
LOG.error "[Plugins] Local path: #{entry["path"]}"
|
|
65
|
+
else
|
|
66
|
+
LOG.error "[Plugins] Is the gem installed? Run: gem install #{gem_name}"
|
|
67
|
+
end
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
LOG.error "[Plugins] Error registering #{gem_name}: #{e.message}"
|
|
70
|
+
LOG.error "[Plugins] #{e.backtrace.first(3).join("\n ")}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Require a plugin gem, honoring a local path override and both the
|
|
74
|
+
# hyphen (brainiac-fizzy) and underscore (brainiac_fizzy) naming conventions.
|
|
75
|
+
def require_plugin_gem(gem_name, entry)
|
|
76
|
+
# If plugin has a local path, add its lib/ to load path before requiring
|
|
77
|
+
if entry.is_a?(Hash) && entry["path"]
|
|
78
|
+
lib_path = File.join(entry["path"], "lib")
|
|
79
|
+
$LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
|
|
80
|
+
LOG.info "[Plugins] Loading #{gem_name} from local path: #{entry["path"]}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
require gem_name
|
|
84
|
+
rescue LoadError
|
|
85
|
+
require gem_name.tr("-", "_")
|
|
82
86
|
end
|
|
83
87
|
|
|
84
88
|
# Resolve the plugin module for a given name.
|
data/lib/brainiac/prompts.rb
CHANGED
|
@@ -18,6 +18,13 @@ PROMPT_CORE = <<~PROMPT
|
|
|
18
18
|
Getting the casing wrong means the mention won't link or notify properly.
|
|
19
19
|
{{AGENT_ROSTER}}
|
|
20
20
|
|
|
21
|
+
## Conversation Focus (CRITICAL)
|
|
22
|
+
Your PRIMARY task is ALWAYS the most recent message/comment you've received.
|
|
23
|
+
- Context and history are reference material — they inform your response but don't define your task
|
|
24
|
+
- If the user asks a question, answer THAT question — don't summarize previous work
|
|
25
|
+
- If the user requests a change, make THAT change — don't rehash what you've already done
|
|
26
|
+
- Previous sessions and memory help you understand context, but the current request is what you're here to do
|
|
27
|
+
|
|
21
28
|
## Memory (CRITICAL — read this first)
|
|
22
29
|
You have no persistent memory between sessions. Every time you are invoked, you start completely fresh.
|
|
23
30
|
Memory files MAY exist at `{{MEMORY_DIR}}/` — this is inside the brain, so they survive worktree deletion.
|
|
@@ -174,8 +181,8 @@ CHANNEL_PROMPTS = {}.freeze
|
|
|
174
181
|
# channel: :discord, :github, or plugin-registered channels (e.g., :fizzy, :linear)
|
|
175
182
|
# ---------------------------------------------------------------------------
|
|
176
183
|
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
def render_prompt(template, vars = {}, brain_context: "", card_context: "", comment_history: "", agent_name: AI_AGENT_NAME, channel: nil,
|
|
185
|
+
board_key: nil)
|
|
179
186
|
result = ""
|
|
180
187
|
result += "#{brain_context}\n" unless brain_context.empty?
|
|
181
188
|
result += card_context unless card_context.empty?
|
|
@@ -187,6 +194,10 @@ def render_prompt(template, vars = {}, brain_context: "", card_context: "", agen
|
|
|
187
194
|
|
|
188
195
|
result += template
|
|
189
196
|
|
|
197
|
+
# Comment history (for follow-up scenarios): placed AFTER the triggering comment
|
|
198
|
+
# so the agent sees the current request first, then can reference history as needed
|
|
199
|
+
result += comment_history unless comment_history.empty?
|
|
200
|
+
|
|
190
201
|
# Pre-post comment check: plugin-registered
|
|
191
202
|
plugin_pre_post = Brainiac.channel_pre_post_checks[channel]
|
|
192
203
|
result += plugin_pre_post if plugin_pre_post
|
|
@@ -194,6 +205,15 @@ def render_prompt(template, vars = {}, brain_context: "", card_context: "", agen
|
|
|
194
205
|
# Reflection prompt — skip for now
|
|
195
206
|
# result += PROMPT_REFLECTION
|
|
196
207
|
|
|
208
|
+
populate_prompt_vars(vars, agent_name: agent_name, board_key: board_key)
|
|
209
|
+
|
|
210
|
+
vars.each { |key, val| result.gsub!("{{#{key}}}", val.to_s) }
|
|
211
|
+
result
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Populate the default template variables (dirs, column IDs, roster) and touch
|
|
215
|
+
# the agent's memory file. Extracted from render_prompt to keep complexity down.
|
|
216
|
+
def populate_prompt_vars(vars, agent_name:, board_key:)
|
|
197
217
|
vars["KNOWLEDGE_DIR"] ||= KNOWLEDGE_DIR
|
|
198
218
|
vars["MEMORY_DIR"] ||= memory_dir_for(agent_name)
|
|
199
219
|
vars["PERSONA_DIR"] ||= persona_dir_for(agent_name)
|
|
@@ -215,14 +235,9 @@ def render_prompt(template, vars = {}, brain_context: "", card_context: "", agen
|
|
|
215
235
|
FileUtils.touch(memory_file)
|
|
216
236
|
end
|
|
217
237
|
|
|
218
|
-
|
|
219
|
-
roster_lines = roster.map { |_key, display| " - @#{display}" }.join("\n")
|
|
238
|
+
roster_lines = agent_roster.map { |_key, display| " - @#{display}" }.join("\n")
|
|
220
239
|
vars["AGENT_ROSTER"] ||= roster_lines
|
|
221
|
-
|
|
222
|
-
vars.each { |key, val| result.gsub!("{{#{key}}}", val.to_s) }
|
|
223
|
-
result
|
|
224
240
|
end
|
|
225
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
226
241
|
|
|
227
242
|
# Lean prompt for resumed sessions. The previous session already has the full context
|
|
228
243
|
# (role, persona, knowledge, core instructions, channel prompts). We only send the new
|
data/lib/brainiac/version.rb
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
"model_flag": "--model",
|
|
6
6
|
"effort_flag": "--effort",
|
|
7
7
|
"prompt_mode": "stdin",
|
|
8
|
-
"
|
|
8
|
+
"resume_id_flag": "--resume-id",
|
|
9
|
+
"session_list_command": "kiro-cli chat --list-sessions -f json",
|
|
10
|
+
"session_id_field": "sessionId",
|
|
11
|
+
"session_directory_field": "cwd",
|
|
12
|
+
"session_updated_field": "updatedAt",
|
|
9
13
|
"list_models_command": "kiro-cli chat --list-models --format json",
|
|
10
14
|
"models": {
|
|
11
15
|
"opus": "claude-opus-4.5",
|