@liendev/lien 0.22.0 → 0.24.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 +5 -8
- package/dist/index.js +142 -234
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/CURSOR_RULES_TEMPLATE.md +0 -157
package/README.md
CHANGED
|
@@ -26,15 +26,10 @@ Lien connects AI coding assistants like Cursor to your codebase through the Mode
|
|
|
26
26
|
## Quick Start
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
# Install
|
|
29
|
+
# 1. Install
|
|
30
30
|
npm install -g @liendev/lien
|
|
31
31
|
|
|
32
|
-
#
|
|
33
|
-
cd /path/to/your/project
|
|
34
|
-
lien init
|
|
35
|
-
lien index
|
|
36
|
-
|
|
37
|
-
# Configure Cursor - create .cursor/mcp.json
|
|
32
|
+
# 2. Add to your project - create .cursor/mcp.json
|
|
38
33
|
{
|
|
39
34
|
"mcpServers": {
|
|
40
35
|
"lien": {
|
|
@@ -44,9 +39,11 @@ lien index
|
|
|
44
39
|
}
|
|
45
40
|
}
|
|
46
41
|
|
|
47
|
-
# Restart Cursor and start asking questions!
|
|
42
|
+
# 3. Restart Cursor and start asking questions!
|
|
48
43
|
```
|
|
49
44
|
|
|
45
|
+
That's it—zero configuration needed. Lien auto-detects your project and indexes on first use.
|
|
46
|
+
|
|
50
47
|
**👉 [Full installation guide](https://lien.dev/guide/installation)**
|
|
51
48
|
|
|
52
49
|
### Qdrant Backend (Cross-Repo Search)
|
package/dist/index.js
CHANGED
|
@@ -3591,15 +3591,13 @@ var require_dist = __commonJS({
|
|
|
3591
3591
|
// src/cli/index.ts
|
|
3592
3592
|
import { Command } from "commander";
|
|
3593
3593
|
import { createRequire as createRequire3 } from "module";
|
|
3594
|
-
import { fileURLToPath as
|
|
3594
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3595
3595
|
import { dirname as dirname3, join as join3 } from "path";
|
|
3596
3596
|
|
|
3597
3597
|
// src/cli/init.ts
|
|
3598
3598
|
import fs from "fs/promises";
|
|
3599
3599
|
import path from "path";
|
|
3600
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3601
3600
|
import chalk2 from "chalk";
|
|
3602
|
-
import inquirer from "inquirer";
|
|
3603
3601
|
|
|
3604
3602
|
// src/utils/banner.ts
|
|
3605
3603
|
import figlet from "figlet";
|
|
@@ -3661,8 +3659,6 @@ function showCompactBanner() {
|
|
|
3661
3659
|
}
|
|
3662
3660
|
|
|
3663
3661
|
// src/cli/init.ts
|
|
3664
|
-
var __filename2 = fileURLToPath2(import.meta.url);
|
|
3665
|
-
var __dirname2 = path.dirname(__filename2);
|
|
3666
3662
|
async function initCommand(options = {}) {
|
|
3667
3663
|
showCompactBanner();
|
|
3668
3664
|
console.log(chalk2.bold("\nLien Initialization\n"));
|
|
@@ -3695,129 +3691,6 @@ async function initCommand(options = {}) {
|
|
|
3695
3691
|
console.log(chalk2.dim(" You can safely delete it."));
|
|
3696
3692
|
} catch {
|
|
3697
3693
|
}
|
|
3698
|
-
await promptAndInstallCursorRules(rootDir, options);
|
|
3699
|
-
}
|
|
3700
|
-
async function getPathType(filepath) {
|
|
3701
|
-
try {
|
|
3702
|
-
const stats = await fs.stat(filepath);
|
|
3703
|
-
if (stats.isDirectory()) return "directory";
|
|
3704
|
-
if (stats.isFile()) return "file";
|
|
3705
|
-
return "other";
|
|
3706
|
-
} catch {
|
|
3707
|
-
return "none";
|
|
3708
|
-
}
|
|
3709
|
-
}
|
|
3710
|
-
async function convertRulesFileToDirectory(rulesPath, templatePath) {
|
|
3711
|
-
const existingRules = await fs.readFile(rulesPath, "utf-8");
|
|
3712
|
-
const parentDir = path.dirname(rulesPath);
|
|
3713
|
-
const baseName = path.basename(rulesPath);
|
|
3714
|
-
const tempDir = await fs.mkdtemp(path.join(parentDir, baseName + "_tmp_"));
|
|
3715
|
-
const backupPath = rulesPath + ".backup";
|
|
3716
|
-
try {
|
|
3717
|
-
await fs.writeFile(path.join(tempDir, "project.mdc"), existingRules);
|
|
3718
|
-
await fs.copyFile(templatePath, path.join(tempDir, "lien.mdc"));
|
|
3719
|
-
try {
|
|
3720
|
-
await fs.unlink(backupPath);
|
|
3721
|
-
} catch {
|
|
3722
|
-
}
|
|
3723
|
-
await fs.rename(rulesPath, backupPath);
|
|
3724
|
-
try {
|
|
3725
|
-
await fs.rename(tempDir, rulesPath);
|
|
3726
|
-
try {
|
|
3727
|
-
await fs.unlink(backupPath);
|
|
3728
|
-
} catch {
|
|
3729
|
-
console.log(chalk2.yellow("\u26A0\uFE0F Could not remove backup file, but conversion succeeded"));
|
|
3730
|
-
console.log(chalk2.dim(`Backup file: ${backupPath}`));
|
|
3731
|
-
}
|
|
3732
|
-
} catch (renameErr) {
|
|
3733
|
-
try {
|
|
3734
|
-
await fs.rename(backupPath, rulesPath);
|
|
3735
|
-
} catch (restoreErr) {
|
|
3736
|
-
console.log(chalk2.red("\u274C Failed to restore original .cursor/rules from backup after failed conversion."));
|
|
3737
|
-
console.log(chalk2.red(` - Original error: ${renameErr instanceof Error ? renameErr.message : renameErr}`));
|
|
3738
|
-
console.log(chalk2.red(` - Restore error: ${restoreErr instanceof Error ? restoreErr.message : restoreErr}`));
|
|
3739
|
-
console.log(chalk2.red(` - Backup file location: ${backupPath}`));
|
|
3740
|
-
throw new Error("Failed to convert .cursor/rules to directory and failed to restore from backup. Manual recovery needed.");
|
|
3741
|
-
}
|
|
3742
|
-
throw renameErr;
|
|
3743
|
-
}
|
|
3744
|
-
console.log(chalk2.green("\u2713 Converted .cursor/rules to directory"));
|
|
3745
|
-
console.log(chalk2.green(" - Your project rules: .cursor/rules/project.mdc"));
|
|
3746
|
-
console.log(chalk2.green(" - Lien rules: .cursor/rules/lien.mdc"));
|
|
3747
|
-
} catch (err) {
|
|
3748
|
-
try {
|
|
3749
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
3750
|
-
} catch {
|
|
3751
|
-
}
|
|
3752
|
-
throw err;
|
|
3753
|
-
}
|
|
3754
|
-
}
|
|
3755
|
-
async function handleExistingRulesDirectory(rulesPath, templatePath) {
|
|
3756
|
-
const targetPath = path.join(rulesPath, "lien.mdc");
|
|
3757
|
-
try {
|
|
3758
|
-
await fs.access(targetPath);
|
|
3759
|
-
console.log(chalk2.dim("lien.mdc already exists in .cursor/rules/, skipping..."));
|
|
3760
|
-
return;
|
|
3761
|
-
} catch {
|
|
3762
|
-
}
|
|
3763
|
-
await fs.copyFile(templatePath, targetPath);
|
|
3764
|
-
console.log(chalk2.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
|
|
3765
|
-
}
|
|
3766
|
-
async function handleExistingRulesFile(rulesPath, templatePath, options) {
|
|
3767
|
-
if (options.yes) {
|
|
3768
|
-
console.log(chalk2.dim("Skipped Cursor rules installation (preserving existing .cursor/rules file)"));
|
|
3769
|
-
return;
|
|
3770
|
-
}
|
|
3771
|
-
const { convertToDir } = await inquirer.prompt([{
|
|
3772
|
-
type: "confirm",
|
|
3773
|
-
name: "convertToDir",
|
|
3774
|
-
message: "Existing .cursor/rules file found. Convert to directory and preserve your rules?",
|
|
3775
|
-
default: true
|
|
3776
|
-
}]);
|
|
3777
|
-
if (convertToDir) {
|
|
3778
|
-
await convertRulesFileToDirectory(rulesPath, templatePath);
|
|
3779
|
-
} else {
|
|
3780
|
-
console.log(chalk2.dim("Skipped Cursor rules installation (preserving existing file)"));
|
|
3781
|
-
}
|
|
3782
|
-
}
|
|
3783
|
-
async function handleInvalidRulesPath() {
|
|
3784
|
-
console.log(chalk2.yellow("\u26A0\uFE0F .cursor/rules exists but is not a regular file or directory"));
|
|
3785
|
-
console.log(chalk2.dim("Skipped Cursor rules installation"));
|
|
3786
|
-
}
|
|
3787
|
-
async function handleFreshRulesInstall(rulesPath, templatePath) {
|
|
3788
|
-
await fs.mkdir(rulesPath, { recursive: true });
|
|
3789
|
-
await fs.copyFile(templatePath, path.join(rulesPath, "lien.mdc"));
|
|
3790
|
-
console.log(chalk2.green("\u2713 Installed Cursor rules as .cursor/rules/lien.mdc"));
|
|
3791
|
-
}
|
|
3792
|
-
async function installCursorRulesFiles(rootDir, options) {
|
|
3793
|
-
const cursorRulesDir = path.join(rootDir, ".cursor");
|
|
3794
|
-
await fs.mkdir(cursorRulesDir, { recursive: true });
|
|
3795
|
-
const templatePath = path.join(__dirname2, "../CURSOR_RULES_TEMPLATE.md");
|
|
3796
|
-
const rulesPath = path.join(cursorRulesDir, "rules");
|
|
3797
|
-
const pathType = await getPathType(rulesPath);
|
|
3798
|
-
const handlers = {
|
|
3799
|
-
directory: () => handleExistingRulesDirectory(rulesPath, templatePath),
|
|
3800
|
-
file: () => handleExistingRulesFile(rulesPath, templatePath, options),
|
|
3801
|
-
other: () => handleInvalidRulesPath(),
|
|
3802
|
-
none: () => handleFreshRulesInstall(rulesPath, templatePath)
|
|
3803
|
-
};
|
|
3804
|
-
await handlers[pathType]();
|
|
3805
|
-
}
|
|
3806
|
-
async function promptAndInstallCursorRules(rootDir, options) {
|
|
3807
|
-
const shouldInstall = options.yes || (await inquirer.prompt([{
|
|
3808
|
-
type: "confirm",
|
|
3809
|
-
name: "installCursorRules",
|
|
3810
|
-
message: "Install recommended Cursor rules?",
|
|
3811
|
-
default: true
|
|
3812
|
-
}])).installCursorRules;
|
|
3813
|
-
if (!shouldInstall) return;
|
|
3814
|
-
try {
|
|
3815
|
-
await installCursorRulesFiles(rootDir, options);
|
|
3816
|
-
} catch (error) {
|
|
3817
|
-
console.log(chalk2.yellow("\u26A0\uFE0F Could not install Cursor rules"));
|
|
3818
|
-
console.log(chalk2.dim(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
3819
|
-
console.log(chalk2.dim("You can manually copy CURSOR_RULES_TEMPLATE.md to .cursor/rules/lien.mdc"));
|
|
3820
|
-
}
|
|
3821
3694
|
}
|
|
3822
3695
|
|
|
3823
3696
|
// src/cli/status.ts
|
|
@@ -3984,98 +3857,123 @@ function getModelLoadingMessage() {
|
|
|
3984
3857
|
}
|
|
3985
3858
|
|
|
3986
3859
|
// src/cli/index-cmd.ts
|
|
3860
|
+
async function clearExistingIndex() {
|
|
3861
|
+
const { VectorDB: VectorDB2 } = await import("@liendev/core");
|
|
3862
|
+
const { ManifestManager: ManifestManager2 } = await import("@liendev/core");
|
|
3863
|
+
console.log(chalk4.yellow("Clearing existing index and manifest..."));
|
|
3864
|
+
const vectorDB = new VectorDB2(process.cwd());
|
|
3865
|
+
await vectorDB.initialize();
|
|
3866
|
+
await vectorDB.clear();
|
|
3867
|
+
const manifest = new ManifestManager2(vectorDB.dbPath);
|
|
3868
|
+
await manifest.clear();
|
|
3869
|
+
console.log(chalk4.green("\u2713 Index and manifest cleared\n"));
|
|
3870
|
+
}
|
|
3871
|
+
function createProgressTracker() {
|
|
3872
|
+
return {
|
|
3873
|
+
current: {
|
|
3874
|
+
phase: "initializing",
|
|
3875
|
+
message: "Starting...",
|
|
3876
|
+
filesTotal: 0,
|
|
3877
|
+
filesProcessed: 0
|
|
3878
|
+
},
|
|
3879
|
+
wittyMessage: getIndexingMessage(),
|
|
3880
|
+
lastUpdateTime: 0,
|
|
3881
|
+
updateCount: 0,
|
|
3882
|
+
completedViaProgress: false
|
|
3883
|
+
};
|
|
3884
|
+
}
|
|
3885
|
+
function updateSpinner(spinner, tracker, forceUpdate = false) {
|
|
3886
|
+
const now = Date.now();
|
|
3887
|
+
tracker.updateCount++;
|
|
3888
|
+
const shouldThrottle = tracker.updateCount > 20;
|
|
3889
|
+
const throttleMs = 50;
|
|
3890
|
+
if (!forceUpdate && shouldThrottle && now - tracker.lastUpdateTime < throttleMs) {
|
|
3891
|
+
return;
|
|
3892
|
+
}
|
|
3893
|
+
tracker.lastUpdateTime = now;
|
|
3894
|
+
const { current, wittyMessage } = tracker;
|
|
3895
|
+
const text = current.filesTotal && current.filesProcessed !== void 0 ? `${current.filesProcessed}/${current.filesTotal} files | ${wittyMessage}` : wittyMessage;
|
|
3896
|
+
spinner.text = text;
|
|
3897
|
+
setImmediate(() => spinner.render());
|
|
3898
|
+
}
|
|
3899
|
+
function updateWittyMessage(tracker) {
|
|
3900
|
+
const { current } = tracker;
|
|
3901
|
+
if (current.phase === "embedding" || current.phase === "indexing") {
|
|
3902
|
+
tracker.wittyMessage = getIndexingMessage();
|
|
3903
|
+
} else if (current.phase === "initializing") {
|
|
3904
|
+
tracker.wittyMessage = getModelLoadingMessage();
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
function startMessageRotation(spinner, tracker) {
|
|
3908
|
+
tracker.messageRotationInterval = setInterval(() => {
|
|
3909
|
+
updateWittyMessage(tracker);
|
|
3910
|
+
updateSpinner(spinner, tracker, true);
|
|
3911
|
+
}, 8e3);
|
|
3912
|
+
}
|
|
3913
|
+
function stopMessageRotation(tracker) {
|
|
3914
|
+
if (tracker.messageRotationInterval) {
|
|
3915
|
+
clearInterval(tracker.messageRotationInterval);
|
|
3916
|
+
tracker.messageRotationInterval = void 0;
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
function createProgressCallback(spinner, tracker) {
|
|
3920
|
+
return (progress) => {
|
|
3921
|
+
tracker.current = progress;
|
|
3922
|
+
if (progress.phase === "initializing" && !tracker.messageRotationInterval) {
|
|
3923
|
+
tracker.wittyMessage = getModelLoadingMessage();
|
|
3924
|
+
} else if (progress.phase === "embedding") {
|
|
3925
|
+
tracker.wittyMessage = getEmbeddingMessage();
|
|
3926
|
+
} else if (progress.phase === "indexing") {
|
|
3927
|
+
tracker.wittyMessage = getIndexingMessage();
|
|
3928
|
+
}
|
|
3929
|
+
if (progress.phase === "complete") {
|
|
3930
|
+
tracker.completedViaProgress = true;
|
|
3931
|
+
stopMessageRotation(tracker);
|
|
3932
|
+
let message = progress.message;
|
|
3933
|
+
if (progress.filesTotal && progress.filesProcessed !== void 0) {
|
|
3934
|
+
message = `${message} (${progress.filesProcessed}/${progress.filesTotal})`;
|
|
3935
|
+
}
|
|
3936
|
+
spinner.succeed(chalk4.green(message));
|
|
3937
|
+
} else {
|
|
3938
|
+
updateSpinner(spinner, tracker);
|
|
3939
|
+
}
|
|
3940
|
+
};
|
|
3941
|
+
}
|
|
3942
|
+
function displayFinalResult(spinner, tracker, result) {
|
|
3943
|
+
if (!tracker.completedViaProgress) {
|
|
3944
|
+
if (result.filesIndexed === 0) {
|
|
3945
|
+
spinner.succeed(chalk4.green("Index is up to date - no changes detected"));
|
|
3946
|
+
} else {
|
|
3947
|
+
spinner.succeed(chalk4.green(`Indexed ${result.filesIndexed} files, ${result.chunksCreated} chunks`));
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3987
3951
|
async function indexCommand(options) {
|
|
3988
3952
|
showCompactBanner();
|
|
3989
3953
|
try {
|
|
3990
3954
|
if (options.force) {
|
|
3991
|
-
|
|
3992
|
-
const { ManifestManager: ManifestManager2 } = await import("@liendev/core");
|
|
3993
|
-
console.log(chalk4.yellow("Clearing existing index and manifest..."));
|
|
3994
|
-
const vectorDB = new VectorDB2(process.cwd());
|
|
3995
|
-
await vectorDB.initialize();
|
|
3996
|
-
await vectorDB.clear();
|
|
3997
|
-
const manifest = new ManifestManager2(vectorDB.dbPath);
|
|
3998
|
-
await manifest.clear();
|
|
3999
|
-
console.log(chalk4.green("\u2713 Index and manifest cleared\n"));
|
|
3955
|
+
await clearExistingIndex();
|
|
4000
3956
|
}
|
|
4001
3957
|
const spinner = ora({
|
|
4002
3958
|
text: "Starting indexing...",
|
|
4003
3959
|
interval: 30
|
|
4004
3960
|
// Faster refresh rate for smoother progress
|
|
4005
3961
|
}).start();
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
let messageRotationInterval;
|
|
4009
|
-
let lastUpdateTime = 0;
|
|
4010
|
-
let updateCount = 0;
|
|
4011
|
-
let currentProgress = {
|
|
4012
|
-
phase: "initializing",
|
|
4013
|
-
message: "Starting...",
|
|
4014
|
-
filesTotal: 0,
|
|
4015
|
-
filesProcessed: 0
|
|
4016
|
-
};
|
|
4017
|
-
const updateSpinner = (forceUpdate = false) => {
|
|
4018
|
-
const now = Date.now();
|
|
4019
|
-
updateCount++;
|
|
4020
|
-
const shouldThrottle = updateCount > 20;
|
|
4021
|
-
const throttleMs = 50;
|
|
4022
|
-
if (!forceUpdate && shouldThrottle && now - lastUpdateTime < throttleMs) {
|
|
4023
|
-
return;
|
|
4024
|
-
}
|
|
4025
|
-
lastUpdateTime = now;
|
|
4026
|
-
let text = "";
|
|
4027
|
-
if (currentProgress.filesTotal && currentProgress.filesProcessed !== void 0) {
|
|
4028
|
-
text = `${currentProgress.filesProcessed}/${currentProgress.filesTotal} files | ${wittyMessage}`;
|
|
4029
|
-
} else {
|
|
4030
|
-
text = wittyMessage;
|
|
4031
|
-
}
|
|
4032
|
-
spinner.text = text;
|
|
4033
|
-
setImmediate(() => {
|
|
4034
|
-
spinner.render();
|
|
4035
|
-
});
|
|
4036
|
-
};
|
|
4037
|
-
messageRotationInterval = setInterval(() => {
|
|
4038
|
-
if (currentProgress.phase === "embedding" || currentProgress.phase === "indexing") {
|
|
4039
|
-
wittyMessage = getIndexingMessage();
|
|
4040
|
-
} else if (currentProgress.phase === "initializing") {
|
|
4041
|
-
wittyMessage = getModelLoadingMessage();
|
|
4042
|
-
}
|
|
4043
|
-
updateSpinner(true);
|
|
4044
|
-
}, 8e3);
|
|
3962
|
+
const tracker = createProgressTracker();
|
|
3963
|
+
startMessageRotation(spinner, tracker);
|
|
4045
3964
|
const result = await indexCodebase({
|
|
4046
3965
|
rootDir: process.cwd(),
|
|
4047
3966
|
verbose: options.verbose || false,
|
|
4048
3967
|
force: options.force || false,
|
|
4049
|
-
onProgress: (
|
|
4050
|
-
currentProgress = progress;
|
|
4051
|
-
if (progress.phase === "initializing" && !messageRotationInterval) {
|
|
4052
|
-
wittyMessage = getModelLoadingMessage();
|
|
4053
|
-
} else if (progress.phase === "embedding") {
|
|
4054
|
-
wittyMessage = getEmbeddingMessage();
|
|
4055
|
-
} else if (progress.phase === "indexing") {
|
|
4056
|
-
wittyMessage = getIndexingMessage();
|
|
4057
|
-
}
|
|
4058
|
-
if (progress.phase === "complete") {
|
|
4059
|
-
completedViaProgress = true;
|
|
4060
|
-
if (messageRotationInterval) clearInterval(messageRotationInterval);
|
|
4061
|
-
let message = progress.message;
|
|
4062
|
-
if (progress.filesTotal && progress.filesProcessed !== void 0) {
|
|
4063
|
-
message = `${message} (${progress.filesProcessed}/${progress.filesTotal})`;
|
|
4064
|
-
}
|
|
4065
|
-
spinner.succeed(chalk4.green(message));
|
|
4066
|
-
} else {
|
|
4067
|
-
updateSpinner();
|
|
4068
|
-
}
|
|
4069
|
-
}
|
|
3968
|
+
onProgress: createProgressCallback(spinner, tracker)
|
|
4070
3969
|
});
|
|
4071
|
-
|
|
4072
|
-
if (!
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
spinner.succeed(chalk4.green(`Indexed ${result.filesIndexed} files, ${result.chunksCreated} chunks`));
|
|
4077
|
-
}
|
|
3970
|
+
stopMessageRotation(tracker);
|
|
3971
|
+
if (!result.success && result.error) {
|
|
3972
|
+
spinner.fail(chalk4.red("Indexing failed"));
|
|
3973
|
+
console.error(chalk4.red("\n" + result.error));
|
|
3974
|
+
process.exit(1);
|
|
4078
3975
|
}
|
|
3976
|
+
displayFinalResult(spinner, tracker, result);
|
|
4079
3977
|
if (options.watch) {
|
|
4080
3978
|
console.log(chalk4.yellow("\n\u26A0\uFE0F Watch mode not yet implemented"));
|
|
4081
3979
|
}
|
|
@@ -4094,7 +3992,7 @@ import path4 from "path";
|
|
|
4094
3992
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4095
3993
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4096
3994
|
import { createRequire as createRequire2 } from "module";
|
|
4097
|
-
import { fileURLToPath as
|
|
3995
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4098
3996
|
import { dirname as dirname2, join as join2 } from "path";
|
|
4099
3997
|
import {
|
|
4100
3998
|
LocalEmbeddings,
|
|
@@ -8462,7 +8360,7 @@ var tools = [
|
|
|
8462
8360
|
toMCPToolSchema(
|
|
8463
8361
|
SemanticSearchSchema,
|
|
8464
8362
|
"semantic_search",
|
|
8465
|
-
`Search codebase by MEANING, not text.
|
|
8363
|
+
`Search codebase by MEANING, not text. Complements grep - use this for discovery and understanding, grep for exact matches.
|
|
8466
8364
|
|
|
8467
8365
|
Examples:
|
|
8468
8366
|
- "Where is authentication handled?" \u2192 semantic_search({ query: "handles user authentication" })
|
|
@@ -8913,6 +8811,25 @@ async function handleGetFilesContext(args, ctx) {
|
|
|
8913
8811
|
}
|
|
8914
8812
|
|
|
8915
8813
|
// src/mcp/handlers/list-functions.ts
|
|
8814
|
+
async function performContentScan(vectorDB, args, log) {
|
|
8815
|
+
log("Falling back to content scan...");
|
|
8816
|
+
let results = await vectorDB.scanWithFilter({
|
|
8817
|
+
language: args.language,
|
|
8818
|
+
limit: 200
|
|
8819
|
+
// Fetch more, we'll filter by symbolName
|
|
8820
|
+
});
|
|
8821
|
+
if (args.pattern) {
|
|
8822
|
+
const regex = new RegExp(args.pattern, "i");
|
|
8823
|
+
results = results.filter((r) => {
|
|
8824
|
+
const symbolName = r.metadata?.symbolName;
|
|
8825
|
+
return symbolName && regex.test(symbolName);
|
|
8826
|
+
});
|
|
8827
|
+
}
|
|
8828
|
+
return {
|
|
8829
|
+
results: results.slice(0, 50),
|
|
8830
|
+
method: "content"
|
|
8831
|
+
};
|
|
8832
|
+
}
|
|
8916
8833
|
async function handleListFunctions(args, ctx) {
|
|
8917
8834
|
const { vectorDB, log, checkAndReconnect, getIndexMetadata } = ctx;
|
|
8918
8835
|
return await wrapToolHandler(
|
|
@@ -8920,38 +8837,29 @@ async function handleListFunctions(args, ctx) {
|
|
|
8920
8837
|
async (validatedArgs) => {
|
|
8921
8838
|
log("Listing functions with symbol metadata...");
|
|
8922
8839
|
await checkAndReconnect();
|
|
8923
|
-
let
|
|
8924
|
-
let usedMethod = "symbols";
|
|
8840
|
+
let queryResult;
|
|
8925
8841
|
try {
|
|
8926
|
-
results = await vectorDB.querySymbols({
|
|
8842
|
+
const results = await vectorDB.querySymbols({
|
|
8927
8843
|
language: validatedArgs.language,
|
|
8928
8844
|
pattern: validatedArgs.pattern,
|
|
8929
8845
|
limit: 50
|
|
8930
8846
|
});
|
|
8931
8847
|
if (results.length === 0 && (validatedArgs.language || validatedArgs.pattern)) {
|
|
8932
8848
|
log("No symbol results, falling back to content scan...");
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
limit: 50
|
|
8937
|
-
});
|
|
8938
|
-
usedMethod = "content";
|
|
8849
|
+
queryResult = await performContentScan(vectorDB, validatedArgs, log);
|
|
8850
|
+
} else {
|
|
8851
|
+
queryResult = { results, method: "symbols" };
|
|
8939
8852
|
}
|
|
8940
8853
|
} catch (error) {
|
|
8941
|
-
log(`Symbol query failed
|
|
8942
|
-
|
|
8943
|
-
language: validatedArgs.language,
|
|
8944
|
-
pattern: validatedArgs.pattern,
|
|
8945
|
-
limit: 50
|
|
8946
|
-
});
|
|
8947
|
-
usedMethod = "content";
|
|
8854
|
+
log(`Symbol query failed: ${error}`);
|
|
8855
|
+
queryResult = await performContentScan(vectorDB, validatedArgs, log);
|
|
8948
8856
|
}
|
|
8949
|
-
log(`Found ${results.length} matches using ${
|
|
8857
|
+
log(`Found ${queryResult.results.length} matches using ${queryResult.method} method`);
|
|
8950
8858
|
return {
|
|
8951
8859
|
indexInfo: getIndexMetadata(),
|
|
8952
|
-
method:
|
|
8953
|
-
results,
|
|
8954
|
-
note:
|
|
8860
|
+
method: queryResult.method,
|
|
8861
|
+
results: queryResult.results,
|
|
8862
|
+
note: queryResult.method === "content" ? 'Using content search. Run "lien reindex" to enable faster symbol-based queries.' : void 0
|
|
8955
8863
|
};
|
|
8956
8864
|
}
|
|
8957
8865
|
)(args);
|
|
@@ -9352,14 +9260,14 @@ function registerMCPHandlers(server, toolContext, log) {
|
|
|
9352
9260
|
}
|
|
9353
9261
|
|
|
9354
9262
|
// src/mcp/server.ts
|
|
9355
|
-
var
|
|
9356
|
-
var
|
|
9263
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
9264
|
+
var __dirname2 = dirname2(__filename2);
|
|
9357
9265
|
var require3 = createRequire2(import.meta.url);
|
|
9358
9266
|
var packageJson2;
|
|
9359
9267
|
try {
|
|
9360
|
-
packageJson2 = require3(join2(
|
|
9268
|
+
packageJson2 = require3(join2(__dirname2, "../package.json"));
|
|
9361
9269
|
} catch {
|
|
9362
|
-
packageJson2 = require3(join2(
|
|
9270
|
+
packageJson2 = require3(join2(__dirname2, "../../package.json"));
|
|
9363
9271
|
}
|
|
9364
9272
|
async function initializeDatabase(rootDir, log) {
|
|
9365
9273
|
const embeddings = new LocalEmbeddings();
|
|
@@ -9694,14 +9602,14 @@ async function complexityCommand(options) {
|
|
|
9694
9602
|
}
|
|
9695
9603
|
|
|
9696
9604
|
// src/cli/index.ts
|
|
9697
|
-
var
|
|
9698
|
-
var
|
|
9605
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
9606
|
+
var __dirname3 = dirname3(__filename3);
|
|
9699
9607
|
var require4 = createRequire3(import.meta.url);
|
|
9700
9608
|
var packageJson3;
|
|
9701
9609
|
try {
|
|
9702
|
-
packageJson3 = require4(join3(
|
|
9610
|
+
packageJson3 = require4(join3(__dirname3, "../package.json"));
|
|
9703
9611
|
} catch {
|
|
9704
|
-
packageJson3 = require4(join3(
|
|
9612
|
+
packageJson3 = require4(join3(__dirname3, "../../package.json"));
|
|
9705
9613
|
}
|
|
9706
9614
|
var program = new Command();
|
|
9707
9615
|
program.name("lien").description("Local semantic code search for AI assistants via MCP").version(packageJson3.version);
|