@a13xu/lucid 1.9.2 → 1.9.4
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/build/index.js +2 -2
- package/build/indexer/ast.js +28 -0
- package/build/indexer/file.js +34 -0
- package/build/indexer/project.js +83 -3
- package/package.json +3 -3
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.4" }, { capabilities: { tools: {} } });
|
|
56
56
|
// ---------------------------------------------------------------------------
|
|
57
57
|
// Tool definitions
|
|
58
58
|
// ---------------------------------------------------------------------------
|
|
@@ -178,7 +178,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
178
178
|
type: "object",
|
|
179
179
|
properties: {
|
|
180
180
|
pattern: { type: "string", description: "Regex pattern to search for." },
|
|
181
|
-
language: { type: "string", enum: ["python", "javascript", "typescript", "generic"], description: "Filter by language." },
|
|
181
|
+
language: { type: "string", enum: ["python", "javascript", "typescript", "vue", "generic"], description: "Filter by language." },
|
|
182
182
|
context: { type: "number", description: "Lines of context before/after each match (0-10, default 2)." },
|
|
183
183
|
},
|
|
184
184
|
required: ["pattern"],
|
package/build/indexer/ast.js
CHANGED
|
@@ -108,6 +108,32 @@ function skeletonPython(source) {
|
|
|
108
108
|
return { imports, exports, todos, summary };
|
|
109
109
|
}
|
|
110
110
|
// ---------------------------------------------------------------------------
|
|
111
|
+
// Vue SFC
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
function skeletonVue(source) {
|
|
114
|
+
// Extract <script> or <script setup> block and run TS skeleton on it
|
|
115
|
+
const scriptMatch = source.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
116
|
+
const sk = scriptMatch ? skeletonTS(scriptMatch[1]) : { imports: [], exports: [], todos: [], summary: "" };
|
|
117
|
+
// Prepend Vue macro signatures (defineProps, defineEmits, defineExpose)
|
|
118
|
+
const scriptContent = scriptMatch?.[1] ?? "";
|
|
119
|
+
for (const macro of ["defineProps", "defineEmits", "defineExpose"]) {
|
|
120
|
+
const m = scriptContent.match(new RegExp(`${macro}[\\s\\S]*?(?=\\n\\n|\\n[^\\s]|$)`, "m"));
|
|
121
|
+
if (m)
|
|
122
|
+
sk.exports.unshift(m[0].split("\n")[0].slice(0, 120));
|
|
123
|
+
}
|
|
124
|
+
// HTML comment as summary fallback
|
|
125
|
+
if (!sk.summary) {
|
|
126
|
+
const htmlComment = source.match(/<!--\s*([\s\S]*?)\s*-->/)?.[1];
|
|
127
|
+
if (htmlComment)
|
|
128
|
+
sk.summary = htmlComment.replace(/\n/g, " ").trim().slice(0, 150);
|
|
129
|
+
}
|
|
130
|
+
// Also note top-level template structure (first tag inside <template>)
|
|
131
|
+
const templateMatch = source.match(/<template[^>]*>\s*<(\w[\w-]*)/);
|
|
132
|
+
if (templateMatch)
|
|
133
|
+
sk.exports.unshift(`<template> root: <${templateMatch[1]}>`);
|
|
134
|
+
return sk;
|
|
135
|
+
}
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
111
137
|
// Generic (markdown, yaml, json, etc.)
|
|
112
138
|
// ---------------------------------------------------------------------------
|
|
113
139
|
function skeletonGeneric(source) {
|
|
@@ -135,6 +161,8 @@ export function extractSkeleton(source, language) {
|
|
|
135
161
|
return skeletonTS(source);
|
|
136
162
|
case "python":
|
|
137
163
|
return skeletonPython(source);
|
|
164
|
+
case "vue":
|
|
165
|
+
return skeletonVue(source);
|
|
138
166
|
default:
|
|
139
167
|
return skeletonGeneric(source);
|
|
140
168
|
}
|
package/build/indexer/file.js
CHANGED
|
@@ -36,6 +36,36 @@ function extractPython(source) {
|
|
|
36
36
|
}
|
|
37
37
|
return { exports, description, todos };
|
|
38
38
|
}
|
|
39
|
+
function extractVue(source) {
|
|
40
|
+
const exports = [];
|
|
41
|
+
const todos = [];
|
|
42
|
+
// Extract <script> or <script setup> block
|
|
43
|
+
const scriptMatch = source.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
44
|
+
const scriptContent = scriptMatch?.[1] ?? "";
|
|
45
|
+
// defineExpose({ foo, bar }) — symbols available to parent components
|
|
46
|
+
const exposeMatch = scriptContent.match(/defineExpose\(\s*\{([^}]+)\}/);
|
|
47
|
+
if (exposeMatch) {
|
|
48
|
+
for (const m of exposeMatch[1].matchAll(/\b([a-zA-Z_]\w*)\b/g)) {
|
|
49
|
+
if (!["true", "false", "null", "undefined"].includes(m[1])) {
|
|
50
|
+
exports.push(m[1]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Regular named exports inside <script> (non-setup composables, types, etc.)
|
|
55
|
+
for (const m of scriptContent.matchAll(/export\s+(?:async\s+)?(?:function|class|const|type|interface|enum)\s+(\w+)/g)) {
|
|
56
|
+
exports.push(m[1]);
|
|
57
|
+
}
|
|
58
|
+
// TODOs from entire SFC (template + script + style)
|
|
59
|
+
for (const m of source.matchAll(/\/\/\s*(TODO|FIXME|HACK)[:\s]+(.+)/gi)) {
|
|
60
|
+
todos.push(`${m[1]}: ${m[2].trim()}`);
|
|
61
|
+
}
|
|
62
|
+
// Description: first HTML comment, then first JSDoc in script
|
|
63
|
+
const htmlComment = source.match(/<!--\s*([\s\S]*?)\s*-->/)?.[1] ?? "";
|
|
64
|
+
const jsdoc = scriptContent.match(/^\/\*\*([\s\S]*?)\*\//m)?.[1] ?? "";
|
|
65
|
+
const raw = htmlComment || jsdoc;
|
|
66
|
+
const description = raw.replace(/\s*\*\s*/g, " ").trim().slice(0, 200);
|
|
67
|
+
return { exports, description, todos };
|
|
68
|
+
}
|
|
39
69
|
function extractGeneric(source) {
|
|
40
70
|
const todos = [];
|
|
41
71
|
for (const m of source.matchAll(/(?:\/\/|#)\s*(TODO|FIXME|HACK)[:\s]+(.+)/gi)) {
|
|
@@ -63,6 +93,10 @@ export function indexFile(filepath) {
|
|
|
63
93
|
extracted = extractPython(source);
|
|
64
94
|
language = "python";
|
|
65
95
|
}
|
|
96
|
+
else if (ext === ".vue") {
|
|
97
|
+
extracted = extractVue(source);
|
|
98
|
+
language = "vue";
|
|
99
|
+
}
|
|
66
100
|
else {
|
|
67
101
|
extracted = extractGeneric(source);
|
|
68
102
|
language = "generic";
|
package/build/indexer/project.js
CHANGED
|
@@ -181,9 +181,53 @@ function indexLogicGuardianYaml(path, stmts, results) {
|
|
|
181
181
|
results.push({ entity: `${patternMatches.length} drift patterns`, type: "pattern", observations: patternMatches.length, source: "logic-guardian.yaml" });
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
function indexNuxtConfig(path, projectName, stmts, results) {
|
|
185
|
+
const content = readFile(path);
|
|
186
|
+
if (!content)
|
|
187
|
+
return;
|
|
188
|
+
const obs = [`nuxt config: ${basename(path)}`];
|
|
189
|
+
// Modules list: modules: ['@nuxtjs/...', ...]
|
|
190
|
+
const modulesMatch = content.match(/modules\s*:\s*\[([^\]]+)\]/);
|
|
191
|
+
if (modulesMatch) {
|
|
192
|
+
const modules = [...modulesMatch[1].matchAll(/['"`](@?[\w/@-]+)['"`]/g)].map((m) => m[1]);
|
|
193
|
+
if (modules.length > 0)
|
|
194
|
+
obs.push(`nuxt modules: ${modules.join(", ")}`);
|
|
195
|
+
}
|
|
196
|
+
// extends (Nuxt layers)
|
|
197
|
+
const extendsMatch = content.match(/extends\s*:\s*\[?['"`]([^'"`]+)['"`]/);
|
|
198
|
+
if (extendsMatch)
|
|
199
|
+
obs.push(`extends layer: ${extendsMatch[1]}`);
|
|
200
|
+
// ssr setting
|
|
201
|
+
const ssrMatch = content.match(/ssr\s*:\s*(true|false)/);
|
|
202
|
+
if (ssrMatch)
|
|
203
|
+
obs.push(`ssr: ${ssrMatch[1]}`);
|
|
204
|
+
// runtimeConfig public keys
|
|
205
|
+
const rtMatch = content.match(/runtimeConfig\s*:\s*\{([\s\S]*?)(?=\n\s{0,4}\w|\n\})/);
|
|
206
|
+
if (rtMatch) {
|
|
207
|
+
const keys = [...rtMatch[1].matchAll(/^\s{2,}(\w+)\s*:/gm)].map((m) => m[1]);
|
|
208
|
+
if (keys.length > 0)
|
|
209
|
+
obs.push(`runtimeConfig keys: ${keys.slice(0, 10).join(", ")}`);
|
|
210
|
+
}
|
|
211
|
+
upsert(stmts, projectName, "project", obs);
|
|
212
|
+
results.push({ entity: projectName, type: "project", observations: obs.length, source: basename(path) });
|
|
213
|
+
}
|
|
184
214
|
// Source file indexing — extrage exporturi, clase, funcții principale
|
|
185
|
-
const SOURCE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs"]);
|
|
186
|
-
const SKIP_DIRS = new Set([
|
|
215
|
+
const SOURCE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".vue", ".py", ".go", ".rs"]);
|
|
216
|
+
const SKIP_DIRS = new Set([
|
|
217
|
+
"node_modules", ".git",
|
|
218
|
+
// Generic build output
|
|
219
|
+
"build", "dist",
|
|
220
|
+
// Python
|
|
221
|
+
"__pycache__", "venv", ".venv",
|
|
222
|
+
// Rust
|
|
223
|
+
"target",
|
|
224
|
+
// Next.js
|
|
225
|
+
".next",
|
|
226
|
+
// Nuxt
|
|
227
|
+
".nuxt", ".output",
|
|
228
|
+
// General
|
|
229
|
+
".cache", "coverage", ".nyc_output",
|
|
230
|
+
]);
|
|
187
231
|
const MAX_SOURCE_FILES = 10_000;
|
|
188
232
|
function indexSourceFile(filepath, rootDir, projectName, stmts) {
|
|
189
233
|
const content = readFile(filepath);
|
|
@@ -204,6 +248,27 @@ function indexSourceFile(filepath, rootDir, projectName, stmts) {
|
|
|
204
248
|
exports.push(m[1]);
|
|
205
249
|
}
|
|
206
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
|
+
}
|
|
207
272
|
if (exports.length === 0)
|
|
208
273
|
return [];
|
|
209
274
|
// Cale relativă față de rădăcina proiectului
|
|
@@ -314,7 +379,22 @@ export function indexProject(directory, stmts) {
|
|
|
314
379
|
if (existsSync(join(dir, "logic-guardian.yaml"))) {
|
|
315
380
|
indexLogicGuardianYaml(join(dir, "logic-guardian.yaml"), stmts, results);
|
|
316
381
|
}
|
|
317
|
-
// 7.
|
|
382
|
+
// 7. Nuxt config files (nuxt.config.ts/.js, app.config.ts)
|
|
383
|
+
for (const p of ["nuxt.config.ts", "nuxt.config.js", "nuxt.config.mts"]) {
|
|
384
|
+
const full = join(dir, p);
|
|
385
|
+
if (existsSync(full)) {
|
|
386
|
+
indexNuxtConfig(full, projectName, stmts, results);
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
for (const p of ["app.config.ts", "app.config.js"]) {
|
|
391
|
+
const full = join(dir, p);
|
|
392
|
+
if (existsSync(full)) {
|
|
393
|
+
indexNuxtConfig(full, projectName, stmts, results);
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// 8. Surse
|
|
318
398
|
scanSources(dir, projectName, stmts, results);
|
|
319
399
|
return results;
|
|
320
400
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a13xu/lucid",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.4",
|
|
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": {
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
47
|
-
"better-sqlite3": "^
|
|
47
|
+
"better-sqlite3": "^12.0.0",
|
|
48
48
|
"zod": "^3.23.8"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/better-sqlite3": "^7.6.11",
|
|
52
|
-
"@types/node": "^
|
|
52
|
+
"@types/node": "^22.0.0",
|
|
53
53
|
"typescript": "^5.4.0"
|
|
54
54
|
}
|
|
55
55
|
}
|