repowisestage 0.0.39 → 0.0.41
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/bin/repowise.js +1199 -566
- package/package.json +1 -1
package/dist/bin/repowise.js
CHANGED
|
@@ -456,6 +456,271 @@ var init_service_installer = __esm({
|
|
|
456
456
|
}
|
|
457
457
|
});
|
|
458
458
|
|
|
459
|
+
// ../listener/dist/lsp/registry.js
|
|
460
|
+
function detectLanguage(path) {
|
|
461
|
+
const dot = path.lastIndexOf(".");
|
|
462
|
+
if (dot < 0)
|
|
463
|
+
return null;
|
|
464
|
+
const ext = path.slice(dot).toLowerCase();
|
|
465
|
+
for (const [lang, configs] of Object.entries(LSP_REGISTRY)) {
|
|
466
|
+
if (configs.some((c) => c.extensions.includes(ext))) {
|
|
467
|
+
return lang;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
async function probeServers(isAvailable) {
|
|
473
|
+
const out = [];
|
|
474
|
+
for (const [lang, configs] of Object.entries(LSP_REGISTRY)) {
|
|
475
|
+
const checkedCommands = configs.map((c) => c.command);
|
|
476
|
+
let found = null;
|
|
477
|
+
for (const config2 of configs) {
|
|
478
|
+
if (await isAvailable(config2.command)) {
|
|
479
|
+
found = config2;
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
out.push({ language: lang, config: found, checkedCommands });
|
|
484
|
+
}
|
|
485
|
+
return out;
|
|
486
|
+
}
|
|
487
|
+
var LSP_REGISTRY;
|
|
488
|
+
var init_registry = __esm({
|
|
489
|
+
"../listener/dist/lsp/registry.js"() {
|
|
490
|
+
"use strict";
|
|
491
|
+
LSP_REGISTRY = {
|
|
492
|
+
typescript: [
|
|
493
|
+
{
|
|
494
|
+
id: "typescript-language-server",
|
|
495
|
+
displayName: "typescript-language-server",
|
|
496
|
+
command: "typescript-language-server",
|
|
497
|
+
args: ["--stdio"],
|
|
498
|
+
extensions: [".ts", ".tsx", ".mts", ".cts"],
|
|
499
|
+
lspLanguageId: "typescript",
|
|
500
|
+
installHint: "npm i -g typescript typescript-language-server",
|
|
501
|
+
npmPackage: "typescript-language-server"
|
|
502
|
+
}
|
|
503
|
+
],
|
|
504
|
+
javascript: [
|
|
505
|
+
{
|
|
506
|
+
id: "typescript-language-server-js",
|
|
507
|
+
displayName: "typescript-language-server (for JS)",
|
|
508
|
+
command: "typescript-language-server",
|
|
509
|
+
args: ["--stdio"],
|
|
510
|
+
extensions: [".js", ".jsx", ".mjs", ".cjs"],
|
|
511
|
+
lspLanguageId: "javascript",
|
|
512
|
+
installHint: "npm i -g typescript typescript-language-server",
|
|
513
|
+
npmPackage: "typescript-language-server"
|
|
514
|
+
}
|
|
515
|
+
],
|
|
516
|
+
python: [
|
|
517
|
+
{
|
|
518
|
+
id: "pyright",
|
|
519
|
+
displayName: "pyright",
|
|
520
|
+
command: "pyright-langserver",
|
|
521
|
+
args: ["--stdio"],
|
|
522
|
+
extensions: [".py", ".pyi"],
|
|
523
|
+
lspLanguageId: "python",
|
|
524
|
+
installHint: "npm i -g pyright",
|
|
525
|
+
npmPackage: "pyright"
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
id: "pylsp",
|
|
529
|
+
displayName: "python-lsp-server",
|
|
530
|
+
command: "pylsp",
|
|
531
|
+
args: [],
|
|
532
|
+
extensions: [".py", ".pyi"],
|
|
533
|
+
lspLanguageId: "python",
|
|
534
|
+
installHint: 'pipx install "python-lsp-server[all]"'
|
|
535
|
+
}
|
|
536
|
+
],
|
|
537
|
+
go: [
|
|
538
|
+
{
|
|
539
|
+
id: "gopls",
|
|
540
|
+
displayName: "gopls",
|
|
541
|
+
command: "gopls",
|
|
542
|
+
// Modern gopls uses stdio by default; `serve` is the legacy
|
|
543
|
+
// subcommand and no longer needed (review finding).
|
|
544
|
+
args: [],
|
|
545
|
+
extensions: [".go"],
|
|
546
|
+
lspLanguageId: "go",
|
|
547
|
+
installHint: "go install golang.org/x/tools/gopls@latest"
|
|
548
|
+
}
|
|
549
|
+
],
|
|
550
|
+
rust: [
|
|
551
|
+
{
|
|
552
|
+
id: "rust-analyzer",
|
|
553
|
+
displayName: "rust-analyzer",
|
|
554
|
+
command: "rust-analyzer",
|
|
555
|
+
args: [],
|
|
556
|
+
extensions: [".rs"],
|
|
557
|
+
lspLanguageId: "rust",
|
|
558
|
+
// `rustup component add rust-analyzer` installs a rustup proxy
|
|
559
|
+
// — the binary may not be on PATH directly. `brew install
|
|
560
|
+
// rust-analyzer` or a release tarball is more reliable.
|
|
561
|
+
installHint: "brew install rust-analyzer (or 'rustup component add rust-analyzer' and ensure it is on PATH)"
|
|
562
|
+
}
|
|
563
|
+
],
|
|
564
|
+
java: [
|
|
565
|
+
// The `jdtls` binary exists when installed via Homebrew (which
|
|
566
|
+
// ships a wrapper over the Eclipse JDT launcher) or the nvim
|
|
567
|
+
// jdtls scripts. Raw Eclipse downloads require invoking
|
|
568
|
+
// `java -jar org.eclipse.equinox.launcher_*.jar`; doctor needs
|
|
569
|
+
// to surface that when the bare `jdtls` binary is absent.
|
|
570
|
+
{
|
|
571
|
+
id: "jdtls",
|
|
572
|
+
displayName: "Eclipse JDT Language Server",
|
|
573
|
+
command: "jdtls",
|
|
574
|
+
args: [],
|
|
575
|
+
extensions: [".java"],
|
|
576
|
+
lspLanguageId: "java",
|
|
577
|
+
installHint: "brew install jdtls (see https://github.com/eclipse-jdtls/eclipse.jdt.ls)"
|
|
578
|
+
}
|
|
579
|
+
],
|
|
580
|
+
csharp: [
|
|
581
|
+
{
|
|
582
|
+
id: "omnisharp",
|
|
583
|
+
displayName: "OmniSharp",
|
|
584
|
+
command: "omnisharp",
|
|
585
|
+
args: ["-lsp"],
|
|
586
|
+
extensions: [".cs"],
|
|
587
|
+
lspLanguageId: "csharp",
|
|
588
|
+
installHint: "https://github.com/OmniSharp/omnisharp-roslyn/releases"
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
id: "csharp-ls",
|
|
592
|
+
displayName: "csharp-ls",
|
|
593
|
+
command: "csharp-ls",
|
|
594
|
+
args: [],
|
|
595
|
+
extensions: [".cs"],
|
|
596
|
+
lspLanguageId: "csharp",
|
|
597
|
+
installHint: "dotnet tool install -g csharp-ls"
|
|
598
|
+
}
|
|
599
|
+
],
|
|
600
|
+
php: [
|
|
601
|
+
{
|
|
602
|
+
id: "phpactor",
|
|
603
|
+
displayName: "phpactor",
|
|
604
|
+
command: "phpactor",
|
|
605
|
+
args: ["language-server"],
|
|
606
|
+
extensions: [".php"],
|
|
607
|
+
lspLanguageId: "php",
|
|
608
|
+
installHint: "https://phpactor.readthedocs.io/en/master/usage/standalone.html"
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
id: "intelephense",
|
|
612
|
+
displayName: "intelephense",
|
|
613
|
+
command: "intelephense",
|
|
614
|
+
args: ["--stdio"],
|
|
615
|
+
extensions: [".php"],
|
|
616
|
+
lspLanguageId: "php",
|
|
617
|
+
installHint: "npm i -g intelephense",
|
|
618
|
+
npmPackage: "intelephense"
|
|
619
|
+
}
|
|
620
|
+
],
|
|
621
|
+
ruby: [
|
|
622
|
+
{
|
|
623
|
+
id: "ruby-lsp",
|
|
624
|
+
displayName: "ruby-lsp",
|
|
625
|
+
command: "ruby-lsp",
|
|
626
|
+
args: [],
|
|
627
|
+
extensions: [".rb"],
|
|
628
|
+
lspLanguageId: "ruby",
|
|
629
|
+
installHint: "gem install ruby-lsp"
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
id: "solargraph",
|
|
633
|
+
displayName: "solargraph",
|
|
634
|
+
command: "solargraph",
|
|
635
|
+
args: ["stdio"],
|
|
636
|
+
extensions: [".rb"],
|
|
637
|
+
lspLanguageId: "ruby",
|
|
638
|
+
installHint: "gem install solargraph"
|
|
639
|
+
}
|
|
640
|
+
],
|
|
641
|
+
kotlin: [
|
|
642
|
+
{
|
|
643
|
+
id: "kotlin-language-server",
|
|
644
|
+
displayName: "kotlin-language-server",
|
|
645
|
+
command: "kotlin-language-server",
|
|
646
|
+
args: [],
|
|
647
|
+
extensions: [".kt", ".kts"],
|
|
648
|
+
lspLanguageId: "kotlin",
|
|
649
|
+
installHint: "brew install kotlin-language-server (or see https://github.com/fwcd/kotlin-language-server)"
|
|
650
|
+
}
|
|
651
|
+
],
|
|
652
|
+
swift: [
|
|
653
|
+
{
|
|
654
|
+
id: "sourcekit-lsp",
|
|
655
|
+
displayName: "sourcekit-lsp",
|
|
656
|
+
command: "sourcekit-lsp",
|
|
657
|
+
args: [],
|
|
658
|
+
extensions: [".swift"],
|
|
659
|
+
lspLanguageId: "swift",
|
|
660
|
+
installHint: "Bundled with Xcode Command Line Tools on macOS"
|
|
661
|
+
}
|
|
662
|
+
],
|
|
663
|
+
dart: [
|
|
664
|
+
{
|
|
665
|
+
id: "dart-language-server",
|
|
666
|
+
displayName: "dart language-server",
|
|
667
|
+
command: "dart",
|
|
668
|
+
args: ["language-server", "--protocol=lsp"],
|
|
669
|
+
extensions: [".dart"],
|
|
670
|
+
lspLanguageId: "dart",
|
|
671
|
+
installHint: "https://dart.dev/get-dart"
|
|
672
|
+
}
|
|
673
|
+
],
|
|
674
|
+
c: [
|
|
675
|
+
{
|
|
676
|
+
id: "clangd-c",
|
|
677
|
+
displayName: "clangd",
|
|
678
|
+
command: "clangd",
|
|
679
|
+
args: ["--background-index"],
|
|
680
|
+
extensions: [".c", ".h"],
|
|
681
|
+
lspLanguageId: "c",
|
|
682
|
+
installHint: "brew install llvm (includes clangd)"
|
|
683
|
+
}
|
|
684
|
+
],
|
|
685
|
+
cpp: [
|
|
686
|
+
{
|
|
687
|
+
id: "clangd-cpp",
|
|
688
|
+
displayName: "clangd",
|
|
689
|
+
command: "clangd",
|
|
690
|
+
args: ["--background-index"],
|
|
691
|
+
extensions: [".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx"],
|
|
692
|
+
lspLanguageId: "cpp",
|
|
693
|
+
installHint: "brew install llvm (includes clangd)"
|
|
694
|
+
}
|
|
695
|
+
],
|
|
696
|
+
vue: [
|
|
697
|
+
{
|
|
698
|
+
id: "vue-language-server",
|
|
699
|
+
displayName: "Vue Language Server (volar)",
|
|
700
|
+
command: "vue-language-server",
|
|
701
|
+
args: ["--stdio"],
|
|
702
|
+
extensions: [".vue"],
|
|
703
|
+
lspLanguageId: "vue",
|
|
704
|
+
installHint: "npm i -g @vue/language-server",
|
|
705
|
+
npmPackage: "@vue/language-server"
|
|
706
|
+
}
|
|
707
|
+
],
|
|
708
|
+
svelte: [
|
|
709
|
+
{
|
|
710
|
+
id: "svelte-language-server",
|
|
711
|
+
displayName: "svelte-language-server",
|
|
712
|
+
command: "svelteserver",
|
|
713
|
+
args: ["--stdio"],
|
|
714
|
+
extensions: [".svelte"],
|
|
715
|
+
lspLanguageId: "svelte",
|
|
716
|
+
installHint: "npm i -g svelte-language-server",
|
|
717
|
+
npmPackage: "svelte-language-server"
|
|
718
|
+
}
|
|
719
|
+
]
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
|
|
459
724
|
// ../../packages/shared/src/types/typed-resolution.ts
|
|
460
725
|
function typedResolutionKey(filePath, propertyName, line, column) {
|
|
461
726
|
return `${filePath}\0${propertyName}\0${line.toString()}\0${column.toString()}`;
|
|
@@ -609,7 +874,7 @@ __export(sidecar_cache_exports, {
|
|
|
609
874
|
sidecarCachePaths: () => sidecarCachePaths
|
|
610
875
|
});
|
|
611
876
|
import { mkdir as mkdir8, readFile as readFile7, readdir as readdir3, stat as stat2, unlink as unlink7, writeFile as writeFile8 } from "fs/promises";
|
|
612
|
-
import { dirname as dirname5, join as
|
|
877
|
+
import { dirname as dirname5, join as join15 } from "path";
|
|
613
878
|
function repoIdSafe(repoId) {
|
|
614
879
|
return /^[A-Za-z0-9_.-]{1,128}$/.test(repoId) && !repoId.startsWith(".");
|
|
615
880
|
}
|
|
@@ -623,8 +888,8 @@ function sidecarCachePaths(repoId, commitSha) {
|
|
|
623
888
|
if (!commitShaSafe(commitSha)) {
|
|
624
889
|
throw new Error(`unsafe commitSha: ${commitSha}`);
|
|
625
890
|
}
|
|
626
|
-
const repoDir =
|
|
627
|
-
const fullPath =
|
|
891
|
+
const repoDir = join15(getConfigDir(), "typed-resolution", repoId);
|
|
892
|
+
const fullPath = join15(repoDir, `${commitSha}.jsonl`);
|
|
628
893
|
return { fullPath, repoDir };
|
|
629
894
|
}
|
|
630
895
|
async function persistMergedSidecar(repoId, commitSha, sidecar) {
|
|
@@ -647,7 +912,7 @@ async function sweepSidecarDir(repoDir) {
|
|
|
647
912
|
const withMtime = [];
|
|
648
913
|
for (const name of jsonlFiles) {
|
|
649
914
|
try {
|
|
650
|
-
const s = await stat2(
|
|
915
|
+
const s = await stat2(join15(repoDir, name));
|
|
651
916
|
withMtime.push({ name, mtimeMs: s.mtimeMs });
|
|
652
917
|
} catch {
|
|
653
918
|
}
|
|
@@ -658,7 +923,7 @@ async function sweepSidecarDir(repoDir) {
|
|
|
658
923
|
for (const { name, mtimeMs } of candidates) {
|
|
659
924
|
if (now - mtimeMs >= SWEEP_MAX_AGE_MS) {
|
|
660
925
|
try {
|
|
661
|
-
await unlink7(
|
|
926
|
+
await unlink7(join15(repoDir, name));
|
|
662
927
|
} catch {
|
|
663
928
|
}
|
|
664
929
|
}
|
|
@@ -742,374 +1007,115 @@ async function uploadSidecar(req, fetchImpl = fetch) {
|
|
|
742
1007
|
const res = await fetchImpl(url, {
|
|
743
1008
|
method: "POST",
|
|
744
1009
|
headers: {
|
|
745
|
-
"content-type": "application/json",
|
|
746
|
-
authorization: `Bearer ${req.authToken}`
|
|
747
|
-
},
|
|
748
|
-
body
|
|
749
|
-
});
|
|
750
|
-
if (!res.ok) {
|
|
751
|
-
const text = await res.text().catch(() => "");
|
|
752
|
-
throw new Error(`upload failed: HTTP ${res.status.toString()} ${text}`);
|
|
753
|
-
}
|
|
754
|
-
const payload = await res.json();
|
|
755
|
-
if (!payload.data)
|
|
756
|
-
throw new Error("upload response missing `data`");
|
|
757
|
-
return payload.data;
|
|
758
|
-
}
|
|
759
|
-
async function downloadMergedSidecar(req, fetchImpl = fetch) {
|
|
760
|
-
const url = `${req.apiUrl.replace(/\/$/, "")}/v1/repos/${encodeURIComponent(req.repoId)}/typed-resolution/${encodeURIComponent(req.commitSha)}`;
|
|
761
|
-
const res = await fetchImpl(url, {
|
|
762
|
-
method: "GET",
|
|
763
|
-
headers: { authorization: `Bearer ${req.authToken}` }
|
|
764
|
-
});
|
|
765
|
-
if (res.status === 404)
|
|
766
|
-
return null;
|
|
767
|
-
if (!res.ok) {
|
|
768
|
-
throw new Error(`download failed: HTTP ${res.status.toString()}`);
|
|
769
|
-
}
|
|
770
|
-
const body = await res.json();
|
|
771
|
-
if (body.schemaVersion !== TYPED_RESOLUTION_SCHEMA_VERSION)
|
|
772
|
-
return null;
|
|
773
|
-
if (!Array.isArray(body.resolutions))
|
|
774
|
-
return null;
|
|
775
|
-
return body;
|
|
776
|
-
}
|
|
777
|
-
function buildSidecarIndex(sidecar) {
|
|
778
|
-
const map = /* @__PURE__ */ new Map();
|
|
779
|
-
if (!sidecar)
|
|
780
|
-
return map;
|
|
781
|
-
for (const r of sidecar.resolutions) {
|
|
782
|
-
map.set(typedResolutionKey(r.filePath, r.propertyName, r.line, r.column), r);
|
|
783
|
-
}
|
|
784
|
-
return map;
|
|
785
|
-
}
|
|
786
|
-
function overlayMergedSidecar(edges, sidecar) {
|
|
787
|
-
let edgesUpgraded = 0;
|
|
788
|
-
let edgesUnchanged = 0;
|
|
789
|
-
const index = buildSidecarIndex(sidecar);
|
|
790
|
-
for (const edge of edges) {
|
|
791
|
-
if (!edge.filePath || !edge.propertyName || !edge.location || edge.resolution === "typed") {
|
|
792
|
-
edgesUnchanged += 1;
|
|
793
|
-
continue;
|
|
794
|
-
}
|
|
795
|
-
const key = typedResolutionKey(edge.filePath, edge.propertyName, edge.location.line, edge.location.column);
|
|
796
|
-
const hit = index.get(key);
|
|
797
|
-
if (!hit || hit.targetSymbolId === null) {
|
|
798
|
-
edgesUnchanged += 1;
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
edge.to = hit.targetSymbolId;
|
|
802
|
-
edge.resolution = "typed";
|
|
803
|
-
edge.confidence = hit.confidence;
|
|
804
|
-
edgesUpgraded += 1;
|
|
805
|
-
}
|
|
806
|
-
return { edgesUpgraded, edgesUnchanged };
|
|
807
|
-
}
|
|
808
|
-
async function buildSidecar(req) {
|
|
809
|
-
const groupKeyFn = req.groupKey ?? (() => "default");
|
|
810
|
-
const groups = /* @__PURE__ */ new Map();
|
|
811
|
-
req.receivers.forEach((receiver, index) => {
|
|
812
|
-
const key = groupKeyFn(receiver) ?? "__default__";
|
|
813
|
-
let bucket = groups.get(key);
|
|
814
|
-
if (!bucket) {
|
|
815
|
-
bucket = [];
|
|
816
|
-
groups.set(key, bucket);
|
|
817
|
-
}
|
|
818
|
-
bucket.push({ index, receiver });
|
|
819
|
-
});
|
|
820
|
-
const resolutions = new Array(req.receivers.length);
|
|
821
|
-
await Promise.all(Array.from(groups.values()).map(async (bucket) => {
|
|
822
|
-
for (const { index, receiver } of bucket) {
|
|
823
|
-
let targetSymbolId;
|
|
824
|
-
try {
|
|
825
|
-
targetSymbolId = await req.resolve(receiver);
|
|
826
|
-
} catch (err) {
|
|
827
|
-
console.warn(`[typed-resolution] resolve failed for ${receiver.filePath}:${receiver.line.toString()}:${receiver.column.toString()} property=${receiver.propertyName} errMsg=${err instanceof Error ? err.message : String(err)}`);
|
|
828
|
-
targetSymbolId = null;
|
|
829
|
-
}
|
|
830
|
-
resolutions[index] = {
|
|
831
|
-
filePath: receiver.filePath,
|
|
832
|
-
propertyName: receiver.propertyName,
|
|
833
|
-
line: receiver.line,
|
|
834
|
-
column: receiver.column,
|
|
835
|
-
targetSymbolId,
|
|
836
|
-
confidence: 0.9
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
}));
|
|
840
|
-
return {
|
|
841
|
-
schemaVersion: TYPED_RESOLUTION_SCHEMA_VERSION,
|
|
842
|
-
producer: req.producer,
|
|
843
|
-
emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
844
|
-
languages: [...req.languages],
|
|
845
|
-
resolutions
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
var UPLOAD_BODY_BYTE_LIMIT;
|
|
849
|
-
var init_sidecar_client = __esm({
|
|
850
|
-
"../listener/dist/typed-resolution/sidecar-client.js"() {
|
|
851
|
-
"use strict";
|
|
852
|
-
init_src();
|
|
853
|
-
UPLOAD_BODY_BYTE_LIMIT = 8 * 1024 * 1024;
|
|
854
|
-
}
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
// ../listener/dist/lsp/registry.js
|
|
858
|
-
function detectLanguage(path) {
|
|
859
|
-
const dot = path.lastIndexOf(".");
|
|
860
|
-
if (dot < 0)
|
|
861
|
-
return null;
|
|
862
|
-
const ext = path.slice(dot).toLowerCase();
|
|
863
|
-
for (const [lang, configs] of Object.entries(LSP_REGISTRY)) {
|
|
864
|
-
if (configs.some((c) => c.extensions.includes(ext))) {
|
|
865
|
-
return lang;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
return null;
|
|
869
|
-
}
|
|
870
|
-
async function probeServers(isAvailable) {
|
|
871
|
-
const out = [];
|
|
872
|
-
for (const [lang, configs] of Object.entries(LSP_REGISTRY)) {
|
|
873
|
-
const checkedCommands = configs.map((c) => c.command);
|
|
874
|
-
let found = null;
|
|
875
|
-
for (const config2 of configs) {
|
|
876
|
-
if (await isAvailable(config2.command)) {
|
|
877
|
-
found = config2;
|
|
878
|
-
break;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
out.push({ language: lang, config: found, checkedCommands });
|
|
882
|
-
}
|
|
883
|
-
return out;
|
|
884
|
-
}
|
|
885
|
-
var LSP_REGISTRY;
|
|
886
|
-
var init_registry = __esm({
|
|
887
|
-
"../listener/dist/lsp/registry.js"() {
|
|
888
|
-
"use strict";
|
|
889
|
-
LSP_REGISTRY = {
|
|
890
|
-
typescript: [
|
|
891
|
-
{
|
|
892
|
-
id: "typescript-language-server",
|
|
893
|
-
displayName: "typescript-language-server",
|
|
894
|
-
command: "typescript-language-server",
|
|
895
|
-
args: ["--stdio"],
|
|
896
|
-
extensions: [".ts", ".tsx", ".mts", ".cts"],
|
|
897
|
-
lspLanguageId: "typescript",
|
|
898
|
-
installHint: "npm i -g typescript typescript-language-server"
|
|
899
|
-
}
|
|
900
|
-
],
|
|
901
|
-
javascript: [
|
|
902
|
-
{
|
|
903
|
-
id: "typescript-language-server-js",
|
|
904
|
-
displayName: "typescript-language-server (for JS)",
|
|
905
|
-
command: "typescript-language-server",
|
|
906
|
-
args: ["--stdio"],
|
|
907
|
-
extensions: [".js", ".jsx", ".mjs", ".cjs"],
|
|
908
|
-
lspLanguageId: "javascript",
|
|
909
|
-
installHint: "npm i -g typescript typescript-language-server"
|
|
910
|
-
}
|
|
911
|
-
],
|
|
912
|
-
python: [
|
|
913
|
-
{
|
|
914
|
-
id: "pyright",
|
|
915
|
-
displayName: "pyright",
|
|
916
|
-
command: "pyright-langserver",
|
|
917
|
-
args: ["--stdio"],
|
|
918
|
-
extensions: [".py", ".pyi"],
|
|
919
|
-
lspLanguageId: "python",
|
|
920
|
-
installHint: "npm i -g pyright"
|
|
921
|
-
},
|
|
922
|
-
{
|
|
923
|
-
id: "pylsp",
|
|
924
|
-
displayName: "python-lsp-server",
|
|
925
|
-
command: "pylsp",
|
|
926
|
-
args: [],
|
|
927
|
-
extensions: [".py", ".pyi"],
|
|
928
|
-
lspLanguageId: "python",
|
|
929
|
-
installHint: 'pipx install "python-lsp-server[all]"'
|
|
930
|
-
}
|
|
931
|
-
],
|
|
932
|
-
go: [
|
|
933
|
-
{
|
|
934
|
-
id: "gopls",
|
|
935
|
-
displayName: "gopls",
|
|
936
|
-
command: "gopls",
|
|
937
|
-
// Modern gopls uses stdio by default; `serve` is the legacy
|
|
938
|
-
// subcommand and no longer needed (review finding).
|
|
939
|
-
args: [],
|
|
940
|
-
extensions: [".go"],
|
|
941
|
-
lspLanguageId: "go",
|
|
942
|
-
installHint: "go install golang.org/x/tools/gopls@latest"
|
|
943
|
-
}
|
|
944
|
-
],
|
|
945
|
-
rust: [
|
|
946
|
-
{
|
|
947
|
-
id: "rust-analyzer",
|
|
948
|
-
displayName: "rust-analyzer",
|
|
949
|
-
command: "rust-analyzer",
|
|
950
|
-
args: [],
|
|
951
|
-
extensions: [".rs"],
|
|
952
|
-
lspLanguageId: "rust",
|
|
953
|
-
// `rustup component add rust-analyzer` installs a rustup proxy
|
|
954
|
-
// — the binary may not be on PATH directly. `brew install
|
|
955
|
-
// rust-analyzer` or a release tarball is more reliable.
|
|
956
|
-
installHint: "brew install rust-analyzer (or 'rustup component add rust-analyzer' and ensure it is on PATH)"
|
|
957
|
-
}
|
|
958
|
-
],
|
|
959
|
-
java: [
|
|
960
|
-
// The `jdtls` binary exists when installed via Homebrew (which
|
|
961
|
-
// ships a wrapper over the Eclipse JDT launcher) or the nvim
|
|
962
|
-
// jdtls scripts. Raw Eclipse downloads require invoking
|
|
963
|
-
// `java -jar org.eclipse.equinox.launcher_*.jar`; doctor needs
|
|
964
|
-
// to surface that when the bare `jdtls` binary is absent.
|
|
965
|
-
{
|
|
966
|
-
id: "jdtls",
|
|
967
|
-
displayName: "Eclipse JDT Language Server",
|
|
968
|
-
command: "jdtls",
|
|
969
|
-
args: [],
|
|
970
|
-
extensions: [".java"],
|
|
971
|
-
lspLanguageId: "java",
|
|
972
|
-
installHint: "brew install jdtls (see https://github.com/eclipse-jdtls/eclipse.jdt.ls)"
|
|
973
|
-
}
|
|
974
|
-
],
|
|
975
|
-
csharp: [
|
|
976
|
-
{
|
|
977
|
-
id: "omnisharp",
|
|
978
|
-
displayName: "OmniSharp",
|
|
979
|
-
command: "omnisharp",
|
|
980
|
-
args: ["-lsp"],
|
|
981
|
-
extensions: [".cs"],
|
|
982
|
-
lspLanguageId: "csharp",
|
|
983
|
-
installHint: "https://github.com/OmniSharp/omnisharp-roslyn/releases"
|
|
984
|
-
},
|
|
985
|
-
{
|
|
986
|
-
id: "csharp-ls",
|
|
987
|
-
displayName: "csharp-ls",
|
|
988
|
-
command: "csharp-ls",
|
|
989
|
-
args: [],
|
|
990
|
-
extensions: [".cs"],
|
|
991
|
-
lspLanguageId: "csharp",
|
|
992
|
-
installHint: "dotnet tool install -g csharp-ls"
|
|
993
|
-
}
|
|
994
|
-
],
|
|
995
|
-
php: [
|
|
996
|
-
{
|
|
997
|
-
id: "phpactor",
|
|
998
|
-
displayName: "phpactor",
|
|
999
|
-
command: "phpactor",
|
|
1000
|
-
args: ["language-server"],
|
|
1001
|
-
extensions: [".php"],
|
|
1002
|
-
lspLanguageId: "php",
|
|
1003
|
-
installHint: "https://phpactor.readthedocs.io/en/master/usage/standalone.html"
|
|
1004
|
-
},
|
|
1005
|
-
{
|
|
1006
|
-
id: "intelephense",
|
|
1007
|
-
displayName: "intelephense",
|
|
1008
|
-
command: "intelephense",
|
|
1009
|
-
args: ["--stdio"],
|
|
1010
|
-
extensions: [".php"],
|
|
1011
|
-
lspLanguageId: "php",
|
|
1012
|
-
installHint: "npm i -g intelephense"
|
|
1013
|
-
}
|
|
1014
|
-
],
|
|
1015
|
-
ruby: [
|
|
1016
|
-
{
|
|
1017
|
-
id: "ruby-lsp",
|
|
1018
|
-
displayName: "ruby-lsp",
|
|
1019
|
-
command: "ruby-lsp",
|
|
1020
|
-
args: [],
|
|
1021
|
-
extensions: [".rb"],
|
|
1022
|
-
lspLanguageId: "ruby",
|
|
1023
|
-
installHint: "gem install ruby-lsp"
|
|
1024
|
-
},
|
|
1025
|
-
{
|
|
1026
|
-
id: "solargraph",
|
|
1027
|
-
displayName: "solargraph",
|
|
1028
|
-
command: "solargraph",
|
|
1029
|
-
args: ["stdio"],
|
|
1030
|
-
extensions: [".rb"],
|
|
1031
|
-
lspLanguageId: "ruby",
|
|
1032
|
-
installHint: "gem install solargraph"
|
|
1033
|
-
}
|
|
1034
|
-
],
|
|
1035
|
-
kotlin: [
|
|
1036
|
-
{
|
|
1037
|
-
id: "kotlin-language-server",
|
|
1038
|
-
displayName: "kotlin-language-server",
|
|
1039
|
-
command: "kotlin-language-server",
|
|
1040
|
-
args: [],
|
|
1041
|
-
extensions: [".kt", ".kts"],
|
|
1042
|
-
lspLanguageId: "kotlin",
|
|
1043
|
-
installHint: "brew install kotlin-language-server (or see https://github.com/fwcd/kotlin-language-server)"
|
|
1044
|
-
}
|
|
1045
|
-
],
|
|
1046
|
-
swift: [
|
|
1047
|
-
{
|
|
1048
|
-
id: "sourcekit-lsp",
|
|
1049
|
-
displayName: "sourcekit-lsp",
|
|
1050
|
-
command: "sourcekit-lsp",
|
|
1051
|
-
args: [],
|
|
1052
|
-
extensions: [".swift"],
|
|
1053
|
-
lspLanguageId: "swift",
|
|
1054
|
-
installHint: "Bundled with Xcode Command Line Tools on macOS"
|
|
1055
|
-
}
|
|
1056
|
-
],
|
|
1057
|
-
dart: [
|
|
1058
|
-
{
|
|
1059
|
-
id: "dart-language-server",
|
|
1060
|
-
displayName: "dart language-server",
|
|
1061
|
-
command: "dart",
|
|
1062
|
-
args: ["language-server", "--protocol=lsp"],
|
|
1063
|
-
extensions: [".dart"],
|
|
1064
|
-
lspLanguageId: "dart",
|
|
1065
|
-
installHint: "https://dart.dev/get-dart"
|
|
1066
|
-
}
|
|
1067
|
-
],
|
|
1068
|
-
c: [
|
|
1069
|
-
{
|
|
1070
|
-
id: "clangd-c",
|
|
1071
|
-
displayName: "clangd",
|
|
1072
|
-
command: "clangd",
|
|
1073
|
-
args: ["--background-index"],
|
|
1074
|
-
extensions: [".c", ".h"],
|
|
1075
|
-
lspLanguageId: "c",
|
|
1076
|
-
installHint: "brew install llvm (includes clangd)"
|
|
1077
|
-
}
|
|
1078
|
-
],
|
|
1079
|
-
cpp: [
|
|
1080
|
-
{
|
|
1081
|
-
id: "clangd-cpp",
|
|
1082
|
-
displayName: "clangd",
|
|
1083
|
-
command: "clangd",
|
|
1084
|
-
args: ["--background-index"],
|
|
1085
|
-
extensions: [".cpp", ".cc", ".cxx", ".hpp", ".hh", ".hxx"],
|
|
1086
|
-
lspLanguageId: "cpp",
|
|
1087
|
-
installHint: "brew install llvm (includes clangd)"
|
|
1088
|
-
}
|
|
1089
|
-
],
|
|
1090
|
-
vue: [
|
|
1091
|
-
{
|
|
1092
|
-
id: "vue-language-server",
|
|
1093
|
-
displayName: "Vue Language Server (volar)",
|
|
1094
|
-
command: "vue-language-server",
|
|
1095
|
-
args: ["--stdio"],
|
|
1096
|
-
extensions: [".vue"],
|
|
1097
|
-
lspLanguageId: "vue",
|
|
1098
|
-
installHint: "npm i -g @vue/language-server"
|
|
1099
|
-
}
|
|
1100
|
-
],
|
|
1101
|
-
svelte: [
|
|
1102
|
-
{
|
|
1103
|
-
id: "svelte-language-server",
|
|
1104
|
-
displayName: "svelte-language-server",
|
|
1105
|
-
command: "svelteserver",
|
|
1106
|
-
args: ["--stdio"],
|
|
1107
|
-
extensions: [".svelte"],
|
|
1108
|
-
lspLanguageId: "svelte",
|
|
1109
|
-
installHint: "npm i -g svelte-language-server"
|
|
1110
|
-
}
|
|
1111
|
-
]
|
|
1112
|
-
};
|
|
1010
|
+
"content-type": "application/json",
|
|
1011
|
+
authorization: `Bearer ${req.authToken}`
|
|
1012
|
+
},
|
|
1013
|
+
body
|
|
1014
|
+
});
|
|
1015
|
+
if (!res.ok) {
|
|
1016
|
+
const text = await res.text().catch(() => "");
|
|
1017
|
+
throw new Error(`upload failed: HTTP ${res.status.toString()} ${text}`);
|
|
1018
|
+
}
|
|
1019
|
+
const payload = await res.json();
|
|
1020
|
+
if (!payload.data)
|
|
1021
|
+
throw new Error("upload response missing `data`");
|
|
1022
|
+
return payload.data;
|
|
1023
|
+
}
|
|
1024
|
+
async function downloadMergedSidecar(req, fetchImpl = fetch) {
|
|
1025
|
+
const url = `${req.apiUrl.replace(/\/$/, "")}/v1/repos/${encodeURIComponent(req.repoId)}/typed-resolution/${encodeURIComponent(req.commitSha)}`;
|
|
1026
|
+
const res = await fetchImpl(url, {
|
|
1027
|
+
method: "GET",
|
|
1028
|
+
headers: { authorization: `Bearer ${req.authToken}` }
|
|
1029
|
+
});
|
|
1030
|
+
if (res.status === 404)
|
|
1031
|
+
return null;
|
|
1032
|
+
if (!res.ok) {
|
|
1033
|
+
throw new Error(`download failed: HTTP ${res.status.toString()}`);
|
|
1034
|
+
}
|
|
1035
|
+
const body = await res.json();
|
|
1036
|
+
if (body.schemaVersion !== TYPED_RESOLUTION_SCHEMA_VERSION)
|
|
1037
|
+
return null;
|
|
1038
|
+
if (!Array.isArray(body.resolutions))
|
|
1039
|
+
return null;
|
|
1040
|
+
return body;
|
|
1041
|
+
}
|
|
1042
|
+
function buildSidecarIndex(sidecar) {
|
|
1043
|
+
const map = /* @__PURE__ */ new Map();
|
|
1044
|
+
if (!sidecar)
|
|
1045
|
+
return map;
|
|
1046
|
+
for (const r of sidecar.resolutions) {
|
|
1047
|
+
map.set(typedResolutionKey(r.filePath, r.propertyName, r.line, r.column), r);
|
|
1048
|
+
}
|
|
1049
|
+
return map;
|
|
1050
|
+
}
|
|
1051
|
+
function overlayMergedSidecar(edges, sidecar) {
|
|
1052
|
+
let edgesUpgraded = 0;
|
|
1053
|
+
let edgesUnchanged = 0;
|
|
1054
|
+
const index = buildSidecarIndex(sidecar);
|
|
1055
|
+
for (const edge of edges) {
|
|
1056
|
+
if (!edge.filePath || !edge.propertyName || !edge.location || edge.resolution === "typed") {
|
|
1057
|
+
edgesUnchanged += 1;
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
const key = typedResolutionKey(edge.filePath, edge.propertyName, edge.location.line, edge.location.column);
|
|
1061
|
+
const hit = index.get(key);
|
|
1062
|
+
if (!hit || hit.targetSymbolId === null) {
|
|
1063
|
+
edgesUnchanged += 1;
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
edge.to = hit.targetSymbolId;
|
|
1067
|
+
edge.resolution = "typed";
|
|
1068
|
+
edge.confidence = hit.confidence;
|
|
1069
|
+
edgesUpgraded += 1;
|
|
1070
|
+
}
|
|
1071
|
+
return { edgesUpgraded, edgesUnchanged };
|
|
1072
|
+
}
|
|
1073
|
+
async function buildSidecar(req) {
|
|
1074
|
+
const groupKeyFn = req.groupKey ?? (() => "default");
|
|
1075
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1076
|
+
req.receivers.forEach((receiver, index) => {
|
|
1077
|
+
const key = groupKeyFn(receiver) ?? "__default__";
|
|
1078
|
+
let bucket = groups.get(key);
|
|
1079
|
+
if (!bucket) {
|
|
1080
|
+
bucket = [];
|
|
1081
|
+
groups.set(key, bucket);
|
|
1082
|
+
}
|
|
1083
|
+
bucket.push({ index, receiver });
|
|
1084
|
+
});
|
|
1085
|
+
const resolutions = new Array(req.receivers.length);
|
|
1086
|
+
await Promise.all(Array.from(groups.values()).map(async (bucket) => {
|
|
1087
|
+
for (const { index, receiver } of bucket) {
|
|
1088
|
+
let targetSymbolId;
|
|
1089
|
+
try {
|
|
1090
|
+
targetSymbolId = await req.resolve(receiver);
|
|
1091
|
+
} catch (err) {
|
|
1092
|
+
console.warn(`[typed-resolution] resolve failed for ${receiver.filePath}:${receiver.line.toString()}:${receiver.column.toString()} property=${receiver.propertyName} errMsg=${err instanceof Error ? err.message : String(err)}`);
|
|
1093
|
+
targetSymbolId = null;
|
|
1094
|
+
}
|
|
1095
|
+
resolutions[index] = {
|
|
1096
|
+
filePath: receiver.filePath,
|
|
1097
|
+
propertyName: receiver.propertyName,
|
|
1098
|
+
line: receiver.line,
|
|
1099
|
+
column: receiver.column,
|
|
1100
|
+
targetSymbolId,
|
|
1101
|
+
confidence: 0.9
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
}));
|
|
1105
|
+
return {
|
|
1106
|
+
schemaVersion: TYPED_RESOLUTION_SCHEMA_VERSION,
|
|
1107
|
+
producer: req.producer,
|
|
1108
|
+
emittedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1109
|
+
languages: [...req.languages],
|
|
1110
|
+
resolutions
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
var UPLOAD_BODY_BYTE_LIMIT;
|
|
1114
|
+
var init_sidecar_client = __esm({
|
|
1115
|
+
"../listener/dist/typed-resolution/sidecar-client.js"() {
|
|
1116
|
+
"use strict";
|
|
1117
|
+
init_src();
|
|
1118
|
+
UPLOAD_BODY_BYTE_LIMIT = 8 * 1024 * 1024;
|
|
1113
1119
|
}
|
|
1114
1120
|
});
|
|
1115
1121
|
|
|
@@ -1466,19 +1472,91 @@ var init_lsp_tools = __esm({
|
|
|
1466
1472
|
// bin/repowise.ts
|
|
1467
1473
|
import { readFileSync as readFileSync3 } from "fs";
|
|
1468
1474
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1469
|
-
import { dirname as
|
|
1475
|
+
import { dirname as dirname17, join as join45 } from "path";
|
|
1470
1476
|
import { Command } from "commander";
|
|
1471
1477
|
|
|
1472
1478
|
// ../listener/dist/main.js
|
|
1473
1479
|
init_config_dir();
|
|
1474
|
-
import { readFile as readFile12, writeFile as writeFile14, mkdir as mkdir14 } from "fs/promises";
|
|
1475
|
-
import { join as
|
|
1480
|
+
import { readFile as readFile12, writeFile as writeFile14, mkdir as mkdir14, stat as fsStat } from "fs/promises";
|
|
1481
|
+
import { join as join27, dirname as dirname12 } from "path";
|
|
1476
1482
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1477
1483
|
import lockfile3 from "proper-lockfile";
|
|
1478
1484
|
|
|
1479
1485
|
// ../../packages/shared/dist/lib/ai-tools.js
|
|
1480
1486
|
import { readFile, writeFile, mkdir, readdir, stat, unlink } from "fs/promises";
|
|
1481
1487
|
import { join as join2, dirname } from "path";
|
|
1488
|
+
|
|
1489
|
+
// ../../packages/shared/dist/lib/project-overview-injection.js
|
|
1490
|
+
var START_MARKER = "<!-- repowise:agent-instructions:start -->";
|
|
1491
|
+
var END_MARKER = "<!-- repowise:agent-instructions:end -->";
|
|
1492
|
+
var AI_AGENT_USAGE_INSTRUCTIONS = `${START_MARKER}
|
|
1493
|
+
## How AI Agents Should Use This Context
|
|
1494
|
+
|
|
1495
|
+
This document is the **entry point** for RepoWise's pre-analyzed codebase context. The \`repowise-context/\` folder contains architecture, API contracts, data models, coding patterns, user flows, and domain knowledge \u2014 pre-extracted so AI agents can perform ANY codebase task (implementing features, debugging, refactoring, writing tests, reviewing diffs, planning changes) with deep context.
|
|
1496
|
+
|
|
1497
|
+
Treat RepoWise as your default starting point for every task that touches this codebase, not a Q&A reference.
|
|
1498
|
+
|
|
1499
|
+
**Rules for using this context:**
|
|
1500
|
+
|
|
1501
|
+
1. **Always start with RepoWise.** Before any task \u2014 questions, planning, coding, debugging, refactoring, tests, code review \u2014 gather context from RepoWise FIRST. Skipping it leads to wrong patterns, duplicated abstractions, and missed constraints.
|
|
1502
|
+
|
|
1503
|
+
2. **Use the three-tier hierarchy in this order:**
|
|
1504
|
+
- **Tier 1 \u2014 RepoWise MCP** for surgical structural queries: symbol resolution, callers, references, call graphs, refactor impact, dependency graphs, type info (\`mcp__repowise__find_symbol\`, \`mcp__repowise__get_impact\`, \`mcp__repowise__lsp_definition\`, etc.). Each tool's description tells you when to use it.
|
|
1505
|
+
- **Tier 2 \u2014 Context files in \`repowise-context/\`** for narrative context: how a domain is designed, what patterns exist, what conventions to follow. Use the *Context File Routing Map* below to pick which files to read.
|
|
1506
|
+
- **Tier 3 \u2014 Native code search** (\`read_file\` / \`grep\` / \`glob\` on raw source) ONLY when tiers 1-2 don't cover the question, or for ephemeral state (recent commits, in-flight branches).
|
|
1507
|
+
|
|
1508
|
+
3. **Batch tool calls in parallel.** For maximum efficiency, whenever you need multiple independent operations to gather context, invoke all relevant tools SIMULTANEOUSLY rather than sequentially. Examples:
|
|
1509
|
+
- Reading 3 context files \u2192 3 \`read_file\` calls in parallel.
|
|
1510
|
+
- Confirming symbol shape AND its callers \u2192 \`get_structure\` + \`find_callers\` in parallel.
|
|
1511
|
+
- Initial reconnaissance for a new task \u2192 \`get_facts\` AND \`project-overview.md\` AND 2-3 likely-relevant domain files TOGETHER in your first turn.
|
|
1512
|
+
|
|
1513
|
+
Only chain serially when one tool's output is genuinely required as input to another (e.g., \`glob\` \u2192 then \`read_file\` on the matches).
|
|
1514
|
+
|
|
1515
|
+
4. **Apply per-task patterns** (always batch the gathering step):
|
|
1516
|
+
- Planning \u2192 Tier 2 in parallel (architecture/patterns/domain) + Tier 1 (\`get_impact\`, \`get_deps\`).
|
|
1517
|
+
- Implementing \u2192 Tier 1 (\`find_symbol\`, \`lsp_definition\`) + Tier 2 (\`coding-patterns.md\`).
|
|
1518
|
+
- Debugging \u2192 Tier 1 (\`find_callers\`, \`get_call_graph\`) + Tier 2 (\`error-handling.md\`, \`integration-patterns.md\`).
|
|
1519
|
+
- Writing tests \u2192 Tier 1 (\`find_tests_for_symbol\`) + Tier 2 (\`testing-strategy.md\`).
|
|
1520
|
+
- Code review / architecture decisions \u2192 Tier 2 in parallel (\`architecture.md\`, \`tribal-knowledge.md\`, relevant domain file).
|
|
1521
|
+
|
|
1522
|
+
5. **Propagate to subagents.** When delegating work, explicitly instruct the sub-task to read \`repowise-context/project-overview.md\` AND to use RepoWise MCP tools. Subagents don't inherit parent instructions automatically.
|
|
1523
|
+
|
|
1524
|
+
6. **Don't edit \`repowise-context/\` files manually.** They're regenerated by RepoWise on every sync. Manual edits will be overwritten. Update source code; let the next sync regenerate.
|
|
1525
|
+
|
|
1526
|
+
7. **Mark every response that used RepoWise.** When your work used any RepoWise resource \u2014 a file under \`repowise-context/\`, or a RepoWise MCP tool \u2014 begin your response with a single tag line:
|
|
1527
|
+
|
|
1528
|
+
_Powered by RepoWise._
|
|
1529
|
+
|
|
1530
|
+
That's it \u2014 one short line, no longer preamble. The host's tool-call UI already shows \`mcp__repowise__*\` calls visibly; this tag adds the same surface for context-file reads. If you didn't use RepoWise on this turn, omit the tag \u2014 never fake it.
|
|
1531
|
+
|
|
1532
|
+
8. **Surface MCP gaps explicitly.** If RepoWise MCP is unavailable (no \`.mcp.json\` configured, listener not running), tell the user \u2014 don't silently fall back to \`grep\`.
|
|
1533
|
+
${END_MARKER}`;
|
|
1534
|
+
function injectAgentInstructions(content) {
|
|
1535
|
+
const lineEnding = content.includes("\r\n") ? "\r\n" : "\n";
|
|
1536
|
+
const canonicalBlock = AI_AGENT_USAGE_INSTRUCTIONS.replace(/\n/g, lineEnding);
|
|
1537
|
+
const startIdx = content.indexOf(START_MARKER);
|
|
1538
|
+
const endIdx = content.indexOf(END_MARKER);
|
|
1539
|
+
if (startIdx >= 0 && endIdx > startIdx) {
|
|
1540
|
+
const currentBlock = content.slice(startIdx, endIdx + END_MARKER.length);
|
|
1541
|
+
if (currentBlock === canonicalBlock) {
|
|
1542
|
+
return content;
|
|
1543
|
+
}
|
|
1544
|
+
return content.slice(0, startIdx) + canonicalBlock + content.slice(endIdx + END_MARKER.length);
|
|
1545
|
+
}
|
|
1546
|
+
const h1Match = content.match(/^# Project Overview\s*$/m);
|
|
1547
|
+
if (h1Match && h1Match.index !== void 0) {
|
|
1548
|
+
const insertAt = h1Match.index + h1Match[0].length;
|
|
1549
|
+
return content.slice(0, insertAt) + lineEnding + lineEnding + canonicalBlock + content.slice(insertAt);
|
|
1550
|
+
}
|
|
1551
|
+
const h2Match = content.match(/^## Summary\s*$/m);
|
|
1552
|
+
if (h2Match && h2Match.index !== void 0) {
|
|
1553
|
+
const insertAt = h2Match.index + h2Match[0].length;
|
|
1554
|
+
return content.slice(0, insertAt) + lineEnding + lineEnding + canonicalBlock + content.slice(insertAt);
|
|
1555
|
+
}
|
|
1556
|
+
return canonicalBlock + lineEnding + lineEnding + content;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// ../../packages/shared/dist/lib/ai-tools.js
|
|
1482
1560
|
var AI_TOOL_CONFIG = {
|
|
1483
1561
|
cursor: {
|
|
1484
1562
|
label: "Cursor",
|
|
@@ -1625,15 +1703,15 @@ function generateReference(tool, repoName, contextFolder, contextFiles) {
|
|
|
1625
1703
|
`## Project Context \u2014 ${safeName}`,
|
|
1626
1704
|
"",
|
|
1627
1705
|
`This repository has AI-optimized context files generated by RepoWise.`,
|
|
1628
|
-
`**IMPORTANT: Before answering questions about the codebase or making any changes, ALWAYS check the \`${contextFolder}/\` folder first.** These files contain pre-analyzed architecture, patterns, API contracts, and domain knowledge that will answer most questions without needing to search the codebase.`,
|
|
1629
1706
|
"",
|
|
1630
|
-
|
|
1707
|
+
AI_AGENT_USAGE_INSTRUCTIONS,
|
|
1708
|
+
"",
|
|
1709
|
+
`**Start here:** \`${contextFolder}/project-overview.md\` \u2014 the routing document that maps every context file to its domain.`,
|
|
1631
1710
|
""
|
|
1632
1711
|
];
|
|
1633
1712
|
if (hasFiles) {
|
|
1634
|
-
contentLines.push(`**
|
|
1713
|
+
contentLines.push(`**Context File Routing Map:**`, "", ...fileLines.map((f) => `- \`${f.path}\` \u2014 ${f.desc}`), "", `> Additional context files may exist beyond this list. Check \`project-overview.md\` for the complete index.`);
|
|
1635
1714
|
}
|
|
1636
|
-
contentLines.push(`**Subagents:** When delegating tasks to sub-agents, always include this instruction: "Read \`${contextFolder}/project-overview.md\` before performing any work."`);
|
|
1637
1715
|
if (config2.owned) {
|
|
1638
1716
|
const parts = [];
|
|
1639
1717
|
if (config2.frontmatter) {
|
|
@@ -1777,55 +1855,6 @@ async function scanLocalContextFiles(repoRoot, contextFolder) {
|
|
|
1777
1855
|
}
|
|
1778
1856
|
}
|
|
1779
1857
|
|
|
1780
|
-
// ../../packages/shared/dist/lib/project-overview-injection.js
|
|
1781
|
-
var START_MARKER = "<!-- repowise:agent-instructions:start -->";
|
|
1782
|
-
var END_MARKER = "<!-- repowise:agent-instructions:end -->";
|
|
1783
|
-
var AI_AGENT_USAGE_INSTRUCTIONS = `${START_MARKER}
|
|
1784
|
-
## How AI Agents Should Use This Context
|
|
1785
|
-
|
|
1786
|
-
This document is the **entry point** for RepoWise's pre-analyzed codebase context. The \`repowise-context/\` folder contains architecture, API contracts, data models, coding patterns, user flows, and domain knowledge \u2014 pre-extracted so AI agents can answer most questions without searching the codebase.
|
|
1787
|
-
|
|
1788
|
-
**Rules for using this context:**
|
|
1789
|
-
|
|
1790
|
-
1. **Route first.** Use the *Context File Routing Map* below to identify which file(s) cover the domain of the current task. Files are organized by scope (root = cross-cutting, \`apps/<name>/\` = app-specific) and by concern (architecture, data models, API contracts, coding patterns, etc.).
|
|
1791
|
-
|
|
1792
|
-
2. **Read before searching.** Consult the matching context file(s) before running grep/glob or reading source files. These files are the authoritative source for architecture, patterns, and conventions in this project.
|
|
1793
|
-
|
|
1794
|
-
3. **Use MCP tools for code intelligence.** This codebase has a live code-intelligence server (the RepoWise listener) exposed as MCP tools. For questions about symbol resolution, call graphs, refactoring impact, type definitions, references, or pattern search across files \u2014 call the MCP tools rather than reading raw files. Tool names are prefixed by your host's MCP convention (e.g. \`mcp__repowise__find_symbol\`); each tool's schema description tells you when to use it. If MCP is unavailable, surface that to the user rather than guessing.
|
|
1795
|
-
|
|
1796
|
-
4. **Fall back when needed.** If the context files and MCP tools don't cover the question \u2014 or if the question is about ephemeral state (recent commits, branch status, in-flight work) \u2014 then use code search (grep/glob).
|
|
1797
|
-
|
|
1798
|
-
5. **Propagate to subagents.** When delegating work to a sub-task or spawning another agent, explicitly instruct it to read \`repowise-context/project-overview.md\` first. Subagents do not inherit the parent agent's instructions automatically.
|
|
1799
|
-
|
|
1800
|
-
6. **Don't edit these files manually.** The \`repowise-context/\` folder is regenerated by RepoWise on every sync. Manual edits will be overwritten. Update the source code and let the next sync regenerate the context.
|
|
1801
|
-
|
|
1802
|
-
7. **Attribute RepoWise when you use its context.** When you cite information from RepoWise (any file under \`repowise-context/\` or any RepoWise MCP tool), tell the user where the context came from. Example phrasing: "Based on RepoWise's code graph, ..." or "From your RepoWise context docs, ..."
|
|
1803
|
-
${END_MARKER}`;
|
|
1804
|
-
function injectAgentInstructions(content) {
|
|
1805
|
-
const lineEnding = content.includes("\r\n") ? "\r\n" : "\n";
|
|
1806
|
-
const canonicalBlock = AI_AGENT_USAGE_INSTRUCTIONS.replace(/\n/g, lineEnding);
|
|
1807
|
-
const startIdx = content.indexOf(START_MARKER);
|
|
1808
|
-
const endIdx = content.indexOf(END_MARKER);
|
|
1809
|
-
if (startIdx >= 0 && endIdx > startIdx) {
|
|
1810
|
-
const currentBlock = content.slice(startIdx, endIdx + END_MARKER.length);
|
|
1811
|
-
if (currentBlock === canonicalBlock) {
|
|
1812
|
-
return content;
|
|
1813
|
-
}
|
|
1814
|
-
return content.slice(0, startIdx) + canonicalBlock + content.slice(endIdx + END_MARKER.length);
|
|
1815
|
-
}
|
|
1816
|
-
const h1Match = content.match(/^# Project Overview\s*$/m);
|
|
1817
|
-
if (h1Match && h1Match.index !== void 0) {
|
|
1818
|
-
const insertAt = h1Match.index + h1Match[0].length;
|
|
1819
|
-
return content.slice(0, insertAt) + lineEnding + lineEnding + canonicalBlock + content.slice(insertAt);
|
|
1820
|
-
}
|
|
1821
|
-
const h2Match = content.match(/^## Summary\s*$/m);
|
|
1822
|
-
if (h2Match && h2Match.index !== void 0) {
|
|
1823
|
-
const insertAt = h2Match.index + h2Match[0].length;
|
|
1824
|
-
return content.slice(0, insertAt) + lineEnding + lineEnding + canonicalBlock + content.slice(insertAt);
|
|
1825
|
-
}
|
|
1826
|
-
return canonicalBlock + lineEnding + lineEnding + content;
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
1858
|
// ../listener/dist/lib/config.js
|
|
1830
1859
|
init_config_dir();
|
|
1831
1860
|
import { readFile as readFile2, writeFile as writeFile2, rename, unlink as unlink2, mkdir as mkdir2, chmod, open } from "fs/promises";
|
|
@@ -2738,7 +2767,7 @@ async function stopListener() {
|
|
|
2738
2767
|
|
|
2739
2768
|
// ../listener/dist/mcp/bootstrap.js
|
|
2740
2769
|
init_config_dir();
|
|
2741
|
-
import { join as
|
|
2770
|
+
import { join as join18 } from "path";
|
|
2742
2771
|
|
|
2743
2772
|
// ../listener/dist/lsp/workspace-session.js
|
|
2744
2773
|
import { pathToFileURL } from "url";
|
|
@@ -2993,25 +3022,193 @@ var LspClient = class extends EventEmitter {
|
|
|
2993
3022
|
this.emit("notification", msg["method"], msg["params"]);
|
|
2994
3023
|
return;
|
|
2995
3024
|
}
|
|
2996
|
-
if (msg["method"] !== void 0 && msg["id"] !== void 0) {
|
|
2997
|
-
const method = msg["method"];
|
|
2998
|
-
const reqId = msg["id"];
|
|
2999
|
-
if (this.listenerCount("request") === 0) {
|
|
3000
|
-
this.respondError(reqId, -32601, `method not found: ${method}`);
|
|
3025
|
+
if (msg["method"] !== void 0 && msg["id"] !== void 0) {
|
|
3026
|
+
const method = msg["method"];
|
|
3027
|
+
const reqId = msg["id"];
|
|
3028
|
+
if (this.listenerCount("request") === 0) {
|
|
3029
|
+
this.respondError(reqId, -32601, `method not found: ${method}`);
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
this.emit("request", method, msg["params"], reqId);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
failAllPending(err) {
|
|
3036
|
+
for (const pending of this.pending.values()) {
|
|
3037
|
+
if (pending.timer)
|
|
3038
|
+
clearTimeout(pending.timer);
|
|
3039
|
+
pending.reject(err);
|
|
3040
|
+
}
|
|
3041
|
+
this.pending.clear();
|
|
3042
|
+
}
|
|
3043
|
+
};
|
|
3044
|
+
|
|
3045
|
+
// ../listener/dist/lsp/installer.js
|
|
3046
|
+
init_config_dir();
|
|
3047
|
+
init_registry();
|
|
3048
|
+
import { promises as fs } from "fs";
|
|
3049
|
+
import { join as join14 } from "path";
|
|
3050
|
+
import { spawn as spawn3 } from "child_process";
|
|
3051
|
+
function getLspInstallDir() {
|
|
3052
|
+
return join14(getConfigDir(), "lsp-servers");
|
|
3053
|
+
}
|
|
3054
|
+
function getLspBinDir() {
|
|
3055
|
+
return join14(getLspInstallDir(), "node_modules", ".bin");
|
|
3056
|
+
}
|
|
3057
|
+
async function ensureNpmLspInstalled(config2) {
|
|
3058
|
+
if (!config2.npmPackage)
|
|
3059
|
+
return { installed: false, skipped: "no-npm-package" };
|
|
3060
|
+
const installDir = getLspInstallDir();
|
|
3061
|
+
const binPath = join14(installDir, "node_modules", ".bin", config2.command);
|
|
3062
|
+
if (await pathExists(binPath)) {
|
|
3063
|
+
return { installed: false, skipped: "already-present" };
|
|
3064
|
+
}
|
|
3065
|
+
try {
|
|
3066
|
+
await fs.mkdir(installDir, { recursive: true });
|
|
3067
|
+
const pkgJsonPath = join14(installDir, "package.json");
|
|
3068
|
+
if (!await pathExists(pkgJsonPath)) {
|
|
3069
|
+
await fs.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
|
|
3070
|
+
}
|
|
3071
|
+
await runNpmInstall(installDir, config2.npmPackage);
|
|
3072
|
+
if (!await pathExists(binPath)) {
|
|
3073
|
+
return {
|
|
3074
|
+
installed: false,
|
|
3075
|
+
error: `npm install completed but ${config2.command} still not at ${binPath}`
|
|
3076
|
+
};
|
|
3077
|
+
}
|
|
3078
|
+
return { installed: true };
|
|
3079
|
+
} catch (err) {
|
|
3080
|
+
return {
|
|
3081
|
+
installed: false,
|
|
3082
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3083
|
+
};
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
function runNpmInstall(cwd, pkg2) {
|
|
3087
|
+
return new Promise((resolve4, reject) => {
|
|
3088
|
+
const child = spawn3("npm", ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
|
|
3089
|
+
cwd,
|
|
3090
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3091
|
+
env: { ...process.env }
|
|
3092
|
+
});
|
|
3093
|
+
let stderr = "";
|
|
3094
|
+
child.stderr.on("data", (chunk) => {
|
|
3095
|
+
stderr += chunk.toString();
|
|
3096
|
+
});
|
|
3097
|
+
child.on("error", (err) => reject(err));
|
|
3098
|
+
child.on("close", (code) => {
|
|
3099
|
+
if (code === 0) {
|
|
3100
|
+
resolve4();
|
|
3101
|
+
} else {
|
|
3102
|
+
reject(new Error(`npm install ${pkg2} exited ${(code ?? -1).toString()}: ${sanitizeNpmStderr(stderr)}`));
|
|
3103
|
+
}
|
|
3104
|
+
});
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
function sanitizeNpmStderr(raw) {
|
|
3108
|
+
const truncated = raw.split("\n").slice(0, 3).join(" ").trim();
|
|
3109
|
+
return truncated.replace(/(https?:\/\/)[^@\s/]*@/g, (_match, scheme) => `${scheme}<redacted>@`);
|
|
3110
|
+
}
|
|
3111
|
+
async function pathExists(p) {
|
|
3112
|
+
try {
|
|
3113
|
+
await fs.access(p);
|
|
3114
|
+
return true;
|
|
3115
|
+
} catch {
|
|
3116
|
+
return false;
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
async function detectRepoLanguages(repoRoot) {
|
|
3120
|
+
const found = /* @__PURE__ */ new Set();
|
|
3121
|
+
let inspected = 0;
|
|
3122
|
+
const MAX_INSPECT = 200;
|
|
3123
|
+
async function inspect(dir) {
|
|
3124
|
+
if (inspected >= MAX_INSPECT)
|
|
3125
|
+
return;
|
|
3126
|
+
let entries;
|
|
3127
|
+
try {
|
|
3128
|
+
entries = await fs.readdir(dir);
|
|
3129
|
+
} catch {
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
for (const name of entries) {
|
|
3133
|
+
if (inspected >= MAX_INSPECT)
|
|
3001
3134
|
return;
|
|
3135
|
+
if (name.startsWith("."))
|
|
3136
|
+
continue;
|
|
3137
|
+
if (name === "node_modules" || name === "dist" || name === "build")
|
|
3138
|
+
continue;
|
|
3139
|
+
const lang = detectLanguage(name);
|
|
3140
|
+
if (lang) {
|
|
3141
|
+
found.add(lang);
|
|
3142
|
+
inspected += 1;
|
|
3002
3143
|
}
|
|
3003
|
-
this.emit("request", method, msg["params"], reqId);
|
|
3004
3144
|
}
|
|
3005
3145
|
}
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3146
|
+
await inspect(repoRoot);
|
|
3147
|
+
let topEntries = [];
|
|
3148
|
+
try {
|
|
3149
|
+
topEntries = await fs.readdir(repoRoot);
|
|
3150
|
+
} catch {
|
|
3151
|
+
return found;
|
|
3152
|
+
}
|
|
3153
|
+
for (const name of topEntries) {
|
|
3154
|
+
if (name.startsWith("."))
|
|
3155
|
+
continue;
|
|
3156
|
+
if (name === "node_modules" || name === "dist" || name === "build")
|
|
3157
|
+
continue;
|
|
3158
|
+
const sub = join14(repoRoot, name);
|
|
3159
|
+
try {
|
|
3160
|
+
const stat7 = await fs.stat(sub);
|
|
3161
|
+
if (stat7.isDirectory())
|
|
3162
|
+
await inspect(sub);
|
|
3163
|
+
} catch {
|
|
3011
3164
|
}
|
|
3012
|
-
this.pending.clear();
|
|
3013
3165
|
}
|
|
3014
|
-
|
|
3166
|
+
return found;
|
|
3167
|
+
}
|
|
3168
|
+
async function prepareLspServersForRepos(repos) {
|
|
3169
|
+
const detected = /* @__PURE__ */ new Set();
|
|
3170
|
+
for (const r of repos) {
|
|
3171
|
+
if (!r.localPath)
|
|
3172
|
+
continue;
|
|
3173
|
+
try {
|
|
3174
|
+
const stat7 = await fs.stat(r.localPath);
|
|
3175
|
+
if (!stat7.isDirectory())
|
|
3176
|
+
continue;
|
|
3177
|
+
} catch {
|
|
3178
|
+
continue;
|
|
3179
|
+
}
|
|
3180
|
+
try {
|
|
3181
|
+
const langs = await detectRepoLanguages(r.localPath);
|
|
3182
|
+
for (const l of langs)
|
|
3183
|
+
detected.add(l);
|
|
3184
|
+
} catch {
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
const results = [];
|
|
3188
|
+
for (const language of detected) {
|
|
3189
|
+
const configs = LSP_REGISTRY[language];
|
|
3190
|
+
const npmConfig = configs.find((c) => c.npmPackage);
|
|
3191
|
+
if (!npmConfig) {
|
|
3192
|
+
results.push({
|
|
3193
|
+
language,
|
|
3194
|
+
installed: false,
|
|
3195
|
+
alreadyPresent: false,
|
|
3196
|
+
skippedNoNpmPackage: true,
|
|
3197
|
+
hint: configs[0]?.installHint
|
|
3198
|
+
});
|
|
3199
|
+
continue;
|
|
3200
|
+
}
|
|
3201
|
+
const outcome = await ensureNpmLspInstalled(npmConfig);
|
|
3202
|
+
results.push({
|
|
3203
|
+
language,
|
|
3204
|
+
installed: outcome.installed,
|
|
3205
|
+
alreadyPresent: outcome.skipped === "already-present",
|
|
3206
|
+
skippedNoNpmPackage: false,
|
|
3207
|
+
error: outcome.error
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
3210
|
+
return results;
|
|
3211
|
+
}
|
|
3015
3212
|
|
|
3016
3213
|
// ../listener/dist/lsp/workspace-session.js
|
|
3017
3214
|
function keyOf(args) {
|
|
@@ -3027,10 +3224,14 @@ var WorkspaceManager = class {
|
|
|
3027
3224
|
this.idleTimeoutMs = opts.idleTimeoutMs ?? 10 * 60 * 1e3;
|
|
3028
3225
|
this.maxSessions = opts.maxSessions ?? 32;
|
|
3029
3226
|
this.clientFactory = opts.clientFactory ?? ((config2, cwd) => {
|
|
3227
|
+
const env = { ...process.env };
|
|
3228
|
+
const lspBin = getLspBinDir();
|
|
3229
|
+
env.PATH = env.PATH ? `${env.PATH}:${lspBin}` : lspBin;
|
|
3030
3230
|
const client = new LspClient({
|
|
3031
3231
|
command: config2.command,
|
|
3032
3232
|
args: config2.args,
|
|
3033
|
-
cwd
|
|
3233
|
+
cwd,
|
|
3234
|
+
env
|
|
3034
3235
|
});
|
|
3035
3236
|
client.on("stderr", (line) => {
|
|
3036
3237
|
process.stderr.write(`[lsp:${config2.id}] ${line}`);
|
|
@@ -3358,7 +3559,7 @@ var PathEscapeError = class extends Error {
|
|
|
3358
3559
|
// ../listener/dist/mcp/graph-cache.js
|
|
3359
3560
|
init_config_dir();
|
|
3360
3561
|
import { readFile as readFile8, stat as stat3 } from "fs/promises";
|
|
3361
|
-
import { join as
|
|
3562
|
+
import { join as join16 } from "path";
|
|
3362
3563
|
var EVICT_DEBOUNCE_MS = 6e4;
|
|
3363
3564
|
function assertSafeRepoId(repoId) {
|
|
3364
3565
|
if (!repoId || typeof repoId !== "string") {
|
|
@@ -3373,7 +3574,7 @@ function assertSafeRepoId(repoId) {
|
|
|
3373
3574
|
}
|
|
3374
3575
|
function createGraphCache(options = {}) {
|
|
3375
3576
|
const evictMs = options.evictDebounceMs ?? EVICT_DEBOUNCE_MS;
|
|
3376
|
-
const resolvePath = options.resolveGraphPath ?? ((repoId) =>
|
|
3577
|
+
const resolvePath = options.resolveGraphPath ?? ((repoId) => join16(defaultRepoWiseHome(), "graphs", `${repoId}.json`));
|
|
3377
3578
|
const entries = /* @__PURE__ */ new Map();
|
|
3378
3579
|
const inFlight = /* @__PURE__ */ new Map();
|
|
3379
3580
|
async function loadFromDisk(repoId) {
|
|
@@ -3882,7 +4083,7 @@ async function isLocallyConsented(flagPath2) {
|
|
|
3882
4083
|
init_config_dir();
|
|
3883
4084
|
import { createServer } from "http";
|
|
3884
4085
|
import { mkdir as mkdir13, writeFile as writeFile13 } from "fs/promises";
|
|
3885
|
-
import { dirname as dirname10, join as
|
|
4086
|
+
import { dirname as dirname10, join as join17 } from "path";
|
|
3886
4087
|
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
3887
4088
|
|
|
3888
4089
|
// ../listener/dist/mcp/sanitize.js
|
|
@@ -5235,7 +5436,7 @@ function extractBearer(header) {
|
|
|
5235
5436
|
return m ? m[1] : null;
|
|
5236
5437
|
}
|
|
5237
5438
|
function defaultEndpointFile() {
|
|
5238
|
-
return
|
|
5439
|
+
return join17(getConfigDir(), "listener.endpoint");
|
|
5239
5440
|
}
|
|
5240
5441
|
|
|
5241
5442
|
// ../listener/dist/mcp/bootstrap.js
|
|
@@ -5243,7 +5444,7 @@ async function startMcp(options) {
|
|
|
5243
5444
|
const disabled = process.env.REPOWISE_MCP_DISABLED === "true";
|
|
5244
5445
|
const graphsDir = options.graphsDir ?? defaultGraphsDir();
|
|
5245
5446
|
const graphCache = createGraphCache({
|
|
5246
|
-
resolveGraphPath: (repoId) =>
|
|
5447
|
+
resolveGraphPath: (repoId) => join18(graphsDir, `${repoId}.json`)
|
|
5247
5448
|
});
|
|
5248
5449
|
if (disabled) {
|
|
5249
5450
|
return {
|
|
@@ -5266,11 +5467,11 @@ async function startMcp(options) {
|
|
|
5266
5467
|
}
|
|
5267
5468
|
const firstRepoLocal = options.repos.find((r) => r.localPath)?.localPath;
|
|
5268
5469
|
const mcpHome = defaultMcpHome();
|
|
5269
|
-
const logFilePath =
|
|
5270
|
-
const keyStore = createFileKeyStore(
|
|
5470
|
+
const logFilePath = join18(mcpHome, "mcp-log.jsonl.enc");
|
|
5471
|
+
const keyStore = createFileKeyStore(join18(mcpHome, "mcp-log.key"));
|
|
5271
5472
|
const mcpLogger = createMcpLogger({ filePath: logFilePath, keyStore });
|
|
5272
|
-
const flagFilePath =
|
|
5273
|
-
const watermarkFilePath =
|
|
5473
|
+
const flagFilePath = join18(mcpHome, "mcp-log.flag");
|
|
5474
|
+
const watermarkFilePath = join18(mcpHome, "mcp-log.watermark");
|
|
5274
5475
|
let uploader = null;
|
|
5275
5476
|
let lastConsentState = false;
|
|
5276
5477
|
function ensureUploader() {
|
|
@@ -5314,7 +5515,7 @@ async function startMcp(options) {
|
|
|
5314
5515
|
apiBaseUrl: repo.apiUrl,
|
|
5315
5516
|
getAuthToken: options.getAuthToken,
|
|
5316
5517
|
repoId: repo.repoId,
|
|
5317
|
-
targetPath:
|
|
5518
|
+
targetPath: join18(graphsDir, `${repo.repoId}.json`),
|
|
5318
5519
|
graphCache,
|
|
5319
5520
|
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
|
|
5320
5521
|
}));
|
|
@@ -5390,7 +5591,7 @@ async function startMcp(options) {
|
|
|
5390
5591
|
apiBaseUrl: repo.apiUrl,
|
|
5391
5592
|
getAuthToken: options.getAuthToken,
|
|
5392
5593
|
repoId: repo.repoId,
|
|
5393
|
-
targetPath:
|
|
5594
|
+
targetPath: join18(graphsDir, `${repo.repoId}.json`),
|
|
5394
5595
|
graphCache,
|
|
5395
5596
|
...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
|
|
5396
5597
|
}));
|
|
@@ -5411,12 +5612,389 @@ async function startMcp(options) {
|
|
|
5411
5612
|
};
|
|
5412
5613
|
}
|
|
5413
5614
|
function defaultGraphsDir() {
|
|
5414
|
-
return
|
|
5615
|
+
return join18(getConfigDir(), "graphs");
|
|
5415
5616
|
}
|
|
5416
5617
|
function defaultMcpHome() {
|
|
5417
5618
|
return getConfigDir();
|
|
5418
5619
|
}
|
|
5419
5620
|
|
|
5621
|
+
// ../listener/dist/mcp/auto-config/index.js
|
|
5622
|
+
import { homedir as homedir4 } from "os";
|
|
5623
|
+
|
|
5624
|
+
// ../listener/dist/mcp/auto-config/writers/claude-code.js
|
|
5625
|
+
import { promises as fs3 } from "fs";
|
|
5626
|
+
import { join as join19 } from "path";
|
|
5627
|
+
|
|
5628
|
+
// ../listener/dist/mcp/auto-config/markers.js
|
|
5629
|
+
import { promises as fs2 } from "fs";
|
|
5630
|
+
import { dirname as dirname11 } from "path";
|
|
5631
|
+
async function writeMergedConfig(params) {
|
|
5632
|
+
const current = await readConfig(params.path);
|
|
5633
|
+
const servers = { ...current.mcpServers ?? {} };
|
|
5634
|
+
servers[params.serverName] = params.spec;
|
|
5635
|
+
const next = { ...current, mcpServers: servers };
|
|
5636
|
+
const canonical = canonicalize(next);
|
|
5637
|
+
const existing = safeExistingContent(params.path, current);
|
|
5638
|
+
if (existing === canonical)
|
|
5639
|
+
return "unchanged";
|
|
5640
|
+
await fs2.mkdir(dirname11(params.path), { recursive: true });
|
|
5641
|
+
await fs2.writeFile(params.path, canonical, "utf-8");
|
|
5642
|
+
return "written";
|
|
5643
|
+
}
|
|
5644
|
+
async function removeFromConfig(path, serverName) {
|
|
5645
|
+
const current = await readConfig(path);
|
|
5646
|
+
if (!current.mcpServers || !(serverName in current.mcpServers))
|
|
5647
|
+
return;
|
|
5648
|
+
const servers = { ...current.mcpServers };
|
|
5649
|
+
delete servers[serverName];
|
|
5650
|
+
const next = { ...current, mcpServers: servers };
|
|
5651
|
+
if (Object.keys(servers).length === 0) {
|
|
5652
|
+
delete next.mcpServers;
|
|
5653
|
+
}
|
|
5654
|
+
await fs2.writeFile(path, canonicalize(next), "utf-8");
|
|
5655
|
+
}
|
|
5656
|
+
async function readConfig(path) {
|
|
5657
|
+
try {
|
|
5658
|
+
const raw = await fs2.readFile(path, "utf-8");
|
|
5659
|
+
return JSON.parse(raw);
|
|
5660
|
+
} catch (err) {
|
|
5661
|
+
if (err.code === "ENOENT")
|
|
5662
|
+
return {};
|
|
5663
|
+
throw err;
|
|
5664
|
+
}
|
|
5665
|
+
}
|
|
5666
|
+
function canonicalize(config2) {
|
|
5667
|
+
const serversSorted = {};
|
|
5668
|
+
if (config2.mcpServers) {
|
|
5669
|
+
const keys = Object.keys(config2.mcpServers).sort();
|
|
5670
|
+
for (const k of keys)
|
|
5671
|
+
serversSorted[k] = normalizeSpec(config2.mcpServers[k]);
|
|
5672
|
+
}
|
|
5673
|
+
const top = {};
|
|
5674
|
+
const otherKeys = Object.keys(config2).filter((k) => k !== "mcpServers").sort();
|
|
5675
|
+
for (const k of otherKeys)
|
|
5676
|
+
top[k] = config2[k];
|
|
5677
|
+
if (Object.keys(serversSorted).length > 0)
|
|
5678
|
+
top.mcpServers = serversSorted;
|
|
5679
|
+
return JSON.stringify(top, null, 2) + "\n";
|
|
5680
|
+
}
|
|
5681
|
+
function normalizeSpec(spec) {
|
|
5682
|
+
const out = { command: spec.command };
|
|
5683
|
+
if (spec.args && spec.args.length > 0)
|
|
5684
|
+
out.args = [...spec.args];
|
|
5685
|
+
if (spec.env && Object.keys(spec.env).length > 0) {
|
|
5686
|
+
const sorted = {};
|
|
5687
|
+
for (const k of Object.keys(spec.env).sort())
|
|
5688
|
+
sorted[k] = spec.env[k];
|
|
5689
|
+
out.env = sorted;
|
|
5690
|
+
}
|
|
5691
|
+
return out;
|
|
5692
|
+
}
|
|
5693
|
+
function safeExistingContent(path, current) {
|
|
5694
|
+
try {
|
|
5695
|
+
return canonicalize(current);
|
|
5696
|
+
} catch {
|
|
5697
|
+
return null;
|
|
5698
|
+
}
|
|
5699
|
+
void path;
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5702
|
+
// ../listener/dist/mcp/auto-config/writers/claude-code.js
|
|
5703
|
+
var claudeCodeWriter = {
|
|
5704
|
+
tool: "claude-code",
|
|
5705
|
+
async detect(repoRoot, home) {
|
|
5706
|
+
return await fileExists2(join19(repoRoot, "CLAUDE.md")) || await hasClaudeBinary(home);
|
|
5707
|
+
},
|
|
5708
|
+
async write(ctx) {
|
|
5709
|
+
const path = join19(ctx.repoRoot, ".mcp.json");
|
|
5710
|
+
const status2 = await writeMergedConfig({
|
|
5711
|
+
path,
|
|
5712
|
+
serverName: `repowise-${ctx.repoId}`,
|
|
5713
|
+
spec: { command: ctx.shimCmd, args: ["--repo-id", ctx.repoId] }
|
|
5714
|
+
});
|
|
5715
|
+
return { status: status2, path };
|
|
5716
|
+
},
|
|
5717
|
+
async remove(ctx) {
|
|
5718
|
+
await removeFromConfig(join19(ctx.repoRoot, ".mcp.json"), `repowise-${ctx.repoId}`);
|
|
5719
|
+
}
|
|
5720
|
+
};
|
|
5721
|
+
async function fileExists2(path) {
|
|
5722
|
+
try {
|
|
5723
|
+
await fs3.access(path);
|
|
5724
|
+
return true;
|
|
5725
|
+
} catch {
|
|
5726
|
+
return false;
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
async function hasClaudeBinary(home) {
|
|
5730
|
+
return fileExists2(join19(home, ".claude", "claude.json"));
|
|
5731
|
+
}
|
|
5732
|
+
|
|
5733
|
+
// ../listener/dist/mcp/auto-config/writers/cline.js
|
|
5734
|
+
import { promises as fs4 } from "fs";
|
|
5735
|
+
import { join as join20 } from "path";
|
|
5736
|
+
var clineWriter = {
|
|
5737
|
+
tool: "cline",
|
|
5738
|
+
async detect(repoRoot, home) {
|
|
5739
|
+
return await fileExists3(join20(repoRoot, ".clinerules")) || await fileExists3(join20(home, ".cline"));
|
|
5740
|
+
},
|
|
5741
|
+
async write(ctx) {
|
|
5742
|
+
const path = join20(ctx.home, ".cline", "mcp.json");
|
|
5743
|
+
const status2 = await writeMergedConfig({
|
|
5744
|
+
path,
|
|
5745
|
+
serverName: `repowise-${ctx.repoId}`,
|
|
5746
|
+
spec: { command: ctx.shimCmd, args: ["--repo-id", ctx.repoId] }
|
|
5747
|
+
});
|
|
5748
|
+
return { status: status2, path };
|
|
5749
|
+
},
|
|
5750
|
+
async remove(ctx) {
|
|
5751
|
+
await removeFromConfig(join20(ctx.home, ".cline", "mcp.json"), `repowise-${ctx.repoId}`);
|
|
5752
|
+
}
|
|
5753
|
+
};
|
|
5754
|
+
async function fileExists3(path) {
|
|
5755
|
+
try {
|
|
5756
|
+
await fs4.access(path);
|
|
5757
|
+
return true;
|
|
5758
|
+
} catch {
|
|
5759
|
+
return false;
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
5762
|
+
|
|
5763
|
+
// ../listener/dist/mcp/auto-config/writers/codex.js
|
|
5764
|
+
import { promises as fs5 } from "fs";
|
|
5765
|
+
import { join as join21 } from "path";
|
|
5766
|
+
var codexWriter = {
|
|
5767
|
+
tool: "codex",
|
|
5768
|
+
async detect(_repoRoot, home) {
|
|
5769
|
+
return fileExists4(join21(home, ".codex"));
|
|
5770
|
+
},
|
|
5771
|
+
async write(ctx) {
|
|
5772
|
+
const path = join21(ctx.home, ".codex", "mcp.json");
|
|
5773
|
+
const status2 = await writeMergedConfig({
|
|
5774
|
+
path,
|
|
5775
|
+
serverName: `repowise-${ctx.repoId}`,
|
|
5776
|
+
spec: { command: ctx.shimCmd, args: ["--repo-id", ctx.repoId] }
|
|
5777
|
+
});
|
|
5778
|
+
return { status: status2, path };
|
|
5779
|
+
},
|
|
5780
|
+
async remove(ctx) {
|
|
5781
|
+
await removeFromConfig(join21(ctx.home, ".codex", "mcp.json"), `repowise-${ctx.repoId}`);
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5784
|
+
async function fileExists4(path) {
|
|
5785
|
+
try {
|
|
5786
|
+
await fs5.access(path);
|
|
5787
|
+
return true;
|
|
5788
|
+
} catch {
|
|
5789
|
+
return false;
|
|
5790
|
+
}
|
|
5791
|
+
}
|
|
5792
|
+
|
|
5793
|
+
// ../listener/dist/mcp/auto-config/writers/copilot.js
|
|
5794
|
+
import { promises as fs6 } from "fs";
|
|
5795
|
+
import { join as join22 } from "path";
|
|
5796
|
+
var copilotWriter = {
|
|
5797
|
+
tool: "copilot",
|
|
5798
|
+
async detect(repoRoot) {
|
|
5799
|
+
return fileExists5(join22(repoRoot, ".vscode"));
|
|
5800
|
+
},
|
|
5801
|
+
async write(ctx) {
|
|
5802
|
+
const path = join22(ctx.repoRoot, ".vscode", "mcp.json");
|
|
5803
|
+
const status2 = await writeMergedConfig({
|
|
5804
|
+
path,
|
|
5805
|
+
serverName: `repowise-${ctx.repoId}`,
|
|
5806
|
+
spec: { command: ctx.shimCmd, args: ["--repo-id", ctx.repoId] }
|
|
5807
|
+
});
|
|
5808
|
+
return { status: status2, path };
|
|
5809
|
+
},
|
|
5810
|
+
async remove(ctx) {
|
|
5811
|
+
await removeFromConfig(join22(ctx.repoRoot, ".vscode", "mcp.json"), `repowise-${ctx.repoId}`);
|
|
5812
|
+
}
|
|
5813
|
+
};
|
|
5814
|
+
async function fileExists5(path) {
|
|
5815
|
+
try {
|
|
5816
|
+
await fs6.access(path);
|
|
5817
|
+
return true;
|
|
5818
|
+
} catch {
|
|
5819
|
+
return false;
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
|
|
5823
|
+
// ../listener/dist/mcp/auto-config/writers/cursor.js
|
|
5824
|
+
import { promises as fs7 } from "fs";
|
|
5825
|
+
import { join as join23 } from "path";
|
|
5826
|
+
var cursorWriter = {
|
|
5827
|
+
tool: "cursor",
|
|
5828
|
+
async detect(repoRoot) {
|
|
5829
|
+
return await fileExists6(join23(repoRoot, ".cursor")) || await fileExists6(join23(repoRoot, ".cursorrules"));
|
|
5830
|
+
},
|
|
5831
|
+
async write(ctx) {
|
|
5832
|
+
const path = join23(ctx.repoRoot, ".cursor", "mcp.json");
|
|
5833
|
+
const status2 = await writeMergedConfig({
|
|
5834
|
+
path,
|
|
5835
|
+
serverName: `repowise-${ctx.repoId}`,
|
|
5836
|
+
spec: { command: ctx.shimCmd, args: ["--repo-id", ctx.repoId] }
|
|
5837
|
+
});
|
|
5838
|
+
return { status: status2, path };
|
|
5839
|
+
},
|
|
5840
|
+
async remove(ctx) {
|
|
5841
|
+
await removeFromConfig(join23(ctx.repoRoot, ".cursor", "mcp.json"), `repowise-${ctx.repoId}`);
|
|
5842
|
+
}
|
|
5843
|
+
};
|
|
5844
|
+
async function fileExists6(path) {
|
|
5845
|
+
try {
|
|
5846
|
+
await fs7.access(path);
|
|
5847
|
+
return true;
|
|
5848
|
+
} catch {
|
|
5849
|
+
return false;
|
|
5850
|
+
}
|
|
5851
|
+
}
|
|
5852
|
+
|
|
5853
|
+
// ../listener/dist/mcp/auto-config/writers/gemini-cli.js
|
|
5854
|
+
import { promises as fs8 } from "fs";
|
|
5855
|
+
import { join as join24 } from "path";
|
|
5856
|
+
var geminiCliWriter = {
|
|
5857
|
+
tool: "gemini-cli",
|
|
5858
|
+
async detect(_repoRoot, home) {
|
|
5859
|
+
return fileExists7(join24(home, ".gemini"));
|
|
5860
|
+
},
|
|
5861
|
+
async write(ctx) {
|
|
5862
|
+
const path = join24(ctx.home, ".gemini", "settings.json");
|
|
5863
|
+
const status2 = await writeMergedConfig({
|
|
5864
|
+
path,
|
|
5865
|
+
serverName: `repowise-${ctx.repoId}`,
|
|
5866
|
+
spec: { command: ctx.shimCmd, args: ["--repo-id", ctx.repoId] }
|
|
5867
|
+
});
|
|
5868
|
+
return { status: status2, path };
|
|
5869
|
+
},
|
|
5870
|
+
async remove(ctx) {
|
|
5871
|
+
await removeFromConfig(join24(ctx.home, ".gemini", "settings.json"), `repowise-${ctx.repoId}`);
|
|
5872
|
+
}
|
|
5873
|
+
};
|
|
5874
|
+
async function fileExists7(path) {
|
|
5875
|
+
try {
|
|
5876
|
+
await fs8.access(path);
|
|
5877
|
+
return true;
|
|
5878
|
+
} catch {
|
|
5879
|
+
return false;
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
|
|
5883
|
+
// ../listener/dist/mcp/auto-config/writers/roo.js
|
|
5884
|
+
import { promises as fs9 } from "fs";
|
|
5885
|
+
import { join as join25 } from "path";
|
|
5886
|
+
var rooWriter = {
|
|
5887
|
+
tool: "roo",
|
|
5888
|
+
async detect(repoRoot, home) {
|
|
5889
|
+
return await fileExists8(join25(repoRoot, ".roo")) || await fileExists8(join25(home, ".roo"));
|
|
5890
|
+
},
|
|
5891
|
+
async write(ctx) {
|
|
5892
|
+
const repoConfig = join25(ctx.repoRoot, ".roo", "mcp.json");
|
|
5893
|
+
const useRepoScope = await fileExists8(join25(ctx.repoRoot, ".roo"));
|
|
5894
|
+
const path = useRepoScope ? repoConfig : join25(ctx.home, ".roo", "mcp.json");
|
|
5895
|
+
const status2 = await writeMergedConfig({
|
|
5896
|
+
path,
|
|
5897
|
+
serverName: `repowise-${ctx.repoId}`,
|
|
5898
|
+
spec: { command: ctx.shimCmd, args: ["--repo-id", ctx.repoId] }
|
|
5899
|
+
});
|
|
5900
|
+
return { status: status2, path };
|
|
5901
|
+
},
|
|
5902
|
+
async remove(ctx) {
|
|
5903
|
+
await removeFromConfig(join25(ctx.repoRoot, ".roo", "mcp.json"), `repowise-${ctx.repoId}`);
|
|
5904
|
+
await removeFromConfig(join25(ctx.home, ".roo", "mcp.json"), `repowise-${ctx.repoId}`);
|
|
5905
|
+
}
|
|
5906
|
+
};
|
|
5907
|
+
async function fileExists8(path) {
|
|
5908
|
+
try {
|
|
5909
|
+
await fs9.access(path);
|
|
5910
|
+
return true;
|
|
5911
|
+
} catch {
|
|
5912
|
+
return false;
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
|
|
5916
|
+
// ../listener/dist/mcp/auto-config/writers/windsurf.js
|
|
5917
|
+
import { promises as fs10 } from "fs";
|
|
5918
|
+
import { join as join26 } from "path";
|
|
5919
|
+
var windsurfWriter = {
|
|
5920
|
+
tool: "windsurf",
|
|
5921
|
+
async detect(_repoRoot, home) {
|
|
5922
|
+
return fileExists9(join26(home, ".codeium", "windsurf"));
|
|
5923
|
+
},
|
|
5924
|
+
async write(ctx) {
|
|
5925
|
+
const path = join26(ctx.home, ".codeium", "windsurf", "mcp_config.json");
|
|
5926
|
+
const status2 = await writeMergedConfig({
|
|
5927
|
+
path,
|
|
5928
|
+
serverName: `repowise-${ctx.repoId}`,
|
|
5929
|
+
spec: { command: ctx.shimCmd, args: ["--repo-id", ctx.repoId] }
|
|
5930
|
+
});
|
|
5931
|
+
return { status: status2, path };
|
|
5932
|
+
},
|
|
5933
|
+
async remove(ctx) {
|
|
5934
|
+
await removeFromConfig(join26(ctx.home, ".codeium", "windsurf", "mcp_config.json"), `repowise-${ctx.repoId}`);
|
|
5935
|
+
}
|
|
5936
|
+
};
|
|
5937
|
+
async function fileExists9(path) {
|
|
5938
|
+
try {
|
|
5939
|
+
await fs10.access(path);
|
|
5940
|
+
return true;
|
|
5941
|
+
} catch {
|
|
5942
|
+
return false;
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5945
|
+
|
|
5946
|
+
// ../listener/dist/mcp/auto-config/writers/index.js
|
|
5947
|
+
var WRITERS = [
|
|
5948
|
+
claudeCodeWriter,
|
|
5949
|
+
clineWriter,
|
|
5950
|
+
codexWriter,
|
|
5951
|
+
copilotWriter,
|
|
5952
|
+
cursorWriter,
|
|
5953
|
+
geminiCliWriter,
|
|
5954
|
+
rooWriter,
|
|
5955
|
+
windsurfWriter
|
|
5956
|
+
];
|
|
5957
|
+
|
|
5958
|
+
// ../listener/dist/mcp/auto-config/index.js
|
|
5959
|
+
async function runAutoConfig(opts) {
|
|
5960
|
+
const home = opts.home ?? homedir4();
|
|
5961
|
+
const writers = opts.writers ?? WRITERS;
|
|
5962
|
+
const results = [];
|
|
5963
|
+
for (const writer of writers) {
|
|
5964
|
+
let detected;
|
|
5965
|
+
try {
|
|
5966
|
+
detected = await writer.detect(opts.repoRoot, home);
|
|
5967
|
+
} catch (err) {
|
|
5968
|
+
results.push({
|
|
5969
|
+
tool: writer.tool,
|
|
5970
|
+
detected: false,
|
|
5971
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5972
|
+
});
|
|
5973
|
+
continue;
|
|
5974
|
+
}
|
|
5975
|
+
if (!detected) {
|
|
5976
|
+
results.push({ tool: writer.tool, detected: false });
|
|
5977
|
+
continue;
|
|
5978
|
+
}
|
|
5979
|
+
try {
|
|
5980
|
+
const outcome = await writer.write({
|
|
5981
|
+
repoRoot: opts.repoRoot,
|
|
5982
|
+
repoId: opts.repoId,
|
|
5983
|
+
shimCmd: opts.shimCmd,
|
|
5984
|
+
home
|
|
5985
|
+
});
|
|
5986
|
+
results.push({ tool: writer.tool, detected: true, outcome });
|
|
5987
|
+
} catch (err) {
|
|
5988
|
+
results.push({
|
|
5989
|
+
tool: writer.tool,
|
|
5990
|
+
detected: true,
|
|
5991
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5992
|
+
});
|
|
5993
|
+
}
|
|
5994
|
+
}
|
|
5995
|
+
return results;
|
|
5996
|
+
}
|
|
5997
|
+
|
|
5420
5998
|
// ../listener/dist/typed-resolution/resolver-loop.js
|
|
5421
5999
|
init_src();
|
|
5422
6000
|
init_registry();
|
|
@@ -5621,7 +6199,7 @@ var CRASH_LOOP_WINDOW_MS = 3e4;
|
|
|
5621
6199
|
var CRASH_LOOP_THRESHOLD = 3;
|
|
5622
6200
|
async function readRawToolConfig() {
|
|
5623
6201
|
try {
|
|
5624
|
-
const configPath =
|
|
6202
|
+
const configPath = join27(getConfigDir(), "config.json");
|
|
5625
6203
|
const data = await readFile12(configPath, "utf-8");
|
|
5626
6204
|
const raw = JSON.parse(data);
|
|
5627
6205
|
return {
|
|
@@ -5682,11 +6260,11 @@ function resolveAuditRoots() {
|
|
|
5682
6260
|
return override.split(":").map((s) => s.trim()).filter(Boolean);
|
|
5683
6261
|
}
|
|
5684
6262
|
const out = /* @__PURE__ */ new Set();
|
|
5685
|
-
let dir =
|
|
6263
|
+
let dir = dirname12(process.execPath);
|
|
5686
6264
|
for (let i = 0; i < 8; i += 1) {
|
|
5687
|
-
out.add(
|
|
5688
|
-
out.add(
|
|
5689
|
-
const parent =
|
|
6265
|
+
out.add(join27(dir, "node_modules"));
|
|
6266
|
+
out.add(join27(dir, "lib", "node_modules"));
|
|
6267
|
+
const parent = dirname12(dir);
|
|
5690
6268
|
if (parent === dir)
|
|
5691
6269
|
break;
|
|
5692
6270
|
dir = parent;
|
|
@@ -5905,7 +6483,7 @@ async function checkStaleContext(repos, state, groups) {
|
|
|
5905
6483
|
if (group?.offline.isOffline)
|
|
5906
6484
|
continue;
|
|
5907
6485
|
const { statSync: statSync2, readdirSync: readdirSync2 } = await import("fs");
|
|
5908
|
-
const contextPath =
|
|
6486
|
+
const contextPath = join27(repo.localPath, "repowise-context");
|
|
5909
6487
|
let isMissingOrEmpty = false;
|
|
5910
6488
|
try {
|
|
5911
6489
|
const s = statSync2(contextPath);
|
|
@@ -5934,9 +6512,36 @@ async function checkStaleContext(repos, state, groups) {
|
|
|
5934
6512
|
}
|
|
5935
6513
|
return dirty;
|
|
5936
6514
|
}
|
|
6515
|
+
async function reconcileMcpConfigs(repos, packageName) {
|
|
6516
|
+
const shimCmd = packageName;
|
|
6517
|
+
for (const repo of repos) {
|
|
6518
|
+
if (!repo.localPath)
|
|
6519
|
+
continue;
|
|
6520
|
+
try {
|
|
6521
|
+
const s = await fsStat(repo.localPath);
|
|
6522
|
+
if (!s.isDirectory())
|
|
6523
|
+
continue;
|
|
6524
|
+
} catch {
|
|
6525
|
+
continue;
|
|
6526
|
+
}
|
|
6527
|
+
try {
|
|
6528
|
+
const results = await runAutoConfig({
|
|
6529
|
+
repoRoot: repo.localPath,
|
|
6530
|
+
repoId: repo.repoId,
|
|
6531
|
+
shimCmd
|
|
6532
|
+
});
|
|
6533
|
+
const written = results.filter((r) => r.detected && r.outcome?.status === "written");
|
|
6534
|
+
if (written.length > 0) {
|
|
6535
|
+
console.log(`[mcp-config] Wrote ${written.length.toString()} config(s) for ${repo.repoId}: ${written.map((r) => `${r.tool}=${r.outcome.path}`).join(", ")}`);
|
|
6536
|
+
}
|
|
6537
|
+
} catch (err) {
|
|
6538
|
+
console.warn(`[mcp-config] Reconcile failed for ${repo.repoId}:`, err instanceof Error ? err.message : String(err));
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6541
|
+
}
|
|
5937
6542
|
async function reconcileAgentInstructions(repos) {
|
|
5938
6543
|
for (const repo of repos) {
|
|
5939
|
-
const path =
|
|
6544
|
+
const path = join27(repo.localPath, "repowise-context", "project-overview.md");
|
|
5940
6545
|
let content;
|
|
5941
6546
|
try {
|
|
5942
6547
|
content = await readFile12(path, "utf-8");
|
|
@@ -5998,7 +6603,7 @@ async function startListener() {
|
|
|
5998
6603
|
}
|
|
5999
6604
|
const configDir = getConfigDir();
|
|
6000
6605
|
await mkdir14(configDir, { recursive: true });
|
|
6001
|
-
const lockPath =
|
|
6606
|
+
const lockPath = join27(configDir, "listener.lock");
|
|
6002
6607
|
await writeFile14(lockPath, "", { flag: "a" });
|
|
6003
6608
|
let lockIsHeld = false;
|
|
6004
6609
|
try {
|
|
@@ -6041,7 +6646,7 @@ async function startListener() {
|
|
|
6041
6646
|
return;
|
|
6042
6647
|
}
|
|
6043
6648
|
if (config2.repos.length === 0 && !config2.autoDiscoverRepos) {
|
|
6044
|
-
console.error(`No repos configured. Add repos to ${
|
|
6649
|
+
console.error(`No repos configured. Add repos to ${join27(configDir, "config.json")}`);
|
|
6045
6650
|
await releaseLockAndExit();
|
|
6046
6651
|
process.exitCode = 1;
|
|
6047
6652
|
return;
|
|
@@ -6108,8 +6713,8 @@ async function startListener() {
|
|
|
6108
6713
|
const packageName = true ? "repowisestage" : "repowise";
|
|
6109
6714
|
let currentVersion = "";
|
|
6110
6715
|
try {
|
|
6111
|
-
const selfDir =
|
|
6112
|
-
const pkgJsonPath =
|
|
6716
|
+
const selfDir = dirname12(fileURLToPath3(import.meta.url));
|
|
6717
|
+
const pkgJsonPath = join27(selfDir, "..", "..", "package.json");
|
|
6113
6718
|
const pkgJson = JSON.parse(await readFile12(pkgJsonPath, "utf-8"));
|
|
6114
6719
|
currentVersion = pkgJson.version;
|
|
6115
6720
|
} catch (err) {
|
|
@@ -6162,6 +6767,29 @@ async function startListener() {
|
|
|
6162
6767
|
} catch (err) {
|
|
6163
6768
|
console.warn("[reconcile] Initial agent instructions reconciliation failed:", err instanceof Error ? err.message : String(err));
|
|
6164
6769
|
}
|
|
6770
|
+
void (async () => {
|
|
6771
|
+
try {
|
|
6772
|
+
await reconcileMcpConfigs(config2.repos, packageName);
|
|
6773
|
+
} catch (err) {
|
|
6774
|
+
console.warn("[mcp-config] Initial MCP config reconciliation failed:", err instanceof Error ? err.message : String(err));
|
|
6775
|
+
}
|
|
6776
|
+
})();
|
|
6777
|
+
void (async () => {
|
|
6778
|
+
try {
|
|
6779
|
+
const lspResults = await prepareLspServersForRepos(config2.repos);
|
|
6780
|
+
for (const r of lspResults) {
|
|
6781
|
+
if (r.installed) {
|
|
6782
|
+
console.log(`[lsp-install] Installed ${r.language} LSP server`);
|
|
6783
|
+
} else if (r.skippedNoNpmPackage && r.hint) {
|
|
6784
|
+
console.log(`[lsp-install] ${r.language} LSP not auto-installable. To enable: ${r.hint}`);
|
|
6785
|
+
} else if (r.error) {
|
|
6786
|
+
console.warn(`[lsp-install] ${r.language} install failed: ${r.error}`);
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
} catch (err) {
|
|
6790
|
+
console.warn("[lsp-install] Pre-install scan failed:", err instanceof Error ? err.message : String(err));
|
|
6791
|
+
}
|
|
6792
|
+
})();
|
|
6165
6793
|
const typedResolutionHooks = mcpRuntime?.lspWorkspaces ? buildTypedResolutionHooks({
|
|
6166
6794
|
workspaces: mcpRuntime.lspWorkspaces,
|
|
6167
6795
|
graphCache: mcpRuntime.graphCache,
|
|
@@ -6362,6 +6990,11 @@ async function startListener() {
|
|
|
6362
6990
|
} catch (err) {
|
|
6363
6991
|
console.warn("[reconcile] Agent instructions reconciliation failed:", err instanceof Error ? err.message : String(err));
|
|
6364
6992
|
}
|
|
6993
|
+
try {
|
|
6994
|
+
await reconcileMcpConfigs(config2.repos, packageName);
|
|
6995
|
+
} catch (err) {
|
|
6996
|
+
console.warn("[mcp-config] Reconciliation failed:", err instanceof Error ? err.message : String(err));
|
|
6997
|
+
}
|
|
6365
6998
|
}
|
|
6366
6999
|
if (latestCliVersion && currentVersion && !config2.noAutoUpdate && (state.crashCount ?? 0) < CRASH_LOOP_THRESHOLD) {
|
|
6367
7000
|
if (latestCliVersion !== state.lastUpdateTargetVersion) {
|
|
@@ -6425,12 +7058,12 @@ async function startListener() {
|
|
|
6425
7058
|
} catch {
|
|
6426
7059
|
}
|
|
6427
7060
|
}
|
|
6428
|
-
const credentialsPath =
|
|
7061
|
+
const credentialsPath = join27(getConfigDir(), "credentials.json");
|
|
6429
7062
|
let credentialsChanged = false;
|
|
6430
7063
|
let watcher = null;
|
|
6431
7064
|
try {
|
|
6432
|
-
const
|
|
6433
|
-
watcher =
|
|
7065
|
+
const fs18 = await import("fs");
|
|
7066
|
+
watcher = fs18.watch(credentialsPath, () => {
|
|
6434
7067
|
credentialsChanged = true;
|
|
6435
7068
|
});
|
|
6436
7069
|
} catch {
|
|
@@ -6470,8 +7103,8 @@ if (isDirectRun) {
|
|
|
6470
7103
|
}
|
|
6471
7104
|
|
|
6472
7105
|
// src/lib/env.ts
|
|
6473
|
-
import { homedir as
|
|
6474
|
-
import { join as
|
|
7106
|
+
import { homedir as homedir5 } from "os";
|
|
7107
|
+
import { join as join28 } from "path";
|
|
6475
7108
|
var IS_STAGING2 = true ? true : false;
|
|
6476
7109
|
var PRODUCTION = {
|
|
6477
7110
|
apiUrl: "https://api.repowise.ai",
|
|
@@ -6491,7 +7124,7 @@ function getEnvConfig() {
|
|
|
6491
7124
|
return IS_STAGING2 ? STAGING : PRODUCTION;
|
|
6492
7125
|
}
|
|
6493
7126
|
function getConfigDir2() {
|
|
6494
|
-
return
|
|
7127
|
+
return join28(homedir5(), IS_STAGING2 ? ".repowise-staging" : ".repowise");
|
|
6495
7128
|
}
|
|
6496
7129
|
function getPackageName() {
|
|
6497
7130
|
return true ? "repowisestage" : "repowise";
|
|
@@ -6502,11 +7135,11 @@ import chalk from "chalk";
|
|
|
6502
7135
|
|
|
6503
7136
|
// src/lib/config.ts
|
|
6504
7137
|
import { readFile as readFile13, writeFile as writeFile15, mkdir as mkdir15, rename as rename4, unlink as unlink9 } from "fs/promises";
|
|
6505
|
-
import { join as
|
|
7138
|
+
import { join as join29 } from "path";
|
|
6506
7139
|
import lockfile4 from "proper-lockfile";
|
|
6507
7140
|
async function getConfig() {
|
|
6508
7141
|
try {
|
|
6509
|
-
const data = await readFile13(
|
|
7142
|
+
const data = await readFile13(join29(getConfigDir2(), "config.json"), "utf-8");
|
|
6510
7143
|
return JSON.parse(data);
|
|
6511
7144
|
} catch {
|
|
6512
7145
|
return {};
|
|
@@ -6514,7 +7147,7 @@ async function getConfig() {
|
|
|
6514
7147
|
}
|
|
6515
7148
|
async function saveConfig(config2) {
|
|
6516
7149
|
const dir = getConfigDir2();
|
|
6517
|
-
const path =
|
|
7150
|
+
const path = join29(dir, "config.json");
|
|
6518
7151
|
await mkdir15(dir, { recursive: true });
|
|
6519
7152
|
const tmpPath = path + ".tmp";
|
|
6520
7153
|
try {
|
|
@@ -6530,7 +7163,7 @@ async function saveConfig(config2) {
|
|
|
6530
7163
|
}
|
|
6531
7164
|
async function mergeAndSaveConfig(updates) {
|
|
6532
7165
|
const dir = getConfigDir2();
|
|
6533
|
-
const path =
|
|
7166
|
+
const path = join29(dir, "config.json");
|
|
6534
7167
|
await mkdir15(dir, { recursive: true });
|
|
6535
7168
|
try {
|
|
6536
7169
|
await writeFile15(path, "", { flag: "a" });
|
|
@@ -6601,7 +7234,7 @@ async function showWelcome(currentVersion) {
|
|
|
6601
7234
|
|
|
6602
7235
|
// src/commands/create.ts
|
|
6603
7236
|
import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
6604
|
-
import { dirname as
|
|
7237
|
+
import { dirname as dirname13, join as join33 } from "path";
|
|
6605
7238
|
init_src();
|
|
6606
7239
|
import chalk5 from "chalk";
|
|
6607
7240
|
import ora from "ora";
|
|
@@ -6610,7 +7243,7 @@ import ora from "ora";
|
|
|
6610
7243
|
import { createHash as createHash3, randomBytes as randomBytes3 } from "crypto";
|
|
6611
7244
|
import { readFile as readFile14, writeFile as writeFile16, mkdir as mkdir16, chmod as chmod4, unlink as unlink10 } from "fs/promises";
|
|
6612
7245
|
import http from "http";
|
|
6613
|
-
import { join as
|
|
7246
|
+
import { join as join30 } from "path";
|
|
6614
7247
|
var CLI_CALLBACK_PORT = 19876;
|
|
6615
7248
|
var CALLBACK_TIMEOUT_MS = 12e4;
|
|
6616
7249
|
function getCognitoConfigForStorage() {
|
|
@@ -6776,7 +7409,7 @@ async function refreshTokens2(refreshToken) {
|
|
|
6776
7409
|
}
|
|
6777
7410
|
async function getStoredCredentials2() {
|
|
6778
7411
|
try {
|
|
6779
|
-
const credPath =
|
|
7412
|
+
const credPath = join30(getConfigDir2(), "credentials.json");
|
|
6780
7413
|
const data = await readFile14(credPath, "utf-8");
|
|
6781
7414
|
return JSON.parse(data);
|
|
6782
7415
|
} catch (err) {
|
|
@@ -6788,14 +7421,14 @@ async function getStoredCredentials2() {
|
|
|
6788
7421
|
}
|
|
6789
7422
|
async function storeCredentials2(credentials) {
|
|
6790
7423
|
const dir = getConfigDir2();
|
|
6791
|
-
const credPath =
|
|
7424
|
+
const credPath = join30(dir, "credentials.json");
|
|
6792
7425
|
await mkdir16(dir, { recursive: true, mode: 448 });
|
|
6793
7426
|
await writeFile16(credPath, JSON.stringify(credentials, null, 2));
|
|
6794
7427
|
await chmod4(credPath, 384);
|
|
6795
7428
|
}
|
|
6796
7429
|
async function clearCredentials() {
|
|
6797
7430
|
try {
|
|
6798
|
-
await unlink10(
|
|
7431
|
+
await unlink10(join30(getConfigDir2(), "credentials.json"));
|
|
6799
7432
|
} catch (err) {
|
|
6800
7433
|
if (err.code !== "ENOENT") throw err;
|
|
6801
7434
|
}
|
|
@@ -7002,10 +7635,10 @@ async function selectAiTools() {
|
|
|
7002
7635
|
|
|
7003
7636
|
// src/lib/ai-tools.ts
|
|
7004
7637
|
import { readFile as readFile15, writeFile as writeFile17, mkdir as mkdir17 } from "fs/promises";
|
|
7005
|
-
import { join as
|
|
7638
|
+
import { join as join31 } from "path";
|
|
7006
7639
|
var REPOWISE_HOOK_MARKER = "repowise-context";
|
|
7007
7640
|
async function writeClaudeSubagentHook(repoRoot, contextFolder) {
|
|
7008
|
-
const settingsPath =
|
|
7641
|
+
const settingsPath = join31(repoRoot, ".claude", "settings.json");
|
|
7009
7642
|
let settings = {};
|
|
7010
7643
|
try {
|
|
7011
7644
|
const raw = await readFile15(settingsPath, "utf-8");
|
|
@@ -7040,15 +7673,15 @@ async function writeClaudeSubagentHook(repoRoot, contextFolder) {
|
|
|
7040
7673
|
}
|
|
7041
7674
|
hooks["SubagentStart"] = subagentStart;
|
|
7042
7675
|
settings["hooks"] = hooks;
|
|
7043
|
-
await mkdir17(
|
|
7676
|
+
await mkdir17(join31(repoRoot, ".claude"), { recursive: true });
|
|
7044
7677
|
await writeFile17(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
7045
7678
|
}
|
|
7046
7679
|
|
|
7047
7680
|
// src/lib/gitignore.ts
|
|
7048
7681
|
import { readFileSync as readFileSync2, writeFileSync, existsSync } from "fs";
|
|
7049
|
-
import { join as
|
|
7682
|
+
import { join as join32 } from "path";
|
|
7050
7683
|
function ensureGitignore(repoRoot, entry) {
|
|
7051
|
-
const gitignorePath =
|
|
7684
|
+
const gitignorePath = join32(repoRoot, ".gitignore");
|
|
7052
7685
|
if (existsSync(gitignorePath)) {
|
|
7053
7686
|
const content = readFileSync2(gitignorePath, "utf-8");
|
|
7054
7687
|
const lines = content.split("\n").map((l) => l.trim());
|
|
@@ -7838,7 +8471,7 @@ async function create() {
|
|
|
7838
8471
|
const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
|
|
7839
8472
|
const files = listResult.data?.files ?? listResult.files ?? [];
|
|
7840
8473
|
if (files.length > 0) {
|
|
7841
|
-
const contextDir =
|
|
8474
|
+
const contextDir = join33(repoRoot, DEFAULT_CONTEXT_FOLDER);
|
|
7842
8475
|
mkdirSync(contextDir, { recursive: true });
|
|
7843
8476
|
let downloadedCount = 0;
|
|
7844
8477
|
let failedCount = 0;
|
|
@@ -7852,8 +8485,8 @@ async function create() {
|
|
|
7852
8485
|
const response = await fetch(presignedUrl);
|
|
7853
8486
|
if (response.ok) {
|
|
7854
8487
|
const content = await response.text();
|
|
7855
|
-
const filePath =
|
|
7856
|
-
mkdirSync(
|
|
8488
|
+
const filePath = join33(contextDir, file.fileName);
|
|
8489
|
+
mkdirSync(dirname13(filePath), { recursive: true });
|
|
7857
8490
|
writeFileSync2(filePath, content, "utf-8");
|
|
7858
8491
|
downloadedCount++;
|
|
7859
8492
|
} else {
|
|
@@ -7994,7 +8627,7 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
7994
8627
|
|
|
7995
8628
|
// src/commands/member.ts
|
|
7996
8629
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
7997
|
-
import { dirname as
|
|
8630
|
+
import { dirname as dirname14, join as join34, resolve, sep } from "path";
|
|
7998
8631
|
import chalk6 from "chalk";
|
|
7999
8632
|
import ora2 from "ora";
|
|
8000
8633
|
var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
|
|
@@ -8118,7 +8751,7 @@ async function member() {
|
|
|
8118
8751
|
spinner.succeed(`Found ${chalk6.bold(files.length)} context files on server`);
|
|
8119
8752
|
const { tools } = await selectAiTools();
|
|
8120
8753
|
spinner.start("Downloading context files...");
|
|
8121
|
-
const contextDir =
|
|
8754
|
+
const contextDir = join34(repoRoot, DEFAULT_CONTEXT_FOLDER2);
|
|
8122
8755
|
mkdirSync2(contextDir, { recursive: true });
|
|
8123
8756
|
let downloadedCount = 0;
|
|
8124
8757
|
let failedCount = 0;
|
|
@@ -8139,7 +8772,7 @@ async function member() {
|
|
|
8139
8772
|
const response = await fetch(presignedUrl);
|
|
8140
8773
|
if (response.ok) {
|
|
8141
8774
|
const content = await response.text();
|
|
8142
|
-
mkdirSync2(
|
|
8775
|
+
mkdirSync2(dirname14(safePath), { recursive: true });
|
|
8143
8776
|
writeFileSync3(safePath, content, "utf-8");
|
|
8144
8777
|
downloadedCount++;
|
|
8145
8778
|
} else {
|
|
@@ -8263,15 +8896,15 @@ import chalk7 from "chalk";
|
|
|
8263
8896
|
import ora3 from "ora";
|
|
8264
8897
|
|
|
8265
8898
|
// src/lib/tenant-graph-purge.ts
|
|
8266
|
-
import { promises as
|
|
8267
|
-
import { homedir as
|
|
8268
|
-
import { join as
|
|
8269
|
-
async function purgeForeignGraphs(validRepoIds, home =
|
|
8270
|
-
const graphsDir =
|
|
8899
|
+
import { promises as fs11 } from "fs";
|
|
8900
|
+
import { homedir as homedir6 } from "os";
|
|
8901
|
+
import { join as join35 } from "path";
|
|
8902
|
+
async function purgeForeignGraphs(validRepoIds, home = homedir6()) {
|
|
8903
|
+
const graphsDir = join35(home, ".repowise", "graphs");
|
|
8271
8904
|
const result = { kept: [], removed: [] };
|
|
8272
8905
|
let entries;
|
|
8273
8906
|
try {
|
|
8274
|
-
entries = await
|
|
8907
|
+
entries = await fs11.readdir(graphsDir);
|
|
8275
8908
|
} catch (err) {
|
|
8276
8909
|
if (err.code === "ENOENT") return result;
|
|
8277
8910
|
throw err;
|
|
@@ -8285,15 +8918,15 @@ async function purgeForeignGraphs(validRepoIds, home = homedir5()) {
|
|
|
8285
8918
|
result.kept.push(entry);
|
|
8286
8919
|
continue;
|
|
8287
8920
|
}
|
|
8288
|
-
const path =
|
|
8921
|
+
const path = join35(graphsDir, entry);
|
|
8289
8922
|
try {
|
|
8290
|
-
const stat7 = await
|
|
8923
|
+
const stat7 = await fs11.lstat(path);
|
|
8291
8924
|
if (stat7.isSymbolicLink()) {
|
|
8292
|
-
await
|
|
8925
|
+
await fs11.unlink(path);
|
|
8293
8926
|
result.removed.push(entry);
|
|
8294
8927
|
continue;
|
|
8295
8928
|
}
|
|
8296
|
-
await
|
|
8929
|
+
await fs11.rm(path, { recursive: true, force: true });
|
|
8297
8930
|
result.removed.push(entry);
|
|
8298
8931
|
} catch {
|
|
8299
8932
|
}
|
|
@@ -8378,11 +9011,11 @@ async function logout() {
|
|
|
8378
9011
|
|
|
8379
9012
|
// src/commands/status.ts
|
|
8380
9013
|
import { readFile as readFile16 } from "fs/promises";
|
|
8381
|
-
import { basename as basename2, join as
|
|
9014
|
+
import { basename as basename2, join as join36 } from "path";
|
|
8382
9015
|
async function status() {
|
|
8383
9016
|
const configDir = getConfigDir2();
|
|
8384
|
-
const STATE_PATH =
|
|
8385
|
-
const CONFIG_PATH =
|
|
9017
|
+
const STATE_PATH = join36(configDir, "listener-state.json");
|
|
9018
|
+
const CONFIG_PATH = join36(configDir, "config.json");
|
|
8386
9019
|
let state = null;
|
|
8387
9020
|
try {
|
|
8388
9021
|
const data = await readFile16(STATE_PATH, "utf-8");
|
|
@@ -8430,7 +9063,7 @@ async function status() {
|
|
|
8430
9063
|
|
|
8431
9064
|
// src/commands/sync.ts
|
|
8432
9065
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
8433
|
-
import { dirname as
|
|
9066
|
+
import { dirname as dirname15, join as join37 } from "path";
|
|
8434
9067
|
import chalk9 from "chalk";
|
|
8435
9068
|
import ora4 from "ora";
|
|
8436
9069
|
var POLL_INTERVAL_MS2 = 3e3;
|
|
@@ -8579,7 +9212,7 @@ async function sync() {
|
|
|
8579
9212
|
const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
|
|
8580
9213
|
const files = listResult.data?.files ?? listResult.files ?? [];
|
|
8581
9214
|
if (files.length > 0) {
|
|
8582
|
-
const contextDir =
|
|
9215
|
+
const contextDir = join37(repoRoot, DEFAULT_CONTEXT_FOLDER3);
|
|
8583
9216
|
mkdirSync3(contextDir, { recursive: true });
|
|
8584
9217
|
let downloadedCount = 0;
|
|
8585
9218
|
let failedCount = 0;
|
|
@@ -8593,8 +9226,8 @@ async function sync() {
|
|
|
8593
9226
|
const response = await fetch(presignedUrl);
|
|
8594
9227
|
if (response.ok) {
|
|
8595
9228
|
const content = await response.text();
|
|
8596
|
-
const filePath =
|
|
8597
|
-
mkdirSync3(
|
|
9229
|
+
const filePath = join37(contextDir, file.fileName);
|
|
9230
|
+
mkdirSync3(dirname15(filePath), { recursive: true });
|
|
8598
9231
|
writeFileSync4(filePath, content, "utf-8");
|
|
8599
9232
|
downloadedCount++;
|
|
8600
9233
|
} else {
|
|
@@ -8846,7 +9479,7 @@ async function config() {
|
|
|
8846
9479
|
// src/commands/mcp-log.ts
|
|
8847
9480
|
import { createDecipheriv as createDecipheriv2 } from "crypto";
|
|
8848
9481
|
import { mkdir as mkdir18, readFile as readFile17, stat as stat6, writeFile as writeFile18 } from "fs/promises";
|
|
8849
|
-
import { dirname as
|
|
9482
|
+
import { dirname as dirname16, join as join38 } from "path";
|
|
8850
9483
|
var FLAG_FILE = "mcp-log.flag";
|
|
8851
9484
|
var LOG_FILE = "mcp-log.jsonl.enc";
|
|
8852
9485
|
var KEY_FILE = "mcp-log.key";
|
|
@@ -8854,10 +9487,10 @@ var ENDPOINT_FILE = "listener.endpoint";
|
|
|
8854
9487
|
var IV_BYTES2 = 12;
|
|
8855
9488
|
var TAG_BYTES2 = 16;
|
|
8856
9489
|
function flagPath() {
|
|
8857
|
-
return
|
|
9490
|
+
return join38(getConfigDir2(), FLAG_FILE);
|
|
8858
9491
|
}
|
|
8859
9492
|
function logPath() {
|
|
8860
|
-
return
|
|
9493
|
+
return join38(getConfigDir2(), LOG_FILE);
|
|
8861
9494
|
}
|
|
8862
9495
|
async function readFlag() {
|
|
8863
9496
|
try {
|
|
@@ -8873,7 +9506,7 @@ async function readFlag() {
|
|
|
8873
9506
|
}
|
|
8874
9507
|
async function writeFlag(flag) {
|
|
8875
9508
|
const path = flagPath();
|
|
8876
|
-
await mkdir18(
|
|
9509
|
+
await mkdir18(dirname16(path), { recursive: true });
|
|
8877
9510
|
await writeFile18(path, JSON.stringify(flag, null, 2), { encoding: "utf-8", mode: 384 });
|
|
8878
9511
|
}
|
|
8879
9512
|
async function showConsentPrompt() {
|
|
@@ -8944,14 +9577,14 @@ async function trySendConsentToServer() {
|
|
|
8944
9577
|
let apiUrl = null;
|
|
8945
9578
|
let token = null;
|
|
8946
9579
|
try {
|
|
8947
|
-
const body = await readFile17(
|
|
9580
|
+
const body = await readFile17(join38(getConfigDir2(), "config.json"), "utf-8");
|
|
8948
9581
|
const parsed = JSON.parse(body);
|
|
8949
9582
|
apiUrl = parsed.repos?.find((r) => Boolean(r.apiUrl))?.apiUrl ?? parsed.defaultApiUrl ?? null;
|
|
8950
9583
|
} catch {
|
|
8951
9584
|
return false;
|
|
8952
9585
|
}
|
|
8953
9586
|
try {
|
|
8954
|
-
const body = await readFile17(
|
|
9587
|
+
const body = await readFile17(join38(getConfigDir2(), "credentials.json"), "utf-8");
|
|
8955
9588
|
const parsed = JSON.parse(body);
|
|
8956
9589
|
token = parsed.idToken ?? null;
|
|
8957
9590
|
} catch {
|
|
@@ -9004,7 +9637,7 @@ async function mcpLogStatus() {
|
|
|
9004
9637
|
process.stderr.write("Log size: no file yet\n");
|
|
9005
9638
|
}
|
|
9006
9639
|
try {
|
|
9007
|
-
const endpointBody = await readFile17(
|
|
9640
|
+
const endpointBody = await readFile17(join38(getConfigDir2(), ENDPOINT_FILE), "utf-8");
|
|
9008
9641
|
const match = /endpoint=([^\n]+)/.exec(endpointBody);
|
|
9009
9642
|
process.stderr.write(`MCP endpoint: ${match?.[1] ?? "(malformed endpoint file)"}
|
|
9010
9643
|
`);
|
|
@@ -9031,7 +9664,7 @@ Run \`repowise mcp-log on\` to grant consent and view them.
|
|
|
9031
9664
|
const key = await readKey();
|
|
9032
9665
|
if (!key) {
|
|
9033
9666
|
process.stderr.write(
|
|
9034
|
-
`No encryption key at ${
|
|
9667
|
+
`No encryption key at ${join38(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
|
|
9035
9668
|
`
|
|
9036
9669
|
);
|
|
9037
9670
|
return;
|
|
@@ -9107,7 +9740,7 @@ Run \`repowise mcp-log on\` to grant consent and view them.
|
|
|
9107
9740
|
}
|
|
9108
9741
|
async function readKey() {
|
|
9109
9742
|
try {
|
|
9110
|
-
const body = await readFile17(
|
|
9743
|
+
const body = await readFile17(join38(getConfigDir2(), KEY_FILE), "utf-8");
|
|
9111
9744
|
const parsed = Buffer.from(body.trim(), "base64");
|
|
9112
9745
|
if (parsed.length !== 32) return null;
|
|
9113
9746
|
return parsed;
|
|
@@ -9150,8 +9783,8 @@ async function mcpLog(subcommand, flags = {}) {
|
|
|
9150
9783
|
import chalk11 from "chalk";
|
|
9151
9784
|
|
|
9152
9785
|
// src/lib/graph-loader.ts
|
|
9153
|
-
import { promises as
|
|
9154
|
-
import { join as
|
|
9786
|
+
import { promises as fs12 } from "fs";
|
|
9787
|
+
import { join as join39, resolve as resolve2 } from "path";
|
|
9155
9788
|
import { gunzipSync } from "zlib";
|
|
9156
9789
|
var RELATIVE_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json";
|
|
9157
9790
|
var GZIPPED_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json.gz";
|
|
@@ -9168,8 +9801,8 @@ var GraphNotFoundError = class extends Error {
|
|
|
9168
9801
|
var cache = /* @__PURE__ */ new Map();
|
|
9169
9802
|
async function loadGraph(repoRoot = process.cwd()) {
|
|
9170
9803
|
const root = resolve2(repoRoot);
|
|
9171
|
-
const gzPath =
|
|
9172
|
-
const plainPath =
|
|
9804
|
+
const gzPath = join39(root, GZIPPED_GRAPH_PATH);
|
|
9805
|
+
const plainPath = join39(root, RELATIVE_GRAPH_PATH);
|
|
9173
9806
|
const cached = cache.get(root);
|
|
9174
9807
|
if (cached) {
|
|
9175
9808
|
return { graph: cached, path: plainPath, bytes: 0, parseMs: 0, fromCache: true };
|
|
@@ -9177,14 +9810,14 @@ async function loadGraph(repoRoot = process.cwd()) {
|
|
|
9177
9810
|
let graphPath = null;
|
|
9178
9811
|
let raw = null;
|
|
9179
9812
|
try {
|
|
9180
|
-
raw = await
|
|
9813
|
+
raw = await fs12.readFile(gzPath);
|
|
9181
9814
|
graphPath = gzPath;
|
|
9182
9815
|
} catch (err) {
|
|
9183
9816
|
if (err.code !== "ENOENT") throw err;
|
|
9184
9817
|
}
|
|
9185
9818
|
if (!raw) {
|
|
9186
9819
|
try {
|
|
9187
|
-
raw = await
|
|
9820
|
+
raw = await fs12.readFile(plainPath);
|
|
9188
9821
|
graphPath = plainPath;
|
|
9189
9822
|
} catch (err) {
|
|
9190
9823
|
if (err.code === "ENOENT") {
|
|
@@ -9583,14 +10216,14 @@ function registerQueryCommand(program2) {
|
|
|
9583
10216
|
}
|
|
9584
10217
|
|
|
9585
10218
|
// src/commands/uninstall.ts
|
|
9586
|
-
import { promises as
|
|
9587
|
-
import { homedir as
|
|
9588
|
-
import { join as
|
|
10219
|
+
import { promises as fs16 } from "fs";
|
|
10220
|
+
import { homedir as homedir8 } from "os";
|
|
10221
|
+
import { join as join43 } from "path";
|
|
9589
10222
|
import chalk12 from "chalk";
|
|
9590
10223
|
|
|
9591
10224
|
// src/lib/cleanup/marker-blocks.ts
|
|
9592
|
-
import { promises as
|
|
9593
|
-
import { join as
|
|
10225
|
+
import { promises as fs13 } from "fs";
|
|
10226
|
+
import { join as join40 } from "path";
|
|
9594
10227
|
var MARKER_START = "<!-- repowise-start -->";
|
|
9595
10228
|
var MARKER_END = "<!-- repowise-end -->";
|
|
9596
10229
|
var CONTEXT_FILES = [
|
|
@@ -9606,7 +10239,7 @@ var CONTEXT_FILES = [
|
|
|
9606
10239
|
async function stripMarkerBlock(filePath) {
|
|
9607
10240
|
let raw;
|
|
9608
10241
|
try {
|
|
9609
|
-
raw = await
|
|
10242
|
+
raw = await fs13.readFile(filePath, "utf-8");
|
|
9610
10243
|
} catch (err) {
|
|
9611
10244
|
if (err.code === "ENOENT")
|
|
9612
10245
|
return { path: filePath, status: "missing" };
|
|
@@ -9621,16 +10254,16 @@ async function stripMarkerBlock(filePath) {
|
|
|
9621
10254
|
const after = raw.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
|
|
9622
10255
|
const stripped = (before + (before && after ? "\n\n" : "") + after).trim();
|
|
9623
10256
|
if (stripped.length === 0) {
|
|
9624
|
-
await
|
|
10257
|
+
await fs13.unlink(filePath);
|
|
9625
10258
|
return { path: filePath, status: "deleted" };
|
|
9626
10259
|
}
|
|
9627
|
-
await
|
|
10260
|
+
await fs13.writeFile(filePath, stripped + "\n", "utf-8");
|
|
9628
10261
|
return { path: filePath, status: "stripped" };
|
|
9629
10262
|
}
|
|
9630
10263
|
async function stripAllMarkerBlocks(repoRoot) {
|
|
9631
10264
|
const out = [];
|
|
9632
10265
|
for (const relative of CONTEXT_FILES) {
|
|
9633
|
-
const full =
|
|
10266
|
+
const full = join40(repoRoot, relative);
|
|
9634
10267
|
const result = await stripMarkerBlock(full).catch((err) => ({
|
|
9635
10268
|
path: full,
|
|
9636
10269
|
status: "untouched",
|
|
@@ -9642,25 +10275,25 @@ async function stripAllMarkerBlocks(repoRoot) {
|
|
|
9642
10275
|
}
|
|
9643
10276
|
|
|
9644
10277
|
// src/lib/cleanup/mcp-configs.ts
|
|
9645
|
-
import { promises as
|
|
9646
|
-
import { join as
|
|
10278
|
+
import { promises as fs14 } from "fs";
|
|
10279
|
+
import { join as join41 } from "path";
|
|
9647
10280
|
function mcpConfigPaths(repoRoot, home) {
|
|
9648
10281
|
return [
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
10282
|
+
join41(repoRoot, ".mcp.json"),
|
|
10283
|
+
join41(repoRoot, ".cursor", "mcp.json"),
|
|
10284
|
+
join41(repoRoot, ".vscode", "mcp.json"),
|
|
10285
|
+
join41(repoRoot, ".roo", "mcp.json"),
|
|
10286
|
+
join41(home, ".cline", "mcp.json"),
|
|
10287
|
+
join41(home, ".codeium", "windsurf", "mcp_config.json"),
|
|
10288
|
+
join41(home, ".gemini", "settings.json"),
|
|
10289
|
+
join41(home, ".codex", "mcp.json"),
|
|
10290
|
+
join41(home, ".roo", "mcp.json")
|
|
9658
10291
|
];
|
|
9659
10292
|
}
|
|
9660
10293
|
async function removeRepowiseFromConfig(path, serverName) {
|
|
9661
10294
|
let raw;
|
|
9662
10295
|
try {
|
|
9663
|
-
raw = await
|
|
10296
|
+
raw = await fs14.readFile(path, "utf-8");
|
|
9664
10297
|
} catch (err) {
|
|
9665
10298
|
if (err.code === "ENOENT") return { path, status: "not-found" };
|
|
9666
10299
|
return { path, status: "error", error: err.message };
|
|
@@ -9680,7 +10313,7 @@ async function removeRepowiseFromConfig(path, serverName) {
|
|
|
9680
10313
|
} else {
|
|
9681
10314
|
next.mcpServers = servers;
|
|
9682
10315
|
}
|
|
9683
|
-
await
|
|
10316
|
+
await fs14.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
9684
10317
|
return { path, status: "removed" };
|
|
9685
10318
|
}
|
|
9686
10319
|
async function removeAllMcpEntries(repoRoot, home, repoId) {
|
|
@@ -9693,17 +10326,17 @@ async function removeAllMcpEntries(repoRoot, home, repoId) {
|
|
|
9693
10326
|
}
|
|
9694
10327
|
|
|
9695
10328
|
// src/lib/cleanup/local-state.ts
|
|
9696
|
-
import { promises as
|
|
9697
|
-
import { homedir as
|
|
9698
|
-
import { join as
|
|
10329
|
+
import { promises as fs15 } from "fs";
|
|
10330
|
+
import { homedir as homedir7 } from "os";
|
|
10331
|
+
import { join as join42, resolve as resolve3 } from "path";
|
|
9699
10332
|
async function clearLocalState(homeOverride) {
|
|
9700
|
-
const home = homeOverride ??
|
|
9701
|
-
const target = resolve3(
|
|
10333
|
+
const home = homeOverride ?? homedir7();
|
|
10334
|
+
const target = resolve3(join42(home, ".repowise"));
|
|
9702
10335
|
if (target === resolve3(home) || !target.startsWith(resolve3(home))) {
|
|
9703
10336
|
return { path: target, status: "error", error: "refused: not under home" };
|
|
9704
10337
|
}
|
|
9705
10338
|
try {
|
|
9706
|
-
await
|
|
10339
|
+
await fs15.rm(target, { recursive: true, force: false });
|
|
9707
10340
|
return { path: target, status: "removed" };
|
|
9708
10341
|
} catch (err) {
|
|
9709
10342
|
if (err.code === "ENOENT")
|
|
@@ -9733,7 +10366,7 @@ async function stopAndUninstallService(uninstaller) {
|
|
|
9733
10366
|
// src/commands/uninstall.ts
|
|
9734
10367
|
async function uninstall2(opts = {}) {
|
|
9735
10368
|
const tier = opts.tier ?? "uninstall";
|
|
9736
|
-
const home = opts.home ??
|
|
10369
|
+
const home = opts.home ?? homedir8();
|
|
9737
10370
|
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
9738
10371
|
const loadRepoIds = opts.loadRepoIds ?? defaultLoadRepoIds;
|
|
9739
10372
|
const report = { tier, removed: [], preserved: [], skipped: [] };
|
|
@@ -9742,7 +10375,7 @@ async function uninstall2(opts = {}) {
|
|
|
9742
10375
|
else if (svc.error) report.skipped.push({ path: "listener service", reason: svc.error });
|
|
9743
10376
|
if (tier === "stop") return report;
|
|
9744
10377
|
try {
|
|
9745
|
-
await
|
|
10378
|
+
await fs16.unlink(join43(home, ".repowise", "credentials.json"));
|
|
9746
10379
|
report.removed.push("credentials");
|
|
9747
10380
|
} catch (err) {
|
|
9748
10381
|
if (err.code !== "ENOENT") {
|
|
@@ -9769,7 +10402,7 @@ async function uninstall2(opts = {}) {
|
|
|
9769
10402
|
const allPaths = mcpConfigPaths(repoRoot, home);
|
|
9770
10403
|
for (const p of allPaths) {
|
|
9771
10404
|
try {
|
|
9772
|
-
await
|
|
10405
|
+
await fs16.access(p);
|
|
9773
10406
|
} catch {
|
|
9774
10407
|
}
|
|
9775
10408
|
}
|
|
@@ -9781,7 +10414,7 @@ async function uninstall2(opts = {}) {
|
|
|
9781
10414
|
}
|
|
9782
10415
|
async function defaultLoadRepoIds(home) {
|
|
9783
10416
|
try {
|
|
9784
|
-
const raw = await
|
|
10417
|
+
const raw = await fs16.readFile(join43(home, ".repowise", "config.json"), "utf-8");
|
|
9785
10418
|
const parsed = JSON.parse(raw);
|
|
9786
10419
|
return (parsed.repos ?? []).map((r) => r.repoId);
|
|
9787
10420
|
} catch {
|
|
@@ -9827,13 +10460,13 @@ Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
|
|
|
9827
10460
|
}
|
|
9828
10461
|
|
|
9829
10462
|
// src/commands/mcp-shim.ts
|
|
9830
|
-
import { promises as
|
|
10463
|
+
import { promises as fs17 } from "fs";
|
|
9831
10464
|
import { createInterface as createInterface2 } from "readline";
|
|
9832
|
-
import { homedir as
|
|
9833
|
-
import { join as
|
|
10465
|
+
import { homedir as homedir9 } from "os";
|
|
10466
|
+
import { join as join44 } from "path";
|
|
9834
10467
|
var DEFAULT_MAX = 200 * 1024;
|
|
9835
10468
|
async function mcpShim(opts) {
|
|
9836
|
-
const endpointPath = opts.endpointFile ??
|
|
10469
|
+
const endpointPath = opts.endpointFile ?? join44(homedir9(), ".repowise", "listener.endpoint");
|
|
9837
10470
|
const stdin = opts.stdin ?? process.stdin;
|
|
9838
10471
|
const stdout = opts.stdout ?? process.stdout;
|
|
9839
10472
|
const stderr = opts.stderr ?? process.stderr;
|
|
@@ -9904,7 +10537,7 @@ async function mcpShim(opts) {
|
|
|
9904
10537
|
}
|
|
9905
10538
|
async function readEndpoint(path) {
|
|
9906
10539
|
try {
|
|
9907
|
-
const raw = (await
|
|
10540
|
+
const raw = (await fs17.readFile(path, "utf-8")).trim();
|
|
9908
10541
|
if (!raw) return null;
|
|
9909
10542
|
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
9910
10543
|
return { endpoint: raw.split("\n")[0].trim(), secret: null };
|
|
@@ -10118,14 +10751,14 @@ function writeError(stream, id, code, message) {
|
|
|
10118
10751
|
|
|
10119
10752
|
// src/commands/lsp.ts
|
|
10120
10753
|
init_registry();
|
|
10121
|
-
import { spawn as
|
|
10754
|
+
import { spawn as spawn4 } from "child_process";
|
|
10122
10755
|
import chalk13 from "chalk";
|
|
10123
10756
|
async function isOnPath(command) {
|
|
10124
10757
|
if (/[^\w./+-]/.test(command)) return false;
|
|
10125
10758
|
const isWin = process.platform === "win32";
|
|
10126
10759
|
const probeCmd = isWin ? "where" : "which";
|
|
10127
10760
|
return new Promise((resolve4) => {
|
|
10128
|
-
const child =
|
|
10761
|
+
const child = spawn4(probeCmd, [command], { stdio: "ignore" });
|
|
10129
10762
|
child.on("close", (code) => {
|
|
10130
10763
|
resolve4(code === 0);
|
|
10131
10764
|
});
|
|
@@ -10186,8 +10819,8 @@ async function lspDoctor() {
|
|
|
10186
10819
|
|
|
10187
10820
|
// bin/repowise.ts
|
|
10188
10821
|
var __filename = fileURLToPath4(import.meta.url);
|
|
10189
|
-
var __dirname =
|
|
10190
|
-
var pkg = JSON.parse(readFileSync3(
|
|
10822
|
+
var __dirname = dirname17(__filename);
|
|
10823
|
+
var pkg = JSON.parse(readFileSync3(join45(__dirname, "..", "..", "package.json"), "utf-8"));
|
|
10191
10824
|
var program = new Command();
|
|
10192
10825
|
program.name(getPackageName()).description("AI-optimized codebase context generator").version(pkg.version).hook("preAction", async () => {
|
|
10193
10826
|
await showWelcome(pkg.version);
|