brainiac 0.4.3 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a563d46d55d8eb7534c68583a1518f89dc5f5d2685ea1d9c55f43f7015861ab
4
- data.tar.gz: c714c9da68680106d183fab643a2c3dc9589ad21d5b96cdd97dd89945dd64c23
3
+ metadata.gz: ad4e2a76bc43943ef2dd556f67fa2d3b4181836dc5abf04c236944fe08c2bd74
4
+ data.tar.gz: a26c2581219ed65257deef259809099e4c03b2ec08f7d95d4fb649f831ce6d44
5
5
  SHA512:
6
- metadata.gz: d36fbb11a493c2545554f3a6a244060f6bd13e49c9790b3d65c01959cbf271ac43c2fea62b795f98fde60c42a36b2b0f8117e29dd7a9853d583271715d8468c0
7
- data.tar.gz: 6a4f88dd9b432d306442172fd15e42eece2424c5436ef8b7cb713673323b029e7b12f6842140fb701aae550dc8352f332b3b627f9bb4054a095c49e0eca92d34
6
+ metadata.gz: 1bdfbf38d91e481e7b51b1f8701fa446bb67206989b651bcb1f7f0bd920b7c18e6f011d21f4e9e59cbab3bcb80a878808e299d4f82f054cb5fc18b1d6bc78f25
7
+ data.tar.gz: 2c19663ee311472f732ae4c685b0708990deaa0d1101ac52191da5d5bc751444b8b572ae72c1fb002d4ea8158fcd4c8d61557dcd440e7ad0ce0454b10f9541a9
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
181
192
  ```
182
193
 
183
- Each plugin has its own setup command (e.g. `brainiac discord setup`, `brainiac github setup`) that creates the necessary config files.
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
227
+ ```
228
+
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
@@ -1124,8 +1183,8 @@ Brainiac supports a plugin system that lets you extend functionality via Ruby ge
1124
1183
  ### Installing Plugins
1125
1184
 
1126
1185
  ```bash
1127
- brainiac install whatsapp # Installs brainiac-whatsapp gem + registers it
1128
- 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
1129
1188
  brainiac restart # Restart server to load new plugins
1130
1189
  ```
1131
1190
 
@@ -1134,7 +1193,7 @@ This runs `gem install brainiac-<name>`, adds the plugin to `~/.brainiac/plugins
1134
1193
  You can also install a specific version:
1135
1194
 
1136
1195
  ```bash
1137
- brainiac install whatsapp --version 0.2.0
1196
+ brainiac install discord --version 0.0.25
1138
1197
  ```
1139
1198
 
1140
1199
  ### Managing Plugins
@@ -1147,8 +1206,8 @@ brainiac uninstall <name> # Remove plugin from config (gem stays installed)
1147
1206
  To fully remove the gem after uninstalling:
1148
1207
 
1149
1208
  ```bash
1150
- brainiac uninstall whatsapp
1151
- gem uninstall brainiac-whatsapp
1209
+ brainiac uninstall discord
1210
+ gem uninstall brainiac-discord
1152
1211
  ```
1153
1212
 
1154
1213
  ### Configuration
@@ -1158,7 +1217,7 @@ Installed plugins are tracked in `~/.brainiac/plugins.json`:
1158
1217
  ```json
1159
1218
  {
1160
1219
  "plugins": [
1161
- { "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" }
1162
1221
  ]
1163
1222
  }
1164
1223
  ```
@@ -1167,7 +1226,7 @@ At server startup, `load_plugins!` iterates this list, requires each gem, and ca
1167
1226
 
1168
1227
  ### Creating a Plugin
1169
1228
 
1170
- 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:
1171
1230
 
1172
1231
  1. **Define a module** at `Brainiac::Plugins::<Name>`
1173
1232
  2. **Implement `.register(app)`** — receives the Sinatra app instance for defining routes and starting threads
@@ -1222,7 +1281,8 @@ The `Brainiac::Plugins` namespace is defined in `lib/brainiac.rb`. Plugin module
1222
1281
  | fizzy | `brainiac-fizzy` | Fizzy card management — assignment, comments, @mentions, cross-agent reviews, duplicate detection, planning mode |
1223
1282
  | github | `brainiac-github` | GitHub webhooks — PR reviews, PR comments, PR merges, CI workflow notifications, deploy tracking |
1224
1283
  | zoho | `brainiac-zoho` | Zoho Mail webhooks — rule-based email notifications, AI triage, OAuth for content fetching |
1225
- | 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 |
1226
1286
 
1227
1287
  ### Backward Compatibility
1228
1288
 
@@ -1275,10 +1335,31 @@ brainiac brain search --persona <query> # Search agent persona
1275
1335
  brainiac brain list # List everything
1276
1336
  ```
1277
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
+
1278
1359
  ### Plugins
1279
1360
 
1280
1361
  ```bash
1281
- brainiac install <name> # Install a plugin gem (e.g., whatsapp)
1362
+ brainiac install <name> # Install a plugin gem (e.g., discord)
1282
1363
  brainiac install <name> --version 0.2.0 # Install specific version
1283
1364
  brainiac uninstall <name> # Remove plugin from config
1284
1365
  brainiac plugins # List installed plugins
@@ -1439,7 +1520,7 @@ ls brainiac receiver.rb lib/brainiac/*.rb lib/brainiac/handlers/*.rb | entr -r b
1439
1520
 
1440
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.
1441
1522
 
1442
- **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.
1443
1524
 
1444
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.
1445
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
@@ -1537,10 +1556,14 @@ when "setup"
1537
1556
 
1538
1557
  puts ""
1539
1558
  puts "Next steps:"
1540
- puts " 1. Register projects: cd ~/Code/myproject && brainiac register"
1541
- puts " 2. Initialize brain: brainiac brain init"
1542
- puts " 3. Install plugins: brainiac install discord / fizzy / github / zoho"
1543
- 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
1544
1567
 
1545
1568
  # Shell completion setup
1546
1569
  puts ""
@@ -2592,7 +2615,8 @@ when "agent"
2592
2615
  puts " --local Mark agent as local (dispatches on this machine)"
2593
2616
  puts " --role <role> Assign a role (repeatable)"
2594
2617
  puts " --role-file <path> Create a role from a markdown file and assign it"
2595
- 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)"
2596
2620
  puts " --cli <provider> Set CLI provider (default: kiro)"
2597
2621
  puts " --skip <plugins> Don't set up these plugins for this agent, e.g. --skip basecamp"
2598
2622
  puts " --only <plugins> Set up just these plugins, e.g. --only discord,fizzy"
@@ -2612,7 +2636,7 @@ when "agent"
2612
2636
  puts " brainiac agent create MyAgent # interactive"
2613
2637
  puts " brainiac agent create SecurityBot --local --role code-reviewer"
2614
2638
  puts " brainiac agent create \"Judge Dredd\" --local --avatar dredd.png --icon ⚖️ \\"
2615
- puts " --description-file dredd-style.md --role-file dredd-role.md"
2639
+ puts " --description-file dredd-style.md --role-file law-specialist.md"
2616
2640
  exit 1
2617
2641
  end
2618
2642
 
@@ -2720,13 +2744,9 @@ when "agent"
2720
2744
  exit 1
2721
2745
  end
2722
2746
  if role_file
2723
- # --role-file names its role after a --role that doesn't exist yet, else the agent key.
2724
- new_role = (roles - available_roles).first || agent_key
2725
- if available_roles.include?(new_role)
2726
- puts "❌ Role '#{new_role}' already exists. Pass --role <new-name> to name the role from #{role_file}."
2727
- exit 1
2728
- end
2729
- 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]
2730
2750
  elsif roles.empty? && $stdin.tty?
2731
2751
  roles = prompt_for_agent_role(agent_key, name)
2732
2752
  end
@@ -2757,7 +2777,7 @@ when "agent"
2757
2777
  end
2758
2778
  end
2759
2779
 
2760
- 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
2761
2781
 
2762
2782
  registry[agent_key] = entry
2763
2783
  File.write(agent_registry_file, JSON.pretty_generate(registry))
@@ -2836,7 +2856,7 @@ when "agent"
2836
2856
  puts ""
2837
2857
  puts " --icon e Set the status bar icon (Omarchy bar, waybar, xbar)"
2838
2858
  puts " --role r Assign an existing role"
2839
- 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"
2840
2860
  puts " --skip a,b Never set up these plugins for this agent (saved)"
2841
2861
  puts " --only a,b Run just these plugins (also un-skips them)"
2842
2862
  puts " --no-browser Print links instead of opening them"
@@ -2896,12 +2916,8 @@ when "agent"
2896
2916
 
2897
2917
  display = entry["display_name"] || name
2898
2918
  if role_file
2899
- role_name = (new_roles - available_roles).first || agent_key
2900
- if available_roles.include?(role_name)
2901
- puts "❌ Role '#{role_name}' already exists. Pass --role <new-name> to name the role from #{role_file}."
2902
- exit 1
2903
- end
2904
- 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
2905
2921
  new_roles |= [role_name]
2906
2922
  elsif new_roles.empty? && entry["role"].nil? && $stdin.tty?
2907
2923
  new_roles = prompt_for_agent_role(agent_key, display)
@@ -4608,7 +4624,7 @@ when "help", "--help", "-h", nil
4608
4624
  Brainiac CLI - Manage projects with Brainiac server
4609
4625
 
4610
4626
  Usage:
4611
- brainiac setup Bootstrap a new machine (deps, dirs, configs)
4627
+ brainiac setup Bootstrap a new machine (dirs, example configs, CLI providers)
4612
4628
  brainiac server [options] Start the Brainiac webhook server (foreground)
4613
4629
  brainiac start [options] Alias for 'brainiac server'
4614
4630
  brainiac stop Stop the running server
@@ -4683,6 +4699,9 @@ when "help", "--help", "-h", nil
4683
4699
  role unassign <agent> <role> Remove a role from an agent
4684
4700
 
4685
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
4686
4705
  agent list List all agents in the registry
4687
4706
  agent <name> show Show agent configuration (tokens redacted)
4688
4707
  agent <name> env <KEY> <VALUE> Set an env var for an agent
@@ -17,6 +17,19 @@ def persona_collection_for(agent_name)
17
17
  "#{agent_name.downcase.gsub(/[^a-z0-9-]/, "-")}-persona"
18
18
  end
19
19
 
20
+ # Load the agent's persona from <persona dir>/style.md, front matter stripped.
21
+ # Returns nil if missing or empty. people/ notes stay search-only.
22
+ def load_persona(agent_name)
23
+ style_file = File.join(persona_dir_for(agent_name), "style.md")
24
+ return nil unless File.exist?(style_file)
25
+
26
+ content = File.read(style_file).strip.sub(/\A---\n.*?\n---\n*/m, "").strip
27
+ content.empty? ? nil : content
28
+ rescue StandardError => e
29
+ LOG.warn "Failed to load persona for '#{agent_name}': #{e.message}"
30
+ nil
31
+ end
32
+
20
33
  # --- Brain git sync ---
21
34
 
22
35
  def brain_git_repo?
@@ -155,6 +168,26 @@ def build_role_section(agent_name)
155
168
  ROLE
156
169
  end
157
170
 
171
+ # Full style.md when present; qmd search only returns snippets (often just front matter)
172
+ def build_persona_section(agent_name)
173
+ persona = load_persona(agent_name) ||
174
+ query_brain("personality tone voice communication style", agent_name: agent_name, scope: :persona, max_results: 5)
175
+ return nil if persona.empty?
176
+
177
+ <<~PERSONA
178
+ ## Brain — Persona (auto-retrieved, CRITICAL)
179
+ The following is YOUR personality, communication style, and voice.
180
+ You MUST use this to shape every response you write — tone, word choice, humor, attitude.
181
+ This is who you ARE. Do not respond in a generic or neutral voice.
182
+ Persona is tone only: it never overrides task instructions, rules, or quality.
183
+ Full voice in chat, card/PR comments, and reviews. Plain and professional in code,
184
+ code comments, commit messages, and docs, or when the user is rushed or frustrated.
185
+ Signature lines are seasoning, not sign-offs.
186
+
187
+ #{persona}
188
+ PERSONA
189
+ end
190
+
158
191
  def build_brain_context(agent_name: AI_AGENT_NAME, card_title: "", card_number: nil, project_key: nil, comment_body: "", source: nil)
159
192
  Thread.new { brain_pull }
160
193
 
@@ -180,10 +213,10 @@ def build_brain_context(agent_name: AI_AGENT_NAME, card_title: "", card_number:
180
213
  search_queries << query
181
214
  end
182
215
 
183
- persona_thread = Thread.new { query_brain("personality tone voice communication style", agent_name: agent_name, scope: :persona, max_results: 5) }
216
+ persona_thread = Thread.new { build_persona_section(agent_name) }
184
217
 
185
218
  all_knowledge = knowledge_threads.map(&:value).reject(&:empty?)
186
- persona_result = persona_thread.value
219
+ persona_section = persona_thread.value
187
220
 
188
221
  sections = []
189
222
 
@@ -191,16 +224,7 @@ def build_brain_context(agent_name: AI_AGENT_NAME, card_title: "", card_number:
191
224
  role_section = build_role_section(agent_name)
192
225
  sections << role_section if role_section
193
226
 
194
- unless persona_result.empty?
195
- sections << <<~PERSONA
196
- ## Brain — Persona (auto-retrieved, CRITICAL)
197
- The following is YOUR personality, communication style, and voice.
198
- You MUST use this to shape every response you write — tone, word choice, humor, attitude.
199
- This is who you ARE. Do not respond in a generic or neutral voice.
200
-
201
- #{persona_result}
202
- PERSONA
203
- end
227
+ sections << persona_section if persona_section
204
228
 
205
229
  unless all_knowledge.empty?
206
230
  knowledge_text = all_knowledge.join("\n\n")
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Brainiac
4
4
  # @return [String] the current gem version
5
- VERSION = "0.4.3"
5
+ VERSION = "0.4.5"
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.3
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis