@memoraone/mcp 0.1.28 → 0.1.29
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/cli.cjs +307 -59
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var require_package = __commonJS({
|
|
|
30
30
|
"package.json"(exports2, module2) {
|
|
31
31
|
module2.exports = {
|
|
32
32
|
name: "@memoraone/mcp",
|
|
33
|
-
version: "0.1.
|
|
33
|
+
version: "0.1.29",
|
|
34
34
|
type: "module",
|
|
35
35
|
main: "dist/index.cjs",
|
|
36
36
|
bin: {
|
|
@@ -67,9 +67,9 @@ var require_package = __commonJS({
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
// src/cli.ts
|
|
70
|
-
var
|
|
70
|
+
var path6 = __toESM(require("path"), 1);
|
|
71
71
|
var net = __toESM(require("net"), 1);
|
|
72
|
-
var
|
|
72
|
+
var import_node_child_process3 = require("child_process");
|
|
73
73
|
|
|
74
74
|
// src/socketPaths.ts
|
|
75
75
|
var os = __toESM(require("os"), 1);
|
|
@@ -292,8 +292,8 @@ function encodeResolvedBinding(binding) {
|
|
|
292
292
|
}
|
|
293
293
|
|
|
294
294
|
// src/setupIdeFiles.ts
|
|
295
|
-
var
|
|
296
|
-
var
|
|
295
|
+
var fs5 = __toESM(require("fs/promises"), 1);
|
|
296
|
+
var path5 = __toESM(require("path"), 1);
|
|
297
297
|
|
|
298
298
|
// src/cleanup.ts
|
|
299
299
|
var fs3 = __toESM(require("fs/promises"), 1);
|
|
@@ -685,13 +685,219 @@ async function cliCleanup(argv) {
|
|
|
685
685
|
return result.exitCode;
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
+
// src/cursorGlobalMcpConfig.ts
|
|
689
|
+
var fs4 = __toESM(require("fs/promises"), 1);
|
|
690
|
+
var os2 = __toESM(require("os"), 1);
|
|
691
|
+
var path4 = __toESM(require("path"), 1);
|
|
692
|
+
var import_node_child_process2 = require("child_process");
|
|
693
|
+
var import_node_util2 = require("util");
|
|
694
|
+
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
695
|
+
function buildMemoraoneCursorMcpServer(npxPath) {
|
|
696
|
+
return {
|
|
697
|
+
command: npxPath,
|
|
698
|
+
args: ["-y", "@memoraone/mcp@latest"],
|
|
699
|
+
env: {
|
|
700
|
+
MEMORAONE_API_URL: "https://api.memoraone.com",
|
|
701
|
+
MEMORAONE_IDE_TYPE: "cursor"
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
async function pathExists(filePath) {
|
|
706
|
+
try {
|
|
707
|
+
await fs4.access(filePath);
|
|
708
|
+
return true;
|
|
709
|
+
} catch {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function stripLeadingLineComments(text) {
|
|
714
|
+
return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
|
|
715
|
+
}
|
|
716
|
+
function getKnownCursorGlobalMcpConfigCandidates(homeDir) {
|
|
717
|
+
return [path4.join(homeDir, ".cursor", "mcp.json")];
|
|
718
|
+
}
|
|
719
|
+
async function detectCursorGlobalMcpConfig(options) {
|
|
720
|
+
if (options?.explicitPath) {
|
|
721
|
+
return { ok: true, path: options.explicitPath, detectedExisting: await pathExists(options.explicitPath) };
|
|
722
|
+
}
|
|
723
|
+
const homeDir = options?.homeDir ?? os2.homedir();
|
|
724
|
+
const candidates = getKnownCursorGlobalMcpConfigCandidates(homeDir);
|
|
725
|
+
const existing = [];
|
|
726
|
+
for (const candidate of candidates) {
|
|
727
|
+
if (await pathExists(candidate)) existing.push(candidate);
|
|
728
|
+
}
|
|
729
|
+
if (existing.length > 1) {
|
|
730
|
+
return {
|
|
731
|
+
ok: false,
|
|
732
|
+
error: "[setup-ide-files] Multiple Cursor global MCP config paths found. Specify one explicitly.",
|
|
733
|
+
candidates: existing
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
if (existing.length === 1) {
|
|
737
|
+
return { ok: true, path: existing[0], detectedExisting: true };
|
|
738
|
+
}
|
|
739
|
+
const defaultPath = candidates[0];
|
|
740
|
+
if (!defaultPath) {
|
|
741
|
+
return {
|
|
742
|
+
ok: false,
|
|
743
|
+
error: "[setup-ide-files] No known Cursor global MCP config path.",
|
|
744
|
+
candidates: []
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
return { ok: true, path: defaultPath, detectedExisting: false };
|
|
748
|
+
}
|
|
749
|
+
function formatBackupTimestamp(d = /* @__PURE__ */ new Date()) {
|
|
750
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
751
|
+
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
752
|
+
}
|
|
753
|
+
async function isWorkingNpx(npxPath) {
|
|
754
|
+
try {
|
|
755
|
+
if (!await pathExists(npxPath)) return false;
|
|
756
|
+
if (process.platform !== "win32") {
|
|
757
|
+
try {
|
|
758
|
+
await fs4.access(npxPath, fs4.constants.X_OK);
|
|
759
|
+
} catch {
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
await execFileAsync2(npxPath, ["--version"], { timeout: 1e4 });
|
|
764
|
+
return true;
|
|
765
|
+
} catch {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
async function resolveNpxPath() {
|
|
770
|
+
const npxName = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
771
|
+
const candidates = [];
|
|
772
|
+
if (process.platform === "darwin") {
|
|
773
|
+
candidates.push("/opt/homebrew/bin/npx", "/usr/local/bin/npx");
|
|
774
|
+
} else if (process.platform === "linux") {
|
|
775
|
+
candidates.push("/usr/local/bin/npx");
|
|
776
|
+
}
|
|
777
|
+
const pathSep = process.platform === "win32" ? ";" : ":";
|
|
778
|
+
for (const dir of (process.env.PATH ?? "").split(pathSep)) {
|
|
779
|
+
if (!dir) continue;
|
|
780
|
+
candidates.push(path4.join(dir, npxName));
|
|
781
|
+
}
|
|
782
|
+
try {
|
|
783
|
+
const lookupCmd = process.platform === "win32" ? "where" : "which";
|
|
784
|
+
const { stdout } = await execFileAsync2(lookupCmd, [npxName], { timeout: 5e3 });
|
|
785
|
+
const first = stdout.trim().split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
786
|
+
if (first) candidates.unshift(first);
|
|
787
|
+
} catch {
|
|
788
|
+
}
|
|
789
|
+
const seen = /* @__PURE__ */ new Set();
|
|
790
|
+
for (const candidate of candidates) {
|
|
791
|
+
const abs = path4.isAbsolute(candidate) ? candidate : path4.resolve(candidate);
|
|
792
|
+
const key = process.platform === "win32" ? abs.toLowerCase() : abs;
|
|
793
|
+
if (seen.has(key)) continue;
|
|
794
|
+
seen.add(key);
|
|
795
|
+
if (await isWorkingNpx(abs)) return abs;
|
|
796
|
+
}
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
799
|
+
function mergeCursorGlobalMcpConfigObject(existing, npxPath) {
|
|
800
|
+
const base = existing && typeof existing === "object" ? { ...existing } : { mcpServers: {} };
|
|
801
|
+
const mcpServers = typeof base.mcpServers === "object" && base.mcpServers !== null && !Array.isArray(base.mcpServers) ? { ...base.mcpServers } : {};
|
|
802
|
+
mcpServers.memoraone = buildMemoraoneCursorMcpServer(npxPath);
|
|
803
|
+
return { ...base, mcpServers };
|
|
804
|
+
}
|
|
805
|
+
function memoraoneServerMatches(server, npxPath) {
|
|
806
|
+
if (!server || typeof server !== "object") return false;
|
|
807
|
+
const s = server;
|
|
808
|
+
if (s.command !== npxPath) return false;
|
|
809
|
+
if (!Array.isArray(s.args) || s.args.length !== 2) return false;
|
|
810
|
+
if (s.args[0] !== "-y" || s.args[1] !== "@memoraone/mcp@latest") return false;
|
|
811
|
+
const env = s.env;
|
|
812
|
+
if (!env || typeof env !== "object") return false;
|
|
813
|
+
const e = env;
|
|
814
|
+
return e.MEMORAONE_API_URL === "https://api.memoraone.com" && e.MEMORAONE_IDE_TYPE === "cursor";
|
|
815
|
+
}
|
|
816
|
+
function validateCursorGlobalMcpConfig(parsed, npxPath) {
|
|
817
|
+
if (!parsed || typeof parsed !== "object") {
|
|
818
|
+
throw new Error("[setup-ide-files] Cursor global MCP config must be a JSON object.");
|
|
819
|
+
}
|
|
820
|
+
const mcpServers = parsed.mcpServers;
|
|
821
|
+
if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) {
|
|
822
|
+
throw new Error("[setup-ide-files] Cursor global MCP config missing mcpServers object.");
|
|
823
|
+
}
|
|
824
|
+
const memoraone = mcpServers.memoraone;
|
|
825
|
+
if (!memoraoneServerMatches(memoraone, npxPath)) {
|
|
826
|
+
throw new Error(
|
|
827
|
+
"[setup-ide-files] Cursor global MCP config mcpServers.memoraone is missing or invalid."
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
async function setupCursorGlobalMcpConfig(options) {
|
|
832
|
+
const { configPath, npxPath, dryRun } = options;
|
|
833
|
+
const existed = await pathExists(configPath);
|
|
834
|
+
let existing = null;
|
|
835
|
+
if (existed) {
|
|
836
|
+
const raw = await fs4.readFile(configPath, "utf8");
|
|
837
|
+
try {
|
|
838
|
+
existing = JSON.parse(stripLeadingLineComments(raw));
|
|
839
|
+
} catch {
|
|
840
|
+
throw new Error(
|
|
841
|
+
`[setup-ide-files] Invalid JSON in Cursor global MCP config: ${configPath}`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
const merged = mergeCursorGlobalMcpConfigObject(existing, npxPath);
|
|
846
|
+
const body = JSON.stringify(merged, null, 2) + "\n";
|
|
847
|
+
if (existed) {
|
|
848
|
+
const currentMemoraone = existing && typeof existing.mcpServers === "object" && existing.mcpServers !== null && !Array.isArray(existing.mcpServers) ? existing.mcpServers.memoraone : void 0;
|
|
849
|
+
if (memoraoneServerMatches(currentMemoraone, npxPath)) {
|
|
850
|
+
return { outcome: "skipped" };
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (dryRun) {
|
|
854
|
+
return { outcome: existed ? "updated" : "created" };
|
|
855
|
+
}
|
|
856
|
+
let backupPath;
|
|
857
|
+
if (existed) {
|
|
858
|
+
backupPath = `${configPath}.backup-${formatBackupTimestamp()}`;
|
|
859
|
+
await fs4.copyFile(configPath, backupPath);
|
|
860
|
+
}
|
|
861
|
+
await fs4.mkdir(path4.dirname(configPath), { recursive: true });
|
|
862
|
+
await fs4.writeFile(configPath, body, "utf8");
|
|
863
|
+
const verifyRaw = await fs4.readFile(configPath, "utf8");
|
|
864
|
+
const verifyParsed = JSON.parse(stripLeadingLineComments(verifyRaw));
|
|
865
|
+
validateCursorGlobalMcpConfig(verifyParsed, npxPath);
|
|
866
|
+
return { outcome: existed ? "updated" : "created", backupPath };
|
|
867
|
+
}
|
|
868
|
+
function logCursorGlobalMcpCliSummary(info, dryRun) {
|
|
869
|
+
const { configPath, npxPath, backupPath, outcome } = info;
|
|
870
|
+
console.log(`[setup-ide-files] Cursor global MCP config: ${configPath}`);
|
|
871
|
+
console.log(`[setup-ide-files] Resolved npx: ${npxPath}`);
|
|
872
|
+
if (backupPath) {
|
|
873
|
+
console.log(`[setup-ide-files] Cursor global MCP config backup: ${backupPath}`);
|
|
874
|
+
}
|
|
875
|
+
if (outcome === "created") {
|
|
876
|
+
console.log(
|
|
877
|
+
dryRun ? `[setup-ide-files] Cursor global MCP config would be created: ${configPath}` : `[setup-ide-files] Cursor global MCP config created: ${configPath}`
|
|
878
|
+
);
|
|
879
|
+
} else if (outcome === "updated") {
|
|
880
|
+
console.log(
|
|
881
|
+
dryRun ? `[setup-ide-files] Cursor global MCP config would be updated: ${configPath}` : `[setup-ide-files] Cursor global MCP config updated: ${configPath}`
|
|
882
|
+
);
|
|
883
|
+
} else if (outcome === "skipped") {
|
|
884
|
+
console.log(`[setup-ide-files] Cursor global MCP config unchanged: ${configPath}`);
|
|
885
|
+
}
|
|
886
|
+
console.log(
|
|
887
|
+
"[setup-ide-files] Repo-level .cursor/mcp.json was not created or modified."
|
|
888
|
+
);
|
|
889
|
+
console.log(
|
|
890
|
+
"[setup-ide-files] Fully quit Cursor and reopen this repo for MCP changes to take effect."
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
|
|
688
894
|
// src/setupIdeFiles.ts
|
|
689
895
|
var MANAGED_MARKER = "<!-- MemoraOne managed IDE helper -->";
|
|
690
896
|
var GITIGNORE_MEMORAONE_COMMENT = "# MemoraOne local project binding / API key";
|
|
691
897
|
var GITIGNORE_MEMORAONE_ENTRY = "memoraone.m1";
|
|
692
|
-
function buildMemoraoneMcpServer(ideType) {
|
|
898
|
+
function buildMemoraoneMcpServer(ideType, command = "npx") {
|
|
693
899
|
return {
|
|
694
|
-
command
|
|
900
|
+
command,
|
|
695
901
|
args: ["-y", "@memoraone/mcp@latest"],
|
|
696
902
|
env: {
|
|
697
903
|
MEMORAONE_API_URL: "https://api.memoraone.com",
|
|
@@ -700,15 +906,15 @@ function buildMemoraoneMcpServer(ideType) {
|
|
|
700
906
|
};
|
|
701
907
|
}
|
|
702
908
|
function assertUnderRepoRoot(repoRoot, absPath) {
|
|
703
|
-
const normRoot =
|
|
704
|
-
const normPath =
|
|
705
|
-
if (normPath !==
|
|
909
|
+
const normRoot = path5.resolve(repoRoot) + path5.sep;
|
|
910
|
+
const normPath = path5.resolve(absPath);
|
|
911
|
+
if (normPath !== path5.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
|
|
706
912
|
throw new Error(`[setup-ide-files] Refusing to write outside repo root: ${absPath}`);
|
|
707
913
|
}
|
|
708
914
|
}
|
|
709
|
-
async function
|
|
915
|
+
async function pathExists2(filePath) {
|
|
710
916
|
try {
|
|
711
|
-
await
|
|
917
|
+
await fs5.access(filePath);
|
|
712
918
|
return true;
|
|
713
919
|
} catch {
|
|
714
920
|
return false;
|
|
@@ -729,12 +935,12 @@ ${GITIGNORE_MEMORAONE_ENTRY}
|
|
|
729
935
|
}
|
|
730
936
|
async function ensureGitignoreMemoraone(repoRoot, opts) {
|
|
731
937
|
if (opts.noGitignore) return "skipped";
|
|
732
|
-
const abs =
|
|
938
|
+
const abs = path5.join(repoRoot, ".gitignore");
|
|
733
939
|
assertUnderRepoRoot(repoRoot, abs);
|
|
734
940
|
let prior = "";
|
|
735
941
|
let existed = false;
|
|
736
942
|
try {
|
|
737
|
-
prior = await
|
|
943
|
+
prior = await fs5.readFile(abs, "utf8");
|
|
738
944
|
existed = true;
|
|
739
945
|
} catch (err) {
|
|
740
946
|
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
@@ -745,25 +951,25 @@ async function ensureGitignoreMemoraone(repoRoot, opts) {
|
|
|
745
951
|
const separator = existed && prior.length > 0 ? prior.endsWith("\n") ? "\n" : "\n\n" : "";
|
|
746
952
|
const next = (existed ? prior : "") + separator + block;
|
|
747
953
|
if (opts.dryRun) return existed ? "updated" : "created";
|
|
748
|
-
await
|
|
954
|
+
await fs5.writeFile(abs, next, "utf8");
|
|
749
955
|
return existed ? "updated" : "created";
|
|
750
956
|
}
|
|
751
957
|
async function findRepoRoot(startDir) {
|
|
752
|
-
let current =
|
|
753
|
-
const root =
|
|
958
|
+
let current = path5.resolve(startDir);
|
|
959
|
+
const root = path5.parse(current).root;
|
|
754
960
|
while (true) {
|
|
755
|
-
const gitPath =
|
|
756
|
-
const m1Path =
|
|
757
|
-
if (await
|
|
961
|
+
const gitPath = path5.join(current, ".git");
|
|
962
|
+
const m1Path = path5.join(current, "memoraone.m1");
|
|
963
|
+
if (await pathExists2(gitPath) || await pathExists2(m1Path)) {
|
|
758
964
|
return current;
|
|
759
965
|
}
|
|
760
966
|
if (current === root) {
|
|
761
967
|
return null;
|
|
762
968
|
}
|
|
763
|
-
current =
|
|
969
|
+
current = path5.dirname(current);
|
|
764
970
|
}
|
|
765
971
|
}
|
|
766
|
-
function
|
|
972
|
+
function stripLeadingLineComments2(text) {
|
|
767
973
|
return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
|
|
768
974
|
}
|
|
769
975
|
function cursorRuleBody() {
|
|
@@ -813,50 +1019,43 @@ function buildVscodeMcpJsonBody(existing) {
|
|
|
813
1019
|
const merged = { ...base, servers };
|
|
814
1020
|
return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
|
|
815
1021
|
}
|
|
816
|
-
function buildCursorMcpJsonBody(existing) {
|
|
817
|
-
const base = existing && typeof existing === "object" ? { ...existing } : { mcpServers: {} };
|
|
818
|
-
const mcpServers = typeof base.mcpServers === "object" && base.mcpServers !== null && !Array.isArray(base.mcpServers) ? { ...base.mcpServers } : {};
|
|
819
|
-
mcpServers.memoraone = buildMemoraoneMcpServer("cursor");
|
|
820
|
-
const merged = { ...base, mcpServers };
|
|
821
|
-
return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
|
|
822
|
-
}
|
|
823
1022
|
async function writeManagedMarkdown(repoRoot, relPath, fullContent, opts) {
|
|
824
|
-
const abs =
|
|
1023
|
+
const abs = path5.join(repoRoot, relPath);
|
|
825
1024
|
assertUnderRepoRoot(repoRoot, abs);
|
|
826
1025
|
let prior = "";
|
|
827
1026
|
let existed = false;
|
|
828
1027
|
try {
|
|
829
|
-
prior = await
|
|
1028
|
+
prior = await fs5.readFile(abs, "utf8");
|
|
830
1029
|
existed = true;
|
|
831
1030
|
} catch (err) {
|
|
832
1031
|
if (err?.code !== "ENOENT") throw err;
|
|
833
1032
|
}
|
|
834
1033
|
if (!existed) {
|
|
835
1034
|
if (opts.dryRun) return "created";
|
|
836
|
-
await
|
|
837
|
-
await
|
|
1035
|
+
await fs5.mkdir(path5.dirname(abs), { recursive: true });
|
|
1036
|
+
await fs5.writeFile(abs, fullContent, "utf8");
|
|
838
1037
|
return "created";
|
|
839
1038
|
}
|
|
840
1039
|
if (prior.includes(MANAGED_MARKER)) {
|
|
841
1040
|
if (prior === fullContent) return "skipped";
|
|
842
1041
|
if (opts.dryRun) return "updated";
|
|
843
|
-
await
|
|
844
|
-
await
|
|
1042
|
+
await fs5.mkdir(path5.dirname(abs), { recursive: true });
|
|
1043
|
+
await fs5.writeFile(abs, fullContent, "utf8");
|
|
845
1044
|
return "updated";
|
|
846
1045
|
}
|
|
847
1046
|
if (!opts.force) return "skipped-untracked";
|
|
848
1047
|
if (opts.dryRun) return "updated";
|
|
849
|
-
await
|
|
850
|
-
await
|
|
1048
|
+
await fs5.mkdir(path5.dirname(abs), { recursive: true });
|
|
1049
|
+
await fs5.writeFile(abs, fullContent, "utf8");
|
|
851
1050
|
return "updated";
|
|
852
1051
|
}
|
|
853
1052
|
async function writeIdeMcpJson(repoRoot, relPath, buildBody, opts) {
|
|
854
|
-
const abs =
|
|
1053
|
+
const abs = path5.join(repoRoot, relPath);
|
|
855
1054
|
assertUnderRepoRoot(repoRoot, abs);
|
|
856
1055
|
let raw = "";
|
|
857
1056
|
let existed = false;
|
|
858
1057
|
try {
|
|
859
|
-
raw = await
|
|
1058
|
+
raw = await fs5.readFile(abs, "utf8");
|
|
860
1059
|
existed = true;
|
|
861
1060
|
} catch (err) {
|
|
862
1061
|
if (err?.code !== "ENOENT") throw err;
|
|
@@ -864,15 +1063,15 @@ async function writeIdeMcpJson(repoRoot, relPath, buildBody, opts) {
|
|
|
864
1063
|
if (!existed) {
|
|
865
1064
|
const body = buildBody(null);
|
|
866
1065
|
if (opts.dryRun) return "created";
|
|
867
|
-
await
|
|
868
|
-
await
|
|
1066
|
+
await fs5.mkdir(path5.dirname(abs), { recursive: true });
|
|
1067
|
+
await fs5.writeFile(abs, body, "utf8");
|
|
869
1068
|
return "created";
|
|
870
1069
|
}
|
|
871
1070
|
const managed = raw.includes(MANAGED_MARKER);
|
|
872
1071
|
if (!managed && !opts.force) return "skipped-untracked";
|
|
873
1072
|
let parsed = null;
|
|
874
1073
|
try {
|
|
875
|
-
parsed = JSON.parse(
|
|
1074
|
+
parsed = JSON.parse(stripLeadingLineComments2(raw));
|
|
876
1075
|
} catch {
|
|
877
1076
|
parsed = null;
|
|
878
1077
|
}
|
|
@@ -880,8 +1079,8 @@ async function writeIdeMcpJson(repoRoot, relPath, buildBody, opts) {
|
|
|
880
1079
|
const next = buildBody(parsed);
|
|
881
1080
|
if (managed && next === raw) return "skipped";
|
|
882
1081
|
if (opts.dryRun) return "updated";
|
|
883
|
-
await
|
|
884
|
-
await
|
|
1082
|
+
await fs5.mkdir(path5.dirname(abs), { recursive: true });
|
|
1083
|
+
await fs5.writeFile(abs, next, "utf8");
|
|
885
1084
|
return "updated";
|
|
886
1085
|
}
|
|
887
1086
|
function parseSetupIdeFlags(argv) {
|
|
@@ -936,6 +1135,7 @@ function summarizeOutcomes(outcomes) {
|
|
|
936
1135
|
}
|
|
937
1136
|
async function runSetupIdeFiles(o) {
|
|
938
1137
|
const outcomes = {};
|
|
1138
|
+
let cursorGlobalMcp;
|
|
939
1139
|
const repoRoot = await findRepoRoot(o.cwd);
|
|
940
1140
|
if (!repoRoot) {
|
|
941
1141
|
return {
|
|
@@ -955,21 +1155,62 @@ description: MemoraOne MCP \u2014 IDE agent instructions
|
|
|
955
1155
|
|
|
956
1156
|
` + cursorRuleBody();
|
|
957
1157
|
if (o.targets.cursor) {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1158
|
+
const detection = await detectCursorGlobalMcpConfig({
|
|
1159
|
+
homeDir: o.homeDir,
|
|
1160
|
+
explicitPath: o.cursorGlobalMcpConfigPath
|
|
1161
|
+
});
|
|
1162
|
+
if (!detection.ok) {
|
|
1163
|
+
return {
|
|
1164
|
+
exitCode: 1,
|
|
1165
|
+
repoRoot,
|
|
1166
|
+
outcomes,
|
|
1167
|
+
error: `${detection.error}
|
|
1168
|
+
${detection.candidates.join("\n ")}`
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
let npxPath;
|
|
1172
|
+
if (o.npxPathOverride !== void 0) {
|
|
1173
|
+
npxPath = o.npxPathOverride;
|
|
1174
|
+
} else {
|
|
1175
|
+
npxPath = await resolveNpxPath();
|
|
1176
|
+
}
|
|
1177
|
+
if (!npxPath) {
|
|
1178
|
+
return {
|
|
1179
|
+
exitCode: 1,
|
|
1180
|
+
repoRoot,
|
|
1181
|
+
outcomes,
|
|
1182
|
+
error: "[setup-ide-files] Could not resolve a working npx executable. Install Node.js/npm or ensure npx is on PATH before configuring Cursor global MCP."
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
967
1185
|
outcomes[".cursor/rules/memoraone-mcp.mdc"] = await writeManagedMarkdown(
|
|
968
1186
|
repoRoot,
|
|
969
1187
|
".cursor/rules/memoraone-mcp.mdc",
|
|
970
1188
|
cursorContent,
|
|
971
1189
|
{ force: o.force, dryRun: o.dryRun }
|
|
972
1190
|
);
|
|
1191
|
+
try {
|
|
1192
|
+
const globalSetup = await setupCursorGlobalMcpConfig({
|
|
1193
|
+
configPath: detection.path,
|
|
1194
|
+
npxPath,
|
|
1195
|
+
dryRun: o.dryRun
|
|
1196
|
+
});
|
|
1197
|
+
cursorGlobalMcp = {
|
|
1198
|
+
configPath: detection.path,
|
|
1199
|
+
outcome: globalSetup.outcome,
|
|
1200
|
+
npxPath,
|
|
1201
|
+
backupPath: globalSetup.backupPath
|
|
1202
|
+
};
|
|
1203
|
+
outcomes[`cursor-global:${detection.path}`] = globalSetup.outcome;
|
|
1204
|
+
} catch (err) {
|
|
1205
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1206
|
+
return {
|
|
1207
|
+
exitCode: 1,
|
|
1208
|
+
repoRoot,
|
|
1209
|
+
outcomes,
|
|
1210
|
+
cursorGlobalMcp: { configPath: detection.path, outcome: "skipped", npxPath },
|
|
1211
|
+
error: message
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
973
1214
|
}
|
|
974
1215
|
if (o.targets.vscode) {
|
|
975
1216
|
outcomes[".vscode/mcp.json"] = await writeIdeMcpJson(
|
|
@@ -996,7 +1237,7 @@ description: MemoraOne MCP \u2014 IDE agent instructions
|
|
|
996
1237
|
{ force: o.force, dryRun: o.dryRun }
|
|
997
1238
|
);
|
|
998
1239
|
}
|
|
999
|
-
return { exitCode: 0, repoRoot, outcomes };
|
|
1240
|
+
return { exitCode: 0, repoRoot, outcomes, cursorGlobalMcp };
|
|
1000
1241
|
}
|
|
1001
1242
|
async function cliSetupIdeFiles(argv) {
|
|
1002
1243
|
const { targets, force, dryRun, noGitignore, cleanup, unknown } = parseSetupIdeFlags(argv);
|
|
@@ -1013,11 +1254,18 @@ async function cliSetupIdeFiles(argv) {
|
|
|
1013
1254
|
});
|
|
1014
1255
|
if (result.error) {
|
|
1015
1256
|
console.error(result.error);
|
|
1257
|
+
if (result.repoRoot) {
|
|
1258
|
+
console.log(`[setup-ide-files] Repo root: ${result.repoRoot}`);
|
|
1259
|
+
}
|
|
1260
|
+
summarizeOutcomes(result.outcomes);
|
|
1016
1261
|
return result.exitCode;
|
|
1017
1262
|
}
|
|
1018
1263
|
if (result.repoRoot) {
|
|
1019
1264
|
console.log(`[setup-ide-files] Repo root: ${result.repoRoot}`);
|
|
1020
1265
|
}
|
|
1266
|
+
if (targets.cursor && result.cursorGlobalMcp) {
|
|
1267
|
+
logCursorGlobalMcpCliSummary(result.cursorGlobalMcp, dryRun);
|
|
1268
|
+
}
|
|
1021
1269
|
summarizeOutcomes(result.outcomes);
|
|
1022
1270
|
if (dryRun) {
|
|
1023
1271
|
console.log("[setup-ide-files] Dry run: no files written.");
|
|
@@ -1081,8 +1329,8 @@ if (args[0] === "cleanup") {
|
|
|
1081
1329
|
const raw = process.env.WORKSPACE_FOLDER_PATHS;
|
|
1082
1330
|
const parts = [];
|
|
1083
1331
|
if (raw !== void 0 && raw.trim() !== "") {
|
|
1084
|
-
for (const p of raw.split(
|
|
1085
|
-
parts.push(
|
|
1332
|
+
for (const p of raw.split(path6.delimiter).map((s) => s.trim()).filter(Boolean)) {
|
|
1333
|
+
parts.push(path6.resolve(p));
|
|
1086
1334
|
}
|
|
1087
1335
|
}
|
|
1088
1336
|
parts.push(process.cwd());
|
|
@@ -1096,10 +1344,10 @@ if (args[0] === "cleanup") {
|
|
|
1096
1344
|
}
|
|
1097
1345
|
return deduped;
|
|
1098
1346
|
}, connectWithRetry = function(socketPath) {
|
|
1099
|
-
return new Promise((
|
|
1347
|
+
return new Promise((resolve6, reject) => {
|
|
1100
1348
|
const tryConnect = (attempt) => {
|
|
1101
1349
|
const socket = net.connect(socketPath, () => {
|
|
1102
|
-
|
|
1350
|
+
resolve6(socket);
|
|
1103
1351
|
});
|
|
1104
1352
|
socket.on("error", (err) => {
|
|
1105
1353
|
if (attempt >= MAX_RETRIES) {
|
|
@@ -1135,7 +1383,7 @@ if (args[0] === "cleanup") {
|
|
|
1135
1383
|
socket = await connectWithRetry(socketPath);
|
|
1136
1384
|
} catch {
|
|
1137
1385
|
log("daemon not running, spawning...");
|
|
1138
|
-
const child = (0,
|
|
1386
|
+
const child = (0, import_node_child_process3.spawn)(
|
|
1139
1387
|
process.execPath,
|
|
1140
1388
|
buildDaemonSpawnArgs(process.argv[1], binding.projectId),
|
|
1141
1389
|
{
|