wicked-brain 0.4.13 → 0.4.15

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.13",
3
+ "version": "0.4.15",
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": [
@@ -36,26 +36,33 @@ export class FileWatcher {
36
36
  }
37
37
 
38
38
  start() {
39
+ const brainDirs = ["chunks", "wiki", "memory"];
40
+
39
41
  // Build initial hash map
40
- this.#scanAndHash("chunks");
41
- this.#scanAndHash("wiki");
42
- this.#scanAndHash("memory");
42
+ for (const dir of brainDirs) this.#scanAndHash(dir);
43
43
 
44
44
  // Watch directories
45
- for (const dir of ["chunks", "wiki", "memory"]) {
46
- const absDir = join(this.#brainPath, dir);
47
- if (!existsSync(absDir)) continue;
45
+ const unwatched = [];
46
+ for (const dir of brainDirs) {
47
+ if (!this.#tryWatch(dir)) unwatched.push(dir);
48
+ }
48
49
 
49
- try {
50
- const watcher = watch(absDir, { recursive: true }, (eventType, filename) => {
51
- if (!filename || !filename.endsWith(".md")) return;
52
- const relPath = normalizePath(`${dir}/${filename}`);
53
- this.#debounce(relPath, () => this.#handleChange(relPath));
54
- });
55
- this.#watchers.push(watcher);
56
- } catch {
57
- // recursive watch not supported on this platform (Linux) — fall back to polling
58
- }
50
+ // Retry directories that were missing at startup (check every 3s, stop after 30s)
51
+ if (unwatched.length > 0) {
52
+ const pending = new Set(unwatched);
53
+ let elapsed = 0;
54
+ const retryInterval = setInterval(() => {
55
+ elapsed += 3000;
56
+ for (const dir of pending) {
57
+ if (this.#tryWatch(dir)) {
58
+ this.#scanAndHash(dir);
59
+ pending.delete(dir);
60
+ }
61
+ }
62
+ if (pending.size === 0 || elapsed >= 30000) clearInterval(retryInterval);
63
+ }, 3000);
64
+ // Don't keep the process alive just for retries
65
+ retryInterval.unref();
59
66
  }
60
67
 
61
68
  // Watch registered project directories
@@ -84,6 +91,24 @@ export class FileWatcher {
84
91
  }
85
92
  }
86
93
 
94
+ /** Try to set up fs.watch for a brain subdirectory. Returns true on success. */
95
+ #tryWatch(dir) {
96
+ const absDir = join(this.#brainPath, dir);
97
+ if (!existsSync(absDir)) return false;
98
+ try {
99
+ const watcher = watch(absDir, { recursive: true }, (eventType, filename) => {
100
+ if (!filename || !filename.endsWith(".md")) return;
101
+ const relPath = normalizePath(`${dir}/${filename}`);
102
+ this.#debounce(relPath, () => this.#handleChange(relPath));
103
+ });
104
+ this.#watchers.push(watcher);
105
+ return true;
106
+ } catch {
107
+ // recursive watch not supported (Linux) — polling fallback handles it
108
+ return false;
109
+ }
110
+ }
111
+
87
112
  stop() {
88
113
  for (const w of this.#watchers) w.close();
89
114
  this.#watchers = [];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain-server",
3
- "version": "0.4.13",
3
+ "version": "0.4.15",
4
4
  "type": "module",
5
5
  "description": "SQLite FTS5 search server for wicked-brain digital knowledge bases",
6
6
  "keywords": [
@@ -120,24 +120,59 @@ directory, not a project subdirectory), push back: explain the per-project
120
120
  convention and suggest `~/.wicked-brain/projects/{project_name}` instead.
121
121
  Only accept the flat path if the user explicitly insists.
122
122
 
123
- ### Step 2: Check for existing brains
123
+ ### The `projects/` infix is mandatory — do not invent alternate layouts
124
124
 
125
- #### 2a: Detect a flat brain at the parent path
125
+ The ONLY valid brain path under the default container is
126
+ `~/.wicked-brain/projects/{name}/`. Common misreadings to avoid:
127
+
128
+ - ❌ `~/.wicked-brain/{name}/` — missing the `projects/` segment. Not valid.
129
+ - ❌ `~/.{name}-brain/` — sibling directory. Not the convention.
130
+ - ❌ `~/.wicked-brain/projects/{name}/projects/{subname}/` — nested brains. Not supported.
126
131
 
127
- If `~/.wicked-brain/brain.json` exists (note: `brain.json` at the flat parent
128
- path, NOT inside a `projects/` subdirectory), this is a legacy flat brain from
129
- before v0.4.7. Stop and tell the user:
132
+ If the user asks "why not `~/.wicked-brain/{name}`?" the answer is: the `projects/`
133
+ segment is a deliberate namespace. The parent `~/.wicked-brain/` is a container
134
+ that may hold other metadata (linked brain indexes, federation config, etc.),
135
+ and every brain lives under `projects/` to keep that clean.
130
136
 
131
- "I found an existing flat brain at `~/.wicked-brain/`. The current layout puts
132
- each project under `~/.wicked-brain/projects/{name}/`. I can migrate the flat
133
- brain with `wicked-brain:migrate` before creating the new one. Migrate now?"
137
+ Do not improvise alternate layouts when you hit an obstacle. If the documented
138
+ path is occupied, invoke the migration flow (Step 2a) — do not pick a sibling
139
+ directory or a nested path.
134
140
 
135
- If yes, invoke `wicked-brain:migrate` with `flat_path=~/.wicked-brain` and
136
- wait for it to complete before continuing.
141
+ ### Step 2: Check for existing brains
142
+
143
+ #### 2a: Detect a flat brain at the parent path
137
144
 
138
- If no, confirm the user wants to keep the flat brain and proceed (accept the
139
- tradeoff: the new project brain will live under `projects/` but the old one
140
- stays at the flat path).
145
+ **Check first, before anything else:** does `~/.wicked-brain/brain.json` exist?
146
+ (That is `brain.json` at the flat parent path, NOT inside `projects/`.) If yes,
147
+ this is a legacy flat brain from before v0.4.7 and you **MUST STOP** the init
148
+ flow and resolve it before continuing.
149
+
150
+ Read `~/.wicked-brain/brain.json` to find the existing brain's name, then tell
151
+ the user exactly this (substituting the real name):
152
+
153
+ > "Heads up — there's already a legacy flat brain at `~/.wicked-brain/` named
154
+ > `{existing_name}`. The current layout puts each project under
155
+ > `~/.wicked-brain/projects/{name}/`, so I can't just create a new brain at the
156
+ > parent path without clobbering it. You have two options:
157
+ >
158
+ > 1. **Migrate** the existing `{existing_name}` brain to
159
+ > `~/.wicked-brain/projects/{existing_name}/` first, then create the new
160
+ > `{project_name}` brain alongside it. Recommended.
161
+ > 2. **Keep** the flat `{existing_name}` brain where it is and create the new
162
+ > brain at `~/.wicked-brain/projects/{project_name}/`. This works but leaves
163
+ > the old brain in a legacy layout.
164
+ >
165
+ > Which do you want?"
166
+
167
+ Do NOT propose any other options. Do NOT suggest a sibling directory like
168
+ `~/.wicked-bus-brain`. Do NOT suggest nesting inside the existing brain. Do NOT
169
+ proceed with overwriting `~/.wicked-brain/brain.json` under any circumstances.
170
+
171
+ If the user picks option 1, invoke `wicked-brain:migrate` with
172
+ `flat_path=~/.wicked-brain` and wait for it to complete before continuing.
173
+
174
+ If the user picks option 2, proceed with the new brain at
175
+ `~/.wicked-brain/projects/{project_name}/` — the flat brain stays untouched.
141
176
 
142
177
  #### 2b: Check target path
143
178
 
@@ -154,17 +189,18 @@ Use your native Write tool to create these directories (write a `.gitkeep` place
154
189
  - `{brain_path}/chunks/inferred`
155
190
  - `{brain_path}/wiki/concepts`
156
191
  - `{brain_path}/wiki/topics`
192
+ - `{brain_path}/memory`
157
193
  - `{brain_path}/_meta`
158
194
 
159
195
  Shell equivalents if needed:
160
196
  ```bash
161
197
  # macOS/Linux
162
198
  mkdir -p {brain_path}/raw {brain_path}/chunks/extracted {brain_path}/chunks/inferred \
163
- {brain_path}/wiki/concepts {brain_path}/wiki/topics {brain_path}/_meta
199
+ {brain_path}/wiki/concepts {brain_path}/wiki/topics {brain_path}/memory {brain_path}/_meta
164
200
  ```
165
201
  ```powershell
166
202
  # Windows PowerShell
167
- New-Item -ItemType Directory -Force -Path "{brain_path}\raw","{brain_path}\chunks\extracted","{brain_path}\chunks\inferred","{brain_path}\wiki\concepts","{brain_path}\wiki\topics","{brain_path}\_meta"
203
+ New-Item -ItemType Directory -Force -Path "{brain_path}\raw","{brain_path}\chunks\extracted","{brain_path}\chunks\inferred","{brain_path}\wiki\concepts","{brain_path}\wiki\topics","{brain_path}\memory","{brain_path}\_meta"
168
204
  ```
169
205
 
170
206
  ### Step 4: Write brain.json
@@ -17,7 +17,7 @@ Store and recall experiential learnings in the brain's memory system.
17
17
  - Uses `curl` for server API calls (available on Windows 10+, macOS, Linux)
18
18
  - File writes use agent-native tools (Write/Edit), not shell commands
19
19
  - Path separator: always use forward slashes in `contains:` and `path` fields
20
- - Brain path default: `~/.wicked-brain` (macOS/Linux), `%USERPROFILE%\.wicked-brain` (Windows)
20
+ - Brain path default: `~/.wicked-brain/projects/{project-name}` (macOS/Linux), `%USERPROFILE%\.wicked-brain\projects\{project-name}` (Windows)
21
21
 
22
22
  ## Config
23
23