brainiac 0.1.2 → 0.1.3
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 +66 -0
- data/lib/brainiac/helpers.rb +115 -7
- data/lib/brainiac/profiles.rb +46 -6
- data/lib/brainiac/session_history.rb +1 -0
- data/lib/brainiac/version.rb +1 -1
- data/templates/cli-providers/kiro.json.example +2 -1
- data/templates/profiles.json.example +7 -5
- 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: be4dc2e28622b168e577e1813ab7bda98ce074fc04d0f4b63fe69502e24ef6f2
|
|
4
|
+
data.tar.gz: 06513ef234e4a5ce4c23bc809c02942193a73394953a749df3b63653ceb70bc2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8bedd4bef849857ac72d040d925bb74d23f97c10e575fdb8326c85d1580c60fbaff5e77a65d8958685c00b3b79f3b3d22e5b7304b194a5746e2aff74c4e850e
|
|
7
|
+
data.tar.gz: c47e8739c7a655bde2283876538fd08d5b3b9500fd6bb56f38c2a02f4214d89b15be2f127f0752a78d68a4ddc13af0e7fb97a03d8db1a8fa1d43a61ac76495f1
|
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.3)
|
|
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.3)
|
|
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
|
@@ -661,6 +661,72 @@ brainiac discord config # Show current Discord config
|
|
|
661
661
|
brainiac discord status # Check bot status via server API
|
|
662
662
|
```
|
|
663
663
|
|
|
664
|
+
## Profiles (Multi-Account)
|
|
665
|
+
|
|
666
|
+
Profiles let you inject a named set of environment variables into an agent dispatch without standing up a separate agent. The canonical use-case is running different CLI accounts (e.g. two AWS accounts) from the same brainiac instance.
|
|
667
|
+
|
|
668
|
+
### How it works
|
|
669
|
+
|
|
670
|
+
Add a `[profile:name]` or `[p:name]` inline tag to any Discord message, Fizzy comment, or GitHub message. The matching profile's env is merged into the dispatch — same CLI, same agent, different credentials.
|
|
671
|
+
|
|
672
|
+
### kiro-cli: two env vars, two concerns
|
|
673
|
+
|
|
674
|
+
kiro-cli splits state across two independent dirs:
|
|
675
|
+
|
|
676
|
+
| Env var | What it controls | File |
|
|
677
|
+
|---------|-----------------|------|
|
|
678
|
+
| `XDG_DATA_HOME` | Auth store — tokens + which account you're logged into | `kiro-cli/data.sqlite3` |
|
|
679
|
+
| `KIRO_HOME` | Settings — `chat.defaultModel`, agents, MCP, permissions, steering | `settings/cli.json` |
|
|
680
|
+
|
|
681
|
+
Give each profile its own `KIRO_HOME` and each account gets its own `chat.defaultModel`. That's the clean way to run different accounts on different models — set each account's model once and let it drive.
|
|
682
|
+
|
|
683
|
+
### Configuration (`~/.brainiac/profiles.json`)
|
|
684
|
+
|
|
685
|
+
```json
|
|
686
|
+
{
|
|
687
|
+
"q": {
|
|
688
|
+
"env": {
|
|
689
|
+
"XDG_DATA_HOME": "/home/you/.local/share",
|
|
690
|
+
"KIRO_HOME": "/home/you/.kiro"
|
|
691
|
+
},
|
|
692
|
+
"default": true
|
|
693
|
+
},
|
|
694
|
+
"k+": {
|
|
695
|
+
"env": {
|
|
696
|
+
"XDG_DATA_HOME": "/home/you/.local/share/brainiac/k+",
|
|
697
|
+
"KIRO_HOME": "/home/you/.kiro-accounts/k+"
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
Exactly one profile should be marked `"default": true` — it applies when no `[profile:X]` tag is present.
|
|
704
|
+
|
|
705
|
+
### Model selection
|
|
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:
|
|
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
|
|
711
|
+
|
|
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.
|
|
715
|
+
|
|
716
|
+
### Seeding a new KIRO_HOME
|
|
717
|
+
|
|
718
|
+
A fresh `KIRO_HOME` starts empty. brainiac dispatches with `--agent <name>`, so each home needs agent definitions or dispatches will fail. Symlink the shared bits from your real `~/.kiro` and keep only `settings/cli.json` per-account:
|
|
719
|
+
|
|
720
|
+
```bash
|
|
721
|
+
mkdir -p ~/.kiro-accounts/k+/settings
|
|
722
|
+
ln -s ~/.kiro/agents ~/.kiro-accounts/k+/agents
|
|
723
|
+
ln -s ~/.kiro/mcp.json ~/.kiro-accounts/k+/mcp.json
|
|
724
|
+
ln -s ~/.kiro/settings/permissions.yaml ~/.kiro-accounts/k+/settings/permissions.yaml
|
|
725
|
+
KIRO_HOME=~/.kiro-accounts/k+ kiro-cli settings chat.defaultModel claude-opus-5
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
Everything stays in sync with `~/.kiro` via the symlinks — only the model differs.
|
|
729
|
+
|
|
664
730
|
## Cron (Scheduled Tasks)
|
|
665
731
|
|
|
666
732
|
Agents can be dispatched on a schedule — daily standups, weekly summaries, periodic code reviews, whatever you want. Jobs are stored in `~/.brainiac/cron.json` and run in a background thread inside `brainiac server`.
|
data/lib/brainiac/helpers.rb
CHANGED
|
@@ -35,7 +35,7 @@ def load_cli_provider(provider_name)
|
|
|
35
35
|
%w[prompt_flag list_models_command resume_flag resume_args resume_id_flag new_session_id_flag
|
|
36
36
|
session_list_command session_id_field session_directory_field session_updated_field
|
|
37
37
|
session_list_path session_dir output_last_message_flag cwd_flag config_override_flag
|
|
38
|
-
effort_config_key effort_map title_flag heaviness_probe].each do |key|
|
|
38
|
+
effort_config_key effort_map title_flag heaviness_probe settings_model_cmd].each do |key|
|
|
39
39
|
next unless raw[key]
|
|
40
40
|
next if raw[key].respond_to?(:empty?) && raw[key].empty?
|
|
41
41
|
|
|
@@ -774,12 +774,13 @@ end
|
|
|
774
774
|
# Plugins should NOT build their own resume logic; pass `resume: true` and let core handle it.
|
|
775
775
|
def run_agent(prompt, project_config:, chdir: nil, log_name: "agent", model: nil, effort: nil, agent_name: nil, card_number: nil, comment_id: nil,
|
|
776
776
|
source: nil, source_context: {}, skip_column_move: false, cli_provider: nil, resume: false,
|
|
777
|
-
message: nil, channel: nil, context: nil, env: {}, profile: nil)
|
|
777
|
+
message: nil, channel: nil, context: nil, env: {}, profile: nil, explicit_model: nil)
|
|
778
778
|
# Intent gate: if a raw message is provided, check whether the agent should respond.
|
|
779
779
|
return nil if intent_skip?(message, agent_name: agent_name, source: source, channel: channel, context: context)
|
|
780
780
|
|
|
781
781
|
resolved = resolve_project_cli_config(project_config, cli_provider_override: cli_provider, agent_name: agent_name)
|
|
782
782
|
chdir ||= resolved["repo_path"]
|
|
783
|
+
explicit_model = model_explicit?(model, explicit_model)
|
|
783
784
|
model ||= resolved["agent_model"]
|
|
784
785
|
effort ||= resolved["agent_effort"]
|
|
785
786
|
agent_config_name = agent_name&.downcase&.gsub(/[^a-z0-9-]/, "-")
|
|
@@ -810,6 +811,12 @@ def run_agent(prompt, project_config:, chdir: nil, log_name: "agent", model: nil
|
|
|
810
811
|
# wins over agent env; the default profile is only a fallback. See profiles.rb.
|
|
811
812
|
spawn_env = profile_spawn_env(agent_env_for(agent_name), profile).merge(env)
|
|
812
813
|
|
|
814
|
+
# For providers that use settings_model_cmd instead of a --model flag (e.g. kiro-cli,
|
|
815
|
+
# which requires `kiro-cli settings chat.defaultModel <name>`), apply the model to
|
|
816
|
+
# the provider's settings store before spawning. The write runs under spawn_env so it
|
|
817
|
+
# targets the correct account's KIRO_HOME. No-op when model_flag is non-empty.
|
|
818
|
+
apply_settings_model(model, resolved, spawn_env, explicit: explicit_model)
|
|
819
|
+
|
|
813
820
|
log_agent_launch(resolved: resolved, chdir: chdir, log_file: log_file, prompt_file: prompt_file,
|
|
814
821
|
output_file: output_file, cmd: cmd, should_resume: should_resume,
|
|
815
822
|
spawn_env: spawn_env, agent_name: agent_name)
|
|
@@ -829,7 +836,8 @@ def run_agent(prompt, project_config:, chdir: nil, log_name: "agent", model: nil
|
|
|
829
836
|
card_number: card_number, skip_column_move: skip_column_move,
|
|
830
837
|
output_file: output_file, resolved: resolved, minted_session_id: minted_session_id,
|
|
831
838
|
head_before: head_before, status_before: status_before,
|
|
832
|
-
project_key_for_restart: project_key_for_restart, model: model, started_at: started_at
|
|
839
|
+
project_key_for_restart: project_key_for_restart, model: model, started_at: started_at,
|
|
840
|
+
profile: effective_profile_name(profile)
|
|
833
841
|
)
|
|
834
842
|
|
|
835
843
|
LOG.info "#{resolved["agent_cli"]} started (pid: #{pid}, agent: #{agent_config_name || "default"}, " \
|
|
@@ -946,6 +954,56 @@ def append_model_to_cmd(cmd, model, resolved)
|
|
|
946
954
|
cmd.push(resolved["agent_model_flag"], effective_model) if is_known
|
|
947
955
|
end
|
|
948
956
|
|
|
957
|
+
# A model is only "explicit" if the caller actually supplied one (i.e. it came from a
|
|
958
|
+
# tag) AND flagged it explicit. A nil model means we'll fall back to the project default,
|
|
959
|
+
# which must never be treated as explicit — otherwise no-tag dispatches would write it.
|
|
960
|
+
def model_explicit?(model, explicit_flag)
|
|
961
|
+
explicit_flag && !model.nil?
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
# When a provider uses settings_model_cmd instead of a runtime --model flag,
|
|
965
|
+
# run that command synchronously before spawning the agent so the correct model
|
|
966
|
+
# is already persisted in the provider's settings dir (e.g. KIRO_HOME/settings/cli.json).
|
|
967
|
+
#
|
|
968
|
+
# Only fires when:
|
|
969
|
+
# - model_flag is absent or empty (the provider doesn't support runtime model selection)
|
|
970
|
+
# - settings_model_cmd is configured on the provider
|
|
971
|
+
# - a model was resolved (nil model = let the account default stand)
|
|
972
|
+
#
|
|
973
|
+
# The `explicit` flag decides whether "auto" gets written:
|
|
974
|
+
# - explicit == true → the model came from an inline/card tag ([opus], [auto], ...).
|
|
975
|
+
# ALWAYS write it, including "auto" — an explicit [auto] is a deliberate reset that
|
|
976
|
+
# clears a polluted default back to the account's no-preference state.
|
|
977
|
+
# - explicit == false → the model is the project's default fallback (no tag given).
|
|
978
|
+
# Skip "auto" (writing it would clobber each account's per-account default on every
|
|
979
|
+
# no-tag dispatch). A non-auto default still writes, preserving prior behavior.
|
|
980
|
+
# - explicit == nil → caller didn't specify (older/other call sites). Treat as
|
|
981
|
+
# not-explicit for safety — same as the pre-existing "skip auto" behavior.
|
|
982
|
+
#
|
|
983
|
+
# spawn_env is passed in so the command runs under the same env as the agent
|
|
984
|
+
# (picks up KIRO_HOME, XDG_DATA_HOME, etc.) — ensuring the write goes to the right
|
|
985
|
+
# account's settings dir. With per-profile KIRO_HOME the write is isolated to that
|
|
986
|
+
# account, so concurrent dispatches on different profiles never race.
|
|
987
|
+
def apply_settings_model(model, resolved, spawn_env, explicit: nil)
|
|
988
|
+
return unless model
|
|
989
|
+
return if resolved["agent_model_flag"] && !resolved["agent_model_flag"].empty?
|
|
990
|
+
|
|
991
|
+
settings_cmd = resolved["settings_model_cmd"]
|
|
992
|
+
return unless settings_cmd.is_a?(Array) && settings_cmd.length >= 2
|
|
993
|
+
|
|
994
|
+
allowed = resolved["allowed_models"] || {}
|
|
995
|
+
effective_model = allowed.key?(model) ? allowed[model] : model
|
|
996
|
+
is_known = allowed.value?(effective_model) || allowed.key?(effective_model)
|
|
997
|
+
return unless is_known
|
|
998
|
+
# "auto" only writes when it was an explicit tag (used as a reset). A defaulted
|
|
999
|
+
# "auto" is skipped so no-tag dispatches never overwrite an account's default.
|
|
1000
|
+
return if effective_model == "auto" && !explicit
|
|
1001
|
+
|
|
1002
|
+
full_cmd = settings_cmd + [effective_model]
|
|
1003
|
+
LOG.info "Pre-dispatch: setting model via #{full_cmd.join(" ")} (env: #{spawn_env.keys.join(", ")})"
|
|
1004
|
+
system(spawn_env, *full_cmd)
|
|
1005
|
+
end
|
|
1006
|
+
|
|
949
1007
|
# Resume via --session <id> (OpenCode), a bare flag (grok -c), or resume_args (already applied).
|
|
950
1008
|
def append_resume_to_cmd(cmd, resume)
|
|
951
1009
|
case resume
|
|
@@ -1232,23 +1290,39 @@ def check_brainiac_restart(head_before, status_before, chdir, project_key_for_re
|
|
|
1232
1290
|
end
|
|
1233
1291
|
|
|
1234
1292
|
def detect_model(project_config, tags: [], text: "", cli_provider_override: nil, agent_name: nil)
|
|
1293
|
+
detect_model_explicit(project_config, tags: tags, text: text,
|
|
1294
|
+
cli_provider_override: cli_provider_override, agent_name: agent_name).first
|
|
1295
|
+
end
|
|
1296
|
+
|
|
1297
|
+
# Like detect_model, but also reports whether the model came from an EXPLICIT source
|
|
1298
|
+
# (an inline [model] tag or a card tag) versus the project's default fallback.
|
|
1299
|
+
#
|
|
1300
|
+
# Returns [model_string, explicit_bool].
|
|
1301
|
+
# explicit == true → the user asked for this model via a tag ([opus], [auto], etc.)
|
|
1302
|
+
# explicit == false → no tag matched; the value is the project's agent_model default
|
|
1303
|
+
#
|
|
1304
|
+
# This distinction is load-bearing for apply_settings_model: an explicit tag should
|
|
1305
|
+
# always write the provider's default-model setting (even "auto", used as a reset),
|
|
1306
|
+
# while a fell-back-to-default value must NOT write — otherwise every no-tag dispatch
|
|
1307
|
+
# would clobber each account's per-account default back to the project default.
|
|
1308
|
+
def detect_model_explicit(project_config, tags: [], text: "", cli_provider_override: nil, agent_name: nil)
|
|
1235
1309
|
# If no explicit CLI provider override, check if the agent has one configured
|
|
1236
1310
|
effective_cli_provider = cli_provider_override || agent_cli_provider_for(agent_name)
|
|
1237
1311
|
resolved = resolve_project_cli_config(project_config, cli_provider_override: effective_cli_provider, agent_name: agent_name)
|
|
1238
1312
|
allowed_models = resolved["allowed_models"] || {}
|
|
1239
|
-
return resolved["agent_model"] if allowed_models.empty?
|
|
1313
|
+
return [resolved["agent_model"], false] if allowed_models.empty?
|
|
1240
1314
|
|
|
1241
1315
|
if (match = text.match(/\[(\w+)\]/))
|
|
1242
1316
|
key = match[1].downcase
|
|
1243
|
-
return allowed_models[key] if allowed_models.key?(key)
|
|
1317
|
+
return [allowed_models[key], true] if allowed_models.key?(key)
|
|
1244
1318
|
end
|
|
1245
1319
|
|
|
1246
1320
|
tags.each do |tag|
|
|
1247
1321
|
key = (tag.is_a?(Hash) ? tag["name"] : tag).to_s.downcase
|
|
1248
|
-
return allowed_models[key] if allowed_models.key?(key)
|
|
1322
|
+
return [allowed_models[key], true] if allowed_models.key?(key)
|
|
1249
1323
|
end
|
|
1250
1324
|
|
|
1251
|
-
resolved["agent_model"]
|
|
1325
|
+
[resolved["agent_model"], false]
|
|
1252
1326
|
end
|
|
1253
1327
|
|
|
1254
1328
|
# Detect effort level from inline tags [effort:high] or card tags (effort-high).
|
|
@@ -1274,6 +1348,40 @@ def detect_effort(project_config, tags: [], text: "", cli_provider_override: nil
|
|
|
1274
1348
|
resolved["agent_effort"]
|
|
1275
1349
|
end
|
|
1276
1350
|
|
|
1351
|
+
# Detect a profile ([profile:X]/[p:X]) from inline text OR card tags.
|
|
1352
|
+
#
|
|
1353
|
+
# A profile is a named env bundle (e.g. an alternate kiro-cli account via
|
|
1354
|
+
# XDG_DATA_HOME/KIRO_HOME) — see profiles.rb. This mirrors detect_model/detect_effort:
|
|
1355
|
+
# it checks inline text first, then falls back to card tags. Without the tag path,
|
|
1356
|
+
# adding a `p:k+` chip to a Fizzy card was silently ignored (only inline title text
|
|
1357
|
+
# was honored), so dispatches ran on the default profile.
|
|
1358
|
+
#
|
|
1359
|
+
# Card tags can be named either "profile:X"/"p:X" (colon form, matches inline syntax)
|
|
1360
|
+
# or a bare "X" that happens to be a known profile name.
|
|
1361
|
+
# Returns the lowercased profile name or nil.
|
|
1362
|
+
def detect_profile(text: "", tags: [])
|
|
1363
|
+
# Inline tag: [profile:k+] / [p:k+] — works in any channel
|
|
1364
|
+
inline = parse_inline_tags(text.to_s)[:profile]
|
|
1365
|
+
return inline if inline
|
|
1366
|
+
|
|
1367
|
+
known = defined?(PROFILES) ? PROFILES : {}
|
|
1368
|
+
|
|
1369
|
+
tags.each do |tag|
|
|
1370
|
+
name = (tag.is_a?(Hash) ? tag["name"] : tag).to_s.strip.downcase
|
|
1371
|
+
next if name.empty?
|
|
1372
|
+
|
|
1373
|
+
# "profile:k+" or "p:k+" colon form
|
|
1374
|
+
if (m = name.match(/\A(?:profile|p):(.+)\z/))
|
|
1375
|
+
return m[1].strip
|
|
1376
|
+
end
|
|
1377
|
+
|
|
1378
|
+
# bare tag that matches a configured profile name (e.g. a "k+" chip)
|
|
1379
|
+
return name if known.key?(name)
|
|
1380
|
+
end
|
|
1381
|
+
|
|
1382
|
+
nil
|
|
1383
|
+
end
|
|
1384
|
+
|
|
1277
1385
|
# If a level isn't in allowed_efforts, return the closest lower level.
|
|
1278
1386
|
def resolve_effort_level(level, allowed)
|
|
1279
1387
|
all_levels = %w[low medium high xhigh max]
|
data/lib/brainiac/profiles.rb
CHANGED
|
@@ -8,17 +8,40 @@
|
|
|
8
8
|
# dispatch runs against via an inline tag ([profile:X] / [p:X]) — same CLI, same
|
|
9
9
|
# agent, different credentials.
|
|
10
10
|
#
|
|
11
|
+
# kiro-cli splits its state across two independent env vars — a profile should set both:
|
|
12
|
+
#
|
|
13
|
+
# XDG_DATA_HOME → auth store (kiro-cli/data.sqlite3): tokens + which account you're
|
|
14
|
+
# logged into. This selects the account.
|
|
15
|
+
# KIRO_HOME → settings dir (~/.kiro, incl. settings/cli.json): chat.defaultModel,
|
|
16
|
+
# agents, MCP, permissions, steering. This is where chat.defaultModel
|
|
17
|
+
# lives — giving each profile its own KIRO_HOME means each account has
|
|
18
|
+
# its own model setting.
|
|
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.
|
|
24
|
+
#
|
|
25
|
+
# NOTE: a fresh KIRO_HOME starts empty. brainiac dispatches with --agent <name>, so each
|
|
26
|
+
# KIRO_HOME needs agent definitions (and any MCP/permissions you rely on) or dispatches
|
|
27
|
+
# break. Seed a new home by symlinking the shared bits from ~/.kiro and keeping only
|
|
28
|
+
# settings/cli.json per-account:
|
|
29
|
+
#
|
|
30
|
+
# mkdir -p ~/.kiro-accounts/k+/settings
|
|
31
|
+
# ln -s ~/.kiro/agents ~/.kiro-accounts/k+/agents
|
|
32
|
+
# ln -s ~/.kiro/mcp.json ~/.kiro-accounts/k+/mcp.json
|
|
33
|
+
# ln -s ~/.kiro/settings/permissions.yaml ~/.kiro-accounts/k+/settings/permissions.yaml
|
|
34
|
+
# KIRO_HOME=~/.kiro-accounts/k+ kiro-cli settings chat.defaultModel claude-opus-5
|
|
35
|
+
#
|
|
11
36
|
# Config lives at ~/.brainiac/profiles.json:
|
|
12
37
|
#
|
|
13
38
|
# {
|
|
14
|
-
# "q": { "env": { "XDG_DATA_HOME": "/home/andy/.local/share"
|
|
15
|
-
#
|
|
39
|
+
# "q": { "env": { "XDG_DATA_HOME": "/home/andy/.local/share",
|
|
40
|
+
# "KIRO_HOME": "/home/andy/.kiro" }, "default": true },
|
|
41
|
+
# "k+": { "env": { "XDG_DATA_HOME": "/home/andy/.local/share/brainiac/k+",
|
|
42
|
+
# "KIRO_HOME": "/home/andy/.kiro-accounts/k+" } }
|
|
16
43
|
# }
|
|
17
44
|
#
|
|
18
|
-
# A profile may also optionally pin a model and/or cli_provider:
|
|
19
|
-
#
|
|
20
|
-
# "k+": { "env": {...}, "model": "opus", "cli_provider": "kiro-pro" }
|
|
21
|
-
#
|
|
22
45
|
# Exactly one profile may be marked "default": true — it applies when no [profile:X]
|
|
23
46
|
# tag is present. Because it's only a fallback, an agent's own env (from agents.json)
|
|
24
47
|
# takes precedence over the default profile. An *explicitly requested* profile, by
|
|
@@ -70,6 +93,23 @@ def default_profile
|
|
|
70
93
|
PROFILES.find { |_name, entry| entry.is_a?(Hash) && entry["default"] }
|
|
71
94
|
end
|
|
72
95
|
|
|
96
|
+
# Resolve the *effective* profile name that a dispatch actually ran under, mirroring
|
|
97
|
+
# profile_spawn_env's fallback logic. This is what should be recorded in durable
|
|
98
|
+
# session history and shown in the monitor so you can tell whether a session used the
|
|
99
|
+
# default account (e.g. "q") or an explicitly-requested one (e.g. "k+").
|
|
100
|
+
#
|
|
101
|
+
# - An explicitly-requested, known profile → that name (normalized lowercase).
|
|
102
|
+
# - An explicitly-requested but UNKNOWN profile → nil (it was ignored at spawn, so it
|
|
103
|
+
# didn't actually run under any profile — matches profile_spawn_env dropping it).
|
|
104
|
+
# - No profile requested → the default profile's name, or nil if there's no default.
|
|
105
|
+
def effective_profile_name(profile_name)
|
|
106
|
+
if profile_name
|
|
107
|
+
profile_entry(profile_name) ? profile_name.to_s.downcase : nil
|
|
108
|
+
else
|
|
109
|
+
default_profile&.first
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
73
113
|
# Resolve the env hash contributed by a profile.
|
|
74
114
|
#
|
|
75
115
|
# When profile_name is given and matches a profile, returns that profile's env.
|
|
@@ -89,6 +89,7 @@ def archive_session_history(ctx:, exit_status:, signaled:)
|
|
|
89
89
|
"log_file" => ctx[:log_file],
|
|
90
90
|
"cli" => ctx[:agent_cli],
|
|
91
91
|
"model" => ctx[:model],
|
|
92
|
+
"profile" => ctx[:profile],
|
|
92
93
|
"started_at" => started_at&.utc&.iso8601,
|
|
93
94
|
"finished_at" => finished_at.utc.iso8601,
|
|
94
95
|
"duration_seconds" => started_at ? (finished_at - started_at).to_i : nil,
|
data/lib/brainiac/version.rb
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
"binary": "kiro-cli",
|
|
3
3
|
"default_args": "chat --trust-all-tools --no-interactive",
|
|
4
4
|
"agent_flag": "--agent",
|
|
5
|
-
"model_flag": "
|
|
5
|
+
"model_flag": "",
|
|
6
|
+
"settings_model_cmd": ["kiro-cli", "settings", "chat.defaultModel"],
|
|
6
7
|
"effort_flag": "--effort",
|
|
7
8
|
"prompt_mode": "stdin",
|
|
8
9
|
"resume_flag": "--resume",
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"q": {
|
|
3
3
|
"env": {
|
|
4
|
-
"XDG_DATA_HOME": "/home/you/.local/share"
|
|
5
|
-
|
|
4
|
+
"XDG_DATA_HOME": "/home/you/.local/share",
|
|
5
|
+
"KIRO_HOME": "/home/you/.kiro"
|
|
6
|
+
},
|
|
7
|
+
"default": true
|
|
6
8
|
},
|
|
7
9
|
"k+": {
|
|
8
10
|
"env": {
|
|
9
|
-
"XDG_DATA_HOME": "/home/you/.brainiac/
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
"XDG_DATA_HOME": "/home/you/.local/share/brainiac/k+",
|
|
12
|
+
"KIRO_HOME": "/home/you/.kiro-accounts/k+"
|
|
13
|
+
}
|
|
12
14
|
}
|
|
13
15
|
}
|