@a13xu/lucid 1.9.4 → 1.9.5
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 +16 -1
- package/build/index.js +1 -1
- package/build/indexer/file.d.ts +2 -0
- package/build/indexer/file.js +12 -8
- package/build/indexer/project.js +18 -46
- package/build/tools/sync.js +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# @a13xu/lucid
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@a13xu/lucid)
|
|
4
|
+
[](https://www.npmjs.com/package/@a13xu/lucid)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
> **MCP server for Claude Code** — persistent memory, smart code indexing, and code quality validation. Works out of the box with zero configuration.
|
|
8
|
+
|
|
3
9
|
Token-efficient memory, code indexing, and validation for Claude Code agents — backed by **SQLite + FTS5**.
|
|
4
10
|
|
|
5
|
-
Stores a persistent knowledge graph (entities, relations, observations), indexes source files as compressed binary with change detection, retrieves minimal relevant context via TF-IDF or Qdrant, and validates code for LLM drift patterns.
|
|
11
|
+
Stores a persistent knowledge graph (entities, relations, observations), indexes source files as compressed binary with change detection, retrieves minimal relevant context via TF-IDF or Qdrant, and validates code for LLM drift patterns. Supports TypeScript, JavaScript, Python, **Vue, Nuxt**.
|
|
6
12
|
|
|
7
13
|
## Install
|
|
8
14
|
|
|
@@ -226,6 +232,15 @@ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{},
|
|
|
226
232
|
|
|
227
233
|
In Claude Code: run `/mcp` — you should see `lucid` with 20 tools.
|
|
228
234
|
|
|
235
|
+
## Contributing
|
|
236
|
+
|
|
237
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/a13xu/lucid).
|
|
238
|
+
|
|
239
|
+
1. Fork the repo
|
|
240
|
+
2. `npm install` → `npm run build`
|
|
241
|
+
3. Test locally: `claude mcp add --transport stdio lucid-dev -- node /path/to/lucid/build/index.js`
|
|
242
|
+
4. Open a PR
|
|
243
|
+
|
|
229
244
|
## Tech stack
|
|
230
245
|
|
|
231
246
|
- **Runtime:** Node.js 18+, TypeScript, ES modules
|
package/build/index.js
CHANGED
|
@@ -52,7 +52,7 @@ else {
|
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
53
53
|
// MCP Server
|
|
54
54
|
// ---------------------------------------------------------------------------
|
|
55
|
-
const server = new Server({ name: "lucid", version: "1.9.
|
|
55
|
+
const server = new Server({ name: "lucid", version: "1.9.5" }, { capabilities: { tools: {} } });
|
|
56
56
|
// ---------------------------------------------------------------------------
|
|
57
57
|
// Tool definitions
|
|
58
58
|
// ---------------------------------------------------------------------------
|
package/build/indexer/file.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface FileIndex {
|
|
|
6
6
|
todos: string[];
|
|
7
7
|
language: string;
|
|
8
8
|
}
|
|
9
|
+
/** Build a FileIndex from already-read source — no IO. */
|
|
10
|
+
export declare function buildFileIndex(filepath: string, source: string): FileIndex;
|
|
9
11
|
export declare function indexFile(filepath: string): FileIndex | null;
|
|
10
12
|
export interface UpsertResult {
|
|
11
13
|
observations: string[];
|
package/build/indexer/file.js
CHANGED
|
@@ -73,14 +73,8 @@ function extractGeneric(source) {
|
|
|
73
73
|
}
|
|
74
74
|
return { exports: [], description: "", todos };
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
source = readFileSync(filepath, { encoding: "utf-8" });
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
76
|
+
/** Build a FileIndex from already-read source — no IO. */
|
|
77
|
+
export function buildFileIndex(filepath, source) {
|
|
84
78
|
const ext = extname(filepath).toLowerCase();
|
|
85
79
|
const module = filepath.replace(/\\/g, "/");
|
|
86
80
|
let extracted;
|
|
@@ -103,6 +97,16 @@ export function indexFile(filepath) {
|
|
|
103
97
|
}
|
|
104
98
|
return { module, language, ...extracted };
|
|
105
99
|
}
|
|
100
|
+
export function indexFile(filepath) {
|
|
101
|
+
let source;
|
|
102
|
+
try {
|
|
103
|
+
source = readFileSync(filepath, { encoding: "utf-8" });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return buildFileIndex(filepath, source);
|
|
109
|
+
}
|
|
106
110
|
export function upsertFileIndex(index, source, stmts) {
|
|
107
111
|
const fileHash = sha256(source);
|
|
108
112
|
// Change detection — skip everything se hash-ul e identic
|
package/build/indexer/project.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
2
2
|
import { join, extname, basename } from "path";
|
|
3
|
+
import { buildFileIndex, upsertFileIndex } from "./file.js";
|
|
3
4
|
// ---------------------------------------------------------------------------
|
|
4
5
|
// Helpers
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
@@ -232,54 +233,23 @@ const MAX_SOURCE_FILES = 10_000;
|
|
|
232
233
|
function indexSourceFile(filepath, rootDir, projectName, stmts) {
|
|
233
234
|
const content = readFile(filepath);
|
|
234
235
|
if (!content)
|
|
235
|
-
return [];
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (!m[1].startsWith("_"))
|
|
248
|
-
exports.push(m[1]);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
// Vue SFC — extract component name + defineExpose + named exports from <script>
|
|
252
|
-
if (lang === ".vue") {
|
|
253
|
-
// Component name from filename (always present)
|
|
254
|
-
exports.push(basename(filepath, ".vue"));
|
|
255
|
-
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
256
|
-
if (scriptMatch) {
|
|
257
|
-
const sc = scriptMatch[1];
|
|
258
|
-
// defineExpose({ foo, bar }) — what the component exposes to parents via template refs
|
|
259
|
-
const exposeMatch = sc.match(/defineExpose\(\s*\{([^}]+)\}/);
|
|
260
|
-
if (exposeMatch) {
|
|
261
|
-
for (const m of exposeMatch[1].matchAll(/\b([a-zA-Z_]\w*)\b/g)) {
|
|
262
|
-
if (!["true", "false", "null", "undefined"].includes(m[1]))
|
|
263
|
-
exports.push(m[1]);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
// Named exports (composables, types re-exported from SFC)
|
|
267
|
-
for (const m of sc.matchAll(/export\s+(?:async\s+)?(?:function|class|const|type|interface)\s+(\w+)/g)) {
|
|
268
|
-
exports.push(m[1]);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (exports.length === 0)
|
|
273
|
-
return [];
|
|
274
|
-
// Cale relativă față de rădăcina proiectului
|
|
275
|
-
const relPath = filepath.replace(/\\/g, "/").replace(rootDir.replace(/\\/g, "/") + "/", "");
|
|
276
|
-
const obs = [`exports from ${relPath}: ${exports.slice(0, 10).join(", ")}`];
|
|
277
|
-
upsert(stmts, projectName, "project", obs);
|
|
278
|
-
return exports;
|
|
236
|
+
return { exports: [], stored: false };
|
|
237
|
+
// Build structured index (language-aware, single read)
|
|
238
|
+
const fileIdx = buildFileIndex(filepath, content);
|
|
239
|
+
// Store compressed content in source file index → enables get_context() + grep_code()
|
|
240
|
+
const result = upsertFileIndex(fileIdx, content, stmts);
|
|
241
|
+
// Add exports to knowledge graph (for recall())
|
|
242
|
+
if (fileIdx.exports.length > 0) {
|
|
243
|
+
const relPath = filepath.replace(/\\/g, "/").replace(rootDir.replace(/\\/g, "/") + "/", "");
|
|
244
|
+
const obs = [`exports from ${relPath}: ${fileIdx.exports.slice(0, 10).join(", ")}`];
|
|
245
|
+
upsert(stmts, projectName, "project", obs);
|
|
246
|
+
}
|
|
247
|
+
return { exports: fileIdx.exports, stored: result.stored };
|
|
279
248
|
}
|
|
280
249
|
function scanSources(dir, projectName, stmts, results) {
|
|
281
250
|
const rootDir = dir.replace(/\\/g, "/");
|
|
282
251
|
let fileCount = 0;
|
|
252
|
+
let storedCount = 0;
|
|
283
253
|
const exportedSymbols = [];
|
|
284
254
|
function walk(d) {
|
|
285
255
|
if (fileCount >= MAX_SOURCE_FILES)
|
|
@@ -306,9 +276,11 @@ function scanSources(dir, projectName, stmts, results) {
|
|
|
306
276
|
walk(full);
|
|
307
277
|
}
|
|
308
278
|
else if (SOURCE_EXTS.has(extname(entry).toLowerCase())) {
|
|
309
|
-
const syms = indexSourceFile(full, rootDir, projectName, stmts);
|
|
279
|
+
const { exports: syms, stored } = indexSourceFile(full, rootDir, projectName, stmts);
|
|
310
280
|
exportedSymbols.push(...syms);
|
|
311
281
|
fileCount++;
|
|
282
|
+
if (stored)
|
|
283
|
+
storedCount++;
|
|
312
284
|
if (fileCount >= MAX_SOURCE_FILES)
|
|
313
285
|
return;
|
|
314
286
|
}
|
|
@@ -320,7 +292,7 @@ function scanSources(dir, projectName, stmts, results) {
|
|
|
320
292
|
entity: projectName,
|
|
321
293
|
type: "project",
|
|
322
294
|
observations: fileCount,
|
|
323
|
-
source: `${fileCount} source files (${exportedSymbols.length} exports)`,
|
|
295
|
+
source: `${fileCount} source files (${storedCount} compressed, ${exportedSymbols.length} exports)`,
|
|
324
296
|
});
|
|
325
297
|
}
|
|
326
298
|
}
|
package/build/tools/sync.js
CHANGED
|
@@ -6,7 +6,7 @@ import { indexProject } from "../indexer/project.js";
|
|
|
6
6
|
import { computeDiff } from "../retrieval/context.js";
|
|
7
7
|
import { decompress } from "../store/content.js";
|
|
8
8
|
import { implicitRewardFromSync } from "../memory/experience.js";
|
|
9
|
-
const SUPPORTED_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs"]);
|
|
9
|
+
const SUPPORTED_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".vue", ".py", ".go", ".rs"]);
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
// sync_file
|
|
12
12
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a13xu/lucid",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.5",
|
|
4
4
|
"description": "Token-efficient memory, code indexing, and validation for Claude Code agents — SQLite + FTS5, TF-IDF + Qdrant retrieval, AST skeleton pruning, diff-aware context, Logic Guardian drift detection",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
"url": "https://github.com/a13xu/lucid.git"
|
|
40
40
|
},
|
|
41
41
|
"homepage": "https://github.com/a13xu/lucid#readme",
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
42
45
|
"engines": {
|
|
43
46
|
"node": ">=18"
|
|
44
47
|
},
|