brainiac 0.4.2 → 0.4.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: ba8a88daeee5e47d89614674cc1855e4a3fb50ca9b7cb8b5f063fa40ef544aeb
4
- data.tar.gz: e0dcd3bc1916e066f1da0b0827cac3952978e44451df5262308eb9003400f268
3
+ metadata.gz: 16f7d64457f92c95cf53466c9d91b14e5011ee8bebe111f0c10eeaba39d26813
4
+ data.tar.gz: 5372f10fed375a774eb62eda8b8c22c80df416b9358b13fc2e1348af966b5bf6
5
5
  SHA512:
6
- metadata.gz: e4a84e1cc4f055ad794ebde722e653af89376ab2f600cd7c35dee4cb5731f99d612b699e6aaf0b6f8e51857ae354ffd3b9627c234d0538c56e3400231b48f0a6
7
- data.tar.gz: bb1dd4fe2cca8a4c84f73a042b0b7dc8d754d4718281dd849d6d3b966ee70c4a3bacddcabd21b6bfc111bd37c5c70a541142fd0443914f24181b75e3966d35e8
6
+ metadata.gz: 7f32967505e0de3e6feb98f3b53707fb64272ce2a02deebe0eb60b74fade6896aa1522359db145dc10b94a08ed5d5e79689ebd70f9f26f8b9853b4a57e97e792
7
+ data.tar.gz: 7dc2927f465ed0998f1fc264946375d1f85ce250c4e3bb05fd048452c3002f9a7b48b25f1033e761de81595b8ab71d3b05b8f7dde6aa32d01e7e04a24a8d4c09
data/README.md CHANGED
@@ -90,7 +90,7 @@ Any agent can be tagged on any card. If Merlin is working card #42 and someone c
90
90
 
91
91
  #### Display Name Accuracy
92
92
 
93
- Agents need to spell @mentions exactly as they appear in Fizzy (e.g. `@Robin` not `@robin`). The agent registry's `fizzy_name` field handles this — every prompt includes an agent roster with the correct spelling:
93
+ Agents need to spell @mentions exactly (e.g. `@Robin` not `@robin`). The agent registry's `display_name` field handles this — every prompt includes an agent roster with the correct spelling:
94
94
 
95
95
  ```
96
96
  ## Agent Roster
@@ -100,7 +100,7 @@ When @mentioning other agents in Fizzy comments, use the EXACT spelling below.
100
100
  - @Merlin
101
101
  ```
102
102
 
103
- Detection is case-insensitive (inbound `@robin` still matches Robin), but outbound mentions use the exact `fizzy_name` from the registry.
103
+ Detection is case-insensitive (inbound `@robin` still matches Robin), but outbound mentions use the exact `display_name` from the registry.
104
104
 
105
105
  #### Agent-to-Agent Loop Prevention
106
106
 
@@ -162,25 +162,73 @@ This installs the `brainiac` binary to your gem bin path (just like `rails`, `rs
162
162
 
163
163
  ### Quick Start (New Machine)
164
164
 
165
- After installing the gem:
165
+ From zero to an agent you can @mention in Discord:
166
166
 
167
167
  ```bash
168
- brainiac setup
168
+ gem install brainiac
169
+ brainiac setup # ~/.brainiac/ dirs, example configs, CLI providers
170
+ brainiac install discord # plus any of: fizzy, github, zoho, basecamp
171
+ cd ~/Code/my-app && brainiac register
172
+ brainiac server --daemon
173
+
174
+ brainiac agent create Quill --local --avatar ~/quill.png --icon 🪶
169
175
  ```
170
176
 
171
- This creates the `~/.brainiac/` directory structure, detects existing agents in `~/.brainiac/agents.json`, and sets your default agent. If no agents exist, it offers to create your first one.
177
+ `agent create` walks you through the rest: it opens `$EDITOR` for the agent's persona, asks for a role, then lets each installed plugin set up its part (for Discord that's one pasted bot token). The bot comes online without a restart. See [Adding an Agent](#adding-an-agent) for what each step does. (If you gave `brainiac setup` a first agent name, finish that agent with `brainiac agent setup <key>` instead.)
178
+
179
+ Make sure the [prerequisites](#prerequisites) are installed first, at least Ruby, an agent CLI, and qmd.
180
+
181
+ Each plugin also has its own setup/config commands (e.g. `brainiac discord default <project>`, `brainiac github setup`) and README.
182
+
183
+ ### Adding an Agent
172
184
 
173
- Then install the plugins you need:
185
+ One command creates everything an agent needs:
174
186
 
175
187
  ```bash
176
- brainiac install discord # Discord bots
177
- brainiac install fizzy # Fizzy card management
178
- brainiac install github # GitHub webhooks
179
- brainiac install zoho # Zoho Mail
180
- brainiac restart # Restart to activate
188
+ brainiac agent create "Judge Dredd" --local \
189
+ --avatar ~/dredd.png --icon ⚖️ \
190
+ --description-file ~/dredd-style.md \
191
+ --role-file ~/law-specialist.md
192
+ ```
193
+
194
+ **Names.** Type the name the way people should see it, quoted if it has spaces. `Judge Dredd` is the display name (Discord username, Fizzy/Basecamp name, @mentions). `judge-dredd` is the key (agents.json, `persona/judge-dredd/`, `memory/judge-dredd/`, `brainiac agent setup judge-dredd`).
195
+
196
+ **What core writes**
197
+
198
+ | Input | Flag | Ends up in | If you leave it out |
199
+ |-------|------|------------|---------------------|
200
+ | Persona (how they talk) | `--description-file f`, `--description "text"`, `--description @f`, `--description -` | `brain/persona/<key>/style.md` | `$EDITOR` opens |
201
+ | Role (what they do) | `--role-file f.md` or `--role <existing>` (repeatable) | `roles/<name>.md`, `role` in agents.json | Pick existing roles by number, `n` for a new one in `$EDITOR`, blank to skip |
202
+ | Avatar | `--avatar pic.png` | `brain/persona/<key>/avatar.png` | Plugins skip the picture |
203
+ | Status bar icon | `--icon 🪶` | `waybar.json` (Omarchy bar, waybar, xbar) | Asked for an emoji |
204
+ | Email | `--email a@b.com` | agents.json `email` | `agent_email_template` in `brainiac.json`, e.g. `"agent+{key}@example.com"` |
205
+ | Local dispatch | `--local` | agents.json `local: true` | Agent is known but won't pick up work on this machine |
206
+
207
+ A `--role-file` keeps its file name: `law-specialist.md` becomes the `law-specialist` role, so the same file can be passed for several agents. Pass `--role <new-name>` to name it something else. If your persona or role file already has YAML frontmatter it's kept as-is.
208
+
209
+ **What the plugins do** (each installed plugin implements `on_agent_created`)
210
+
211
+ | Plugin | Automated | You do |
212
+ |--------|-----------|--------|
213
+ | discord | Bot username, avatar, app icon and description, Message Content intent, token saved to agents.json, `user_mappings`, invite link opened | Create the application in the developer portal (opened for you) and paste the bot token |
214
+ | fizzy | Name, avatar, `authorized_users`, `FIZZY_TOKEN` | Open the printed join link in a **private window**, sign up as the agent's email, create an access token and paste it |
215
+ | basecamp | Finds the person by name, or invites them by email after a confirm prompt; adds them to every mapped project, `bot_accounts` entry | Nothing |
216
+
217
+ Before each plugin runs you're asked `Set up basecamp for Judge Dredd? [Y/n]`. Answer up front with `--skip basecamp` or `--only discord,fizzy`; skips are saved on the agent. `--no-browser` (or `BRAINIAC_NO_BROWSER=1`) prints links instead of opening them.
218
+
219
+ **Finishing or changing an agent later**
220
+
221
+ ```bash
222
+ brainiac agent setup judge-dredd # re-run every plugin; finished steps are skipped
223
+ brainiac agent setup judge-dredd --only fizzy # just one plugin (also un-skips it)
224
+ brainiac agent setup judge-dredd --icon ⚖️ --only none # change the icon, no plugin steps
225
+ brainiac agent setup judge-dredd --role-file ~/r.md # add a role
226
+ brainiac agent remove judge-dredd # remove from the registry and bars
181
227
  ```
182
228
 
183
- Each plugin has its own setup command (e.g. `brainiac discord setup`, `brainiac github setup`) that creates the necessary config files.
229
+ `agent setup` also asks for a role or icon if the agent doesn't have one yet. With the server running, a new or changed token is picked up without a restart.
230
+
231
+ Writing a plugin that should take part in this? See "Agent Onboarding (`on_agent_created`)" in [skills/brainiac-plugins/SKILL.md](skills/brainiac-plugins/SKILL.md). `brainiac plugin new` scaffolds the hook for you.
184
232
 
185
233
  ### Prerequisites
186
234
 
@@ -203,13 +251,16 @@ After setup, `~/.brainiac/` looks like:
203
251
  ```
204
252
  ~/.brainiac/
205
253
  ├── brainiac.json # Core config (default_agent, default_cli_provider)
206
- ├── agents.json # Agent registry (tokens, display names, local flag)
254
+ ├── agents.json # Agent registry (display names, tokens, role, local flag)
207
255
  ├── projects.json # Registered projects
208
256
  ├── users.json # Cross-platform user identity registry
209
257
  ├── plugins.json # Installed plugins
258
+ ├── waybar.json # Status bar icons/colors per agent (Omarchy bar, waybar, xbar)
259
+ ├── roles/ # Role definitions (<name>.md), assigned via agents.json "role"
260
+ ├── cli-providers/ # One JSON per agent CLI (kiro, claude, codex, ...)
210
261
  ├── brain/
211
262
  │ ├── knowledge/ # Shared technical knowledge (all agents)
212
- │ ├── persona/ # Per-agent personality files
263
+ │ ├── persona/ # Per-agent personality files (style.md, avatar)
213
264
  │ └── memory/ # Per-agent session memory
214
265
  ├── handlers/ # Legacy custom webhook handlers
215
266
  ├── plans/ # Planning mode output
@@ -222,12 +273,12 @@ Plugins add their own config files when installed (e.g. `discord.json`, `fizzy.j
222
273
 
223
274
  #### 1. Agent Registry (`~/.brainiac/agents.json`)
224
275
 
225
- Maps agents to their identity and environment. Every agent that should dispatch on this machine needs an entry here with `"local": true`:
276
+ Maps agents to their identity and environment. Every agent that should dispatch on this machine needs an entry here with `"local": true`. `brainiac agent create` writes these entries for you (see [Adding an Agent](#adding-an-agent)); this is what one looks like:
226
277
 
227
278
  ```json
228
279
  {
229
280
  "sherlock": {
230
- "fizzy_name": "Sherlock",
281
+ "display_name": "Sherlock",
231
282
  "local": true,
232
283
  "env": {
233
284
  "FIZZY_TOKEN": "fizzy_abc...",
@@ -389,6 +440,8 @@ Both machines receive the same webhooks. The agent registry (`~/.brainiac/agents
389
440
 
390
441
  ### Step 1: Agent Registry
391
442
 
443
+ `brainiac agent create` writes the entry, persona, role and plugin tokens for you (see [Adding an Agent](#adding-an-agent)). The rest of this section explains what it produces, in case you want to edit it by hand.
444
+
392
445
  The agent registry at `~/.brainiac/agents.json` is the authority for agent identity. This serves four purposes:
393
446
 
394
447
  1. **Per-agent environment variables** — any env var can be set per-agent via the `env` hash (e.g. `FIZZY_TOKEN`, `DISCORD_BOT_TOKEN`, custom vars)
@@ -399,7 +452,7 @@ The agent registry at `~/.brainiac/agents.json` is the authority for agent ident
399
452
  ```json
400
453
  {
401
454
  "sherlock": {
402
- "fizzy_name": "Sherlock",
455
+ "display_name": "Sherlock",
403
456
  "local": true,
404
457
  "env": {
405
458
  "FIZZY_TOKEN": "fizzy_abc...",
@@ -407,19 +460,19 @@ The agent registry at `~/.brainiac/agents.json` is the authority for agent ident
407
460
  }
408
461
  },
409
462
  "robin": {
410
- "fizzy_name": "Robin",
463
+ "display_name": "Robin",
411
464
  "env": {
412
465
  "FIZZY_TOKEN": "fizzy_xyz...",
413
466
  "DISCORD_BOT_TOKEN": "Bot_xyz..."
414
467
  }
415
468
  },
416
469
  "merlin": {
417
- "fizzy_name": "Merlin"
470
+ "display_name": "Merlin"
418
471
  }
419
472
  }
420
473
  ```
421
474
 
422
- Keys are lowercase lookup keys (normalized: non-alphanumeric chars become hyphens). `fizzy_name` is the exact Fizzy account display name. The `env` hash is injected into the spawned agent process — every key/value pair becomes an environment variable.
475
+ Keys are lowercase lookup keys (normalized: non-alphanumeric chars become hyphens). `display_name` is the exact name used for @mentions and on every platform (Discord, Fizzy, Basecamp). The `env` hash is injected into the spawned agent process — every key/value pair becomes an environment variable.
423
476
 
424
477
  The `local` flag controls which agents pick up card assignments on this machine. Agents without `"local": true` are still known for mention detection, display names, tokens, and cross-agent interactions — they just won't pick up card assignments. The default agent (from `AI_AGENT_NAME` or `brainiac.json`) is always considered local.
425
478
 
@@ -427,13 +480,13 @@ Agents without an `env` block (like Merlin above on a Linux box) still appear in
427
480
 
428
481
  The registry reloads on every webhook and via `POST /api/reload`.
429
482
 
430
- ### Step 3: Seed Agent Persona and Knowledge
483
+ ### Step 2: Seed Agent Persona and Knowledge
431
484
 
432
485
  Agents get their personality from persona files and their technical context from knowledge files. These live in the brain directory and are indexed by qmd for semantic search.
433
486
 
434
487
  #### Persona
435
488
 
436
- Each agent needs at least one persona file. Create it directly in the brain:
489
+ Each agent needs at least one persona file. `brainiac agent create` writes `style.md` from `--description-file` or `$EDITOR`. To create one by hand:
437
490
 
438
491
  ```bash
439
492
  # Create persona file for your agent
@@ -471,7 +524,7 @@ qmd update
471
524
 
472
525
  Agents also update knowledge themselves during sessions (when they learn something significant), but seeding it with your project's conventions and tool docs gives them a head start.
473
526
 
474
- ### Step 4: Initialize Each Agent's Brain
527
+ ### Step 3: Initialize Each Agent's Brain
475
528
 
476
529
  ```bash
477
530
  brainiac brain init # Default agent (from brainiac.json or AI_AGENT_NAME)
@@ -479,7 +532,7 @@ brainiac brain init SecurityBot # Additional agent
479
532
  brainiac brain list # Verify all agents
480
533
  ```
481
534
 
482
- This creates the directory structure, sets up qmd collections, and indexes everything. Run this after you've placed your persona and knowledge files.
535
+ This creates the directory structure, sets up qmd collections, and indexes everything. Run this after you've placed your persona and knowledge files (`agent create --local` already does it for the new agent).
483
536
 
484
537
  ### How Dispatch Works
485
538
 
@@ -559,6 +612,8 @@ React with ❌ to any message that triggered an agent to immediately terminate t
559
612
 
560
613
  ### Setup
561
614
 
615
+ **For a new agent, use `brainiac agent create` (or `brainiac agent setup <key> --only discord` for an existing one).** It covers steps 1-3 below: it opens the developer portal, takes the pasted token, sets the bot's name, avatar and Message Content intent, and opens the invite link. The manual steps are here for reference.
616
+
562
617
  #### Step 1: Create Discord Applications
563
618
 
564
619
  Create one Discord application per agent at https://discord.com/developers/applications:
@@ -588,7 +643,7 @@ brainiac discord token robin "BOT_TOKEN_FOR_ROBIN"
588
643
  This adds `DISCORD_BOT_TOKEN` to the agent's `env` hash in `~/.brainiac/agents.json`. You can verify with:
589
644
 
590
645
  ```bash
591
- brainiac discord agents
646
+ brainiac agent <name> show
592
647
  ```
593
648
 
594
649
  #### Step 3: Invite Bots to Your Server
@@ -597,18 +652,22 @@ Go to the "OAuth2" tab for each application:
597
652
 
598
653
  1. Under "OAuth2 URL Generator", check the **bot** scope
599
654
  2. Under "Bot Permissions", select:
655
+ - View Channels
600
656
  - Send Messages
657
+ - Embed Links
658
+ - Attach Files
659
+ - Read Message History
660
+ - Add Reactions
601
661
  - Create Public Threads
602
662
  - Send Messages in Threads
603
- - Add Reactions
604
- - Read Message History
663
+ - Manage Threads
605
664
  3. Copy the generated URL and open it in your browser
606
665
  4. Select your server and authorize
607
666
 
608
- The permission integer for these permissions is `326417591296`. You can also construct the invite URL manually:
667
+ The permission integer for these permissions is `326417632320`. You can also construct the invite URL manually:
609
668
 
610
669
  ```
611
- https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&scope=bot&permissions=326417591296
670
+ https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&scope=bot&permissions=326417632320
612
671
  ```
613
672
 
614
673
  Replace `YOUR_APP_ID` with the Application ID from the "General Information" tab. Repeat for each agent bot.
@@ -709,7 +768,7 @@ Leave `authorized_role_ids` and `authorized_user_ids` empty to allow everyone in
709
768
 
710
769
  ```bash
711
770
  brainiac discord token <agent> <token> # Set Discord bot token for an agent
712
- brainiac discord agents # List agents with Discord bot tokens
771
+ brainiac discord owner <user-id> # Set the owner's Discord user ID
713
772
  brainiac discord default <project> # Set default project for all channels
714
773
  brainiac discord map <channel-id> <project> # Override for a specific channel
715
774
  brainiac discord config # Show current Discord config
@@ -932,6 +991,9 @@ Configure how agents appear in the status bar via `~/.brainiac/waybar.json`:
932
991
  }
933
992
  ```
934
993
 
994
+ `brainiac agent create` asks for each new agent's icon (or pass `--icon ⚖️`).
995
+ Change one later with `brainiac agent setup <name> --icon <emoji>`.
996
+
935
997
  ### macOS (xbar)
936
998
 
937
999
  Requires [xbar](https://xbarapp.com) (free, formerly BitBar).
@@ -1121,8 +1183,8 @@ Brainiac supports a plugin system that lets you extend functionality via Ruby ge
1121
1183
  ### Installing Plugins
1122
1184
 
1123
1185
  ```bash
1124
- brainiac install whatsapp # Installs brainiac-whatsapp gem + registers it
1125
- brainiac install slack # Installs brainiac-slack gem + registers it
1186
+ brainiac install discord # Installs brainiac-discord gem + registers it
1187
+ brainiac install fizzy # Installs brainiac-fizzy gem + registers it
1126
1188
  brainiac restart # Restart server to load new plugins
1127
1189
  ```
1128
1190
 
@@ -1131,7 +1193,7 @@ This runs `gem install brainiac-<name>`, adds the plugin to `~/.brainiac/plugins
1131
1193
  You can also install a specific version:
1132
1194
 
1133
1195
  ```bash
1134
- brainiac install whatsapp --version 0.2.0
1196
+ brainiac install discord --version 0.0.25
1135
1197
  ```
1136
1198
 
1137
1199
  ### Managing Plugins
@@ -1144,8 +1206,8 @@ brainiac uninstall <name> # Remove plugin from config (gem stays installed)
1144
1206
  To fully remove the gem after uninstalling:
1145
1207
 
1146
1208
  ```bash
1147
- brainiac uninstall whatsapp
1148
- gem uninstall brainiac-whatsapp
1209
+ brainiac uninstall discord
1210
+ gem uninstall brainiac-discord
1149
1211
  ```
1150
1212
 
1151
1213
  ### Configuration
@@ -1155,7 +1217,7 @@ Installed plugins are tracked in `~/.brainiac/plugins.json`:
1155
1217
  ```json
1156
1218
  {
1157
1219
  "plugins": [
1158
- { "name": "whatsapp", "gem": "brainiac-whatsapp", "installed_at": "2026-06-30T14:00:00-04:00" }
1220
+ { "name": "discord", "gem": "brainiac-discord", "installed_at": "2026-06-30T14:00:00-04:00" }
1159
1221
  ]
1160
1222
  }
1161
1223
  ```
@@ -1164,7 +1226,7 @@ At server startup, `load_plugins!` iterates this list, requires each gem, and ca
1164
1226
 
1165
1227
  ### Creating a Plugin
1166
1228
 
1167
- Any Ruby gem named `brainiac-<name>` can be a Brainiac plugin. The gem must:
1229
+ Any Ruby gem named `brainiac-<name>` can be a Brainiac plugin. `brainiac plugin new <name>` generates a working skeleton (gemspec, module, CLI with an `on_agent_created` stub, tests, and a skill doc); the full contract is in [skills/brainiac-plugins/SKILL.md](skills/brainiac-plugins/SKILL.md). The gem must:
1168
1230
 
1169
1231
  1. **Define a module** at `Brainiac::Plugins::<Name>`
1170
1232
  2. **Implement `.register(app)`** — receives the Sinatra app instance for defining routes and starting threads
@@ -1219,7 +1281,8 @@ The `Brainiac::Plugins` namespace is defined in `lib/brainiac.rb`. Plugin module
1219
1281
  | fizzy | `brainiac-fizzy` | Fizzy card management — assignment, comments, @mentions, cross-agent reviews, duplicate detection, planning mode |
1220
1282
  | github | `brainiac-github` | GitHub webhooks — PR reviews, PR comments, PR merges, CI workflow notifications, deploy tracking |
1221
1283
  | zoho | `brainiac-zoho` | Zoho Mail webhooks — rule-based email notifications, AI triage, OAuth for content fetching |
1222
- | whatsapp | `brainiac-whatsapp` | WhatsApp Business API integration |
1284
+ | basecamp | `brainiac-basecamp` | Basecamp epic orchestration — card tables, review gates, epic branches |
1285
+ | featureparity | `brainiac-featureparity` | FeatureParity integration for Brainiac agents |
1223
1286
 
1224
1287
  ### Backward Compatibility
1225
1288
 
@@ -1272,10 +1335,31 @@ brainiac brain search --persona <query> # Search agent persona
1272
1335
  brainiac brain list # List everything
1273
1336
  ```
1274
1337
 
1338
+ ### Agents
1339
+
1340
+ ```bash
1341
+ brainiac agent create <name> [flags] # New agent + persona + role + plugin setup (see Adding an Agent)
1342
+ brainiac agent setup <name> [flags] # Finish/re-run plugin setup; change icon, avatar, email, role
1343
+ brainiac agent remove <name> # Remove from the registry
1344
+ brainiac agent list # List agents
1345
+ brainiac agent <name> show # Show config (tokens redacted)
1346
+ brainiac agent <name> env <KEY> <VALUE> # Set an env var
1347
+ ```
1348
+
1349
+ ### Roles
1350
+
1351
+ ```bash
1352
+ brainiac role list # Roles and who has them
1353
+ brainiac role show <name> # Print a role
1354
+ brainiac role create <name> [--from f] [--assign agent] # New role ($EDITOR on a template)
1355
+ brainiac role assign <agent> <role> # Add a role to an agent
1356
+ brainiac role unassign <agent> <role> # Remove it
1357
+ ```
1358
+
1275
1359
  ### Plugins
1276
1360
 
1277
1361
  ```bash
1278
- brainiac install <name> # Install a plugin gem (e.g., whatsapp)
1362
+ brainiac install <name> # Install a plugin gem (e.g., discord)
1279
1363
  brainiac install <name> --version 0.2.0 # Install specific version
1280
1364
  brainiac uninstall <name> # Remove plugin from config
1281
1365
  brainiac plugins # List installed plugins
@@ -1436,7 +1520,7 @@ ls brainiac receiver.rb lib/brainiac/*.rb lib/brainiac/handlers/*.rb | entr -r b
1436
1520
 
1437
1521
  **Agent commenting as wrong user:** Check `~/.brainiac/agents.json` has the correct `FIZZY_TOKEN` in the agent's `env` hash. The env is injected into the spawned agent process — verify with `curl http://localhost:4567/api/agents` to see the roster.
1438
1522
 
1439
- **Agent @mention not linking in Fizzy:** The `fizzy_name` in `agents.json` must match the exact Fizzy account name (case-sensitive). Check `curl http://localhost:4567/api/agents` to see what the roster looks like.
1523
+ **Agent @mention not linking in Fizzy:** The `display_name` in `agents.json` must match the exact Fizzy account name (case-sensitive). Check `curl http://localhost:4567/api/agents` to see what the roster looks like.
1440
1524
 
1441
1525
  **Agent-to-agent loop:** Shouldn't happen — dispatch depth is capped at 10 hops by default. Check `curl http://localhost:4567/api/dispatch-depth` to see current state. Adjust `AGENT_DISPATCH_MAX_DEPTH` in `lib/brainiac/sessions.rb` if needed.
1442
1526
 
data/bin/brainiac CHANGED
@@ -374,6 +374,25 @@ def write_role_file(role_name, text, agent_name = nil)
374
374
  file
375
375
  end
376
376
 
377
+ # Role for --role-file: named after the first --role that doesn't exist yet,
378
+ # else the file's name (blog-writer.md → blog-writer), so one file can be
379
+ # reused across agents. Reusing an existing role is fine when the file is
380
+ # that role or has the same content; a different role by that name is an error.
381
+ # Returns [role_name, needs_write].
382
+ def role_from_file(role_file, requested_roles)
383
+ name = requested_roles.find { |r| !available_roles.include?(r) } || role_name_arg(File.basename(role_file, ".*"))
384
+ existing = File.join(BRAINIAC_DIR, "roles", "#{name}.md")
385
+ return [name, true] unless File.exist?(existing)
386
+
387
+ same = File.expand_path(existing) == role_file || File.read(existing).strip == File.read(role_file).strip ||
388
+ File.read(existing).include?(File.read(role_file).strip)
389
+ return [name, false] if same
390
+
391
+ puts "❌ A different role named '#{name}' already exists (#{existing})."
392
+ puts " Use --role #{name} to assign it as-is, or --role <new-name> to name the role from #{role_file}."
393
+ exit 1
394
+ end
395
+
377
396
  # Add roles to a registry entry, keeping the string-or-array shape.
378
397
  def assign_roles(entry, new_roles)
379
398
  roles = (Array(entry["role"]) + new_roles).uniq
@@ -418,6 +437,48 @@ def prompt_for_agent_role(agent_key, display_name)
418
437
  picked & existing
419
438
  end
420
439
 
440
+ # Status bar icons (Omarchy bar, waybar, xbar) live in ~/.brainiac/waybar.json,
441
+ # keyed by display name (matched case-insensitively). Unlisted agents show ❓.
442
+ def bar_config_file
443
+ File.join(BRAINIAC_DIR, "waybar.json")
444
+ end
445
+
446
+ def read_bar_config
447
+ File.exist?(bar_config_file) ? JSON.parse(File.read(bar_config_file, encoding: "utf-8")) : {}
448
+ rescue JSON::ParserError
449
+ {}
450
+ end
451
+
452
+ def bar_icon(display_name)
453
+ (read_bar_config["agents"] || []).find { |a| a["name"].to_s.casecmp?(display_name) }&.dig("emoji")
454
+ end
455
+
456
+ # Add or update the agent's bar icon, keeping its color and the other agents.
457
+ # A nil icon removes the agent from the file.
458
+ def write_bar_icon(display_name, icon)
459
+ config = read_bar_config
460
+ agents = config["agents"] || []
461
+ existing = agents.find { |a| a["name"].to_s.casecmp?(display_name) }
462
+ if icon.nil?
463
+ agents.delete(existing)
464
+ elsif existing
465
+ existing["emoji"] = icon
466
+ else
467
+ agents << { "name" => display_name, "emoji" => icon }
468
+ end
469
+ config["agents"] = agents
470
+ config["default_emoji"] ||= "❓"
471
+ config["schema_version"] ||= "1.0"
472
+ config["last_updated"] = Time.now.iso8601
473
+ File.write(bar_config_file, "#{JSON.pretty_generate(config)}\n")
474
+ end
475
+
476
+ def prompt_for_bar_icon(display_name)
477
+ print "Status bar icon for #{display_name} (an emoji, blank to skip): "
478
+ icon = $stdin.gets.to_s.strip
479
+ icon.empty? ? nil : icon
480
+ end
481
+
421
482
  # Payload handed to plugins' on_agent_created: the registry entry plus the
422
483
  # setup inputs plugins need to provision accounts (avatar, description, email).
423
484
  # "open_browser" tells plugins whether they may launch a browser for their
@@ -1495,10 +1556,14 @@ when "setup"
1495
1556
 
1496
1557
  puts ""
1497
1558
  puts "Next steps:"
1498
- puts " 1. Register projects: cd ~/Code/myproject && brainiac register"
1499
- puts " 2. Initialize brain: brainiac brain init"
1500
- puts " 3. Install plugins: brainiac install discord / fizzy / github / zoho"
1501
- puts " 4. Start server: brainiac server"
1559
+ puts " 1. Install plugins: brainiac install discord / fizzy / github / zoho / basecamp"
1560
+ puts " 2. Register projects: cd ~/Code/myproject && brainiac register"
1561
+ puts " 3. Start server: brainiac server --daemon"
1562
+ if agent_name
1563
+ puts " 4. Finish #{agent_name} (persona, role, icon, plugin accounts): brainiac agent setup #{existing_agents.first}"
1564
+ else
1565
+ puts " 4. Add an agent (persona, role, icon, plugin accounts): brainiac agent create <Name> --local"
1566
+ end
1502
1567
 
1503
1568
  # Shell completion setup
1504
1569
  puts ""
@@ -2543,13 +2608,15 @@ when "agent"
2543
2608
  puts ""
2544
2609
  puts "Options:"
2545
2610
  puts " --avatar <path> Profile picture (png/jpg/gif/webp), used by plugins (Discord, Fizzy)"
2611
+ puts " --icon <emoji> Status bar icon (Omarchy bar, waybar, xbar), e.g. --icon ⚖️"
2546
2612
  puts " --description <text> Persona description (\"-\" reads stdin, \"@file\" reads a file)"
2547
2613
  puts " --description-file <path> Read the persona description from a file"
2548
2614
  puts " --email <address> Email for the agent's accounts (default: agent_email_template)"
2549
2615
  puts " --local Mark agent as local (dispatches on this machine)"
2550
2616
  puts " --role <role> Assign a role (repeatable)"
2551
2617
  puts " --role-file <path> Create a role from a markdown file and assign it"
2552
- puts " (named after the agent, or after a new --role name)"
2618
+ puts " (named after the file, e.g. blog-writer.md → blog-writer,"
2619
+ puts " or after a new --role name; an identical role is reused)"
2553
2620
  puts " --cli <provider> Set CLI provider (default: kiro)"
2554
2621
  puts " --skip <plugins> Don't set up these plugins for this agent, e.g. --skip basecamp"
2555
2622
  puts " --only <plugins> Set up just these plugins, e.g. --only discord,fizzy"
@@ -2557,7 +2624,8 @@ when "agent"
2557
2624
  puts ""
2558
2625
  puts "With no flags and a terminal, interactive mode is used. If no description is"
2559
2626
  puts "given on a terminal, $EDITOR opens so you can write a full persona. Without"
2560
- puts "--role/--role-file you're asked to pick an existing role or write a new one."
2627
+ puts "--role/--role-file you're asked to pick an existing role or write a new one,"
2628
+ puts "and without --icon you're asked for a status bar emoji."
2561
2629
  puts "After the brainiac files are written, each installed plugin sets up its part"
2562
2630
  puts "(Discord bot, Fizzy user, Basecamp person), asking first unless --skip/--only"
2563
2631
  puts "is given. Re-run with: brainiac agent setup <name>"
@@ -2567,8 +2635,8 @@ when "agent"
2567
2635
  puts " brainiac agent create Quill --local --avatar quill.png --description @quill.md"
2568
2636
  puts " brainiac agent create MyAgent # interactive"
2569
2637
  puts " brainiac agent create SecurityBot --local --role code-reviewer"
2570
- puts " brainiac agent create \"Judge Dredd\" --local --avatar dredd.png \\"
2571
- puts " --description-file dredd-style.md --role-file dredd-role.md"
2638
+ puts " brainiac agent create \"Judge Dredd\" --local --avatar dredd.png --icon ⚖️ \\"
2639
+ puts " --description-file dredd-style.md --role-file law-specialist.md"
2572
2640
  exit 1
2573
2641
  end
2574
2642
 
@@ -2587,6 +2655,7 @@ when "agent"
2587
2655
  cli_provider = nil
2588
2656
  persona_text = nil
2589
2657
  avatar = nil
2658
+ icon = nil
2590
2659
  email = nil
2591
2660
  skip_plugins = []
2592
2661
  only_plugins = nil
@@ -2617,6 +2686,9 @@ when "agent"
2617
2686
  when "--avatar"
2618
2687
  ARGV.shift
2619
2688
  avatar = ARGV.shift
2689
+ when "--icon"
2690
+ ARGV.shift
2691
+ icon = ARGV.shift.to_s.strip
2620
2692
  when "--email"
2621
2693
  ARGV.shift
2622
2694
  email = ARGV.shift
@@ -2672,16 +2744,13 @@ when "agent"
2672
2744
  exit 1
2673
2745
  end
2674
2746
  if role_file
2675
- # --role-file names its role after a --role that doesn't exist yet, else the agent key.
2676
- new_role = (roles - available_roles).first || agent_key
2677
- if available_roles.include?(new_role)
2678
- puts "❌ Role '#{new_role}' already exists. Pass --role <new-name> to name the role from #{role_file}."
2679
- exit 1
2680
- end
2681
- roles |= [new_role]
2747
+ role_name, write_role = role_from_file(role_file, roles)
2748
+ new_role = role_name if write_role
2749
+ roles |= [role_name]
2682
2750
  elsif roles.empty? && $stdin.tty?
2683
2751
  roles = prompt_for_agent_role(agent_key, name)
2684
2752
  end
2753
+ icon = prompt_for_bar_icon(name) if icon.nil? && $stdin.tty?
2685
2754
 
2686
2755
  missing_roles = roles - available_roles - [new_role]
2687
2756
  if missing_roles.any?
@@ -2708,7 +2777,7 @@ when "agent"
2708
2777
  end
2709
2778
  end
2710
2779
 
2711
- puts "✓ Created role #{write_role_file(new_role, File.read(role_file), name)}" if new_role
2780
+ puts "✓ Created role #{write_role_file(new_role, File.read(role_file))}" if new_role
2712
2781
 
2713
2782
  registry[agent_key] = entry
2714
2783
  File.write(agent_registry_file, JSON.pretty_generate(registry))
@@ -2718,6 +2787,10 @@ when "agent"
2718
2787
  puts " CLI: #{cli_provider}" if cli_provider
2719
2788
  puts " Email: #{email}" if email
2720
2789
  puts " Avatar: #{entry["avatar"]}" if entry["avatar"]
2790
+ if icon && !icon.empty?
2791
+ write_bar_icon(name, icon)
2792
+ puts " Bar icon: #{icon}"
2793
+ end
2721
2794
 
2722
2795
  # Create persona directory and style file if requested
2723
2796
  if persona_text
@@ -2772,16 +2845,18 @@ when "agent"
2772
2845
  when "setup"
2773
2846
  name = ARGV.shift
2774
2847
  unless name
2775
- puts "Usage: brainiac agent setup <name> [--avatar <path>] [--email <address>] [--role <role>] [--role-file <path>]"
2776
- puts " [--skip <plugins>] [--only <plugins>] [--no-browser]"
2848
+ puts "Usage: brainiac agent setup <name> [--avatar <path>] [--icon <emoji>] [--email <address>] [--role <role>]"
2849
+ puts " [--role-file <path>] [--skip <plugins>] [--only <plugins>] [--no-browser]"
2777
2850
  puts ""
2778
2851
  puts "Re-runs every plugin's agent setup (Discord bot, Fizzy user, Basecamp person)."
2779
2852
  puts "Steps that are already done are skipped, so it's safe to run after an"
2780
2853
  puts "interrupted 'agent create' or to set up a newly installed plugin."
2781
- puts "An agent without a role is asked to pick or write one."
2854
+ puts "An agent without a role is asked to pick or write one, and one without a"
2855
+ puts "status bar icon is asked for an emoji."
2782
2856
  puts ""
2857
+ puts " --icon e Set the status bar icon (Omarchy bar, waybar, xbar)"
2783
2858
  puts " --role r Assign an existing role"
2784
- puts " --role-file f Create a role from a markdown file (named after the agent) and assign it"
2859
+ puts " --role-file f Create a role from a markdown file (named after the file) and assign it"
2785
2860
  puts " --skip a,b Never set up these plugins for this agent (saved)"
2786
2861
  puts " --only a,b Run just these plugins (also un-skips them)"
2787
2862
  puts " --no-browser Print links instead of opening them"
@@ -2800,8 +2875,11 @@ when "agent"
2800
2875
  open_browser = true
2801
2876
  new_roles = []
2802
2877
  role_file = nil
2878
+ icon = nil
2803
2879
  while ARGV.any?
2804
2880
  case ARGV.shift
2881
+ when "--icon"
2882
+ icon = ARGV.shift.to_s.strip
2805
2883
  when "--role"
2806
2884
  new_roles << role_name_arg(ARGV.shift)
2807
2885
  when "--role-file"
@@ -2838,12 +2916,8 @@ when "agent"
2838
2916
 
2839
2917
  display = entry["display_name"] || name
2840
2918
  if role_file
2841
- role_name = (new_roles - available_roles).first || agent_key
2842
- if available_roles.include?(role_name)
2843
- puts "❌ Role '#{role_name}' already exists. Pass --role <new-name> to name the role from #{role_file}."
2844
- exit 1
2845
- end
2846
- puts "✓ Created role #{write_role_file(role_name, File.read(role_file), display)}"
2919
+ role_name, write_role = role_from_file(role_file, new_roles)
2920
+ puts "✓ Created role #{write_role_file(role_name, File.read(role_file))}" if write_role
2847
2921
  new_roles |= [role_name]
2848
2922
  elsif new_roles.empty? && entry["role"].nil? && $stdin.tty?
2849
2923
  new_roles = prompt_for_agent_role(agent_key, display)
@@ -2855,6 +2929,12 @@ when "agent"
2855
2929
  end
2856
2930
  puts "✓ Role: #{assign_roles(entry, new_roles).join(", ")}" if new_roles.any?
2857
2931
 
2932
+ icon = prompt_for_bar_icon(display) if icon.nil? && bar_icon(display).nil? && $stdin.tty?
2933
+ if icon && !icon.empty?
2934
+ write_bar_icon(display, icon)
2935
+ puts "✓ Bar icon: #{icon}"
2936
+ end
2937
+
2858
2938
  registry[agent_key] = entry
2859
2939
  File.write(agent_registry_file, JSON.pretty_generate(registry))
2860
2940
 
@@ -2883,6 +2963,7 @@ when "agent"
2883
2963
  File.write(agent_registry_file, JSON.pretty_generate(registry))
2884
2964
  puts "✓ Removed agent '#{display}' (#{agent_key}) from registry"
2885
2965
  puts " Note: Persona files at ~/.brainiac/brain/persona/#{agent_key}/ were not deleted."
2966
+ write_bar_icon(display, nil) if bar_icon(display)
2886
2967
 
2887
2968
  # Let plugins clean up their config
2888
2969
  notify_plugins_agent_removed(agent_key, display)
@@ -4543,7 +4624,7 @@ when "help", "--help", "-h", nil
4543
4624
  Brainiac CLI - Manage projects with Brainiac server
4544
4625
 
4545
4626
  Usage:
4546
- brainiac setup Bootstrap a new machine (deps, dirs, configs)
4627
+ brainiac setup Bootstrap a new machine (dirs, example configs, CLI providers)
4547
4628
  brainiac server [options] Start the Brainiac webhook server (foreground)
4548
4629
  brainiac start [options] Alias for 'brainiac server'
4549
4630
  brainiac stop Stop the running server
@@ -4618,6 +4699,9 @@ when "help", "--help", "-h", nil
4618
4699
  role unassign <agent> <role> Remove a role from an agent
4619
4700
 
4620
4701
  Agent Commands:
4702
+ agent create <name> [options] Add an agent: persona, role, icon, plugin accounts
4703
+ agent setup <name> [options] Finish or re-run plugin setup (skips what's done)
4704
+ agent remove <name> Remove an agent from the registry
4621
4705
  agent list List all agents in the registry
4622
4706
  agent <name> show Show agent configuration (tokens redacted)
4623
4707
  agent <name> env <KEY> <VALUE> Set an env var for an agent
@@ -30,6 +30,19 @@ This file configures how agents appear in the status bar — waybar on Linux, xb
30
30
  }
31
31
  ```
32
32
 
33
+ ## Setting an agent's icon
34
+
35
+ You don't need to edit this file by hand. `brainiac agent create` asks for an
36
+ icon (or takes `--icon`), and `brainiac agent setup` asks for one if the agent
37
+ has none. To change one later:
38
+
39
+ ```bash
40
+ brainiac agent setup "Judge Dredd" --icon ⚖️
41
+ ```
42
+
43
+ The Omarchy bar plugin, waybar and xbar all read this file, so one entry covers
44
+ every bar. Agents without an entry show `default_emoji`.
45
+
33
46
  ## Fields
34
47
 
35
48
  - **name**: Agent name (must match agent registry)
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Brainiac
4
4
  # @return [String] the current gem version
5
- VERSION = "0.4.2"
5
+ VERSION = "0.4.4"
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.4.2
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis