lens-engine 2.0.1 → 2.1.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/cli.js +298 -39
- package/daemon.js +430 -70
- package/dashboard/assets/{index-x1mtAEvB.js → index-CzRbHqXG.js} +28 -28
- package/dashboard/index.html +1 -1
- package/drizzle-engine/0002_csharp_namespaces.sql +1 -0
- package/drizzle-engine/meta/0002_snapshot.json +586 -0
- package/drizzle-engine/meta/_journal.json +7 -0
- package/package.json +4 -2
package/cli.js
CHANGED
|
@@ -368,21 +368,211 @@ async function runMain(cmd, opts = {}) {
|
|
|
368
368
|
|
|
369
369
|
// packages/cli/src/commands/daemon.ts
|
|
370
370
|
import { spawn } from "child_process";
|
|
371
|
-
import { existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
371
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
372
|
+
import { createRequire as createRequire2 } from "module";
|
|
373
|
+
import { homedir as homedir2 } from "os";
|
|
374
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
375
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
376
|
+
|
|
377
|
+
// packages/cli/src/commands/daemon-install.ts
|
|
378
|
+
import { existsSync as existsSync2 } from "fs";
|
|
372
379
|
import { createRequire } from "module";
|
|
373
|
-
import {
|
|
374
|
-
import { dirname, join } from "path";
|
|
380
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
375
381
|
import { fileURLToPath } from "url";
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
382
|
+
|
|
383
|
+
// packages/cli/src/lib/launchd.ts
|
|
384
|
+
import { execFileSync } from "child_process";
|
|
385
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
386
|
+
import { homedir, platform } from "os";
|
|
387
|
+
import { dirname, join } from "path";
|
|
388
|
+
var PLIST_LABEL = "com.lens.daemon";
|
|
389
|
+
var PLIST_PATH = join(homedir(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
390
|
+
var LOG_PATH = join(homedir(), ".lens", "daemon.log");
|
|
391
|
+
function isMacOS() {
|
|
392
|
+
return platform() === "darwin";
|
|
393
|
+
}
|
|
394
|
+
function guiDomain() {
|
|
395
|
+
const uid = process.getuid?.();
|
|
396
|
+
if (typeof uid !== "number") {
|
|
397
|
+
throw new Error("process.getuid() unavailable \u2014 macOS required for launchd commands");
|
|
398
|
+
}
|
|
399
|
+
return `gui/${uid}`;
|
|
400
|
+
}
|
|
401
|
+
function renderPlist({ nodePath, daemonEntry, extraEnv = {} }) {
|
|
402
|
+
const env = {
|
|
403
|
+
PATH: `${dirname(nodePath)}:/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin`,
|
|
404
|
+
HOME: homedir(),
|
|
405
|
+
LENS_DAEMON: "1",
|
|
406
|
+
...extraEnv
|
|
407
|
+
};
|
|
408
|
+
const envEntries = Object.entries(env).map(([k, v]) => ` <key>${escapeXml(k)}</key>
|
|
409
|
+
<string>${escapeXml(v)}</string>`).join("\n");
|
|
410
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
411
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
412
|
+
<plist version="1.0">
|
|
413
|
+
<dict>
|
|
414
|
+
<key>Label</key>
|
|
415
|
+
<string>${PLIST_LABEL}</string>
|
|
416
|
+
<key>ProgramArguments</key>
|
|
417
|
+
<array>
|
|
418
|
+
<string>${escapeXml(nodePath)}</string>
|
|
419
|
+
<string>${escapeXml(daemonEntry)}</string>
|
|
420
|
+
</array>
|
|
421
|
+
<key>RunAtLoad</key>
|
|
422
|
+
<true/>
|
|
423
|
+
<key>KeepAlive</key>
|
|
424
|
+
<true/>
|
|
425
|
+
<key>StandardOutPath</key>
|
|
426
|
+
<string>${escapeXml(LOG_PATH)}</string>
|
|
427
|
+
<key>StandardErrorPath</key>
|
|
428
|
+
<string>${escapeXml(LOG_PATH)}</string>
|
|
429
|
+
<key>EnvironmentVariables</key>
|
|
430
|
+
<dict>
|
|
431
|
+
${envEntries}
|
|
432
|
+
</dict>
|
|
433
|
+
</dict>
|
|
434
|
+
</plist>
|
|
435
|
+
`;
|
|
436
|
+
}
|
|
437
|
+
function escapeXml(s) {
|
|
438
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
439
|
+
}
|
|
440
|
+
function writePlist(contents) {
|
|
441
|
+
mkdirSync(dirname(PLIST_PATH), { recursive: true });
|
|
442
|
+
writeFileSync(PLIST_PATH, contents);
|
|
443
|
+
}
|
|
444
|
+
function bootstrapPlist() {
|
|
445
|
+
const domain = guiDomain();
|
|
446
|
+
try {
|
|
447
|
+
execFileSync("launchctl", ["bootout", domain, PLIST_PATH], { stdio: "ignore" });
|
|
448
|
+
} catch {
|
|
449
|
+
}
|
|
450
|
+
try {
|
|
451
|
+
execFileSync("launchctl", ["bootstrap", domain, PLIST_PATH], { stdio: "inherit" });
|
|
452
|
+
} catch (e) {
|
|
453
|
+
throw new Error(
|
|
454
|
+
`launchctl bootstrap failed. Check Console.app (filter: com.lens.daemon) for details.
|
|
455
|
+
${e.message}`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function unloadPlist() {
|
|
460
|
+
const domain = guiDomain();
|
|
461
|
+
try {
|
|
462
|
+
execFileSync("launchctl", ["bootout", domain, PLIST_PATH], { stdio: "ignore" });
|
|
463
|
+
} catch {
|
|
464
|
+
}
|
|
465
|
+
if (existsSync(PLIST_PATH)) unlinkSync(PLIST_PATH);
|
|
466
|
+
}
|
|
467
|
+
function plistStatus() {
|
|
468
|
+
const installed = existsSync(PLIST_PATH);
|
|
469
|
+
let loaded = false;
|
|
470
|
+
let pid = null;
|
|
471
|
+
let lastExit = null;
|
|
472
|
+
try {
|
|
473
|
+
const out = execFileSync("launchctl", ["list"], { encoding: "utf-8" });
|
|
474
|
+
for (const line of out.split("\n")) {
|
|
475
|
+
const parts = line.split(/\s+/);
|
|
476
|
+
if (parts[2] === PLIST_LABEL) {
|
|
477
|
+
loaded = true;
|
|
478
|
+
const pidParsed = parseInt(parts[0], 10);
|
|
479
|
+
const exitParsed = parseInt(parts[1], 10);
|
|
480
|
+
pid = Number.isNaN(pidParsed) ? null : pidParsed;
|
|
481
|
+
lastExit = Number.isNaN(exitParsed) ? null : exitParsed;
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
} catch {
|
|
486
|
+
}
|
|
487
|
+
return { installed, loaded, pid, lastExit };
|
|
488
|
+
}
|
|
489
|
+
function readPlist() {
|
|
490
|
+
try {
|
|
491
|
+
return readFileSync(PLIST_PATH, "utf-8");
|
|
492
|
+
} catch {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// packages/cli/src/commands/daemon-install.ts
|
|
379
498
|
function findDaemonEntry() {
|
|
380
499
|
const self = fileURLToPath(import.meta.url);
|
|
381
|
-
const sibling =
|
|
382
|
-
if (
|
|
500
|
+
const sibling = join2(dirname2(self), "daemon.js");
|
|
501
|
+
if (existsSync2(sibling)) return sibling;
|
|
383
502
|
const req = createRequire(import.meta.url);
|
|
384
503
|
return req.resolve("@lens/daemon");
|
|
385
504
|
}
|
|
505
|
+
var install = defineCommand({
|
|
506
|
+
meta: {
|
|
507
|
+
description: "Install LENS daemon as a launchd service (auto-start at login, auto-restart on crash/sleep)."
|
|
508
|
+
},
|
|
509
|
+
async run() {
|
|
510
|
+
if (!isMacOS()) {
|
|
511
|
+
console.error("`lens daemon install` is macOS-only. On Linux use systemd; on Windows use Task Scheduler.");
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
const daemonEntry = findDaemonEntry();
|
|
515
|
+
const nodePath = process.execPath;
|
|
516
|
+
const plist = renderPlist({ nodePath, daemonEntry });
|
|
517
|
+
const existing = readPlist();
|
|
518
|
+
if (existing === plist) {
|
|
519
|
+
console.log(`Plist already up to date: ${PLIST_PATH}`);
|
|
520
|
+
} else {
|
|
521
|
+
writePlist(plist);
|
|
522
|
+
console.log(`Wrote plist: ${PLIST_PATH}`);
|
|
523
|
+
}
|
|
524
|
+
bootstrapPlist();
|
|
525
|
+
const status2 = plistStatus();
|
|
526
|
+
if (status2.loaded) {
|
|
527
|
+
console.log(`Loaded (PID ${status2.pid ?? "starting"}).`);
|
|
528
|
+
console.log("RunAtLoad=true, KeepAlive=true.");
|
|
529
|
+
console.log("Daemon will survive logout, reboot, sleep/wake, and crashes.");
|
|
530
|
+
} else {
|
|
531
|
+
console.error("launchctl bootstrap reported success but service not visible. Check Console.app for errors.");
|
|
532
|
+
process.exit(1);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
var uninstall = defineCommand({
|
|
537
|
+
meta: { description: "Remove the LENS launchd service." },
|
|
538
|
+
async run() {
|
|
539
|
+
if (!isMacOS()) {
|
|
540
|
+
console.error("`lens daemon uninstall` is macOS-only.");
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
unloadPlist();
|
|
544
|
+
console.log("LENS daemon launchd service removed.");
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
var statusCmd = defineCommand({
|
|
548
|
+
meta: { description: "Show LENS daemon launchd status." },
|
|
549
|
+
async run() {
|
|
550
|
+
if (!isMacOS()) {
|
|
551
|
+
console.error("`lens daemon status` shows launchd state (macOS-only).");
|
|
552
|
+
process.exit(1);
|
|
553
|
+
}
|
|
554
|
+
const s = plistStatus();
|
|
555
|
+
console.log(`plist: ${s.installed ? PLIST_PATH : "(not installed)"}`);
|
|
556
|
+
console.log(`loaded: ${s.loaded}`);
|
|
557
|
+
console.log(`pid: ${s.pid ?? "(none)"}`);
|
|
558
|
+
console.log(`lastExit: ${s.lastExit ?? "(none)"}`);
|
|
559
|
+
if (s.installed && !s.loaded) {
|
|
560
|
+
console.log("\nHint: run `lens daemon install` to load it.");
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
// packages/cli/src/commands/daemon.ts
|
|
566
|
+
var DATA_DIR = join3(homedir2(), ".lens");
|
|
567
|
+
var PID_FILE = join3(DATA_DIR, "daemon.pid");
|
|
568
|
+
var LOG_FILE = join3(DATA_DIR, "daemon.log");
|
|
569
|
+
function findDaemonEntry2() {
|
|
570
|
+
const self = fileURLToPath2(import.meta.url);
|
|
571
|
+
const sibling = join3(dirname3(self), "daemon.js");
|
|
572
|
+
if (existsSync3(sibling)) return sibling;
|
|
573
|
+
const req = createRequire2(import.meta.url);
|
|
574
|
+
return req.resolve("@lens/daemon");
|
|
575
|
+
}
|
|
386
576
|
function isRunning(pid) {
|
|
387
577
|
try {
|
|
388
578
|
process.kill(pid, 0);
|
|
@@ -393,7 +583,7 @@ function isRunning(pid) {
|
|
|
393
583
|
}
|
|
394
584
|
function readPid() {
|
|
395
585
|
try {
|
|
396
|
-
const pid = parseInt(
|
|
586
|
+
const pid = parseInt(readFileSync2(PID_FILE, "utf-8").trim(), 10);
|
|
397
587
|
return Number.isNaN(pid) ? null : isRunning(pid) ? pid : null;
|
|
398
588
|
} catch {
|
|
399
589
|
return null;
|
|
@@ -414,17 +604,17 @@ var start = defineCommand({
|
|
|
414
604
|
console.log(`Daemon already running (PID ${existing})`);
|
|
415
605
|
return;
|
|
416
606
|
}
|
|
417
|
-
|
|
418
|
-
const daemonEntry =
|
|
607
|
+
mkdirSync2(DATA_DIR, { recursive: true });
|
|
608
|
+
const daemonEntry = findDaemonEntry2();
|
|
419
609
|
if (args.foreground) {
|
|
420
610
|
const child2 = spawn("node", [daemonEntry], {
|
|
421
611
|
stdio: "inherit",
|
|
422
612
|
env: { ...process.env, LENS_DATA_DIR: DATA_DIR }
|
|
423
613
|
});
|
|
424
|
-
|
|
614
|
+
writeFileSync2(PID_FILE, String(child2.pid));
|
|
425
615
|
child2.on("exit", (code) => {
|
|
426
616
|
try {
|
|
427
|
-
|
|
617
|
+
unlinkSync2(PID_FILE);
|
|
428
618
|
} catch {
|
|
429
619
|
}
|
|
430
620
|
process.exit(code ?? 1);
|
|
@@ -438,7 +628,7 @@ var start = defineCommand({
|
|
|
438
628
|
env: { ...process.env, LENS_DATA_DIR: DATA_DIR }
|
|
439
629
|
});
|
|
440
630
|
child.unref();
|
|
441
|
-
|
|
631
|
+
writeFileSync2(PID_FILE, String(child.pid));
|
|
442
632
|
const ok = await waitForHealth(1500);
|
|
443
633
|
if (ok) {
|
|
444
634
|
console.log(`Daemon started (PID ${child.pid})`);
|
|
@@ -473,7 +663,7 @@ var stop = defineCommand({
|
|
|
473
663
|
}
|
|
474
664
|
process.kill(pid, "SIGTERM");
|
|
475
665
|
try {
|
|
476
|
-
|
|
666
|
+
unlinkSync2(PID_FILE);
|
|
477
667
|
} catch {
|
|
478
668
|
}
|
|
479
669
|
console.log(`Daemon stopped (PID ${pid})`);
|
|
@@ -481,7 +671,7 @@ var stop = defineCommand({
|
|
|
481
671
|
});
|
|
482
672
|
var daemon = defineCommand({
|
|
483
673
|
meta: { description: "Manage the LENS daemon process." },
|
|
484
|
-
subCommands: { start, stop }
|
|
674
|
+
subCommands: { start, stop, install, uninstall, status: statusCmd }
|
|
485
675
|
});
|
|
486
676
|
|
|
487
677
|
// packages/cli/src/commands/dashboard.ts
|
|
@@ -727,12 +917,66 @@ var list = defineCommand({
|
|
|
727
917
|
}
|
|
728
918
|
});
|
|
729
919
|
|
|
920
|
+
// packages/cli/src/commands/pattern.ts
|
|
921
|
+
var pattern = defineCommand({
|
|
922
|
+
meta: {
|
|
923
|
+
description: "Structural AST search via ast-grep pattern."
|
|
924
|
+
},
|
|
925
|
+
args: {
|
|
926
|
+
pattern: {
|
|
927
|
+
type: "positional",
|
|
928
|
+
required: true,
|
|
929
|
+
description: 'ast-grep pattern, e.g. "function $N($$$) { $$$ }"'
|
|
930
|
+
},
|
|
931
|
+
lang: {
|
|
932
|
+
type: "string",
|
|
933
|
+
alias: "l",
|
|
934
|
+
default: "typescript",
|
|
935
|
+
description: "Source language: typescript | tsx | javascript | csharp"
|
|
936
|
+
},
|
|
937
|
+
repo: {
|
|
938
|
+
type: "string",
|
|
939
|
+
alias: "r",
|
|
940
|
+
description: "Repo root path (defaults to cwd)"
|
|
941
|
+
},
|
|
942
|
+
limit: {
|
|
943
|
+
type: "string",
|
|
944
|
+
default: "50",
|
|
945
|
+
description: "Max matches"
|
|
946
|
+
},
|
|
947
|
+
json: {
|
|
948
|
+
type: "boolean",
|
|
949
|
+
description: "Output full JSON instead of compact text"
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
async run({ args }) {
|
|
953
|
+
const repoPath = args.repo ?? process.cwd();
|
|
954
|
+
const format = args.json ? "json" : "text";
|
|
955
|
+
const res = await daemonFetch("/pattern", {
|
|
956
|
+
method: "POST",
|
|
957
|
+
headers: { "Content-Type": "application/json" },
|
|
958
|
+
body: JSON.stringify({
|
|
959
|
+
repoPath,
|
|
960
|
+
pattern: args.pattern,
|
|
961
|
+
language: args.lang,
|
|
962
|
+
limit: parseInt(args.limit, 10),
|
|
963
|
+
format
|
|
964
|
+
})
|
|
965
|
+
});
|
|
966
|
+
if (format === "json") {
|
|
967
|
+
console.log(JSON.stringify(await res.json(), null, 2));
|
|
968
|
+
} else {
|
|
969
|
+
process.stdout.write(await res.text());
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
|
|
730
974
|
// packages/cli/src/commands/register.ts
|
|
731
975
|
import { resolve } from "path";
|
|
732
976
|
|
|
733
977
|
// packages/cli/src/lib/inject-instructions.ts
|
|
734
|
-
import { existsSync as
|
|
735
|
-
import { dirname as
|
|
978
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
979
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
736
980
|
var MARKER = "<!-- lens-context -->";
|
|
737
981
|
var CANDIDATE_FILES = [
|
|
738
982
|
"CLAUDE.md",
|
|
@@ -750,6 +994,7 @@ This repo is indexed by LENS. **Prefer LENS MCP tools over built-in Grep/Glob**
|
|
|
750
994
|
- \`lens_grep\` \u2014 Ranked search with import graph, co-change, hub scores. One call replaces multiple Grep+Read cycles.
|
|
751
995
|
- \`lens_graph\` \u2014 Dependency map. Architecture, module relationships, change impact.
|
|
752
996
|
- \`lens_graph_neighbors\` \u2014 Blast radius for a file. Importers, co-change partners, what breaks.
|
|
997
|
+
- \`lens_pattern\` \u2014 Structural AST search via ast-grep. Pass a pattern with \`$NAME\` (single capture) or \`$$$\` (any sequence). Language: typescript | tsx | javascript | csharp. Use AFTER \`lens_grep\` when you know the exact AST shape. Example: \`class $C : IController { $$$ }\`.
|
|
753
998
|
|
|
754
999
|
Load tools via \`ToolSearch\` before first use.
|
|
755
1000
|
`;
|
|
@@ -757,14 +1002,14 @@ function injectInstructions(repoRoot) {
|
|
|
757
1002
|
const results = [];
|
|
758
1003
|
const found = [];
|
|
759
1004
|
for (const file of CANDIDATE_FILES) {
|
|
760
|
-
const fullPath =
|
|
761
|
-
if (
|
|
1005
|
+
const fullPath = join4(repoRoot, file);
|
|
1006
|
+
if (existsSync4(fullPath)) {
|
|
762
1007
|
found.push(file);
|
|
763
|
-
const content =
|
|
1008
|
+
const content = readFileSync3(fullPath, "utf-8");
|
|
764
1009
|
if (content.includes(MARKER)) {
|
|
765
1010
|
results.push({ file, action: "skipped" });
|
|
766
1011
|
} else {
|
|
767
|
-
|
|
1012
|
+
writeFileSync3(fullPath, `${TEMPLATE}
|
|
768
1013
|
${content}`);
|
|
769
1014
|
results.push({ file, action: "injected" });
|
|
770
1015
|
}
|
|
@@ -772,10 +1017,10 @@ ${content}`);
|
|
|
772
1017
|
}
|
|
773
1018
|
if (found.length === 0) {
|
|
774
1019
|
for (const file of ["CLAUDE.md", "AGENTS.md"]) {
|
|
775
|
-
const fullPath =
|
|
776
|
-
const dir =
|
|
777
|
-
if (!
|
|
778
|
-
|
|
1020
|
+
const fullPath = join4(repoRoot, file);
|
|
1021
|
+
const dir = dirname4(fullPath);
|
|
1022
|
+
if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true });
|
|
1023
|
+
writeFileSync3(fullPath, `${TEMPLATE}
|
|
779
1024
|
`);
|
|
780
1025
|
results.push({ file, action: "created" });
|
|
781
1026
|
}
|
|
@@ -784,24 +1029,24 @@ ${content}`);
|
|
|
784
1029
|
}
|
|
785
1030
|
|
|
786
1031
|
// packages/cli/src/lib/inject-mcp.ts
|
|
787
|
-
import { existsSync as
|
|
788
|
-
import { join as
|
|
1032
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
1033
|
+
import { join as join5 } from "path";
|
|
789
1034
|
var LENS_MCP_ENTRY = {
|
|
790
1035
|
type: "http",
|
|
791
1036
|
url: "http://localhost:4111/mcp"
|
|
792
1037
|
};
|
|
793
1038
|
function injectMcp(repoRoot) {
|
|
794
|
-
const mcpPath =
|
|
795
|
-
if (!
|
|
796
|
-
|
|
1039
|
+
const mcpPath = join5(repoRoot, ".mcp.json");
|
|
1040
|
+
if (!existsSync5(mcpPath)) {
|
|
1041
|
+
writeFileSync4(mcpPath, `${JSON.stringify({ mcpServers: { lens: LENS_MCP_ENTRY } }, null, 2)}
|
|
797
1042
|
`);
|
|
798
1043
|
return "created";
|
|
799
1044
|
}
|
|
800
|
-
const raw =
|
|
1045
|
+
const raw = readFileSync4(mcpPath, "utf-8");
|
|
801
1046
|
const config = JSON.parse(raw);
|
|
802
1047
|
if (config.mcpServers?.lens) return "exists";
|
|
803
1048
|
config.mcpServers = { ...config.mcpServers, lens: LENS_MCP_ENTRY };
|
|
804
|
-
|
|
1049
|
+
writeFileSync4(mcpPath, `${JSON.stringify(config, null, 2)}
|
|
805
1050
|
`);
|
|
806
1051
|
return "updated";
|
|
807
1052
|
}
|
|
@@ -835,11 +1080,24 @@ var register = defineCommand({
|
|
|
835
1080
|
},
|
|
836
1081
|
async run({ args }) {
|
|
837
1082
|
const absPath = resolve(args.path);
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1083
|
+
let res;
|
|
1084
|
+
try {
|
|
1085
|
+
res = await daemonFetch("/repos", {
|
|
1086
|
+
method: "POST",
|
|
1087
|
+
headers: { "Content-Type": "application/json" },
|
|
1088
|
+
body: JSON.stringify({ path: absPath, name: args.name })
|
|
1089
|
+
});
|
|
1090
|
+
} catch (err) {
|
|
1091
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1092
|
+
if (msg.includes("Not a git repository")) {
|
|
1093
|
+
console.error(`
|
|
1094
|
+
\u2717 ${absPath} is not a git repository.
|
|
1095
|
+
LENS requires a git repo to index.
|
|
1096
|
+
`);
|
|
1097
|
+
process.exit(1);
|
|
1098
|
+
}
|
|
1099
|
+
throw err;
|
|
1100
|
+
}
|
|
843
1101
|
const repo = await res.json();
|
|
844
1102
|
console.log();
|
|
845
1103
|
console.log(` ${bold2("\u26A1 LENS")} ${dim(repo.name)}`);
|
|
@@ -941,7 +1199,8 @@ var main = defineCommand({
|
|
|
941
1199
|
remove,
|
|
942
1200
|
list,
|
|
943
1201
|
grep,
|
|
944
|
-
graph
|
|
1202
|
+
graph,
|
|
1203
|
+
pattern
|
|
945
1204
|
}
|
|
946
1205
|
});
|
|
947
1206
|
runMain(main);
|