wicked-brain 0.4.6 → 0.4.8

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/README.md CHANGED
@@ -127,6 +127,7 @@ Every operation uses **progressive loading** — the agent never pulls more than
127
127
  | Skill | What it does |
128
128
  |---|---|
129
129
  | `wicked-brain:init` | Set up a new brain — creates structure, starts the server, and ingests your project in one shot |
130
+ | `wicked-brain:migrate` | Migrate a legacy flat brain at `~/.wicked-brain/` into the per-project layout |
130
131
  | `wicked-brain:ingest` | Add source files — text extracted deterministically, binary docs read via LLM vision |
131
132
  | `wicked-brain:search` | Parallel search across your brain and linked brains |
132
133
  | `wicked-brain:read` | Progressive loading: depth 0 (stats), depth 1 (summary), depth 2 (full content) |
@@ -159,10 +160,33 @@ Brains can link to other brains. A personal research brain can reference a team
159
160
 
160
161
  When you search, wicked-brain dispatches parallel search agents across all accessible brains and merges the results. Access control is filesystem permissions — if you can read the directory, you can search it.
161
162
 
162
- ## What's on Disk
163
+ ## Per-Project Brains
164
+
165
+ **Each project gets its own brain.** `wicked-brain:init` creates a brain under
166
+ `~/.wicked-brain/projects/{project-name}/` by default, where `{project-name}`
167
+ is the basename of your current working directory. This keeps unrelated
168
+ codebases, clients, and research domains from crowding a single index — and
169
+ makes federated search across projects meaningful.
163
170
 
164
171
  ```
165
172
  ~/.wicked-brain/
173
+ projects/
174
+ my-app/ # one brain per project
175
+ client-site/
176
+ personal-research/
177
+ ```
178
+
179
+ Multiple agents can work on different projects simultaneously without stepping
180
+ on each other. A supervising "meta-brain" can watch `~/.wicked-brain/projects/*`
181
+ and federate queries across all of them via `brain.json` links.
182
+
183
+ If you really want one brain for everything, you can pass a custom path to
184
+ `wicked-brain:init` — but you'll fight the index as it grows.
185
+
186
+ ## What's on Disk
187
+
188
+ ```
189
+ ~/.wicked-brain/projects/{project-name}/
166
190
  brain.json # Identity and brain links
167
191
  raw/ # Your source files
168
192
  chunks/
@@ -171,6 +195,7 @@ When you search, wicked-brain dispatches parallel search agents across all acces
171
195
  wiki/ # Synthesized articles with [[backlinks]]
172
196
  _meta/
173
197
  log.jsonl # Append-only event log
198
+ config.json # Server port, source path
174
199
  .brain.db # SQLite search index (auto-managed)
175
200
  ```
176
201
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
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": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain-server",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "type": "module",
5
5
  "description": "SQLite FTS5 search server for wicked-brain digital knowledge bases",
6
6
  "keywords": [
@@ -18,9 +18,34 @@ Commands in this skill work on macOS, Linux, and Windows. When a command has
18
18
  platform differences, alternatives are shown. Your native tools (Read, Write,
19
19
  Grep, Glob) work everywhere — prefer them over shell commands when possible.
20
20
 
21
+ ## Per-project brains (important)
22
+
23
+ **Each project gets its own brain under `~/.wicked-brain/projects/{project-name}/`.**
24
+ Do NOT initialize a single monolithic brain at `~/.wicked-brain/` — that overwhelms
25
+ the index, mixes unrelated content across clients/codebases, and makes federated
26
+ search useless.
27
+
28
+ The structure is:
29
+ ```
30
+ ~/.wicked-brain/ # parent directory (not a brain)
31
+ projects/
32
+ my-app/ # one brain per project
33
+ brain.json
34
+ chunks/
35
+ _meta/
36
+ client-site/ # another project's brain
37
+ brain.json
38
+ ...
39
+ ```
40
+
41
+ Project name defaults to the basename of the current working directory
42
+ (lowercase, hyphens for spaces). A supervising "meta-brain" agent can watch
43
+ `~/.wicked-brain/projects/*` and federate across all of them via
44
+ `brain.json` links.
45
+
21
46
  For the brain path default:
22
- - macOS/Linux: ~/.wicked-brain
23
- - Windows: %USERPROFILE%\.wicked-brain
47
+ - macOS/Linux: `~/.wicked-brain/projects/{project_name}`
48
+ - Windows: `%USERPROFILE%\.wicked-brain\projects\{project_name}`
24
49
 
25
50
  ## When to use
26
51
 
@@ -31,16 +56,41 @@ For the brain path default:
31
56
 
32
57
  ### Step 1: Ask the user
33
58
 
34
- Ask these questions (provide defaults):
59
+ Compute the default project name from the current working directory basename
60
+ (lowercase, replace non-alphanumerics with hyphens). Then ask:
61
+
62
+ 1. "What should this project's brain be called?" — Default: `{cwd_basename}`
63
+ 2. "Where should it live?" — Default:
64
+ - macOS/Linux: `~/.wicked-brain/projects/{project_name}`
65
+ - Windows: `%USERPROFILE%\.wicked-brain\projects\{project_name}`
66
+
67
+ If the user supplies a path that is exactly `~/.wicked-brain` (the parent
68
+ directory, not a project subdirectory), push back: explain the per-project
69
+ convention and suggest `~/.wicked-brain/projects/{project_name}` instead.
70
+ Only accept the flat path if the user explicitly insists.
71
+
72
+ ### Step 2: Check for existing brains
35
73
 
36
- 1. "Where should your brain live?"
37
- - Default (macOS/Linux): `~/.wicked-brain`
38
- - Default (Windows): `%USERPROFILE%\.wicked-brain`
39
- 2. "What should this brain be called?" — Default: directory name
74
+ #### 2a: Detect a flat brain at the parent path
40
75
 
41
- ### Step 2: Check for existing brain
76
+ If `~/.wicked-brain/brain.json` exists (note: `brain.json` at the flat parent
77
+ path, NOT inside a `projects/` subdirectory), this is a legacy flat brain from
78
+ before v0.4.7. Stop and tell the user:
42
79
 
43
- If `{brain_path}/_meta/config.json` already exists, tell the user:
80
+ "I found an existing flat brain at `~/.wicked-brain/`. The current layout puts
81
+ each project under `~/.wicked-brain/projects/{name}/`. I can migrate the flat
82
+ brain with `wicked-brain:migrate` before creating the new one. Migrate now?"
83
+
84
+ If yes, invoke `wicked-brain:migrate` with `flat_path=~/.wicked-brain` and
85
+ wait for it to complete before continuing.
86
+
87
+ If no, confirm the user wants to keep the flat brain and proceed (accept the
88
+ tradeoff: the new project brain will live under `projects/` but the old one
89
+ stays at the flat path).
90
+
91
+ #### 2b: Check target path
92
+
93
+ If `{brain_path}/_meta/config.json` already exists at the chosen target, tell the user:
44
94
  "A brain already exists at `{brain_path}`. Do you want to re-initialize it (keeps existing chunks) or pick a different path?"
45
95
 
46
96
  Stop and wait for their answer before continuing.
@@ -92,9 +142,12 @@ Write to `{brain_path}/_meta/config.json`:
92
142
  }
93
143
  ```
94
144
 
95
- `server_port: 4242` is the *preferred* port. The server will find a free port starting
96
- from this value on startup and write the actual port back to this file. You do not
97
- need to find a free port manually.
145
+ `server_port: 4242` is the *preferred* starting port, not the guaranteed port.
146
+ When the server starts in Step 7, it probes from this value upward until it
147
+ finds a free port, then writes the **actual** port back to this same file.
148
+ If multiple project brains run at once, each gets a distinct port (4242, 4243,
149
+ 4244, ...). Always re-read `_meta/config.json` after the server starts to get
150
+ the real port — never hardcode `4242` in downstream calls.
98
151
 
99
152
  ### Step 6: Initialize the event log
100
153
 
@@ -109,7 +162,22 @@ The server will pick a free port and write it back to `_meta/config.json`.
109
162
  npx wicked-brain-server --brain {brain_path} &
110
163
  ```
111
164
 
112
- Wait for the health check to confirm it's up before continuing.
165
+ Do NOT pass `--port` unless the user specifies one let the server pick a
166
+ free port. After the process starts, **re-read `{brain_path}/_meta/config.json`**
167
+ to get the actual `server_port` the server bound to. Use that port for the
168
+ health check and all subsequent API calls.
169
+
170
+ Then health-check to confirm it's up before continuing:
171
+
172
+ ```bash
173
+ curl -s -X POST http://localhost:{actual_port}/api \
174
+ -H "Content-Type: application/json" \
175
+ -d '{"action":"health"}'
176
+ ```
177
+
178
+ Verify the response includes `"brain_id"` matching this brain's id — this
179
+ confirms you're talking to the right server (not an unrelated brain on the
180
+ same machine).
113
181
 
114
182
  ### Step 8: Ingest the project
115
183
 
@@ -53,12 +53,14 @@ Read `{brain_path}/_meta/config.json`. Look for a `source_path` key:
53
53
 
54
54
  ```json
55
55
  {
56
- "brain_path": "/Users/me/.wicked-brain",
57
- "server_port": 4243,
56
+ "brain_path": "/Users/me/.wicked-brain/projects/my-project",
57
+ "server_port": 4242,
58
58
  "source_path": "/Users/me/Projects/my-project"
59
59
  }
60
60
  ```
61
61
 
62
+ (`server_port` is the preferred starting port — the server writes the actual bound port back to this field on startup.)
63
+
62
64
  If `source_path` is **present** — LSP is configured. Proceed with calls.
63
65
 
64
66
  If `source_path` is **missing** — LSP will fail. Fix it before continuing.
@@ -108,6 +110,78 @@ Symptoms that indicate missing or wrong `source_path`:
108
110
 
109
111
  Check `source_path` in config. If it points at the brain directory (e.g., `~/.wicked-brain/...`) instead of the source project, that is wrong — the brain dir has no `tsconfig.json` or language config. Set it to the project root and restart.
110
112
 
113
+ ## Warming LSP (important — read this before calling lsp-workspace-symbols)
114
+
115
+ **Language servers start lazily.** The brain server does NOT spawn language
116
+ servers on startup — they only spawn when a file-specific LSP action runs
117
+ against a file in `source_path` (`lsp-symbols`, `lsp-definition`, `lsp-hover`,
118
+ `lsp-references`, `lsp-implementation`, `lsp-call-hierarchy-in`,
119
+ `lsp-call-hierarchy-out`).
120
+
121
+ `lsp-workspace-symbols` and `lsp-health` do NOT trigger server spawn. They
122
+ only report on servers that are already running. If you call
123
+ `lsp-workspace-symbols` on a fresh brain server, you will get:
124
+
125
+ ```json
126
+ {"symbols":[],"error":"no_running_server"}
127
+ ```
128
+
129
+ This does NOT mean "no symbols exist in the project." It means "no language
130
+ server has been spawned yet." Do not report empty results to the user as
131
+ authoritative when you see this error code.
132
+
133
+ ### Warm-up procedure
134
+
135
+ Before calling `lsp-workspace-symbols` for the first time on any brain server:
136
+
137
+ 1. Pick a real source file in `source_path`. Prefer a main entry file — e.g.,
138
+ for TypeScript: `src/index.ts`, `src/main.ts`, or whichever file exists.
139
+ Use Glob against `{source_path}` to find one if unsure. Do NOT pass a file
140
+ that does not exist — the warm-up will silently fail.
141
+
142
+ 2. Call a file-specific action to trigger `ensureReady`:
143
+ ```bash
144
+ curl -s -X POST http://localhost:{port}/api \
145
+ -H "Content-Type: application/json" \
146
+ -d '{"action":"lsp-symbols","params":{"file":"{absolute_path_to_real_file}"}}'
147
+ ```
148
+
149
+ 3. Poll `lsp-health` until a server is `ready` (not `starting`):
150
+ ```bash
151
+ curl -s -X POST http://localhost:{port}/api \
152
+ -H "Content-Type: application/json" \
153
+ -d '{"action":"lsp-health"}'
154
+ ```
155
+ Expected ready response:
156
+ ```json
157
+ {"servers":{"typescript":{"status":"ready","uptime":1234}}}
158
+ ```
159
+ Retry up to 10 times with 500ms between attempts (5 seconds total). Large
160
+ projects can take several seconds to finish indexing.
161
+
162
+ **If 10 retries are exhausted without a `ready` state**, do NOT treat this
163
+ as success-with-empty-results. Check:
164
+ - `lsp-health` response — is the server in `starting` state? Extend the
165
+ poll to 30 retries (15 seconds) for very large projects.
166
+ - Is the server in `error` state? Read the `message` field and surface it
167
+ to the user. Common causes: missing `tsconfig.json` (wrong `source_path`),
168
+ language server binary not installed, or a crash loop (see
169
+ `language_server_crashed` in Step 4 below).
170
+ - Is the `servers` object still empty? The warm-up file action likely
171
+ failed silently — verify the file path exists and is inside `source_path`.
172
+
173
+ 4. Now `lsp-workspace-symbols` will return real results.
174
+
175
+ ### Interpreting `lsp-health` states
176
+
177
+ | State | Meaning | Action |
178
+ |-------|---------|--------|
179
+ | empty `servers` object | No language server spawned yet | Follow warm-up procedure above |
180
+ | `status: "starting"` | Server process spawned, still indexing | Wait and retry |
181
+ | `status: "ready"` | Ready to serve queries | Proceed |
182
+ | `status: "crashed"` | Crashed ≤3 times | Check diagnostics for the language server's stderr |
183
+ | `status: "error"` with `No Project` message | Wrong `source_path` | See source_path prerequisites above |
184
+
111
185
  ## When to Use
112
186
 
113
187
  | You want to... | Action | Example |
@@ -0,0 +1,268 @@
1
+ ---
2
+ name: wicked-brain:migrate
3
+ description: |
4
+ Migrate a flat brain at ~/.wicked-brain/ into the per-project layout at
5
+ ~/.wicked-brain/projects/{name}/. Safe to run on already-migrated brains
6
+ (no-op). Auto-invoked by wicked-brain:init when a flat brain is detected.
7
+
8
+ Use when: "migrate my brain", "move brain to per-project layout",
9
+ "I have an old ~/.wicked-brain brain", or when init detects a flat brain.
10
+ ---
11
+
12
+ # wicked-brain:migrate
13
+
14
+ You migrate a flat brain (all data directly under `~/.wicked-brain/`) into the
15
+ per-project layout (each brain under `~/.wicked-brain/projects/{project-name}/`).
16
+ This was the layout change introduced in v0.4.7.
17
+
18
+ ## Cross-Platform Notes
19
+
20
+ Commands in this skill work on macOS, Linux, and Windows. Prefer your native
21
+ Read/Write/Glob tools over shell commands when possible.
22
+
23
+ - macOS/Linux home: `~`
24
+ - Windows home: `%USERPROFILE%`
25
+
26
+ ## When to use
27
+
28
+ - User explicitly asks to migrate
29
+ - `wicked-brain:init` detected a flat brain and invoked this skill
30
+ - Another skill hit errors caused by the flat layout (e.g. port collisions
31
+ with a second project, meta-brain federation not working)
32
+
33
+ ## Parameters
34
+
35
+ - **flat_path** (optional): path to the existing flat brain. Default: `~/.wicked-brain`
36
+ - **project_name** (optional): target project name. Default: asked interactively
37
+
38
+ ## Multiple flat brains
39
+
40
+ This skill migrates **one** flat brain per invocation. If the user has several
41
+ flat brains in different locations (e.g. `~/.wicked-brain`, `~/work-brain`,
42
+ `~/.config/wicked-brain`), run `wicked-brain:migrate` once per source, passing
43
+ a different `flat_path` each time. Each becomes its own project brain — either
44
+ under the same `~/.wicked-brain/projects/` umbrella (recommended — pick
45
+ distinct project names) or under separate containers if the user prefers
46
+ isolation.
47
+
48
+ If the user asks "migrate all my brains," enumerate the flat brains you can
49
+ find first (`find ~ -maxdepth 3 -name brain.json 2>/dev/null` on macOS/Linux)
50
+ and confirm each with the user before running migration on it.
51
+
52
+ ## Process
53
+
54
+ ### Step 1: Detect what we're dealing with
55
+
56
+ Read `{flat_path}/brain.json` — if it does not exist, there is no flat brain
57
+ to migrate. Tell the user and stop.
58
+
59
+ Read `{flat_path}/brain.json` to get the brain's `id` and `name`. These become
60
+ the defaults for the target project.
61
+
62
+ Check whether `{flat_path}/projects/` already exists:
63
+
64
+ - **If `{flat_path}` looks like a pure container** (contains only a `projects/`
65
+ subdirectory and optionally `_meta/`) — this brain is already migrated.
66
+ Report "Already using per-project layout" and stop.
67
+
68
+ - **If `{flat_path}/brain.json` exists at the root AND `{flat_path}/projects/`
69
+ also exists** — mixed state. This is a real migration case. Continue.
70
+
71
+ - **If `{flat_path}/brain.json` exists and no `projects/` subdir** — standard
72
+ flat layout. Continue.
73
+
74
+ ### Step 2: Confirm target with user
75
+
76
+ Ask the user:
77
+
78
+ 1. "What should this project brain be called?" — Default: the `id` from
79
+ `{flat_path}/brain.json`, or basename of the current working directory.
80
+ 2. "Target path?" — Default: `{flat_path}/projects/{project_name}`
81
+
82
+ If the target directory already exists AND is non-empty, stop and tell the
83
+ user: "A brain already exists at the target path. Pick a different name or
84
+ remove the existing brain manually."
85
+
86
+ ### Step 3: Stop any running server on the flat brain
87
+
88
+ **Critical on Windows** — SQLite's `.brain.db` may be locked by a running server
89
+ process. Moving a locked file silently corrupts the migration.
90
+
91
+ 1. Read `{flat_path}/_meta/server.pid` if it exists.
92
+ 2. Check if the process is alive:
93
+ - macOS/Linux: `kill -0 {pid} 2>/dev/null`
94
+ - Windows: `tasklist /FI "PID eq {pid}" 2>nul | findstr {pid}`
95
+ 3. If alive, stop it:
96
+ - macOS/Linux: `kill {pid}`
97
+ - Windows PowerShell: `Stop-Process -Id {pid}`
98
+ 4. Wait 2 seconds for file handles to release.
99
+ 5. Delete the stale PID file.
100
+
101
+ Also look for any other wicked-brain-server processes targeting this flat path
102
+ and stop them. On macOS/Linux: `pgrep -f "wicked-brain-server.*{flat_path}"`.
103
+
104
+ ### Step 4: Create the target directory structure
105
+
106
+ ```bash
107
+ # macOS/Linux
108
+ mkdir -p {target_path}/_meta
109
+ ```
110
+ ```powershell
111
+ # Windows
112
+ New-Item -ItemType Directory -Force -Path "{target_path}\_meta"
113
+ ```
114
+
115
+ Do NOT pre-create `raw/`, `chunks/`, `wiki/`, `memory/` — they'll be moved
116
+ over in Step 5.
117
+
118
+ ### Step 5: Move data from flat to target
119
+
120
+ Move each of these directories/files from `{flat_path}` to `{target_path}`
121
+ (skip any that don't exist in the source):
122
+
123
+ - `brain.json`
124
+ - `raw/`
125
+ - `chunks/`
126
+ - `wiki/`
127
+ - `memory/`
128
+ - `.brain.db`
129
+ - `.brain.db-shm` (SQLite WAL shared memory, may not exist)
130
+ - `.brain.db-wal` (SQLite WAL log, may not exist)
131
+
132
+ macOS/Linux:
133
+ ```bash
134
+ for item in brain.json raw chunks wiki memory .brain.db .brain.db-shm .brain.db-wal; do
135
+ if [ -e "{flat_path}/$item" ]; then
136
+ mv "{flat_path}/$item" "{target_path}/$item"
137
+ echo "moved $item"
138
+ fi
139
+ done
140
+ ```
141
+
142
+ Windows PowerShell:
143
+ ```powershell
144
+ $items = "brain.json","raw","chunks","wiki","memory",".brain.db",".brain.db-shm",".brain.db-wal"
145
+ foreach ($item in $items) {
146
+ $src = Join-Path "{flat_path}" $item
147
+ if (Test-Path $src) {
148
+ Move-Item $src (Join-Path "{target_path}" $item)
149
+ Write-Host "moved $item"
150
+ }
151
+ }
152
+ ```
153
+
154
+ **Do NOT move `_meta/`.** The flat brain's `_meta/config.json` references the
155
+ flat path and is about to be replaced. We'll write a fresh config in Step 6.
156
+
157
+ If the move fails partway through, stop immediately. Partial migrations leave
158
+ the brain in an unrecoverable state — tell the user what moved and what didn't,
159
+ and ask them to resolve manually before retrying.
160
+
161
+ ### Step 6: Write the target's `_meta/config.json`
162
+
163
+ ```json
164
+ {
165
+ "brain_path": "{target_path}",
166
+ "server_port": 4242,
167
+ "installed_clis": []
168
+ }
169
+ ```
170
+
171
+ `server_port: 4242` is the *preferred starting port*, not the guaranteed port.
172
+ When the server starts in Step 7 it probes from this value upward and writes
173
+ the actual bound port back to this same file. After Step 7, always re-read
174
+ `_meta/config.json` to get the real port — never hardcode `4242` in downstream
175
+ calls. This matters especially when migrating while another brain is already
176
+ running on 4242.
177
+
178
+ If the flat brain's `_meta/config.json` had a `source_path` field, copy it over
179
+ to the new config — the LSP workspace root must follow the brain.
180
+
181
+ Initialize the event log:
182
+ - macOS/Linux: `touch {target_path}/_meta/log.jsonl`
183
+ - Windows: `New-Item -ItemType File -Force -Path "{target_path}\_meta\log.jsonl"`
184
+
185
+ ### Step 7: Start the server against the new path (verification before cleanup)
186
+
187
+ **Ordering matters.** Do not delete the flat `_meta/` yet — if the new server
188
+ fails to start, you need to be able to restore. Start and verify the new
189
+ server first; only clean up the flat path after verification succeeds.
190
+
191
+ Do NOT pass `--port` — let the server pick a free port. It will write the
192
+ actual port back to `{target_path}/_meta/config.json`.
193
+
194
+ ```bash
195
+ npx wicked-brain-server --brain "{target_path}" &
196
+ ```
197
+
198
+ Wait for the server to start, then re-read `{target_path}/_meta/config.json`
199
+ to get the bound port.
200
+
201
+ Health-check:
202
+ ```bash
203
+ curl -s -X POST http://localhost:{port}/api \
204
+ -H "Content-Type: application/json" \
205
+ -d '{"action":"health"}'
206
+ ```
207
+
208
+ Verify the response includes `"brain_id"` matching the id from Step 1. If the
209
+ brain_id is different, the wrong server is responding — likely a stale process
210
+ on that port. Stop and diagnose before proceeding.
211
+
212
+ ### Step 8: Verify the index still works
213
+
214
+ Run a stats call against the new server and confirm the document count is
215
+ non-zero (assuming the flat brain had any documents):
216
+
217
+ ```bash
218
+ curl -s -X POST http://localhost:{port}/api \
219
+ -H "Content-Type: application/json" \
220
+ -d '{"action":"stats"}'
221
+ ```
222
+
223
+ If document counts are zero but `.brain.db` was moved, the SQLite file may have
224
+ been truncated during the move or the server is looking at the wrong path.
225
+ **Do NOT proceed to Step 9.** Stop the new server and investigate — the flat
226
+ `_meta/` is still intact at this point, so you can roll back by moving files
227
+ back to `{flat_path}`.
228
+
229
+ ### Step 9: Clean up the flat path (only after Steps 7 and 8 pass)
230
+
231
+ Do NOT run this step unless the health check AND stats check both succeeded.
232
+ This step is irreversible.
233
+
234
+ What's left at `{flat_path}` should now be:
235
+
236
+ - `_meta/` directory (old config, log, stale PID)
237
+ - Maybe a `projects/` subdirectory (if it existed before)
238
+
239
+ Verify this is the case before cleanup. If other files remain at the flat
240
+ path, stop and report them — they were not part of a standard flat brain and
241
+ the user needs to decide what to do with them.
242
+
243
+ Delete the flat `_meta/` directory:
244
+ - macOS/Linux: `rm -rf {flat_path}/_meta`
245
+ - Windows: `Remove-Item -Recurse -Force "{flat_path}\_meta"`
246
+
247
+ The flat path is now a pure container with only `projects/` beneath it.
248
+
249
+ ### Step 10: Report
250
+
251
+ Tell the user:
252
+
253
+ "Migrated brain `{name}` from `{flat_path}` to `{target_path}`.
254
+ - {N} documents preserved
255
+ - Server running on port {port}
256
+ - `source_path`: {source_path or 'not set'}
257
+
258
+ The flat path `{flat_path}` is now a container for per-project brains. You can
259
+ create additional project brains under `{flat_path}/projects/` with `wicked-brain:init`."
260
+
261
+ ## Rollback
262
+
263
+ If anything goes wrong before Step 9 (cleanup), the migration can be rolled
264
+ back by moving items back from `{target_path}` to `{flat_path}`. The flat
265
+ `_meta/` is still intact through Steps 1-8.
266
+
267
+ After Step 9 there is no clean rollback — the flat `_meta/` has been deleted.
268
+ Never run Step 9 until Steps 7 and 8 both pass.
@@ -30,18 +30,40 @@ For the brain path default:
30
30
 
31
31
  ## Process
32
32
 
33
- ### Step 1: Check current version
33
+ ### Step 1: Check current installed version
34
34
 
35
- Read the installed server version:
36
- ```bash
37
- wicked-brain-server --version 2>/dev/null || npx wicked-brain-server --version 2>/dev/null || echo "not installed"
38
- ```
35
+ The `wicked-brain-server` binary lives inside the globally installed `wicked-brain` npm package. Read its version directly from the installed package:
39
36
 
40
- If that doesn't work, check the package directly:
41
37
  ```bash
42
- npm list -g wicked-brain-server --json 2>/dev/null | grep version
38
+ npm list -g wicked-brain --json 2>/dev/null | python3 -c "
39
+ import json, sys
40
+ try:
41
+ d = json.load(sys.stdin)
42
+ deps = d.get('dependencies', {})
43
+ v = deps.get('wicked-brain', {}).get('version', 'not installed')
44
+ print(v)
45
+ except Exception:
46
+ print('not installed')
47
+ " 2>/dev/null || npm list -g wicked-brain --json 2>/dev/null | python -c "
48
+ import json, sys
49
+ try:
50
+ d = json.load(sys.stdin)
51
+ deps = d.get('dependencies', {})
52
+ v = deps.get('wicked-brain', {}).get('version', 'not installed')
53
+ print(v)
54
+ except Exception:
55
+ print('not installed')
56
+ "
43
57
  ```
44
58
 
59
+ If the result is `not installed`, the package was never globally installed (the
60
+ user may have been running via `npx` only). Treat this as "needs install" and
61
+ proceed to Step 4.
62
+
63
+ **Do NOT use `npx wicked-brain-server --version`** — it may return the version
64
+ of a stale cached npx copy, not the globally installed one that actually runs
65
+ when brain servers start.
66
+
45
67
  ### Step 2: Check latest version on npm
46
68
 
47
69
  ```bash
@@ -58,15 +80,45 @@ If an update is available, ask the user:
58
80
 
59
81
  ### Step 4: Update (if user approves)
60
82
 
61
- #### Update everything (skills + server)
83
+ **Critical:** `npx wicked-brain@latest` only runs the *installer* — it refreshes
84
+ the skill markdown files in your CLI's skills directory, but it does NOT update
85
+ the globally installed `wicked-brain-server` binary. The skills will then expect
86
+ features that the old server doesn't have, producing confusing errors.
62
87
 
63
- The server is bundled in the main package — one command updates both:
88
+ Use `npm install -g` to update the actual binary:
64
89
 
65
90
  ```bash
66
- npx wicked-brain@latest
91
+ npm install -g wicked-brain@latest 2>&1
67
92
  ```
68
93
 
69
- This re-runs the installer with the latest version, updating all skills across detected CLIs. The server binary updates automatically since it's part of the same package.
94
+ On Windows PowerShell (no change needed):
95
+ ```powershell
96
+ npm install -g wicked-brain@latest
97
+ ```
98
+
99
+ If this fails with `EACCES` / permission denied:
100
+ - macOS/Linux: `sudo npm install -g wicked-brain@latest`
101
+ - Windows: re-run the shell as Administrator, or fix npm's global prefix per
102
+ npm docs. Do NOT silently skip — report the failure to the user and stop.
103
+
104
+ After a successful `npm install -g`, also run the installer to refresh skill
105
+ files in all detected CLIs (skills are copied from the installed package, not
106
+ downloaded separately):
107
+
108
+ ```bash
109
+ npx wicked-brain
110
+ ```
111
+
112
+ ### Step 4a: Verify the update landed
113
+
114
+ Re-run the Step 1 version check. The version reported MUST match the latest
115
+ version from Step 2. If it still shows the old version:
116
+
117
+ 1. Check `which wicked-brain-server` (macOS/Linux) or `where wicked-brain-server` (Windows) — the shell may have cached a path to a different installation.
118
+ 2. Clear npm's global cache: `npm cache clean --force`
119
+ 3. Check if a different Node.js version (nvm, fnm, volta) is pinning a stale copy.
120
+
121
+ Do NOT proceed to Step 5 until version verification succeeds. Reporting a successful update while the binary is stale is the top failure mode of this skill.
70
122
 
71
123
  ### Step 5: Restart server if running
72
124