brainiac 0.1.3 → 0.1.4

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: be4dc2e28622b168e577e1813ab7bda98ce074fc04d0f4b63fe69502e24ef6f2
4
- data.tar.gz: 06513ef234e4a5ce4c23bc809c02942193a73394953a749df3b63653ceb70bc2
3
+ metadata.gz: d73484edd8f42558e933347b45b50f5aa27065a86798ae4d764e44b0d07534f9
4
+ data.tar.gz: bcc9a1a5bf6094ef8138cf345e2e517004c412d983c4475c80b89cb005bbf6aa
5
5
  SHA512:
6
- metadata.gz: d8bedd4bef849857ac72d040d925bb74d23f97c10e575fdb8326c85d1580c60fbaff5e77a65d8958685c00b3b79f3b3d22e5b7304b194a5746e2aff74c4e850e
7
- data.tar.gz: c47e8739c7a655bde2283876538fd08d5b3b9500fd6bb56f38c2a02f4214d89b15be2f127f0752a78d68a4ddc13af0e7fb97a03d8db1a8fa1d43a61ac76495f1
6
+ metadata.gz: 1ecd465ba660b6a145c15a9fa7fd17a13dad35bf2dff802b18dcba92949964b5df0be02e26ebc643a74582f010cecf520fc7d04cd32b4acda83891768cd0c6c1
7
+ data.tar.gz: ca830bd810461ff79c474bf5a2744f187a29ee8040d7af0b14c7143dd76c92dbb6e1117f942e51610ecb8e3bdb9d48739a48f7862006f8f84a4d7a66bb40481a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- brainiac (0.1.3)
4
+ brainiac (0.1.4)
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.3)
87
+ brainiac (0.1.4)
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's backend doesn't support the `--model` runtime flag — model selection happens via `settings chat.defaultModel`. The kiro-cli provider is configured to use this approach:
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
- - `"model_flag": ""` — no `--model` flag is ever passed
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
- With per-profile `KIRO_HOME`, the settings write goes to that profile's own `settings/cli.json` — so `[p:k+] [opus]` updates k+'s model, and `[p:q] [sonnet]` updates q's model, independently.
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
 
@@ -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
@@ -440,6 +445,10 @@ end
440
445
  def register_work_item_source(source:, source_data:, work_item_id: nil, branch: nil) # rubocop:disable Naming/PredicateMethod
441
446
  map = load_work_item_map
442
447
 
448
+ # Normalize source to a string key — callers may pass a symbol (e.g. :fizzy).
449
+ # See register_work_item for why mixing symbol/string keys breaks JSON serialization.
450
+ source = source.to_s if source
451
+
443
452
  # Find by ID or branch
444
453
  target_id = work_item_id
445
454
  unless target_id
@@ -453,12 +462,24 @@ def register_work_item_source(source:, source_data:, work_item_id: nil, branch:
453
462
 
454
463
  return false unless target_id && map[target_id]
455
464
 
456
- map[target_id]["sources"] ||= {}
465
+ map[target_id]["sources"] = normalize_source_keys(map[target_id]["sources"])
457
466
  map[target_id]["sources"][source] = source_data
458
467
  save_work_item_map(map)
459
468
  true
460
469
  end
461
470
 
471
+ # Normalize a work item's sources hash so all keys are strings. Guards against
472
+ # a previously-corrupted in-memory hash that mixed symbol and string keys
473
+ # (e.g. both :fizzy and "fizzy"), which would otherwise crash JSON serialization.
474
+ # A later entry (symbol source just written) wins over an existing string entry.
475
+ def normalize_source_keys(sources)
476
+ return {} unless sources.is_a?(Hash)
477
+
478
+ sources.each_with_object({}) do |(key, value), acc|
479
+ acc[key.to_s] = value
480
+ end
481
+ end
482
+
462
483
  def slugify(title, max_length: 40)
463
484
  title.downcase.gsub(/[^a-z0-9\s-]/, "").strip.gsub(/\s+/, "-").slice(0, max_length).chomp("-")
464
485
  end
@@ -965,6 +986,11 @@ end
965
986
  # run that command synchronously before spawning the agent so the correct model
966
987
  # is already persisted in the provider's settings dir (e.g. KIRO_HOME/settings/cli.json).
967
988
  #
989
+ # WARNING: this write is sticky. It changes the account's default for every later
990
+ # dispatch, not just this one, so a tag in one thread leaks into unrelated threads.
991
+ # Only use it for CLIs with no per-run model flag. kiro-cli supports --model and
992
+ # should use "model_flag": "--model" instead.
993
+ #
968
994
  # Only fires when:
969
995
  # - model_flag is absent or empty (the provider doesn't support runtime model selection)
970
996
  # - settings_model_cmd is configured on the provider
@@ -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. To stop passing
21
- # --model (e.g. because the CLI backend doesn't support the flag), set "model_flag": ""
22
- # in the cli-provider config (~/.brainiac/cli-providers/kiro.json). Each account's
23
- # chat.defaultModel (in its own KIRO_HOME) then drives model selection instead.
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Brainiac
4
4
  # @return [String] the current gem version
5
- VERSION = "0.1.3"
5
+ VERSION = "0.1.4"
6
6
  end
@@ -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",
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.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis