@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 CHANGED
@@ -52,7 +52,7 @@ else {
52
52
  // ---------------------------------------------------------------------------
53
53
  // MCP Server
54
54
  // ---------------------------------------------------------------------------
55
- const server = new Server({ name: "lucid", version: "1.9.2" }, { capabilities: { tools: {} } });
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"],
@@ -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
  }
@@ -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";
@@ -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(["node_modules", ".git", "build", "dist", "__pycache__", ".next", "venv", ".venv", "target", ".cache", "coverage", ".nyc_output"]);
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. Surse
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.2",
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": "^11.0.0",
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": "^20.0.0",
52
+ "@types/node": "^22.0.0",
53
53
  "typescript": "^5.4.0"
54
54
  }
55
55
  }