wicked-brain 0.4.8 → 0.4.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
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": [
@@ -96,6 +96,7 @@ const actions = {
96
96
  backlinks: (p) => ({ links: db.backlinks(p.id) }),
97
97
  forward_links: (p) => ({ links: db.forwardLinks(p.id) }),
98
98
  stats: () => db.stats(),
99
+ memory_stats: () => db.memoryStats(),
99
100
  candidates: (p) => ({ candidates: db.candidates(p) }),
100
101
  symbols: async (p) => {
101
102
  // Prefer LSP workspace symbols (structured, language-aware)
@@ -421,7 +421,51 @@ export class SqliteSearch {
421
421
  // in-memory or inaccessible
422
422
  }
423
423
 
424
- return { total, chunks, wiki, memory, last_indexed, db_size };
424
+ return {
425
+ total,
426
+ chunks,
427
+ wiki,
428
+ memory,
429
+ memory_breakdown: this.memoryStats(),
430
+ last_indexed,
431
+ db_size,
432
+ };
433
+ }
434
+
435
+ /**
436
+ * Breakdown of memory documents by type, tier, and age.
437
+ * Parses frontmatter from `memory/%` rows. Missing fields count as "unknown".
438
+ * Age buckets: <1d, 1-7d, 7-30d, 30-90d, >90d.
439
+ */
440
+ memoryStats() {
441
+ const rows = this.#db.prepare(`
442
+ SELECT frontmatter, indexed_at FROM documents WHERE path LIKE 'memory/%'
443
+ `).all();
444
+
445
+ const by_type = {};
446
+ const by_tier = {};
447
+ const by_age = { "<1d": 0, "1-7d": 0, "7-30d": 0, "30-90d": 0, ">90d": 0 };
448
+ const now = Date.now();
449
+ const DAY = 86400000;
450
+
451
+ for (const row of rows) {
452
+ const fm = row.frontmatter || "";
453
+ const typeMatch = fm.match(/^type:\s*(\S+)/m);
454
+ const tierMatch = fm.match(/^tier:\s*(\S+)/m);
455
+ const type = typeMatch ? typeMatch[1].replace(/["']/g, "") : "unknown";
456
+ const tier = tierMatch ? tierMatch[1].replace(/["']/g, "") : "unknown";
457
+ by_type[type] = (by_type[type] ?? 0) + 1;
458
+ by_tier[tier] = (by_tier[tier] ?? 0) + 1;
459
+
460
+ const age = now - (row.indexed_at ?? now);
461
+ if (age < DAY) by_age["<1d"]++;
462
+ else if (age < 7 * DAY) by_age["1-7d"]++;
463
+ else if (age < 30 * DAY) by_age["7-30d"]++;
464
+ else if (age < 90 * DAY) by_age["30-90d"]++;
465
+ else by_age[">90d"]++;
466
+ }
467
+
468
+ return { total: rows.length, by_type, by_tier, by_age };
425
469
  }
426
470
 
427
471
  health() {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain-server",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "type": "module",
5
5
  "description": "SQLite FTS5 search server for wicked-brain digital knowledge bases",
6
6
  "keywords": [
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: wicked-brain:forget
3
+ description: |
4
+ Archive or delete a memory (or any indexed document) by id or path.
5
+ Removes the document from the FTS index and renames the file with an
6
+ `.archived-{timestamp}` suffix so the data is recoverable.
7
+
8
+ Use when: "forget this memory", "archive this", "drop this decision",
9
+ "remove from brain", "brain forget".
10
+ ---
11
+
12
+ # wicked-brain:forget
13
+
14
+ Archive or hard-delete a memory by id or path. Wraps the server `remove` action
15
+ and the archive-rename convention used by wicked-brain:agent dispatch consolidate.
16
+
17
+ ## Cross-Platform Notes
18
+
19
+ - Uses `curl` for server API calls (Windows 10+, macOS, Linux)
20
+ - Uses agent-native Read/Bash tools for file ops — no Unix-only shell features
21
+ - Paths always use forward slashes
22
+
23
+ ## Config
24
+
25
+ Read `_meta/config.json` for brain path and server port.
26
+ If it doesn't exist, trigger wicked-brain:init.
27
+
28
+ ## Parameters
29
+
30
+ - **id** (required if `path` not given): document id as returned by search
31
+ - **path** (required if `id` not given): path relative to the brain root (e.g. `memory/jwt-decision.md`)
32
+ - **mode** (optional, default `archive`): `archive` (rename file + remove from index, recoverable) or `delete` (rename + remove + final deletion is still left to the user — this skill never unlinks files)
33
+ - **reason** (optional): short string recorded in the log for auditability
34
+
35
+ This skill never hard-deletes a file. `delete` mode still renames with
36
+ `.archived-{timestamp}` — actual `rm` is a human decision.
37
+
38
+ ## Process
39
+
40
+ ### Step 1: Resolve id and path
41
+
42
+ If only `id` is given, find the path by calling search or reading the id (ids
43
+ are of the form `{path}` or `{path}::{fragment}` in this brain). If only `path`
44
+ is given, the id is normally the same string for top-level documents.
45
+
46
+ ### Step 2: Confirm the document exists
47
+
48
+ Read the file at `{brain_path}/{path}` to verify it is present and (if it is a
49
+ memory) inspect its frontmatter so the log entry can record type/tier.
50
+
51
+ ### Step 3: Remove from index
52
+
53
+ ```bash
54
+ curl -s -X POST http://localhost:{port}/api \
55
+ -H "Content-Type: application/json" \
56
+ -d '{"action":"remove","params":{"id":"{id}"}}'
57
+ ```
58
+
59
+ ### Step 4: Archive the file
60
+
61
+ Rename the file in-place with an `.archived-{unix-ms}` suffix.
62
+
63
+ macOS / Linux:
64
+ ```bash
65
+ mv "{brain_path}/{path}" "{brain_path}/{path}.archived-$(date +%s)"
66
+ ```
67
+
68
+ Windows (PowerShell):
69
+ ```powershell
70
+ Rename-Item "{brain_path}/{path}" "{path}.archived-$([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())"
71
+ ```
72
+
73
+ Prefer the agent-native Bash tool on the current platform; both forms produce a
74
+ recoverable archive marker.
75
+
76
+ ### Step 5: Log the forget event
77
+
78
+ Append to `{brain_path}/_meta/log.jsonl`:
79
+
80
+ ```json
81
+ {"ts":"{ISO}","op":"memory_forget","path":"{path}","id":"{id}","mode":"{mode}","reason":"{reason}","author":"agent:forget"}
82
+ ```
83
+
84
+ ### Step 6: Report
85
+
86
+ Report: path, id, previous frontmatter type/tier (if memory), archive filename,
87
+ and whether index removal succeeded. Always surface the archive path so the
88
+ user can restore it by renaming back.
89
+
90
+ ## Recovery
91
+
92
+ To restore an archived memory, rename the `.archived-{ts}` file back to its
93
+ original name. The file watcher will pick it up and re-index automatically.
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: wicked-brain:review
3
+ description: |
4
+ Browse stored memories with filters on type, tier, and recency. Read-only —
5
+ use wicked-brain:forget to archive or wicked-brain:agent dispatch consolidate
6
+ to promote.
7
+
8
+ Use when: "review my memories", "browse decisions", "what have I stored",
9
+ "list recent gotchas", "brain review".
10
+ ---
11
+
12
+ # wicked-brain:review
13
+
14
+ Filtered browse over the memory store. Combines the server `memory_stats` and
15
+ `recent_memories` actions with agent-side frontmatter filtering to render a
16
+ compact, navigable list.
17
+
18
+ ## Cross-Platform Notes
19
+
20
+ - Uses `curl` for server API calls (Windows 10+, macOS, Linux)
21
+ - File reads use the agent-native Read tool
22
+ - Paths always use forward slashes
23
+
24
+ ## Config
25
+
26
+ Read `_meta/config.json` for brain path and server port.
27
+ If it doesn't exist, trigger wicked-brain:init.
28
+
29
+ ## Parameters
30
+
31
+ - **filter_type** (optional): `decision`, `pattern`, `preference`, `gotcha`, or `discovery`
32
+ - **filter_tier** (optional): `working`, `episodic`, or `semantic`
33
+ - **days** (optional, default 30): only include memories indexed within this many days
34
+ - **limit** (optional, default 20): max results
35
+ - **depth** (optional, default 0): 0=frontmatter only, 1=+summary line, 2=full content
36
+
37
+ ## Process
38
+
39
+ ### Step 1: Fetch breakdown
40
+
41
+ Start with the aggregate view so the user can see the landscape before the list:
42
+
43
+ ```bash
44
+ curl -s -X POST http://localhost:{port}/api \
45
+ -H "Content-Type: application/json" \
46
+ -d '{"action":"memory_stats"}'
47
+ ```
48
+
49
+ Render `total`, `by_type`, `by_tier`, `by_age` as a one-line header.
50
+
51
+ ### Step 2: Fetch candidates
52
+
53
+ ```bash
54
+ curl -s -X POST http://localhost:{port}/api \
55
+ -H "Content-Type: application/json" \
56
+ -d '{"action":"recent_memories","params":{"days":{days},"limit":{limit * 3}}}'
57
+ ```
58
+
59
+ Over-fetch by 3x so agent-side type/tier filtering still returns a useful page.
60
+
61
+ ### Step 3: Filter
62
+
63
+ For each returned memory, parse its frontmatter. Drop any memory whose `type`
64
+ or `tier` does not match `filter_type` / `filter_tier` when those parameters
65
+ are set. Stop once `limit` matches are collected.
66
+
67
+ ### Step 4: Render
68
+
69
+ For each matching memory, render at the requested depth:
70
+
71
+ - **Depth 0**: `{path} — type={type} tier={tier} importance={importance} age={age}`
72
+ - **Depth 1**: depth 0 line + first 3 lines of content
73
+ - **Depth 2**: depth 0 line + full content
74
+
75
+ Age is derived from `indexed_at` relative to now (`Xd` / `Xh`).
76
+
77
+ ### Step 5: Suggest next actions
78
+
79
+ After the list, suggest one of:
80
+ - `wicked-brain:forget path=…` to archive a specific entry
81
+ - `wicked-brain:agent dispatch consolidate` to promote patterns and drop expired entries
82
+ - `wicked-brain:retag` if many entries have thin `contains:` arrays
83
+
84
+ ## Notes
85
+
86
+ - Review is read-only. It never mutates the index or files.
87
+ - For semantic search (content keywords) use `wicked-brain:search` or the
88
+ `recall` mode of `wicked-brain:memory` — review is for browsing by metadata.
89
+ - If no filters are given and `days=30`, this is effectively "show me what I
90
+ have been remembering lately, grouped by type and tier".