wicked-brain 0.4.15 → 0.5.0
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.
- package/install.mjs +25 -0
- package/package.json +3 -2
- package/server/bin/wicked-brain-server.mjs +45 -7
- package/server/lib/bus.mjs +75 -0
- package/server/package.json +1 -1
- package/skills/wicked-brain-agent/SKILL.md +24 -0
- package/skills/wicked-brain-compile/SKILL.md +15 -1
- package/skills/wicked-brain-configure/SKILL.md +13 -1
- package/skills/wicked-brain-enhance/SKILL.md +15 -1
- package/skills/wicked-brain-forget/SKILL.md +13 -1
- package/skills/wicked-brain-init/SKILL.md +15 -1
- package/skills/wicked-brain-memory/SKILL.md +12 -0
- package/skills/wicked-brain-migrate/SKILL.md +13 -1
- package/skills/wicked-brain-query/SKILL.md +13 -1
- package/skills/wicked-brain-retag/SKILL.md +15 -1
- package/skills/wicked-brain-synonyms/SKILL.md +14 -0
package/install.mjs
CHANGED
|
@@ -126,6 +126,31 @@ if (installHooks) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// Register as a wicked-bus provider if bus is available
|
|
130
|
+
try {
|
|
131
|
+
const { openDb, resolveDbPath, register } = await import("wicked-bus");
|
|
132
|
+
const busDbPath = resolveDbPath();
|
|
133
|
+
const busDb = openDb(busDbPath);
|
|
134
|
+
try {
|
|
135
|
+
register(busDb, {
|
|
136
|
+
plugin: "wicked-brain",
|
|
137
|
+
role: "provider",
|
|
138
|
+
filter: "wicked.*",
|
|
139
|
+
});
|
|
140
|
+
console.log("\nwicked-bus: registered wicked-brain as provider");
|
|
141
|
+
} catch (err) {
|
|
142
|
+
// Already registered or other non-fatal issue
|
|
143
|
+
if (err.message && err.message.includes("UNIQUE")) {
|
|
144
|
+
console.log("\nwicked-bus: wicked-brain already registered as provider");
|
|
145
|
+
} else {
|
|
146
|
+
console.log(`\nwicked-bus: could not register (${err.message})`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
busDb.close();
|
|
150
|
+
} catch {
|
|
151
|
+
console.log("\nwicked-bus: not available (install wicked-bus to enable event integration)");
|
|
152
|
+
}
|
|
153
|
+
|
|
129
154
|
// Server binary is bundled — npx wicked-brain-server works automatically
|
|
130
155
|
// Skills reference it as: npx wicked-brain-server --brain {path} --port {port}
|
|
131
156
|
console.log("\nServer: bundled (use 'npx wicked-brain-server' to start)");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wicked-brain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Digital brain as skills for AI coding CLIs — no vector DB, no embeddings, no infrastructure",
|
|
6
6
|
"keywords": [
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"wicked-brain-server": "./server/bin/wicked-brain-server.mjs"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"better-sqlite3": "^12.0.0"
|
|
34
|
+
"better-sqlite3": "^12.0.0",
|
|
35
|
+
"wicked-bus": "^1.0.0"
|
|
35
36
|
},
|
|
36
37
|
"files": [
|
|
37
38
|
"install.mjs",
|
|
@@ -6,6 +6,7 @@ import { argv, pid, exit } from "node:process";
|
|
|
6
6
|
import { FileWatcher } from "../lib/file-watcher.mjs";
|
|
7
7
|
import { SqliteSearch } from "../lib/sqlite-search.mjs";
|
|
8
8
|
import { LspClient } from "../lib/lsp-client.mjs";
|
|
9
|
+
import { emitEvent, waitForBus } from "../lib/bus.mjs";
|
|
9
10
|
|
|
10
11
|
// Parse args
|
|
11
12
|
const args = argv.slice(2);
|
|
@@ -88,11 +89,38 @@ process.on("SIGINT", () => shutdown());
|
|
|
88
89
|
// Action dispatch
|
|
89
90
|
const actions = {
|
|
90
91
|
health: () => db.health(),
|
|
91
|
-
search: (p) =>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
search: (p) => {
|
|
93
|
+
const result = db.search(p);
|
|
94
|
+
emitEvent("wicked.search.executed", "brain.search", {
|
|
95
|
+
query: p.query, result_count: result.total_matches, brain_id: brainId,
|
|
96
|
+
});
|
|
97
|
+
return result;
|
|
98
|
+
},
|
|
99
|
+
federated_search: (p) => {
|
|
100
|
+
const result = db.federatedSearch(p);
|
|
101
|
+
emitEvent("wicked.search.executed", "brain.search", {
|
|
102
|
+
query: p.query, federated: true, brain_id: brainId,
|
|
103
|
+
});
|
|
104
|
+
return result;
|
|
105
|
+
},
|
|
106
|
+
index: (p) => {
|
|
107
|
+
db.index(p);
|
|
108
|
+
emitEvent("wicked.chunk.indexed", "brain.chunk", {
|
|
109
|
+
id: p.id, path: p.path, brain_id: brainId,
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
remove: (p) => {
|
|
113
|
+
db.remove(p.id);
|
|
114
|
+
emitEvent("wicked.chunk.removed", "brain.chunk", {
|
|
115
|
+
id: p.id, brain_id: brainId,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
reindex: (p) => {
|
|
119
|
+
db.reindex(p.docs);
|
|
120
|
+
emitEvent("wicked.brain.reindexed", "brain", {
|
|
121
|
+
doc_count: p.docs.length, brain_id: brainId,
|
|
122
|
+
});
|
|
123
|
+
},
|
|
96
124
|
backlinks: (p) => ({ links: db.backlinks(p.id) }),
|
|
97
125
|
forward_links: (p) => ({ links: db.forwardLinks(p.id) }),
|
|
98
126
|
stats: () => db.stats(),
|
|
@@ -125,7 +153,13 @@ const actions = {
|
|
|
125
153
|
access_log: (p) => db.accessLog(p.id),
|
|
126
154
|
recent_memories: (p) => ({ memories: db.recentMemories(p) }),
|
|
127
155
|
contradictions: () => ({ links: db.contradictions() }),
|
|
128
|
-
confirm_link: (p) =>
|
|
156
|
+
confirm_link: (p) => {
|
|
157
|
+
const result = db.confirmLink(p.source_id, p.target_path, p.verdict);
|
|
158
|
+
emitEvent("wicked.link.confirmed", "brain.link", {
|
|
159
|
+
source_id: p.source_id, target_path: p.target_path, verdict: p.verdict, brain_id: brainId,
|
|
160
|
+
});
|
|
161
|
+
return result;
|
|
162
|
+
},
|
|
129
163
|
link_health: () => db.linkHealth(),
|
|
130
164
|
tag_frequency: () => ({ tags: db.tagFrequency() }),
|
|
131
165
|
search_misses: (p) => ({ misses: db.searchMisses(p) }),
|
|
@@ -201,7 +235,11 @@ try {
|
|
|
201
235
|
console.error(`Warning: could not write port to config: ${err.message}`);
|
|
202
236
|
}
|
|
203
237
|
|
|
204
|
-
server.listen(port, () => {
|
|
238
|
+
server.listen(port, async () => {
|
|
205
239
|
console.log(`wicked-brain-server running on port ${port} (brain: ${brainId}, pid: ${pid})`);
|
|
206
240
|
watcher.start();
|
|
241
|
+
await waitForBus();
|
|
242
|
+
emitEvent("wicked.server.started", "brain.system", {
|
|
243
|
+
brain_id: brainId, port, pid,
|
|
244
|
+
});
|
|
207
245
|
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wicked-bus integration for wicked-brain-server.
|
|
3
|
+
*
|
|
4
|
+
* Emits events to the bus when the bus is available.
|
|
5
|
+
* Degrades gracefully — if wicked-bus is not installed or the DB
|
|
6
|
+
* is unreachable, events are silently dropped.
|
|
7
|
+
*
|
|
8
|
+
* @module lib/bus
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const DOMAIN = "wicked-brain";
|
|
12
|
+
|
|
13
|
+
let busEmit = null;
|
|
14
|
+
let busDb = null;
|
|
15
|
+
let busConfig = null;
|
|
16
|
+
let available = false;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Try to load wicked-bus at startup. If unavailable, all emit calls are no-ops.
|
|
20
|
+
*/
|
|
21
|
+
async function init() {
|
|
22
|
+
try {
|
|
23
|
+
const bus = await import("wicked-bus");
|
|
24
|
+
busConfig = bus.loadConfig();
|
|
25
|
+
const dbPath = bus.resolveDbPath();
|
|
26
|
+
busDb = bus.openDb(dbPath);
|
|
27
|
+
busEmit = bus.emit;
|
|
28
|
+
available = true;
|
|
29
|
+
} catch {
|
|
30
|
+
// wicked-bus not installed or not initialized — degrade silently
|
|
31
|
+
available = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Initialize on module load (non-blocking)
|
|
36
|
+
const ready = init();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Emit an event to the bus.
|
|
40
|
+
* Fire-and-forget — never throws, never blocks the caller.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} eventType - e.g. "wicked.chunk.indexed"
|
|
43
|
+
* @param {string} subdomain - e.g. "brain.chunk"
|
|
44
|
+
* @param {object} payload - event-specific data
|
|
45
|
+
*/
|
|
46
|
+
export function emitEvent(eventType, subdomain, payload) {
|
|
47
|
+
if (!available) return;
|
|
48
|
+
try {
|
|
49
|
+
busEmit(busDb, busConfig, {
|
|
50
|
+
event_type: eventType,
|
|
51
|
+
domain: DOMAIN,
|
|
52
|
+
subdomain,
|
|
53
|
+
payload,
|
|
54
|
+
});
|
|
55
|
+
} catch {
|
|
56
|
+
// Bus emit failed — degrade silently
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Whether the bus is available.
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
export function busAvailable() {
|
|
65
|
+
return available;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Wait for bus initialization to complete.
|
|
70
|
+
* Only needed if you must know availability before the first emit.
|
|
71
|
+
*/
|
|
72
|
+
export async function waitForBus() {
|
|
73
|
+
await ready;
|
|
74
|
+
return available;
|
|
75
|
+
}
|
package/server/package.json
CHANGED
|
@@ -58,6 +58,30 @@ context or produce incomplete results.
|
|
|
58
58
|
|
|
59
59
|
If the host CLI does not support subagent dispatch, fall back to inline execution — run the pipeline steps directly in the current context.
|
|
60
60
|
|
|
61
|
+
## Bus Events
|
|
62
|
+
|
|
63
|
+
When dispatching an agent, emit:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx wicked-bus emit \
|
|
67
|
+
--type "wicked.agent.dispatched" \
|
|
68
|
+
--domain "wicked-brain" \
|
|
69
|
+
--subdomain "brain.agent" \
|
|
70
|
+
--payload '{"agent":"{agent_name}","brain_id":"{brain_id}"}' 2>/dev/null || true
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The consolidate agent should also emit `wicked.brain.consolidated` on completion:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx wicked-bus emit \
|
|
77
|
+
--type "wicked.brain.consolidated" \
|
|
78
|
+
--domain "wicked-brain" \
|
|
79
|
+
--subdomain "brain" \
|
|
80
|
+
--payload '{"brain_id":"{brain_id}","archived":{N},"promoted":{M},"merged":{P}}' 2>/dev/null || true
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
84
|
+
|
|
61
85
|
## Cross-Platform Notes
|
|
62
86
|
|
|
63
87
|
- Agent definitions are portable markdown — they work on all platforms
|
|
@@ -215,7 +215,21 @@ Append to `{brain_path}/_meta/log.jsonl` for each article:
|
|
|
215
215
|
{"ts":"{ISO}","op":"write","path":"{article_path}","author":"llm:compile","content_hash":"{hash}"}
|
|
216
216
|
```
|
|
217
217
|
|
|
218
|
-
## Step 7:
|
|
218
|
+
## Step 7: Emit bus events
|
|
219
|
+
|
|
220
|
+
For each article written or updated, emit:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
npx wicked-bus emit \
|
|
224
|
+
--type "wicked.article.compiled" \
|
|
225
|
+
--domain "wicked-brain" \
|
|
226
|
+
--subdomain "brain.wiki" \
|
|
227
|
+
--payload '{"path":"{article_path}","brain_id":"{brain_id}","source_chunk_count":{N},"persona":"{synthesis_persona}"}' 2>/dev/null || true
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
231
|
+
|
|
232
|
+
## Step 8: Report
|
|
219
233
|
|
|
220
234
|
State how many articles were created/updated and what concepts they cover.
|
|
221
235
|
```
|
|
@@ -105,7 +105,19 @@ Digital brain: {brain_id} | {total} indexed items | {chunks} chunks, {wiki} wiki
|
|
|
105
105
|
- Capture non-obvious decisions, patterns, and gotchas with `wicked-brain:memory`
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
### Step 4:
|
|
108
|
+
### Step 4: Emit bus event
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx wicked-bus emit \
|
|
112
|
+
--type "wicked.config.updated" \
|
|
113
|
+
--domain "wicked-brain" \
|
|
114
|
+
--subdomain "brain.system" \
|
|
115
|
+
--payload '{"config_file":"{path}","platform":"{detected_platform}","brain_id":"{brain_id}"}' 2>/dev/null || true
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
119
|
+
|
|
120
|
+
### Step 5: Confirm
|
|
109
121
|
|
|
110
122
|
Report what was written and where:
|
|
111
123
|
- Config file: {path}
|
|
@@ -137,7 +137,21 @@ Only synthesize connections and summaries from what already exists.}
|
|
|
137
137
|
Index each new chunk via the server API.
|
|
138
138
|
Append to log.jsonl for each chunk written.
|
|
139
139
|
|
|
140
|
-
## Step 5:
|
|
140
|
+
## Step 5: Emit bus events
|
|
141
|
+
|
|
142
|
+
For each inferred chunk created, emit:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx wicked-bus emit \
|
|
146
|
+
--type "wicked.chunk.enhanced" \
|
|
147
|
+
--domain "wicked-brain" \
|
|
148
|
+
--subdomain "brain.chunk" \
|
|
149
|
+
--payload '{"path":"{chunk_path}","brain_id":"{brain_id}","topic":"{topic}","confidence":0.6}' 2>/dev/null || true
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
153
|
+
|
|
154
|
+
## Step 6: Report
|
|
141
155
|
|
|
142
156
|
State what gaps were identified and how many inferred chunks were created.
|
|
143
157
|
```
|
|
@@ -88,7 +88,19 @@ Append to `{brain_path}/_meta/log.jsonl`:
|
|
|
88
88
|
{"ts":"{ISO}","op":"memory_forget","path":"{path}","id":"{id}","mode":"{mode}","reason":"{reason}","author":"agent:forget"}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
### Step 6:
|
|
91
|
+
### Step 6: Emit bus event
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx wicked-bus emit \
|
|
95
|
+
--type "wicked.memory.archived" \
|
|
96
|
+
--domain "wicked-brain" \
|
|
97
|
+
--subdomain "brain.memory" \
|
|
98
|
+
--payload '{"path":"{path}","id":"{id}","mode":"{mode}","reason":"{reason}"}' 2>/dev/null || true
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
102
|
+
|
|
103
|
+
### Step 7: Report
|
|
92
104
|
|
|
93
105
|
Report: path, id, previous frontmatter type/tier (if memory), archive filename,
|
|
94
106
|
and whether index removal succeeded. Always surface the archive path so the
|
|
@@ -280,7 +280,21 @@ Invoke `wicked-brain:configure` to write routing instructions into the active
|
|
|
280
280
|
CLI's agent config (CLAUDE.md, GEMINI.md, etc.). This is what makes the brain
|
|
281
281
|
the default for search and exploration — do not skip this step.
|
|
282
282
|
|
|
283
|
-
### Step 10:
|
|
283
|
+
### Step 10: Emit bus event
|
|
284
|
+
|
|
285
|
+
If wicked-bus is available, emit an initialization event:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
npx wicked-bus emit \
|
|
289
|
+
--type "wicked.brain.initialized" \
|
|
290
|
+
--domain "wicked-brain" \
|
|
291
|
+
--subdomain "brain" \
|
|
292
|
+
--payload '{"brain_id":"{id}","brain_path":"{brain_path}","name":"{name}"}' 2>/dev/null || true
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
This is fire-and-forget — if the bus is not installed, the command silently fails.
|
|
296
|
+
|
|
297
|
+
### Step 11: Confirm
|
|
284
298
|
|
|
285
299
|
Tell the user:
|
|
286
300
|
"Brain `{name}` is ready at `{brain_path}` — {N} files ingested, {M} chunks indexed.
|
|
@@ -154,6 +154,18 @@ Append to `{brain_path}/_meta/log.jsonl`:
|
|
|
154
154
|
{"ts":"{ISO}","op":"memory_store","path":"memory/{safe_name}.md","type":"{type}","tier":"working","author":"agent:memory"}
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
### Step 7: Emit bus event
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npx wicked-bus emit \
|
|
161
|
+
--type "wicked.memory.stored" \
|
|
162
|
+
--domain "wicked-brain" \
|
|
163
|
+
--subdomain "brain.memory" \
|
|
164
|
+
--payload '{"path":"memory/{safe_name}.md","type":"{type}","tier":"working","brain_id":"{brain_id}"}' 2>/dev/null || true
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
168
|
+
|
|
157
169
|
## Recall Mode
|
|
158
170
|
|
|
159
171
|
### Progressive loading
|
|
@@ -246,7 +246,19 @@ Delete the flat `_meta/` directory:
|
|
|
246
246
|
|
|
247
247
|
The flat path is now a pure container with only `projects/` beneath it.
|
|
248
248
|
|
|
249
|
-
### Step 10:
|
|
249
|
+
### Step 10: Emit bus event
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
npx wicked-bus emit \
|
|
253
|
+
--type "wicked.schema.migrated" \
|
|
254
|
+
--domain "wicked-brain" \
|
|
255
|
+
--subdomain "brain.system" \
|
|
256
|
+
--payload '{"from":"{flat_path}","to":"{target_path}","brain_id":"{brain_id}","doc_count":{N}}' 2>/dev/null || true
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
260
|
+
|
|
261
|
+
### Step 11: Report
|
|
250
262
|
|
|
251
263
|
Tell the user:
|
|
252
264
|
|
|
@@ -177,7 +177,19 @@ Sources:
|
|
|
177
177
|
- {path}: {one-line description of what it contributed}
|
|
178
178
|
- {path}: {one-line description}
|
|
179
179
|
|
|
180
|
-
## Step 5:
|
|
180
|
+
## Step 5: Emit bus event
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npx wicked-bus emit \
|
|
184
|
+
--type "wicked.query.executed" \
|
|
185
|
+
--domain "wicked-brain" \
|
|
186
|
+
--subdomain "brain.query" \
|
|
187
|
+
--payload '{"question":"{question}","sources_found":{count},"brain_id":"{brain_id}"}' 2>/dev/null || true
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
191
|
+
|
|
192
|
+
## Step 6: Log search effectiveness
|
|
181
193
|
|
|
182
194
|
If evidence was insufficient to answer the question fully, append a
|
|
183
195
|
search-miss event to the brain's log:
|
|
@@ -83,7 +83,21 @@ If **dry_run**: report the file path, current tag count, and proposed new tags.
|
|
|
83
83
|
|
|
84
84
|
If not dry_run: update the `contains:` field in the YAML frontmatter in-place using the Edit tool. The server's file watcher will detect the change and re-index.
|
|
85
85
|
|
|
86
|
-
### Step 5:
|
|
86
|
+
### Step 5: Emit bus event
|
|
87
|
+
|
|
88
|
+
After all files are updated (not in dry_run mode), emit a single summary event:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx wicked-bus emit \
|
|
92
|
+
--type "wicked.tag.backfilled" \
|
|
93
|
+
--domain "wicked-brain" \
|
|
94
|
+
--subdomain "brain.chunk" \
|
|
95
|
+
--payload '{"files_updated":{N},"files_scanned":{total},"brain_id":"{brain_id}"}' 2>/dev/null || true
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
99
|
+
|
|
100
|
+
### Step 6: Summary
|
|
87
101
|
|
|
88
102
|
Report:
|
|
89
103
|
- Total files scanned
|
|
@@ -138,3 +138,17 @@ Ask: "Apply all, apply some (specify numbers), or cancel?"
|
|
|
138
138
|
|
|
139
139
|
Only write to `synonyms.json` after explicit user approval. Merge approved
|
|
140
140
|
suggestions with any existing entries using the same add-synonym logic above.
|
|
141
|
+
|
|
142
|
+
## Bus Events
|
|
143
|
+
|
|
144
|
+
After any write to `synonyms.json` (add, remove, or auto-suggest apply), emit:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npx wicked-bus emit \
|
|
148
|
+
--type "wicked.synonym.updated" \
|
|
149
|
+
--domain "wicked-brain" \
|
|
150
|
+
--subdomain "brain.taxonomy" \
|
|
151
|
+
--payload '{"operation":"{add|remove|auto-apply}","term":"{term}","brain_id":"{brain_id}"}' 2>/dev/null || true
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|