brainiac 0.1.3 → 0.1.5
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/Gemfile.lock +2 -2
- data/README.md +3 -6
- data/lib/brainiac/helpers.rb +45 -7
- data/lib/brainiac/profiles.rb +4 -4
- data/lib/brainiac/version.rb +1 -1
- data/templates/cli-providers/kiro.json.example +1 -2
- 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: e0e0e919150a76bcf0b7f289b72cfdbc8000bc0cacf94bc9103685cf49331415
|
|
4
|
+
data.tar.gz: 58174d5dcf9d0c3641f9a96317f6d94a4dda153481d586659275e8e81623fd24
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 73331c38b19e2ef1bb2d352b0a9b6cf6259d7075022e6ea529ad1203fd8dc30369d83b7d88e603c1ab04a59ec2a96856b01a59fc669a4b43c1efbba847ea2399
|
|
7
|
+
data.tar.gz: 917ea682b6749cef4a17d76a2a40d10deabb175c30af77044dc5a3d8be7132ab7f52e8722f8881bc11689c3d597ad83039e107e336cf40d891a0a222396df405
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
brainiac (0.1.
|
|
4
|
+
brainiac (0.1.5)
|
|
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.1.
|
|
87
|
+
brainiac (0.1.5)
|
|
88
88
|
json (2.21.2) sha256=1f1d3b7cf2b3ba1a69beca0bb6db13d5438b80bff3cd54cdaaa620b9b07c1c6a
|
|
89
89
|
language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
|
|
90
90
|
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
data/README.md
CHANGED
|
@@ -704,14 +704,11 @@ Exactly one profile should be marked `"default": true` — it applies when no `[
|
|
|
704
704
|
|
|
705
705
|
### Model selection
|
|
706
706
|
|
|
707
|
-
Model selection is a **cli-provider concern**, not a profile concern. kiro-cli
|
|
707
|
+
Model selection is a **cli-provider concern**, not a profile concern. kiro-cli accepts `--model <id>` per run, so the kiro-cli provider uses `"model_flag": "--model"`. A model tag (`[opus]`, `[sol]`, etc.) only affects that dispatch (and the thread/work item it was sent in). It never changes the account's saved `chat.defaultModel`, so other threads are unaffected.
|
|
708
708
|
|
|
709
|
-
|
|
710
|
-
- `"settings_model_cmd": ["kiro-cli", "settings", "chat.defaultModel"]` — when a model tag (`[opus]`, `[sonnet]`, etc.) or Fizzy card tag resolves to a model, brainiac runs this command (with the profile's env) to set `chat.defaultModel` before spawning the dispatch
|
|
709
|
+
Each account's `chat.defaultModel` (in its own `KIRO_HOME`) only applies when no model is resolved at all. Keep it set to a model that account can actually use.
|
|
711
710
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
**Concurrency note:** the settings write is per-`KIRO_HOME`. Concurrent dispatches on *different* profiles never conflict. Two concurrent dispatches on the *same* profile with different model tags have a narrow write-before-spawn race — acceptable for single-user setups, but worth knowing.
|
|
711
|
+
**`settings_model_cmd` (legacy, avoid for kiro-cli):** for CLIs with no runtime model flag, a provider can set `"model_flag": ""` plus `"settings_model_cmd": [...]`, and brainiac runs that command before each tagged dispatch. That write is *persistent*: it becomes the account's default for every later dispatch, including unrelated threads. If a tag writes a model the default account doesn't have (e.g. `[sol]` landing on `q`), every untagged dispatch on that account fails with `The model '...' is not available`.
|
|
715
712
|
|
|
716
713
|
### Seeding a new KIRO_HOME
|
|
717
714
|
|
data/lib/brainiac/helpers.rb
CHANGED
|
@@ -315,6 +315,11 @@ end
|
|
|
315
315
|
def register_work_item(branch:, worktree: nil, project: nil, agent: nil, source: nil, source_data: {})
|
|
316
316
|
map = load_work_item_map
|
|
317
317
|
|
|
318
|
+
# Normalize source to a string key — callers may pass a symbol (e.g. :fizzy).
|
|
319
|
+
# The sources hash is JSON-serialized with string keys, so mixing symbol and
|
|
320
|
+
# string keys produces a "duplicate key" error in JSON.pretty_generate.
|
|
321
|
+
source = source.to_s if source
|
|
322
|
+
|
|
318
323
|
# Check if a work item already exists for this branch
|
|
319
324
|
existing_id = nil
|
|
320
325
|
map.each do |wid, info|
|
|
@@ -330,7 +335,7 @@ def register_work_item(branch:, worktree: nil, project: nil, agent: nil, source:
|
|
|
330
335
|
# Update existing entry — merge in new source, update worktree/agent if provided
|
|
331
336
|
map[work_item_id]["worktree"] = worktree if worktree
|
|
332
337
|
map[work_item_id]["agent"] = agent if agent
|
|
333
|
-
map[work_item_id]["sources"]
|
|
338
|
+
map[work_item_id]["sources"] = normalize_source_keys(map[work_item_id]["sources"])
|
|
334
339
|
map[work_item_id]["sources"][source] = source_data if source
|
|
335
340
|
else
|
|
336
341
|
# Create new entry
|
|
@@ -348,10 +353,10 @@ def register_work_item(branch:, worktree: nil, project: nil, agent: nil, source:
|
|
|
348
353
|
work_item_id
|
|
349
354
|
end
|
|
350
355
|
|
|
351
|
-
# Update dispatch overrides on a work item (cli_provider, model, effort).
|
|
356
|
+
# Update dispatch overrides on a work item (cli_provider, model, effort, profile).
|
|
352
357
|
# Only non-nil values are stored; nil values are removed from existing overrides.
|
|
353
358
|
# Returns true if the work item was found and updated, false otherwise.
|
|
354
|
-
def update_work_item_overrides(cli_provider: nil, model: nil, effort: nil, work_item_id: nil, branch: nil) # rubocop:disable Naming/PredicateMethod
|
|
359
|
+
def update_work_item_overrides(cli_provider: nil, model: nil, effort: nil, profile: nil, work_item_id: nil, branch: nil) # rubocop:disable Naming/PredicateMethod
|
|
355
360
|
map = load_work_item_map
|
|
356
361
|
|
|
357
362
|
target_id = work_item_id
|
|
@@ -370,6 +375,7 @@ def update_work_item_overrides(cli_provider: nil, model: nil, effort: nil, work_
|
|
|
370
375
|
overrides["cli_provider"] = cli_provider if cli_provider
|
|
371
376
|
overrides["model"] = model if model
|
|
372
377
|
overrides["effort"] = effort if effort
|
|
378
|
+
overrides["profile"] = profile if profile
|
|
373
379
|
overrides.compact!
|
|
374
380
|
|
|
375
381
|
if overrides.empty?
|
|
@@ -412,16 +418,26 @@ end
|
|
|
412
418
|
# inline_cli_provider: — [cli:X] from current message (nil if not specified)
|
|
413
419
|
# inline_model: — resolved model from current message (nil if not specified)
|
|
414
420
|
# inline_effort: — [effort:X] from current message (nil if not specified)
|
|
421
|
+
# inline_profile: — [profile:X]/[p:X] from current message (nil if not specified)
|
|
422
|
+
#
|
|
423
|
+
# A profile sticks to the work item like model/effort do: [p:k+] once and every later
|
|
424
|
+
# untagged reply keeps running on k+ until a different [p:X] (e.g. [p:q]) replaces it.
|
|
425
|
+
# Unknown profile names are never persisted, so a typo can't pin a work item to a
|
|
426
|
+
# profile that doesn't exist (profile_spawn_env ignores it for this dispatch too).
|
|
415
427
|
#
|
|
416
|
-
# Returns: { cli_provider
|
|
417
|
-
def resolve_work_item_overrides(work_item_id: nil, branch: nil, inline_cli_provider: nil, inline_model: nil, inline_effort: nil
|
|
428
|
+
# Returns: { cli_provider:, model:, effort:, profile: } (each String or nil)
|
|
429
|
+
def resolve_work_item_overrides(work_item_id: nil, branch: nil, inline_cli_provider: nil, inline_model: nil, inline_effort: nil,
|
|
430
|
+
inline_profile: nil)
|
|
418
431
|
stored = work_item_overrides_for(work_item_id: work_item_id, branch: branch)
|
|
432
|
+
inline_profile = inline_profile&.to_s&.downcase
|
|
433
|
+
persist_profile = inline_profile && profile_entry(inline_profile) ? inline_profile : nil
|
|
419
434
|
|
|
420
435
|
# Inline tags override stored values
|
|
421
436
|
resolved = {
|
|
422
437
|
cli_provider: inline_cli_provider || stored["cli_provider"],
|
|
423
438
|
model: inline_model || stored["model"],
|
|
424
|
-
effort: inline_effort || stored["effort"]
|
|
439
|
+
effort: inline_effort || stored["effort"],
|
|
440
|
+
profile: inline_profile || stored["profile"]
|
|
425
441
|
}
|
|
426
442
|
|
|
427
443
|
# Persist any new inline overrides to the work item for future dispatches
|
|
@@ -429,6 +445,7 @@ def resolve_work_item_overrides(work_item_id: nil, branch: nil, inline_cli_provi
|
|
|
429
445
|
new_overrides[:cli_provider] = inline_cli_provider if inline_cli_provider
|
|
430
446
|
new_overrides[:model] = inline_model if inline_model
|
|
431
447
|
new_overrides[:effort] = inline_effort if inline_effort
|
|
448
|
+
new_overrides[:profile] = persist_profile if persist_profile
|
|
432
449
|
|
|
433
450
|
update_work_item_overrides(work_item_id: work_item_id, branch: branch, **new_overrides) if new_overrides.any? && (work_item_id || branch)
|
|
434
451
|
|
|
@@ -440,6 +457,10 @@ end
|
|
|
440
457
|
def register_work_item_source(source:, source_data:, work_item_id: nil, branch: nil) # rubocop:disable Naming/PredicateMethod
|
|
441
458
|
map = load_work_item_map
|
|
442
459
|
|
|
460
|
+
# Normalize source to a string key — callers may pass a symbol (e.g. :fizzy).
|
|
461
|
+
# See register_work_item for why mixing symbol/string keys breaks JSON serialization.
|
|
462
|
+
source = source.to_s if source
|
|
463
|
+
|
|
443
464
|
# Find by ID or branch
|
|
444
465
|
target_id = work_item_id
|
|
445
466
|
unless target_id
|
|
@@ -453,12 +474,24 @@ def register_work_item_source(source:, source_data:, work_item_id: nil, branch:
|
|
|
453
474
|
|
|
454
475
|
return false unless target_id && map[target_id]
|
|
455
476
|
|
|
456
|
-
map[target_id]["sources"]
|
|
477
|
+
map[target_id]["sources"] = normalize_source_keys(map[target_id]["sources"])
|
|
457
478
|
map[target_id]["sources"][source] = source_data
|
|
458
479
|
save_work_item_map(map)
|
|
459
480
|
true
|
|
460
481
|
end
|
|
461
482
|
|
|
483
|
+
# Normalize a work item's sources hash so all keys are strings. Guards against
|
|
484
|
+
# a previously-corrupted in-memory hash that mixed symbol and string keys
|
|
485
|
+
# (e.g. both :fizzy and "fizzy"), which would otherwise crash JSON serialization.
|
|
486
|
+
# A later entry (symbol source just written) wins over an existing string entry.
|
|
487
|
+
def normalize_source_keys(sources)
|
|
488
|
+
return {} unless sources.is_a?(Hash)
|
|
489
|
+
|
|
490
|
+
sources.each_with_object({}) do |(key, value), acc|
|
|
491
|
+
acc[key.to_s] = value
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
462
495
|
def slugify(title, max_length: 40)
|
|
463
496
|
title.downcase.gsub(/[^a-z0-9\s-]/, "").strip.gsub(/\s+/, "-").slice(0, max_length).chomp("-")
|
|
464
497
|
end
|
|
@@ -965,6 +998,11 @@ end
|
|
|
965
998
|
# run that command synchronously before spawning the agent so the correct model
|
|
966
999
|
# is already persisted in the provider's settings dir (e.g. KIRO_HOME/settings/cli.json).
|
|
967
1000
|
#
|
|
1001
|
+
# WARNING: this write is sticky. It changes the account's default for every later
|
|
1002
|
+
# dispatch, not just this one, so a tag in one thread leaks into unrelated threads.
|
|
1003
|
+
# Only use it for CLIs with no per-run model flag. kiro-cli supports --model and
|
|
1004
|
+
# should use "model_flag": "--model" instead.
|
|
1005
|
+
#
|
|
968
1006
|
# Only fires when:
|
|
969
1007
|
# - model_flag is absent or empty (the provider doesn't support runtime model selection)
|
|
970
1008
|
# - settings_model_cmd is configured on the provider
|
data/lib/brainiac/profiles.rb
CHANGED
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
# lives — giving each profile its own KIRO_HOME means each account has
|
|
18
18
|
# its own model setting.
|
|
19
19
|
#
|
|
20
|
-
# Model selection is a cli-provider concern, not a profile concern.
|
|
21
|
-
# --model
|
|
22
|
-
#
|
|
23
|
-
#
|
|
20
|
+
# Model selection is a cli-provider concern, not a profile concern. kiro-cli takes
|
|
21
|
+
# --model per run ("model_flag": "--model"), so an inline model tag only affects that
|
|
22
|
+
# dispatch. Each account's chat.defaultModel (in its own KIRO_HOME) is just the fallback
|
|
23
|
+
# when no model resolves — keep it set to a model that account actually has.
|
|
24
24
|
#
|
|
25
25
|
# NOTE: a fresh KIRO_HOME starts empty. brainiac dispatches with --agent <name>, so each
|
|
26
26
|
# KIRO_HOME needs agent definitions (and any MCP/permissions you rely on) or dispatches
|
data/lib/brainiac/version.rb
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
"binary": "kiro-cli",
|
|
3
3
|
"default_args": "chat --trust-all-tools --no-interactive",
|
|
4
4
|
"agent_flag": "--agent",
|
|
5
|
-
"model_flag": "",
|
|
6
|
-
"settings_model_cmd": ["kiro-cli", "settings", "chat.defaultModel"],
|
|
5
|
+
"model_flag": "--model",
|
|
7
6
|
"effort_flag": "--effort",
|
|
8
7
|
"prompt_mode": "stdin",
|
|
9
8
|
"resume_flag": "--resume",
|