zillacore 0.0.1

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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +12 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +126 -0
  6. data/README.md +1166 -0
  7. data/Rakefile +12 -0
  8. data/bin/zillacore +1521 -0
  9. data/certs/stowzilla.pem +26 -0
  10. data/docs/waybar-config.md +96 -0
  11. data/lib/user_registry.rb +159 -0
  12. data/lib/zillacore/agents.rb +203 -0
  13. data/lib/zillacore/brain.rb +197 -0
  14. data/lib/zillacore/card_index.rb +389 -0
  15. data/lib/zillacore/config.rb +263 -0
  16. data/lib/zillacore/cron.rb +629 -0
  17. data/lib/zillacore/deployments.rb +258 -0
  18. data/lib/zillacore/handlers/discord.rb +1643 -0
  19. data/lib/zillacore/handlers/fizzy.rb +1249 -0
  20. data/lib/zillacore/handlers/github.rb +598 -0
  21. data/lib/zillacore/handlers/zoho.rb +487 -0
  22. data/lib/zillacore/helpers.rb +760 -0
  23. data/lib/zillacore/planning.rb +237 -0
  24. data/lib/zillacore/prompts.rb +620 -0
  25. data/lib/zillacore/sessions.rb +282 -0
  26. data/lib/zillacore/skills.rb +276 -0
  27. data/lib/zillacore/users.rb +76 -0
  28. data/lib/zillacore/version.rb +6 -0
  29. data/lib/zillacore/zoho_mail_api.rb +109 -0
  30. data/lib/zillacore.rb +10 -0
  31. data/monitor/daemon.rb +99 -0
  32. data/monitor/deploy-env-macos.rb +131 -0
  33. data/monitor/menubar.rb +295 -0
  34. data/monitor/open-action.sh +15 -0
  35. data/monitor/setup-menubar.rb +78 -0
  36. data/monitor/setup-waybar-deploy-envs.rb +121 -0
  37. data/monitor/setup-waybar-deployments.rb +96 -0
  38. data/monitor/setup-waybar-module.rb +113 -0
  39. data/monitor/setup-xbar-plugin.rb +35 -0
  40. data/monitor/view-logs-macos.rb +210 -0
  41. data/monitor/view-logs-rofi.rb +194 -0
  42. data/monitor/view-logs.rb +119 -0
  43. data/monitor/waybar-config-updater.rb +56 -0
  44. data/monitor/waybar-deploy-env.rb +206 -0
  45. data/monitor/waybar-deployments.rb +239 -0
  46. data/monitor/waybar.rb +146 -0
  47. data/monitor/xbar.3s.rb +179 -0
  48. data/receiver.rb +956 -0
  49. data/templates/agents.json.example +10 -0
  50. data/templates/discord.json.example +17 -0
  51. data/templates/fizzy.json.example +24 -0
  52. data/templates/github.json.example +4 -0
  53. data/templates/testflight.json.example +8 -0
  54. data/templates/users.json.example +121 -0
  55. data/templates/zoho.json.example +27 -0
  56. data/views/dashboard.erb +437 -0
  57. data/zillacore.gemspec +30 -0
  58. data.tar.gz.sig +2 -0
  59. metadata +235 -0
  60. metadata.gz.sig +0 -0
data/README.md ADDED
@@ -0,0 +1,1166 @@
1
+ # ZillaCore
2
+
3
+ A webhook receiver that listens for [Fizzy](https://fizzy.do), GitHub, Discord, and Zoho Mail events, then dispatches work to AI agent CLIs. Each agent has its own persona, brain, and voice — they collaborate on the same projects through @mentions.
4
+
5
+ ## How It Works
6
+
7
+ Webhook events trigger the receiver to spawn an AI agent CLI with a natural language prompt. The receiver can dispatch multiple agents — each with a unique personality and kiro-cli config. Agents are discovered from `~/.kiro/agents/`, and projects registered in `~/.zillacore/projects.json`. Config reloads dynamically, no restart needed.
8
+
9
+ ### Events
10
+
11
+ | Source | Event | What Happens |
12
+ | --------- | ------------------- | ------------------------------------------------------------------------------------------------- |
13
+ | Fizzy | Card assigned | Creates worktree, maps card to branch, spawns assigned agent |
14
+ | Fizzy | Card published | Duplicate detection via trigram similarity + semantic search |
15
+ | Fizzy | @mention in comment | Routes to the mentioned agent (even on another agent's card) |
16
+ | Fizzy | Follow-up comment | Runs card's assigned agent in existing worktree |
17
+ | GitHub | PR review submitted | Agent addresses review feedback |
18
+ | GitHub | PR comment | Agent responds to PR feedback |
19
+ | GitHub | PR merged to main | Comments PR link on Fizzy card, closes card, cleans up worktree |
20
+ | GitHub | Issue opened | Logged for tracking (no agent dispatch) |
21
+ | GitHub | Workflow run | Notifies on CI failures via Discord |
22
+ | Discord | @bot mention | Each agent has its own bot — @mention routes directly to that agent, no worktree — conversational |
23
+ | Zoho Mail | Incoming email | Rule-based matching, notifies via Discord |
24
+
25
+ ### Inline Tags
26
+
27
+ All four channels support inline tags in message/comment text. Tags are stripped before reaching the agent prompt.
28
+
29
+ | Tag | Fizzy | Discord | GitHub | Description |
30
+ | ----------------------------------------------------------------------------- | ----- | ------- | ------ | ----------------------------------------------------------------------------------------------------- |
31
+ | `[project:XYZ]` | — | ✓ | — | Override which project the agent works in (Discord only — Fizzy uses card tags, GitHub uses the repo) |
32
+ | `[opus]` `[sonnet]` `[haiku]` `[deepseek]` `[minimax]` `[minimax21]` `[qwen]` | ✓ | ✓ | ✓ | Override the model for this dispatch |
33
+ | `[worktree:branch-name]` | ✓ | — | — | Direct the agent to a specific worktree instead of the card's default |
34
+ | `[plan]` | ✓ | ✓ | — | Activate planning mode — agent gathers requirements before coding |
35
+
36
+ Model keys come from the project's `allowed_models` config. Fizzy also supports model selection via card tags (e.g. adding an `opus` tag to the card).
37
+
38
+ ### Planning Mode
39
+
40
+ Add a `[plan]` tag to a Discord message or a `plan` tag to a Fizzy card to activate planning mode. Instead of jumping straight into implementation, the agent:
41
+
42
+ 1. Asks clarifying questions to understand the problem, constraints, and desired outcome
43
+ 2. Logs Q&A to its memory file for continuity across sessions
44
+ 3. Generates a plan markdown file at `~/.zillacore/plans/card-<id>-plan.md`
45
+ 4. Automatically creates Fizzy steps from the task breakdown
46
+
47
+ The agent stays read-only during planning — no code changes, no commits. Once the plan is finalized, the system creates Fizzy steps from each `### Task N: Title` heading in the plan file.
48
+
49
+ ### Cross-Agent Mentions
50
+
51
+ Any agent can be tagged on any card. If Kaylee is working card #42 and someone comments "@Galen what do you think?", Galen reviews the card and PR without touching Kaylee's worktree. This enables patterns like:
52
+
53
+ - Engineer agent does the work, security agent reviews it
54
+ - One agent asks another for a second opinion
55
+ - A read-only agent (e.g. GLaDOS) that reviews and comments but doesn't take over the card
56
+
57
+ #### Display Name Accuracy
58
+
59
+ Agents need to spell @mentions exactly as they appear in Fizzy (e.g. `@GLaDOS` not `@glados`). The agent registry's `fizzy_name` field handles this — every prompt includes an agent roster with the correct spelling:
60
+
61
+ ```
62
+ ## Agent Roster
63
+ When @mentioning other agents in Fizzy comments, use the EXACT spelling below.
64
+ - @Galen
65
+ - @GLaDOS
66
+ - @Kaylee
67
+ ```
68
+
69
+ Detection is case-insensitive (inbound `@glados` still matches GLaDOS), but outbound mentions use the exact `fizzy_name` from the registry.
70
+
71
+ #### Agent-to-Agent Loop Prevention
72
+
73
+ When agents can tag each other, infinite loops become possible (Galen tags GLaDOS, GLaDOS tags Galen back, forever). ZillaCore prevents this with layered defenses:
74
+
75
+ 1. **Dispatch depth limit** — a per-card counter tracks agent-to-agent hops since the last human comment. Default max depth is 10: Human → Agent A → Agent B → ... is allowed up to the limit. The counter resets when a human comments on the card, and expires after 1 hour of no human activity.
76
+
77
+ 2. **Prompt instruction** — cross-agent review prompts explicitly tell the dispatched agent not to @mention other agents. It can suggest involving someone in plain text, but not with `@Agent` syntax.
78
+
79
+ 3. **Existing defenses** — `session_active?` prevents concurrent runs on the same card, `COMMENT_COOLDOWN` (60s) suppresses rapid-fire triggers, and self-comment filtering prevents an agent from triggering itself.
80
+
81
+ Tuning knobs in `lib/zillacore/sessions.rb`:
82
+
83
+ - `AGENT_DISPATCH_MAX_DEPTH` — max agent-to-agent hops (default: 10)
84
+ - `AGENT_DISPATCH_WINDOW` — seconds before depth resets without human activity (default: 3600)
85
+ - `GET /api/dispatch-depth` — debug endpoint showing current depth state per card
86
+
87
+ ### Card Context Pre-Fetching
88
+
89
+ Before dispatching an agent, ZillaCore pre-fetches the Fizzy card body and last 5 comments, injecting them directly into the prompt. This means agents don't need to make separate API calls to read the card — the context is already there. Results are cached for 60 seconds to avoid redundant fetches on rapid-fire triggers.
90
+
91
+ ### Card Duplicate Detection
92
+
93
+ When a card is published or triaged, ZillaCore checks for potential duplicates using two methods:
94
+
95
+ 1. **Trigram similarity** — compares the new card's title against all indexed card titles using character trigram overlap
96
+ 2. **Semantic search** — uses qmd to find cards with similar meaning, even if the wording differs
97
+
98
+ If matches are found above the similarity threshold, ZillaCore posts a comment on the card listing the potential duplicates. The card index is stored at `~/.zillacore/card_index.json` and reindexed periodically via qmd.
99
+
100
+ ### Pre-Post Comment Check
101
+
102
+ Before posting a response, agents re-fetch the source (Fizzy card comments or GitHub PR comments) to catch any new messages that arrived while they were working. If a human added context, changed requirements, or asked for adjustments mid-session, the agent incorporates that before posting — avoiding stale or outdated responses.
103
+
104
+ This applies to Fizzy and GitHub channels. Discord uses a different mechanism (session supersede) where follow-up messages within 60 seconds kill the previous run and restart with updated context.
105
+
106
+ ### Pre-Comment Reflection
107
+
108
+ Before writing any comment, agents go through a mandatory reflection ritual:
109
+
110
+ 1. React with 🧠 to signal reflection
111
+ 2. Query their persona collection for communication style and the triggering user
112
+ 3. Decide whether persona or knowledge needs updating
113
+ 4. Update brain files (or consciously skip) and run `qmd update`
114
+ 5. React with a situational emoji (🎉, 💪, 🤔, 😅, 🔥, etc.)
115
+ 6. Write the comment in their unique voice
116
+
117
+ This compounds over dozens of sessions into a rich understanding of each person and codebase.
118
+
119
+ ## Setup
120
+
121
+ ### Installation
122
+
123
+ ```bash
124
+ gem install zillacore
125
+ ```
126
+
127
+ This installs the `zillacore` binary to your gem bin path (just like `rails`, `rspec`, etc.). Ensure your gem bin directory is on your `PATH` — it typically is if you're using a Ruby version manager.
128
+
129
+ ### Quick Start (New Machine)
130
+
131
+ After installing the gem:
132
+
133
+ ```bash
134
+ zillacore setup
135
+ ```
136
+
137
+ This creates the `~/.zillacore/` directory structure and copies example config files. Then edit the configs with your actual secrets and IDs (see below).
138
+
139
+ ### Prerequisites
140
+
141
+ | Dependency | Required | Install |
142
+ |------------|----------|---------|
143
+ | Ruby 3.4+ | Yes | [mise](https://mise.jdx.dev), rbenv, or system |
144
+ | [Kiro CLI](https://kiro.dev) | Yes | Agent dispatch |
145
+ | [Fizzy CLI](https://github.com/robzolkos/fizzy-cli) | For Fizzy | Card management |
146
+ | [GitHub CLI](https://cli.github.com) (`gh`) | For GitHub | PR/issue operations |
147
+ | [ngrok](https://ngrok.com) | Yes | Webhook tunneling |
148
+ | [qmd](https://github.com/tobi/qmd) | For brain | `npm install -g @tobilu/qmd` (Node.js >= 22) |
149
+ | [gum](https://github.com/charmbracelet/gum) | Optional | Manual worktree cleanup |
150
+
151
+ ### Directory Structure
152
+
153
+ After setup, `~/.zillacore/` looks like:
154
+
155
+ ```
156
+ ~/.zillacore/
157
+ ├── agents.json # Agent registry (tokens, display names, local flag)
158
+ ├── projects.json # Registered projects
159
+ ├── github.json # GitHub webhook secret
160
+ ├── fizzy.json # Fizzy board config, authorized users
161
+ ├── discord.json # Discord channel mappings, auth
162
+ ├── users.json # Cross-platform user identity registry
163
+ ├── zoho.json # Zoho Mail rules (optional)
164
+ ├── brain/
165
+ │ ├── knowledge/ # Shared technical knowledge (all agents)
166
+ │ ├── persona/ # Per-agent personality files
167
+ │ └── memory/ # Per-agent session memory
168
+ ├── handlers/ # Custom webhook handlers (plugin system)
169
+ ├── plans/ # Planning mode output
170
+ └── tmp/ # Temp files, drafts, posted responses
171
+ ```
172
+
173
+ ### Step-by-Step Configuration
174
+
175
+ #### 1. Agent Registry (`~/.zillacore/agents.json`)
176
+
177
+ Maps agents to their identity and environment. Every agent that should dispatch on this machine needs an entry here with `"local": true`:
178
+
179
+ ```json
180
+ {
181
+ "galen": {
182
+ "fizzy_name": "Galen",
183
+ "local": true,
184
+ "env": {
185
+ "FIZZY_TOKEN": "fizzy_abc...",
186
+ "DISCORD_BOT_TOKEN": "Bot_abc..."
187
+ }
188
+ }
189
+ }
190
+ ```
191
+
192
+ See [Multi-Agent Setup](#multi-agent-setup) for full details.
193
+
194
+ #### 2. Kiro Agent Configs (`~/.kiro/agents/<name>.json`)
195
+
196
+ Each agent also needs a kiro-cli config. The filename becomes the agent name:
197
+
198
+ ```bash
199
+ kiro-cli agent create # Interactive
200
+ # Or manually create ~/.kiro/agents/galen.json
201
+ ```
202
+
203
+ #### 3. GitHub (`~/.zillacore/github.json`)
204
+
205
+ ```json
206
+ {
207
+ "webhook_secret": "your-github-webhook-secret",
208
+ "repos": {}
209
+ }
210
+ ```
211
+
212
+ The `webhook_secret` verifies incoming GitHub webhook requests. Generate one with `ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'`.
213
+
214
+ **Legacy:** `GITHUB_WEBHOOK_SECRET` env var works as fallback.
215
+
216
+ #### 4. Fizzy (`~/.zillacore/fizzy.json`)
217
+
218
+ Defines authorized users, flags humans, and configures boards:
219
+
220
+ ```json
221
+ {
222
+ "authorized_users": [
223
+ { "id": "user-id-1", "name": "Andy", "human": true },
224
+ { "id": "user-id-2", "name": "Adam", "human": true },
225
+ { "id": "agent-id-1", "name": "Galen", "human": false }
226
+ ],
227
+ "boards": {
228
+ "development": {
229
+ "board_id": "your-board-id",
230
+ "webhook_secret": "secret-for-this-board",
231
+ "columns": {
232
+ "right_now": "column-id",
233
+ "needs_review": "column-id",
234
+ "uat": "column-id"
235
+ }
236
+ }
237
+ }
238
+ }
239
+ ```
240
+
241
+ Each board gets its own webhook secret and column IDs. The board key (e.g., `development`) is used in the webhook URL.
242
+
243
+ When a human is @mentioned on a card assigned to an agent, the agent will skip that comment — allowing human-to-human conversation without agent interference.
244
+
245
+ **Legacy:** `FIZZY_WEBHOOK_SECRET` and `AUTHORIZED_USER_IDS` env vars work as fallbacks.
246
+
247
+ #### 5. Environment Variables
248
+
249
+ Most config lives in JSON files now. The only env var you might want:
250
+
251
+ ```bash
252
+ export AI_AGENT_NAME="Galen" # Defaults to Galen (Linux) or Kaylee (macOS)
253
+ ```
254
+
255
+ #### 6. Register Projects
256
+
257
+ ```bash
258
+ cd ~/Code/marketplace && zillacore register
259
+ cd ~/Code/zillacore && zillacore register
260
+ zillacore list
261
+ ```
262
+
263
+ The CLI prompts for project key, Fizzy tags, GitHub repo, and agent CLI settings.
264
+
265
+ Set a default project (used as fallback when no tags match):
266
+
267
+ ```bash
268
+ zillacore projects default myproject
269
+ ```
270
+
271
+ #### 7. Initialize Brain
272
+
273
+ ```bash
274
+ zillacore brain init Galen
275
+ ```
276
+
277
+ This creates the directory structure, sets up qmd collections, and indexes everything.
278
+
279
+ #### 8. Configure Webhooks
280
+
281
+ **Fizzy:** Board settings → Webhooks → URL: `https://your-ngrok.ngrok-free.app/fizzy/development` (where `development` is the board key from `fizzy.json`), Secret: the board's `webhook_secret`
282
+
283
+ **GitHub:** Repo settings → Webhooks → URL: `https://your-ngrok.ngrok-free.app/github`, Content type: `application/json`, Secret: from `github.json`, Events: Pull requests, Pull request reviews, Issue comments, Issues
284
+
285
+ #### 9. Start
286
+
287
+ ```bash
288
+ zillacore server # Start and tail logs (Ctrl+C to detach, server keeps running)
289
+ ngrok http 4567 # Terminal 2
290
+ ```
291
+
292
+ ## Multi-Agent Setup
293
+
294
+ This is the core of ZillaCore. Each machine runs one receiver that can dispatch multiple agents.
295
+
296
+ ### Architecture
297
+
298
+ ```
299
+ Andy's Linux box Adam's macOS
300
+ ┌─────────────────────┐ ┌─────────────────────┐
301
+ │ zillacore server │ │ zillacore server │
302
+ │ │ │ │
303
+ │ ~/.kiro/agents/: │ │ ~/.kiro/agents/: │
304
+ │ galen.json │ │ kaylee.json │
305
+ │ glados.json │ │ jane.json │
306
+ │ │ │ │
307
+ │ ~/.zillacore/ │ │ ~/.zillacore/ │
308
+ │ agents.json │ │ agents.json │
309
+ └─────────────────────┘ └─────────────────────┘
310
+ │ │
311
+ └──── same Fizzy board + GitHub ────┘
312
+ ```
313
+
314
+ Both machines receive the same webhooks. The receiver discovers available agents by scanning `~/.kiro/agents/*.json` — only agents with a config on that machine will be dispatched, preventing duplicates.
315
+
316
+ ### Step 1: Create Kiro CLI Agent Configs
317
+
318
+ Each agent needs a kiro-cli config at `~/.kiro/agents/<name>.json`. This is the only registry — no separate config file needed.
319
+
320
+ ```bash
321
+ kiro-cli agent create # Interactive
322
+ # Or manually create ~/.kiro/agents/galen.json, ~/.kiro/agents/glados.json
323
+ ```
324
+
325
+ The receiver scans this directory to discover which agents it can dispatch. The filename becomes the agent name (e.g. `galen.json` → Galen). Tool permissions, model, and resources are all defined in the kiro-cli config.
326
+
327
+ ### Step 2: Agent Registry
328
+
329
+ The agent registry at `~/.zillacore/agents.json` maps each agent to its identity and environment. This serves four purposes:
330
+
331
+ 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)
332
+ 2. **Display name mapping** — agents know the exact spelling for @mentions (e.g. `GLaDOS` not `glados`)
333
+ 3. **Discord bot tokens** — agents with `DISCORD_BOT_TOKEN` in their env get their own Discord bot
334
+ 4. **Local flag** — agents marked `"local": true` pick up card assignments on this machine
335
+
336
+ ```json
337
+ {
338
+ "galen": {
339
+ "fizzy_name": "Galen",
340
+ "local": true,
341
+ "env": {
342
+ "FIZZY_TOKEN": "fizzy_abc...",
343
+ "DISCORD_BOT_TOKEN": "Bot_abc..."
344
+ }
345
+ },
346
+ "glados": {
347
+ "fizzy_name": "GLaDOS",
348
+ "env": {
349
+ "FIZZY_TOKEN": "fizzy_xyz...",
350
+ "DISCORD_BOT_TOKEN": "Bot_xyz..."
351
+ }
352
+ },
353
+ "kaylee": {
354
+ "fizzy_name": "Kaylee"
355
+ }
356
+ }
357
+ ```
358
+
359
+ 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.
360
+
361
+ 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. Agents discovered from `~/.kiro/agents/` configs and the default `AI_AGENT_NAME` are always considered local.
362
+
363
+ Agents without an `env` block (like Kaylee above on a Linux box) still appear in the agent roster so local agents spell @mentions correctly.
364
+
365
+ A legacy format with top-level `fizzy_token` / `discord_bot_token` keys is auto-migrated into the `env` hash at load time. A legacy `~/.zillacore/agent_tokens.json` format (flat `{ "galen": "token" }`) is supported as a fallback and auto-migrated.
366
+
367
+ The registry reloads on every webhook and via `POST /api/reload`.
368
+
369
+ ### Step 3: Seed Agent Persona and Knowledge
370
+
371
+ 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.
372
+
373
+ #### Persona
374
+
375
+ Each agent needs at least one persona file. Create it directly in the brain:
376
+
377
+ ```bash
378
+ # Create persona file for your agent
379
+ mkdir -p ~/.zillacore/brain/persona/galen
380
+ cat > ~/.zillacore/brain/persona/galen/style.md << 'EOF'
381
+ ---
382
+ name: galen-persona
383
+ description: Persona voice for Galen.
384
+ ---
385
+ # Galen — Persona
386
+ Gruff, no-nonsense, direct, practical, a little cynical. Zero corporate fluff.
387
+ Keep responses tight and technical.
388
+ EOF
389
+
390
+ # Index it
391
+ qmd update
392
+ ```
393
+
394
+ The persona only affects comments — agents do their actual work (coding, debugging, etc.) without any persona influence.
395
+
396
+ #### Knowledge
397
+
398
+ Shared knowledge goes in `~/.zillacore/brain/knowledge/`. This is where you put project conventions, tool docs, coding patterns — anything all agents should know:
399
+
400
+ ```bash
401
+ mkdir -p ~/.zillacore/brain/knowledge/tools
402
+ cat > ~/.zillacore/brain/knowledge/tools/fizzy.md << 'EOF'
403
+ # Fizzy CLI Reference
404
+ fizzy card list — list cards
405
+ fizzy comment create --card 123 --body "<p>Hello</p>" — post a comment
406
+ EOF
407
+
408
+ qmd update
409
+ ```
410
+
411
+ 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.
412
+
413
+ ### Step 4: Initialize Each Agent's Brain
414
+
415
+ ```bash
416
+ zillacore brain init # Default agent (Galen on Linux, Kaylee on macOS)
417
+ zillacore brain init SecurityBot # Additional agent
418
+ zillacore brain list # Verify all agents
419
+ ```
420
+
421
+ This creates the directory structure, sets up qmd collections, and indexes everything. Run this after you've placed your persona and knowledge files.
422
+
423
+ ### How Dispatch Works
424
+
425
+ When a webhook arrives:
426
+
427
+ 1. **Card assigned** — the receiver checks if any assignee matches a local agent name. Only agents marked `local` (or discovered from `~/.kiro/agents/`) pick up assignments — this prevents multiple machines from dispatching the same card.
428
+ 2. **@mention in comment** — the receiver detects which agent is mentioned (e.g. `@Galen`, `@SecurityBot`). If the mentioned agent differs from the card's assigned agent, it's a cross-agent review — the mentioned agent reviews without a worktree. Non-local agent mentions are ignored.
429
+ 3. **Follow-up comment (no mention)** — the card's assigned agent handles it.
430
+
431
+ The command dispatched looks like:
432
+
433
+ ```bash
434
+ kiro-cli --agent galen chat --trust-all-tools --no-interactive
435
+ ```
436
+
437
+ The `--agent` flag goes before the `chat` subcommand, pointing kiro-cli to the agent's config.
438
+
439
+ ## Brain (Long-Term Memory)
440
+
441
+ Agents have persistent memory powered by [qmd](https://github.com/tobi/qmd):
442
+
443
+ | Part | Location | Scope | Purpose |
444
+ | --------- | ------------------------------------- | ------------------------- | ----------------------------------------------------------- |
445
+ | Knowledge | `~/.zillacore/brain/knowledge/` | Shared across all agents | Project conventions, patterns, architecture decisions |
446
+ | Memory | `~/.zillacore/brain/memory/<agent>/` | Per-agent, per-card files | Session history — decisions, questions, work status |
447
+ | Persona | `~/.zillacore/brain/persona/<agent>/` | Per-agent | Communication style, tone — only used when writing comments |
448
+
449
+ Each part gets its own qmd collection:
450
+
451
+ - `zillacore-knowledge` — shared knowledge
452
+ - `galen-memory`, `kaylee-memory` — per-agent card memory
453
+ - `galen-persona`, `kaylee-persona` — per-agent persona
454
+
455
+ Knowledge is automatically queried and injected into every prompt. Memory is read/written at the start/end of each session. Persona stays out of work context — agents only query it during pre-comment reflection.
456
+
457
+ After every agent session completes, `qmd update` runs automatically as a safety net (agents are told to do this themselves, but don't always remember).
458
+
459
+ To seed the brain, create markdown files directly in the appropriate directories and run `qmd update`. See the [Multi-Agent Setup](#multi-agent-setup) section for examples.
460
+
461
+ ### Git Sync
462
+
463
+ The brain directory (`~/.zillacore/brain/`) can be backed up as a git repo. If a `.git` directory is detected inside the brain, ZillaCore automatically syncs:
464
+
465
+ - **Pull** at the start of every session (before building brain context), with a 30-second debounce to avoid hammering on rapid-fire triggers
466
+ - **Push** after every agent session completes (Fizzy, GitHub, and Discord)
467
+
468
+ This keeps brains in sync across machines. If your co-founder runs their own zillacore with different agents, both machines share the same knowledge and memory through the repo.
469
+
470
+ ```bash
471
+ cd ~/.zillacore/brain
472
+ git init
473
+ git remote add origin git@github.com:yourorg/zillacore-brain.git
474
+ git add -A && git commit -m "initial brain" && git push -u origin main
475
+ ```
476
+
477
+ Conflicts are handled with `git pull --rebase --autostash`. If a rebase fails (rare — the files are markdown), it aborts cleanly and logs a warning. The push retries up to 3 times with exponential backoff.
478
+
479
+ ## Discord Bot
480
+
481
+ Each agent gets its own Discord bot. Users @mention @Galen or @GLaDOS directly in Discord — no shared bot, no agent name detection needed. No Fizzy card or worktree is created; the agent runs in the project's repo for read-only exploration, brain queries, and knowledge/persona updates.
482
+
483
+ All Discord bots run inside `zillacore server` as background threads — no separate processes to manage.
484
+
485
+ ### Session Supersede
486
+
487
+ When a human sends a follow-up message within 60 seconds of triggering an agent, ZillaCore kills the previous agent run and starts a new one with the updated context. This lets you correct typos or add context without waiting for the first run to finish. Draft files from the superseded session are cleaned up so stale responses are never delivered.
488
+
489
+ ### Cancelling Agent Sessions
490
+
491
+ React with ❌ to any message that triggered an agent to immediately terminate that session:
492
+
493
+ 1. User @mentions an agent → agent reacts with 👀 and starts working
494
+ 2. User reacts with ❌ on that same message
495
+ 3. Agent process is killed (SIGKILL)
496
+ 4. Session removed from active sessions
497
+ 5. Reactions update: 👀 → 🛑
498
+
499
+ ### Setup
500
+
501
+ #### Step 1: Create Discord Applications
502
+
503
+ Create one Discord application per agent at https://discord.com/developers/applications:
504
+
505
+ 1. Click "New Application", name it after the agent (e.g. "Galen", "GLaDOS")
506
+ 2. Go to the "Bot" tab in the left sidebar
507
+ 3. Under "Privileged Gateway Intents", enable **Message Content Intent** — this is required for the bot to read message text. Without it, all message content arrives as empty strings.
508
+ 4. Optionally uncheck "Public Bot" if you don't want others to invite your bots
509
+
510
+ Repeat for each agent that needs a Discord bot.
511
+
512
+ #### Step 2: Generate Bot Tokens
513
+
514
+ Still on the "Bot" tab for each application:
515
+
516
+ 1. Click "Reset Token" (or "Copy" if the token is still visible)
517
+ 2. Copy the token immediately — Discord only shows it once
518
+ 3. Store it somewhere safe temporarily
519
+
520
+ Register each token with ZillaCore:
521
+
522
+ ```bash
523
+ zillacore discord token galen "BOT_TOKEN_FOR_GALEN"
524
+ zillacore discord token glados "BOT_TOKEN_FOR_GLADOS"
525
+ ```
526
+
527
+ This adds `DISCORD_BOT_TOKEN` to the agent's `env` hash in `~/.zillacore/agents.json`. You can verify with:
528
+
529
+ ```bash
530
+ zillacore discord agents
531
+ ```
532
+
533
+ #### Step 3: Invite Bots to Your Server
534
+
535
+ Go to the "OAuth2" tab for each application:
536
+
537
+ 1. Under "OAuth2 URL Generator", check the **bot** scope
538
+ 2. Under "Bot Permissions", select:
539
+ - Send Messages
540
+ - Create Public Threads
541
+ - Send Messages in Threads
542
+ - Add Reactions
543
+ - Read Message History
544
+ 3. Copy the generated URL and open it in your browser
545
+ 4. Select your server and authorize
546
+
547
+ The permission integer for these permissions is `326417591296`. You can also construct the invite URL manually:
548
+
549
+ ```
550
+ https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&scope=bot&permissions=326417591296
551
+ ```
552
+
553
+ Replace `YOUR_APP_ID` with the Application ID from the "General Information" tab. Repeat for each agent bot.
554
+
555
+ #### Step 4: Configure Project Mapping
556
+
557
+ Set a default project so bots know which repo to work in:
558
+
559
+ ```bash
560
+ zillacore discord default marketplace
561
+ ```
562
+
563
+ Optionally map specific channels to different projects:
564
+
565
+ ```bash
566
+ zillacore discord map 1234567890 zillacore
567
+ ```
568
+
569
+ To get a channel ID, enable Developer Mode in Discord (User Settings → Advanced → Developer Mode), then right-click a channel and "Copy Channel ID".
570
+
571
+ #### Step 5: Start the Server
572
+
573
+ ```bash
574
+ zillacore server
575
+ ```
576
+
577
+ All bots connect automatically as background threads. Check they're online:
578
+
579
+ ```bash
580
+ zillacore discord status
581
+ ```
582
+
583
+ You should see each bot listed as `connected` with a `user_id`. If a bot shows `error` or `disconnected`, double-check the token and that the Message Content intent is enabled.
584
+
585
+ ### How It Works
586
+
587
+ When someone @mentions an agent's bot in Discord:
588
+
589
+ 1. The bot reacts with 👀 — the agent identity comes from the bot itself, no detection needed
590
+ 2. Dispatches the agent with a conversational prompt — no card, no worktree
591
+ 3. The agent can read project files, search the brain, and update knowledge/persona
592
+ 4. The agent writes its response to a temp file
593
+ 5. The bot creates a thread off your message (named after the agent and your question) and posts the response there
594
+ 6. Follow-up @mentions inside the thread continue the conversation in-thread
595
+
596
+ Use `[project:XYZ]` to target a specific project and `[opus]`/`[sonnet]`/`[haiku]` to override the model:
597
+
598
+ ```
599
+ @Galen [project:zillacore] [opus] how does the webhook signature verification work?
600
+ ```
601
+
602
+ Tags are stripped from the prompt — the agent only sees the question.
603
+
604
+ ### Response Delivery
605
+
606
+ Agents write responses to draft files in `~/.zillacore/tmp/discord/draft/`. A background poller thread checks for completed drafts every 10 seconds and delivers them to Discord. Each draft has a `.meta.json` sidecar with delivery metadata (channel ID, agent, thread info). After successful posting, both files move to `~/.zillacore/tmp/discord/posted/`. This file-based approach survives server restarts — orphaned drafts are recovered by the poller.
607
+
608
+ ### Forum Channel Support
609
+
610
+ Cron jobs targeting a Discord forum channel automatically create new forum posts instead of regular messages. The forum post title defaults to `<Agent> — <Date>` but can be customized with the `-t` flag on `zillacore cron add`.
611
+
612
+ ### Configuration
613
+
614
+ Channel mappings and authorization are stored in `~/.zillacore/discord.json`:
615
+
616
+ ```json
617
+ {
618
+ "default_project": "marketplace",
619
+ "owner_discord_id": "YOUR_DISCORD_USER_ID",
620
+ "dashboard_token": "optional-token-for-web-dashboard",
621
+ "channel_mappings": {
622
+ "0987654321": { "project": "zillacore" }
623
+ },
624
+ "user_mappings": {
625
+ "Andy": "123456789012345678",
626
+ "Adam": "234567890123456789",
627
+ "Kaylee": "345678901234567890"
628
+ },
629
+ "authorized_role_ids": ["role-id"],
630
+ "authorized_user_ids": ["user-id"],
631
+ "giphy_api_key": "your-giphy-api-key"
632
+ }
633
+ ```
634
+
635
+ `default_project` applies to any channel without a specific mapping. Channel mappings are optional overrides for when you want a specific channel tied to a specific project.
636
+
637
+ `owner_discord_id` identifies the server owner for admin-level notifications.
638
+
639
+ `dashboard_token` protects the web dashboard at `/dashboard`. If set, requests must include this token to access the dashboard.
640
+
641
+ `user_mappings` maps display names to Discord user IDs. This serves two purposes: agents use it to format proper `<@ID>` mentions (plain text `@Name` doesn't work in Discord), and the bot recognition system uses it to identify remote agent bots running on other machines. Add humans, remote agents, and anyone else the agents might need to mention. Get IDs by enabling Developer Mode in Discord and right-clicking a user.
642
+
643
+ Leave `authorized_role_ids` and `authorized_user_ids` empty to allow everyone in the server. Add IDs to restrict who can trigger agents.
644
+
645
+ `giphy_api_key` enables GIF support — agents can optionally include GIFs in conversational Discord responses. Get a free API key from [GIPHY Developers](https://developers.giphy.com/). Agents search via `GET /api/gif?q=search+terms` and paste the returned URL into their response; Discord auto-embeds it.
646
+
647
+ ### CLI Commands
648
+
649
+ ```bash
650
+ zillacore discord token <agent> <token> # Set Discord bot token for an agent
651
+ zillacore discord agents # List agents with Discord bot tokens
652
+ zillacore discord default <project> # Set default project for all channels
653
+ zillacore discord map <channel-id> <project> # Override for a specific channel
654
+ zillacore discord config # Show current Discord config
655
+ zillacore discord status # Check bot status via server API
656
+ ```
657
+
658
+ ## Cron (Scheduled Tasks)
659
+
660
+ Agents can be dispatched on a schedule — daily standups, weekly summaries, periodic code reviews, whatever you want. Jobs are stored in `~/.zillacore/cron.json` and run in a background thread inside `zillacore server`.
661
+
662
+ Supports both recurring schedules (standard cron) and one-time scheduled tasks (natural language or ISO8601 timestamps).
663
+
664
+ Cron jobs can run in two modes:
665
+
666
+ 1. **Agent mode** (default) — dispatches an agent with a prompt
667
+ 2. **Script mode** — runs a script directly without an agent, output goes to Discord
668
+
669
+ ### Adding a Job
670
+
671
+ ```bash
672
+ # Agent mode (recurring schedules)
673
+ zillacore cron add -s "0 9 * * 1-5" -p marketplace "Summarize open cards and post a standup update"
674
+ zillacore cron add -s "@daily" -p zillacore -a Galen "Review recent commits and flag anything that needs attention"
675
+ zillacore cron add -s "0 17 * * 5" -p marketplace -d 1234567890 "Post a weekly summary to Discord"
676
+
677
+ # Script mode (no agent, direct execution)
678
+ zillacore cron add -s "0 8 * * 1-5" -p zillacore --script ~/.zillacore/scripts/daily-report.sh -d 1234567890
679
+
680
+ # One-time schedules (natural language)
681
+ zillacore cron add -s "tomorrow at 9am" -p marketplace "Reminder about priorities"
682
+ zillacore cron add -s "in 2 hours" -p zillacore "Follow up on PR review"
683
+ zillacore cron add -s "next monday at 3pm" -p marketplace "Weekly planning session"
684
+
685
+ # One-time schedules (ISO8601)
686
+ zillacore cron add -s "2026-02-27T09:00:00-05:00" -p marketplace "Specific deadline reminder"
687
+
688
+ # Recurring with repeat limit
689
+ zillacore cron add -s "0 9 * * *" -r 7 -p marketplace "Daily reminder for 7 days"
690
+
691
+ # Discord forum channel posting
692
+ zillacore cron add -s "@daily" -p marketplace -d 1234567890 -t "Daily Standup" "Post standup"
693
+ ```
694
+
695
+ Flags:
696
+
697
+ - `-s` / `--schedule` — cron expression, shorthand, natural language, or ISO8601 timestamp
698
+ - `-p` / `--project` — project key (required)
699
+ - `-a` / `--agent` — agent name (defaults to `$AI_AGENT_NAME`, ignored in script mode)
700
+ - `-m` / `--model` — model override (`opus`, `sonnet`, `haiku`, `auto`, ignored in script mode)
701
+ - `-d` / `--discord` — Discord channel ID to post the response to
702
+ - `-t` / `--title` — forum post title (for forum channels)
703
+ - `-r` / `--repeat` — number of times to repeat (auto-disables after limit)
704
+ - `--script` — path to script (enables script mode, mutually exclusive with prompt)
705
+
706
+ ### Script Mode
707
+
708
+ Script mode runs a script directly without dispatching an agent. This is useful for:
709
+
710
+ - Token savings (no LLM calls)
711
+ - Simple data aggregation and reporting
712
+ - Running existing shell scripts on a schedule
713
+
714
+ Requirements:
715
+
716
+ - Script must be executable (`chmod +x script.sh`)
717
+ - Script output (stdout) is captured and posted to Discord if `-d` is set
718
+ - Script runs in the project's repo directory
719
+ - No agent prompt or model selection needed
720
+
721
+ Example script (`~/.zillacore/scripts/daily-report.sh`):
722
+
723
+ ```bash
724
+ #!/bin/bash
725
+ echo "=== Daily Report ==="
726
+ fizzy card list --column done --all --pretty | jq -r '.data[] | "[#\(.number)] \(.title)"'
727
+ ```
728
+
729
+ ### Managing Jobs
730
+
731
+ ```bash
732
+ zillacore cron list # List all jobs with status and last run time
733
+ zillacore cron remove <id> # Remove a job
734
+ zillacore cron enable <id> # Enable a paused job
735
+ zillacore cron disable <id> # Pause a job without removing it
736
+ zillacore cron update <id> -s "42 13 * * 1-5" # Update schedule
737
+ zillacore cron update <id> -c "1234567890" # Update Discord channel
738
+ zillacore cron update <id> -t "New Title" # Update forum title
739
+ ```
740
+
741
+ ### Schedule Format
742
+
743
+ **Recurring (standard cron):**
744
+
745
+ ```
746
+ 0 9 * * 1-5 # 9am weekdays
747
+ 0 */4 * * * # Every 4 hours
748
+ 0 0 1 * * # First of every month
749
+ @daily # Midnight every day
750
+ @weekly # Midnight every Sunday
751
+ ```
752
+
753
+ **One-time (natural language):**
754
+
755
+ ```
756
+ tomorrow at 9am
757
+ in 30 minutes
758
+ in 2 hours
759
+ next monday at 3pm
760
+ ```
761
+
762
+ **One-time (ISO8601):**
763
+
764
+ ```
765
+ 2026-02-27T09:00:00-05:00
766
+ ```
767
+
768
+ One-time jobs are automatically disabled after execution. They remain in the job list with a `[COMPLETED]` marker and can be removed manually. Repeat-limited jobs auto-disable after reaching their execution count.
769
+
770
+ ### Discord Output
771
+
772
+ If `-d <channel-id>` is set, the output is posted to that Discord channel:
773
+
774
+ - **Agent mode**: Agent writes response to temp file, posted using agent's bot identity
775
+ - **Script mode**: Script stdout is captured and posted directly
776
+
777
+ If the target channel is a forum channel, a new forum post is created with a customizable title.
778
+
779
+ ## Web Dashboard
780
+
781
+ ZillaCore includes a web dashboard at `http://localhost:4567/dashboard` that shows active agent sessions, project status, and system health. Protected by a `dashboard_token` configured in `~/.zillacore/discord.json`.
782
+
783
+ ## Monitoring
784
+
785
+ ZillaCore includes a monitoring system that shows active agent sessions in your desktop status bar. A background daemon polls the server API and exposes state via a Unix socket that status bar plugins read from.
786
+
787
+ ### How It Works
788
+
789
+ ```
790
+ zillacore server → /api/status → monitor/daemon.rb → /tmp/zillacore-monitor.sock → xbar/waybar plugin
791
+ ```
792
+
793
+ The monitor daemon starts automatically with `zillacore server`. It polls `/api/status` every 2 seconds and serves the current state to any client connecting to the Unix socket.
794
+
795
+ ### Agent Display Config
796
+
797
+ Configure how agents appear in the status bar via `~/.zillacore/waybar.json`:
798
+
799
+ ```json
800
+ {
801
+ "agents": [
802
+ { "name": "Galen", "emoji": "🛠️", "color": "green" },
803
+ { "name": "GLaDOS", "emoji": "🤖", "color": "blue" },
804
+ { "name": "Kaylee", "emoji": "🔧", "color": "pink" }
805
+ ],
806
+ "default_emoji": "❓",
807
+ "schema_version": "1.0"
808
+ }
809
+ ```
810
+
811
+ ### macOS (xbar)
812
+
813
+ Requires [xbar](https://xbarapp.com) (free, formerly BitBar).
814
+
815
+ ```bash
816
+ ruby monitor/setup-xbar-plugin.rb # One-time setup (symlinks plugin)
817
+ # Restart xbar to activate
818
+ ```
819
+
820
+ When agents are active, their emojis appear in the menu bar. Click to see details (agent name, card/Discord context, elapsed time) and open log files.
821
+
822
+ ### Linux (Waybar)
823
+
824
+ ```bash
825
+ ruby monitor/setup-waybar-module.rb # One-time setup
826
+ omarchy restart waybar # Restart waybar
827
+ ```
828
+
829
+ See `docs/waybar-config.md` for detailed configuration.
830
+
831
+ ## User Identity Registry
832
+
833
+ ZillaCore maintains a centralized user identity registry at `~/.zillacore/users.json` that resolves identities across platforms (Discord, GitHub, Fizzy). This ensures agents know who they're talking to regardless of where the interaction happens.
834
+
835
+ ### Structure
836
+
837
+ ```json
838
+ {
839
+ "users": [
840
+ {
841
+ "canonical_name": "Adam Dalton",
842
+ "identities": {
843
+ "discord": { "username": "fladamd", "user_id": "832331260088287242" },
844
+ "github": { "username": "dalton" },
845
+ "fizzy": { "username": "adam-dalton" }
846
+ },
847
+ "aliases": ["Andy"],
848
+ "notes": "Primary user"
849
+ }
850
+ ],
851
+ "schema_version": "1.0"
852
+ }
853
+ ```
854
+
855
+ ### Usage
856
+
857
+ ```ruby
858
+ # Find by any identifier
859
+ user = find_user('fladamd')
860
+ name = canonical_name_for('fladamd') # => "Adam Dalton"
861
+
862
+ # Filter by type
863
+ humans = human_users
864
+ agents = ai_agents
865
+ ```
866
+
867
+ **API:**
868
+
869
+ ```bash
870
+ curl http://localhost:4567/api/users # All users
871
+ curl http://localhost:4567/api/users?filter=humans # Humans only
872
+ curl http://localhost:4567/api/users?filter=agents # AI agents only
873
+ curl http://localhost:4567/api/users/fladamd # Find by identifier
874
+ ```
875
+
876
+ The registry reloads automatically on every webhook and via `POST /api/reload`.
877
+
878
+ ## Worktree Management
879
+
880
+ When a card is assigned, ZillaCore creates a git worktree for the agent to work in. Two config files in the project root control how gitignored files are handled:
881
+
882
+ - **`.worktreeinclude`** — glob patterns for gitignored files to copy into the worktree (e.g. `.env`, config files)
883
+ - **`.worktreelink`** — glob patterns for gitignored directories to symlink instead of copy (e.g. `node_modules`, `vendor/bundle`)
884
+
885
+ After copying and symlinking, ZillaCore runs the project hook `.zillacore/worktree-setup` if it exists (see below).
886
+
887
+ ### Project Hooks
888
+
889
+ Projects can define lifecycle hooks as executable scripts in `.zillacore/` at the project root:
890
+
891
+ | Hook | When It Runs | Environment |
892
+ | ----------------- | ------------------------------------------- | ------------------------------------ |
893
+ | `worktree-setup` | After worktree creation + file sync | `WORKTREE_PATH` set to worktree dir |
894
+
895
+ Add hooks by creating executable scripts:
896
+
897
+ ```bash
898
+ # .zillacore/worktree-setup
899
+ #!/bin/bash
900
+ cd "$WORKTREE_PATH"
901
+ bundle install --quiet
902
+ ```
903
+
904
+ ## Zoho Mail
905
+
906
+ ZillaCore can receive Zoho Mail webhooks and route email notifications to Discord channels based on configurable rules.
907
+
908
+ ### Setup
909
+
910
+ Create `~/.zillacore/zoho.json`:
911
+
912
+ ```json
913
+ {
914
+ "hook_secret": null,
915
+ "default_discord_channel_id": "YOUR_DISCORD_CHANNEL_ID",
916
+ "notify_as": "threepio",
917
+ "rules": [
918
+ {
919
+ "label": "Item Sold",
920
+ "enabled": true,
921
+ "from_contains": "",
922
+ "subject_contains": "sold",
923
+ "body_contains": "",
924
+ "exclude_words": [],
925
+ "emoji": "💰",
926
+ "discord_channel_id": null,
927
+ "notify_as": null
928
+ }
929
+ ],
930
+ "fallback": {
931
+ "enabled": true,
932
+ "label": "Unmatched Email",
933
+ "emoji": "📬",
934
+ "exclude_words": [],
935
+ "discord_channel_id": null,
936
+ "notify_as": null
937
+ }
938
+ }
939
+ ```
940
+
941
+ Rules are matched in order against incoming emails (from address, subject, body). Each rule can override the Discord channel and which agent bot posts the notification. The fallback rule catches anything that doesn't match a specific rule.
942
+
943
+ The `hook_secret` is auto-captured from Zoho's initial handshake request — no manual configuration needed.
944
+
945
+ **Webhook URL:** `https://your-ngrok.ngrok-free.app/zoho`
946
+
947
+ **Requires:** Discord integration must be enabled (at least one agent with a `DISCORD_BOT_TOKEN`).
948
+
949
+ ## Version Check
950
+
951
+ On startup, ZillaCore checks if the local repo is behind `origin/master`. If it detects the local version is outdated, it logs a warning. This helps ensure agents are always running the latest code.
952
+
953
+ ## Self-Restart
954
+
955
+ When an agent works on the zillacore project itself, the server automatically queues a restart. A background monitor thread checks every 30 seconds — once all active agent sessions finish, it stops the current server and spawns a new one. This ensures code changes agents make to zillacore take effect without manual intervention, and no running sessions are interrupted.
956
+
957
+ ## CLI Reference
958
+
959
+ ### Server
960
+
961
+ ```bash
962
+ zillacore server # Start and tail logs (Ctrl+C to detach, server keeps running)
963
+ zillacore server --daemon # Background mode
964
+ zillacore stop # Stop
965
+ zillacore restart # Restart
966
+ zillacore status # Check if running
967
+ ```
968
+
969
+ To inspect logs, read the log file directly — don't use `zillacore logs` (it streams indefinitely):
970
+
971
+ ```bash
972
+ cat tmp/zillacore-server.log
973
+ tail -100 tmp/zillacore-server.log
974
+ ```
975
+
976
+ ### Projects
977
+
978
+ ```bash
979
+ zillacore register # Register current directory (interactive)
980
+ zillacore list # List all projects
981
+ zillacore projects default <key> # Set default project (fallback when no tags match)
982
+ zillacore show <key> # Show project config
983
+ zillacore unregister <key> # Remove a project
984
+ ```
985
+
986
+ ### Brain
987
+
988
+ ```bash
989
+ zillacore brain init [agent] # Initialize brain
990
+ zillacore brain status [agent] # Show brain status
991
+ zillacore brain search <query> # Search shared knowledge
992
+ zillacore brain search --persona <query> # Search agent persona
993
+ zillacore brain list # List everything
994
+ ```
995
+
996
+ ## Project Configuration
997
+
998
+ Projects are stored in `~/.zillacore/projects.json`:
999
+
1000
+ ```json
1001
+ {
1002
+ "marketplace": {
1003
+ "repo_path": "/home/you/Code/marketplace",
1004
+ "fizzy_tags": ["marketplace", "mp"],
1005
+ "github_repo": "yourorg/marketplace",
1006
+ "agent_cli": "kiro-cli",
1007
+ "agent_cli_args": "chat --trust-all-tools --no-interactive",
1008
+ "agent_model_flag": "--model",
1009
+ "agent_model": "auto",
1010
+ "allowed_models": {
1011
+ "opus": "claude-opus-4.6",
1012
+ "sonnet": "claude-sonnet-4.6",
1013
+ "haiku": "claude-haiku-4.5",
1014
+ "deepseek": "deepseek-3.2",
1015
+ "minimax": "minimax-m2.5",
1016
+ "minimax25": "minimax-m2.5",
1017
+ "minimax21": "minimax-m2.1",
1018
+ "qwen": "qwen3-coder-next",
1019
+ "auto": "auto"
1020
+ }
1021
+ }
1022
+ }
1023
+ ```
1024
+
1025
+ ### Model Selection
1026
+
1027
+ Override the default model per-dispatch:
1028
+
1029
+ - **Fizzy card tags:** Add `opus`, `sonnet`, `haiku`, `deepseek`, `minimax`, `minimax21`, or `qwen` as a tag on the card
1030
+ - **Inline syntax (Fizzy, Discord, GitHub):** Include `[opus]`, `[sonnet]`, `[haiku]`, `[deepseek]`, `[minimax]`, `[minimax21]`, or `[qwen]` in your comment/message
1031
+ - **Priority:** inline comment/message > card tags > project config > CLI default
1032
+
1033
+ Model keys are defined in the project's `allowed_models` config — you can add custom keys beyond the defaults.
1034
+
1035
+ ## Prompt Customization
1036
+
1037
+ Prompts are layered in `lib/zillacore/prompts.rb`:
1038
+
1039
+ | Layer | Constant | Included When |
1040
+ | --------- | ---------------------------- | ----------------------------------------------------------------------- |
1041
+ | Core | `PROMPT_CORE` | Every session — memory, brain, persona, reflection, communication rules |
1042
+ | Channel | `PROMPT_FIZZY_CHANNEL` | Fizzy sessions — HTML formatting, fizzy reactions, screenshots |
1043
+ | Channel | `PROMPT_DISCORD_CHANNEL` | Discord sessions — Discord markdown, response file, mention syntax |
1044
+ | Channel | `PROMPT_GITHUB_CHANNEL` | GitHub sessions — GFM formatting, `gh api` reactions |
1045
+ | Situation | `PROMPT_CARD_ASSIGNED`, etc. | Specific trigger type |
1046
+
1047
+ `render_prompt` composes: core + channel + situation. The `channel:` keyword defaults to `:fizzy`:
1048
+
1049
+ ```ruby
1050
+ render_prompt(PROMPT_CARD_ASSIGNED, vars, brain_context: ctx, agent_name: name) # Fizzy (default)
1051
+ render_prompt(PROMPT_DISCORD, vars, brain_context: ctx, agent_name: name, channel: :discord) # Discord
1052
+ render_prompt(PROMPT_GITHUB_PR_REVIEW, vars, brain_context: ctx, agent_name: name, channel: :github) # GitHub
1053
+ ```
1054
+
1055
+ | Placeholder | Description |
1056
+ | ---------------------------- | --------------------------------------------------------- |
1057
+ | `{{CARD_NUMBER}}` | Fizzy card number |
1058
+ | `{{CARD_TITLE}}` | Card title |
1059
+ | `{{BRANCH}}` | Git branch name |
1060
+ | `{{CARD_INTERNAL_ID}}` | Fizzy internal UUID |
1061
+ | `{{CARD_ID}}` | Card number or internal ID |
1062
+ | `{{CARD_AGENT}}` | Agent assigned to the card (cross-agent reviews) |
1063
+ | `{{COMMENT_CREATOR}}` | Comment author name |
1064
+ | `{{COMMENT_ID}}` | Comment ID |
1065
+ | `{{COMMENT_BODY}}` | Comment text |
1066
+ | `{{KNOWLEDGE_DIR}}` | Path to shared knowledge |
1067
+ | `{{MEMORY_DIR}}` | Path to agent's card memory (per-agent) |
1068
+ | `{{PERSONA_DIR}}` | Path to agent persona |
1069
+ | `{{PERSONA_COLLECTION}}` | qmd collection name for persona |
1070
+ | `{{AGENT_NAME}}` | Agent name |
1071
+ | `{{AGENT_ROSTER}}` | Formatted list of all agents with exact @mention spelling |
1072
+ | `{{DISCORD_USER}}` | Discord username (Discord only) |
1073
+ | `{{CHANNEL_NAME}}` | Discord channel name (Discord only) |
1074
+ | `{{MESSAGE_BODY}}` | Discord message content (Discord only) |
1075
+ | `{{PROJECT_CONTEXT}}` | Project info block (Discord only) |
1076
+ | `{{RESPONSE_FILE}}` | Path to write Discord response (Discord only) |
1077
+ | `{{DISCORD_MENTION_ROSTER}}` | Discord `<@ID>` mention mapping (Discord only) |
1078
+ | `{{PR_NUMBER}}` | GitHub PR number (GitHub only) |
1079
+ | `{{REVIEW_CONTEXT}}` | Formatted review comments (GitHub only) |
1080
+ | `{{WORKTREE_PATH}}` | Worktree directory path (GitHub only) |
1081
+
1082
+ ## API
1083
+
1084
+ ```bash
1085
+ curl http://localhost:4567/api/projects/marketplace # Show specific project
1086
+ curl http://localhost:4567/api/agents # List agents, roster with display names
1087
+ curl http://localhost:4567/api/users # List all users
1088
+ curl http://localhost:4567/api/users?filter=humans # List only human users
1089
+ curl http://localhost:4567/api/users?filter=agents # List only AI agents
1090
+ curl http://localhost:4567/api/users/fladamd # Find user by any identifier
1091
+ curl -X POST http://localhost:4567/api/reload # Reload projects + agent registry + user registry
1092
+ curl http://localhost:4567/api/brain # Brain status (default agent)
1093
+ curl http://localhost:4567/api/brain?agent=GLaDOS # Brain status for specific agent
1094
+ curl "http://localhost:4567/api/brain/search?q=ruby+style" # Search knowledge
1095
+ curl "http://localhost:4567/api/brain/search?q=tone&scope=persona&agent=Galen" # Search persona
1096
+ curl http://localhost:4567/api/card-index # Card duplicate detection index
1097
+ curl http://localhost:4567/api/dispatch-depth # Agent-to-agent loop prevention state
1098
+ curl http://localhost:4567/api/discord # Discord bot status and config
1099
+ curl "http://localhost:4567/api/gif?q=excited" # Search for GIFs (requires giphy_api_key)
1100
+ curl http://localhost:4567/api/cron # Cron jobs and thread status
1101
+ curl http://localhost:4567/api/logs # Read log files
1102
+ curl http://localhost:4567/api/status # Active agent sessions (used by monitor)
1103
+ ```
1104
+
1105
+ ## Development
1106
+
1107
+ Auto-restart on file changes:
1108
+
1109
+ ```bash
1110
+ ls zillacore receiver.rb lib/zillacore/*.rb lib/zillacore/handlers/*.rb | entr -r zillacore server
1111
+ ```
1112
+
1113
+ ## Troubleshooting
1114
+
1115
+ **No projects found:** `zillacore list` to check, `zillacore path` to find config directory.
1116
+
1117
+ **Card not matching a project:** Verify the Fizzy card has a tag matching `fizzy_tags` in the project config. If no tags match, the default project is used (set with `zillacore projects default`).
1118
+
1119
+ **Agent not dispatching:** Check that `~/.kiro/agents/<name>.json` exists for the agent. The receiver discovers agents by scanning that directory. For registry-only agents, ensure `"local": true` is set.
1120
+
1121
+ **Cross-agent mention ignored:** Both machines receive webhooks. Only the machine with the agent's kiro-cli config in `~/.kiro/agents/` (or `"local": true` in the registry) will dispatch it.
1122
+
1123
+ **Agent commenting as wrong user:** Check `~/.zillacore/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.
1124
+
1125
+ **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.
1126
+
1127
+ **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/zillacore/sessions.rb` if needed.
1128
+
1129
+ **Brain not working:** `zillacore brain status` to check. Make sure qmd is installed (`npm install -g @tobilu/qmd`) and `zillacore brain init` has been run for each agent.
1130
+
1131
+ **Brain sync not pushing:** Check that `~/.zillacore/brain/` is a git repo with a remote configured. Look for `[Brain] Push failed` in the server logs. The most common cause is an SSH key issue — make sure the machine can `git push` from that directory.
1132
+
1133
+ **Discord bot not connecting:** Check `zillacore discord status`. Common causes: invalid token (reset it in the Discord Developer Portal and re-register with `zillacore discord token`), Message Content intent not enabled, or the `websocket-client-simple` gem not installed.
1134
+
1135
+ **Discord bot connects but ignores messages:** The Message Content intent must be enabled in the Discord Developer Portal (Bot tab → Privileged Gateway Intents). Without it, the bot receives empty message content and silently drops every message.
1136
+
1137
+ **Discord bot responds but in the wrong project:** Check your channel mapping with `zillacore discord config`. Messages use the channel-specific mapping first, then fall back to `default_project`. Override per-message with `[project:name]` in your Discord message. Set the default with `zillacore discord default <project>`.
1138
+
1139
+ **Discord model override not working:** Make sure the project has `allowed_models` configured (check with `zillacore show <project>`). The tag must match a key in `allowed_models` — e.g. `[opus]` matches `"opus": "claude-opus-4.6"`. If no project is mapped to the channel, model overrides have nothing to look up against.
1140
+
1141
+ **Discord "unauthorized" reaction (🚫):** The user isn't in `authorized_user_ids` or doesn't have a role in `authorized_role_ids` in `~/.zillacore/discord.json`. Leave both arrays empty to allow everyone.
1142
+
1143
+ **Duplicate Discord dispatches:** Check `~/.zillacore/agents.json` for duplicate entries with the same bot token under different key formats (e.g. `sleeper-service` and `sleeper_service`). Keys are normalized to lowercase with hyphens — duplicates after normalization cause multiple gateway connections.
1144
+
1145
+ **Worktree cleanup:** Automatic on PR merge. Manual: `cd /path/to/worktree && gd` (see shell helpers below).
1146
+
1147
+ **Zoho emails not arriving in Discord:** Zoho integration requires Discord to be enabled (at least one agent with a `DISCORD_BOT_TOKEN`). Check `~/.zillacore/zoho.json` exists and rules are configured. The `hook_secret` is auto-captured from Zoho's initial handshake — if it's `null`, the webhook hasn't been triggered yet.
1148
+
1149
+ ### Shell Helpers (Optional)
1150
+
1151
+ ```bash
1152
+ # Add to ~/.bashrc or ~/.zshrc
1153
+ ga() {
1154
+ [[ -z "$1" ]] && { echo "Usage: ga [branch]"; return 1; }
1155
+ local branch="$1" base="$(basename "$PWD")" path="../${base}--${branch}"
1156
+ git worktree add -b "$branch" "$path"
1157
+ mise trust "$path" 2>/dev/null || asdf reshim 2>/dev/null || true
1158
+ cd "$path"
1159
+ }
1160
+
1161
+ gd() {
1162
+ gum confirm "Remove worktree and branch?" || return
1163
+ local worktree="$(basename "$PWD")" root="${worktree%%--*}" branch="${worktree#*--}"
1164
+ [[ "$root" != "$worktree" ]] && cd "../$root" && git worktree remove "$worktree" --force && git branch -D "$branch"
1165
+ }
1166
+ ```