wicked-brain 0.4.2 → 0.4.3

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.2",
3
+ "version": "0.4.3",
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": [
@@ -17,6 +17,9 @@ function getArg(name) {
17
17
  const brainPath = resolve(getArg("brain") || ".");
18
18
  const preferredPort = parseInt(getArg("port") || "4242", 10);
19
19
  const configPath = join(brainPath, "brain.json");
20
+ // Source path for LSP workspace root — prefer --source flag, fall back to config, then brainPath
21
+ const sourceArgRaw = getArg("source");
22
+ const sourceArg = sourceArgRaw ? resolve(sourceArgRaw) : null;
20
23
 
21
24
  /** Find a free TCP port starting from `start`. */
22
25
  function findFreePort(start) {
@@ -57,8 +60,18 @@ const db = new SqliteSearch(dbPath, brainId);
57
60
  const pidPath = join(brainPath, "_meta", "server.pid");
58
61
  writeFileSync(pidPath, String(pid));
59
62
 
60
- // LSP client
61
- const lsp = new LspClient(brainPath, db);
63
+ // Read project directories and source path from config (must happen before LspClient)
64
+ const metaConfigPath = join(brainPath, "_meta", "config.json");
65
+ let projects = [];
66
+ let sourcePath = sourceArg;
67
+ try {
68
+ const metaConfig = JSON.parse(readFileSync(metaConfigPath, "utf-8"));
69
+ projects = metaConfig.projects || [];
70
+ if (!sourcePath && metaConfig.source_path) sourcePath = resolve(metaConfig.source_path);
71
+ } catch {}
72
+
73
+ // LSP client — pass source path so language servers are rooted at the project, not the brain dir
74
+ const lsp = new LspClient(brainPath, db, sourcePath);
62
75
 
63
76
  // Graceful shutdown
64
77
  async function shutdown() {
@@ -168,14 +181,6 @@ const server = createServer((req, res) => {
168
181
  });
169
182
  });
170
183
 
171
- // Read project directories from config
172
- const metaConfigPath = join(brainPath, "_meta", "config.json");
173
- let projects = [];
174
- try {
175
- const metaConfig = JSON.parse(readFileSync(metaConfigPath, "utf-8"));
176
- projects = metaConfig.projects || [];
177
- } catch {}
178
-
179
184
  const watcher = new FileWatcher(brainPath, db, brainId, projects);
180
185
 
181
186
  // Wire file changes to LSP client for didOpen/didChange/didClose
@@ -21,10 +21,10 @@ export class LspClient {
21
21
  #diagnostics = new Map(); // filePath → Diagnostic[]
22
22
  #diagnosticsSetup = new Set(); // server keys with diagnostics wired
23
23
 
24
- constructor(brainPath, db) {
24
+ constructor(brainPath, db, sourcePath) {
25
25
  this.#brainPath = brainPath;
26
26
  this.#db = db;
27
- this.#manager = new LspManager(brainPath);
27
+ this.#manager = new LspManager(brainPath, sourcePath);
28
28
  this.#userConfig = loadUserConfig(brainPath);
29
29
  }
30
30
 
@@ -13,10 +13,18 @@ const RETRY_RESET_MS = 300000; // 5 minutes
13
13
 
14
14
  export class LspManager {
15
15
  #brainPath;
16
+ #sourcePath;
16
17
  #servers = new Map(); // key → { process, client, state, retries, startedAt, openFiles }
17
18
 
18
- constructor(brainPath) {
19
+ /**
20
+ * @param {string} brainPath - Brain storage directory (used as fallback workspace root)
21
+ * @param {string} [sourcePath] - Actual source project root with tsconfig.json, go.mod, etc.
22
+ * When provided, LSP servers are initialized with this as rootUri so they can find
23
+ * project configuration files. Falls back to brainPath if not provided.
24
+ */
25
+ constructor(brainPath, sourcePath) {
19
26
  this.#brainPath = brainPath;
27
+ this.#sourcePath = sourcePath || brainPath;
20
28
  }
21
29
 
22
30
  /**
@@ -71,7 +79,7 @@ export class LspManager {
71
79
 
72
80
  const proc = spawn(config.command, config.args || [], {
73
81
  stdio: ["pipe", "pipe", "pipe"],
74
- cwd: this.#brainPath,
82
+ cwd: this.#sourcePath,
75
83
  });
76
84
 
77
85
  const client = new RpcClient(proc.stdin, proc.stdout);
@@ -100,13 +108,12 @@ export class LspManager {
100
108
 
101
109
  // Initialize
102
110
  try {
103
- const result = await client.request("initialize", {
111
+ const rootUri = pathToFileURL(resolve(this.#sourcePath)).href;
112
+ await client.request("initialize", {
104
113
  processId: process.pid,
105
114
  capabilities: {},
106
- rootUri: pathToFileURL(resolve(this.#brainPath)).href,
107
- workspaceFolders: [
108
- { uri: pathToFileURL(resolve(this.#brainPath)).href, name: "brain" },
109
- ],
115
+ rootUri,
116
+ workspaceFolders: [{ uri: rootUri, name: "project" }],
110
117
  });
111
118
  client.notify("initialized", {});
112
119
  entry.state = "ready";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain-server",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "description": "SQLite FTS5 search server for wicked-brain digital knowledge bases",
6
6
  "keywords": [
@@ -398,7 +398,34 @@ Archived files are invisible to the file watcher, so the server won't clean them
398
398
  - macOS/Linux: `mv "{brain_path}/chunks/extracted/{safe_name}" "{brain_path}/chunks/extracted/{safe_name}.archived-$(date +%s)"`
399
399
  - Windows: `Rename-Item "{brain_path}\chunks\extracted\{safe_name}" "{safe_name}.archived-{timestamp}"`
400
400
 
401
- ### Step 5: Report to user
401
+ ### Step 5: Record source path
402
+
403
+ After ingesting a directory, write the absolute source path to `_meta/config.json`
404
+ so the brain server can use it as the LSP workspace root (enabling symbol lookup,
405
+ go-to-definition, and diagnostics for the ingested project):
406
+
407
+ ```bash
408
+ # Read current config, add source_path, write back
409
+ python3 -c "
410
+ import json, sys
411
+ path = '{brain_path}/_meta/config.json'
412
+ with open(path) as f: cfg = json.load(f)
413
+ cfg['source_path'] = '{absolute_source_path}'
414
+ with open(path, 'w') as f: json.dump(cfg, f, indent=2)
415
+ print('source_path recorded')
416
+ " 2>/dev/null || python -c "
417
+ import json, sys
418
+ path = '{brain_path}/_meta/config.json'
419
+ with open(path) as f: cfg = json.load(f)
420
+ cfg['source_path'] = '{absolute_source_path}'
421
+ with open(path, 'w') as f: json.dump(cfg, f, indent=2)
422
+ print('source_path recorded')
423
+ "
424
+ ```
425
+
426
+ Skip this step if the source is a single file rather than a project directory.
427
+
428
+ ### Step 6: Report to user
402
429
 
403
430
  After the subagent or batch script completes, summarize:
404
431
  - "{N} text files ingested, {M} chunks created"
@@ -47,9 +47,12 @@ For the brain path default:
47
47
  - Windows: `tasklist /FI "PID eq {pid}" 2>nul | findstr {pid}`
48
48
  - Or use Python: `python3 -c "import os; os.kill({pid}, 0)" 2>/dev/null || python -c "import os; os.kill({pid}, 0)"`
49
49
 
50
- c. If the process is dead or no PID file, start the server:
50
+ c. If the process is dead or no PID file, start the server.
51
+ Also pass `--source` if `source_path` is set in `_meta/config.json`
52
+ (this roots LSP language servers at the ingested project so symbol
53
+ lookup and go-to-definition work correctly):
51
54
  ```bash
52
- npx wicked-brain-server --brain {brain_path} --port {port} &
55
+ npx wicked-brain-server --brain {brain_path} --port {port} [--source {source_path}] &
53
56
  ```
54
57
  On Windows (PowerShell):
55
58
  ```powershell