wicked-brain 0.3.5 → 0.3.7

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
@@ -75,6 +75,12 @@ npx wicked-brain
75
75
 
76
76
  That's it. The installer detects your AI CLIs and drops in the skills. First time you use any skill, it walks you through setup.
77
77
 
78
+ To install into a non-standard CLI config path:
79
+
80
+ ```bash
81
+ npx wicked-brain --path=~/alt-configs/.claude
82
+ ```
83
+
78
84
  Or install via [agent-skills-cli](https://github.com/Karanjot786/agent-skills-cli):
79
85
 
80
86
  ```bash
@@ -119,16 +125,22 @@ Every operation uses **progressive loading** — the agent never pulls more than
119
125
 
120
126
  | Skill | What it does |
121
127
  |---|---|
122
- | `wicked-brain:init` | Set up a new brain (auto-triggers on first use) |
128
+ | `wicked-brain:init` | Set up a new brain creates directory structure, then onboards your project in parallel |
123
129
  | `wicked-brain:ingest` | Add source files — text extracted deterministically, binary docs read via LLM vision |
124
130
  | `wicked-brain:search` | Parallel search across your brain and linked brains |
125
131
  | `wicked-brain:read` | Progressive loading: depth 0 (stats), depth 1 (summary), depth 2 (full content) |
126
132
  | `wicked-brain:query` | Answer questions with source citations |
127
133
  | `wicked-brain:compile` | Synthesize wiki articles from chunks |
128
- | `wicked-brain:lint` | Find broken links, orphan chunks, inconsistencies |
129
- | `wicked-brain:enhance` | Identify and fill knowledge gaps |
134
+ | `wicked-brain:lint` | Find broken links, orphan chunks, inconsistencies; auto-fix where possible |
135
+ | `wicked-brain:enhance` | Identify and fill knowledge gaps with inferred content |
136
+ | `wicked-brain:memory` | Store and recall experiential learnings across sessions (working / episodic / semantic tiers) |
130
137
  | `wicked-brain:status` | Brain health, stats, orientation |
131
138
  | `wicked-brain:server` | Manage the background search server (auto-triggered) |
139
+ | `wicked-brain:configure` | Write brain-aware context into your CLI's config (CLAUDE.md, GEMINI.md, etc.) |
140
+ | `wicked-brain:batch` | Generate scripts for bulk operations — avoids burning context on repetitive tool calls |
141
+ | `wicked-brain:retag` | Backfill synonym-expanded tags across all chunks for better search recall |
142
+ | `wicked-brain:update` | Check npm for updates and reinstall skills across all detected CLIs |
143
+ | `wicked-brain:lsp` | Universal code intelligence via LSP — hover, go-to-definition, diagnostics, completions |
132
144
 
133
145
  ## Multi-Brain Federation
134
146
 
@@ -169,7 +181,7 @@ Modern LLMs read PDF, DOCX, PPTX, and XLSX natively. When you ingest a binary do
169
181
 
170
182
  ## Architecture
171
183
 
172
- **~500 lines of server JavaScript** (SQLite FTS5 + file watcher) + **~900 lines of skill markdown** (agent instructions).
184
+ **~300 lines of server JavaScript** (SQLite FTS5 + file watcher) + **~1,400 lines of skill markdown** (agent instructions).
173
185
 
174
186
  That's the entire system. Compare that to a typical RAG stack:
175
187
 
@@ -182,7 +194,7 @@ Typical RAG: wicked-brain:
182
194
  - Re-ranking model - LLM reasoning
183
195
  - Orchestration layer - Skills (markdown)
184
196
  ───────────────── ─────────────────
185
- ~5,000+ lines, 10+ deps ~1,400 lines, 1 dep
197
+ ~5,000+ lines, 10+ deps ~1,700 lines, 1 dep
186
198
  ```
187
199
 
188
200
  ## Supported CLIs
@@ -194,6 +206,8 @@ Typical RAG: wicked-brain:
194
206
  | GitHub Copilot CLI | Supported |
195
207
  | Cursor | Supported |
196
208
  | Codex | Supported |
209
+ | Kiro | Supported |
210
+ | Antigravity | Supported |
197
211
 
198
212
  Skills use only universally available operations (read files, write files, run shell commands, grep). No CLI-specific features.
199
213
 
package/install.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  // wicked-brain installer — detects CLIs and installs skills + agents
3
3
 
4
4
  import { existsSync, mkdirSync, cpSync, readdirSync } from "node:fs";
5
- import { join, resolve } from "node:path";
5
+ import { join, resolve, basename } from "node:path";
6
6
  import { homedir } from "node:os";
7
7
  import { argv } from "node:process";
8
8
  import { fileURLToPath } from "node:url";
@@ -12,56 +12,55 @@ const skillsSource = join(__dirname, "skills");
12
12
  const home = homedir();
13
13
 
14
14
  const CLI_TARGETS = [
15
- { name: "claude", dir: join(home, ".claude", "skills"), agentDir: join(home, ".claude", "agents"), platform: "claude" },
16
- { name: "gemini", dir: join(home, ".gemini", "skills"), agentDir: join(home, ".gemini", "agents"), platform: "gemini" },
17
- { name: "copilot", dir: join(home, ".github", "skills"), agentDir: join(home, ".github", "agents"), platform: "copilot" },
18
- { name: "codex", dir: join(home, ".codex", "skills"), agentDir: join(home, ".codex", "agents"), platform: "codex" },
19
- { name: "cursor", dir: join(home, ".cursor", "skills"), agentDir: join(home, ".cursor", "agents"), platform: "cursor" },
20
- { name: "kiro", dir: join(home, ".kiro", "skills"), agentDir: join(home, ".kiro", "agents"), platform: "kiro" },
21
- { name: "antigravity", dir: join(home, ".antigravity", "skills"), agentDir: join(home, ".antigravity", "rules"), platform: "antigravity" },
15
+ { name: "claude", dir: join(home, ".claude", "skills"), agentDir: join(home, ".claude", "agents"), agentSubdir: "agents", platform: "claude" },
16
+ { name: "gemini", dir: join(home, ".gemini", "skills"), agentDir: join(home, ".gemini", "agents"), agentSubdir: "agents", platform: "gemini" },
17
+ { name: "copilot", dir: join(home, ".github", "skills"), agentDir: join(home, ".github", "agents"), agentSubdir: "agents", platform: "copilot" },
18
+ { name: "codex", dir: join(home, ".codex", "skills"), agentDir: join(home, ".codex", "agents"), agentSubdir: "agents", platform: "codex" },
19
+ { name: "cursor", dir: join(home, ".cursor", "skills"), agentDir: join(home, ".cursor", "agents"), agentSubdir: "agents", platform: "cursor" },
20
+ { name: "kiro", dir: join(home, ".kiro", "skills"), agentDir: join(home, ".kiro", "agents"), agentSubdir: "agents", platform: "kiro" },
21
+ { name: "antigravity", dir: join(home, ".antigravity", "skills"), agentDir: join(home, ".antigravity", "rules"), agentSubdir: "rules", platform: "antigravity" },
22
22
  ];
23
23
 
24
- // Detect which CLIs are installed by checking if parent dir exists
25
- const detected = CLI_TARGETS.filter((t) => {
26
- const parentDir = resolve(t.dir, "..");
27
- return existsSync(parentDir);
28
- });
29
-
30
24
  console.log("wicked-brain installer\n");
31
25
 
32
- if (detected.length === 0) {
33
- console.log("No supported AI CLIs detected. Supported: claude, gemini, copilot, codex, cursor, kiro, antigravity");
34
- console.log("Install skills manually by copying the skills/ directory.");
35
- process.exit(1);
36
- }
37
-
38
- console.log(`Detected CLIs: ${detected.map((d) => d.name).join(", ")}\n`);
39
-
40
- // Allow filtering via --cli flag or custom --path
41
26
  const args = argv.slice(2);
27
+ const argValue = (a) => a.split("=")[1];
42
28
  const cliArg = args.find((a) => a.startsWith("--cli="));
43
29
  const pathArg = args.find((a) => a.startsWith("--path="));
44
30
 
45
31
  let targets;
46
32
 
47
33
  if (pathArg) {
48
- const rawPath = pathArg.split("=")[1].replace(/^~/, home);
49
- const customPath = resolve(rawPath);
50
- const dirName = customPath.split(/[\\/]/).pop().replace(/^\./, ""); // e.g. ".claude" → "claude"
34
+ const rawPath = argValue(pathArg);
35
+ if (!rawPath) {
36
+ console.error("Error: --path requires a value (e.g. --path=~/.claude)");
37
+ process.exit(1);
38
+ }
39
+ const customPath = resolve(rawPath.replace(/^~/, home));
40
+ // Strip leading dot to match CLI_TARGETS names (e.g. ".claude" → "claude")
41
+ const dirName = basename(customPath).replace(/^\./, "");
51
42
  const knownPlatform = CLI_TARGETS.find((t) => t.name === dirName);
52
- // Use platform's agent subdir name (e.g. antigravity uses "rules"), default to "agents"
53
- const agentSubdirName = knownPlatform
54
- ? knownPlatform.agentDir.split(/[\\/]/).pop()
55
- : "agents";
43
+ const agentSubdir = knownPlatform?.agentSubdir ?? "agents";
56
44
  targets = [{
57
45
  name: dirName,
58
46
  dir: join(customPath, "skills"),
59
- agentDir: join(customPath, agentSubdirName),
47
+ agentDir: join(customPath, agentSubdir),
60
48
  platform: knownPlatform?.platform ?? dirName,
61
49
  }];
62
50
  console.log(`Custom path: ${customPath}\n`);
63
51
  } else {
64
- const cliFilter = cliArg ? cliArg.split("=")[1].split(",") : null;
52
+ // Detect which CLIs are installed by checking if parent dir exists
53
+ const detected = CLI_TARGETS.filter((t) => existsSync(resolve(t.dir, "..")));
54
+
55
+ if (detected.length === 0) {
56
+ console.log("No supported AI CLIs detected. Supported: claude, gemini, copilot, codex, cursor, kiro, antigravity");
57
+ console.log("Install skills manually by copying the skills/ directory.");
58
+ process.exit(1);
59
+ }
60
+
61
+ console.log(`Detected CLIs: ${detected.map((d) => d.name).join(", ")}\n`);
62
+
63
+ const cliFilter = cliArg ? argValue(cliArg).split(",") : null;
65
64
  targets = cliFilter ? detected.filter((d) => cliFilter.includes(d.name)) : detected;
66
65
  }
67
66
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
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": [
@@ -2,6 +2,16 @@ import Database from "better-sqlite3";
2
2
  import { parseWikilinks } from "./wikilinks.mjs";
3
3
  import { statSync } from "node:fs";
4
4
 
5
+ /**
6
+ * Extracts body text from a document, stripping YAML frontmatter.
7
+ * Falls back to the raw content if no frontmatter is detected.
8
+ */
9
+ function extractBodyExcerpt(content, maxLen = 300) {
10
+ const match = content.match(/^---\n[\s\S]*?\n---\n?([\s\S]*)/);
11
+ const body = match ? match[1] : content;
12
+ return body.trim().slice(0, maxLen);
13
+ }
14
+
5
15
  function escapeFtsQuery(query) {
6
16
  return query
7
17
  .trim()
@@ -209,6 +219,7 @@ export class SqliteSearch {
209
219
  d.path,
210
220
  d.brain_id,
211
221
  snippet(documents_fts, 2, '<b>', '</b>', '…', 32) AS snippet,
222
+ SUBSTR(d.content, 1, 1000) AS raw_content,
212
223
  COALESCE(link_count.cnt, 0) AS backlink_count,
213
224
  COALESCE(ac.cnt, 0) AS access_count
214
225
  FROM documents_fts f
@@ -228,7 +239,12 @@ export class SqliteSearch {
228
239
  ORDER BY (f.rank - (COALESCE(link_count.cnt, 0) * ${BACKLINK_WEIGHT}) - (COALESCE(ac.cnt, 0) * ${SEARCH_ACCESS_WEIGHT}))
229
240
  LIMIT ? OFFSET ?
230
241
  `)
231
- .all(escaped, ...sinceParams, limit, offset);
242
+ .all(escaped, ...sinceParams, limit, offset)
243
+ .map((row) => {
244
+ const body_excerpt = extractBodyExcerpt(row.raw_content ?? "");
245
+ delete row.raw_content;
246
+ return { ...row, body_excerpt };
247
+ });
232
248
 
233
249
  const countRow = this.#db
234
250
  .prepare(
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-brain-server",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "type": "module",
5
5
  "description": "SQLite FTS5 search server for wicked-brain digital knowledge bases",
6
6
  "keywords": [