@liendev/lien 0.13.0 → 0.14.0

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/dist/index.js CHANGED
@@ -106,59 +106,9 @@ var init_schema = __esm({
106
106
  }
107
107
  });
108
108
 
109
- // src/config/merge.ts
110
- function deepMergeConfig(defaults, user) {
111
- return {
112
- version: user.version ?? defaults.version,
113
- core: {
114
- ...defaults.core,
115
- ...user.core
116
- },
117
- chunking: {
118
- ...defaults.chunking,
119
- ...user.chunking
120
- },
121
- mcp: {
122
- ...defaults.mcp,
123
- ...user.mcp
124
- },
125
- gitDetection: {
126
- ...defaults.gitDetection,
127
- ...user.gitDetection
128
- },
129
- fileWatching: {
130
- ...defaults.fileWatching,
131
- ...user.fileWatching
132
- },
133
- frameworks: user.frameworks ?? defaults.frameworks
134
- };
135
- }
136
- function detectNewFields(before, after) {
137
- const newFields = [];
138
- for (const key of Object.keys(after)) {
139
- if (!(key in before)) {
140
- newFields.push(key);
141
- continue;
142
- }
143
- if (typeof after[key] === "object" && after[key] !== null && !Array.isArray(after[key])) {
144
- const beforeSection = before[key] || {};
145
- const afterSection = after[key];
146
- for (const nestedKey of Object.keys(afterSection)) {
147
- if (!(nestedKey in beforeSection)) {
148
- newFields.push(`${key}.${nestedKey}`);
149
- }
150
- }
151
- }
152
- }
153
- return newFields;
154
- }
155
- var init_merge = __esm({
156
- "src/config/merge.ts"() {
157
- "use strict";
158
- }
159
- });
160
-
161
109
  // src/config/migration.ts
110
+ import fs from "fs/promises";
111
+ import path from "path";
162
112
  function needsMigration(config) {
163
113
  if (!config) {
164
114
  return false;
@@ -248,6 +198,36 @@ function migrateConfig(oldConfig) {
248
198
  }
249
199
  return newConfig;
250
200
  }
201
+ async function migrateConfigFile(rootDir = process.cwd()) {
202
+ const configPath = path.join(rootDir, ".lien.config.json");
203
+ try {
204
+ const configContent = await fs.readFile(configPath, "utf-8");
205
+ const oldConfig = JSON.parse(configContent);
206
+ if (!needsMigration(oldConfig)) {
207
+ return {
208
+ migrated: false,
209
+ config: oldConfig
210
+ };
211
+ }
212
+ const newConfig = migrateConfig(oldConfig);
213
+ const backupPath = `${configPath}.v0.2.0.backup`;
214
+ await fs.copyFile(configPath, backupPath);
215
+ await fs.writeFile(configPath, JSON.stringify(newConfig, null, 2) + "\n", "utf-8");
216
+ return {
217
+ migrated: true,
218
+ backupPath,
219
+ config: newConfig
220
+ };
221
+ } catch (error) {
222
+ if (error.code === "ENOENT") {
223
+ return {
224
+ migrated: false,
225
+ config: defaultConfig
226
+ };
227
+ }
228
+ throw error;
229
+ }
230
+ }
251
231
  var init_migration = __esm({
252
232
  "src/config/migration.ts"() {
253
233
  "use strict";
@@ -256,6 +236,58 @@ var init_migration = __esm({
256
236
  }
257
237
  });
258
238
 
239
+ // src/config/merge.ts
240
+ function deepMergeConfig(defaults, user) {
241
+ return {
242
+ version: user.version ?? defaults.version,
243
+ core: {
244
+ ...defaults.core,
245
+ ...user.core
246
+ },
247
+ chunking: {
248
+ ...defaults.chunking,
249
+ ...user.chunking
250
+ },
251
+ mcp: {
252
+ ...defaults.mcp,
253
+ ...user.mcp
254
+ },
255
+ gitDetection: {
256
+ ...defaults.gitDetection,
257
+ ...user.gitDetection
258
+ },
259
+ fileWatching: {
260
+ ...defaults.fileWatching,
261
+ ...user.fileWatching
262
+ },
263
+ frameworks: user.frameworks ?? defaults.frameworks
264
+ };
265
+ }
266
+ function detectNewFields(before, after) {
267
+ const newFields = [];
268
+ for (const key of Object.keys(after)) {
269
+ if (!(key in before)) {
270
+ newFields.push(key);
271
+ continue;
272
+ }
273
+ if (typeof after[key] === "object" && after[key] !== null && !Array.isArray(after[key])) {
274
+ const beforeSection = before[key] || {};
275
+ const afterSection = after[key];
276
+ for (const nestedKey of Object.keys(afterSection)) {
277
+ if (!(nestedKey in beforeSection)) {
278
+ newFields.push(`${key}.${nestedKey}`);
279
+ }
280
+ }
281
+ }
282
+ }
283
+ return newFields;
284
+ }
285
+ var init_merge = __esm({
286
+ "src/config/merge.ts"() {
287
+ "use strict";
288
+ }
289
+ });
290
+
259
291
  // src/errors/codes.ts
260
292
  var init_codes = __esm({
261
293
  "src/errors/codes.ts"() {
@@ -346,8 +378,8 @@ var init_errors = __esm({
346
378
  });
347
379
 
348
380
  // src/config/service.ts
349
- import fs6 from "fs/promises";
350
- import path6 from "path";
381
+ import fs8 from "fs/promises";
382
+ import path8 from "path";
351
383
  var ConfigService, configService;
352
384
  var init_service = __esm({
353
385
  "src/config/service.ts"() {
@@ -369,13 +401,13 @@ var init_service = __esm({
369
401
  async load(rootDir = process.cwd()) {
370
402
  const configPath = this.getConfigPath(rootDir);
371
403
  try {
372
- const configContent = await fs6.readFile(configPath, "utf-8");
404
+ const configContent = await fs8.readFile(configPath, "utf-8");
373
405
  const userConfig = JSON.parse(configContent);
374
406
  if (this.needsMigration(userConfig)) {
375
407
  console.log("\u{1F504} Migrating config from v0.2.0 to v0.3.0...");
376
408
  const result = await this.migrate(rootDir);
377
409
  if (result.migrated && result.backupPath) {
378
- const backupFilename = path6.basename(result.backupPath);
410
+ const backupFilename = path8.basename(result.backupPath);
379
411
  console.log(`\u2705 Migration complete! Backup saved as ${backupFilename}`);
380
412
  console.log("\u{1F4DD} Your config now uses the framework-based structure.");
381
413
  }
@@ -431,7 +463,7 @@ ${validation.errors.join("\n")}`,
431
463
  }
432
464
  try {
433
465
  const configJson = JSON.stringify(config, null, 2) + "\n";
434
- await fs6.writeFile(configPath, configJson, "utf-8");
466
+ await fs8.writeFile(configPath, configJson, "utf-8");
435
467
  } catch (error) {
436
468
  throw wrapError(error, "Failed to save configuration", { path: configPath });
437
469
  }
@@ -445,7 +477,7 @@ ${validation.errors.join("\n")}`,
445
477
  async exists(rootDir = process.cwd()) {
446
478
  const configPath = this.getConfigPath(rootDir);
447
479
  try {
448
- await fs6.access(configPath);
480
+ await fs8.access(configPath);
449
481
  return true;
450
482
  } catch {
451
483
  return false;
@@ -462,7 +494,7 @@ ${validation.errors.join("\n")}`,
462
494
  async migrate(rootDir = process.cwd()) {
463
495
  const configPath = this.getConfigPath(rootDir);
464
496
  try {
465
- const configContent = await fs6.readFile(configPath, "utf-8");
497
+ const configContent = await fs8.readFile(configPath, "utf-8");
466
498
  const oldConfig = JSON.parse(configContent);
467
499
  if (!this.needsMigration(oldConfig)) {
468
500
  return {
@@ -480,7 +512,7 @@ ${validation.errors.join("\n")}`,
480
512
  );
481
513
  }
482
514
  const backupPath = `${configPath}.v0.2.0.backup`;
483
- await fs6.copyFile(configPath, backupPath);
515
+ await fs8.copyFile(configPath, backupPath);
484
516
  await this.save(rootDir, newConfig);
485
517
  return {
486
518
  migrated: true,
@@ -578,7 +610,7 @@ ${validation.errors.join("\n")}`,
578
610
  * Get the full path to the config file
579
611
  */
580
612
  getConfigPath(rootDir) {
581
- return path6.join(rootDir, _ConfigService.CONFIG_FILENAME);
613
+ return path8.join(rootDir, _ConfigService.CONFIG_FILENAME);
582
614
  }
583
615
  /**
584
616
  * Validate modern (v0.3.0+) configuration
@@ -742,7 +774,7 @@ ${validation.errors.join("\n")}`,
742
774
  errors.push(`frameworks[${index}] missing required field: path`);
743
775
  } else if (typeof fw.path !== "string") {
744
776
  errors.push(`frameworks[${index}].path must be a string`);
745
- } else if (path6.isAbsolute(fw.path)) {
777
+ } else if (path8.isAbsolute(fw.path)) {
746
778
  errors.push(`frameworks[${index}].path must be relative, got: ${fw.path}`);
747
779
  }
748
780
  if (fw.enabled === void 0) {
@@ -802,12 +834,12 @@ __export(utils_exports, {
802
834
  });
803
835
  import { exec } from "child_process";
804
836
  import { promisify } from "util";
805
- import fs7 from "fs/promises";
806
- import path7 from "path";
837
+ import fs9 from "fs/promises";
838
+ import path9 from "path";
807
839
  async function isGitRepo(rootDir) {
808
840
  try {
809
- const gitDir = path7.join(rootDir, ".git");
810
- await fs7.access(gitDir);
841
+ const gitDir = path9.join(rootDir, ".git");
842
+ await fs9.access(gitDir);
811
843
  return true;
812
844
  } catch {
813
845
  return false;
@@ -846,7 +878,7 @@ async function getChangedFiles(rootDir, fromRef, toRef) {
846
878
  // 10 second timeout for diffs
847
879
  }
848
880
  );
849
- const files = stdout.trim().split("\n").filter(Boolean).map((file) => path7.join(rootDir, file));
881
+ const files = stdout.trim().split("\n").filter(Boolean).map((file) => path9.join(rootDir, file));
850
882
  return files;
851
883
  } catch (error) {
852
884
  throw new Error(`Failed to get changed files: ${error}`);
@@ -861,7 +893,7 @@ async function getChangedFilesInCommit(rootDir, commitSha) {
861
893
  timeout: 1e4
862
894
  }
863
895
  );
864
- const files = stdout.trim().split("\n").filter(Boolean).map((file) => path7.join(rootDir, file));
896
+ const files = stdout.trim().split("\n").filter(Boolean).map((file) => path9.join(rootDir, file));
865
897
  return files;
866
898
  } catch (error) {
867
899
  throw new Error(`Failed to get changed files in commit: ${error}`);
@@ -876,7 +908,7 @@ async function getChangedFilesBetweenCommits(rootDir, fromCommit, toCommit) {
876
908
  timeout: 1e4
877
909
  }
878
910
  );
879
- const files = stdout.trim().split("\n").filter(Boolean).map((file) => path7.join(rootDir, file));
911
+ const files = stdout.trim().split("\n").filter(Boolean).map((file) => path9.join(rootDir, file));
880
912
  return files;
881
913
  } catch (error) {
882
914
  throw new Error(`Failed to get changed files between commits: ${error}`);
@@ -899,21 +931,21 @@ var init_utils = __esm({
899
931
  });
900
932
 
901
933
  // src/vectordb/version.ts
902
- import fs8 from "fs/promises";
903
- import path8 from "path";
934
+ import fs10 from "fs/promises";
935
+ import path10 from "path";
904
936
  async function writeVersionFile(indexPath) {
905
937
  try {
906
- const versionFilePath = path8.join(indexPath, VERSION_FILE);
938
+ const versionFilePath = path10.join(indexPath, VERSION_FILE);
907
939
  const timestamp = Date.now().toString();
908
- await fs8.writeFile(versionFilePath, timestamp, "utf-8");
940
+ await fs10.writeFile(versionFilePath, timestamp, "utf-8");
909
941
  } catch (error) {
910
942
  console.error(`Warning: Failed to write version file: ${error}`);
911
943
  }
912
944
  }
913
945
  async function readVersionFile(indexPath) {
914
946
  try {
915
- const versionFilePath = path8.join(indexPath, VERSION_FILE);
916
- const content = await fs8.readFile(versionFilePath, "utf-8");
947
+ const versionFilePath = path10.join(indexPath, VERSION_FILE);
948
+ const content = await fs10.readFile(versionFilePath, "utf-8");
917
949
  const timestamp = parseInt(content.trim(), 10);
918
950
  return isNaN(timestamp) ? 0 : timestamp;
919
951
  } catch (error) {
@@ -931,8 +963,8 @@ var init_version2 = __esm({
931
963
  // src/indexer/scanner.ts
932
964
  import { glob } from "glob";
933
965
  import ignore from "ignore";
934
- import fs10 from "fs/promises";
935
- import path10 from "path";
966
+ import fs12 from "fs/promises";
967
+ import path12 from "path";
936
968
  async function scanCodebaseWithFrameworks(rootDir, config) {
937
969
  const allFiles = [];
938
970
  for (const framework of config.frameworks) {
@@ -945,16 +977,16 @@ async function scanCodebaseWithFrameworks(rootDir, config) {
945
977
  return allFiles;
946
978
  }
947
979
  async function scanFramework(rootDir, framework) {
948
- const frameworkPath = path10.join(rootDir, framework.path);
949
- const gitignorePath = path10.join(frameworkPath, ".gitignore");
980
+ const frameworkPath = path12.join(rootDir, framework.path);
981
+ const gitignorePath = path12.join(frameworkPath, ".gitignore");
950
982
  let ig = ignore();
951
983
  try {
952
- const gitignoreContent = await fs10.readFile(gitignorePath, "utf-8");
984
+ const gitignoreContent = await fs12.readFile(gitignorePath, "utf-8");
953
985
  ig = ignore().add(gitignoreContent);
954
986
  } catch (e) {
955
- const rootGitignorePath = path10.join(rootDir, ".gitignore");
987
+ const rootGitignorePath = path12.join(rootDir, ".gitignore");
956
988
  try {
957
- const gitignoreContent = await fs10.readFile(rootGitignorePath, "utf-8");
989
+ const gitignoreContent = await fs12.readFile(rootGitignorePath, "utf-8");
958
990
  ig = ignore().add(gitignoreContent);
959
991
  } catch (e2) {
960
992
  }
@@ -976,15 +1008,15 @@ async function scanFramework(rootDir, framework) {
976
1008
  }
977
1009
  const uniqueFiles = Array.from(new Set(allFiles));
978
1010
  return uniqueFiles.filter((file) => !ig.ignores(file)).map((file) => {
979
- return framework.path === "." ? file : path10.join(framework.path, file);
1011
+ return framework.path === "." ? file : path12.join(framework.path, file);
980
1012
  });
981
1013
  }
982
1014
  async function scanCodebase(options) {
983
1015
  const { rootDir, includePatterns = [], excludePatterns = [] } = options;
984
- const gitignorePath = path10.join(rootDir, ".gitignore");
1016
+ const gitignorePath = path12.join(rootDir, ".gitignore");
985
1017
  let ig = ignore();
986
1018
  try {
987
- const gitignoreContent = await fs10.readFile(gitignorePath, "utf-8");
1019
+ const gitignoreContent = await fs12.readFile(gitignorePath, "utf-8");
988
1020
  ig = ignore().add(gitignoreContent);
989
1021
  } catch (e) {
990
1022
  }
@@ -1011,12 +1043,12 @@ async function scanCodebase(options) {
1011
1043
  }
1012
1044
  const uniqueFiles = Array.from(new Set(allFiles));
1013
1045
  return uniqueFiles.filter((file) => {
1014
- const relativePath = path10.relative(rootDir, file);
1046
+ const relativePath = path12.relative(rootDir, file);
1015
1047
  return !ig.ignores(relativePath);
1016
1048
  });
1017
1049
  }
1018
1050
  function detectLanguage(filepath) {
1019
- const ext = path10.extname(filepath).toLowerCase();
1051
+ const ext = path12.extname(filepath).toLowerCase();
1020
1052
  const languageMap = {
1021
1053
  ".ts": "typescript",
1022
1054
  ".tsx": "typescript",
@@ -1355,6 +1387,7 @@ var init_symbol_extractor = __esm({
1355
1387
  import Parser from "tree-sitter";
1356
1388
  import TypeScript from "tree-sitter-typescript";
1357
1389
  import JavaScript from "tree-sitter-javascript";
1390
+ import PHPParser from "tree-sitter-php";
1358
1391
  import { extname } from "path";
1359
1392
  function getParser(language) {
1360
1393
  if (!parserCache.has(language)) {
@@ -1379,6 +1412,8 @@ function detectLanguage2(filePath) {
1379
1412
  case "mjs":
1380
1413
  case "cjs":
1381
1414
  return "javascript";
1415
+ case "php":
1416
+ return "php";
1382
1417
  default:
1383
1418
  return null;
1384
1419
  }
@@ -1411,86 +1446,87 @@ var init_parser = __esm({
1411
1446
  parserCache = /* @__PURE__ */ new Map();
1412
1447
  languageConfig = {
1413
1448
  typescript: TypeScript.typescript,
1414
- javascript: JavaScript
1415
- // Use proper JavaScript parser
1449
+ javascript: JavaScript,
1450
+ php: PHPParser.php
1451
+ // Note: tree-sitter-php exports both 'php' (mixed HTML/PHP) and 'php_only'
1416
1452
  };
1417
1453
  }
1418
1454
  });
1419
1455
 
1420
1456
  // src/indexer/ast/symbols.ts
1421
- function extractSymbolInfo(node, content, parentClass) {
1422
- const type = node.type;
1423
- if (type === "function_declaration" || type === "function") {
1424
- const nameNode = node.childForFieldName("name");
1425
- if (!nameNode) return null;
1426
- return {
1427
- name: nameNode.text,
1428
- type: parentClass ? "method" : "function",
1429
- startLine: node.startPosition.row + 1,
1430
- endLine: node.endPosition.row + 1,
1431
- parentClass,
1432
- signature: extractSignature(node, content),
1433
- parameters: extractParameters(node, content),
1434
- returnType: extractReturnType(node, content),
1435
- complexity: calculateComplexity(node)
1436
- };
1437
- }
1438
- if (type === "arrow_function" || type === "function_expression") {
1439
- const parent = node.parent;
1440
- let name = "anonymous";
1441
- if (parent?.type === "variable_declarator") {
1442
- const nameNode = parent.childForFieldName("name");
1443
- name = nameNode?.text || "anonymous";
1444
- }
1445
- return {
1446
- name,
1447
- type: parentClass ? "method" : "function",
1448
- startLine: node.startPosition.row + 1,
1449
- endLine: node.endPosition.row + 1,
1450
- parentClass,
1451
- signature: extractSignature(node, content),
1452
- parameters: extractParameters(node, content),
1453
- complexity: calculateComplexity(node)
1454
- };
1455
- }
1456
- if (type === "method_definition") {
1457
- const nameNode = node.childForFieldName("name");
1458
- if (!nameNode) return null;
1459
- return {
1460
- name: nameNode.text,
1461
- type: "method",
1462
- startLine: node.startPosition.row + 1,
1463
- endLine: node.endPosition.row + 1,
1464
- parentClass,
1465
- signature: extractSignature(node, content),
1466
- parameters: extractParameters(node, content),
1467
- returnType: extractReturnType(node, content),
1468
- complexity: calculateComplexity(node)
1469
- };
1470
- }
1471
- if (type === "class_declaration") {
1472
- const nameNode = node.childForFieldName("name");
1473
- if (!nameNode) return null;
1474
- return {
1475
- name: nameNode.text,
1476
- type: "class",
1477
- startLine: node.startPosition.row + 1,
1478
- endLine: node.endPosition.row + 1,
1479
- signature: `class ${nameNode.text}`
1480
- };
1481
- }
1482
- if (type === "interface_declaration") {
1483
- const nameNode = node.childForFieldName("name");
1484
- if (!nameNode) return null;
1485
- return {
1486
- name: nameNode.text,
1487
- type: "interface",
1488
- startLine: node.startPosition.row + 1,
1489
- endLine: node.endPosition.row + 1,
1490
- signature: `interface ${nameNode.text}`
1491
- };
1457
+ function extractFunctionInfo(node, content, parentClass) {
1458
+ const nameNode = node.childForFieldName("name");
1459
+ if (!nameNode) return null;
1460
+ return {
1461
+ name: nameNode.text,
1462
+ type: parentClass ? "method" : "function",
1463
+ startLine: node.startPosition.row + 1,
1464
+ endLine: node.endPosition.row + 1,
1465
+ parentClass,
1466
+ signature: extractSignature(node, content),
1467
+ parameters: extractParameters(node, content),
1468
+ returnType: extractReturnType(node, content),
1469
+ complexity: calculateComplexity(node)
1470
+ };
1471
+ }
1472
+ function extractArrowFunctionInfo(node, content, parentClass) {
1473
+ const parent = node.parent;
1474
+ let name = "anonymous";
1475
+ if (parent?.type === "variable_declarator") {
1476
+ const nameNode = parent.childForFieldName("name");
1477
+ name = nameNode?.text || "anonymous";
1492
1478
  }
1493
- return null;
1479
+ return {
1480
+ name,
1481
+ type: parentClass ? "method" : "function",
1482
+ startLine: node.startPosition.row + 1,
1483
+ endLine: node.endPosition.row + 1,
1484
+ parentClass,
1485
+ signature: extractSignature(node, content),
1486
+ parameters: extractParameters(node, content),
1487
+ complexity: calculateComplexity(node)
1488
+ };
1489
+ }
1490
+ function extractMethodInfo(node, content, parentClass) {
1491
+ const nameNode = node.childForFieldName("name");
1492
+ if (!nameNode) return null;
1493
+ return {
1494
+ name: nameNode.text,
1495
+ type: "method",
1496
+ startLine: node.startPosition.row + 1,
1497
+ endLine: node.endPosition.row + 1,
1498
+ parentClass,
1499
+ signature: extractSignature(node, content),
1500
+ parameters: extractParameters(node, content),
1501
+ returnType: extractReturnType(node, content),
1502
+ complexity: calculateComplexity(node)
1503
+ };
1504
+ }
1505
+ function extractClassInfo(node, _content, _parentClass) {
1506
+ const nameNode = node.childForFieldName("name");
1507
+ if (!nameNode) return null;
1508
+ return {
1509
+ name: nameNode.text,
1510
+ type: "class",
1511
+ startLine: node.startPosition.row + 1,
1512
+ endLine: node.endPosition.row + 1,
1513
+ signature: `class ${nameNode.text}`
1514
+ };
1515
+ }
1516
+ function extractInterfaceInfo(node, _content, _parentClass) {
1517
+ const nameNode = node.childForFieldName("name");
1518
+ if (!nameNode) return null;
1519
+ return {
1520
+ name: nameNode.text,
1521
+ type: "interface",
1522
+ startLine: node.startPosition.row + 1,
1523
+ endLine: node.endPosition.row + 1,
1524
+ signature: `interface ${nameNode.text}`
1525
+ };
1526
+ }
1527
+ function extractSymbolInfo(node, content, parentClass) {
1528
+ const extractor = symbolExtractors[node.type];
1529
+ return extractor ? extractor(node, content, parentClass) : null;
1494
1530
  }
1495
1531
  function extractSignature(node, content) {
1496
1532
  const startLine = node.startPosition.row;
@@ -1527,6 +1563,7 @@ function extractReturnType(node, _content) {
1527
1563
  function calculateComplexity(node) {
1528
1564
  let complexity = 1;
1529
1565
  const decisionPoints = [
1566
+ // TypeScript/JavaScript
1530
1567
  "if_statement",
1531
1568
  "while_statement",
1532
1569
  "do_statement",
@@ -1538,8 +1575,11 @@ function calculateComplexity(node) {
1538
1575
  "switch_case",
1539
1576
  "catch_clause",
1540
1577
  "ternary_expression",
1541
- "binary_expression"
1578
+ "binary_expression",
1542
1579
  // For && and ||
1580
+ // PHP
1581
+ "foreach_statement"
1582
+ // PHP foreach loops
1543
1583
  ];
1544
1584
  function traverse(n) {
1545
1585
  if (decisionPoints.includes(n.type)) {
@@ -1580,9 +1620,200 @@ function extractImports(rootNode) {
1580
1620
  traverse(rootNode);
1581
1621
  return imports;
1582
1622
  }
1623
+ var symbolExtractors;
1583
1624
  var init_symbols = __esm({
1584
1625
  "src/indexer/ast/symbols.ts"() {
1585
1626
  "use strict";
1627
+ symbolExtractors = {
1628
+ // TypeScript/JavaScript
1629
+ "function_declaration": extractFunctionInfo,
1630
+ "function": extractFunctionInfo,
1631
+ "arrow_function": extractArrowFunctionInfo,
1632
+ "function_expression": extractArrowFunctionInfo,
1633
+ "method_definition": extractMethodInfo,
1634
+ "class_declaration": extractClassInfo,
1635
+ "interface_declaration": extractInterfaceInfo,
1636
+ // PHP
1637
+ "function_definition": extractFunctionInfo,
1638
+ // PHP functions
1639
+ "method_declaration": extractMethodInfo
1640
+ // PHP methods
1641
+ };
1642
+ }
1643
+ });
1644
+
1645
+ // src/indexer/ast/traversers/typescript.ts
1646
+ var TypeScriptTraverser, JavaScriptTraverser;
1647
+ var init_typescript = __esm({
1648
+ "src/indexer/ast/traversers/typescript.ts"() {
1649
+ "use strict";
1650
+ TypeScriptTraverser = class {
1651
+ targetNodeTypes = [
1652
+ "function_declaration",
1653
+ "function",
1654
+ "interface_declaration",
1655
+ "method_definition",
1656
+ "lexical_declaration",
1657
+ // For const/let with arrow functions
1658
+ "variable_declaration"
1659
+ // For var with functions
1660
+ ];
1661
+ containerTypes = [
1662
+ "class_declaration"
1663
+ // We extract methods, not the class itself
1664
+ ];
1665
+ declarationTypes = [
1666
+ "lexical_declaration",
1667
+ // const/let
1668
+ "variable_declaration"
1669
+ // var
1670
+ ];
1671
+ functionTypes = [
1672
+ "arrow_function",
1673
+ "function_expression",
1674
+ "function"
1675
+ ];
1676
+ shouldExtractChildren(node) {
1677
+ return this.containerTypes.includes(node.type);
1678
+ }
1679
+ isDeclarationWithFunction(node) {
1680
+ return this.declarationTypes.includes(node.type);
1681
+ }
1682
+ getContainerBody(node) {
1683
+ if (node.type === "class_declaration") {
1684
+ return node.childForFieldName("body");
1685
+ }
1686
+ return null;
1687
+ }
1688
+ shouldTraverseChildren(node) {
1689
+ return node.type === "program" || node.type === "export_statement" || node.type === "class_body";
1690
+ }
1691
+ findParentContainerName(node) {
1692
+ let current = node.parent;
1693
+ while (current) {
1694
+ if (current.type === "class_declaration") {
1695
+ const nameNode = current.childForFieldName("name");
1696
+ return nameNode?.text;
1697
+ }
1698
+ current = current.parent;
1699
+ }
1700
+ return void 0;
1701
+ }
1702
+ /**
1703
+ * Check if a declaration node contains a function (arrow, function expression, etc.)
1704
+ */
1705
+ findFunctionInDeclaration(node) {
1706
+ const search2 = (n, depth) => {
1707
+ if (depth > 3) return null;
1708
+ if (this.functionTypes.includes(n.type)) {
1709
+ return n;
1710
+ }
1711
+ for (let i = 0; i < n.childCount; i++) {
1712
+ const child = n.child(i);
1713
+ if (child) {
1714
+ const result = search2(child, depth + 1);
1715
+ if (result) return result;
1716
+ }
1717
+ }
1718
+ return null;
1719
+ };
1720
+ const functionNode = search2(node, 0);
1721
+ return {
1722
+ hasFunction: functionNode !== null,
1723
+ functionNode
1724
+ };
1725
+ }
1726
+ };
1727
+ JavaScriptTraverser = class extends TypeScriptTraverser {
1728
+ };
1729
+ }
1730
+ });
1731
+
1732
+ // src/indexer/ast/traversers/php.ts
1733
+ var PHPTraverser;
1734
+ var init_php = __esm({
1735
+ "src/indexer/ast/traversers/php.ts"() {
1736
+ "use strict";
1737
+ PHPTraverser = class {
1738
+ targetNodeTypes = [
1739
+ "function_definition",
1740
+ // function foo() {}
1741
+ "method_declaration"
1742
+ // public function bar() {}
1743
+ ];
1744
+ containerTypes = [
1745
+ "class_declaration",
1746
+ // We extract methods, not the class itself
1747
+ "trait_declaration",
1748
+ // PHP traits
1749
+ "interface_declaration"
1750
+ // PHP interfaces (for interface methods)
1751
+ ];
1752
+ declarationTypes = [
1753
+ // PHP doesn't have arrow functions or const/let like JS
1754
+ // Functions are always defined with 'function' keyword
1755
+ ];
1756
+ functionTypes = [
1757
+ "function_definition",
1758
+ "method_declaration"
1759
+ ];
1760
+ shouldExtractChildren(node) {
1761
+ return this.containerTypes.includes(node.type);
1762
+ }
1763
+ isDeclarationWithFunction(_node) {
1764
+ return false;
1765
+ }
1766
+ getContainerBody(node) {
1767
+ if (node.type === "class_declaration" || node.type === "trait_declaration" || node.type === "interface_declaration") {
1768
+ return node.childForFieldName("body");
1769
+ }
1770
+ return null;
1771
+ }
1772
+ shouldTraverseChildren(node) {
1773
+ return node.type === "program" || // Top-level PHP file
1774
+ node.type === "php" || // PHP block
1775
+ node.type === "declaration_list";
1776
+ }
1777
+ findParentContainerName(node) {
1778
+ let current = node.parent;
1779
+ while (current) {
1780
+ if (current.type === "class_declaration" || current.type === "trait_declaration") {
1781
+ const nameNode = current.childForFieldName("name");
1782
+ return nameNode?.text;
1783
+ }
1784
+ current = current.parent;
1785
+ }
1786
+ return void 0;
1787
+ }
1788
+ findFunctionInDeclaration(_node) {
1789
+ return {
1790
+ hasFunction: false,
1791
+ functionNode: null
1792
+ };
1793
+ }
1794
+ };
1795
+ }
1796
+ });
1797
+
1798
+ // src/indexer/ast/traversers/index.ts
1799
+ function getTraverser(language) {
1800
+ const traverser = traverserRegistry[language];
1801
+ if (!traverser) {
1802
+ throw new Error(`No traverser available for language: ${language}`);
1803
+ }
1804
+ return traverser;
1805
+ }
1806
+ var traverserRegistry;
1807
+ var init_traversers = __esm({
1808
+ "src/indexer/ast/traversers/index.ts"() {
1809
+ "use strict";
1810
+ init_typescript();
1811
+ init_php();
1812
+ traverserRegistry = {
1813
+ typescript: new TypeScriptTraverser(),
1814
+ javascript: new JavaScriptTraverser(),
1815
+ php: new PHPTraverser()
1816
+ };
1586
1817
  }
1587
1818
  });
1588
1819
 
@@ -1600,20 +1831,18 @@ function chunkByAST(filepath, content, options = {}) {
1600
1831
  const chunks = [];
1601
1832
  const lines = content.split("\n");
1602
1833
  const rootNode = parseResult.tree.rootNode;
1834
+ const traverser = getTraverser(language);
1603
1835
  const fileImports = extractImports(rootNode);
1604
- const topLevelNodes = findTopLevelNodes(rootNode);
1836
+ const topLevelNodes = findTopLevelNodes(rootNode, traverser);
1605
1837
  for (const node of topLevelNodes) {
1606
1838
  let actualNode = node;
1607
- if (node.type === "lexical_declaration" || node.type === "variable_declaration") {
1608
- const funcNode = findActualFunctionNode(node);
1609
- if (funcNode) {
1610
- actualNode = funcNode;
1839
+ if (traverser.isDeclarationWithFunction(node)) {
1840
+ const declInfo = traverser.findFunctionInDeclaration(node);
1841
+ if (declInfo.functionNode) {
1842
+ actualNode = declInfo.functionNode;
1611
1843
  }
1612
1844
  }
1613
- let parentClassName;
1614
- if (actualNode.type === "method_definition") {
1615
- parentClassName = findParentClassName(actualNode);
1616
- }
1845
+ const parentClassName = traverser.findParentContainerName(actualNode);
1617
1846
  const symbolInfo = extractSymbolInfo(actualNode, content, parentClassName);
1618
1847
  const nodeContent = getNodeContent(node, lines);
1619
1848
  chunks.push(createChunk(filepath, node, nodeContent, symbolInfo, fileImports, language));
@@ -1634,57 +1863,28 @@ function chunkByAST(filepath, content, options = {}) {
1634
1863
  chunks.sort((a, b) => a.metadata.startLine - b.metadata.startLine);
1635
1864
  return chunks;
1636
1865
  }
1637
- function findParentClassName(methodNode) {
1638
- let current = methodNode.parent;
1639
- while (current) {
1640
- if (current.type === "class_declaration") {
1641
- const nameNode = current.childForFieldName("name");
1642
- return nameNode?.text;
1643
- }
1644
- current = current.parent;
1645
- }
1646
- return void 0;
1647
- }
1648
- function findTopLevelNodes(rootNode) {
1866
+ function findTopLevelNodes(rootNode, traverser) {
1649
1867
  const nodes = [];
1650
- const targetTypes = [
1651
- "function_declaration",
1652
- "function",
1653
- // Note: 'class_declaration' is NOT included here - we extract methods individually
1654
- "interface_declaration",
1655
- "method_definition",
1656
- "lexical_declaration",
1657
- // For const/let with arrow functions
1658
- "variable_declaration"
1659
- // For var with functions
1660
- ];
1661
1868
  function traverse(node, depth) {
1662
- if ((node.type === "lexical_declaration" || node.type === "variable_declaration") && depth === 0) {
1663
- const hasFunction = findFunctionInDeclaration(node);
1664
- if (hasFunction) {
1869
+ if (traverser.isDeclarationWithFunction(node) && depth === 0) {
1870
+ const declInfo = traverser.findFunctionInDeclaration(node);
1871
+ if (declInfo.hasFunction) {
1665
1872
  nodes.push(node);
1666
1873
  return;
1667
1874
  }
1668
1875
  }
1669
- if (depth <= 1 && targetTypes.includes(node.type)) {
1876
+ if (depth <= 1 && traverser.targetNodeTypes.includes(node.type)) {
1670
1877
  nodes.push(node);
1671
1878
  return;
1672
1879
  }
1673
- if (node.type === "class_body") {
1674
- for (let i = 0; i < node.namedChildCount; i++) {
1675
- const child = node.namedChild(i);
1676
- if (child) traverse(child, depth);
1677
- }
1678
- return;
1679
- }
1680
- if (node.type === "class_declaration") {
1681
- const body = node.childForFieldName("body");
1880
+ if (traverser.shouldExtractChildren(node)) {
1881
+ const body = traverser.getContainerBody(node);
1682
1882
  if (body) {
1683
1883
  traverse(body, depth + 1);
1684
1884
  }
1685
1885
  return;
1686
1886
  }
1687
- if (node.type === "program" || node.type === "export_statement") {
1887
+ if (traverser.shouldTraverseChildren(node)) {
1688
1888
  for (let i = 0; i < node.namedChildCount; i++) {
1689
1889
  const child = node.namedChild(i);
1690
1890
  if (child) traverse(child, depth);
@@ -1694,41 +1894,6 @@ function findTopLevelNodes(rootNode) {
1694
1894
  traverse(rootNode, 0);
1695
1895
  return nodes;
1696
1896
  }
1697
- function findFunctionInDeclaration(node) {
1698
- const functionTypes = ["arrow_function", "function_expression", "function"];
1699
- function search(n, depth) {
1700
- if (depth > 3) return false;
1701
- if (functionTypes.includes(n.type)) {
1702
- return true;
1703
- }
1704
- for (let i = 0; i < n.childCount; i++) {
1705
- const child = n.child(i);
1706
- if (child && search(child, depth + 1)) {
1707
- return true;
1708
- }
1709
- }
1710
- return false;
1711
- }
1712
- return search(node, 0);
1713
- }
1714
- function findActualFunctionNode(node) {
1715
- const functionTypes = ["arrow_function", "function_expression", "function"];
1716
- function search(n, depth) {
1717
- if (depth > 3) return null;
1718
- if (functionTypes.includes(n.type)) {
1719
- return n;
1720
- }
1721
- for (let i = 0; i < n.childCount; i++) {
1722
- const child = n.child(i);
1723
- if (child) {
1724
- const result = search(child, depth + 1);
1725
- if (result) return result;
1726
- }
1727
- }
1728
- return null;
1729
- }
1730
- return search(node, 0);
1731
- }
1732
1897
  function getNodeContent(node, lines) {
1733
1898
  const startLine = node.startPosition.row;
1734
1899
  const endLine = node.endPosition.row;
@@ -1770,67 +1935,323 @@ function createChunk(filepath, node, content, symbolInfo, imports, language) {
1770
1935
  }
1771
1936
  };
1772
1937
  }
1773
- function extractUncoveredCode(lines, coveredRanges, filepath, minChunkSize, imports, language) {
1774
- const chunks = [];
1938
+ function findUncoveredRanges(coveredRanges, totalLines) {
1939
+ const uncoveredRanges = [];
1775
1940
  let currentStart = 0;
1776
- coveredRanges.sort((a, b) => a.start - b.start);
1777
- for (const range of coveredRanges) {
1941
+ const sortedRanges = [...coveredRanges].sort((a, b) => a.start - b.start);
1942
+ for (const range of sortedRanges) {
1778
1943
  if (currentStart < range.start) {
1779
- const uncoveredLines = lines.slice(currentStart, range.start);
1780
- const content = uncoveredLines.join("\n").trim();
1781
- if (content.length > 0 && uncoveredLines.length >= minChunkSize) {
1782
- chunks.push({
1783
- content,
1784
- metadata: {
1785
- file: filepath,
1786
- startLine: currentStart + 1,
1787
- endLine: range.start,
1788
- type: "block",
1789
- language,
1790
- // Empty symbols for uncovered code (imports, exports, etc.)
1791
- symbols: { functions: [], classes: [], interfaces: [] },
1792
- imports
1793
- }
1794
- });
1795
- }
1944
+ uncoveredRanges.push({
1945
+ start: currentStart,
1946
+ end: range.start - 1
1947
+ });
1796
1948
  }
1797
1949
  currentStart = range.end + 1;
1798
1950
  }
1799
- if (currentStart < lines.length) {
1800
- const uncoveredLines = lines.slice(currentStart);
1801
- const content = uncoveredLines.join("\n").trim();
1802
- if (content.length > 0 && uncoveredLines.length >= minChunkSize) {
1951
+ if (currentStart < totalLines) {
1952
+ uncoveredRanges.push({
1953
+ start: currentStart,
1954
+ end: totalLines - 1
1955
+ });
1956
+ }
1957
+ return uncoveredRanges;
1958
+ }
1959
+ function createChunkFromRange(range, lines, filepath, language, imports) {
1960
+ const uncoveredLines = lines.slice(range.start, range.end + 1);
1961
+ const content = uncoveredLines.join("\n").trim();
1962
+ return {
1963
+ content,
1964
+ metadata: {
1965
+ file: filepath,
1966
+ startLine: range.start + 1,
1967
+ endLine: range.end + 1,
1968
+ type: "block",
1969
+ language,
1970
+ // Empty symbols for uncovered code (imports, exports, etc.)
1971
+ symbols: { functions: [], classes: [], interfaces: [] },
1972
+ imports
1973
+ }
1974
+ };
1975
+ }
1976
+ function isValidChunk(chunk, minChunkSize) {
1977
+ const lineCount = chunk.metadata.endLine - chunk.metadata.startLine + 1;
1978
+ return chunk.content.length > 0 && lineCount >= minChunkSize;
1979
+ }
1980
+ function extractUncoveredCode(lines, coveredRanges, filepath, minChunkSize, imports, language) {
1981
+ const uncoveredRanges = findUncoveredRanges(coveredRanges, lines.length);
1982
+ return uncoveredRanges.map((range) => createChunkFromRange(range, lines, filepath, language, imports)).filter((chunk) => isValidChunk(chunk, minChunkSize));
1983
+ }
1984
+ function shouldUseAST(filepath) {
1985
+ return isASTSupported(filepath);
1986
+ }
1987
+ var init_chunker = __esm({
1988
+ "src/indexer/ast/chunker.ts"() {
1989
+ "use strict";
1990
+ init_parser();
1991
+ init_symbols();
1992
+ init_traversers();
1993
+ }
1994
+ });
1995
+
1996
+ // src/indexer/liquid-chunker.ts
1997
+ function extractSchemaName(schemaContent) {
1998
+ try {
1999
+ let jsonContent = schemaContent.replace(/\{%-?\s*schema\s*-?%\}/g, "").replace(/\{%-?\s*endschema\s*-?%\}/g, "").trim();
2000
+ const schema = JSON.parse(jsonContent);
2001
+ return typeof schema.name === "string" ? schema.name : void 0;
2002
+ } catch (error) {
2003
+ }
2004
+ return void 0;
2005
+ }
2006
+ function removeComments(content) {
2007
+ return content.replace(/\{%-?\s*comment\s*-?%\}[\s\S]*?\{%-?\s*endcomment\s*-?%\}/g, "");
2008
+ }
2009
+ function extractRenderTags(contentWithoutComments) {
2010
+ const dependencies = /* @__PURE__ */ new Set();
2011
+ const renderPattern = /\{%-?\s*render\s+['"]([^'"]+)['"]/g;
2012
+ let match;
2013
+ while ((match = renderPattern.exec(contentWithoutComments)) !== null) {
2014
+ dependencies.add(match[1]);
2015
+ }
2016
+ const includePattern = /\{%-?\s*include\s+['"]([^'"]+)['"]/g;
2017
+ while ((match = includePattern.exec(contentWithoutComments)) !== null) {
2018
+ dependencies.add(match[1]);
2019
+ }
2020
+ const sectionPattern = /\{%-?\s*section\s+['"]([^'"]+)['"]/g;
2021
+ while ((match = sectionPattern.exec(contentWithoutComments)) !== null) {
2022
+ dependencies.add(match[1]);
2023
+ }
2024
+ return Array.from(dependencies);
2025
+ }
2026
+ function findLiquidBlocks(content) {
2027
+ const lines = content.split("\n");
2028
+ const blocks = [];
2029
+ const blockPatterns = [
2030
+ { type: "schema", start: /\{%-?\s*schema\s*-?%\}/, end: /\{%-?\s*endschema\s*-?%\}/ },
2031
+ { type: "style", start: /\{%-?\s*style\s*-?%\}/, end: /\{%-?\s*endstyle\s*-?%\}/ },
2032
+ { type: "javascript", start: /\{%-?\s*javascript\s*-?%\}/, end: /\{%-?\s*endjavascript\s*-?%\}/ }
2033
+ ];
2034
+ for (const pattern of blockPatterns) {
2035
+ let searchStart = 0;
2036
+ while (searchStart < lines.length) {
2037
+ const startIdx = lines.findIndex(
2038
+ (line, idx) => idx >= searchStart && pattern.start.test(line)
2039
+ );
2040
+ if (startIdx === -1) break;
2041
+ const endIdx = lines.findIndex(
2042
+ (line, idx) => idx >= startIdx && pattern.end.test(line)
2043
+ );
2044
+ if (endIdx === -1) {
2045
+ break;
2046
+ }
2047
+ const blockContent = lines.slice(startIdx, endIdx + 1).join("\n");
2048
+ blocks.push({
2049
+ type: pattern.type,
2050
+ startLine: startIdx,
2051
+ endLine: endIdx,
2052
+ content: blockContent
2053
+ });
2054
+ searchStart = endIdx + 1;
2055
+ }
2056
+ }
2057
+ return blocks.sort((a, b) => a.startLine - b.startLine);
2058
+ }
2059
+ function chunkLiquidFile(filepath, content, chunkSize = 75, chunkOverlap = 10) {
2060
+ const lines = content.split("\n");
2061
+ const blocks = findLiquidBlocks(content);
2062
+ const chunks = [];
2063
+ const contentWithoutComments = removeComments(content);
2064
+ const linesWithoutComments = contentWithoutComments.split("\n");
2065
+ const coveredLines = /* @__PURE__ */ new Set();
2066
+ for (const block of blocks) {
2067
+ for (let i = block.startLine; i <= block.endLine; i++) {
2068
+ coveredLines.add(i);
2069
+ }
2070
+ let symbolName;
2071
+ if (block.type === "schema") {
2072
+ symbolName = extractSchemaName(block.content);
2073
+ }
2074
+ const blockContentWithoutComments = linesWithoutComments.slice(block.startLine, block.endLine + 1).join("\n");
2075
+ const imports = extractRenderTags(blockContentWithoutComments);
2076
+ const blockLineCount = block.endLine - block.startLine + 1;
2077
+ const maxBlockSize = chunkSize * 3;
2078
+ if (blockLineCount <= maxBlockSize) {
1803
2079
  chunks.push({
1804
- content,
2080
+ content: block.content,
1805
2081
  metadata: {
1806
2082
  file: filepath,
1807
- startLine: currentStart + 1,
1808
- endLine: lines.length,
2083
+ startLine: block.startLine + 1,
2084
+ // 1-indexed
2085
+ endLine: block.endLine + 1,
2086
+ language: "liquid",
1809
2087
  type: "block",
1810
- language,
1811
- // Empty symbols for uncovered code (imports, exports, etc.)
1812
- symbols: { functions: [], classes: [], interfaces: [] },
1813
- imports
2088
+ symbolName,
2089
+ symbolType: block.type,
2090
+ imports: imports.length > 0 ? imports : void 0
1814
2091
  }
1815
2092
  });
2093
+ } else {
2094
+ const blockLines = block.content.split("\n");
2095
+ for (let offset = 0; offset < blockLines.length; offset += chunkSize - chunkOverlap) {
2096
+ const endOffset = Math.min(offset + chunkSize, blockLines.length);
2097
+ const chunkContent = blockLines.slice(offset, endOffset).join("\n");
2098
+ if (chunkContent.trim().length > 0) {
2099
+ chunks.push({
2100
+ content: chunkContent,
2101
+ metadata: {
2102
+ file: filepath,
2103
+ startLine: block.startLine + offset + 1,
2104
+ // 1-indexed
2105
+ endLine: block.startLine + endOffset,
2106
+ // 1-indexed (endOffset already accounts for exclusivity)
2107
+ language: "liquid",
2108
+ type: "block",
2109
+ symbolName,
2110
+ // Preserve symbol name for all split chunks
2111
+ symbolType: block.type,
2112
+ imports: imports.length > 0 ? imports : void 0
2113
+ }
2114
+ });
2115
+ }
2116
+ if (endOffset >= blockLines.length) break;
2117
+ }
1816
2118
  }
1817
2119
  }
1818
- return chunks;
2120
+ let currentChunk = [];
2121
+ let chunkStartLine = 0;
2122
+ for (let i = 0; i < lines.length; i++) {
2123
+ if (coveredLines.has(i)) {
2124
+ if (currentChunk.length > 0) {
2125
+ const chunkContent = currentChunk.join("\n");
2126
+ if (chunkContent.trim().length > 0) {
2127
+ const cleanedChunk = linesWithoutComments.slice(chunkStartLine, i).join("\n");
2128
+ const imports = extractRenderTags(cleanedChunk);
2129
+ chunks.push({
2130
+ content: chunkContent,
2131
+ metadata: {
2132
+ file: filepath,
2133
+ startLine: chunkStartLine + 1,
2134
+ endLine: i,
2135
+ language: "liquid",
2136
+ type: "template",
2137
+ imports: imports.length > 0 ? imports : void 0
2138
+ }
2139
+ });
2140
+ }
2141
+ currentChunk = [];
2142
+ }
2143
+ continue;
2144
+ }
2145
+ if (currentChunk.length === 0) {
2146
+ chunkStartLine = i;
2147
+ }
2148
+ currentChunk.push(lines[i]);
2149
+ if (currentChunk.length >= chunkSize) {
2150
+ const chunkContent = currentChunk.join("\n");
2151
+ if (chunkContent.trim().length > 0) {
2152
+ const cleanedChunk = linesWithoutComments.slice(chunkStartLine, i + 1).join("\n");
2153
+ const imports = extractRenderTags(cleanedChunk);
2154
+ chunks.push({
2155
+ content: chunkContent,
2156
+ metadata: {
2157
+ file: filepath,
2158
+ startLine: chunkStartLine + 1,
2159
+ endLine: i + 1,
2160
+ language: "liquid",
2161
+ type: "template",
2162
+ imports: imports.length > 0 ? imports : void 0
2163
+ }
2164
+ });
2165
+ }
2166
+ currentChunk = currentChunk.slice(-chunkOverlap);
2167
+ chunkStartLine = Math.max(0, i + 1 - chunkOverlap);
2168
+ }
2169
+ }
2170
+ if (currentChunk.length > 0) {
2171
+ const chunkContent = currentChunk.join("\n");
2172
+ if (chunkContent.trim().length === 0) {
2173
+ return chunks.sort((a, b) => a.metadata.startLine - b.metadata.startLine);
2174
+ }
2175
+ const cleanedChunk = linesWithoutComments.slice(chunkStartLine, lines.length).join("\n");
2176
+ const imports = extractRenderTags(cleanedChunk);
2177
+ chunks.push({
2178
+ content: chunkContent,
2179
+ metadata: {
2180
+ file: filepath,
2181
+ startLine: chunkStartLine + 1,
2182
+ endLine: lines.length,
2183
+ language: "liquid",
2184
+ type: "template",
2185
+ imports: imports.length > 0 ? imports : void 0
2186
+ }
2187
+ });
2188
+ }
2189
+ return chunks.sort((a, b) => a.metadata.startLine - b.metadata.startLine);
1819
2190
  }
1820
- function shouldUseAST(filepath) {
1821
- return isASTSupported(filepath);
2191
+ var init_liquid_chunker = __esm({
2192
+ "src/indexer/liquid-chunker.ts"() {
2193
+ "use strict";
2194
+ }
2195
+ });
2196
+
2197
+ // src/indexer/json-template-chunker.ts
2198
+ function extractSectionReferences(jsonContent) {
2199
+ try {
2200
+ const template = JSON.parse(jsonContent);
2201
+ const sectionTypes = /* @__PURE__ */ new Set();
2202
+ if (template.sections && typeof template.sections === "object") {
2203
+ for (const section of Object.values(template.sections)) {
2204
+ if (typeof section === "object" && section !== null && "type" in section && typeof section.type === "string") {
2205
+ sectionTypes.add(section.type);
2206
+ }
2207
+ }
2208
+ }
2209
+ return Array.from(sectionTypes);
2210
+ } catch (error) {
2211
+ console.warn(`[Lien] Failed to parse JSON template: ${error instanceof Error ? error.message : String(error)}`);
2212
+ return [];
2213
+ }
1822
2214
  }
1823
- var init_chunker = __esm({
1824
- "src/indexer/ast/chunker.ts"() {
2215
+ function extractTemplateName(filepath) {
2216
+ const match = filepath.match(/templates\/(.+)\.json$/);
2217
+ return match ? match[1] : void 0;
2218
+ }
2219
+ function chunkJSONTemplate(filepath, content) {
2220
+ if (content.trim().length === 0) {
2221
+ return [];
2222
+ }
2223
+ const lines = content.split("\n");
2224
+ const templateName = extractTemplateName(filepath);
2225
+ const sectionReferences = extractSectionReferences(content);
2226
+ return [{
2227
+ content,
2228
+ metadata: {
2229
+ file: filepath,
2230
+ startLine: 1,
2231
+ endLine: lines.length,
2232
+ language: "json",
2233
+ type: "template",
2234
+ symbolName: templateName,
2235
+ symbolType: "template",
2236
+ imports: sectionReferences.length > 0 ? sectionReferences : void 0
2237
+ }
2238
+ }];
2239
+ }
2240
+ var init_json_template_chunker = __esm({
2241
+ "src/indexer/json-template-chunker.ts"() {
1825
2242
  "use strict";
1826
- init_parser();
1827
- init_symbols();
1828
2243
  }
1829
2244
  });
1830
2245
 
1831
2246
  // src/indexer/chunker.ts
1832
2247
  function chunkFile(filepath, content, options = {}) {
1833
2248
  const { chunkSize = 75, chunkOverlap = 10, useAST = true, astFallback = "line-based" } = options;
2249
+ if (filepath.endsWith(".liquid")) {
2250
+ return chunkLiquidFile(filepath, content, chunkSize, chunkOverlap);
2251
+ }
2252
+ if (filepath.endsWith(".json") && /(?:^|\/)templates\//.test(filepath)) {
2253
+ return chunkJSONTemplate(filepath, content);
2254
+ }
1834
2255
  if (useAST && shouldUseAST(filepath)) {
1835
2256
  try {
1836
2257
  return chunkByAST(filepath, content, {
@@ -1884,6 +2305,8 @@ var init_chunker2 = __esm({
1884
2305
  init_scanner();
1885
2306
  init_symbol_extractor();
1886
2307
  init_chunker();
2308
+ init_liquid_chunker();
2309
+ init_json_template_chunker();
1887
2310
  }
1888
2311
  });
1889
2312
 
@@ -1994,18 +2417,11 @@ var init_intent_classifier = __esm({
1994
2417
  }
1995
2418
  });
1996
2419
 
1997
- // src/vectordb/lancedb.ts
1998
- var lancedb_exports = {};
1999
- __export(lancedb_exports, {
2000
- VectorDB: () => VectorDB
2001
- });
2002
- import * as lancedb from "vectordb";
2003
- import path11 from "path";
2004
- import os2 from "os";
2005
- import crypto2 from "crypto";
2420
+ // src/vectordb/query.ts
2421
+ import path13 from "path";
2006
2422
  function isDocumentationFile(filepath) {
2007
2423
  const lower = filepath.toLowerCase();
2008
- const filename = path11.basename(filepath).toLowerCase();
2424
+ const filename = path13.basename(filepath).toLowerCase();
2009
2425
  if (filename.startsWith("readme")) return true;
2010
2426
  if (filename.startsWith("changelog")) return true;
2011
2427
  if (filename.endsWith(".md") || filename.endsWith(".mdx") || filename.endsWith(".markdown")) {
@@ -2052,7 +2468,7 @@ function boostPathRelevance(query, filepath, baseScore) {
2052
2468
  return baseScore * boostFactor;
2053
2469
  }
2054
2470
  function boostFilenameRelevance(query, filepath, baseScore) {
2055
- const filename = path11.basename(filepath, path11.extname(filepath)).toLowerCase();
2471
+ const filename = path13.basename(filepath, path13.extname(filepath)).toLowerCase();
2056
2472
  const queryTokens = query.toLowerCase().split(/\s+/);
2057
2473
  let boostFactor = 1;
2058
2474
  for (const token of queryTokens) {
@@ -2067,7 +2483,7 @@ function boostFilenameRelevance(query, filepath, baseScore) {
2067
2483
  }
2068
2484
  function boostForLocationIntent(query, filepath, baseScore) {
2069
2485
  let score = baseScore;
2070
- const filename = path11.basename(filepath, path11.extname(filepath)).toLowerCase();
2486
+ const filename = path13.basename(filepath, path13.extname(filepath)).toLowerCase();
2071
2487
  const queryTokens = query.toLowerCase().split(/\s+/);
2072
2488
  for (const token of queryTokens) {
2073
2489
  if (token.length <= 2) continue;
@@ -2095,51 +2511,408 @@ function boostForConceptualIntent(query, filepath, baseScore) {
2095
2511
  if (isUtilityFile(filepath)) {
2096
2512
  score *= 1.05;
2097
2513
  }
2098
- const filename = path11.basename(filepath, path11.extname(filepath)).toLowerCase();
2099
- const queryTokens = query.toLowerCase().split(/\s+/);
2100
- for (const token of queryTokens) {
2101
- if (token.length <= 2) continue;
2102
- if (filename.includes(token)) {
2103
- score *= 0.9;
2104
- }
2514
+ const filename = path13.basename(filepath, path13.extname(filepath)).toLowerCase();
2515
+ const queryTokens = query.toLowerCase().split(/\s+/);
2516
+ for (const token of queryTokens) {
2517
+ if (token.length <= 2) continue;
2518
+ if (filename.includes(token)) {
2519
+ score *= 0.9;
2520
+ }
2521
+ }
2522
+ const pathSegments = filepath.toLowerCase().split(path13.sep);
2523
+ for (const token of queryTokens) {
2524
+ if (token.length <= 2) continue;
2525
+ for (const segment of pathSegments) {
2526
+ if (segment.includes(token)) {
2527
+ score *= 0.95;
2528
+ break;
2529
+ }
2530
+ }
2531
+ }
2532
+ return score;
2533
+ }
2534
+ function boostForImplementationIntent(query, filepath, baseScore) {
2535
+ let score = baseScore;
2536
+ score = boostFilenameRelevance(query, filepath, score);
2537
+ score = boostPathRelevance(query, filepath, score);
2538
+ if (isTestFile(filepath)) {
2539
+ score *= 0.9;
2540
+ }
2541
+ return score;
2542
+ }
2543
+ function applyRelevanceBoosting(query, filepath, baseScore) {
2544
+ if (!query) {
2545
+ return baseScore;
2546
+ }
2547
+ const intent = classifyQueryIntent(query);
2548
+ switch (intent) {
2549
+ case "location" /* LOCATION */:
2550
+ return boostForLocationIntent(query, filepath, baseScore);
2551
+ case "conceptual" /* CONCEPTUAL */:
2552
+ return boostForConceptualIntent(query, filepath, baseScore);
2553
+ case "implementation" /* IMPLEMENTATION */:
2554
+ return boostForImplementationIntent(query, filepath, baseScore);
2555
+ default:
2556
+ return boostForImplementationIntent(query, filepath, baseScore);
2557
+ }
2558
+ }
2559
+ function dbRecordToSearchResult(r, query) {
2560
+ const baseScore = r._distance ?? 0;
2561
+ const boostedScore = applyRelevanceBoosting(query, r.file, baseScore);
2562
+ return {
2563
+ content: r.content,
2564
+ metadata: {
2565
+ file: r.file,
2566
+ startLine: r.startLine,
2567
+ endLine: r.endLine,
2568
+ type: r.type,
2569
+ language: r.language,
2570
+ // AST-derived metadata (v0.13.0)
2571
+ symbolName: r.symbolName || void 0,
2572
+ symbolType: r.symbolType,
2573
+ parentClass: r.parentClass || void 0,
2574
+ complexity: r.complexity || void 0,
2575
+ parameters: r.parameters && r.parameters.length > 0 && r.parameters[0] !== "" ? r.parameters : void 0,
2576
+ signature: r.signature || void 0,
2577
+ imports: r.imports && r.imports.length > 0 && r.imports[0] !== "" ? r.imports : void 0
2578
+ },
2579
+ score: boostedScore,
2580
+ relevance: calculateRelevance(boostedScore)
2581
+ };
2582
+ }
2583
+ async function search(table, queryVector, limit = 5, query) {
2584
+ if (!table) {
2585
+ throw new DatabaseError("Vector database not initialized");
2586
+ }
2587
+ try {
2588
+ const results = await table.search(Array.from(queryVector)).limit(limit + 20).execute();
2589
+ const filtered = results.filter(
2590
+ (r) => r.content && r.content.trim().length > 0 && r.file && r.file.length > 0
2591
+ ).map((r) => dbRecordToSearchResult(r, query)).sort((a, b) => a.score - b.score).slice(0, limit);
2592
+ return filtered;
2593
+ } catch (error) {
2594
+ const errorMsg = String(error);
2595
+ if (errorMsg.includes("Not found:") || errorMsg.includes(".lance")) {
2596
+ throw new DatabaseError(
2597
+ `Index appears corrupted or outdated. Please restart the MCP server or run 'lien reindex' in the project directory.`,
2598
+ { originalError: error }
2599
+ );
2600
+ }
2601
+ throw wrapError(error, "Failed to search vector database");
2602
+ }
2603
+ }
2604
+ async function scanWithFilter(table, options) {
2605
+ if (!table) {
2606
+ throw new DatabaseError("Vector database not initialized");
2607
+ }
2608
+ const { language, pattern, limit = 100 } = options;
2609
+ try {
2610
+ const zeroVector = Array(EMBEDDING_DIMENSION).fill(0);
2611
+ const query = table.search(zeroVector).where('file != ""').limit(Math.max(limit * 5, 200));
2612
+ const results = await query.execute();
2613
+ let filtered = results.filter(
2614
+ (r) => r.content && r.content.trim().length > 0 && r.file && r.file.length > 0
2615
+ );
2616
+ if (language) {
2617
+ filtered = filtered.filter(
2618
+ (r) => r.language && r.language.toLowerCase() === language.toLowerCase()
2619
+ );
2620
+ }
2621
+ if (pattern) {
2622
+ const regex = new RegExp(pattern, "i");
2623
+ filtered = filtered.filter(
2624
+ (r) => regex.test(r.content) || regex.test(r.file)
2625
+ );
2626
+ }
2627
+ return filtered.slice(0, limit).map((r) => {
2628
+ const score = 0;
2629
+ return {
2630
+ content: r.content,
2631
+ metadata: {
2632
+ file: r.file,
2633
+ startLine: r.startLine,
2634
+ endLine: r.endLine,
2635
+ type: r.type,
2636
+ language: r.language,
2637
+ // AST-derived metadata (v0.13.0)
2638
+ symbolName: r.symbolName || void 0,
2639
+ symbolType: r.symbolType,
2640
+ parentClass: r.parentClass || void 0,
2641
+ complexity: r.complexity || void 0,
2642
+ parameters: r.parameters && r.parameters.length > 0 && r.parameters[0] !== "" ? r.parameters : void 0,
2643
+ signature: r.signature || void 0,
2644
+ imports: r.imports && r.imports.length > 0 && r.imports[0] !== "" ? r.imports : void 0
2645
+ },
2646
+ score,
2647
+ relevance: calculateRelevance(score)
2648
+ };
2649
+ });
2650
+ } catch (error) {
2651
+ throw wrapError(error, "Failed to scan with filter");
2652
+ }
2653
+ }
2654
+ function matchesSymbolType(record, symbolType, symbols) {
2655
+ if (record.symbolType) {
2656
+ if (symbolType === "function") {
2657
+ return record.symbolType === "function" || record.symbolType === "method";
2658
+ } else if (symbolType === "class") {
2659
+ return record.symbolType === "class";
2660
+ } else if (symbolType === "interface") {
2661
+ return record.symbolType === "interface";
2662
+ }
2663
+ return false;
2664
+ }
2665
+ return symbols.length > 0 && symbols.some((s) => s.length > 0 && s !== "");
2666
+ }
2667
+ async function querySymbols(table, options) {
2668
+ if (!table) {
2669
+ throw new DatabaseError("Vector database not initialized");
2670
+ }
2671
+ const { language, pattern, symbolType, limit = 50 } = options;
2672
+ try {
2673
+ const zeroVector = Array(EMBEDDING_DIMENSION).fill(0);
2674
+ const query = table.search(zeroVector).where('file != ""').limit(Math.max(limit * 10, 500));
2675
+ const results = await query.execute();
2676
+ let filtered = results.filter((r) => {
2677
+ if (!r.content || r.content.trim().length === 0) {
2678
+ return false;
2679
+ }
2680
+ if (!r.file || r.file.length === 0) {
2681
+ return false;
2682
+ }
2683
+ if (language && (!r.language || r.language.toLowerCase() !== language.toLowerCase())) {
2684
+ return false;
2685
+ }
2686
+ const symbols = symbolType === "function" ? r.functionNames || [] : symbolType === "class" ? r.classNames || [] : symbolType === "interface" ? r.interfaceNames || [] : [...r.functionNames || [], ...r.classNames || [], ...r.interfaceNames || []];
2687
+ const astSymbolName = r.symbolName || "";
2688
+ if (symbols.length === 0 && !astSymbolName) {
2689
+ return false;
2690
+ }
2691
+ if (pattern) {
2692
+ const regex = new RegExp(pattern, "i");
2693
+ const matchesOldSymbols = symbols.some((s) => regex.test(s));
2694
+ const matchesASTSymbol = regex.test(astSymbolName);
2695
+ const nameMatches = matchesOldSymbols || matchesASTSymbol;
2696
+ if (!nameMatches) return false;
2697
+ if (symbolType) {
2698
+ return matchesSymbolType(r, symbolType, symbols);
2699
+ }
2700
+ return nameMatches;
2701
+ }
2702
+ if (symbolType) {
2703
+ return matchesSymbolType(r, symbolType, symbols);
2704
+ }
2705
+ return true;
2706
+ });
2707
+ return filtered.slice(0, limit).map((r) => {
2708
+ const score = 0;
2709
+ return {
2710
+ content: r.content,
2711
+ metadata: {
2712
+ file: r.file,
2713
+ startLine: r.startLine,
2714
+ endLine: r.endLine,
2715
+ type: r.type,
2716
+ language: r.language,
2717
+ symbols: {
2718
+ functions: r.functionNames && r.functionNames.length > 0 && r.functionNames[0] !== "" ? r.functionNames : [],
2719
+ classes: r.classNames && r.classNames.length > 0 && r.classNames[0] !== "" ? r.classNames : [],
2720
+ interfaces: r.interfaceNames && r.interfaceNames.length > 0 && r.interfaceNames[0] !== "" ? r.interfaceNames : []
2721
+ },
2722
+ // AST-derived metadata (v0.13.0)
2723
+ symbolName: r.symbolName || void 0,
2724
+ symbolType: r.symbolType,
2725
+ parentClass: r.parentClass || void 0,
2726
+ complexity: r.complexity || void 0,
2727
+ parameters: r.parameters && r.parameters.length > 0 && r.parameters[0] !== "" ? r.parameters : void 0,
2728
+ signature: r.signature || void 0,
2729
+ imports: r.imports && r.imports.length > 0 && r.imports[0] !== "" ? r.imports : void 0
2730
+ },
2731
+ score,
2732
+ relevance: calculateRelevance(score)
2733
+ };
2734
+ });
2735
+ } catch (error) {
2736
+ throw wrapError(error, "Failed to query symbols");
2737
+ }
2738
+ }
2739
+ var init_query = __esm({
2740
+ "src/vectordb/query.ts"() {
2741
+ "use strict";
2742
+ init_types();
2743
+ init_errors();
2744
+ init_relevance();
2745
+ init_intent_classifier();
2746
+ }
2747
+ });
2748
+
2749
+ // src/vectordb/batch-insert.ts
2750
+ async function insertBatch(db, table, tableName, vectors, metadatas, contents) {
2751
+ if (!db) {
2752
+ throw new DatabaseError("Vector database not initialized");
2753
+ }
2754
+ if (vectors.length !== metadatas.length || vectors.length !== contents.length) {
2755
+ throw new DatabaseError("Vectors, metadatas, and contents arrays must have the same length", {
2756
+ vectorsLength: vectors.length,
2757
+ metadatasLength: metadatas.length,
2758
+ contentsLength: contents.length
2759
+ });
2760
+ }
2761
+ if (vectors.length === 0) {
2762
+ return table;
2763
+ }
2764
+ if (vectors.length > VECTOR_DB_MAX_BATCH_SIZE) {
2765
+ let currentTable = table;
2766
+ for (let i = 0; i < vectors.length; i += VECTOR_DB_MAX_BATCH_SIZE) {
2767
+ const batchVectors = vectors.slice(i, Math.min(i + VECTOR_DB_MAX_BATCH_SIZE, vectors.length));
2768
+ const batchMetadata = metadatas.slice(i, Math.min(i + VECTOR_DB_MAX_BATCH_SIZE, vectors.length));
2769
+ const batchContents = contents.slice(i, Math.min(i + VECTOR_DB_MAX_BATCH_SIZE, vectors.length));
2770
+ currentTable = await insertBatchInternal(db, currentTable, tableName, batchVectors, batchMetadata, batchContents);
2771
+ }
2772
+ if (!currentTable) {
2773
+ throw new DatabaseError("Failed to create table during batch insert");
2774
+ }
2775
+ return currentTable;
2776
+ } else {
2777
+ return insertBatchInternal(db, table, tableName, vectors, metadatas, contents);
2778
+ }
2779
+ }
2780
+ async function insertBatchInternal(db, table, tableName, vectors, metadatas, contents) {
2781
+ const queue = [{ vectors, metadatas, contents }];
2782
+ const failedRecords = [];
2783
+ let currentTable = table;
2784
+ while (queue.length > 0) {
2785
+ const batch = queue.shift();
2786
+ if (!batch) break;
2787
+ try {
2788
+ const records = batch.vectors.map((vector, i) => ({
2789
+ vector: Array.from(vector),
2790
+ content: batch.contents[i],
2791
+ file: batch.metadatas[i].file,
2792
+ startLine: batch.metadatas[i].startLine,
2793
+ endLine: batch.metadatas[i].endLine,
2794
+ type: batch.metadatas[i].type,
2795
+ language: batch.metadatas[i].language,
2796
+ // Ensure arrays have at least empty string for Arrow type inference
2797
+ functionNames: batch.metadatas[i].symbols?.functions && batch.metadatas[i].symbols.functions.length > 0 ? batch.metadatas[i].symbols.functions : [""],
2798
+ classNames: batch.metadatas[i].symbols?.classes && batch.metadatas[i].symbols.classes.length > 0 ? batch.metadatas[i].symbols.classes : [""],
2799
+ interfaceNames: batch.metadatas[i].symbols?.interfaces && batch.metadatas[i].symbols.interfaces.length > 0 ? batch.metadatas[i].symbols.interfaces : [""],
2800
+ // AST-derived metadata (v0.13.0)
2801
+ symbolName: batch.metadatas[i].symbolName || "",
2802
+ symbolType: batch.metadatas[i].symbolType || "",
2803
+ parentClass: batch.metadatas[i].parentClass || "",
2804
+ complexity: batch.metadatas[i].complexity || 0,
2805
+ parameters: batch.metadatas[i].parameters && batch.metadatas[i].parameters.length > 0 ? batch.metadatas[i].parameters : [""],
2806
+ signature: batch.metadatas[i].signature || "",
2807
+ imports: batch.metadatas[i].imports && batch.metadatas[i].imports.length > 0 ? batch.metadatas[i].imports : [""]
2808
+ }));
2809
+ if (!currentTable) {
2810
+ currentTable = await db.createTable(tableName, records);
2811
+ } else {
2812
+ await currentTable.add(records);
2813
+ }
2814
+ } catch (error) {
2815
+ if (batch.vectors.length > VECTOR_DB_MIN_BATCH_SIZE) {
2816
+ const half = Math.floor(batch.vectors.length / 2);
2817
+ queue.push({
2818
+ vectors: batch.vectors.slice(0, half),
2819
+ metadatas: batch.metadatas.slice(0, half),
2820
+ contents: batch.contents.slice(0, half)
2821
+ });
2822
+ queue.push({
2823
+ vectors: batch.vectors.slice(half),
2824
+ metadatas: batch.metadatas.slice(half),
2825
+ contents: batch.contents.slice(half)
2826
+ });
2827
+ } else {
2828
+ failedRecords.push(batch);
2829
+ }
2830
+ }
2831
+ }
2832
+ if (failedRecords.length > 0) {
2833
+ const totalFailed = failedRecords.reduce((sum, batch) => sum + batch.vectors.length, 0);
2834
+ throw new DatabaseError(
2835
+ `Failed to insert ${totalFailed} record(s) after retry attempts`,
2836
+ {
2837
+ failedBatches: failedRecords.length,
2838
+ totalRecords: totalFailed,
2839
+ sampleFile: failedRecords[0].metadatas[0].file
2840
+ }
2841
+ );
2842
+ }
2843
+ if (!currentTable) {
2844
+ throw new DatabaseError("Failed to create table during batch insert");
2845
+ }
2846
+ return currentTable;
2847
+ }
2848
+ var init_batch_insert = __esm({
2849
+ "src/vectordb/batch-insert.ts"() {
2850
+ "use strict";
2851
+ init_errors();
2852
+ init_constants();
2853
+ }
2854
+ });
2855
+
2856
+ // src/vectordb/maintenance.ts
2857
+ async function clear(db, table, tableName) {
2858
+ if (!db) {
2859
+ throw new DatabaseError("Vector database not initialized");
2105
2860
  }
2106
- const pathSegments = filepath.toLowerCase().split(path11.sep);
2107
- for (const token of queryTokens) {
2108
- if (token.length <= 2) continue;
2109
- for (const segment of pathSegments) {
2110
- if (segment.includes(token)) {
2111
- score *= 0.95;
2112
- break;
2113
- }
2861
+ try {
2862
+ if (table) {
2863
+ await db.dropTable(tableName);
2114
2864
  }
2865
+ } catch (error) {
2866
+ throw wrapError(error, "Failed to clear vector database");
2115
2867
  }
2116
- return score;
2117
2868
  }
2118
- function boostForImplementationIntent(query, filepath, baseScore) {
2119
- let score = baseScore;
2120
- score = boostFilenameRelevance(query, filepath, score);
2121
- score = boostPathRelevance(query, filepath, score);
2122
- if (isTestFile(filepath)) {
2123
- score *= 0.9;
2869
+ async function deleteByFile(table, filepath) {
2870
+ if (!table) {
2871
+ throw new DatabaseError("Vector database not initialized");
2872
+ }
2873
+ try {
2874
+ await table.delete(`file = "${filepath}"`);
2875
+ } catch (error) {
2876
+ throw wrapError(error, "Failed to delete file from vector database");
2124
2877
  }
2125
- return score;
2126
2878
  }
2127
- function applyRelevanceBoosting(query, filepath, baseScore) {
2128
- if (!query) {
2129
- return baseScore;
2879
+ async function updateFile(db, table, tableName, dbPath, filepath, vectors, metadatas, contents) {
2880
+ if (!table) {
2881
+ throw new DatabaseError("Vector database not initialized");
2130
2882
  }
2131
- const intent = classifyQueryIntent(query);
2132
- switch (intent) {
2133
- case "location" /* LOCATION */:
2134
- return boostForLocationIntent(query, filepath, baseScore);
2135
- case "conceptual" /* CONCEPTUAL */:
2136
- return boostForConceptualIntent(query, filepath, baseScore);
2137
- case "implementation" /* IMPLEMENTATION */:
2138
- return boostForImplementationIntent(query, filepath, baseScore);
2139
- default:
2140
- return boostForImplementationIntent(query, filepath, baseScore);
2883
+ try {
2884
+ await deleteByFile(table, filepath);
2885
+ let updatedTable = table;
2886
+ if (vectors.length > 0) {
2887
+ updatedTable = await insertBatch(db, table, tableName, vectors, metadatas, contents);
2888
+ if (!updatedTable) {
2889
+ throw new DatabaseError("insertBatch unexpectedly returned null");
2890
+ }
2891
+ }
2892
+ await writeVersionFile(dbPath);
2893
+ return updatedTable;
2894
+ } catch (error) {
2895
+ throw wrapError(error, "Failed to update file in vector database");
2141
2896
  }
2142
2897
  }
2898
+ var init_maintenance = __esm({
2899
+ "src/vectordb/maintenance.ts"() {
2900
+ "use strict";
2901
+ init_errors();
2902
+ init_version2();
2903
+ init_batch_insert();
2904
+ }
2905
+ });
2906
+
2907
+ // src/vectordb/lancedb.ts
2908
+ var lancedb_exports = {};
2909
+ __export(lancedb_exports, {
2910
+ VectorDB: () => VectorDB
2911
+ });
2912
+ import * as lancedb from "vectordb";
2913
+ import path14 from "path";
2914
+ import os2 from "os";
2915
+ import crypto2 from "crypto";
2143
2916
  var VectorDB;
2144
2917
  var init_lancedb = __esm({
2145
2918
  "src/vectordb/lancedb.ts"() {
@@ -2147,9 +2920,9 @@ var init_lancedb = __esm({
2147
2920
  init_types();
2148
2921
  init_version2();
2149
2922
  init_errors();
2150
- init_relevance();
2151
- init_intent_classifier();
2152
- init_constants();
2923
+ init_query();
2924
+ init_batch_insert();
2925
+ init_maintenance();
2153
2926
  VectorDB = class _VectorDB {
2154
2927
  db = null;
2155
2928
  table = null;
@@ -2158,9 +2931,9 @@ var init_lancedb = __esm({
2158
2931
  lastVersionCheck = 0;
2159
2932
  currentVersion = 0;
2160
2933
  constructor(projectRoot) {
2161
- const projectName = path11.basename(projectRoot);
2934
+ const projectName = path14.basename(projectRoot);
2162
2935
  const pathHash = crypto2.createHash("md5").update(projectRoot).digest("hex").substring(0, 8);
2163
- this.dbPath = path11.join(
2936
+ this.dbPath = path14.join(
2164
2937
  os2.homedir(),
2165
2938
  ".lien",
2166
2939
  "indices",
@@ -2188,150 +2961,30 @@ var init_lancedb = __esm({
2188
2961
  if (!this.db) {
2189
2962
  throw new DatabaseError("Vector database not initialized");
2190
2963
  }
2191
- if (vectors.length !== metadatas.length || vectors.length !== contents.length) {
2192
- throw new DatabaseError("Vectors, metadatas, and contents arrays must have the same length", {
2193
- vectorsLength: vectors.length,
2194
- metadatasLength: metadatas.length,
2195
- contentsLength: contents.length
2196
- });
2197
- }
2198
- if (vectors.length === 0) {
2199
- return;
2200
- }
2201
- if (vectors.length > VECTOR_DB_MAX_BATCH_SIZE) {
2202
- for (let i = 0; i < vectors.length; i += VECTOR_DB_MAX_BATCH_SIZE) {
2203
- const batchVectors = vectors.slice(i, Math.min(i + VECTOR_DB_MAX_BATCH_SIZE, vectors.length));
2204
- const batchMetadata = metadatas.slice(i, Math.min(i + VECTOR_DB_MAX_BATCH_SIZE, vectors.length));
2205
- const batchContents = contents.slice(i, Math.min(i + VECTOR_DB_MAX_BATCH_SIZE, vectors.length));
2206
- await this._insertBatchInternal(batchVectors, batchMetadata, batchContents);
2207
- }
2208
- } else {
2209
- await this._insertBatchInternal(vectors, metadatas, contents);
2210
- }
2211
- }
2212
- /**
2213
- * Internal method to insert a single batch with iterative retry logic.
2214
- * Uses a queue-based approach to avoid deep recursion on large batch failures.
2215
- */
2216
- async _insertBatchInternal(vectors, metadatas, contents) {
2217
- const queue = [{ vectors, metadatas, contents }];
2218
- const failedRecords = [];
2219
- while (queue.length > 0) {
2220
- const batch = queue.shift();
2221
- try {
2222
- const records = batch.vectors.map((vector, i) => ({
2223
- vector: Array.from(vector),
2224
- content: batch.contents[i],
2225
- file: batch.metadatas[i].file,
2226
- startLine: batch.metadatas[i].startLine,
2227
- endLine: batch.metadatas[i].endLine,
2228
- type: batch.metadatas[i].type,
2229
- language: batch.metadatas[i].language,
2230
- // Ensure arrays have at least empty string for Arrow type inference
2231
- functionNames: batch.metadatas[i].symbols?.functions && batch.metadatas[i].symbols.functions.length > 0 ? batch.metadatas[i].symbols.functions : [""],
2232
- classNames: batch.metadatas[i].symbols?.classes && batch.metadatas[i].symbols.classes.length > 0 ? batch.metadatas[i].symbols.classes : [""],
2233
- interfaceNames: batch.metadatas[i].symbols?.interfaces && batch.metadatas[i].symbols.interfaces.length > 0 ? batch.metadatas[i].symbols.interfaces : [""],
2234
- // AST-derived metadata (v0.13.0)
2235
- symbolName: batch.metadatas[i].symbolName || "",
2236
- symbolType: batch.metadatas[i].symbolType || "",
2237
- parentClass: batch.metadatas[i].parentClass || "",
2238
- complexity: batch.metadatas[i].complexity || 0,
2239
- parameters: batch.metadatas[i].parameters && batch.metadatas[i].parameters.length > 0 ? batch.metadatas[i].parameters : [""],
2240
- signature: batch.metadatas[i].signature || "",
2241
- imports: batch.metadatas[i].imports && batch.metadatas[i].imports.length > 0 ? batch.metadatas[i].imports : [""]
2242
- }));
2243
- if (!this.table) {
2244
- this.table = await this.db.createTable(this.tableName, records);
2245
- } else {
2246
- await this.table.add(records);
2247
- }
2248
- } catch (error) {
2249
- if (batch.vectors.length > VECTOR_DB_MIN_BATCH_SIZE) {
2250
- const half = Math.floor(batch.vectors.length / 2);
2251
- queue.push({
2252
- vectors: batch.vectors.slice(0, half),
2253
- metadatas: batch.metadatas.slice(0, half),
2254
- contents: batch.contents.slice(0, half)
2255
- });
2256
- queue.push({
2257
- vectors: batch.vectors.slice(half),
2258
- metadatas: batch.metadatas.slice(half),
2259
- contents: batch.contents.slice(half)
2260
- });
2261
- } else {
2262
- failedRecords.push(batch);
2263
- }
2264
- }
2265
- }
2266
- if (failedRecords.length > 0) {
2267
- const totalFailed = failedRecords.reduce((sum, batch) => sum + batch.vectors.length, 0);
2268
- throw new DatabaseError(
2269
- `Failed to insert ${totalFailed} record(s) after retry attempts`,
2270
- {
2271
- failedBatches: failedRecords.length,
2272
- totalRecords: totalFailed,
2273
- sampleFile: failedRecords[0].metadatas[0].file
2274
- }
2275
- );
2276
- }
2964
+ this.table = await insertBatch(
2965
+ this.db,
2966
+ this.table,
2967
+ this.tableName,
2968
+ vectors,
2969
+ metadatas,
2970
+ contents
2971
+ );
2277
2972
  }
2278
2973
  async search(queryVector, limit = 5, query) {
2279
2974
  if (!this.table) {
2280
2975
  throw new DatabaseError("Vector database not initialized");
2281
2976
  }
2282
2977
  try {
2283
- const results = await this.table.search(Array.from(queryVector)).limit(limit + 20).execute();
2284
- const filtered = results.filter(
2285
- (r) => r.content && r.content.trim().length > 0 && r.file && r.file.length > 0
2286
- ).map((r) => {
2287
- const baseScore = r._distance ?? 0;
2288
- const boostedScore = applyRelevanceBoosting(query, r.file, baseScore);
2289
- return {
2290
- content: r.content,
2291
- metadata: {
2292
- file: r.file,
2293
- startLine: r.startLine,
2294
- endLine: r.endLine,
2295
- type: r.type,
2296
- language: r.language,
2297
- // AST-derived metadata (v0.13.0)
2298
- symbolName: r.symbolName || void 0,
2299
- symbolType: r.symbolType,
2300
- parentClass: r.parentClass || void 0,
2301
- complexity: r.complexity || void 0,
2302
- parameters: r.parameters && r.parameters.length > 0 && r.parameters[0] !== "" ? r.parameters : void 0,
2303
- signature: r.signature || void 0,
2304
- imports: r.imports && r.imports.length > 0 && r.imports[0] !== "" ? r.imports : void 0
2305
- },
2306
- score: boostedScore,
2307
- relevance: calculateRelevance(boostedScore)
2308
- };
2309
- }).sort((a, b) => a.score - b.score).slice(0, limit);
2310
- return filtered;
2978
+ return await search(this.table, queryVector, limit, query);
2311
2979
  } catch (error) {
2312
2980
  const errorMsg = String(error);
2313
2981
  if (errorMsg.includes("Not found:") || errorMsg.includes(".lance")) {
2314
2982
  try {
2315
2983
  await this.initialize();
2316
- const results = await this.table.search(Array.from(queryVector)).limit(limit + 20).execute();
2317
- return results.filter(
2318
- (r) => r.content && r.content.trim().length > 0 && r.file && r.file.length > 0
2319
- ).map((r) => {
2320
- const baseScore = r._distance ?? 0;
2321
- const boostedScore = applyRelevanceBoosting(query, r.file, baseScore);
2322
- return {
2323
- content: r.content,
2324
- metadata: {
2325
- file: r.file,
2326
- startLine: r.startLine,
2327
- endLine: r.endLine,
2328
- type: r.type,
2329
- language: r.language
2330
- },
2331
- score: boostedScore,
2332
- relevance: calculateRelevance(boostedScore)
2333
- };
2334
- }).sort((a, b) => a.score - b.score).slice(0, limit);
2984
+ if (!this.table) {
2985
+ throw new DatabaseError("Vector database not initialized after reconnection");
2986
+ }
2987
+ return await search(this.table, queryVector, limit, query);
2335
2988
  } catch (retryError) {
2336
2989
  throw new DatabaseError(
2337
2990
  `Index appears corrupted or outdated. Please restart the MCP server or run 'lien reindex' in the project directory.`,
@@ -2339,209 +2992,52 @@ var init_lancedb = __esm({
2339
2992
  );
2340
2993
  }
2341
2994
  }
2342
- throw wrapError(error, "Failed to search vector database");
2995
+ throw error;
2343
2996
  }
2344
2997
  }
2345
2998
  async scanWithFilter(options) {
2346
2999
  if (!this.table) {
2347
3000
  throw new DatabaseError("Vector database not initialized");
2348
3001
  }
2349
- const { language, pattern, limit = 100 } = options;
2350
- try {
2351
- const zeroVector = Array(EMBEDDING_DIMENSION).fill(0);
2352
- const query = this.table.search(zeroVector).where('file != ""').limit(Math.max(limit * 5, 200));
2353
- const results = await query.execute();
2354
- let filtered = results.filter(
2355
- (r) => r.content && r.content.trim().length > 0 && r.file && r.file.length > 0
2356
- );
2357
- if (language) {
2358
- filtered = filtered.filter(
2359
- (r) => r.language && r.language.toLowerCase() === language.toLowerCase()
2360
- );
2361
- }
2362
- if (pattern) {
2363
- const regex = new RegExp(pattern, "i");
2364
- filtered = filtered.filter(
2365
- (r) => regex.test(r.content) || regex.test(r.file)
2366
- );
2367
- }
2368
- return filtered.slice(0, limit).map((r) => {
2369
- const score = 0;
2370
- return {
2371
- content: r.content,
2372
- metadata: {
2373
- file: r.file,
2374
- startLine: r.startLine,
2375
- endLine: r.endLine,
2376
- type: r.type,
2377
- language: r.language,
2378
- // AST-derived metadata (v0.13.0)
2379
- symbolName: r.symbolName || void 0,
2380
- symbolType: r.symbolType,
2381
- parentClass: r.parentClass || void 0,
2382
- complexity: r.complexity || void 0,
2383
- parameters: r.parameters && r.parameters.length > 0 && r.parameters[0] !== "" ? r.parameters : void 0,
2384
- signature: r.signature || void 0,
2385
- imports: r.imports && r.imports.length > 0 && r.imports[0] !== "" ? r.imports : void 0
2386
- },
2387
- score,
2388
- relevance: calculateRelevance(score)
2389
- };
2390
- });
2391
- } catch (error) {
2392
- throw wrapError(error, "Failed to scan with filter");
2393
- }
3002
+ return scanWithFilter(this.table, options);
2394
3003
  }
2395
3004
  async querySymbols(options) {
2396
3005
  if (!this.table) {
2397
3006
  throw new DatabaseError("Vector database not initialized");
2398
3007
  }
2399
- const { language, pattern, symbolType, limit = 50 } = options;
2400
- try {
2401
- const zeroVector = Array(EMBEDDING_DIMENSION).fill(0);
2402
- const query = this.table.search(zeroVector).where('file != ""').limit(Math.max(limit * 10, 500));
2403
- const results = await query.execute();
2404
- let filtered = results.filter((r) => {
2405
- if (!r.content || r.content.trim().length === 0) {
2406
- return false;
2407
- }
2408
- if (!r.file || r.file.length === 0) {
2409
- return false;
2410
- }
2411
- if (language && (!r.language || r.language.toLowerCase() !== language.toLowerCase())) {
2412
- return false;
2413
- }
2414
- const symbols = symbolType === "function" ? r.functionNames || [] : symbolType === "class" ? r.classNames || [] : symbolType === "interface" ? r.interfaceNames || [] : [...r.functionNames || [], ...r.classNames || [], ...r.interfaceNames || []];
2415
- const astSymbolName = r.symbolName || "";
2416
- if (symbols.length === 0 && !astSymbolName) {
2417
- return false;
2418
- }
2419
- if (pattern) {
2420
- const regex = new RegExp(pattern, "i");
2421
- const matchesOldSymbols = symbols.some((s) => regex.test(s));
2422
- const matchesASTSymbol = regex.test(astSymbolName);
2423
- const nameMatches = matchesOldSymbols || matchesASTSymbol;
2424
- if (!nameMatches) return false;
2425
- if (symbolType) {
2426
- if (r.symbolType) {
2427
- if (symbolType === "function") {
2428
- return r.symbolType === "function" || r.symbolType === "method";
2429
- } else if (symbolType === "class") {
2430
- return r.symbolType === "class";
2431
- } else if (symbolType === "interface") {
2432
- return r.symbolType === "interface";
2433
- }
2434
- return false;
2435
- }
2436
- return nameMatches;
2437
- }
2438
- return nameMatches;
2439
- }
2440
- if (symbolType) {
2441
- if (r.symbolType) {
2442
- if (symbolType === "function") {
2443
- return r.symbolType === "function" || r.symbolType === "method";
2444
- } else if (symbolType === "class") {
2445
- return r.symbolType === "class";
2446
- } else if (symbolType === "interface") {
2447
- return r.symbolType === "interface";
2448
- }
2449
- return false;
2450
- }
2451
- return symbols.length > 0 && symbols.some((s) => s.length > 0 && s !== "");
2452
- }
2453
- return true;
2454
- });
2455
- return filtered.slice(0, limit).map((r) => {
2456
- const score = 0;
2457
- return {
2458
- content: r.content,
2459
- metadata: {
2460
- file: r.file,
2461
- startLine: r.startLine,
2462
- endLine: r.endLine,
2463
- type: r.type,
2464
- language: r.language,
2465
- symbols: {
2466
- functions: r.functionNames || [],
2467
- classes: r.classNames || [],
2468
- interfaces: r.interfaceNames || []
2469
- },
2470
- // AST-derived metadata (v0.13.0)
2471
- symbolName: r.symbolName || void 0,
2472
- symbolType: r.symbolType,
2473
- parentClass: r.parentClass || void 0,
2474
- complexity: r.complexity || void 0,
2475
- parameters: r.parameters && r.parameters.length > 0 && r.parameters[0] !== "" ? r.parameters : void 0,
2476
- signature: r.signature || void 0,
2477
- imports: r.imports && r.imports.length > 0 && r.imports[0] !== "" ? r.imports : void 0
2478
- },
2479
- score,
2480
- relevance: calculateRelevance(score)
2481
- };
2482
- });
2483
- } catch (error) {
2484
- throw wrapError(error, "Failed to query symbols");
2485
- }
3008
+ return querySymbols(this.table, options);
2486
3009
  }
2487
3010
  async clear() {
2488
3011
  if (!this.db) {
2489
3012
  throw new DatabaseError("Vector database not initialized");
2490
3013
  }
2491
- try {
2492
- if (this.table) {
2493
- await this.db.dropTable(this.tableName);
2494
- }
2495
- this.table = null;
2496
- } catch (error) {
2497
- throw wrapError(error, "Failed to clear vector database");
2498
- }
3014
+ await clear(this.db, this.table, this.tableName);
3015
+ this.table = null;
2499
3016
  }
2500
- /**
2501
- * Deletes all chunks from a specific file.
2502
- * Used for incremental reindexing when a file is deleted or needs to be re-indexed.
2503
- *
2504
- * @param filepath - Path to the file whose chunks should be deleted
2505
- */
2506
3017
  async deleteByFile(filepath) {
2507
3018
  if (!this.table) {
2508
3019
  throw new DatabaseError("Vector database not initialized");
2509
3020
  }
2510
- try {
2511
- await this.table.delete(`file = "${filepath}"`);
2512
- } catch (error) {
2513
- throw wrapError(error, "Failed to delete file from vector database");
2514
- }
3021
+ await deleteByFile(this.table, filepath);
2515
3022
  }
2516
- /**
2517
- * Updates a file in the index by atomically deleting old chunks and inserting new ones.
2518
- * This is the primary method for incremental reindexing.
2519
- *
2520
- * @param filepath - Path to the file being updated
2521
- * @param vectors - New embedding vectors
2522
- * @param metadatas - New chunk metadata
2523
- * @param contents - New chunk contents
2524
- */
2525
3023
  async updateFile(filepath, vectors, metadatas, contents) {
2526
- if (!this.table) {
2527
- throw new DatabaseError("Vector database not initialized");
3024
+ if (!this.db) {
3025
+ throw new DatabaseError("Vector database connection not initialized");
2528
3026
  }
2529
- try {
2530
- await this.deleteByFile(filepath);
2531
- if (vectors.length > 0) {
2532
- await this.insertBatch(vectors, metadatas, contents);
2533
- }
2534
- await writeVersionFile(this.dbPath);
2535
- } catch (error) {
2536
- throw wrapError(error, "Failed to update file in vector database");
3027
+ if (!this.table) {
3028
+ throw new DatabaseError("Vector database table not initialized");
2537
3029
  }
3030
+ this.table = await updateFile(
3031
+ this.db,
3032
+ this.table,
3033
+ this.tableName,
3034
+ this.dbPath,
3035
+ filepath,
3036
+ vectors,
3037
+ metadatas,
3038
+ contents
3039
+ );
2538
3040
  }
2539
- /**
2540
- * Checks if the index version has changed since last check.
2541
- * Uses caching to minimize I/O overhead (checks at most once per second).
2542
- *
2543
- * @returns true if version has changed, false otherwise
2544
- */
2545
3041
  async checkVersion() {
2546
3042
  const now = Date.now();
2547
3043
  if (now - this.lastVersionCheck < 1e3) {
@@ -2559,11 +3055,6 @@ var init_lancedb = __esm({
2559
3055
  return false;
2560
3056
  }
2561
3057
  }
2562
- /**
2563
- * Reconnects to the database by reinitializing the connection.
2564
- * Used when the index has been rebuilt/reindexed.
2565
- * Forces a complete reload from disk by closing existing connections first.
2566
- */
2567
3058
  async reconnect() {
2568
3059
  try {
2569
3060
  this.table = null;
@@ -2573,31 +3064,15 @@ var init_lancedb = __esm({
2573
3064
  throw wrapError(error, "Failed to reconnect to vector database");
2574
3065
  }
2575
3066
  }
2576
- /**
2577
- * Gets the current index version (timestamp of last reindex).
2578
- *
2579
- * @returns Version timestamp, or 0 if unknown
2580
- */
2581
3067
  getCurrentVersion() {
2582
3068
  return this.currentVersion;
2583
3069
  }
2584
- /**
2585
- * Gets the current index version as a human-readable date string.
2586
- *
2587
- * @returns Formatted date string, or 'Unknown' if no version
2588
- */
2589
3070
  getVersionDate() {
2590
3071
  if (this.currentVersion === 0) {
2591
3072
  return "Unknown";
2592
3073
  }
2593
3074
  return new Date(this.currentVersion).toLocaleString();
2594
3075
  }
2595
- /**
2596
- * Checks if the database contains real indexed data.
2597
- * Used to detect first run and trigger auto-indexing.
2598
- *
2599
- * @returns true if database has real code chunks, false if empty or only schema rows
2600
- */
2601
3076
  async hasData() {
2602
3077
  if (!this.table) {
2603
3078
  return false;
@@ -2630,8 +3105,8 @@ var manifest_exports = {};
2630
3105
  __export(manifest_exports, {
2631
3106
  ManifestManager: () => ManifestManager
2632
3107
  });
2633
- import fs11 from "fs/promises";
2634
- import path12 from "path";
3108
+ import fs13 from "fs/promises";
3109
+ import path15 from "path";
2635
3110
  var MANIFEST_FILE, ManifestManager;
2636
3111
  var init_manifest = __esm({
2637
3112
  "src/indexer/manifest.ts"() {
@@ -2653,7 +3128,7 @@ var init_manifest = __esm({
2653
3128
  */
2654
3129
  constructor(indexPath) {
2655
3130
  this.indexPath = indexPath;
2656
- this.manifestPath = path12.join(indexPath, MANIFEST_FILE);
3131
+ this.manifestPath = path15.join(indexPath, MANIFEST_FILE);
2657
3132
  }
2658
3133
  /**
2659
3134
  * Loads the manifest from disk.
@@ -2666,7 +3141,7 @@ var init_manifest = __esm({
2666
3141
  */
2667
3142
  async load() {
2668
3143
  try {
2669
- const content = await fs11.readFile(this.manifestPath, "utf-8");
3144
+ const content = await fs13.readFile(this.manifestPath, "utf-8");
2670
3145
  const manifest = JSON.parse(content);
2671
3146
  if (manifest.formatVersion !== INDEX_FORMAT_VERSION) {
2672
3147
  console.error(
@@ -2693,7 +3168,7 @@ var init_manifest = __esm({
2693
3168
  */
2694
3169
  async save(manifest) {
2695
3170
  try {
2696
- await fs11.mkdir(this.indexPath, { recursive: true });
3171
+ await fs13.mkdir(this.indexPath, { recursive: true });
2697
3172
  const manifestToSave = {
2698
3173
  ...manifest,
2699
3174
  formatVersion: INDEX_FORMAT_VERSION,
@@ -2701,7 +3176,7 @@ var init_manifest = __esm({
2701
3176
  lastIndexed: Date.now()
2702
3177
  };
2703
3178
  const content = JSON.stringify(manifestToSave, null, 2);
2704
- await fs11.writeFile(this.manifestPath, content, "utf-8");
3179
+ await fs13.writeFile(this.manifestPath, content, "utf-8");
2705
3180
  } catch (error) {
2706
3181
  console.error(`[Lien] Warning: Failed to save manifest: ${error}`);
2707
3182
  }
@@ -2838,7 +3313,7 @@ var init_manifest = __esm({
2838
3313
  */
2839
3314
  async clear() {
2840
3315
  try {
2841
- await fs11.unlink(this.manifestPath);
3316
+ await fs13.unlink(this.manifestPath);
2842
3317
  } catch (error) {
2843
3318
  if (error.code !== "ENOENT") {
2844
3319
  console.error(`[Lien] Warning: Failed to clear manifest: ${error}`);
@@ -2867,8 +3342,8 @@ var tracker_exports = {};
2867
3342
  __export(tracker_exports, {
2868
3343
  GitStateTracker: () => GitStateTracker
2869
3344
  });
2870
- import fs12 from "fs/promises";
2871
- import path13 from "path";
3345
+ import fs14 from "fs/promises";
3346
+ import path16 from "path";
2872
3347
  var GitStateTracker;
2873
3348
  var init_tracker = __esm({
2874
3349
  "src/git/tracker.ts"() {
@@ -2880,7 +3355,7 @@ var init_tracker = __esm({
2880
3355
  currentState = null;
2881
3356
  constructor(rootDir, indexPath) {
2882
3357
  this.rootDir = rootDir;
2883
- this.stateFile = path13.join(indexPath, ".git-state.json");
3358
+ this.stateFile = path16.join(indexPath, ".git-state.json");
2884
3359
  }
2885
3360
  /**
2886
3361
  * Loads the last known git state from disk.
@@ -2888,7 +3363,7 @@ var init_tracker = __esm({
2888
3363
  */
2889
3364
  async loadState() {
2890
3365
  try {
2891
- const content = await fs12.readFile(this.stateFile, "utf-8");
3366
+ const content = await fs14.readFile(this.stateFile, "utf-8");
2892
3367
  return JSON.parse(content);
2893
3368
  } catch {
2894
3369
  return null;
@@ -2900,7 +3375,7 @@ var init_tracker = __esm({
2900
3375
  async saveState(state) {
2901
3376
  try {
2902
3377
  const content = JSON.stringify(state, null, 2);
2903
- await fs12.writeFile(this.stateFile, content, "utf-8");
3378
+ await fs14.writeFile(this.stateFile, content, "utf-8");
2904
3379
  } catch (error) {
2905
3380
  console.error(`[Lien] Warning: Failed to save git state: ${error}`);
2906
3381
  }
@@ -3051,7 +3526,7 @@ var init_tracker = __esm({
3051
3526
  });
3052
3527
 
3053
3528
  // src/indexer/change-detector.ts
3054
- import fs13 from "fs/promises";
3529
+ import fs15 from "fs/promises";
3055
3530
  async function detectChanges(rootDir, vectorDB, config) {
3056
3531
  const manifest = new ManifestManager(vectorDB.dbPath);
3057
3532
  const savedManifest = await manifest.load();
@@ -3155,7 +3630,7 @@ async function mtimeBasedDetection(rootDir, savedManifest, config) {
3155
3630
  const fileStats = /* @__PURE__ */ new Map();
3156
3631
  for (const filepath of currentFiles) {
3157
3632
  try {
3158
- const stats = await fs13.stat(filepath);
3633
+ const stats = await fs15.stat(filepath);
3159
3634
  fileStats.set(filepath, stats.mtimeMs);
3160
3635
  } catch {
3161
3636
  continue;
@@ -3193,7 +3668,7 @@ var init_change_detector = __esm({
3193
3668
  });
3194
3669
 
3195
3670
  // src/indexer/incremental.ts
3196
- import fs14 from "fs/promises";
3671
+ import fs16 from "fs/promises";
3197
3672
  async function processFileContent(filepath, content, embeddings, config, verbose) {
3198
3673
  const chunkSize = isModernConfig(config) ? config.core.chunkSize : isLegacyConfig(config) ? config.indexing.chunkSize : 75;
3199
3674
  const chunkOverlap = isModernConfig(config) ? config.core.chunkOverlap : isLegacyConfig(config) ? config.indexing.chunkOverlap : 10;
@@ -3232,7 +3707,7 @@ async function indexSingleFile(filepath, vectorDB, embeddings, config, options =
3232
3707
  const { verbose } = options;
3233
3708
  try {
3234
3709
  try {
3235
- await fs14.access(filepath);
3710
+ await fs16.access(filepath);
3236
3711
  } catch {
3237
3712
  if (verbose) {
3238
3713
  console.error(`[Lien] File deleted: ${filepath}`);
@@ -3242,9 +3717,9 @@ async function indexSingleFile(filepath, vectorDB, embeddings, config, options =
3242
3717
  await manifest2.removeFile(filepath);
3243
3718
  return;
3244
3719
  }
3245
- const content = await fs14.readFile(filepath, "utf-8");
3720
+ const content = await fs16.readFile(filepath, "utf-8");
3246
3721
  const result = await processFileContent(filepath, content, embeddings, config, verbose || false);
3247
- const stats = await fs14.stat(filepath);
3722
+ const stats = await fs16.stat(filepath);
3248
3723
  const manifest = new ManifestManager(vectorDB.dbPath);
3249
3724
  if (result === null) {
3250
3725
  await vectorDB.deleteByFile(filepath);
@@ -3281,9 +3756,9 @@ async function indexMultipleFiles(filepaths, vectorDB, embeddings, config, optio
3281
3756
  let content;
3282
3757
  let fileMtime;
3283
3758
  try {
3284
- const stats = await fs14.stat(filepath);
3759
+ const stats = await fs16.stat(filepath);
3285
3760
  fileMtime = stats.mtimeMs;
3286
- content = await fs14.readFile(filepath, "utf-8");
3761
+ content = await fs16.readFile(filepath, "utf-8");
3287
3762
  } catch (error) {
3288
3763
  if (verbose) {
3289
3764
  console.error(`[Lien] File not readable: ${filepath}`);
@@ -3448,9 +3923,9 @@ var indexer_exports = {};
3448
3923
  __export(indexer_exports, {
3449
3924
  indexCodebase: () => indexCodebase
3450
3925
  });
3451
- import fs15 from "fs/promises";
3926
+ import fs17 from "fs/promises";
3452
3927
  import ora from "ora";
3453
- import chalk4 from "chalk";
3928
+ import chalk5 from "chalk";
3454
3929
  import pLimit from "p-limit";
3455
3930
  async function indexCodebase(options = {}) {
3456
3931
  const rootDir = options.rootDir ?? process.cwd();
@@ -3523,7 +3998,7 @@ async function indexCodebase(options = {}) {
3523
3998
  await manifest2.updateGitState(gitState);
3524
3999
  }
3525
4000
  }
3526
- console.log(chalk4.dim("\nNext step: Run"), chalk4.bold("lien serve"), chalk4.dim("to start the MCP server"));
4001
+ console.log(chalk5.dim("\nNext step: Run"), chalk5.bold("lien serve"), chalk5.dim("to start the MCP server"));
3527
4002
  return;
3528
4003
  }
3529
4004
  spinner.text = "Full reindex required...";
@@ -3612,8 +4087,8 @@ async function indexCodebase(options = {}) {
3612
4087
  const filePromises = files.map(
3613
4088
  (file) => limit(async () => {
3614
4089
  try {
3615
- const stats = await fs15.stat(file);
3616
- const content = await fs15.readFile(file, "utf-8");
4090
+ const stats = await fs17.stat(file);
4091
+ const content = await fs17.readFile(file, "utf-8");
3617
4092
  const chunkSize = isModernConfig(config) ? config.core.chunkSize : 75;
3618
4093
  const chunkOverlap = isModernConfig(config) ? config.core.chunkOverlap : 10;
3619
4094
  const useAST = isModernConfig(config) ? config.chunking.useAST : true;
@@ -3647,7 +4122,7 @@ async function indexCodebase(options = {}) {
3647
4122
  }
3648
4123
  } catch (error) {
3649
4124
  if (options.verbose) {
3650
- console.error(chalk4.yellow(`
4125
+ console.error(chalk5.yellow(`
3651
4126
  \u26A0\uFE0F Skipping ${file}: ${error}`));
3652
4127
  }
3653
4128
  processedFiles++;
@@ -3687,7 +4162,7 @@ async function indexCodebase(options = {}) {
3687
4162
  spinner.succeed(
3688
4163
  `Indexed ${processedFiles} files (${processedChunks} chunks) in ${totalTime}s using ${concurrency}x concurrency`
3689
4164
  );
3690
- console.log(chalk4.dim("\nNext step: Run"), chalk4.bold("lien serve"), chalk4.dim("to start the MCP server"));
4165
+ console.log(chalk5.dim("\nNext step: Run"), chalk5.bold("lien serve"), chalk5.dim("to start the MCP server"));
3691
4166
  } catch (error) {
3692
4167
  if (updateInterval) {
3693
4168
  clearInterval(updateInterval);
@@ -3722,11 +4197,10 @@ import { dirname as dirname4, join as join4 } from "path";
3722
4197
 
3723
4198
  // src/cli/init.ts
3724
4199
  init_schema();
3725
- init_merge();
3726
- import fs5 from "fs/promises";
3727
- import path5 from "path";
4200
+ import fs7 from "fs/promises";
4201
+ import path7 from "path";
3728
4202
  import { fileURLToPath as fileURLToPath3 } from "url";
3729
- import chalk2 from "chalk";
4203
+ import chalk3 from "chalk";
3730
4204
  import inquirer from "inquirer";
3731
4205
 
3732
4206
  // src/utils/banner.ts
@@ -3788,12 +4262,111 @@ function showCompactBanner() {
3788
4262
  console.log();
3789
4263
  }
3790
4264
 
3791
- // src/cli/init.ts
4265
+ // src/config/migration-manager.ts
4266
+ init_schema();
3792
4267
  init_migration();
4268
+ init_merge();
4269
+ init_constants();
4270
+ import fs2 from "fs/promises";
4271
+ import path2 from "path";
4272
+ import chalk2 from "chalk";
4273
+ var MigrationManager = class {
4274
+ constructor(rootDir = process.cwd()) {
4275
+ this.rootDir = rootDir;
4276
+ }
4277
+ /**
4278
+ * Get the config file path
4279
+ */
4280
+ getConfigPath() {
4281
+ return path2.join(this.rootDir, ".lien.config.json");
4282
+ }
4283
+ /**
4284
+ * Check if the current config needs migration
4285
+ */
4286
+ async needsMigration() {
4287
+ try {
4288
+ const configPath = this.getConfigPath();
4289
+ const content = await fs2.readFile(configPath, "utf-8");
4290
+ const config = JSON.parse(content);
4291
+ return needsMigration(config);
4292
+ } catch (error) {
4293
+ return false;
4294
+ }
4295
+ }
4296
+ /**
4297
+ * Perform silent migration (for auto-migration during load)
4298
+ * Returns the migrated config without user interaction
4299
+ */
4300
+ async autoMigrate() {
4301
+ const result = await migrateConfigFile(this.rootDir);
4302
+ if (result.migrated && result.backupPath) {
4303
+ const backupFilename = path2.basename(result.backupPath);
4304
+ console.log(`\u2705 Migration complete! Backup saved as ${backupFilename}`);
4305
+ console.log("\u{1F4DD} Your config now uses the framework-based structure.");
4306
+ }
4307
+ return result.config;
4308
+ }
4309
+ /**
4310
+ * Perform interactive upgrade (for CLI upgrade command)
4311
+ * Provides detailed feedback and handles edge cases
4312
+ */
4313
+ async upgradeInteractive() {
4314
+ const configPath = this.getConfigPath();
4315
+ try {
4316
+ const existingContent = await fs2.readFile(configPath, "utf-8");
4317
+ const existingConfig = JSON.parse(existingContent);
4318
+ const migrationNeeded = needsMigration(existingConfig);
4319
+ const newFields = migrationNeeded ? [] : detectNewFields(existingConfig, defaultConfig);
4320
+ const hasChanges = migrationNeeded || newFields.length > 0;
4321
+ if (!hasChanges) {
4322
+ console.log(chalk2.green("\u2713 Config is already up to date"));
4323
+ console.log(chalk2.dim("No changes needed"));
4324
+ return;
4325
+ }
4326
+ const backupPath = `${configPath}.backup`;
4327
+ await fs2.copyFile(configPath, backupPath);
4328
+ let upgradedConfig;
4329
+ let migrated = false;
4330
+ if (migrationNeeded) {
4331
+ console.log(chalk2.blue(`\u{1F504} Migrating config from v0.2.0 to v${CURRENT_CONFIG_VERSION}...`));
4332
+ upgradedConfig = migrateConfig(existingConfig);
4333
+ migrated = true;
4334
+ } else {
4335
+ upgradedConfig = deepMergeConfig(defaultConfig, existingConfig);
4336
+ console.log(chalk2.dim("\nNew options added:"));
4337
+ newFields.forEach((field) => console.log(chalk2.dim(" \u2022"), chalk2.bold(field)));
4338
+ }
4339
+ await fs2.writeFile(
4340
+ configPath,
4341
+ JSON.stringify(upgradedConfig, null, 2) + "\n",
4342
+ "utf-8"
4343
+ );
4344
+ console.log(chalk2.green("\u2713 Config upgraded successfully"));
4345
+ console.log(chalk2.dim("Backup saved to:"), backupPath);
4346
+ if (migrated) {
4347
+ console.log(chalk2.dim("\n\u{1F4DD} Your config now uses the framework-based structure."));
4348
+ }
4349
+ } catch (error) {
4350
+ if (error.code === "ENOENT") {
4351
+ console.log(chalk2.red("Error: No config file found"));
4352
+ console.log(chalk2.dim("Run"), chalk2.bold("lien init"), chalk2.dim("to create a config file"));
4353
+ return;
4354
+ }
4355
+ throw error;
4356
+ }
4357
+ }
4358
+ /**
4359
+ * Perform migration and return result
4360
+ * Used when programmatic access to migration result is needed
4361
+ */
4362
+ async migrate() {
4363
+ return migrateConfigFile(this.rootDir);
4364
+ }
4365
+ };
3793
4366
 
3794
4367
  // src/frameworks/detector-service.ts
3795
- import fs4 from "fs/promises";
3796
- import path4 from "path";
4368
+ import fs6 from "fs/promises";
4369
+ import path6 from "path";
3797
4370
 
3798
4371
  // src/frameworks/types.ts
3799
4372
  var defaultDetectionOptions = {
@@ -3815,8 +4388,8 @@ var defaultDetectionOptions = {
3815
4388
  };
3816
4389
 
3817
4390
  // src/frameworks/nodejs/detector.ts
3818
- import fs from "fs/promises";
3819
- import path from "path";
4391
+ import fs3 from "fs/promises";
4392
+ import path3 from "path";
3820
4393
 
3821
4394
  // src/frameworks/nodejs/config.ts
3822
4395
  async function generateNodeJsConfig(_rootDir, _relativePath) {
@@ -3867,7 +4440,7 @@ var nodejsDetector = {
3867
4440
  priority: 50,
3868
4441
  // Generic, yields to specific frameworks like Laravel
3869
4442
  async detect(rootDir, relativePath) {
3870
- const fullPath = path.join(rootDir, relativePath);
4443
+ const fullPath = path3.join(rootDir, relativePath);
3871
4444
  const result = {
3872
4445
  detected: false,
3873
4446
  name: "nodejs",
@@ -3875,10 +4448,10 @@ var nodejsDetector = {
3875
4448
  confidence: "low",
3876
4449
  evidence: []
3877
4450
  };
3878
- const packageJsonPath = path.join(fullPath, "package.json");
4451
+ const packageJsonPath = path3.join(fullPath, "package.json");
3879
4452
  let packageJson5 = null;
3880
4453
  try {
3881
- const content = await fs.readFile(packageJsonPath, "utf-8");
4454
+ const content = await fs3.readFile(packageJsonPath, "utf-8");
3882
4455
  packageJson5 = JSON.parse(content);
3883
4456
  result.evidence.push("Found package.json");
3884
4457
  } catch {
@@ -3926,8 +4499,8 @@ var nodejsDetector = {
3926
4499
  };
3927
4500
 
3928
4501
  // src/frameworks/laravel/detector.ts
3929
- import fs2 from "fs/promises";
3930
- import path2 from "path";
4502
+ import fs4 from "fs/promises";
4503
+ import path4 from "path";
3931
4504
 
3932
4505
  // src/frameworks/laravel/config.ts
3933
4506
  async function generateLaravelConfig(_rootDir, _relativePath) {
@@ -3984,7 +4557,7 @@ var laravelDetector = {
3984
4557
  priority: 100,
3985
4558
  // Laravel takes precedence over Node.js
3986
4559
  async detect(rootDir, relativePath) {
3987
- const fullPath = path2.join(rootDir, relativePath);
4560
+ const fullPath = path4.join(rootDir, relativePath);
3988
4561
  const result = {
3989
4562
  detected: false,
3990
4563
  name: "laravel",
@@ -3992,10 +4565,10 @@ var laravelDetector = {
3992
4565
  confidence: "low",
3993
4566
  evidence: []
3994
4567
  };
3995
- const composerJsonPath = path2.join(fullPath, "composer.json");
4568
+ const composerJsonPath = path4.join(fullPath, "composer.json");
3996
4569
  let composerJson = null;
3997
4570
  try {
3998
- const content = await fs2.readFile(composerJsonPath, "utf-8");
4571
+ const content = await fs4.readFile(composerJsonPath, "utf-8");
3999
4572
  composerJson = JSON.parse(content);
4000
4573
  result.evidence.push("Found composer.json");
4001
4574
  } catch {
@@ -4006,9 +4579,9 @@ var laravelDetector = {
4006
4579
  return result;
4007
4580
  }
4008
4581
  result.evidence.push("Laravel framework detected in composer.json");
4009
- const artisanPath = path2.join(fullPath, "artisan");
4582
+ const artisanPath = path4.join(fullPath, "artisan");
4010
4583
  try {
4011
- await fs2.access(artisanPath);
4584
+ await fs4.access(artisanPath);
4012
4585
  result.evidence.push("Found artisan file");
4013
4586
  result.confidence = "high";
4014
4587
  } catch {
@@ -4018,8 +4591,8 @@ var laravelDetector = {
4018
4591
  let foundDirs = 0;
4019
4592
  for (const dir of laravelDirs) {
4020
4593
  try {
4021
- const dirPath = path2.join(fullPath, dir);
4022
- const stats = await fs2.stat(dirPath);
4594
+ const dirPath = path4.join(fullPath, dir);
4595
+ const stats = await fs4.stat(dirPath);
4023
4596
  if (stats.isDirectory()) {
4024
4597
  foundDirs++;
4025
4598
  }
@@ -4031,14 +4604,14 @@ var laravelDetector = {
4031
4604
  result.confidence = "high";
4032
4605
  }
4033
4606
  const testDirsToCheck = [
4034
- path2.join(fullPath, "tests", "Feature"),
4035
- path2.join(fullPath, "tests", "Unit")
4607
+ path4.join(fullPath, "tests", "Feature"),
4608
+ path4.join(fullPath, "tests", "Unit")
4036
4609
  ];
4037
4610
  for (const testDir of testDirsToCheck) {
4038
4611
  try {
4039
- const stats = await fs2.stat(testDir);
4612
+ const stats = await fs4.stat(testDir);
4040
4613
  if (stats.isDirectory()) {
4041
- const dirName = path2.basename(path2.dirname(testDir)) + "/" + path2.basename(testDir);
4614
+ const dirName = path4.basename(path4.dirname(testDir)) + "/" + path4.basename(testDir);
4042
4615
  result.evidence.push(`Found ${dirName} test directory`);
4043
4616
  }
4044
4617
  } catch {
@@ -4056,8 +4629,8 @@ var laravelDetector = {
4056
4629
  };
4057
4630
 
4058
4631
  // src/frameworks/shopify/detector.ts
4059
- import fs3 from "fs/promises";
4060
- import path3 from "path";
4632
+ import fs5 from "fs/promises";
4633
+ import path5 from "path";
4061
4634
 
4062
4635
  // src/frameworks/shopify/config.ts
4063
4636
  async function generateShopifyConfig(_rootDir, _relativePath) {
@@ -4069,6 +4642,8 @@ async function generateShopifyConfig(_rootDir, _relativePath) {
4069
4642
  "snippets/**/*.liquid",
4070
4643
  "templates/**/*.liquid",
4071
4644
  // Matches any nesting level (e.g., templates/customers/account.liquid)
4645
+ "templates/**/*.json",
4646
+ // JSON template definitions (Shopify 2.0+)
4072
4647
  // Theme editor blocks (Online Store 2.0)
4073
4648
  "blocks/**/*.liquid",
4074
4649
  // Assets (CSS, JS with optional Liquid templating)
@@ -4113,7 +4688,7 @@ var shopifyDetector = {
4113
4688
  priority: 100,
4114
4689
  // High priority (same as Laravel)
4115
4690
  async detect(rootDir, relativePath) {
4116
- const fullPath = path3.join(rootDir, relativePath);
4691
+ const fullPath = path5.join(rootDir, relativePath);
4117
4692
  const result = {
4118
4693
  detected: false,
4119
4694
  name: "shopify",
@@ -4121,18 +4696,18 @@ var shopifyDetector = {
4121
4696
  confidence: "low",
4122
4697
  evidence: []
4123
4698
  };
4124
- const settingsSchemaPath = path3.join(fullPath, "config", "settings_schema.json");
4699
+ const settingsSchemaPath = path5.join(fullPath, "config", "settings_schema.json");
4125
4700
  let hasSettingsSchema = false;
4126
4701
  try {
4127
- await fs3.access(settingsSchemaPath);
4702
+ await fs5.access(settingsSchemaPath);
4128
4703
  hasSettingsSchema = true;
4129
4704
  result.evidence.push("Found config/settings_schema.json");
4130
4705
  } catch {
4131
4706
  }
4132
- const themeLayoutPath = path3.join(fullPath, "layout", "theme.liquid");
4707
+ const themeLayoutPath = path5.join(fullPath, "layout", "theme.liquid");
4133
4708
  let hasThemeLayout = false;
4134
4709
  try {
4135
- await fs3.access(themeLayoutPath);
4710
+ await fs5.access(themeLayoutPath);
4136
4711
  hasThemeLayout = true;
4137
4712
  result.evidence.push("Found layout/theme.liquid");
4138
4713
  } catch {
@@ -4141,8 +4716,8 @@ var shopifyDetector = {
4141
4716
  let foundDirs = 0;
4142
4717
  for (const dir of shopifyDirs) {
4143
4718
  try {
4144
- const dirPath = path3.join(fullPath, dir);
4145
- const stats = await fs3.stat(dirPath);
4719
+ const dirPath = path5.join(fullPath, dir);
4720
+ const stats = await fs5.stat(dirPath);
4146
4721
  if (stats.isDirectory()) {
4147
4722
  foundDirs++;
4148
4723
  }
@@ -4153,14 +4728,14 @@ var shopifyDetector = {
4153
4728
  result.evidence.push(`Shopify directory structure detected (${foundDirs}/${shopifyDirs.length} dirs)`);
4154
4729
  }
4155
4730
  try {
4156
- const tomlPath = path3.join(fullPath, "shopify.theme.toml");
4157
- await fs3.access(tomlPath);
4731
+ const tomlPath = path5.join(fullPath, "shopify.theme.toml");
4732
+ await fs5.access(tomlPath);
4158
4733
  result.evidence.push("Found shopify.theme.toml");
4159
4734
  } catch {
4160
4735
  }
4161
4736
  try {
4162
- const ignorePath = path3.join(fullPath, ".shopifyignore");
4163
- await fs3.access(ignorePath);
4737
+ const ignorePath = path5.join(fullPath, ".shopifyignore");
4738
+ await fs5.access(ignorePath);
4164
4739
  result.evidence.push("Found .shopifyignore");
4165
4740
  } catch {
4166
4741
  }
@@ -4206,7 +4781,7 @@ async function detectAllFrameworks(rootDir, options = {}) {
4206
4781
  return results;
4207
4782
  }
4208
4783
  async function detectAtPath(rootDir, relativePath, results, visited) {
4209
- const fullPath = path4.join(rootDir, relativePath);
4784
+ const fullPath = path6.join(rootDir, relativePath);
4210
4785
  if (visited.has(fullPath)) {
4211
4786
  return;
4212
4787
  }
@@ -4273,9 +4848,9 @@ async function scanSubdirectories(rootDir, relativePath, results, visited, depth
4273
4848
  if (depth >= options.maxDepth) {
4274
4849
  return;
4275
4850
  }
4276
- const fullPath = path4.join(rootDir, relativePath);
4851
+ const fullPath = path6.join(rootDir, relativePath);
4277
4852
  try {
4278
- const entries = await fs4.readdir(fullPath, { withFileTypes: true });
4853
+ const entries = await fs6.readdir(fullPath, { withFileTypes: true });
4279
4854
  const dirs = entries.filter((e) => e.isDirectory());
4280
4855
  for (const dir of dirs) {
4281
4856
  if (options.skipDirs.includes(dir.name)) {
@@ -4284,7 +4859,7 @@ async function scanSubdirectories(rootDir, relativePath, results, visited, depth
4284
4859
  if (dir.name.startsWith(".")) {
4285
4860
  continue;
4286
4861
  }
4287
- const subPath = relativePath === "." ? dir.name : path4.join(relativePath, dir.name);
4862
+ const subPath = relativePath === "." ? dir.name : path6.join(relativePath, dir.name);
4288
4863
  await detectAtPath(rootDir, subPath, results, visited);
4289
4864
  await scanSubdirectories(rootDir, subPath, results, visited, depth + 1, options);
4290
4865
  }
@@ -4294,44 +4869,44 @@ async function scanSubdirectories(rootDir, relativePath, results, visited, depth
4294
4869
  }
4295
4870
 
4296
4871
  // src/cli/init.ts
4297
- init_constants();
4298
4872
  var __filename3 = fileURLToPath3(import.meta.url);
4299
- var __dirname3 = path5.dirname(__filename3);
4873
+ var __dirname3 = path7.dirname(__filename3);
4300
4874
  async function initCommand(options = {}) {
4301
4875
  const rootDir = options.path || process.cwd();
4302
- const configPath = path5.join(rootDir, ".lien.config.json");
4876
+ const configPath = path7.join(rootDir, ".lien.config.json");
4303
4877
  try {
4304
4878
  let configExists = false;
4305
4879
  try {
4306
- await fs5.access(configPath);
4880
+ await fs7.access(configPath);
4307
4881
  configExists = true;
4308
4882
  } catch {
4309
4883
  }
4310
4884
  if (configExists && options.upgrade) {
4311
- await upgradeConfig(configPath);
4885
+ const migrationManager = new MigrationManager(rootDir);
4886
+ await migrationManager.upgradeInteractive();
4312
4887
  return;
4313
4888
  }
4314
4889
  if (configExists && !options.upgrade) {
4315
- console.log(chalk2.yellow("\u26A0\uFE0F .lien.config.json already exists"));
4316
- console.log(chalk2.dim("Run"), chalk2.bold("lien init --upgrade"), chalk2.dim("to merge new config options"));
4890
+ console.log(chalk3.yellow("\u26A0\uFE0F .lien.config.json already exists"));
4891
+ console.log(chalk3.dim("Run"), chalk3.bold("lien init --upgrade"), chalk3.dim("to merge new config options"));
4317
4892
  return;
4318
4893
  }
4319
4894
  if (!configExists) {
4320
4895
  await createNewConfig(rootDir, options);
4321
4896
  }
4322
4897
  } catch (error) {
4323
- console.error(chalk2.red("Error creating config file:"), error);
4898
+ console.error(chalk3.red("Error creating config file:"), error);
4324
4899
  process.exit(1);
4325
4900
  }
4326
4901
  }
4327
4902
  async function createNewConfig(rootDir, options) {
4328
4903
  showCompactBanner();
4329
- console.log(chalk2.bold("Initializing Lien...\n"));
4330
- console.log(chalk2.dim("\u{1F50D} Detecting frameworks in"), chalk2.bold(rootDir));
4904
+ console.log(chalk3.bold("Initializing Lien...\n"));
4905
+ console.log(chalk3.dim("\u{1F50D} Detecting frameworks in"), chalk3.bold(rootDir));
4331
4906
  const detections = await detectAllFrameworks(rootDir);
4332
4907
  let frameworks = [];
4333
4908
  if (detections.length === 0) {
4334
- console.log(chalk2.yellow("\n\u26A0\uFE0F No frameworks detected"));
4909
+ console.log(chalk3.yellow("\n\u26A0\uFE0F No frameworks detected"));
4335
4910
  if (!options.yes) {
4336
4911
  const { useGeneric } = await inquirer.prompt([
4337
4912
  {
@@ -4342,7 +4917,7 @@ async function createNewConfig(rootDir, options) {
4342
4917
  }
4343
4918
  ]);
4344
4919
  if (!useGeneric) {
4345
- console.log(chalk2.dim("Aborted."));
4920
+ console.log(chalk3.dim("Aborted."));
4346
4921
  return;
4347
4922
  }
4348
4923
  }
@@ -4365,16 +4940,16 @@ async function createNewConfig(rootDir, options) {
4365
4940
  }
4366
4941
  });
4367
4942
  } else {
4368
- console.log(chalk2.green(`
4943
+ console.log(chalk3.green(`
4369
4944
  \u2713 Found ${detections.length} framework(s):
4370
4945
  `));
4371
4946
  for (const det of detections) {
4372
4947
  const pathDisplay = det.path === "." ? "root" : det.path;
4373
- console.log(chalk2.bold(` ${det.name}`), chalk2.dim(`(${det.confidence} confidence)`));
4374
- console.log(chalk2.dim(` Location: ${pathDisplay}`));
4948
+ console.log(chalk3.bold(` ${det.name}`), chalk3.dim(`(${det.confidence} confidence)`));
4949
+ console.log(chalk3.dim(` Location: ${pathDisplay}`));
4375
4950
  if (det.evidence.length > 0) {
4376
4951
  det.evidence.forEach((e) => {
4377
- console.log(chalk2.dim(` \u2022 ${e}`));
4952
+ console.log(chalk3.dim(` \u2022 ${e}`));
4378
4953
  });
4379
4954
  }
4380
4955
  console.log();
@@ -4389,14 +4964,14 @@ async function createNewConfig(rootDir, options) {
4389
4964
  }
4390
4965
  ]);
4391
4966
  if (!confirm) {
4392
- console.log(chalk2.dim("Aborted."));
4967
+ console.log(chalk3.dim("Aborted."));
4393
4968
  return;
4394
4969
  }
4395
4970
  }
4396
4971
  for (const det of detections) {
4397
4972
  const detector = getFrameworkDetector(det.name);
4398
4973
  if (!detector) {
4399
- console.warn(chalk2.yellow(`\u26A0\uFE0F No detector found for ${det.name}, skipping`));
4974
+ console.warn(chalk3.yellow(`\u26A0\uFE0F No detector found for ${det.name}, skipping`));
4400
4975
  continue;
4401
4976
  }
4402
4977
  const frameworkConfig = await detector.generateConfig(rootDir, det.path);
@@ -4418,7 +4993,7 @@ async function createNewConfig(rootDir, options) {
4418
4993
  finalConfig = { ...frameworkConfig, ...customized };
4419
4994
  } else {
4420
4995
  const pathDisplay = det.path === "." ? "root" : det.path;
4421
- console.log(chalk2.dim(` \u2192 Using defaults for ${det.name} at ${pathDisplay}`));
4996
+ console.log(chalk3.dim(` \u2192 Using defaults for ${det.name} at ${pathDisplay}`));
4422
4997
  }
4423
4998
  frameworks.push({
4424
4999
  name: det.name,
@@ -4439,23 +5014,23 @@ async function createNewConfig(rootDir, options) {
4439
5014
  ]);
4440
5015
  if (installCursorRules) {
4441
5016
  try {
4442
- const cursorRulesDir = path5.join(rootDir, ".cursor");
4443
- await fs5.mkdir(cursorRulesDir, { recursive: true });
4444
- const templatePath = path5.join(__dirname3, "../CURSOR_RULES_TEMPLATE.md");
4445
- const rulesPath = path5.join(cursorRulesDir, "rules");
5017
+ const cursorRulesDir = path7.join(rootDir, ".cursor");
5018
+ await fs7.mkdir(cursorRulesDir, { recursive: true });
5019
+ const templatePath = path7.join(__dirname3, "../CURSOR_RULES_TEMPLATE.md");
5020
+ const rulesPath = path7.join(cursorRulesDir, "rules");
4446
5021
  let targetPath;
4447
5022
  let isDirectory = false;
4448
5023
  let isFile = false;
4449
5024
  try {
4450
- const stats = await fs5.stat(rulesPath);
5025
+ const stats = await fs7.stat(rulesPath);
4451
5026
  isDirectory = stats.isDirectory();
4452
5027
  isFile = stats.isFile();
4453
5028
  } catch {
4454
5029
  }
4455
5030
  if (isDirectory) {
4456
- targetPath = path5.join(rulesPath, "lien.mdc");
4457
- await fs5.copyFile(templatePath, targetPath);
4458
- console.log(chalk2.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
5031
+ targetPath = path7.join(rulesPath, "lien.mdc");
5032
+ await fs7.copyFile(templatePath, targetPath);
5033
+ console.log(chalk3.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
4459
5034
  } else if (isFile) {
4460
5035
  const { convertToDir } = await inquirer.prompt([
4461
5036
  {
@@ -4466,27 +5041,27 @@ async function createNewConfig(rootDir, options) {
4466
5041
  }
4467
5042
  ]);
4468
5043
  if (convertToDir) {
4469
- const existingRules = await fs5.readFile(rulesPath, "utf-8");
4470
- await fs5.unlink(rulesPath);
4471
- await fs5.mkdir(rulesPath);
4472
- await fs5.writeFile(path5.join(rulesPath, "project.mdc"), existingRules);
4473
- await fs5.copyFile(templatePath, path5.join(rulesPath, "lien.mdc"));
4474
- console.log(chalk2.green("\u2713 Converted .cursor/rules to directory"));
4475
- console.log(chalk2.green(" - Your project rules: .cursor/rules/project.mdc"));
4476
- console.log(chalk2.green(" - Lien rules: .cursor/rules/lien.mdc"));
5044
+ const existingRules = await fs7.readFile(rulesPath, "utf-8");
5045
+ await fs7.unlink(rulesPath);
5046
+ await fs7.mkdir(rulesPath);
5047
+ await fs7.writeFile(path7.join(rulesPath, "project.mdc"), existingRules);
5048
+ await fs7.copyFile(templatePath, path7.join(rulesPath, "lien.mdc"));
5049
+ console.log(chalk3.green("\u2713 Converted .cursor/rules to directory"));
5050
+ console.log(chalk3.green(" - Your project rules: .cursor/rules/project.mdc"));
5051
+ console.log(chalk3.green(" - Lien rules: .cursor/rules/lien.mdc"));
4477
5052
  } else {
4478
- console.log(chalk2.dim("Skipped Cursor rules installation (preserving existing file)"));
5053
+ console.log(chalk3.dim("Skipped Cursor rules installation (preserving existing file)"));
4479
5054
  }
4480
5055
  } else {
4481
- await fs5.mkdir(rulesPath, { recursive: true });
4482
- targetPath = path5.join(rulesPath, "lien.mdc");
4483
- await fs5.copyFile(templatePath, targetPath);
4484
- console.log(chalk2.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
5056
+ await fs7.mkdir(rulesPath, { recursive: true });
5057
+ targetPath = path7.join(rulesPath, "lien.mdc");
5058
+ await fs7.copyFile(templatePath, targetPath);
5059
+ console.log(chalk3.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
4485
5060
  }
4486
5061
  } catch (error) {
4487
- console.log(chalk2.yellow("\u26A0\uFE0F Could not install Cursor rules"));
4488
- console.log(chalk2.dim(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
4489
- console.log(chalk2.dim("You can manually copy CURSOR_RULES_TEMPLATE.md to .cursor/rules/lien.mdc"));
5062
+ console.log(chalk3.yellow("\u26A0\uFE0F Could not install Cursor rules"));
5063
+ console.log(chalk3.dim(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
5064
+ console.log(chalk3.dim("You can manually copy CURSOR_RULES_TEMPLATE.md to .cursor/rules/lien.mdc"));
4490
5065
  }
4491
5066
  }
4492
5067
  }
@@ -4494,17 +5069,17 @@ async function createNewConfig(rootDir, options) {
4494
5069
  ...defaultConfig,
4495
5070
  frameworks
4496
5071
  };
4497
- const configPath = path5.join(rootDir, ".lien.config.json");
4498
- await fs5.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
4499
- console.log(chalk2.green("\n\u2713 Created .lien.config.json"));
4500
- console.log(chalk2.green(`\u2713 Configured ${frameworks.length} framework(s)`));
4501
- console.log(chalk2.dim("\nNext steps:"));
4502
- console.log(chalk2.dim(" 1. Run"), chalk2.bold("lien index"), chalk2.dim("to index your codebase"));
4503
- console.log(chalk2.dim(" 2. Run"), chalk2.bold("lien serve"), chalk2.dim("to start the MCP server"));
4504
- console.log(chalk2.dim(" 3. Configure Cursor to use the MCP server (see README.md)"));
5072
+ const configPath = path7.join(rootDir, ".lien.config.json");
5073
+ await fs7.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
5074
+ console.log(chalk3.green("\n\u2713 Created .lien.config.json"));
5075
+ console.log(chalk3.green(`\u2713 Configured ${frameworks.length} framework(s)`));
5076
+ console.log(chalk3.dim("\nNext steps:"));
5077
+ console.log(chalk3.dim(" 1. Run"), chalk3.bold("lien index"), chalk3.dim("to index your codebase"));
5078
+ console.log(chalk3.dim(" 2. Run"), chalk3.bold("lien serve"), chalk3.dim("to start the MCP server"));
5079
+ console.log(chalk3.dim(" 3. Configure Cursor to use the MCP server (see README.md)"));
4505
5080
  }
4506
5081
  async function promptForCustomization(frameworkName, config) {
4507
- console.log(chalk2.bold(`
5082
+ console.log(chalk3.bold(`
4508
5083
  Customizing ${frameworkName} settings:`));
4509
5084
  const answers = await inquirer.prompt([
4510
5085
  {
@@ -4527,155 +5102,115 @@ Customizing ${frameworkName} settings:`));
4527
5102
  exclude: answers.exclude
4528
5103
  };
4529
5104
  }
4530
- async function upgradeConfig(configPath) {
4531
- try {
4532
- const existingContent = await fs5.readFile(configPath, "utf-8");
4533
- const existingConfig = JSON.parse(existingContent);
4534
- const migrationNeeded = needsMigration(existingConfig);
4535
- const newFields = migrationNeeded ? [] : detectNewFields(existingConfig, defaultConfig);
4536
- const hasChanges = migrationNeeded || newFields.length > 0;
4537
- if (!hasChanges) {
4538
- console.log(chalk2.green("\u2713 Config is already up to date"));
4539
- console.log(chalk2.dim("No changes needed"));
4540
- return;
4541
- }
4542
- const backupPath = `${configPath}.backup`;
4543
- await fs5.copyFile(configPath, backupPath);
4544
- let upgradedConfig;
4545
- let migrated = false;
4546
- if (migrationNeeded) {
4547
- console.log(chalk2.blue(`\u{1F504} Migrating config from v0.2.0 to v${CURRENT_CONFIG_VERSION}...`));
4548
- upgradedConfig = migrateConfig(existingConfig);
4549
- migrated = true;
4550
- } else {
4551
- upgradedConfig = deepMergeConfig(defaultConfig, existingConfig);
4552
- console.log(chalk2.dim("\nNew options added:"));
4553
- newFields.forEach((field) => console.log(chalk2.dim(" \u2022"), chalk2.bold(field)));
4554
- }
4555
- await fs5.writeFile(
4556
- configPath,
4557
- JSON.stringify(upgradedConfig, null, 2) + "\n",
4558
- "utf-8"
4559
- );
4560
- console.log(chalk2.green("\u2713 Config upgraded successfully"));
4561
- console.log(chalk2.dim("Backup saved to:"), backupPath);
4562
- if (migrated) {
4563
- console.log(chalk2.dim("\n\u{1F4DD} Your config now uses the framework-based structure."));
4564
- }
4565
- } catch (error) {
4566
- console.error(chalk2.red("Error upgrading config:"), error);
4567
- throw error;
4568
- }
4569
- }
4570
5105
 
4571
5106
  // src/cli/status.ts
4572
5107
  init_service();
4573
5108
  init_utils();
4574
5109
  init_version2();
4575
- import chalk3 from "chalk";
4576
- import fs9 from "fs/promises";
4577
- import path9 from "path";
5110
+ import chalk4 from "chalk";
5111
+ import fs11 from "fs/promises";
5112
+ import path11 from "path";
4578
5113
  import os from "os";
4579
5114
  import crypto from "crypto";
4580
5115
  init_schema();
4581
5116
  async function statusCommand() {
4582
5117
  const rootDir = process.cwd();
4583
- const projectName = path9.basename(rootDir);
5118
+ const projectName = path11.basename(rootDir);
4584
5119
  const pathHash = crypto.createHash("md5").update(rootDir).digest("hex").substring(0, 8);
4585
- const indexPath = path9.join(os.homedir(), ".lien", "indices", `${projectName}-${pathHash}`);
5120
+ const indexPath = path11.join(os.homedir(), ".lien", "indices", `${projectName}-${pathHash}`);
4586
5121
  showCompactBanner();
4587
- console.log(chalk3.bold("Status\n"));
5122
+ console.log(chalk4.bold("Status\n"));
4588
5123
  const hasConfig = await configService.exists(rootDir);
4589
- console.log(chalk3.dim("Configuration:"), hasConfig ? chalk3.green("\u2713 Found") : chalk3.red("\u2717 Not initialized"));
5124
+ console.log(chalk4.dim("Configuration:"), hasConfig ? chalk4.green("\u2713 Found") : chalk4.red("\u2717 Not initialized"));
4590
5125
  if (!hasConfig) {
4591
- console.log(chalk3.yellow("\nRun"), chalk3.bold("lien init"), chalk3.yellow("to initialize"));
5126
+ console.log(chalk4.yellow("\nRun"), chalk4.bold("lien init"), chalk4.yellow("to initialize"));
4592
5127
  return;
4593
5128
  }
4594
5129
  try {
4595
- const stats = await fs9.stat(indexPath);
4596
- console.log(chalk3.dim("Index location:"), indexPath);
4597
- console.log(chalk3.dim("Index status:"), chalk3.green("\u2713 Exists"));
5130
+ const stats = await fs11.stat(indexPath);
5131
+ console.log(chalk4.dim("Index location:"), indexPath);
5132
+ console.log(chalk4.dim("Index status:"), chalk4.green("\u2713 Exists"));
4598
5133
  try {
4599
- const files = await fs9.readdir(indexPath, { recursive: true });
4600
- console.log(chalk3.dim("Index files:"), files.length);
5134
+ const files = await fs11.readdir(indexPath, { recursive: true });
5135
+ console.log(chalk4.dim("Index files:"), files.length);
4601
5136
  } catch (e) {
4602
5137
  }
4603
- console.log(chalk3.dim("Last modified:"), stats.mtime.toLocaleString());
5138
+ console.log(chalk4.dim("Last modified:"), stats.mtime.toLocaleString());
4604
5139
  try {
4605
5140
  const version = await readVersionFile(indexPath);
4606
5141
  if (version > 0) {
4607
5142
  const versionDate = new Date(version);
4608
- console.log(chalk3.dim("Last reindex:"), versionDate.toLocaleString());
5143
+ console.log(chalk4.dim("Last reindex:"), versionDate.toLocaleString());
4609
5144
  }
4610
5145
  } catch {
4611
5146
  }
4612
5147
  } catch (error) {
4613
- console.log(chalk3.dim("Index status:"), chalk3.yellow("\u2717 Not indexed"));
4614
- console.log(chalk3.yellow("\nRun"), chalk3.bold("lien index"), chalk3.yellow("to index your codebase"));
5148
+ console.log(chalk4.dim("Index status:"), chalk4.yellow("\u2717 Not indexed"));
5149
+ console.log(chalk4.yellow("\nRun"), chalk4.bold("lien index"), chalk4.yellow("to index your codebase"));
4615
5150
  }
4616
5151
  try {
4617
5152
  const config = await configService.load(rootDir);
4618
- console.log(chalk3.bold("\nFeatures:"));
5153
+ console.log(chalk4.bold("\nFeatures:"));
4619
5154
  const isRepo = await isGitRepo(rootDir);
4620
5155
  if (config.gitDetection.enabled && isRepo) {
4621
- console.log(chalk3.dim("Git detection:"), chalk3.green("\u2713 Enabled"));
4622
- console.log(chalk3.dim(" Poll interval:"), `${config.gitDetection.pollIntervalMs / 1e3}s`);
5156
+ console.log(chalk4.dim("Git detection:"), chalk4.green("\u2713 Enabled"));
5157
+ console.log(chalk4.dim(" Poll interval:"), `${config.gitDetection.pollIntervalMs / 1e3}s`);
4623
5158
  try {
4624
5159
  const branch = await getCurrentBranch(rootDir);
4625
5160
  const commit = await getCurrentCommit(rootDir);
4626
- console.log(chalk3.dim(" Current branch:"), branch);
4627
- console.log(chalk3.dim(" Current commit:"), commit.substring(0, 8));
4628
- const gitStateFile = path9.join(indexPath, ".git-state.json");
5161
+ console.log(chalk4.dim(" Current branch:"), branch);
5162
+ console.log(chalk4.dim(" Current commit:"), commit.substring(0, 8));
5163
+ const gitStateFile = path11.join(indexPath, ".git-state.json");
4629
5164
  try {
4630
- const gitStateContent = await fs9.readFile(gitStateFile, "utf-8");
5165
+ const gitStateContent = await fs11.readFile(gitStateFile, "utf-8");
4631
5166
  const gitState = JSON.parse(gitStateContent);
4632
5167
  if (gitState.branch !== branch || gitState.commit !== commit) {
4633
- console.log(chalk3.yellow(" \u26A0\uFE0F Git state changed - will reindex on next serve"));
5168
+ console.log(chalk4.yellow(" \u26A0\uFE0F Git state changed - will reindex on next serve"));
4634
5169
  }
4635
5170
  } catch {
4636
5171
  }
4637
5172
  } catch {
4638
5173
  }
4639
5174
  } else if (config.gitDetection.enabled && !isRepo) {
4640
- console.log(chalk3.dim("Git detection:"), chalk3.yellow("Enabled (not a git repo)"));
5175
+ console.log(chalk4.dim("Git detection:"), chalk4.yellow("Enabled (not a git repo)"));
4641
5176
  } else {
4642
- console.log(chalk3.dim("Git detection:"), chalk3.gray("Disabled"));
5177
+ console.log(chalk4.dim("Git detection:"), chalk4.gray("Disabled"));
4643
5178
  }
4644
5179
  if (config.fileWatching.enabled) {
4645
- console.log(chalk3.dim("File watching:"), chalk3.green("\u2713 Enabled"));
4646
- console.log(chalk3.dim(" Debounce:"), `${config.fileWatching.debounceMs}ms`);
5180
+ console.log(chalk4.dim("File watching:"), chalk4.green("\u2713 Enabled"));
5181
+ console.log(chalk4.dim(" Debounce:"), `${config.fileWatching.debounceMs}ms`);
4647
5182
  } else {
4648
- console.log(chalk3.dim("File watching:"), chalk3.gray("Disabled"));
4649
- console.log(chalk3.dim(" Enable with:"), chalk3.bold("lien serve --watch"));
5183
+ console.log(chalk4.dim("File watching:"), chalk4.gray("Disabled"));
5184
+ console.log(chalk4.dim(" Enable with:"), chalk4.bold("lien serve --watch"));
4650
5185
  }
4651
- console.log(chalk3.bold("\nIndexing Settings:"));
5186
+ console.log(chalk4.bold("\nIndexing Settings:"));
4652
5187
  if (isModernConfig(config)) {
4653
- console.log(chalk3.dim("Concurrency:"), config.core.concurrency);
4654
- console.log(chalk3.dim("Batch size:"), config.core.embeddingBatchSize);
4655
- console.log(chalk3.dim("Chunk size:"), config.core.chunkSize);
4656
- console.log(chalk3.dim("Chunk overlap:"), config.core.chunkOverlap);
5188
+ console.log(chalk4.dim("Concurrency:"), config.core.concurrency);
5189
+ console.log(chalk4.dim("Batch size:"), config.core.embeddingBatchSize);
5190
+ console.log(chalk4.dim("Chunk size:"), config.core.chunkSize);
5191
+ console.log(chalk4.dim("Chunk overlap:"), config.core.chunkOverlap);
4657
5192
  }
4658
5193
  } catch (error) {
4659
- console.log(chalk3.yellow("\nWarning: Could not load configuration"));
5194
+ console.log(chalk4.yellow("\nWarning: Could not load configuration"));
4660
5195
  }
4661
5196
  }
4662
5197
 
4663
5198
  // src/cli/index-cmd.ts
4664
5199
  init_indexer();
4665
- import chalk5 from "chalk";
5200
+ import chalk6 from "chalk";
4666
5201
  async function indexCommand(options) {
4667
5202
  showCompactBanner();
4668
5203
  try {
4669
5204
  if (options.force) {
4670
5205
  const { VectorDB: VectorDB2 } = await Promise.resolve().then(() => (init_lancedb(), lancedb_exports));
4671
5206
  const { ManifestManager: ManifestManager2 } = await Promise.resolve().then(() => (init_manifest(), manifest_exports));
4672
- console.log(chalk5.yellow("Clearing existing index and manifest..."));
5207
+ console.log(chalk6.yellow("Clearing existing index and manifest..."));
4673
5208
  const vectorDB = new VectorDB2(process.cwd());
4674
5209
  await vectorDB.initialize();
4675
5210
  await vectorDB.clear();
4676
5211
  const manifest = new ManifestManager2(vectorDB.dbPath);
4677
5212
  await manifest.clear();
4678
- console.log(chalk5.green("\u2713 Index and manifest cleared\n"));
5213
+ console.log(chalk6.green("\u2713 Index and manifest cleared\n"));
4679
5214
  }
4680
5215
  await indexCodebase({
4681
5216
  rootDir: process.cwd(),
@@ -4683,18 +5218,18 @@ async function indexCommand(options) {
4683
5218
  force: options.force || false
4684
5219
  });
4685
5220
  if (options.watch) {
4686
- console.log(chalk5.yellow("\n\u26A0\uFE0F Watch mode not yet implemented"));
5221
+ console.log(chalk6.yellow("\n\u26A0\uFE0F Watch mode not yet implemented"));
4687
5222
  }
4688
5223
  } catch (error) {
4689
- console.error(chalk5.red("Error during indexing:"), error);
5224
+ console.error(chalk6.red("Error during indexing:"), error);
4690
5225
  process.exit(1);
4691
5226
  }
4692
5227
  }
4693
5228
 
4694
5229
  // src/cli/serve.ts
4695
- import chalk6 from "chalk";
4696
- import fs16 from "fs/promises";
4697
- import path14 from "path";
5230
+ import chalk7 from "chalk";
5231
+ import fs18 from "fs/promises";
5232
+ import path17 from "path";
4698
5233
 
4699
5234
  // src/mcp/server.ts
4700
5235
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -5324,36 +5859,36 @@ async function startMCPServer(options) {
5324
5859
 
5325
5860
  // src/cli/serve.ts
5326
5861
  async function serveCommand(options) {
5327
- const rootDir = options.root ? path14.resolve(options.root) : process.cwd();
5862
+ const rootDir = options.root ? path17.resolve(options.root) : process.cwd();
5328
5863
  try {
5329
5864
  if (options.root) {
5330
5865
  try {
5331
- const stats = await fs16.stat(rootDir);
5866
+ const stats = await fs18.stat(rootDir);
5332
5867
  if (!stats.isDirectory()) {
5333
- console.error(chalk6.red(`Error: --root path is not a directory: ${rootDir}`));
5868
+ console.error(chalk7.red(`Error: --root path is not a directory: ${rootDir}`));
5334
5869
  process.exit(1);
5335
5870
  }
5336
5871
  } catch (error) {
5337
5872
  if (error.code === "ENOENT") {
5338
- console.error(chalk6.red(`Error: --root directory does not exist: ${rootDir}`));
5873
+ console.error(chalk7.red(`Error: --root directory does not exist: ${rootDir}`));
5339
5874
  } else if (error.code === "EACCES") {
5340
- console.error(chalk6.red(`Error: --root directory is not accessible: ${rootDir}`));
5875
+ console.error(chalk7.red(`Error: --root directory is not accessible: ${rootDir}`));
5341
5876
  } else {
5342
- console.error(chalk6.red(`Error: Failed to access --root directory: ${rootDir}`));
5343
- console.error(chalk6.dim(error.message));
5877
+ console.error(chalk7.red(`Error: Failed to access --root directory: ${rootDir}`));
5878
+ console.error(chalk7.dim(error.message));
5344
5879
  }
5345
5880
  process.exit(1);
5346
5881
  }
5347
5882
  }
5348
5883
  showBanner();
5349
- console.error(chalk6.bold("Starting MCP server...\n"));
5884
+ console.error(chalk7.bold("Starting MCP server...\n"));
5350
5885
  if (options.root) {
5351
- console.error(chalk6.dim(`Serving from: ${rootDir}
5886
+ console.error(chalk7.dim(`Serving from: ${rootDir}
5352
5887
  `));
5353
5888
  }
5354
5889
  if (options.watch) {
5355
- console.error(chalk6.yellow("\u26A0\uFE0F --watch flag is deprecated (file watching is now default)"));
5356
- console.error(chalk6.dim(" Use --no-watch to disable file watching\n"));
5890
+ console.error(chalk7.yellow("\u26A0\uFE0F --watch flag is deprecated (file watching is now default)"));
5891
+ console.error(chalk7.dim(" Use --no-watch to disable file watching\n"));
5357
5892
  }
5358
5893
  const watch = options.noWatch ? false : options.watch ? true : void 0;
5359
5894
  await startMCPServer({
@@ -5362,7 +5897,7 @@ async function serveCommand(options) {
5362
5897
  watch
5363
5898
  });
5364
5899
  } catch (error) {
5365
- console.error(chalk6.red("Failed to start MCP server:"), error);
5900
+ console.error(chalk7.red("Failed to start MCP server:"), error);
5366
5901
  process.exit(1);
5367
5902
  }
5368
5903
  }