wicked-brain 0.4.15 → 0.6.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 +34 -9
- 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.6.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.
|
|
@@ -35,10 +35,12 @@ project root.
|
|
|
35
35
|
|
|
36
36
|
- **mode** (required): `store` or `recall`
|
|
37
37
|
- **content** (store mode): the memory content to store
|
|
38
|
-
- **type** (store mode, optional): `decision`, `pattern
|
|
38
|
+
- **type** (store mode, optional): `decision`, `pattern` (alias: `procedural`), `preference`, `gotcha`, or `discovery`. Auto-detected if omitted. `procedural` is an input alias only — storage frontmatter always writes `type: pattern`.
|
|
39
39
|
- **ttl_days** (store mode, optional): number of days before this memory expires. Defaults by type.
|
|
40
|
+
- **importance** (store mode, optional): `low`, `medium`, or `high`. Overrides the type default. Bands: low = 1-3, medium = 4-6, high = 7-10. Used to derive the initial tier (see Step 2b).
|
|
41
|
+
- **tier** (store mode, optional): explicit tier override (`working`, `episodic`, `semantic`). Takes precedence over `importance`.
|
|
40
42
|
- **query** (recall mode): search term for finding memories
|
|
41
|
-
- **filter_type** (recall mode, optional): filter by memory type
|
|
43
|
+
- **filter_type** (recall mode, optional): filter by memory type. `procedural` is normalized to `pattern`.
|
|
42
44
|
- **filter_tier** (recall mode, optional): filter by tier (`working`, `episodic`, `semantic`)
|
|
43
45
|
|
|
44
46
|
## Store Mode
|
|
@@ -47,11 +49,15 @@ project root.
|
|
|
47
49
|
|
|
48
50
|
If type is not provided, classify the content:
|
|
49
51
|
- Contains "decided", "chose", "will use", "going with" → `decision`
|
|
50
|
-
- Contains "pattern", "always", "tends to", "convention" → `pattern`
|
|
52
|
+
- Contains "pattern", "always", "tends to", "convention" → `pattern` (if the caller explicitly passed `type: procedural`, normalize to `pattern` before continuing)
|
|
51
53
|
- Contains "prefer", "like", "want", "should always" → `preference`
|
|
52
54
|
- Contains "watch out", "careful", "gotcha", "trap", "bug" → `gotcha`
|
|
53
55
|
- Otherwise → `discovery`
|
|
54
56
|
|
|
57
|
+
#### Type aliases
|
|
58
|
+
|
|
59
|
+
- `procedural` → `pattern` (normalize on input; storage always writes `type: pattern`)
|
|
60
|
+
|
|
55
61
|
### Step 2: Apply type defaults
|
|
56
62
|
|
|
57
63
|
| Type | Default importance | Default ttl_days |
|
|
@@ -62,7 +68,14 @@ If type is not provided, classify the content:
|
|
|
62
68
|
| gotcha | 5 | 30 |
|
|
63
69
|
| discovery | 4 | 14 |
|
|
64
70
|
|
|
65
|
-
Agent-provided overrides take precedence.
|
|
71
|
+
Agent-provided overrides take precedence. An explicit `importance` arg overrides the type default.
|
|
72
|
+
|
|
73
|
+
### Step 2b: Resolve initial tier from importance
|
|
74
|
+
|
|
75
|
+
- If `tier` is explicitly passed → use that, skip the rest.
|
|
76
|
+
- Else if `importance` is `high` (or numeric importance >= 7) → `semantic`
|
|
77
|
+
- Else if `importance` is `low` (or numeric importance <= 3) → `working`
|
|
78
|
+
- Else (medium, or 4-6) → `episodic`
|
|
66
79
|
|
|
67
80
|
### Step 3: Generate tags with synonym expansion
|
|
68
81
|
|
|
@@ -88,7 +101,7 @@ Write to `{brain_path}/memory/{safe_name}.md`:
|
|
|
88
101
|
```yaml
|
|
89
102
|
---
|
|
90
103
|
type: {detected or provided type}
|
|
91
|
-
tier:
|
|
104
|
+
tier: {resolved tier from Step 2b}
|
|
92
105
|
confidence: 0.5
|
|
93
106
|
importance: {from type defaults or override}
|
|
94
107
|
ttl_days: {from type defaults or override, null if permanent}
|
|
@@ -112,14 +125,14 @@ indexed_at: "{ISO 8601 timestamp}"
|
|
|
112
125
|
- **episodic**: Specific events or decisions from past sessions. Medium longevity. Use for "we decided X on date Y" or "this happened in project Z".
|
|
113
126
|
- **semantic**: Generalized patterns and facts extracted from experience. Permanent by default. Use for stable conventions, recurring patterns, and distilled knowledge that transcends any single session.
|
|
114
127
|
|
|
115
|
-
New memories
|
|
128
|
+
New memories start at the tier resolved from importance (default `episodic` for medium importance, `working` for low, `semantic` for high). Consolidation (wicked-brain:consolidate) still promotes them across tiers based on access frequency and age.
|
|
116
129
|
|
|
117
130
|
#### Complete example
|
|
118
131
|
|
|
119
132
|
```yaml
|
|
120
133
|
---
|
|
121
134
|
type: decision
|
|
122
|
-
tier:
|
|
135
|
+
tier: semantic
|
|
123
136
|
confidence: 0.9
|
|
124
137
|
importance: 7
|
|
125
138
|
ttl_days: null
|
|
@@ -151,9 +164,21 @@ The server's file watcher will auto-index this file.
|
|
|
151
164
|
Append to `{brain_path}/_meta/log.jsonl`:
|
|
152
165
|
|
|
153
166
|
```json
|
|
154
|
-
{"ts":"{ISO}","op":"memory_store","path":"memory/{safe_name}.md","type":"{type}","tier":"
|
|
167
|
+
{"ts":"{ISO}","op":"memory_store","path":"memory/{safe_name}.md","type":"{type}","tier":"{resolved tier}","author":"agent:memory"}
|
|
155
168
|
```
|
|
156
169
|
|
|
170
|
+
### Step 7: Emit bus event
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npx wicked-bus emit \
|
|
174
|
+
--type "wicked.memory.stored" \
|
|
175
|
+
--domain "wicked-brain" \
|
|
176
|
+
--subdomain "brain.memory" \
|
|
177
|
+
--payload '{"path":"memory/{safe_name}.md","type":"{type}","tier":"{resolved tier}","brain_id":"{brain_id}"}' 2>/dev/null || true
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Fire-and-forget — if the bus is not installed, silently skip.
|
|
181
|
+
|
|
157
182
|
## Recall Mode
|
|
158
183
|
|
|
159
184
|
### Progressive loading
|
|
@@ -175,7 +200,7 @@ consolidation. Use a consistent session_id for the entire conversation.
|
|
|
175
200
|
|
|
176
201
|
### Step 2: Filter results
|
|
177
202
|
|
|
178
|
-
Filter to paths starting with `memory/`. If filter_type or filter_tier provided, read frontmatter and filter accordingly.
|
|
203
|
+
Filter to paths starting with `memory/`. If filter_type or filter_tier provided, read frontmatter and filter accordingly. Normalize `filter_type: procedural` to `pattern` before matching, so it matches memories stored with `type: pattern`.
|
|
179
204
|
|
|
180
205
|
### Step 3: Apply tier weighting
|
|
181
206
|
|
|
@@ -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.
|