@liendev/lien 0.19.2 → 0.19.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +353 -293
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -994,7 +994,7 @@ async function scanCodebaseWithFrameworks(rootDir, config) {
|
|
|
994
994
|
const frameworkFiles = await scanFramework(rootDir, framework);
|
|
995
995
|
allFiles.push(...frameworkFiles);
|
|
996
996
|
}
|
|
997
|
-
return allFiles;
|
|
997
|
+
return Array.from(new Set(allFiles));
|
|
998
998
|
}
|
|
999
999
|
async function scanFramework(rootDir, framework) {
|
|
1000
1000
|
const frameworkPath = path13.join(rootDir, framework.path);
|
|
@@ -2766,90 +2766,89 @@ function findLiquidBlocks(content) {
|
|
|
2766
2766
|
}
|
|
2767
2767
|
return blocks.sort((a, b) => a.startLine - b.startLine);
|
|
2768
2768
|
}
|
|
2769
|
-
function
|
|
2770
|
-
|
|
2771
|
-
|
|
2769
|
+
function createCodeChunk(content, startLine, endLine, filepath, type, options = {}) {
|
|
2770
|
+
return {
|
|
2771
|
+
content,
|
|
2772
|
+
metadata: {
|
|
2773
|
+
file: filepath,
|
|
2774
|
+
startLine,
|
|
2775
|
+
endLine,
|
|
2776
|
+
language: "liquid",
|
|
2777
|
+
type,
|
|
2778
|
+
symbolName: options.symbolName,
|
|
2779
|
+
symbolType: options.symbolType,
|
|
2780
|
+
imports: options.imports?.length ? options.imports : void 0
|
|
2781
|
+
}
|
|
2782
|
+
};
|
|
2783
|
+
}
|
|
2784
|
+
function splitLargeBlock(block, ctx, symbolName, imports) {
|
|
2772
2785
|
const chunks = [];
|
|
2773
|
-
const
|
|
2774
|
-
const
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
const maxBlockSize = chunkSize * 3;
|
|
2788
|
-
if (blockLineCount <= maxBlockSize) {
|
|
2789
|
-
chunks.push({
|
|
2790
|
-
content: block.content,
|
|
2791
|
-
metadata: {
|
|
2792
|
-
file: filepath,
|
|
2793
|
-
startLine: block.startLine + 1,
|
|
2794
|
-
// 1-indexed
|
|
2795
|
-
endLine: block.endLine + 1,
|
|
2796
|
-
language: "liquid",
|
|
2797
|
-
type: "block",
|
|
2798
|
-
symbolName,
|
|
2799
|
-
symbolType: block.type,
|
|
2800
|
-
imports: imports.length > 0 ? imports : void 0
|
|
2801
|
-
}
|
|
2802
|
-
});
|
|
2803
|
-
} else {
|
|
2804
|
-
const blockLines = block.content.split("\n");
|
|
2805
|
-
for (let offset = 0; offset < blockLines.length; offset += chunkSize - chunkOverlap) {
|
|
2806
|
-
const endOffset = Math.min(offset + chunkSize, blockLines.length);
|
|
2807
|
-
const chunkContent = blockLines.slice(offset, endOffset).join("\n");
|
|
2808
|
-
if (chunkContent.trim().length > 0) {
|
|
2809
|
-
chunks.push({
|
|
2810
|
-
content: chunkContent,
|
|
2811
|
-
metadata: {
|
|
2812
|
-
file: filepath,
|
|
2813
|
-
startLine: block.startLine + offset + 1,
|
|
2814
|
-
// 1-indexed
|
|
2815
|
-
endLine: block.startLine + endOffset,
|
|
2816
|
-
// 1-indexed (endOffset already accounts for exclusivity)
|
|
2817
|
-
language: "liquid",
|
|
2818
|
-
type: "block",
|
|
2819
|
-
symbolName,
|
|
2820
|
-
// Preserve symbol name for all split chunks
|
|
2821
|
-
symbolType: block.type,
|
|
2822
|
-
imports: imports.length > 0 ? imports : void 0
|
|
2823
|
-
}
|
|
2824
|
-
});
|
|
2825
|
-
}
|
|
2826
|
-
if (endOffset >= blockLines.length) break;
|
|
2827
|
-
}
|
|
2786
|
+
const blockLines = block.content.split("\n");
|
|
2787
|
+
const { chunkSize, chunkOverlap, filepath } = ctx.params;
|
|
2788
|
+
for (let offset = 0; offset < blockLines.length; offset += chunkSize - chunkOverlap) {
|
|
2789
|
+
const endOffset = Math.min(offset + chunkSize, blockLines.length);
|
|
2790
|
+
const chunkContent = blockLines.slice(offset, endOffset).join("\n");
|
|
2791
|
+
if (chunkContent.trim().length > 0) {
|
|
2792
|
+
chunks.push(createCodeChunk(
|
|
2793
|
+
chunkContent,
|
|
2794
|
+
block.startLine + offset + 1,
|
|
2795
|
+
block.startLine + endOffset,
|
|
2796
|
+
filepath,
|
|
2797
|
+
"block",
|
|
2798
|
+
{ symbolName, symbolType: block.type, imports }
|
|
2799
|
+
));
|
|
2828
2800
|
}
|
|
2801
|
+
if (endOffset >= blockLines.length) break;
|
|
2829
2802
|
}
|
|
2803
|
+
return chunks;
|
|
2804
|
+
}
|
|
2805
|
+
function processSpecialBlock(block, ctx, coveredLines) {
|
|
2806
|
+
for (let i = block.startLine; i <= block.endLine; i++) {
|
|
2807
|
+
coveredLines.add(i);
|
|
2808
|
+
}
|
|
2809
|
+
const symbolName = block.type === "schema" ? extractSchemaName(block.content) : void 0;
|
|
2810
|
+
const blockContentWithoutComments = ctx.linesWithoutComments.slice(block.startLine, block.endLine + 1).join("\n");
|
|
2811
|
+
const imports = extractRenderTags(blockContentWithoutComments);
|
|
2812
|
+
const blockLineCount = block.endLine - block.startLine + 1;
|
|
2813
|
+
const maxBlockSize = ctx.params.chunkSize * 3;
|
|
2814
|
+
if (blockLineCount <= maxBlockSize) {
|
|
2815
|
+
return [createCodeChunk(
|
|
2816
|
+
block.content,
|
|
2817
|
+
block.startLine + 1,
|
|
2818
|
+
block.endLine + 1,
|
|
2819
|
+
ctx.params.filepath,
|
|
2820
|
+
"block",
|
|
2821
|
+
{ symbolName, symbolType: block.type, imports }
|
|
2822
|
+
)];
|
|
2823
|
+
}
|
|
2824
|
+
return splitLargeBlock(block, ctx, symbolName, imports);
|
|
2825
|
+
}
|
|
2826
|
+
function flushTemplateChunk(currentChunk, chunkStartLine, endLine, ctx) {
|
|
2827
|
+
if (currentChunk.length === 0) return null;
|
|
2828
|
+
const chunkContent = currentChunk.join("\n");
|
|
2829
|
+
if (chunkContent.trim().length === 0) return null;
|
|
2830
|
+
const cleanedChunk = ctx.linesWithoutComments.slice(chunkStartLine, endLine).join("\n");
|
|
2831
|
+
const imports = extractRenderTags(cleanedChunk);
|
|
2832
|
+
return createCodeChunk(
|
|
2833
|
+
chunkContent,
|
|
2834
|
+
chunkStartLine + 1,
|
|
2835
|
+
endLine,
|
|
2836
|
+
ctx.params.filepath,
|
|
2837
|
+
"template",
|
|
2838
|
+
{ imports }
|
|
2839
|
+
);
|
|
2840
|
+
}
|
|
2841
|
+
function processTemplateContent(ctx, coveredLines) {
|
|
2842
|
+
const chunks = [];
|
|
2843
|
+
const { lines, params } = ctx;
|
|
2844
|
+
const { chunkSize, chunkOverlap } = params;
|
|
2830
2845
|
let currentChunk = [];
|
|
2831
2846
|
let chunkStartLine = 0;
|
|
2832
2847
|
for (let i = 0; i < lines.length; i++) {
|
|
2833
2848
|
if (coveredLines.has(i)) {
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
const cleanedChunk = linesWithoutComments.slice(chunkStartLine, i).join("\n");
|
|
2838
|
-
const imports = extractRenderTags(cleanedChunk);
|
|
2839
|
-
chunks.push({
|
|
2840
|
-
content: chunkContent,
|
|
2841
|
-
metadata: {
|
|
2842
|
-
file: filepath,
|
|
2843
|
-
startLine: chunkStartLine + 1,
|
|
2844
|
-
endLine: i,
|
|
2845
|
-
language: "liquid",
|
|
2846
|
-
type: "template",
|
|
2847
|
-
imports: imports.length > 0 ? imports : void 0
|
|
2848
|
-
}
|
|
2849
|
-
});
|
|
2850
|
-
}
|
|
2851
|
-
currentChunk = [];
|
|
2852
|
-
}
|
|
2849
|
+
const chunk = flushTemplateChunk(currentChunk, chunkStartLine, i, ctx);
|
|
2850
|
+
if (chunk) chunks.push(chunk);
|
|
2851
|
+
currentChunk = [];
|
|
2853
2852
|
continue;
|
|
2854
2853
|
}
|
|
2855
2854
|
if (currentChunk.length === 0) {
|
|
@@ -2857,46 +2856,28 @@ function chunkLiquidFile(filepath, content, chunkSize = 75, chunkOverlap = 10) {
|
|
|
2857
2856
|
}
|
|
2858
2857
|
currentChunk.push(lines[i]);
|
|
2859
2858
|
if (currentChunk.length >= chunkSize) {
|
|
2860
|
-
const
|
|
2861
|
-
if (
|
|
2862
|
-
const cleanedChunk = linesWithoutComments.slice(chunkStartLine, i + 1).join("\n");
|
|
2863
|
-
const imports = extractRenderTags(cleanedChunk);
|
|
2864
|
-
chunks.push({
|
|
2865
|
-
content: chunkContent,
|
|
2866
|
-
metadata: {
|
|
2867
|
-
file: filepath,
|
|
2868
|
-
startLine: chunkStartLine + 1,
|
|
2869
|
-
endLine: i + 1,
|
|
2870
|
-
language: "liquid",
|
|
2871
|
-
type: "template",
|
|
2872
|
-
imports: imports.length > 0 ? imports : void 0
|
|
2873
|
-
}
|
|
2874
|
-
});
|
|
2875
|
-
}
|
|
2859
|
+
const chunk = flushTemplateChunk(currentChunk, chunkStartLine, i + 1, ctx);
|
|
2860
|
+
if (chunk) chunks.push(chunk);
|
|
2876
2861
|
currentChunk = currentChunk.slice(-chunkOverlap);
|
|
2877
2862
|
chunkStartLine = Math.max(0, i + 1 - chunkOverlap);
|
|
2878
2863
|
}
|
|
2879
2864
|
}
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
}
|
|
2897
|
-
});
|
|
2898
|
-
}
|
|
2899
|
-
return chunks.sort((a, b) => a.metadata.startLine - b.metadata.startLine);
|
|
2865
|
+
const finalChunk = flushTemplateChunk(currentChunk, chunkStartLine, lines.length, ctx);
|
|
2866
|
+
if (finalChunk) chunks.push(finalChunk);
|
|
2867
|
+
return chunks;
|
|
2868
|
+
}
|
|
2869
|
+
function chunkLiquidFile(filepath, content, chunkSize = 75, chunkOverlap = 10) {
|
|
2870
|
+
const contentWithoutComments = removeComments(content);
|
|
2871
|
+
const ctx = {
|
|
2872
|
+
lines: content.split("\n"),
|
|
2873
|
+
linesWithoutComments: contentWithoutComments.split("\n"),
|
|
2874
|
+
params: { filepath, chunkSize, chunkOverlap }
|
|
2875
|
+
};
|
|
2876
|
+
const blocks = findLiquidBlocks(content);
|
|
2877
|
+
const coveredLines = /* @__PURE__ */ new Set();
|
|
2878
|
+
const blockChunks = blocks.flatMap((block) => processSpecialBlock(block, ctx, coveredLines));
|
|
2879
|
+
const templateChunks = processTemplateContent(ctx, coveredLines);
|
|
2880
|
+
return [...blockChunks, ...templateChunks].sort((a, b) => a.metadata.startLine - b.metadata.startLine);
|
|
2900
2881
|
}
|
|
2901
2882
|
var init_liquid_chunker = __esm({
|
|
2902
2883
|
"src/indexer/liquid-chunker.ts"() {
|
|
@@ -6150,176 +6131,241 @@ async function initCommand(options = {}) {
|
|
|
6150
6131
|
process.exit(1);
|
|
6151
6132
|
}
|
|
6152
6133
|
}
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
]
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6134
|
+
function createGenericFramework() {
|
|
6135
|
+
return {
|
|
6136
|
+
name: "generic",
|
|
6137
|
+
path: ".",
|
|
6138
|
+
enabled: true,
|
|
6139
|
+
config: {
|
|
6140
|
+
include: ["**/*.{ts,tsx,js,jsx,py,php,go,rs,java,c,cpp,cs}"],
|
|
6141
|
+
exclude: [
|
|
6142
|
+
"**/node_modules/**",
|
|
6143
|
+
"**/dist/**",
|
|
6144
|
+
"**/build/**",
|
|
6145
|
+
"**/.git/**",
|
|
6146
|
+
"**/coverage/**",
|
|
6147
|
+
"**/.next/**",
|
|
6148
|
+
"**/.nuxt/**",
|
|
6149
|
+
"**/vendor/**"
|
|
6150
|
+
]
|
|
6151
|
+
}
|
|
6152
|
+
};
|
|
6153
|
+
}
|
|
6154
|
+
async function handleNoFrameworksDetected(options) {
|
|
6155
|
+
console.log(chalk3.yellow("\n\u26A0\uFE0F No frameworks detected"));
|
|
6156
|
+
if (!options.yes) {
|
|
6157
|
+
const { useGeneric } = await inquirer.prompt([{
|
|
6158
|
+
type: "confirm",
|
|
6159
|
+
name: "useGeneric",
|
|
6160
|
+
message: "Create a generic config (index all supported file types)?",
|
|
6161
|
+
default: true
|
|
6162
|
+
}]);
|
|
6163
|
+
if (!useGeneric) {
|
|
6164
|
+
console.log(chalk3.dim("Aborted."));
|
|
6165
|
+
return null;
|
|
6174
6166
|
}
|
|
6175
|
-
frameworks.push({
|
|
6176
|
-
name: "generic",
|
|
6177
|
-
path: ".",
|
|
6178
|
-
enabled: true,
|
|
6179
|
-
config: {
|
|
6180
|
-
include: ["**/*.{ts,tsx,js,jsx,py,php,go,rs,java,c,cpp,cs}"],
|
|
6181
|
-
exclude: [
|
|
6182
|
-
"**/node_modules/**",
|
|
6183
|
-
"**/dist/**",
|
|
6184
|
-
"**/build/**",
|
|
6185
|
-
"**/.git/**",
|
|
6186
|
-
"**/coverage/**",
|
|
6187
|
-
"**/.next/**",
|
|
6188
|
-
"**/.nuxt/**",
|
|
6189
|
-
"**/vendor/**"
|
|
6190
|
-
]
|
|
6191
|
-
}
|
|
6192
|
-
});
|
|
6193
6167
|
} else {
|
|
6194
|
-
console.log(chalk3.
|
|
6168
|
+
console.log(chalk3.dim("Creating generic config (no frameworks detected)..."));
|
|
6169
|
+
}
|
|
6170
|
+
return [createGenericFramework()];
|
|
6171
|
+
}
|
|
6172
|
+
function displayDetectedFrameworks(detections) {
|
|
6173
|
+
console.log(chalk3.green(`
|
|
6195
6174
|
\u2713 Found ${detections.length} framework(s):
|
|
6196
6175
|
`));
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
for (const det of detections) {
|
|
6223
|
-
const detector = getFrameworkDetector(det.name);
|
|
6224
|
-
if (!detector) {
|
|
6225
|
-
console.warn(chalk3.yellow(`\u26A0\uFE0F No detector found for ${det.name}, skipping`));
|
|
6226
|
-
continue;
|
|
6227
|
-
}
|
|
6228
|
-
const frameworkConfig = await detector.generateConfig(rootDir, det.path);
|
|
6229
|
-
let shouldCustomize = false;
|
|
6230
|
-
if (!options.yes) {
|
|
6231
|
-
const { customize } = await inquirer.prompt([
|
|
6232
|
-
{
|
|
6233
|
-
type: "confirm",
|
|
6234
|
-
name: "customize",
|
|
6235
|
-
message: `Customize ${det.name} settings?`,
|
|
6236
|
-
default: false
|
|
6237
|
-
}
|
|
6238
|
-
]);
|
|
6239
|
-
shouldCustomize = customize;
|
|
6240
|
-
}
|
|
6241
|
-
let finalConfig = frameworkConfig;
|
|
6242
|
-
if (shouldCustomize) {
|
|
6243
|
-
const customized = await promptForCustomization(det.name, frameworkConfig);
|
|
6244
|
-
finalConfig = { ...frameworkConfig, ...customized };
|
|
6245
|
-
} else {
|
|
6246
|
-
const pathDisplay = det.path === "." ? "root" : det.path;
|
|
6247
|
-
console.log(chalk3.dim(` \u2192 Using defaults for ${det.name} at ${pathDisplay}`));
|
|
6248
|
-
}
|
|
6249
|
-
frameworks.push({
|
|
6250
|
-
name: det.name,
|
|
6251
|
-
path: det.path,
|
|
6252
|
-
enabled: true,
|
|
6253
|
-
config: finalConfig
|
|
6254
|
-
});
|
|
6255
|
-
}
|
|
6176
|
+
for (const det of detections) {
|
|
6177
|
+
const pathDisplay = det.path === "." ? "root" : det.path;
|
|
6178
|
+
console.log(chalk3.bold(` ${det.name}`), chalk3.dim(`(${det.confidence} confidence)`));
|
|
6179
|
+
console.log(chalk3.dim(` Location: ${pathDisplay}`));
|
|
6180
|
+
if (det.evidence.length > 0) {
|
|
6181
|
+
det.evidence.forEach((e) => console.log(chalk3.dim(` \u2022 ${e}`)));
|
|
6182
|
+
}
|
|
6183
|
+
console.log();
|
|
6184
|
+
}
|
|
6185
|
+
}
|
|
6186
|
+
async function confirmFrameworkConfiguration(options) {
|
|
6187
|
+
if (options.yes) return true;
|
|
6188
|
+
const { confirm } = await inquirer.prompt([{
|
|
6189
|
+
type: "confirm",
|
|
6190
|
+
name: "confirm",
|
|
6191
|
+
message: "Configure these frameworks?",
|
|
6192
|
+
default: true
|
|
6193
|
+
}]);
|
|
6194
|
+
return confirm;
|
|
6195
|
+
}
|
|
6196
|
+
async function generateSingleFrameworkConfig(det, rootDir, options) {
|
|
6197
|
+
const detector = getFrameworkDetector(det.name);
|
|
6198
|
+
if (!detector) {
|
|
6199
|
+
console.warn(chalk3.yellow(`\u26A0\uFE0F No detector found for ${det.name}, skipping`));
|
|
6200
|
+
return null;
|
|
6256
6201
|
}
|
|
6202
|
+
const frameworkConfig = await detector.generateConfig(rootDir, det.path);
|
|
6203
|
+
let finalConfig = frameworkConfig;
|
|
6204
|
+
const pathDisplay = det.path === "." ? "root" : det.path;
|
|
6257
6205
|
if (!options.yes) {
|
|
6258
|
-
const {
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6206
|
+
const { customize } = await inquirer.prompt([{
|
|
6207
|
+
type: "confirm",
|
|
6208
|
+
name: "customize",
|
|
6209
|
+
message: `Customize ${det.name} settings?`,
|
|
6210
|
+
default: false
|
|
6211
|
+
}]);
|
|
6212
|
+
if (customize) {
|
|
6213
|
+
const customized = await promptForCustomization(det.name, frameworkConfig);
|
|
6214
|
+
finalConfig = { ...frameworkConfig, ...customized };
|
|
6215
|
+
} else {
|
|
6216
|
+
console.log(chalk3.dim(` \u2192 Using defaults for ${det.name} at ${pathDisplay}`));
|
|
6217
|
+
}
|
|
6218
|
+
} else {
|
|
6219
|
+
console.log(chalk3.dim(` \u2192 Using defaults for ${det.name} at ${pathDisplay}`));
|
|
6220
|
+
}
|
|
6221
|
+
return {
|
|
6222
|
+
name: det.name,
|
|
6223
|
+
path: det.path,
|
|
6224
|
+
enabled: true,
|
|
6225
|
+
config: finalConfig
|
|
6226
|
+
};
|
|
6227
|
+
}
|
|
6228
|
+
async function handleFrameworksDetected(detections, rootDir, options) {
|
|
6229
|
+
displayDetectedFrameworks(detections);
|
|
6230
|
+
if (!await confirmFrameworkConfiguration(options)) {
|
|
6231
|
+
console.log(chalk3.dim("Aborted."));
|
|
6232
|
+
return null;
|
|
6233
|
+
}
|
|
6234
|
+
const frameworks = [];
|
|
6235
|
+
for (const det of detections) {
|
|
6236
|
+
const framework = await generateSingleFrameworkConfig(det, rootDir, options);
|
|
6237
|
+
if (framework) frameworks.push(framework);
|
|
6238
|
+
}
|
|
6239
|
+
if (frameworks.length === 0) {
|
|
6240
|
+
console.log(chalk3.yellow("\n\u26A0\uFE0F No framework configs could be generated"));
|
|
6241
|
+
return null;
|
|
6242
|
+
}
|
|
6243
|
+
return frameworks;
|
|
6244
|
+
}
|
|
6245
|
+
async function getPathType(filepath) {
|
|
6246
|
+
try {
|
|
6247
|
+
const stats = await fs8.stat(filepath);
|
|
6248
|
+
if (stats.isDirectory()) return "directory";
|
|
6249
|
+
if (stats.isFile()) return "file";
|
|
6250
|
+
return "other";
|
|
6251
|
+
} catch {
|
|
6252
|
+
return "none";
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
async function convertRulesFileToDirectory(rulesPath, templatePath) {
|
|
6256
|
+
const existingRules = await fs8.readFile(rulesPath, "utf-8");
|
|
6257
|
+
const parentDir = path8.dirname(rulesPath);
|
|
6258
|
+
const baseName = path8.basename(rulesPath);
|
|
6259
|
+
const tempDir = await fs8.mkdtemp(path8.join(parentDir, baseName + "_tmp_"));
|
|
6260
|
+
const backupPath = rulesPath + ".backup";
|
|
6261
|
+
try {
|
|
6262
|
+
await fs8.writeFile(path8.join(tempDir, "project.mdc"), existingRules);
|
|
6263
|
+
await fs8.copyFile(templatePath, path8.join(tempDir, "lien.mdc"));
|
|
6264
|
+
try {
|
|
6265
|
+
await fs8.unlink(backupPath);
|
|
6266
|
+
} catch {
|
|
6267
|
+
}
|
|
6268
|
+
await fs8.rename(rulesPath, backupPath);
|
|
6269
|
+
try {
|
|
6270
|
+
await fs8.rename(tempDir, rulesPath);
|
|
6267
6271
|
try {
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
let targetPath;
|
|
6273
|
-
let isDirectory = false;
|
|
6274
|
-
let isFile = false;
|
|
6275
|
-
try {
|
|
6276
|
-
const stats = await fs8.stat(rulesPath);
|
|
6277
|
-
isDirectory = stats.isDirectory();
|
|
6278
|
-
isFile = stats.isFile();
|
|
6279
|
-
} catch {
|
|
6280
|
-
}
|
|
6281
|
-
if (isDirectory) {
|
|
6282
|
-
targetPath = path8.join(rulesPath, "lien.mdc");
|
|
6283
|
-
await fs8.copyFile(templatePath, targetPath);
|
|
6284
|
-
console.log(chalk3.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
|
|
6285
|
-
} else if (isFile) {
|
|
6286
|
-
const { convertToDir } = await inquirer.prompt([
|
|
6287
|
-
{
|
|
6288
|
-
type: "confirm",
|
|
6289
|
-
name: "convertToDir",
|
|
6290
|
-
message: "Existing .cursor/rules file found. Convert to directory and preserve your rules?",
|
|
6291
|
-
default: true
|
|
6292
|
-
}
|
|
6293
|
-
]);
|
|
6294
|
-
if (convertToDir) {
|
|
6295
|
-
const existingRules = await fs8.readFile(rulesPath, "utf-8");
|
|
6296
|
-
await fs8.unlink(rulesPath);
|
|
6297
|
-
await fs8.mkdir(rulesPath);
|
|
6298
|
-
await fs8.writeFile(path8.join(rulesPath, "project.mdc"), existingRules);
|
|
6299
|
-
await fs8.copyFile(templatePath, path8.join(rulesPath, "lien.mdc"));
|
|
6300
|
-
console.log(chalk3.green("\u2713 Converted .cursor/rules to directory"));
|
|
6301
|
-
console.log(chalk3.green(" - Your project rules: .cursor/rules/project.mdc"));
|
|
6302
|
-
console.log(chalk3.green(" - Lien rules: .cursor/rules/lien.mdc"));
|
|
6303
|
-
} else {
|
|
6304
|
-
console.log(chalk3.dim("Skipped Cursor rules installation (preserving existing file)"));
|
|
6305
|
-
}
|
|
6306
|
-
} else {
|
|
6307
|
-
await fs8.mkdir(rulesPath, { recursive: true });
|
|
6308
|
-
targetPath = path8.join(rulesPath, "lien.mdc");
|
|
6309
|
-
await fs8.copyFile(templatePath, targetPath);
|
|
6310
|
-
console.log(chalk3.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
|
|
6311
|
-
}
|
|
6312
|
-
} catch (error) {
|
|
6313
|
-
console.log(chalk3.yellow("\u26A0\uFE0F Could not install Cursor rules"));
|
|
6314
|
-
console.log(chalk3.dim(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
6315
|
-
console.log(chalk3.dim("You can manually copy CURSOR_RULES_TEMPLATE.md to .cursor/rules/lien.mdc"));
|
|
6272
|
+
await fs8.unlink(backupPath);
|
|
6273
|
+
} catch {
|
|
6274
|
+
console.log(chalk3.yellow("\u26A0\uFE0F Could not remove backup file, but conversion succeeded"));
|
|
6275
|
+
console.log(chalk3.dim(`Backup file: ${backupPath}`));
|
|
6316
6276
|
}
|
|
6277
|
+
} catch (renameErr) {
|
|
6278
|
+
try {
|
|
6279
|
+
await fs8.rename(backupPath, rulesPath);
|
|
6280
|
+
} catch (restoreErr) {
|
|
6281
|
+
console.log(chalk3.red("\u274C Failed to restore original .cursor/rules from backup after failed conversion."));
|
|
6282
|
+
console.log(chalk3.red(` - Original error: ${renameErr instanceof Error ? renameErr.message : renameErr}`));
|
|
6283
|
+
console.log(chalk3.red(` - Restore error: ${restoreErr instanceof Error ? restoreErr.message : restoreErr}`));
|
|
6284
|
+
console.log(chalk3.red(` - Backup file location: ${backupPath}`));
|
|
6285
|
+
throw new Error("Failed to convert .cursor/rules to directory and failed to restore from backup. Manual recovery needed.");
|
|
6286
|
+
}
|
|
6287
|
+
throw renameErr;
|
|
6288
|
+
}
|
|
6289
|
+
console.log(chalk3.green("\u2713 Converted .cursor/rules to directory"));
|
|
6290
|
+
console.log(chalk3.green(" - Your project rules: .cursor/rules/project.mdc"));
|
|
6291
|
+
console.log(chalk3.green(" - Lien rules: .cursor/rules/lien.mdc"));
|
|
6292
|
+
} catch (err) {
|
|
6293
|
+
try {
|
|
6294
|
+
await fs8.rm(tempDir, { recursive: true, force: true });
|
|
6295
|
+
} catch {
|
|
6317
6296
|
}
|
|
6297
|
+
throw err;
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
6300
|
+
async function handleExistingRulesDirectory(rulesPath, templatePath) {
|
|
6301
|
+
const targetPath = path8.join(rulesPath, "lien.mdc");
|
|
6302
|
+
try {
|
|
6303
|
+
await fs8.access(targetPath);
|
|
6304
|
+
console.log(chalk3.dim("lien.mdc already exists in .cursor/rules/, skipping..."));
|
|
6305
|
+
return;
|
|
6306
|
+
} catch {
|
|
6307
|
+
}
|
|
6308
|
+
await fs8.copyFile(templatePath, targetPath);
|
|
6309
|
+
console.log(chalk3.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
|
|
6310
|
+
}
|
|
6311
|
+
async function handleExistingRulesFile(rulesPath, templatePath, options) {
|
|
6312
|
+
if (options.yes) {
|
|
6313
|
+
console.log(chalk3.dim("Skipped Cursor rules installation (preserving existing .cursor/rules file)"));
|
|
6314
|
+
return;
|
|
6318
6315
|
}
|
|
6319
|
-
const
|
|
6320
|
-
|
|
6321
|
-
|
|
6316
|
+
const { convertToDir } = await inquirer.prompt([{
|
|
6317
|
+
type: "confirm",
|
|
6318
|
+
name: "convertToDir",
|
|
6319
|
+
message: "Existing .cursor/rules file found. Convert to directory and preserve your rules?",
|
|
6320
|
+
default: true
|
|
6321
|
+
}]);
|
|
6322
|
+
if (convertToDir) {
|
|
6323
|
+
await convertRulesFileToDirectory(rulesPath, templatePath);
|
|
6324
|
+
} else {
|
|
6325
|
+
console.log(chalk3.dim("Skipped Cursor rules installation (preserving existing file)"));
|
|
6326
|
+
}
|
|
6327
|
+
}
|
|
6328
|
+
async function handleInvalidRulesPath() {
|
|
6329
|
+
console.log(chalk3.yellow("\u26A0\uFE0F .cursor/rules exists but is not a regular file or directory"));
|
|
6330
|
+
console.log(chalk3.dim("Skipped Cursor rules installation"));
|
|
6331
|
+
}
|
|
6332
|
+
async function handleFreshRulesInstall(rulesPath, templatePath) {
|
|
6333
|
+
await fs8.mkdir(rulesPath, { recursive: true });
|
|
6334
|
+
await fs8.copyFile(templatePath, path8.join(rulesPath, "lien.mdc"));
|
|
6335
|
+
console.log(chalk3.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
|
|
6336
|
+
}
|
|
6337
|
+
async function installCursorRulesFiles(rootDir, options) {
|
|
6338
|
+
const cursorRulesDir = path8.join(rootDir, ".cursor");
|
|
6339
|
+
await fs8.mkdir(cursorRulesDir, { recursive: true });
|
|
6340
|
+
const templatePath = path8.join(__dirname3, "../CURSOR_RULES_TEMPLATE.md");
|
|
6341
|
+
const rulesPath = path8.join(cursorRulesDir, "rules");
|
|
6342
|
+
const pathType = await getPathType(rulesPath);
|
|
6343
|
+
const handlers = {
|
|
6344
|
+
directory: () => handleExistingRulesDirectory(rulesPath, templatePath),
|
|
6345
|
+
file: () => handleExistingRulesFile(rulesPath, templatePath, options),
|
|
6346
|
+
other: () => handleInvalidRulesPath(),
|
|
6347
|
+
none: () => handleFreshRulesInstall(rulesPath, templatePath)
|
|
6322
6348
|
};
|
|
6349
|
+
await handlers[pathType]();
|
|
6350
|
+
}
|
|
6351
|
+
async function promptAndInstallCursorRules(rootDir, options) {
|
|
6352
|
+
const shouldInstall = options.yes || (await inquirer.prompt([{
|
|
6353
|
+
type: "confirm",
|
|
6354
|
+
name: "installCursorRules",
|
|
6355
|
+
message: "Install recommended Cursor rules?",
|
|
6356
|
+
default: true
|
|
6357
|
+
}])).installCursorRules;
|
|
6358
|
+
if (!shouldInstall) return;
|
|
6359
|
+
try {
|
|
6360
|
+
await installCursorRulesFiles(rootDir, options);
|
|
6361
|
+
} catch (error) {
|
|
6362
|
+
console.log(chalk3.yellow("\u26A0\uFE0F Could not install Cursor rules"));
|
|
6363
|
+
console.log(chalk3.dim(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
6364
|
+
console.log(chalk3.dim("You can manually copy CURSOR_RULES_TEMPLATE.md to .cursor/rules/lien.mdc"));
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
async function writeConfigAndShowSuccess(rootDir, frameworks) {
|
|
6368
|
+
const config = { ...defaultConfig, frameworks };
|
|
6323
6369
|
const configPath = path8.join(rootDir, ".lien.config.json");
|
|
6324
6370
|
await fs8.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
6325
6371
|
console.log(chalk3.green("\n\u2713 Created .lien.config.json"));
|
|
@@ -6329,6 +6375,16 @@ async function createNewConfig(rootDir, options) {
|
|
|
6329
6375
|
console.log(chalk3.dim(" 2. Run"), chalk3.bold("lien serve"), chalk3.dim("to start the MCP server"));
|
|
6330
6376
|
console.log(chalk3.dim(" 3. Configure Cursor to use the MCP server (see README.md)"));
|
|
6331
6377
|
}
|
|
6378
|
+
async function createNewConfig(rootDir, options) {
|
|
6379
|
+
showCompactBanner();
|
|
6380
|
+
console.log(chalk3.bold("Initializing Lien...\n"));
|
|
6381
|
+
console.log(chalk3.dim("\u{1F50D} Detecting frameworks in"), chalk3.bold(rootDir));
|
|
6382
|
+
const detections = await detectAllFrameworks(rootDir);
|
|
6383
|
+
const frameworks = detections.length === 0 ? await handleNoFrameworksDetected(options) : await handleFrameworksDetected(detections, rootDir, options);
|
|
6384
|
+
if (!frameworks) return;
|
|
6385
|
+
await promptAndInstallCursorRules(rootDir, options);
|
|
6386
|
+
await writeConfigAndShowSuccess(rootDir, frameworks);
|
|
6387
|
+
}
|
|
6332
6388
|
async function promptForCustomization(frameworkName, config) {
|
|
6333
6389
|
console.log(chalk3.bold(`
|
|
6334
6390
|
Customizing ${frameworkName} settings:`));
|
|
@@ -6480,7 +6536,7 @@ async function indexCommand(options) {
|
|
|
6480
6536
|
// src/cli/serve.ts
|
|
6481
6537
|
import chalk7 from "chalk";
|
|
6482
6538
|
import fs20 from "fs/promises";
|
|
6483
|
-
import
|
|
6539
|
+
import path22 from "path";
|
|
6484
6540
|
|
|
6485
6541
|
// src/mcp/server.ts
|
|
6486
6542
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -6793,8 +6849,8 @@ async function handleFindSimilar(args, ctx) {
|
|
|
6793
6849
|
}
|
|
6794
6850
|
|
|
6795
6851
|
// src/mcp/utils/path-matching.ts
|
|
6796
|
-
function normalizePath(
|
|
6797
|
-
let normalized =
|
|
6852
|
+
function normalizePath(path24, workspaceRoot) {
|
|
6853
|
+
let normalized = path24.replace(/['"]/g, "").trim().replace(/\\/g, "/");
|
|
6798
6854
|
normalized = normalized.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
6799
6855
|
if (normalized.startsWith(workspaceRoot + "/")) {
|
|
6800
6856
|
normalized = normalized.substring(workspaceRoot.length + 1);
|
|
@@ -6890,10 +6946,10 @@ async function handleGetFilesContext(args, ctx) {
|
|
|
6890
6946
|
log(`Scanned ${SCAN_LIMIT} chunks (limit reached). Test associations may be incomplete for large codebases.`, "warning");
|
|
6891
6947
|
}
|
|
6892
6948
|
const pathCache = /* @__PURE__ */ new Map();
|
|
6893
|
-
const normalizePathCached = (
|
|
6894
|
-
if (pathCache.has(
|
|
6895
|
-
const normalized = normalizePath(
|
|
6896
|
-
pathCache.set(
|
|
6949
|
+
const normalizePathCached = (path24) => {
|
|
6950
|
+
if (pathCache.has(path24)) return pathCache.get(path24);
|
|
6951
|
+
const normalized = normalizePath(path24, workspaceRoot);
|
|
6952
|
+
pathCache.set(path24, normalized);
|
|
6897
6953
|
return normalized;
|
|
6898
6954
|
};
|
|
6899
6955
|
const testAssociationsMap = filepaths.map((filepath) => {
|
|
@@ -7134,9 +7190,9 @@ async function handleGetDependents(args, ctx) {
|
|
|
7134
7190
|
log(`Scanning ${allChunks.length} chunks for imports...`);
|
|
7135
7191
|
const workspaceRoot = process.cwd().replace(/\\/g, "/");
|
|
7136
7192
|
const pathCache = /* @__PURE__ */ new Map();
|
|
7137
|
-
const normalizePathCached = (
|
|
7138
|
-
if (!pathCache.has(
|
|
7139
|
-
return pathCache.get(
|
|
7193
|
+
const normalizePathCached = (path24) => {
|
|
7194
|
+
if (!pathCache.has(path24)) pathCache.set(path24, normalizePath(path24, workspaceRoot));
|
|
7195
|
+
return pathCache.get(path24);
|
|
7140
7196
|
};
|
|
7141
7197
|
const importIndex = buildImportIndex(allChunks, normalizePathCached);
|
|
7142
7198
|
const normalizedTarget = normalizePathCached(validatedArgs.filepath);
|
|
@@ -7202,11 +7258,11 @@ var COMPLEXITY_THRESHOLDS2 = {
|
|
|
7202
7258
|
};
|
|
7203
7259
|
function createPathNormalizer(workspaceRoot) {
|
|
7204
7260
|
const cache = /* @__PURE__ */ new Map();
|
|
7205
|
-
return (
|
|
7206
|
-
const cached = cache.get(
|
|
7261
|
+
return (path24) => {
|
|
7262
|
+
const cached = cache.get(path24);
|
|
7207
7263
|
if (cached !== void 0) return cached;
|
|
7208
|
-
const normalized = normalizePath(
|
|
7209
|
-
cache.set(
|
|
7264
|
+
const normalized = normalizePath(path24, workspaceRoot);
|
|
7265
|
+
cache.set(path24, normalized);
|
|
7210
7266
|
return normalized;
|
|
7211
7267
|
};
|
|
7212
7268
|
}
|
|
@@ -7717,6 +7773,7 @@ init_utils();
|
|
|
7717
7773
|
// src/watcher/index.ts
|
|
7718
7774
|
init_schema();
|
|
7719
7775
|
import chokidar from "chokidar";
|
|
7776
|
+
import path21 from "path";
|
|
7720
7777
|
var FileWatcher = class {
|
|
7721
7778
|
watcher = null;
|
|
7722
7779
|
debounceTimers = /* @__PURE__ */ new Map();
|
|
@@ -7755,9 +7812,12 @@ var FileWatcher = class {
|
|
|
7755
7812
|
persistent: true,
|
|
7756
7813
|
ignoreInitial: true,
|
|
7757
7814
|
// Don't trigger for existing files
|
|
7815
|
+
// Handle atomic saves from modern editors (VS Code, Sublime, etc.)
|
|
7816
|
+
// Editors write to temp file then rename - without this, we get unlink+add instead of change
|
|
7817
|
+
atomic: true,
|
|
7758
7818
|
awaitWriteFinish: {
|
|
7759
|
-
stabilityThreshold:
|
|
7760
|
-
//
|
|
7819
|
+
stabilityThreshold: 300,
|
|
7820
|
+
// Reduced from 500ms for faster detection
|
|
7761
7821
|
pollInterval: 100
|
|
7762
7822
|
},
|
|
7763
7823
|
// Performance optimizations
|
|
@@ -7786,7 +7846,7 @@ var FileWatcher = class {
|
|
|
7786
7846
|
const timer = setTimeout(() => {
|
|
7787
7847
|
this.debounceTimers.delete(filepath);
|
|
7788
7848
|
if (this.onChangeHandler) {
|
|
7789
|
-
const absolutePath =
|
|
7849
|
+
const absolutePath = path21.isAbsolute(filepath) ? filepath : path21.join(this.rootDir, filepath);
|
|
7790
7850
|
try {
|
|
7791
7851
|
const result = this.onChangeHandler({
|
|
7792
7852
|
type,
|
|
@@ -8061,7 +8121,7 @@ async function startMCPServer(options) {
|
|
|
8061
8121
|
|
|
8062
8122
|
// src/cli/serve.ts
|
|
8063
8123
|
async function serveCommand(options) {
|
|
8064
|
-
const rootDir = options.root ?
|
|
8124
|
+
const rootDir = options.root ? path22.resolve(options.root) : process.cwd();
|
|
8065
8125
|
try {
|
|
8066
8126
|
if (options.root) {
|
|
8067
8127
|
try {
|
|
@@ -8109,7 +8169,7 @@ init_lancedb();
|
|
|
8109
8169
|
init_service();
|
|
8110
8170
|
import chalk9 from "chalk";
|
|
8111
8171
|
import fs21 from "fs";
|
|
8112
|
-
import
|
|
8172
|
+
import path23 from "path";
|
|
8113
8173
|
|
|
8114
8174
|
// src/insights/formatters/text.ts
|
|
8115
8175
|
import chalk8 from "chalk";
|
|
@@ -8384,7 +8444,7 @@ function validateFormat(format) {
|
|
|
8384
8444
|
function validateFilesExist(files, rootDir) {
|
|
8385
8445
|
if (!files || files.length === 0) return;
|
|
8386
8446
|
const missingFiles = files.filter((file) => {
|
|
8387
|
-
const fullPath =
|
|
8447
|
+
const fullPath = path23.isAbsolute(file) ? file : path23.join(rootDir, file);
|
|
8388
8448
|
return !fs21.existsSync(fullPath);
|
|
8389
8449
|
});
|
|
8390
8450
|
if (missingFiles.length > 0) {
|