@liendev/lien 0.19.3 → 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 +329 -273
- 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;
|
|
6318
6298
|
}
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
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;
|
|
6315
|
+
}
|
|
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:`));
|