wicked-brain 0.3.1 → 0.3.2

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.3.1",
3
+ "version": "0.3.2",
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,4 +1,4 @@
1
- import { watch, readFileSync, existsSync, readdirSync } from "node:fs";
1
+ import { watch, readFileSync, existsSync, readdirSync, statSync } from "node:fs";
2
2
  import { join, relative } from "node:path";
3
3
  import { createHash } from "node:crypto";
4
4
 
@@ -106,15 +106,29 @@ export class FileWatcher {
106
106
  }
107
107
 
108
108
  #scanAndHashProject(project) {
109
+ let indexed = 0;
109
110
  this.#walkDir(project.path, (absPath) => {
110
- const parts = absPath.split(/[/\\]/);
111
- if (parts.some(p => IGNORE_DIRS.has(p))) return;
112
- if (!absPath.endsWith(".md") && !this.#isCodeFile(absPath)) return;
111
+ if (!this.#isCodeFile(absPath)) return;
112
+ try {
113
+ const stat = statSync(absPath);
114
+ if (stat.size > FileWatcher.#MAX_FILE_SIZE) return;
115
+ } catch { return; }
113
116
  const relPath = normalizePath(`projects/${project.name}/${relative(project.path, absPath)}`);
114
- const content = readFileSync(absPath, "utf-8");
115
- const hash = this.#hash(content);
116
- this.#hashes.set(relPath, hash);
117
+ try {
118
+ const content = readFileSync(absPath, "utf-8");
119
+ const hash = this.#hash(content);
120
+ this.#hashes.set(relPath, hash);
121
+ // Index project files on first scan (brain dirs are pre-indexed, project dirs are not)
122
+ this.#db.index({
123
+ id: relPath,
124
+ path: relPath,
125
+ content,
126
+ brain_id: this.#brainId,
127
+ });
128
+ indexed++;
129
+ } catch { /* binary or unreadable — skip */ }
117
130
  });
131
+ console.log(`[watcher] Scanned project ${project.name}: ${indexed} files indexed`);
118
132
  }
119
133
 
120
134
  #walkDir(dir, callback) {
@@ -183,6 +197,12 @@ export class FileWatcher {
183
197
  return;
184
198
  }
185
199
 
200
+ if (!this.#isCodeFile(absPath)) return;
201
+ try {
202
+ const stat = statSync(absPath);
203
+ if (stat.size > FileWatcher.#MAX_FILE_SIZE) return;
204
+ } catch { return; }
205
+
186
206
  try {
187
207
  const content = readFileSync(absPath, "utf-8");
188
208
  const newHash = this.#hash(content);
@@ -203,11 +223,40 @@ export class FileWatcher {
203
223
  } catch {}
204
224
  }
205
225
 
226
+ /** Max file size to index (1MB). Skips binaries and large generated files. */
227
+ static #MAX_FILE_SIZE = 1048576;
228
+
229
+ /** Text file extensions safe to read and index. */
230
+ static #CODE_EXTENSIONS = new Set([
231
+ // Web
232
+ ".ts", ".tsx", ".js", ".jsx", ".mjs", ".mts", ".cjs",
233
+ ".html", ".htm", ".css", ".scss", ".less", ".sass",
234
+ ".vue", ".svelte", ".astro",
235
+ // Backend
236
+ ".py", ".go", ".rs", ".java", ".cs", ".rb", ".php",
237
+ ".kt", ".kts", ".scala", ".sc", ".ex", ".exs", ".erl",
238
+ // Systems
239
+ ".c", ".cpp", ".cc", ".cxx", ".h", ".hpp", ".zig", ".nim", ".d",
240
+ ".hs", ".lhs", ".ml", ".mli",
241
+ // Scripting
242
+ ".lua", ".pl", ".pm", ".r", ".R", ".jl", ".sh", ".bash", ".zsh",
243
+ // Data / Config
244
+ ".sql", ".graphql", ".gql", ".tf", ".yaml", ".yml",
245
+ ".toml", ".json", ".jsonc", ".xml", ".csv",
246
+ // Mobile
247
+ ".swift", ".dart",
248
+ // Other
249
+ ".clj", ".cljs", ".cljc", ".edn", ".fs", ".fsx", ".fsi",
250
+ ".gleam", ".sol", ".prisma", ".proto",
251
+ // Docs
252
+ ".md", ".markdown", ".tex", ".txt", ".rst",
253
+ ]);
254
+
206
255
  #isCodeFile(absPath) {
207
256
  const dot = absPath.lastIndexOf(".");
208
257
  if (dot === -1) return false;
209
- const ext = absPath.slice(dot);
210
- return ext.length <= 6;
258
+ const ext = absPath.slice(dot).toLowerCase();
259
+ return FileWatcher.#CODE_EXTENSIONS.has(ext);
211
260
  }
212
261
 
213
262
  #startPolling() {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain-server",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "SQLite FTS5 search server for wicked-brain digital knowledge bases",
6
6
  "keywords": [
@@ -71,8 +71,8 @@ Digital brain: {brain_id} | {total} indexed items | {chunks} chunks, {wiki} wiki
71
71
 
72
72
  ### How to use
73
73
 
74
- - **Before responding**: call `wicked-brain:context` to surface relevant knowledge
75
- - **Capture learnings**: call `wicked-brain:session-teardown` at session end
74
+ - **Before responding**: call `wicked-brain:agent` (context) to surface relevant knowledge
75
+ - **Capture learnings**: call `wicked-brain:agent` (session-teardown) at session end
76
76
  - **Store a decision/pattern/gotcha**: call `wicked-brain:memory` (store mode)
77
77
  - **Ask the brain**: call `wicked-brain:query` for cited answers
78
78
  - **Available agents**: consolidate, context, session-teardown, onboard (via `wicked-brain:agent`)