@liendev/lien 0.9.0 → 0.10.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/README.md +79 -0
- package/dist/index.js +341 -135
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -329,8 +329,8 @@ var init_errors = __esm({
|
|
|
329
329
|
});
|
|
330
330
|
|
|
331
331
|
// src/config/service.ts
|
|
332
|
-
import
|
|
333
|
-
import
|
|
332
|
+
import fs6 from "fs/promises";
|
|
333
|
+
import path6 from "path";
|
|
334
334
|
var ConfigService, configService;
|
|
335
335
|
var init_service = __esm({
|
|
336
336
|
"src/config/service.ts"() {
|
|
@@ -352,13 +352,13 @@ var init_service = __esm({
|
|
|
352
352
|
async load(rootDir = process.cwd()) {
|
|
353
353
|
const configPath = this.getConfigPath(rootDir);
|
|
354
354
|
try {
|
|
355
|
-
const configContent = await
|
|
355
|
+
const configContent = await fs6.readFile(configPath, "utf-8");
|
|
356
356
|
const userConfig = JSON.parse(configContent);
|
|
357
357
|
if (this.needsMigration(userConfig)) {
|
|
358
358
|
console.log("\u{1F504} Migrating config from v0.2.0 to v0.3.0...");
|
|
359
359
|
const result = await this.migrate(rootDir);
|
|
360
360
|
if (result.migrated && result.backupPath) {
|
|
361
|
-
const backupFilename =
|
|
361
|
+
const backupFilename = path6.basename(result.backupPath);
|
|
362
362
|
console.log(`\u2705 Migration complete! Backup saved as ${backupFilename}`);
|
|
363
363
|
console.log("\u{1F4DD} Your config now uses the framework-based structure.");
|
|
364
364
|
}
|
|
@@ -414,7 +414,7 @@ ${validation.errors.join("\n")}`,
|
|
|
414
414
|
}
|
|
415
415
|
try {
|
|
416
416
|
const configJson = JSON.stringify(config, null, 2) + "\n";
|
|
417
|
-
await
|
|
417
|
+
await fs6.writeFile(configPath, configJson, "utf-8");
|
|
418
418
|
} catch (error) {
|
|
419
419
|
throw wrapError(error, "Failed to save configuration", { path: configPath });
|
|
420
420
|
}
|
|
@@ -428,7 +428,7 @@ ${validation.errors.join("\n")}`,
|
|
|
428
428
|
async exists(rootDir = process.cwd()) {
|
|
429
429
|
const configPath = this.getConfigPath(rootDir);
|
|
430
430
|
try {
|
|
431
|
-
await
|
|
431
|
+
await fs6.access(configPath);
|
|
432
432
|
return true;
|
|
433
433
|
} catch {
|
|
434
434
|
return false;
|
|
@@ -445,7 +445,7 @@ ${validation.errors.join("\n")}`,
|
|
|
445
445
|
async migrate(rootDir = process.cwd()) {
|
|
446
446
|
const configPath = this.getConfigPath(rootDir);
|
|
447
447
|
try {
|
|
448
|
-
const configContent = await
|
|
448
|
+
const configContent = await fs6.readFile(configPath, "utf-8");
|
|
449
449
|
const oldConfig = JSON.parse(configContent);
|
|
450
450
|
if (!this.needsMigration(oldConfig)) {
|
|
451
451
|
return {
|
|
@@ -463,7 +463,7 @@ ${validation.errors.join("\n")}`,
|
|
|
463
463
|
);
|
|
464
464
|
}
|
|
465
465
|
const backupPath = `${configPath}.v0.2.0.backup`;
|
|
466
|
-
await
|
|
466
|
+
await fs6.copyFile(configPath, backupPath);
|
|
467
467
|
await this.save(rootDir, newConfig);
|
|
468
468
|
return {
|
|
469
469
|
migrated: true,
|
|
@@ -561,7 +561,7 @@ ${validation.errors.join("\n")}`,
|
|
|
561
561
|
* Get the full path to the config file
|
|
562
562
|
*/
|
|
563
563
|
getConfigPath(rootDir) {
|
|
564
|
-
return
|
|
564
|
+
return path6.join(rootDir, _ConfigService.CONFIG_FILENAME);
|
|
565
565
|
}
|
|
566
566
|
/**
|
|
567
567
|
* Validate modern (v0.3.0+) configuration
|
|
@@ -725,7 +725,7 @@ ${validation.errors.join("\n")}`,
|
|
|
725
725
|
errors.push(`frameworks[${index}] missing required field: path`);
|
|
726
726
|
} else if (typeof fw.path !== "string") {
|
|
727
727
|
errors.push(`frameworks[${index}].path must be a string`);
|
|
728
|
-
} else if (
|
|
728
|
+
} else if (path6.isAbsolute(fw.path)) {
|
|
729
729
|
errors.push(`frameworks[${index}].path must be relative, got: ${fw.path}`);
|
|
730
730
|
}
|
|
731
731
|
if (fw.enabled === void 0) {
|
|
@@ -773,21 +773,21 @@ ${validation.errors.join("\n")}`,
|
|
|
773
773
|
});
|
|
774
774
|
|
|
775
775
|
// src/vectordb/version.ts
|
|
776
|
-
import
|
|
777
|
-
import
|
|
776
|
+
import fs8 from "fs/promises";
|
|
777
|
+
import path8 from "path";
|
|
778
778
|
async function writeVersionFile(indexPath) {
|
|
779
779
|
try {
|
|
780
|
-
const versionFilePath =
|
|
780
|
+
const versionFilePath = path8.join(indexPath, VERSION_FILE);
|
|
781
781
|
const timestamp = Date.now().toString();
|
|
782
|
-
await
|
|
782
|
+
await fs8.writeFile(versionFilePath, timestamp, "utf-8");
|
|
783
783
|
} catch (error) {
|
|
784
784
|
console.error(`Warning: Failed to write version file: ${error}`);
|
|
785
785
|
}
|
|
786
786
|
}
|
|
787
787
|
async function readVersionFile(indexPath) {
|
|
788
788
|
try {
|
|
789
|
-
const versionFilePath =
|
|
790
|
-
const content = await
|
|
789
|
+
const versionFilePath = path8.join(indexPath, VERSION_FILE);
|
|
790
|
+
const content = await fs8.readFile(versionFilePath, "utf-8");
|
|
791
791
|
const timestamp = parseInt(content.trim(), 10);
|
|
792
792
|
return isNaN(timestamp) ? 0 : timestamp;
|
|
793
793
|
} catch (error) {
|
|
@@ -805,8 +805,8 @@ var init_version = __esm({
|
|
|
805
805
|
// src/indexer/scanner.ts
|
|
806
806
|
import { glob } from "glob";
|
|
807
807
|
import ignore from "ignore";
|
|
808
|
-
import
|
|
809
|
-
import
|
|
808
|
+
import fs10 from "fs/promises";
|
|
809
|
+
import path10 from "path";
|
|
810
810
|
async function scanCodebaseWithFrameworks(rootDir, config) {
|
|
811
811
|
const allFiles = [];
|
|
812
812
|
for (const framework of config.frameworks) {
|
|
@@ -819,16 +819,16 @@ async function scanCodebaseWithFrameworks(rootDir, config) {
|
|
|
819
819
|
return allFiles;
|
|
820
820
|
}
|
|
821
821
|
async function scanFramework(rootDir, framework) {
|
|
822
|
-
const frameworkPath =
|
|
823
|
-
const gitignorePath =
|
|
822
|
+
const frameworkPath = path10.join(rootDir, framework.path);
|
|
823
|
+
const gitignorePath = path10.join(frameworkPath, ".gitignore");
|
|
824
824
|
let ig = ignore();
|
|
825
825
|
try {
|
|
826
|
-
const gitignoreContent = await
|
|
826
|
+
const gitignoreContent = await fs10.readFile(gitignorePath, "utf-8");
|
|
827
827
|
ig = ignore().add(gitignoreContent);
|
|
828
828
|
} catch (e) {
|
|
829
|
-
const rootGitignorePath =
|
|
829
|
+
const rootGitignorePath = path10.join(rootDir, ".gitignore");
|
|
830
830
|
try {
|
|
831
|
-
const gitignoreContent = await
|
|
831
|
+
const gitignoreContent = await fs10.readFile(rootGitignorePath, "utf-8");
|
|
832
832
|
ig = ignore().add(gitignoreContent);
|
|
833
833
|
} catch (e2) {
|
|
834
834
|
}
|
|
@@ -850,15 +850,15 @@ async function scanFramework(rootDir, framework) {
|
|
|
850
850
|
}
|
|
851
851
|
const uniqueFiles = Array.from(new Set(allFiles));
|
|
852
852
|
return uniqueFiles.filter((file) => !ig.ignores(file)).map((file) => {
|
|
853
|
-
return framework.path === "." ? file :
|
|
853
|
+
return framework.path === "." ? file : path10.join(framework.path, file);
|
|
854
854
|
});
|
|
855
855
|
}
|
|
856
856
|
async function scanCodebase(options) {
|
|
857
857
|
const { rootDir, includePatterns = [], excludePatterns = [] } = options;
|
|
858
|
-
const gitignorePath =
|
|
858
|
+
const gitignorePath = path10.join(rootDir, ".gitignore");
|
|
859
859
|
let ig = ignore();
|
|
860
860
|
try {
|
|
861
|
-
const gitignoreContent = await
|
|
861
|
+
const gitignoreContent = await fs10.readFile(gitignorePath, "utf-8");
|
|
862
862
|
ig = ignore().add(gitignoreContent);
|
|
863
863
|
} catch (e) {
|
|
864
864
|
}
|
|
@@ -885,12 +885,12 @@ async function scanCodebase(options) {
|
|
|
885
885
|
}
|
|
886
886
|
const uniqueFiles = Array.from(new Set(allFiles));
|
|
887
887
|
return uniqueFiles.filter((file) => {
|
|
888
|
-
const relativePath =
|
|
888
|
+
const relativePath = path10.relative(rootDir, file);
|
|
889
889
|
return !ig.ignores(relativePath);
|
|
890
890
|
});
|
|
891
891
|
}
|
|
892
892
|
function detectLanguage(filepath) {
|
|
893
|
-
const ext =
|
|
893
|
+
const ext = path10.extname(filepath).toLowerCase();
|
|
894
894
|
const languageMap = {
|
|
895
895
|
".ts": "typescript",
|
|
896
896
|
".tsx": "typescript",
|
|
@@ -915,6 +915,7 @@ function detectLanguage(filepath) {
|
|
|
915
915
|
".kt": "kotlin",
|
|
916
916
|
".cs": "csharp",
|
|
917
917
|
".scala": "scala",
|
|
918
|
+
".liquid": "liquid",
|
|
918
919
|
".md": "markdown",
|
|
919
920
|
".mdx": "markdown",
|
|
920
921
|
".markdown": "markdown"
|
|
@@ -1380,12 +1381,12 @@ __export(lancedb_exports, {
|
|
|
1380
1381
|
VectorDB: () => VectorDB
|
|
1381
1382
|
});
|
|
1382
1383
|
import * as lancedb from "vectordb";
|
|
1383
|
-
import
|
|
1384
|
+
import path11 from "path";
|
|
1384
1385
|
import os2 from "os";
|
|
1385
1386
|
import crypto2 from "crypto";
|
|
1386
1387
|
function isDocumentationFile(filepath) {
|
|
1387
1388
|
const lower = filepath.toLowerCase();
|
|
1388
|
-
const filename =
|
|
1389
|
+
const filename = path11.basename(filepath).toLowerCase();
|
|
1389
1390
|
if (filename.startsWith("readme")) return true;
|
|
1390
1391
|
if (filename.startsWith("changelog")) return true;
|
|
1391
1392
|
if (filename.endsWith(".md") || filename.endsWith(".mdx") || filename.endsWith(".markdown")) {
|
|
@@ -1432,7 +1433,7 @@ function boostPathRelevance(query, filepath, baseScore) {
|
|
|
1432
1433
|
return baseScore * boostFactor;
|
|
1433
1434
|
}
|
|
1434
1435
|
function boostFilenameRelevance(query, filepath, baseScore) {
|
|
1435
|
-
const filename =
|
|
1436
|
+
const filename = path11.basename(filepath, path11.extname(filepath)).toLowerCase();
|
|
1436
1437
|
const queryTokens = query.toLowerCase().split(/\s+/);
|
|
1437
1438
|
let boostFactor = 1;
|
|
1438
1439
|
for (const token of queryTokens) {
|
|
@@ -1447,7 +1448,7 @@ function boostFilenameRelevance(query, filepath, baseScore) {
|
|
|
1447
1448
|
}
|
|
1448
1449
|
function boostForLocationIntent(query, filepath, baseScore) {
|
|
1449
1450
|
let score = baseScore;
|
|
1450
|
-
const filename =
|
|
1451
|
+
const filename = path11.basename(filepath, path11.extname(filepath)).toLowerCase();
|
|
1451
1452
|
const queryTokens = query.toLowerCase().split(/\s+/);
|
|
1452
1453
|
for (const token of queryTokens) {
|
|
1453
1454
|
if (token.length <= 2) continue;
|
|
@@ -1475,7 +1476,7 @@ function boostForConceptualIntent(query, filepath, baseScore) {
|
|
|
1475
1476
|
if (isUtilityFile(filepath)) {
|
|
1476
1477
|
score *= 1.05;
|
|
1477
1478
|
}
|
|
1478
|
-
const filename =
|
|
1479
|
+
const filename = path11.basename(filepath, path11.extname(filepath)).toLowerCase();
|
|
1479
1480
|
const queryTokens = query.toLowerCase().split(/\s+/);
|
|
1480
1481
|
for (const token of queryTokens) {
|
|
1481
1482
|
if (token.length <= 2) continue;
|
|
@@ -1483,7 +1484,7 @@ function boostForConceptualIntent(query, filepath, baseScore) {
|
|
|
1483
1484
|
score *= 0.9;
|
|
1484
1485
|
}
|
|
1485
1486
|
}
|
|
1486
|
-
const pathSegments = filepath.toLowerCase().split(
|
|
1487
|
+
const pathSegments = filepath.toLowerCase().split(path11.sep);
|
|
1487
1488
|
for (const token of queryTokens) {
|
|
1488
1489
|
if (token.length <= 2) continue;
|
|
1489
1490
|
for (const segment of pathSegments) {
|
|
@@ -1537,9 +1538,9 @@ var init_lancedb = __esm({
|
|
|
1537
1538
|
lastVersionCheck = 0;
|
|
1538
1539
|
currentVersion = 0;
|
|
1539
1540
|
constructor(projectRoot) {
|
|
1540
|
-
const projectName =
|
|
1541
|
+
const projectName = path11.basename(projectRoot);
|
|
1541
1542
|
const pathHash = crypto2.createHash("md5").update(projectRoot).digest("hex").substring(0, 8);
|
|
1542
|
-
this.dbPath =
|
|
1543
|
+
this.dbPath = path11.join(
|
|
1543
1544
|
os2.homedir(),
|
|
1544
1545
|
".lien",
|
|
1545
1546
|
"indices",
|
|
@@ -1897,7 +1898,7 @@ var indexer_exports = {};
|
|
|
1897
1898
|
__export(indexer_exports, {
|
|
1898
1899
|
indexCodebase: () => indexCodebase
|
|
1899
1900
|
});
|
|
1900
|
-
import
|
|
1901
|
+
import fs11 from "fs/promises";
|
|
1901
1902
|
import ora from "ora";
|
|
1902
1903
|
import chalk4 from "chalk";
|
|
1903
1904
|
import pLimit from "p-limit";
|
|
@@ -1963,7 +1964,7 @@ async function indexCodebase(options = {}) {
|
|
|
1963
1964
|
const filePromises = files.map(
|
|
1964
1965
|
(file) => limit(async () => {
|
|
1965
1966
|
try {
|
|
1966
|
-
const content = await
|
|
1967
|
+
const content = await fs11.readFile(file, "utf-8");
|
|
1967
1968
|
const chunkSize = isModernConfig(config) ? config.core.chunkSize : 75;
|
|
1968
1969
|
const chunkOverlap = isModernConfig(config) ? config.core.chunkOverlap : 10;
|
|
1969
1970
|
const chunks = chunkFile(file, content, {
|
|
@@ -2034,15 +2035,15 @@ init_schema();
|
|
|
2034
2035
|
init_merge();
|
|
2035
2036
|
init_banner();
|
|
2036
2037
|
init_migration();
|
|
2037
|
-
import
|
|
2038
|
-
import
|
|
2038
|
+
import fs5 from "fs/promises";
|
|
2039
|
+
import path5 from "path";
|
|
2039
2040
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2040
2041
|
import chalk2 from "chalk";
|
|
2041
2042
|
import inquirer from "inquirer";
|
|
2042
2043
|
|
|
2043
2044
|
// src/frameworks/detector-service.ts
|
|
2044
|
-
import
|
|
2045
|
-
import
|
|
2045
|
+
import fs4 from "fs/promises";
|
|
2046
|
+
import path4 from "path";
|
|
2046
2047
|
|
|
2047
2048
|
// src/frameworks/types.ts
|
|
2048
2049
|
var defaultDetectionOptions = {
|
|
@@ -2071,24 +2072,17 @@ import path from "path";
|
|
|
2071
2072
|
async function generateNodeJsConfig(_rootDir, _relativePath) {
|
|
2072
2073
|
return {
|
|
2073
2074
|
include: [
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
"
|
|
2077
|
-
"
|
|
2078
|
-
"
|
|
2079
|
-
"
|
|
2080
|
-
"
|
|
2081
|
-
"
|
|
2082
|
-
"
|
|
2083
|
-
"*.js",
|
|
2084
|
-
"*.mjs",
|
|
2085
|
-
"*.cjs",
|
|
2075
|
+
// Broader patterns to catch all common project structures
|
|
2076
|
+
// (frontend/, src/, lib/, app/, components/, etc.)
|
|
2077
|
+
"**/*.ts",
|
|
2078
|
+
"**/*.tsx",
|
|
2079
|
+
"**/*.js",
|
|
2080
|
+
"**/*.jsx",
|
|
2081
|
+
"**/*.vue",
|
|
2082
|
+
"**/*.mjs",
|
|
2083
|
+
"**/*.cjs",
|
|
2086
2084
|
"**/*.md",
|
|
2087
|
-
"**/*.mdx"
|
|
2088
|
-
"docs/**/*.md",
|
|
2089
|
-
"README.md",
|
|
2090
|
-
"CHANGELOG.md",
|
|
2091
|
-
"CONTRIBUTING.md"
|
|
2085
|
+
"**/*.mdx"
|
|
2092
2086
|
],
|
|
2093
2087
|
exclude: [
|
|
2094
2088
|
"node_modules/**",
|
|
@@ -2097,10 +2091,22 @@ async function generateNodeJsConfig(_rootDir, _relativePath) {
|
|
|
2097
2091
|
"coverage/**",
|
|
2098
2092
|
".next/**",
|
|
2099
2093
|
".nuxt/**",
|
|
2094
|
+
".vite/**",
|
|
2095
|
+
".lien/**",
|
|
2100
2096
|
"out/**",
|
|
2101
2097
|
"*.min.js",
|
|
2102
2098
|
"*.min.css",
|
|
2103
|
-
"*.bundle.js"
|
|
2099
|
+
"*.bundle.js",
|
|
2100
|
+
// Test artifacts (source files are indexed, but not output)
|
|
2101
|
+
"playwright-report/**",
|
|
2102
|
+
"test-results/**",
|
|
2103
|
+
// Build/generated artifacts
|
|
2104
|
+
"__generated__/**",
|
|
2105
|
+
// Common build/cache directories
|
|
2106
|
+
".cache/**",
|
|
2107
|
+
".turbo/**",
|
|
2108
|
+
".vercel/**",
|
|
2109
|
+
".netlify/**"
|
|
2104
2110
|
]
|
|
2105
2111
|
};
|
|
2106
2112
|
}
|
|
@@ -2185,12 +2191,12 @@ async function generateLaravelConfig(_rootDir, _relativePath) {
|
|
|
2185
2191
|
"resources/**/*.php",
|
|
2186
2192
|
"tests/**/*.php",
|
|
2187
2193
|
"*.php",
|
|
2188
|
-
// Frontend assets (Vue/React/Inertia)
|
|
2189
|
-
"
|
|
2190
|
-
"
|
|
2191
|
-
"
|
|
2192
|
-
"
|
|
2193
|
-
"
|
|
2194
|
+
// Frontend assets (Vue/React/Inertia) - Broadened for flexibility
|
|
2195
|
+
"**/*.js",
|
|
2196
|
+
"**/*.ts",
|
|
2197
|
+
"**/*.jsx",
|
|
2198
|
+
"**/*.tsx",
|
|
2199
|
+
"**/*.vue",
|
|
2194
2200
|
// Blade templates
|
|
2195
2201
|
"resources/views/**/*.blade.php",
|
|
2196
2202
|
// Documentation
|
|
@@ -2205,7 +2211,19 @@ async function generateLaravelConfig(_rootDir, _relativePath) {
|
|
|
2205
2211
|
"storage/**",
|
|
2206
2212
|
"bootstrap/cache/**",
|
|
2207
2213
|
"public/**",
|
|
2208
|
-
"node_modules/**"
|
|
2214
|
+
"node_modules/**",
|
|
2215
|
+
"dist/**",
|
|
2216
|
+
"build/**",
|
|
2217
|
+
// Test artifacts (source files are indexed, but not output)
|
|
2218
|
+
"playwright-report/**",
|
|
2219
|
+
"test-results/**",
|
|
2220
|
+
"coverage/**",
|
|
2221
|
+
// Build/generated artifacts
|
|
2222
|
+
"__generated__/**",
|
|
2223
|
+
// Frontend build outputs
|
|
2224
|
+
".vite/**",
|
|
2225
|
+
".nuxt/**",
|
|
2226
|
+
".next/**"
|
|
2209
2227
|
]
|
|
2210
2228
|
};
|
|
2211
2229
|
}
|
|
@@ -2287,10 +2305,142 @@ var laravelDetector = {
|
|
|
2287
2305
|
}
|
|
2288
2306
|
};
|
|
2289
2307
|
|
|
2308
|
+
// src/frameworks/shopify/detector.ts
|
|
2309
|
+
import fs3 from "fs/promises";
|
|
2310
|
+
import path3 from "path";
|
|
2311
|
+
|
|
2312
|
+
// src/frameworks/shopify/config.ts
|
|
2313
|
+
async function generateShopifyConfig(_rootDir, _relativePath) {
|
|
2314
|
+
return {
|
|
2315
|
+
include: [
|
|
2316
|
+
// Core Liquid templates
|
|
2317
|
+
"layout/**/*.liquid",
|
|
2318
|
+
"sections/**/*.liquid",
|
|
2319
|
+
"snippets/**/*.liquid",
|
|
2320
|
+
"templates/**/*.liquid",
|
|
2321
|
+
// Matches any nesting level (e.g., templates/customers/account.liquid)
|
|
2322
|
+
// Theme editor blocks (Online Store 2.0)
|
|
2323
|
+
"blocks/**/*.liquid",
|
|
2324
|
+
// Assets (CSS, JS with optional Liquid templating)
|
|
2325
|
+
"assets/**/*.js",
|
|
2326
|
+
"assets/**/*.js.liquid",
|
|
2327
|
+
"assets/**/*.css",
|
|
2328
|
+
"assets/**/*.css.liquid",
|
|
2329
|
+
"assets/**/*.scss",
|
|
2330
|
+
"assets/**/*.scss.liquid",
|
|
2331
|
+
// Configuration files
|
|
2332
|
+
"config/*.json",
|
|
2333
|
+
// Locales (i18n)
|
|
2334
|
+
"locales/*.json",
|
|
2335
|
+
// Documentation
|
|
2336
|
+
"*.md",
|
|
2337
|
+
"docs/**/*.md",
|
|
2338
|
+
// Shopify-specific config files
|
|
2339
|
+
"shopify.theme.toml",
|
|
2340
|
+
".shopifyignore"
|
|
2341
|
+
],
|
|
2342
|
+
exclude: [
|
|
2343
|
+
"node_modules/**",
|
|
2344
|
+
"dist/**",
|
|
2345
|
+
"build/**",
|
|
2346
|
+
".git/**",
|
|
2347
|
+
// Playwright/testing artifacts
|
|
2348
|
+
"playwright-report/**",
|
|
2349
|
+
"test-results/**",
|
|
2350
|
+
// Build/generated artifacts
|
|
2351
|
+
"__generated__/**",
|
|
2352
|
+
// Common frontend build outputs
|
|
2353
|
+
".vite/**",
|
|
2354
|
+
".nuxt/**",
|
|
2355
|
+
".next/**"
|
|
2356
|
+
]
|
|
2357
|
+
};
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
// src/frameworks/shopify/detector.ts
|
|
2361
|
+
var shopifyDetector = {
|
|
2362
|
+
name: "shopify",
|
|
2363
|
+
priority: 100,
|
|
2364
|
+
// High priority (same as Laravel)
|
|
2365
|
+
async detect(rootDir, relativePath) {
|
|
2366
|
+
const fullPath = path3.join(rootDir, relativePath);
|
|
2367
|
+
const result = {
|
|
2368
|
+
detected: false,
|
|
2369
|
+
name: "shopify",
|
|
2370
|
+
path: relativePath,
|
|
2371
|
+
confidence: "low",
|
|
2372
|
+
evidence: []
|
|
2373
|
+
};
|
|
2374
|
+
const settingsSchemaPath = path3.join(fullPath, "config", "settings_schema.json");
|
|
2375
|
+
let hasSettingsSchema = false;
|
|
2376
|
+
try {
|
|
2377
|
+
await fs3.access(settingsSchemaPath);
|
|
2378
|
+
hasSettingsSchema = true;
|
|
2379
|
+
result.evidence.push("Found config/settings_schema.json");
|
|
2380
|
+
} catch {
|
|
2381
|
+
}
|
|
2382
|
+
const themeLayoutPath = path3.join(fullPath, "layout", "theme.liquid");
|
|
2383
|
+
let hasThemeLayout = false;
|
|
2384
|
+
try {
|
|
2385
|
+
await fs3.access(themeLayoutPath);
|
|
2386
|
+
hasThemeLayout = true;
|
|
2387
|
+
result.evidence.push("Found layout/theme.liquid");
|
|
2388
|
+
} catch {
|
|
2389
|
+
}
|
|
2390
|
+
const shopifyDirs = ["sections", "snippets", "templates", "locales"];
|
|
2391
|
+
let foundDirs = 0;
|
|
2392
|
+
for (const dir of shopifyDirs) {
|
|
2393
|
+
try {
|
|
2394
|
+
const dirPath = path3.join(fullPath, dir);
|
|
2395
|
+
const stats = await fs3.stat(dirPath);
|
|
2396
|
+
if (stats.isDirectory()) {
|
|
2397
|
+
foundDirs++;
|
|
2398
|
+
}
|
|
2399
|
+
} catch {
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
if (foundDirs >= 2) {
|
|
2403
|
+
result.evidence.push(`Shopify directory structure detected (${foundDirs}/${shopifyDirs.length} dirs)`);
|
|
2404
|
+
}
|
|
2405
|
+
try {
|
|
2406
|
+
const tomlPath = path3.join(fullPath, "shopify.theme.toml");
|
|
2407
|
+
await fs3.access(tomlPath);
|
|
2408
|
+
result.evidence.push("Found shopify.theme.toml");
|
|
2409
|
+
} catch {
|
|
2410
|
+
}
|
|
2411
|
+
try {
|
|
2412
|
+
const ignorePath = path3.join(fullPath, ".shopifyignore");
|
|
2413
|
+
await fs3.access(ignorePath);
|
|
2414
|
+
result.evidence.push("Found .shopifyignore");
|
|
2415
|
+
} catch {
|
|
2416
|
+
}
|
|
2417
|
+
if (hasSettingsSchema && foundDirs >= 2) {
|
|
2418
|
+
result.detected = true;
|
|
2419
|
+
result.confidence = "high";
|
|
2420
|
+
return result;
|
|
2421
|
+
}
|
|
2422
|
+
if (hasSettingsSchema || hasThemeLayout && foundDirs >= 1) {
|
|
2423
|
+
result.detected = true;
|
|
2424
|
+
result.confidence = "medium";
|
|
2425
|
+
return result;
|
|
2426
|
+
}
|
|
2427
|
+
if (foundDirs >= 3) {
|
|
2428
|
+
result.detected = true;
|
|
2429
|
+
result.confidence = "medium";
|
|
2430
|
+
return result;
|
|
2431
|
+
}
|
|
2432
|
+
return result;
|
|
2433
|
+
},
|
|
2434
|
+
async generateConfig(rootDir, relativePath) {
|
|
2435
|
+
return generateShopifyConfig(rootDir, relativePath);
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
|
|
2290
2439
|
// src/frameworks/registry.ts
|
|
2291
2440
|
var frameworkDetectors = [
|
|
2292
2441
|
nodejsDetector,
|
|
2293
|
-
laravelDetector
|
|
2442
|
+
laravelDetector,
|
|
2443
|
+
shopifyDetector
|
|
2294
2444
|
];
|
|
2295
2445
|
function getFrameworkDetector(name) {
|
|
2296
2446
|
return frameworkDetectors.find((d) => d.name === name);
|
|
@@ -2306,7 +2456,7 @@ async function detectAllFrameworks(rootDir, options = {}) {
|
|
|
2306
2456
|
return results;
|
|
2307
2457
|
}
|
|
2308
2458
|
async function detectAtPath(rootDir, relativePath, results, visited) {
|
|
2309
|
-
const fullPath =
|
|
2459
|
+
const fullPath = path4.join(rootDir, relativePath);
|
|
2310
2460
|
if (visited.has(fullPath)) {
|
|
2311
2461
|
return;
|
|
2312
2462
|
}
|
|
@@ -2326,25 +2476,56 @@ async function detectAtPath(rootDir, relativePath, results, visited) {
|
|
|
2326
2476
|
}
|
|
2327
2477
|
}
|
|
2328
2478
|
if (detectedAtPath.length > 1) {
|
|
2329
|
-
detectedAtPath.
|
|
2330
|
-
const
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2479
|
+
const highConfidence = detectedAtPath.filter((d) => d.confidence === "high");
|
|
2480
|
+
const mediumConfidence = detectedAtPath.filter((d) => d.confidence === "medium");
|
|
2481
|
+
const lowConfidence = detectedAtPath.filter((d) => d.confidence === "low");
|
|
2482
|
+
if (highConfidence.length > 1) {
|
|
2483
|
+
const cleanResults = highConfidence.map(({ priority, ...result }) => result);
|
|
2484
|
+
results.push(...cleanResults);
|
|
2485
|
+
const names = highConfidence.map((d) => d.name).join(" + ");
|
|
2486
|
+
console.log(` \u2192 Detected hybrid project: ${names}`);
|
|
2487
|
+
if (mediumConfidence.length > 0 || lowConfidence.length > 0) {
|
|
2488
|
+
const skippedNames = [...mediumConfidence, ...lowConfidence].map((d) => d.name).join(", ");
|
|
2489
|
+
console.log(` \u2192 Skipping lower confidence detections: ${skippedNames}`);
|
|
2490
|
+
}
|
|
2491
|
+
} else if (highConfidence.length === 1) {
|
|
2492
|
+
const { priority, ...result } = highConfidence[0];
|
|
2493
|
+
results.push(result);
|
|
2494
|
+
if (mediumConfidence.length > 0 || lowConfidence.length > 0) {
|
|
2495
|
+
const skippedNames = [...mediumConfidence, ...lowConfidence].map((d) => d.name).join(", ");
|
|
2496
|
+
console.log(` \u2192 Skipping lower confidence detections: ${skippedNames}`);
|
|
2497
|
+
}
|
|
2498
|
+
} else if (mediumConfidence.length > 0) {
|
|
2499
|
+
mediumConfidence.sort((a, b) => b.priority - a.priority);
|
|
2500
|
+
const { priority, ...winner } = mediumConfidence[0];
|
|
2501
|
+
results.push(winner);
|
|
2502
|
+
const skipped = [...mediumConfidence.slice(1), ...lowConfidence];
|
|
2503
|
+
if (skipped.length > 0) {
|
|
2504
|
+
const skippedNames = skipped.map((d) => d.name).join(", ");
|
|
2505
|
+
console.log(` \u2192 Skipping ${skippedNames} at ${relativePath} (${winner.name} takes precedence)`);
|
|
2506
|
+
}
|
|
2507
|
+
} else if (lowConfidence.length > 0) {
|
|
2508
|
+
lowConfidence.sort((a, b) => b.priority - a.priority);
|
|
2509
|
+
const { priority, ...winner } = lowConfidence[0];
|
|
2510
|
+
results.push(winner);
|
|
2511
|
+
const skipped = lowConfidence.slice(1);
|
|
2512
|
+
if (skipped.length > 0) {
|
|
2513
|
+
const skippedNames = skipped.map((d) => d.name).join(", ");
|
|
2514
|
+
console.log(` \u2192 Skipping ${skippedNames} at ${relativePath} (${winner.name} takes precedence)`);
|
|
2515
|
+
}
|
|
2336
2516
|
}
|
|
2337
2517
|
} else if (detectedAtPath.length === 1) {
|
|
2338
|
-
|
|
2518
|
+
const { priority, ...result } = detectedAtPath[0];
|
|
2519
|
+
results.push(result);
|
|
2339
2520
|
}
|
|
2340
2521
|
}
|
|
2341
2522
|
async function scanSubdirectories(rootDir, relativePath, results, visited, depth, options) {
|
|
2342
2523
|
if (depth >= options.maxDepth) {
|
|
2343
2524
|
return;
|
|
2344
2525
|
}
|
|
2345
|
-
const fullPath =
|
|
2526
|
+
const fullPath = path4.join(rootDir, relativePath);
|
|
2346
2527
|
try {
|
|
2347
|
-
const entries = await
|
|
2528
|
+
const entries = await fs4.readdir(fullPath, { withFileTypes: true });
|
|
2348
2529
|
const dirs = entries.filter((e) => e.isDirectory());
|
|
2349
2530
|
for (const dir of dirs) {
|
|
2350
2531
|
if (options.skipDirs.includes(dir.name)) {
|
|
@@ -2353,7 +2534,7 @@ async function scanSubdirectories(rootDir, relativePath, results, visited, depth
|
|
|
2353
2534
|
if (dir.name.startsWith(".")) {
|
|
2354
2535
|
continue;
|
|
2355
2536
|
}
|
|
2356
|
-
const subPath = relativePath === "." ? dir.name :
|
|
2537
|
+
const subPath = relativePath === "." ? dir.name : path4.join(relativePath, dir.name);
|
|
2357
2538
|
await detectAtPath(rootDir, subPath, results, visited);
|
|
2358
2539
|
await scanSubdirectories(rootDir, subPath, results, visited, depth + 1, options);
|
|
2359
2540
|
}
|
|
@@ -2364,14 +2545,14 @@ async function scanSubdirectories(rootDir, relativePath, results, visited, depth
|
|
|
2364
2545
|
|
|
2365
2546
|
// src/cli/init.ts
|
|
2366
2547
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
2367
|
-
var __dirname2 =
|
|
2548
|
+
var __dirname2 = path5.dirname(__filename2);
|
|
2368
2549
|
async function initCommand(options = {}) {
|
|
2369
2550
|
const rootDir = options.path || process.cwd();
|
|
2370
|
-
const configPath =
|
|
2551
|
+
const configPath = path5.join(rootDir, ".lien.config.json");
|
|
2371
2552
|
try {
|
|
2372
2553
|
let configExists = false;
|
|
2373
2554
|
try {
|
|
2374
|
-
await
|
|
2555
|
+
await fs5.access(configPath);
|
|
2375
2556
|
configExists = true;
|
|
2376
2557
|
} catch {
|
|
2377
2558
|
}
|
|
@@ -2507,22 +2688,22 @@ async function createNewConfig(rootDir, options) {
|
|
|
2507
2688
|
]);
|
|
2508
2689
|
if (installCursorRules) {
|
|
2509
2690
|
try {
|
|
2510
|
-
const cursorRulesDir =
|
|
2511
|
-
await
|
|
2512
|
-
const templatePath =
|
|
2513
|
-
const rulesPath =
|
|
2691
|
+
const cursorRulesDir = path5.join(rootDir, ".cursor");
|
|
2692
|
+
await fs5.mkdir(cursorRulesDir, { recursive: true });
|
|
2693
|
+
const templatePath = path5.join(__dirname2, "../CURSOR_RULES_TEMPLATE.md");
|
|
2694
|
+
const rulesPath = path5.join(cursorRulesDir, "rules");
|
|
2514
2695
|
let targetPath;
|
|
2515
2696
|
let isDirectory = false;
|
|
2516
2697
|
let isFile = false;
|
|
2517
2698
|
try {
|
|
2518
|
-
const stats = await
|
|
2699
|
+
const stats = await fs5.stat(rulesPath);
|
|
2519
2700
|
isDirectory = stats.isDirectory();
|
|
2520
2701
|
isFile = stats.isFile();
|
|
2521
2702
|
} catch {
|
|
2522
2703
|
}
|
|
2523
2704
|
if (isDirectory) {
|
|
2524
|
-
targetPath =
|
|
2525
|
-
await
|
|
2705
|
+
targetPath = path5.join(rulesPath, "lien.mdc");
|
|
2706
|
+
await fs5.copyFile(templatePath, targetPath);
|
|
2526
2707
|
console.log(chalk2.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
|
|
2527
2708
|
} else if (isFile) {
|
|
2528
2709
|
const { convertToDir } = await inquirer.prompt([
|
|
@@ -2534,11 +2715,11 @@ async function createNewConfig(rootDir, options) {
|
|
|
2534
2715
|
}
|
|
2535
2716
|
]);
|
|
2536
2717
|
if (convertToDir) {
|
|
2537
|
-
const existingRules = await
|
|
2538
|
-
await
|
|
2539
|
-
await
|
|
2540
|
-
await
|
|
2541
|
-
await
|
|
2718
|
+
const existingRules = await fs5.readFile(rulesPath, "utf-8");
|
|
2719
|
+
await fs5.unlink(rulesPath);
|
|
2720
|
+
await fs5.mkdir(rulesPath);
|
|
2721
|
+
await fs5.writeFile(path5.join(rulesPath, "project.mdc"), existingRules);
|
|
2722
|
+
await fs5.copyFile(templatePath, path5.join(rulesPath, "lien.mdc"));
|
|
2542
2723
|
console.log(chalk2.green("\u2713 Converted .cursor/rules to directory"));
|
|
2543
2724
|
console.log(chalk2.green(" - Your project rules: .cursor/rules/project.mdc"));
|
|
2544
2725
|
console.log(chalk2.green(" - Lien rules: .cursor/rules/lien.mdc"));
|
|
@@ -2546,9 +2727,9 @@ async function createNewConfig(rootDir, options) {
|
|
|
2546
2727
|
console.log(chalk2.dim("Skipped Cursor rules installation (preserving existing file)"));
|
|
2547
2728
|
}
|
|
2548
2729
|
} else {
|
|
2549
|
-
await
|
|
2550
|
-
targetPath =
|
|
2551
|
-
await
|
|
2730
|
+
await fs5.mkdir(rulesPath, { recursive: true });
|
|
2731
|
+
targetPath = path5.join(rulesPath, "lien.mdc");
|
|
2732
|
+
await fs5.copyFile(templatePath, targetPath);
|
|
2552
2733
|
console.log(chalk2.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
|
|
2553
2734
|
}
|
|
2554
2735
|
} catch (error) {
|
|
@@ -2562,8 +2743,8 @@ async function createNewConfig(rootDir, options) {
|
|
|
2562
2743
|
...defaultConfig,
|
|
2563
2744
|
frameworks
|
|
2564
2745
|
};
|
|
2565
|
-
const configPath =
|
|
2566
|
-
await
|
|
2746
|
+
const configPath = path5.join(rootDir, ".lien.config.json");
|
|
2747
|
+
await fs5.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2567
2748
|
console.log(chalk2.green("\n\u2713 Created .lien.config.json"));
|
|
2568
2749
|
console.log(chalk2.green(`\u2713 Configured ${frameworks.length} framework(s)`));
|
|
2569
2750
|
console.log(chalk2.dim("\nNext steps:"));
|
|
@@ -2598,8 +2779,8 @@ Customizing ${frameworkName} settings:`));
|
|
|
2598
2779
|
async function upgradeConfig(configPath) {
|
|
2599
2780
|
try {
|
|
2600
2781
|
const backupPath = `${configPath}.backup`;
|
|
2601
|
-
await
|
|
2602
|
-
const existingContent = await
|
|
2782
|
+
await fs5.copyFile(configPath, backupPath);
|
|
2783
|
+
const existingContent = await fs5.readFile(configPath, "utf-8");
|
|
2603
2784
|
const existingConfig = JSON.parse(existingContent);
|
|
2604
2785
|
let upgradedConfig;
|
|
2605
2786
|
let migrated = false;
|
|
@@ -2615,7 +2796,7 @@ async function upgradeConfig(configPath) {
|
|
|
2615
2796
|
newFields.forEach((field) => console.log(chalk2.dim(" \u2022"), chalk2.bold(field)));
|
|
2616
2797
|
}
|
|
2617
2798
|
}
|
|
2618
|
-
await
|
|
2799
|
+
await fs5.writeFile(
|
|
2619
2800
|
configPath,
|
|
2620
2801
|
JSON.stringify(upgradedConfig, null, 2) + "\n",
|
|
2621
2802
|
"utf-8"
|
|
@@ -2634,21 +2815,21 @@ async function upgradeConfig(configPath) {
|
|
|
2634
2815
|
// src/cli/status.ts
|
|
2635
2816
|
init_service();
|
|
2636
2817
|
import chalk3 from "chalk";
|
|
2637
|
-
import
|
|
2638
|
-
import
|
|
2818
|
+
import fs9 from "fs/promises";
|
|
2819
|
+
import path9 from "path";
|
|
2639
2820
|
import os from "os";
|
|
2640
2821
|
import crypto from "crypto";
|
|
2641
2822
|
|
|
2642
2823
|
// src/git/utils.ts
|
|
2643
2824
|
import { exec } from "child_process";
|
|
2644
2825
|
import { promisify } from "util";
|
|
2645
|
-
import
|
|
2646
|
-
import
|
|
2826
|
+
import fs7 from "fs/promises";
|
|
2827
|
+
import path7 from "path";
|
|
2647
2828
|
var execAsync = promisify(exec);
|
|
2648
2829
|
async function isGitRepo(rootDir) {
|
|
2649
2830
|
try {
|
|
2650
|
-
const gitDir =
|
|
2651
|
-
await
|
|
2831
|
+
const gitDir = path7.join(rootDir, ".git");
|
|
2832
|
+
await fs7.access(gitDir);
|
|
2652
2833
|
return true;
|
|
2653
2834
|
} catch {
|
|
2654
2835
|
return false;
|
|
@@ -2687,7 +2868,7 @@ async function getChangedFiles(rootDir, fromRef, toRef) {
|
|
|
2687
2868
|
// 10 second timeout for diffs
|
|
2688
2869
|
}
|
|
2689
2870
|
);
|
|
2690
|
-
const files = stdout.trim().split("\n").filter(Boolean).map((file) =>
|
|
2871
|
+
const files = stdout.trim().split("\n").filter(Boolean).map((file) => path7.join(rootDir, file));
|
|
2691
2872
|
return files;
|
|
2692
2873
|
} catch (error) {
|
|
2693
2874
|
throw new Error(`Failed to get changed files: ${error}`);
|
|
@@ -2702,7 +2883,7 @@ async function getChangedFilesBetweenCommits(rootDir, fromCommit, toCommit) {
|
|
|
2702
2883
|
timeout: 1e4
|
|
2703
2884
|
}
|
|
2704
2885
|
);
|
|
2705
|
-
const files = stdout.trim().split("\n").filter(Boolean).map((file) =>
|
|
2886
|
+
const files = stdout.trim().split("\n").filter(Boolean).map((file) => path7.join(rootDir, file));
|
|
2706
2887
|
return files;
|
|
2707
2888
|
} catch (error) {
|
|
2708
2889
|
throw new Error(`Failed to get changed files between commits: ${error}`);
|
|
@@ -2723,9 +2904,9 @@ init_banner();
|
|
|
2723
2904
|
init_schema();
|
|
2724
2905
|
async function statusCommand() {
|
|
2725
2906
|
const rootDir = process.cwd();
|
|
2726
|
-
const projectName =
|
|
2907
|
+
const projectName = path9.basename(rootDir);
|
|
2727
2908
|
const pathHash = crypto.createHash("md5").update(rootDir).digest("hex").substring(0, 8);
|
|
2728
|
-
const indexPath =
|
|
2909
|
+
const indexPath = path9.join(os.homedir(), ".lien", "indices", `${projectName}-${pathHash}`);
|
|
2729
2910
|
showCompactBanner();
|
|
2730
2911
|
console.log(chalk3.bold("Status\n"));
|
|
2731
2912
|
const hasConfig = await configService.exists(rootDir);
|
|
@@ -2735,11 +2916,11 @@ async function statusCommand() {
|
|
|
2735
2916
|
return;
|
|
2736
2917
|
}
|
|
2737
2918
|
try {
|
|
2738
|
-
const stats = await
|
|
2919
|
+
const stats = await fs9.stat(indexPath);
|
|
2739
2920
|
console.log(chalk3.dim("Index location:"), indexPath);
|
|
2740
2921
|
console.log(chalk3.dim("Index status:"), chalk3.green("\u2713 Exists"));
|
|
2741
2922
|
try {
|
|
2742
|
-
const files = await
|
|
2923
|
+
const files = await fs9.readdir(indexPath, { recursive: true });
|
|
2743
2924
|
console.log(chalk3.dim("Index files:"), files.length);
|
|
2744
2925
|
} catch (e) {
|
|
2745
2926
|
}
|
|
@@ -2768,9 +2949,9 @@ async function statusCommand() {
|
|
|
2768
2949
|
const commit = await getCurrentCommit(rootDir);
|
|
2769
2950
|
console.log(chalk3.dim(" Current branch:"), branch);
|
|
2770
2951
|
console.log(chalk3.dim(" Current commit:"), commit.substring(0, 8));
|
|
2771
|
-
const gitStateFile =
|
|
2952
|
+
const gitStateFile = path9.join(indexPath, ".git-state.json");
|
|
2772
2953
|
try {
|
|
2773
|
-
const gitStateContent = await
|
|
2954
|
+
const gitStateContent = await fs9.readFile(gitStateFile, "utf-8");
|
|
2774
2955
|
const gitState = JSON.parse(gitStateContent);
|
|
2775
2956
|
if (gitState.branch !== branch || gitState.commit !== commit) {
|
|
2776
2957
|
console.log(chalk3.yellow(" \u26A0\uFE0F Git state changed - will reindex on next serve"));
|
|
@@ -2825,6 +3006,8 @@ async function indexCommand(options) {
|
|
|
2825
3006
|
|
|
2826
3007
|
// src/cli/serve.ts
|
|
2827
3008
|
import chalk6 from "chalk";
|
|
3009
|
+
import fs14 from "fs/promises";
|
|
3010
|
+
import path13 from "path";
|
|
2828
3011
|
|
|
2829
3012
|
// src/mcp/server.ts
|
|
2830
3013
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -2920,15 +3103,15 @@ init_lancedb();
|
|
|
2920
3103
|
init_local();
|
|
2921
3104
|
|
|
2922
3105
|
// src/git/tracker.ts
|
|
2923
|
-
import
|
|
2924
|
-
import
|
|
3106
|
+
import fs12 from "fs/promises";
|
|
3107
|
+
import path12 from "path";
|
|
2925
3108
|
var GitStateTracker = class {
|
|
2926
3109
|
stateFile;
|
|
2927
3110
|
rootDir;
|
|
2928
3111
|
currentState = null;
|
|
2929
3112
|
constructor(rootDir, indexPath) {
|
|
2930
3113
|
this.rootDir = rootDir;
|
|
2931
|
-
this.stateFile =
|
|
3114
|
+
this.stateFile = path12.join(indexPath, ".git-state.json");
|
|
2932
3115
|
}
|
|
2933
3116
|
/**
|
|
2934
3117
|
* Loads the last known git state from disk.
|
|
@@ -2936,7 +3119,7 @@ var GitStateTracker = class {
|
|
|
2936
3119
|
*/
|
|
2937
3120
|
async loadState() {
|
|
2938
3121
|
try {
|
|
2939
|
-
const content = await
|
|
3122
|
+
const content = await fs12.readFile(this.stateFile, "utf-8");
|
|
2940
3123
|
return JSON.parse(content);
|
|
2941
3124
|
} catch {
|
|
2942
3125
|
return null;
|
|
@@ -2948,7 +3131,7 @@ var GitStateTracker = class {
|
|
|
2948
3131
|
async saveState(state) {
|
|
2949
3132
|
try {
|
|
2950
3133
|
const content = JSON.stringify(state, null, 2);
|
|
2951
|
-
await
|
|
3134
|
+
await fs12.writeFile(this.stateFile, content, "utf-8");
|
|
2952
3135
|
} catch (error) {
|
|
2953
3136
|
console.error(`[Lien] Warning: Failed to save git state: ${error}`);
|
|
2954
3137
|
}
|
|
@@ -3099,12 +3282,12 @@ var GitStateTracker = class {
|
|
|
3099
3282
|
// src/indexer/incremental.ts
|
|
3100
3283
|
init_chunker();
|
|
3101
3284
|
init_schema();
|
|
3102
|
-
import
|
|
3285
|
+
import fs13 from "fs/promises";
|
|
3103
3286
|
async function indexSingleFile(filepath, vectorDB, embeddings, config, options = {}) {
|
|
3104
3287
|
const { verbose } = options;
|
|
3105
3288
|
try {
|
|
3106
3289
|
try {
|
|
3107
|
-
await
|
|
3290
|
+
await fs13.access(filepath);
|
|
3108
3291
|
} catch {
|
|
3109
3292
|
if (verbose) {
|
|
3110
3293
|
console.error(`[Lien] File deleted: ${filepath}`);
|
|
@@ -3112,7 +3295,7 @@ async function indexSingleFile(filepath, vectorDB, embeddings, config, options =
|
|
|
3112
3295
|
await vectorDB.deleteByFile(filepath);
|
|
3113
3296
|
return;
|
|
3114
3297
|
}
|
|
3115
|
-
const content = await
|
|
3298
|
+
const content = await fs13.readFile(filepath, "utf-8");
|
|
3116
3299
|
const chunkSize = isModernConfig(config) ? config.core.chunkSize : isLegacyConfig(config) ? config.indexing.chunkSize : 75;
|
|
3117
3300
|
const chunkOverlap = isModernConfig(config) ? config.core.chunkOverlap : isLegacyConfig(config) ? config.indexing.chunkOverlap : 10;
|
|
3118
3301
|
const chunks = chunkFile(filepath, content, {
|
|
@@ -3353,14 +3536,6 @@ async function startMCPServer(options) {
|
|
|
3353
3536
|
const versionCheckInterval = setInterval(async () => {
|
|
3354
3537
|
await checkAndReconnect();
|
|
3355
3538
|
}, VERSION_CHECK_INTERVAL_MS);
|
|
3356
|
-
process.on("SIGINT", () => {
|
|
3357
|
-
clearInterval(versionCheckInterval);
|
|
3358
|
-
process.exit(0);
|
|
3359
|
-
});
|
|
3360
|
-
process.on("SIGTERM", () => {
|
|
3361
|
-
clearInterval(versionCheckInterval);
|
|
3362
|
-
process.exit(0);
|
|
3363
|
-
});
|
|
3364
3539
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
3365
3540
|
const { name, arguments: args } = request.params;
|
|
3366
3541
|
try {
|
|
@@ -3619,6 +3794,7 @@ async function startMCPServer(options) {
|
|
|
3619
3794
|
}
|
|
3620
3795
|
const cleanup = async () => {
|
|
3621
3796
|
log("Shutting down MCP server...");
|
|
3797
|
+
clearInterval(versionCheckInterval);
|
|
3622
3798
|
if (gitPollInterval) {
|
|
3623
3799
|
clearInterval(gitPollInterval);
|
|
3624
3800
|
}
|
|
@@ -3630,6 +3806,13 @@ async function startMCPServer(options) {
|
|
|
3630
3806
|
process.on("SIGINT", cleanup);
|
|
3631
3807
|
process.on("SIGTERM", cleanup);
|
|
3632
3808
|
const transport = new StdioServerTransport();
|
|
3809
|
+
transport.onclose = () => {
|
|
3810
|
+
log("Transport closed, parent process likely terminated");
|
|
3811
|
+
cleanup().catch(() => process.exit(0));
|
|
3812
|
+
};
|
|
3813
|
+
transport.onerror = (error) => {
|
|
3814
|
+
log(`Transport error: ${error}`);
|
|
3815
|
+
};
|
|
3633
3816
|
await server.connect(transport);
|
|
3634
3817
|
log("MCP server started and listening on stdio");
|
|
3635
3818
|
}
|
|
@@ -3637,10 +3820,33 @@ async function startMCPServer(options) {
|
|
|
3637
3820
|
// src/cli/serve.ts
|
|
3638
3821
|
init_banner();
|
|
3639
3822
|
async function serveCommand(options) {
|
|
3640
|
-
const rootDir = process.cwd();
|
|
3823
|
+
const rootDir = options.root ? path13.resolve(options.root) : process.cwd();
|
|
3641
3824
|
try {
|
|
3825
|
+
if (options.root) {
|
|
3826
|
+
try {
|
|
3827
|
+
const stats = await fs14.stat(rootDir);
|
|
3828
|
+
if (!stats.isDirectory()) {
|
|
3829
|
+
console.error(chalk6.red(`Error: --root path is not a directory: ${rootDir}`));
|
|
3830
|
+
process.exit(1);
|
|
3831
|
+
}
|
|
3832
|
+
} catch (error) {
|
|
3833
|
+
if (error.code === "ENOENT") {
|
|
3834
|
+
console.error(chalk6.red(`Error: --root directory does not exist: ${rootDir}`));
|
|
3835
|
+
} else if (error.code === "EACCES") {
|
|
3836
|
+
console.error(chalk6.red(`Error: --root directory is not accessible: ${rootDir}`));
|
|
3837
|
+
} else {
|
|
3838
|
+
console.error(chalk6.red(`Error: Failed to access --root directory: ${rootDir}`));
|
|
3839
|
+
console.error(chalk6.dim(error.message));
|
|
3840
|
+
}
|
|
3841
|
+
process.exit(1);
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3642
3844
|
showBanner();
|
|
3643
3845
|
console.error(chalk6.bold("Starting MCP server...\n"));
|
|
3846
|
+
if (options.root) {
|
|
3847
|
+
console.error(chalk6.dim(`Serving from: ${rootDir}
|
|
3848
|
+
`));
|
|
3849
|
+
}
|
|
3644
3850
|
await startMCPServer({
|
|
3645
3851
|
rootDir,
|
|
3646
3852
|
verbose: true,
|
|
@@ -3666,7 +3872,7 @@ var program = new Command();
|
|
|
3666
3872
|
program.name("lien").description("Local semantic code search for AI assistants via MCP").version(packageJson3.version);
|
|
3667
3873
|
program.command("init").description("Initialize Lien in the current directory").option("-u, --upgrade", "Upgrade existing config with new options").option("-y, --yes", "Skip interactive prompts and use defaults").option("-p, --path <path>", "Path to initialize (defaults to current directory)").action(initCommand);
|
|
3668
3874
|
program.command("index").description("Index the codebase for semantic search").option("-w, --watch", "Watch for changes and re-index automatically").option("-v, --verbose", "Show detailed logging during indexing").action(indexCommand);
|
|
3669
|
-
program.command("serve").description("Start the MCP server for Cursor integration").option("-p, --port <port>", "Port number (for future use)", "7133").option("-w, --watch", "Enable file watching for real-time reindexing").action(serveCommand);
|
|
3875
|
+
program.command("serve").description("Start the MCP server for Cursor integration").option("-p, --port <port>", "Port number (for future use)", "7133").option("-w, --watch", "Enable file watching for real-time reindexing").option("-r, --root <path>", "Root directory to serve (defaults to current directory)").action(serveCommand);
|
|
3670
3876
|
program.command("status").description("Show indexing status and statistics").action(statusCommand);
|
|
3671
3877
|
program.command("reindex").description("Clear index and re-index the entire codebase").option("-v, --verbose", "Show detailed logging during indexing").action(async (options) => {
|
|
3672
3878
|
const { showCompactBanner: showCompactBanner2 } = await Promise.resolve().then(() => (init_banner(), banner_exports));
|