@memoraone/mcp 0.1.29 → 0.1.30

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.
Files changed (2) hide show
  1. package/dist/cli.cjs +519 -45
  2. 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.29",
33
+ version: "0.1.30",
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 path6 = __toESM(require("path"), 1);
70
+ var path7 = __toESM(require("path"), 1);
71
71
  var net = __toESM(require("net"), 1);
72
- var import_node_child_process3 = require("child_process");
72
+ var import_node_child_process4 = require("child_process");
73
73
 
74
74
  // src/socketPaths.ts
75
75
  var os = __toESM(require("os"), 1);
@@ -292,8 +292,9 @@ function encodeResolvedBinding(binding) {
292
292
  }
293
293
 
294
294
  // src/setupIdeFiles.ts
295
- var fs5 = __toESM(require("fs/promises"), 1);
296
- var path5 = __toESM(require("path"), 1);
295
+ var fs6 = __toESM(require("fs/promises"), 1);
296
+ var os4 = __toESM(require("os"), 1);
297
+ var path6 = __toESM(require("path"), 1);
297
298
 
298
299
  // src/cleanup.ts
299
300
  var fs3 = __toESM(require("fs/promises"), 1);
@@ -891,6 +892,435 @@ function logCursorGlobalMcpCliSummary(info, dryRun) {
891
892
  );
892
893
  }
893
894
 
895
+ // src/jetbrainsMcpConfig.ts
896
+ var fs5 = __toESM(require("fs/promises"), 1);
897
+ var os3 = __toESM(require("os"), 1);
898
+ var path5 = __toESM(require("path"), 1);
899
+ var import_node_child_process3 = require("child_process");
900
+
901
+ // src/configUtils.ts
902
+ var DEV_API_URL = "http://localhost:3001";
903
+
904
+ // src/jetbrainsMcpConfig.ts
905
+ var PROD_API_URL = "https://api.memoraone.com";
906
+ var JETBRAINS_DEBUG_ENV_VARS = [
907
+ "MEMORAONE_DEBUG_INIT",
908
+ "MEMORAONE_DEBUG_MINIMAL_TOOLS",
909
+ "MEMORAONE_DEBUG_MINIMAL_INITIALIZE",
910
+ "MEMORAONE_DEBUG_DIRECT_STDIO"
911
+ ];
912
+ function stripLeadingLineComments2(text) {
913
+ return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
914
+ }
915
+ async function pathExists2(filePath) {
916
+ try {
917
+ await fs5.access(filePath);
918
+ return true;
919
+ } catch {
920
+ return false;
921
+ }
922
+ }
923
+ function formatJetBrainsBackupTimestamp(d = /* @__PURE__ */ new Date()) {
924
+ const pad = (n) => String(n).padStart(2, "0");
925
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
926
+ }
927
+ function getJetBrainsGlobalMcpConfigPath(homeDir) {
928
+ return path5.join(homeDir, ".ai", "mcp", "mcp.json");
929
+ }
930
+ function getJetBrainsProjectMcpConfigPaths(repoRoot) {
931
+ return [
932
+ { kind: "project-ai", path: path5.join(repoRoot, ".ai", "mcp", "mcp.json") },
933
+ { kind: "project-ij", path: path5.join(repoRoot, ".ij", "mcp", "mcp.json") }
934
+ ];
935
+ }
936
+ function getKnownJetBrainsMcpConfigLocations(homeDir, repoRoot) {
937
+ return [
938
+ { kind: "global", path: getJetBrainsGlobalMcpConfigPath(homeDir) },
939
+ ...getJetBrainsProjectMcpConfigPaths(repoRoot)
940
+ ];
941
+ }
942
+ async function isZeroByteConfigFile(filePath) {
943
+ if (!await pathExists2(filePath)) return false;
944
+ const stat2 = await fs5.stat(filePath);
945
+ return stat2.size === 0;
946
+ }
947
+ function buildMemoraoneJetBrainsMcpServer(options) {
948
+ const env = {
949
+ MEMORAONE_API_URL: options.devMode ? DEV_API_URL : PROD_API_URL,
950
+ MEMORAONE_IDE_TYPE: "jetbrains",
951
+ MEMORAONE_M1_PATH: options.m1Path
952
+ };
953
+ if (options.devMode) {
954
+ env.MEMORAONE_DEV_MODE = "1";
955
+ }
956
+ return {
957
+ command: options.command,
958
+ args: options.args,
959
+ env
960
+ };
961
+ }
962
+ function mergeJetBrainsMcpConfigObject(existing, memoraone) {
963
+ const base = existing && typeof existing === "object" ? { ...existing } : { mcpServers: {} };
964
+ const mcpServers = typeof base.mcpServers === "object" && base.mcpServers !== null && !Array.isArray(base.mcpServers) ? { ...base.mcpServers } : {};
965
+ mcpServers.memoraone = memoraone;
966
+ return { ...base, mcpServers };
967
+ }
968
+ function memoraoneServerMatches2(server, expected) {
969
+ if (!server || typeof server !== "object") return false;
970
+ const s = server;
971
+ if (s.command !== expected.command) return false;
972
+ if (!Array.isArray(s.args) || s.args.length !== expected.args.length) return false;
973
+ for (let i = 0; i < expected.args.length; i += 1) {
974
+ if (s.args[i] !== expected.args[i]) return false;
975
+ }
976
+ const env = s.env;
977
+ if (!env || typeof env !== "object") return false;
978
+ const e = env;
979
+ for (const [key, value] of Object.entries(expected.env)) {
980
+ if (e[key] !== value) return false;
981
+ }
982
+ for (const debugKey of JETBRAINS_DEBUG_ENV_VARS) {
983
+ if (debugKey in e) return false;
984
+ }
985
+ return true;
986
+ }
987
+ function validateJetBrainsMcpConfig(parsed, expected) {
988
+ if (!parsed || typeof parsed !== "object") {
989
+ throw new Error("[setup-ide-files] JetBrains MCP config must be a JSON object.");
990
+ }
991
+ const mcpServers = parsed.mcpServers;
992
+ if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) {
993
+ throw new Error("[setup-ide-files] JetBrains MCP config missing mcpServers object.");
994
+ }
995
+ const memoraone = mcpServers.memoraone;
996
+ if (!memoraoneServerMatches2(memoraone, expected)) {
997
+ throw new Error(
998
+ "[setup-ide-files] JetBrains MCP config mcpServers.memoraone is missing or invalid."
999
+ );
1000
+ }
1001
+ }
1002
+ async function readJsonConfig(filePath) {
1003
+ const raw = await fs5.readFile(filePath, "utf8");
1004
+ if (raw.trim() === "") return null;
1005
+ return JSON.parse(stripLeadingLineComments2(raw));
1006
+ }
1007
+ async function backupConfigFile(filePath) {
1008
+ const backupPath = `${filePath}.bak-${formatJetBrainsBackupTimestamp()}`;
1009
+ await fs5.copyFile(filePath, backupPath);
1010
+ return backupPath;
1011
+ }
1012
+ async function repairZeroByteConfigFile(filePath, dryRun) {
1013
+ if (!await isZeroByteConfigFile(filePath)) {
1014
+ return { repaired: false };
1015
+ }
1016
+ if (dryRun) {
1017
+ return { repaired: true, backupPath: `${filePath}.bak-<timestamp>` };
1018
+ }
1019
+ const backupPath = await backupConfigFile(filePath);
1020
+ await fs5.unlink(filePath);
1021
+ return { repaired: true, backupPath };
1022
+ }
1023
+ function configHasMemoraone(parsed) {
1024
+ if (!parsed) return false;
1025
+ const mcpServers = parsed.mcpServers;
1026
+ if (!mcpServers || typeof mcpServers !== "object" || Array.isArray(mcpServers)) return false;
1027
+ return Boolean(mcpServers.memoraone);
1028
+ }
1029
+ async function removeMemoraoneFromProjectConfig(options) {
1030
+ const { configPath, dryRun } = options;
1031
+ if (!await pathExists2(configPath)) {
1032
+ return { changed: false };
1033
+ }
1034
+ let parsed = null;
1035
+ try {
1036
+ parsed = await readJsonConfig(configPath);
1037
+ } catch {
1038
+ return { changed: false };
1039
+ }
1040
+ if (!configHasMemoraone(parsed)) {
1041
+ return { changed: false };
1042
+ }
1043
+ if (dryRun) {
1044
+ return { changed: true, backupPath: `${configPath}.bak-<timestamp>` };
1045
+ }
1046
+ const backupPath = await backupConfigFile(configPath);
1047
+ const mcpServers = parsed && typeof parsed.mcpServers === "object" && parsed.mcpServers !== null && !Array.isArray(parsed.mcpServers) ? { ...parsed.mcpServers } : {};
1048
+ delete mcpServers.memoraone;
1049
+ const hasOtherServers = Object.keys(mcpServers).length > 0;
1050
+ if (!hasOtherServers) {
1051
+ await fs5.unlink(configPath);
1052
+ return { changed: true, backupPath };
1053
+ }
1054
+ const next = { ...parsed, mcpServers };
1055
+ await fs5.mkdir(path5.dirname(configPath), { recursive: true });
1056
+ await fs5.writeFile(configPath, JSON.stringify(next, null, 2) + "\n", "utf8");
1057
+ return { changed: true, backupPath };
1058
+ }
1059
+ async function resolveLocalCliPathAsync() {
1060
+ const here = process.argv[1] ? path5.dirname(path5.resolve(process.argv[1])) : process.cwd();
1061
+ const candidates = [
1062
+ path5.join(here, "cli.cjs"),
1063
+ path5.join(here, "..", "dist", "cli.cjs"),
1064
+ path5.join(here, "..", "..", "dist", "cli.cjs")
1065
+ ];
1066
+ for (const candidate of candidates) {
1067
+ if (await pathExists2(candidate)) {
1068
+ return path5.resolve(candidate);
1069
+ }
1070
+ }
1071
+ return null;
1072
+ }
1073
+ async function buildJetBrainsMemoraoneServer(options) {
1074
+ if (options.devMode) {
1075
+ let cliPath = options.cliPathOverride;
1076
+ if (cliPath === void 0) {
1077
+ cliPath = await resolveLocalCliPathAsync();
1078
+ }
1079
+ if (!cliPath) {
1080
+ throw new Error(
1081
+ "[setup-ide-files] Dev mode requires a built CLI at packages/mcp/dist/cli.cjs. Run pnpm build first."
1082
+ );
1083
+ }
1084
+ return buildMemoraoneJetBrainsMcpServer({
1085
+ command: process.execPath,
1086
+ args: [cliPath],
1087
+ m1Path: options.m1Path,
1088
+ devMode: true
1089
+ });
1090
+ }
1091
+ let npxPath = options.npxPathOverride;
1092
+ if (npxPath === void 0) {
1093
+ npxPath = await resolveNpxPath();
1094
+ }
1095
+ if (!npxPath) {
1096
+ throw new Error(
1097
+ "[setup-ide-files] Could not resolve a working npx executable. Install Node.js/npm or ensure npx is on PATH before configuring JetBrains MCP."
1098
+ );
1099
+ }
1100
+ return buildMemoraoneJetBrainsMcpServer({
1101
+ command: npxPath,
1102
+ args: ["-y", "@memoraone/mcp@latest"],
1103
+ m1Path: options.m1Path,
1104
+ devMode: false
1105
+ });
1106
+ }
1107
+ async function verifyJetBrainsMcpHandshake(options) {
1108
+ const timeoutMs = options.timeoutMs ?? 15e3;
1109
+ const { server } = options;
1110
+ return new Promise((resolve7) => {
1111
+ let settled = false;
1112
+ const finish = (ok, detail) => {
1113
+ if (settled) return;
1114
+ settled = true;
1115
+ clearTimeout(timer);
1116
+ try {
1117
+ child.kill();
1118
+ } catch {
1119
+ }
1120
+ resolve7({ ok, detail });
1121
+ };
1122
+ const child = (0, import_node_child_process3.spawn)(server.command, [...server.args], {
1123
+ env: { ...process.env, ...server.env },
1124
+ stdio: ["pipe", "pipe", "pipe"]
1125
+ });
1126
+ let buffer = "";
1127
+ let initializeOk = false;
1128
+ let toolsListOk = false;
1129
+ let nextId = 1;
1130
+ const send = (method, params) => {
1131
+ const msg = JSON.stringify({ jsonrpc: "2.0", id: nextId, method, params }) + "\n";
1132
+ nextId += 1;
1133
+ child.stdin?.write(msg);
1134
+ };
1135
+ const timer = setTimeout(() => {
1136
+ finish(false, `handshake timed out after ${timeoutMs}ms`);
1137
+ }, timeoutMs);
1138
+ child.stdout?.on("data", (chunk) => {
1139
+ buffer += chunk.toString("utf8");
1140
+ const lines = buffer.split("\n");
1141
+ buffer = lines.pop() ?? "";
1142
+ for (const line of lines) {
1143
+ if (!line.trim()) continue;
1144
+ let msg;
1145
+ try {
1146
+ msg = JSON.parse(line);
1147
+ } catch {
1148
+ continue;
1149
+ }
1150
+ if (msg.id === 1 && msg.result) {
1151
+ initializeOk = true;
1152
+ send("notifications/initialized", {});
1153
+ send("tools/list", {});
1154
+ }
1155
+ if (msg.id === 2 && msg.result) {
1156
+ toolsListOk = true;
1157
+ finish(true, "initialize OK; tools/list OK");
1158
+ }
1159
+ if (msg.error) {
1160
+ finish(false, `JSON-RPC error: ${JSON.stringify(msg.error)}`);
1161
+ }
1162
+ }
1163
+ });
1164
+ child.on("error", (err) => {
1165
+ finish(false, `spawn error: ${String(err)}`);
1166
+ });
1167
+ child.on("exit", (code) => {
1168
+ if (!settled) {
1169
+ if (initializeOk && toolsListOk) {
1170
+ finish(true, "initialize OK; tools/list OK");
1171
+ } else {
1172
+ finish(
1173
+ false,
1174
+ `process exited code=${code ?? "null"} (initialize=${initializeOk}, tools/list=${toolsListOk})`
1175
+ );
1176
+ }
1177
+ }
1178
+ });
1179
+ send("initialize", {
1180
+ protocolVersion: "2024-11-05",
1181
+ capabilities: {},
1182
+ clientInfo: { name: "memoraone-setup", version: "1.0.0" }
1183
+ });
1184
+ });
1185
+ }
1186
+ async function setupJetBrainsMcpConfig(options) {
1187
+ const homeDir = options.homeDir ?? os3.homedir();
1188
+ const globalPath = options.globalConfigPath ?? getJetBrainsGlobalMcpConfigPath(homeDir);
1189
+ const m1Path = path5.join(path5.resolve(options.repoRoot), "memoraone.m1");
1190
+ const repairActions = [];
1191
+ const allLocations = getKnownJetBrainsMcpConfigLocations(homeDir, options.repoRoot);
1192
+ for (const location of allLocations) {
1193
+ if (await pathExists2(location.path)) {
1194
+ repairActions.push({ type: "found-config", location });
1195
+ }
1196
+ }
1197
+ for (const location of allLocations) {
1198
+ const zeroByte = await repairZeroByteConfigFile(location.path, options.dryRun);
1199
+ if (zeroByte.repaired) {
1200
+ repairActions.push({
1201
+ type: "repaired-zero-byte",
1202
+ path: location.path,
1203
+ backupPath: zeroByte.backupPath ?? `${location.path}.bak-<timestamp>`
1204
+ });
1205
+ }
1206
+ }
1207
+ const memoraone = await buildJetBrainsMemoraoneServer({
1208
+ m1Path,
1209
+ devMode: options.devMode,
1210
+ npxPathOverride: options.npxPathOverride,
1211
+ cliPathOverride: options.cliPathOverride
1212
+ });
1213
+ for (const location of getJetBrainsProjectMcpConfigPaths(options.repoRoot)) {
1214
+ const removal = await removeMemoraoneFromProjectConfig({
1215
+ configPath: location.path,
1216
+ dryRun: options.dryRun
1217
+ });
1218
+ if (removal.changed) {
1219
+ if (removal.backupPath) {
1220
+ repairActions.push({
1221
+ type: "backed-up-conflicting-project-config",
1222
+ path: location.path,
1223
+ backupPath: removal.backupPath
1224
+ });
1225
+ }
1226
+ repairActions.push({ type: "removed-project-memoraone", path: location.path });
1227
+ }
1228
+ }
1229
+ const existed = await pathExists2(globalPath);
1230
+ let existing = null;
1231
+ if (existed) {
1232
+ try {
1233
+ existing = await readJsonConfig(globalPath);
1234
+ } catch {
1235
+ if (options.dryRun) {
1236
+ existing = null;
1237
+ } else {
1238
+ const backupPath2 = await backupConfigFile(globalPath);
1239
+ repairActions.push({
1240
+ type: "repaired-zero-byte",
1241
+ path: globalPath,
1242
+ backupPath: backupPath2
1243
+ });
1244
+ await fs5.unlink(globalPath);
1245
+ existing = null;
1246
+ }
1247
+ }
1248
+ }
1249
+ const merged = mergeJetBrainsMcpConfigObject(existing, memoraone);
1250
+ const body = JSON.stringify(merged, null, 2) + "\n";
1251
+ if (existed && existing) {
1252
+ const currentMemoraone = existing.mcpServers && typeof existing.mcpServers === "object" && !Array.isArray(existing.mcpServers) ? existing.mcpServers.memoraone : void 0;
1253
+ if (memoraoneServerMatches2(currentMemoraone, memoraone)) {
1254
+ repairActions.push({ type: "wrote-global-config", path: globalPath, outcome: "skipped" });
1255
+ return { outcome: "skipped", repairActions, memoraone };
1256
+ }
1257
+ }
1258
+ if (options.dryRun) {
1259
+ const outcome2 = existed ? "updated" : "created";
1260
+ repairActions.push({ type: "wrote-global-config", path: globalPath, outcome: outcome2 });
1261
+ return { outcome: outcome2, repairActions, memoraone };
1262
+ }
1263
+ let backupPath;
1264
+ if (existed) {
1265
+ backupPath = await backupConfigFile(globalPath);
1266
+ }
1267
+ await fs5.mkdir(path5.dirname(globalPath), { recursive: true });
1268
+ await fs5.writeFile(globalPath, body, "utf8");
1269
+ const verifyRaw = await fs5.readFile(globalPath, "utf8");
1270
+ const verifyParsed = JSON.parse(stripLeadingLineComments2(verifyRaw));
1271
+ validateJetBrainsMcpConfig(verifyParsed, memoraone);
1272
+ const outcome = existed ? "updated" : "created";
1273
+ repairActions.push({ type: "wrote-global-config", path: globalPath, outcome });
1274
+ let verifyOk;
1275
+ let verifyDetail;
1276
+ if (options.verify !== false) {
1277
+ const verify = await verifyJetBrainsMcpHandshake({ server: memoraone });
1278
+ verifyOk = verify.ok;
1279
+ verifyDetail = verify.detail;
1280
+ repairActions.push({ type: "verify-handshake", ok: verify.ok, detail: verify.detail });
1281
+ }
1282
+ return { outcome, backupPath, repairActions, verifyOk, verifyDetail, memoraone };
1283
+ }
1284
+ function logJetBrainsMcpCliSummary(info, dryRun) {
1285
+ for (const action of info.repairActions) {
1286
+ if (action.type === "found-config") {
1287
+ console.log(`[setup-ide-files] Found JetBrains MCP config (${action.location.kind}): ${action.location.path}`);
1288
+ } else if (action.type === "repaired-zero-byte") {
1289
+ console.log(`[setup-ide-files] Repaired zero-byte MCP config: ${action.path}`);
1290
+ console.log(`[setup-ide-files] Backup: ${action.backupPath}`);
1291
+ } else if (action.type === "backed-up-conflicting-project-config") {
1292
+ console.log(`[setup-ide-files] Backed up conflicting project MCP config: ${action.path}`);
1293
+ console.log(`[setup-ide-files] Backup: ${action.backupPath}`);
1294
+ } else if (action.type === "removed-project-memoraone") {
1295
+ console.log(`[setup-ide-files] Removed project-scoped memoraone definition: ${action.path}`);
1296
+ } else if (action.type === "verify-handshake") {
1297
+ if (action.ok) {
1298
+ console.log(`[setup-ide-files] MCP handshake verification: ${action.detail}`);
1299
+ } else {
1300
+ console.log(`[setup-ide-files] MCP handshake verification skipped/failed: ${action.detail}`);
1301
+ }
1302
+ }
1303
+ }
1304
+ const prefix = dryRun ? "would be " : "";
1305
+ if (info.outcome === "created") {
1306
+ console.log(`[setup-ide-files] JetBrains global MCP config ${prefix}created: ${info.activeConfigPath}`);
1307
+ } else if (info.outcome === "updated") {
1308
+ console.log(`[setup-ide-files] JetBrains global MCP config ${prefix}updated: ${info.activeConfigPath}`);
1309
+ } else {
1310
+ console.log(`[setup-ide-files] JetBrains global MCP config unchanged: ${info.activeConfigPath}`);
1311
+ }
1312
+ if (info.backupPath) {
1313
+ console.log(`[setup-ide-files] JetBrains global MCP config backup: ${info.backupPath}`);
1314
+ }
1315
+ if (info.npxPath) {
1316
+ console.log(`[setup-ide-files] Resolved npx: ${info.npxPath}`);
1317
+ }
1318
+ console.log(`[setup-ide-files] Final active JetBrains MCP config: ${info.activeConfigPath}`);
1319
+ console.log(
1320
+ "[setup-ide-files] Fully quit JetBrains IDE and reopen this repo for MCP changes to take effect."
1321
+ );
1322
+ }
1323
+
894
1324
  // src/setupIdeFiles.ts
895
1325
  var MANAGED_MARKER = "<!-- MemoraOne managed IDE helper -->";
896
1326
  var GITIGNORE_MEMORAONE_COMMENT = "# MemoraOne local project binding / API key";
@@ -906,15 +1336,15 @@ function buildMemoraoneMcpServer(ideType, command = "npx") {
906
1336
  };
907
1337
  }
908
1338
  function assertUnderRepoRoot(repoRoot, absPath) {
909
- const normRoot = path5.resolve(repoRoot) + path5.sep;
910
- const normPath = path5.resolve(absPath);
911
- if (normPath !== path5.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
1339
+ const normRoot = path6.resolve(repoRoot) + path6.sep;
1340
+ const normPath = path6.resolve(absPath);
1341
+ if (normPath !== path6.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
912
1342
  throw new Error(`[setup-ide-files] Refusing to write outside repo root: ${absPath}`);
913
1343
  }
914
1344
  }
915
- async function pathExists2(filePath) {
1345
+ async function pathExists3(filePath) {
916
1346
  try {
917
- await fs5.access(filePath);
1347
+ await fs6.access(filePath);
918
1348
  return true;
919
1349
  } catch {
920
1350
  return false;
@@ -935,12 +1365,12 @@ ${GITIGNORE_MEMORAONE_ENTRY}
935
1365
  }
936
1366
  async function ensureGitignoreMemoraone(repoRoot, opts) {
937
1367
  if (opts.noGitignore) return "skipped";
938
- const abs = path5.join(repoRoot, ".gitignore");
1368
+ const abs = path6.join(repoRoot, ".gitignore");
939
1369
  assertUnderRepoRoot(repoRoot, abs);
940
1370
  let prior = "";
941
1371
  let existed = false;
942
1372
  try {
943
- prior = await fs5.readFile(abs, "utf8");
1373
+ prior = await fs6.readFile(abs, "utf8");
944
1374
  existed = true;
945
1375
  } catch (err) {
946
1376
  const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
@@ -951,25 +1381,25 @@ async function ensureGitignoreMemoraone(repoRoot, opts) {
951
1381
  const separator = existed && prior.length > 0 ? prior.endsWith("\n") ? "\n" : "\n\n" : "";
952
1382
  const next = (existed ? prior : "") + separator + block;
953
1383
  if (opts.dryRun) return existed ? "updated" : "created";
954
- await fs5.writeFile(abs, next, "utf8");
1384
+ await fs6.writeFile(abs, next, "utf8");
955
1385
  return existed ? "updated" : "created";
956
1386
  }
957
1387
  async function findRepoRoot(startDir) {
958
- let current = path5.resolve(startDir);
959
- const root = path5.parse(current).root;
1388
+ let current = path6.resolve(startDir);
1389
+ const root = path6.parse(current).root;
960
1390
  while (true) {
961
- const gitPath = path5.join(current, ".git");
962
- const m1Path = path5.join(current, "memoraone.m1");
963
- if (await pathExists2(gitPath) || await pathExists2(m1Path)) {
1391
+ const gitPath = path6.join(current, ".git");
1392
+ const m1Path = path6.join(current, "memoraone.m1");
1393
+ if (await pathExists3(gitPath) || await pathExists3(m1Path)) {
964
1394
  return current;
965
1395
  }
966
1396
  if (current === root) {
967
1397
  return null;
968
1398
  }
969
- current = path5.dirname(current);
1399
+ current = path6.dirname(current);
970
1400
  }
971
1401
  }
972
- function stripLeadingLineComments2(text) {
1402
+ function stripLeadingLineComments3(text) {
973
1403
  return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
974
1404
  }
975
1405
  function cursorRuleBody() {
@@ -1020,42 +1450,42 @@ function buildVscodeMcpJsonBody(existing) {
1020
1450
  return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
1021
1451
  }
1022
1452
  async function writeManagedMarkdown(repoRoot, relPath, fullContent, opts) {
1023
- const abs = path5.join(repoRoot, relPath);
1453
+ const abs = path6.join(repoRoot, relPath);
1024
1454
  assertUnderRepoRoot(repoRoot, abs);
1025
1455
  let prior = "";
1026
1456
  let existed = false;
1027
1457
  try {
1028
- prior = await fs5.readFile(abs, "utf8");
1458
+ prior = await fs6.readFile(abs, "utf8");
1029
1459
  existed = true;
1030
1460
  } catch (err) {
1031
1461
  if (err?.code !== "ENOENT") throw err;
1032
1462
  }
1033
1463
  if (!existed) {
1034
1464
  if (opts.dryRun) return "created";
1035
- await fs5.mkdir(path5.dirname(abs), { recursive: true });
1036
- await fs5.writeFile(abs, fullContent, "utf8");
1465
+ await fs6.mkdir(path6.dirname(abs), { recursive: true });
1466
+ await fs6.writeFile(abs, fullContent, "utf8");
1037
1467
  return "created";
1038
1468
  }
1039
1469
  if (prior.includes(MANAGED_MARKER)) {
1040
1470
  if (prior === fullContent) return "skipped";
1041
1471
  if (opts.dryRun) return "updated";
1042
- await fs5.mkdir(path5.dirname(abs), { recursive: true });
1043
- await fs5.writeFile(abs, fullContent, "utf8");
1472
+ await fs6.mkdir(path6.dirname(abs), { recursive: true });
1473
+ await fs6.writeFile(abs, fullContent, "utf8");
1044
1474
  return "updated";
1045
1475
  }
1046
1476
  if (!opts.force) return "skipped-untracked";
1047
1477
  if (opts.dryRun) return "updated";
1048
- await fs5.mkdir(path5.dirname(abs), { recursive: true });
1049
- await fs5.writeFile(abs, fullContent, "utf8");
1478
+ await fs6.mkdir(path6.dirname(abs), { recursive: true });
1479
+ await fs6.writeFile(abs, fullContent, "utf8");
1050
1480
  return "updated";
1051
1481
  }
1052
1482
  async function writeIdeMcpJson(repoRoot, relPath, buildBody, opts) {
1053
- const abs = path5.join(repoRoot, relPath);
1483
+ const abs = path6.join(repoRoot, relPath);
1054
1484
  assertUnderRepoRoot(repoRoot, abs);
1055
1485
  let raw = "";
1056
1486
  let existed = false;
1057
1487
  try {
1058
- raw = await fs5.readFile(abs, "utf8");
1488
+ raw = await fs6.readFile(abs, "utf8");
1059
1489
  existed = true;
1060
1490
  } catch (err) {
1061
1491
  if (err?.code !== "ENOENT") throw err;
@@ -1063,15 +1493,15 @@ async function writeIdeMcpJson(repoRoot, relPath, buildBody, opts) {
1063
1493
  if (!existed) {
1064
1494
  const body = buildBody(null);
1065
1495
  if (opts.dryRun) return "created";
1066
- await fs5.mkdir(path5.dirname(abs), { recursive: true });
1067
- await fs5.writeFile(abs, body, "utf8");
1496
+ await fs6.mkdir(path6.dirname(abs), { recursive: true });
1497
+ await fs6.writeFile(abs, body, "utf8");
1068
1498
  return "created";
1069
1499
  }
1070
1500
  const managed = raw.includes(MANAGED_MARKER);
1071
1501
  if (!managed && !opts.force) return "skipped-untracked";
1072
1502
  let parsed = null;
1073
1503
  try {
1074
- parsed = JSON.parse(stripLeadingLineComments2(raw));
1504
+ parsed = JSON.parse(stripLeadingLineComments3(raw));
1075
1505
  } catch {
1076
1506
  parsed = null;
1077
1507
  }
@@ -1079,8 +1509,8 @@ async function writeIdeMcpJson(repoRoot, relPath, buildBody, opts) {
1079
1509
  const next = buildBody(parsed);
1080
1510
  if (managed && next === raw) return "skipped";
1081
1511
  if (opts.dryRun) return "updated";
1082
- await fs5.mkdir(path5.dirname(abs), { recursive: true });
1083
- await fs5.writeFile(abs, next, "utf8");
1512
+ await fs6.mkdir(path6.dirname(abs), { recursive: true });
1513
+ await fs6.writeFile(abs, next, "utf8");
1084
1514
  return "updated";
1085
1515
  }
1086
1516
  function parseSetupIdeFlags(argv) {
@@ -1092,6 +1522,8 @@ function parseSetupIdeFlags(argv) {
1092
1522
  let dryRun = false;
1093
1523
  let noGitignore = false;
1094
1524
  let cleanup = false;
1525
+ let devMode = false;
1526
+ let repair = false;
1095
1527
  const unknown = [];
1096
1528
  for (const a of argv) {
1097
1529
  if (a === "--cursor") cursor = true;
@@ -1102,6 +1534,8 @@ function parseSetupIdeFlags(argv) {
1102
1534
  else if (a === "--dry-run") dryRun = true;
1103
1535
  else if (a === "--no-gitignore") noGitignore = true;
1104
1536
  else if (a === "--cleanup") cleanup = true;
1537
+ else if (a === "--dev") devMode = true;
1538
+ else if (a === "--repair") repair = true;
1105
1539
  else if (a.startsWith("-")) unknown.push(a);
1106
1540
  }
1107
1541
  const specific = cursor || vscode || jetbrains;
@@ -1111,7 +1545,7 @@ function parseSetupIdeFlags(argv) {
1111
1545
  } else {
1112
1546
  targets = { cursor, vscode, jetbrains };
1113
1547
  }
1114
- return { targets, force, dryRun, noGitignore, cleanup, unknown };
1548
+ return { targets, force, dryRun, noGitignore, cleanup, devMode, repair, unknown };
1115
1549
  }
1116
1550
  function summarizeOutcomes(outcomes) {
1117
1551
  const created = [];
@@ -1136,6 +1570,7 @@ function summarizeOutcomes(outcomes) {
1136
1570
  async function runSetupIdeFiles(o) {
1137
1571
  const outcomes = {};
1138
1572
  let cursorGlobalMcp;
1573
+ let jetbrainsMcp;
1139
1574
  const repoRoot = await findRepoRoot(o.cwd);
1140
1575
  if (!repoRoot) {
1141
1576
  return {
@@ -1236,11 +1671,45 @@ description: MemoraOne MCP \u2014 IDE agent instructions
1236
1671
  copilotAndJetBrainsBody("MemoraOne MCP \u2014 JetBrains AI Assistant"),
1237
1672
  { force: o.force, dryRun: o.dryRun }
1238
1673
  );
1674
+ try {
1675
+ const homeDir = o.jetbrainsHomeDir ?? os4.homedir();
1676
+ const activePath = o.jetbrainsGlobalMcpConfigPath ?? getJetBrainsGlobalMcpConfigPath(homeDir);
1677
+ const jetbrainsSetup = await setupJetBrainsMcpConfig({
1678
+ homeDir,
1679
+ repoRoot,
1680
+ globalConfigPath: activePath,
1681
+ dryRun: o.dryRun,
1682
+ devMode: o.devMode,
1683
+ repair: o.repair ?? false,
1684
+ verify: o.verifyHandshake ?? !o.dryRun,
1685
+ npxPathOverride: o.npxPathOverride,
1686
+ cliPathOverride: o.cliPathOverride
1687
+ });
1688
+ jetbrainsMcp = {
1689
+ activeConfigPath: activePath,
1690
+ outcome: jetbrainsSetup.outcome,
1691
+ npxPath: jetbrainsSetup.memoraone?.command,
1692
+ backupPath: jetbrainsSetup.backupPath,
1693
+ repairActions: jetbrainsSetup.repairActions,
1694
+ verifyOk: jetbrainsSetup.verifyOk,
1695
+ verifyDetail: jetbrainsSetup.verifyDetail
1696
+ };
1697
+ outcomes[`jetbrains-global:${activePath}`] = jetbrainsSetup.outcome;
1698
+ } catch (err) {
1699
+ const message = err instanceof Error ? err.message : String(err);
1700
+ return {
1701
+ exitCode: 1,
1702
+ repoRoot,
1703
+ outcomes,
1704
+ cursorGlobalMcp,
1705
+ error: message
1706
+ };
1707
+ }
1239
1708
  }
1240
- return { exitCode: 0, repoRoot, outcomes, cursorGlobalMcp };
1709
+ return { exitCode: 0, repoRoot, outcomes, cursorGlobalMcp, jetbrainsMcp };
1241
1710
  }
1242
1711
  async function cliSetupIdeFiles(argv) {
1243
- const { targets, force, dryRun, noGitignore, cleanup, unknown } = parseSetupIdeFlags(argv);
1712
+ const { targets, force, dryRun, noGitignore, cleanup, devMode, repair, unknown } = parseSetupIdeFlags(argv);
1244
1713
  if (unknown.length) {
1245
1714
  console.error(`[setup-ide-files] Unknown option(s): ${unknown.join(", ")}`);
1246
1715
  return 1;
@@ -1250,7 +1719,9 @@ async function cliSetupIdeFiles(argv) {
1250
1719
  targets,
1251
1720
  force,
1252
1721
  dryRun,
1253
- noGitignore
1722
+ noGitignore,
1723
+ devMode,
1724
+ repair
1254
1725
  });
1255
1726
  if (result.error) {
1256
1727
  console.error(result.error);
@@ -1266,6 +1737,9 @@ async function cliSetupIdeFiles(argv) {
1266
1737
  if (targets.cursor && result.cursorGlobalMcp) {
1267
1738
  logCursorGlobalMcpCliSummary(result.cursorGlobalMcp, dryRun);
1268
1739
  }
1740
+ if (targets.jetbrains && result.jetbrainsMcp) {
1741
+ logJetBrainsMcpCliSummary(result.jetbrainsMcp, dryRun);
1742
+ }
1269
1743
  summarizeOutcomes(result.outcomes);
1270
1744
  if (dryRun) {
1271
1745
  console.log("[setup-ide-files] Dry run: no files written.");
@@ -1302,7 +1776,7 @@ if (args.includes("--version") || args.includes("-v")) {
1302
1776
  }
1303
1777
  if (args.includes("--help") || args.includes("-h")) {
1304
1778
  console.log(
1305
- "Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid> [--ide cursor|copilot-vscode|jetbrains]]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run] [--no-gitignore] [--cleanup]\n memoraone-mcp cleanup [--project-id <uuid>] [--ide cursor|copilot-vscode|jetbrains] [--dry-run] [--all-projects] [--yes]"
1779
+ "Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid> [--ide cursor|copilot-vscode|jetbrains]]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run] [--no-gitignore] [--cleanup] [--dev] [--repair]\n memoraone-mcp cleanup [--project-id <uuid>] [--ide cursor|copilot-vscode|jetbrains] [--dry-run] [--all-projects] [--yes]"
1306
1780
  );
1307
1781
  process.exit(0);
1308
1782
  }
@@ -1329,8 +1803,8 @@ if (args[0] === "cleanup") {
1329
1803
  const raw = process.env.WORKSPACE_FOLDER_PATHS;
1330
1804
  const parts = [];
1331
1805
  if (raw !== void 0 && raw.trim() !== "") {
1332
- for (const p of raw.split(path6.delimiter).map((s) => s.trim()).filter(Boolean)) {
1333
- parts.push(path6.resolve(p));
1806
+ for (const p of raw.split(path7.delimiter).map((s) => s.trim()).filter(Boolean)) {
1807
+ parts.push(path7.resolve(p));
1334
1808
  }
1335
1809
  }
1336
1810
  parts.push(process.cwd());
@@ -1344,10 +1818,10 @@ if (args[0] === "cleanup") {
1344
1818
  }
1345
1819
  return deduped;
1346
1820
  }, connectWithRetry = function(socketPath) {
1347
- return new Promise((resolve6, reject) => {
1821
+ return new Promise((resolve7, reject) => {
1348
1822
  const tryConnect = (attempt) => {
1349
1823
  const socket = net.connect(socketPath, () => {
1350
- resolve6(socket);
1824
+ resolve7(socket);
1351
1825
  });
1352
1826
  socket.on("error", (err) => {
1353
1827
  if (attempt >= MAX_RETRIES) {
@@ -1383,7 +1857,7 @@ if (args[0] === "cleanup") {
1383
1857
  socket = await connectWithRetry(socketPath);
1384
1858
  } catch {
1385
1859
  log("daemon not running, spawning...");
1386
- const child = (0, import_node_child_process3.spawn)(
1860
+ const child = (0, import_node_child_process4.spawn)(
1387
1861
  process.execPath,
1388
1862
  buildDaemonSpawnArgs(process.argv[1], binding.projectId),
1389
1863
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoraone/mcp",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {