@memoraone/mcp 0.1.36 → 0.1.38

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 +687 -384
  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.36",
33
+ version: "0.1.38",
34
34
  type: "module",
35
35
  main: "dist/index.cjs",
36
36
  bin: {
@@ -366,7 +366,7 @@ async function acquireLocalLock(lockName, options = {}) {
366
366
  `[memoraone-mcp] Failed to acquire lock ${lockName} after ${maxRetries} retries`
367
367
  );
368
368
  }
369
- await new Promise((resolve16) => setTimeout(resolve16, retryDelayMs));
369
+ await new Promise((resolve17) => setTimeout(resolve17, retryDelayMs));
370
370
  continue;
371
371
  }
372
372
  throw err;
@@ -855,9 +855,9 @@ var MemoraOneHttpError = class extends Error {
855
855
  };
856
856
 
857
857
  // src/localState/localConnectClient.ts
858
- async function requestJson(baseUrl, method, path20, options = {}) {
858
+ async function requestJson(baseUrl, method, path21, options = {}) {
859
859
  const fetchImpl = options.fetchImpl ?? fetch;
860
- const url = `${baseUrl.replace(/\/+$/, "")}${path20.startsWith("/") ? path20 : `/${path20}`}`;
860
+ const url = `${baseUrl.replace(/\/+$/, "")}${path21.startsWith("/") ? path21 : `/${path21}`}`;
861
861
  const res = await fetchImpl(url, {
862
862
  method,
863
863
  headers: {
@@ -1528,8 +1528,8 @@ var StdioLineReader = class {
1528
1528
  if (this.closed) {
1529
1529
  return null;
1530
1530
  }
1531
- return new Promise((resolve16) => {
1532
- this.waiters.push(resolve16);
1531
+ return new Promise((resolve17) => {
1532
+ this.waiters.push(resolve17);
1533
1533
  });
1534
1534
  }
1535
1535
  /** Re-queue lines read during an intermediate protocol step (e.g. roots/list) for the main bridge loop. */
@@ -1602,17 +1602,17 @@ async function requestClientRootsListUris(options) {
1602
1602
  }
1603
1603
 
1604
1604
  // src/cleanup.ts
1605
- var fs10 = __toESM(require("fs/promises"), 1);
1606
- var path13 = __toESM(require("path"), 1);
1605
+ var fs11 = __toESM(require("fs/promises"), 1);
1606
+ var path15 = __toESM(require("path"), 1);
1607
1607
  var readline2 = __toESM(require("readline/promises"), 1);
1608
1608
  var import_node_child_process3 = require("child_process");
1609
1609
  var import_node_util3 = require("util");
1610
1610
  var import_node_process = require("process");
1611
1611
 
1612
1612
  // src/cursorGlobalMcpConfig.ts
1613
- var fs9 = __toESM(require("fs/promises"), 1);
1613
+ var fs10 = __toESM(require("fs/promises"), 1);
1614
1614
  var os4 = __toESM(require("os"), 1);
1615
- var path12 = __toESM(require("path"), 1);
1615
+ var path14 = __toESM(require("path"), 1);
1616
1616
  var import_node_child_process2 = require("child_process");
1617
1617
  var import_node_util2 = require("util");
1618
1618
 
@@ -1787,19 +1787,194 @@ async function resolveBindingFromInitializeParams(params, options = {}) {
1787
1787
  });
1788
1788
  }
1789
1789
 
1790
+ // src/packageExecutionMode.ts
1791
+ var path13 = __toESM(require("path"), 1);
1792
+
1793
+ // src/resolveBuiltCliPath.ts
1794
+ var fs9 = __toESM(require("fs/promises"), 1);
1795
+ var path12 = __toESM(require("path"), 1);
1796
+ var MONOREPO_CLI_REL = path12.join("packages", "mcp", "dist", "cli.cjs");
1797
+ async function pathExists(filePath) {
1798
+ try {
1799
+ await fs9.access(filePath);
1800
+ return true;
1801
+ } catch {
1802
+ return false;
1803
+ }
1804
+ }
1805
+ async function findMonorepoCliFrom(startDir) {
1806
+ let current = path12.resolve(startDir);
1807
+ const root = path12.parse(current).root;
1808
+ while (true) {
1809
+ const candidate = path12.join(current, MONOREPO_CLI_REL);
1810
+ if (await pathExists(candidate)) {
1811
+ return path12.resolve(candidate);
1812
+ }
1813
+ if (current === root) break;
1814
+ current = path12.dirname(current);
1815
+ }
1816
+ return null;
1817
+ }
1818
+ async function resolveFromRunningScript() {
1819
+ if (!process.argv[1]) return null;
1820
+ const script = path12.resolve(process.argv[1]);
1821
+ const base = path12.basename(script);
1822
+ if ((base === "cli.cjs" || base === "cli.ts" || base === "memoraone-mcp.cjs") && await pathExists(script)) {
1823
+ return script;
1824
+ }
1825
+ const here = path12.dirname(script);
1826
+ const candidates = [
1827
+ path12.join(here, "cli.cjs"),
1828
+ path12.join(here, "..", "dist", "cli.cjs"),
1829
+ path12.join(here, "..", "..", "dist", "cli.cjs")
1830
+ ];
1831
+ for (const candidate of candidates) {
1832
+ if (await pathExists(candidate)) {
1833
+ return path12.resolve(candidate);
1834
+ }
1835
+ }
1836
+ return null;
1837
+ }
1838
+ async function resolveBuiltCliPathAsync(options) {
1839
+ const preferRunning = options?.preferRunningScript !== false;
1840
+ if (preferRunning) {
1841
+ const fromRunning = await resolveFromRunningScript();
1842
+ if (fromRunning) return fromRunning;
1843
+ }
1844
+ const searchDirs = [];
1845
+ if (options?.searchFrom !== void 0) {
1846
+ const dirs = Array.isArray(options.searchFrom) ? options.searchFrom : [options.searchFrom];
1847
+ searchDirs.push(...dirs);
1848
+ }
1849
+ searchDirs.push(process.cwd());
1850
+ const seen = /* @__PURE__ */ new Set();
1851
+ for (const dir of searchDirs) {
1852
+ const key = path12.resolve(dir);
1853
+ if (seen.has(key)) continue;
1854
+ seen.add(key);
1855
+ const found = await findMonorepoCliFrom(key);
1856
+ if (found) return found;
1857
+ }
1858
+ if (!preferRunning) {
1859
+ return resolveFromRunningScript();
1860
+ }
1861
+ return null;
1862
+ }
1863
+
1864
+ // src/packageExecutionMode.ts
1865
+ var PROD_API_URL = "https://api.memoraone.com";
1866
+ var STAGING_API_URL = "https://memora-api-staging-phbtrzocjq-uk.a.run.app";
1867
+ var STAGING_API_URL_PREFIX = "https://memora-api-staging-";
1868
+ function normalizeApiUrl(url) {
1869
+ return url.trim().replace(/\/+$/, "");
1870
+ }
1871
+ function memoraoneNpmPackageSpec(channel) {
1872
+ return channel === "staging" ? "@memoraone/mcp@staging" : "@memoraone/mcp@latest";
1873
+ }
1874
+ function isMemoraoneNpmPackageSpec(value) {
1875
+ return value === "@memoraone/mcp@staging" || value === "@memoraone/mcp@latest";
1876
+ }
1877
+ function npmPackageChannelFromEnvironment(environment) {
1878
+ return environment === "staging" ? "staging" : "latest";
1879
+ }
1880
+ function cursorEnvironmentFromPackageExecutionMode(mode) {
1881
+ if (mode.kind === "local") return "local";
1882
+ return mode.channel === "staging" ? "staging" : "production";
1883
+ }
1884
+ function npmPackageChannelFromApiUrl(apiUrl) {
1885
+ if (!apiUrl) return "latest";
1886
+ const normalized = normalizeApiUrl(apiUrl);
1887
+ if (normalized === STAGING_API_URL || normalized.startsWith(STAGING_API_URL_PREFIX)) {
1888
+ return "staging";
1889
+ }
1890
+ if (normalized === PROD_API_URL) return "latest";
1891
+ return "latest";
1892
+ }
1893
+ function looksLikeLocalMonorepoCliPath(scriptPath) {
1894
+ const normalized = scriptPath.replace(/\\/g, "/");
1895
+ if (normalized.includes("/node_modules/@memoraone/mcp/")) return false;
1896
+ if (normalized.includes("/.npm/_npx/")) return false;
1897
+ if (normalized.includes("/_npx/")) return false;
1898
+ const base = path13.basename(normalized);
1899
+ if (base === "cli.ts" || base === "cli.cjs" || base === "memoraone-mcp.cjs") {
1900
+ if (normalized.includes("/packages/mcp/dist/") || normalized.includes("/packages/mcp/dist-bin/") || normalized.includes("/packages/mcp/src/")) {
1901
+ return true;
1902
+ }
1903
+ }
1904
+ return false;
1905
+ }
1906
+ function looksLikePublishedPackagePath(scriptPath) {
1907
+ const normalized = scriptPath.replace(/\\/g, "/");
1908
+ return normalized.includes("/node_modules/@memoraone/mcp/") || normalized.includes("/.npm/_npx/") || /\/_npx\//.test(normalized);
1909
+ }
1910
+ function channelFromEnv(env2) {
1911
+ const explicit = env2.MEMORAONE_NPM_CHANNEL?.trim().toLowerCase() || env2.npm_config_tag?.trim().toLowerCase();
1912
+ if (explicit === "staging") return "staging";
1913
+ if (explicit === "latest") return "latest";
1914
+ return void 0;
1915
+ }
1916
+ async function resolvePackageExecutionMode(options = {}) {
1917
+ if (options.executionMode) return options.executionMode;
1918
+ const env2 = options.env ?? process.env;
1919
+ const scriptPath = options.scriptPath !== void 0 ? options.scriptPath : process.argv[1] ? path13.resolve(process.argv[1]) : null;
1920
+ if (scriptPath && looksLikePublishedPackagePath(scriptPath)) {
1921
+ const channel2 = channelFromEnv(env2) ?? npmPackageChannelFromApiUrl(options.apiUrl);
1922
+ return { kind: "published", channel: channel2 };
1923
+ }
1924
+ if (scriptPath && looksLikeLocalMonorepoCliPath(scriptPath)) {
1925
+ return { kind: "local", cliPath: scriptPath };
1926
+ }
1927
+ if (options.cliPath) {
1928
+ return { kind: "local", cliPath: path13.resolve(options.cliPath) };
1929
+ }
1930
+ const resolveCli = options.resolveBuiltCliPath ?? resolveBuiltCliPathAsync;
1931
+ const builtCli = await resolveCli();
1932
+ if (builtCli && looksLikeLocalMonorepoCliPath(builtCli) && !(scriptPath && looksLikePublishedPackagePath(scriptPath))) {
1933
+ return { kind: "local", cliPath: builtCli };
1934
+ }
1935
+ const channel = channelFromEnv(env2) ?? npmPackageChannelFromApiUrl(options.apiUrl);
1936
+ return { kind: "published", channel };
1937
+ }
1938
+ function setupOptionsFromPackageExecutionMode(mode) {
1939
+ if (mode.kind === "local") {
1940
+ return {
1941
+ cursorEnvironment: "local",
1942
+ devMode: true,
1943
+ npmPackageChannel: "latest",
1944
+ cursorLocalCliPathOverride: mode.cliPath,
1945
+ cliPathOverride: mode.cliPath
1946
+ };
1947
+ }
1948
+ return {
1949
+ cursorEnvironment: cursorEnvironmentFromPackageExecutionMode(mode),
1950
+ devMode: false,
1951
+ npmPackageChannel: mode.channel,
1952
+ cursorLocalCliPathOverride: null,
1953
+ cliPathOverride: null
1954
+ };
1955
+ }
1956
+ function formatIdeSetupRepairHint(workspaceRoot, mode) {
1957
+ if (mode.kind === "local") {
1958
+ return `Repair with (no new connect code): node ${mode.cliPath} setup-ide-files --all --local --dev --force --workspace-root ${workspaceRoot}`;
1959
+ }
1960
+ const spec = memoraoneNpmPackageSpec(mode.channel);
1961
+ const stagingFlag = mode.channel === "staging" ? " --staging" : "";
1962
+ return `Repair with (no new connect code): npx -y ${spec} setup-ide-files --all${stagingFlag} --force --workspace-root ${workspaceRoot}`;
1963
+ }
1964
+
1790
1965
  // src/cursorGlobalMcpConfig.ts
1791
1966
  var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
1792
1967
  var MEMORAONE_PROD_API_URL = "https://api.memoraone.com";
1793
1968
  var MEMORAONE_LOCAL_API_URL = "http://localhost:3001";
1794
1969
  var MEMORAONE_STAGING_API_URL = "https://memora-api-staging-phbtrzocjq-uk.a.run.app";
1795
1970
  var MEMORAONE_STAGING_API_URL_PREFIX = "https://memora-api-staging-";
1796
- function normalizeApiUrl(url) {
1971
+ function normalizeApiUrl2(url) {
1797
1972
  return url.trim().replace(/\/+$/, "");
1798
1973
  }
1799
1974
  function resolveIdeApiUrl(options) {
1975
+ if (options.apiUrl) return normalizeApiUrl2(options.apiUrl);
1800
1976
  if (options.environment === "staging") return MEMORAONE_STAGING_API_URL;
1801
1977
  if (options.environment === "production") return MEMORAONE_PROD_API_URL;
1802
- if (options.apiUrl) return normalizeApiUrl(options.apiUrl);
1803
1978
  return MEMORAONE_LOCAL_API_URL;
1804
1979
  }
1805
1980
  function buildMemoraoneCursorMcpServer(options) {
@@ -1811,7 +1986,7 @@ function buildMemoraoneCursorMcpServer(options) {
1811
1986
  MEMORAONE_IDE_TYPE: "cursor"
1812
1987
  };
1813
1988
  if (options.workspaceRoot !== void 0) {
1814
- env2[MEMORAONE_WORKSPACE_ROOT_ENV] = path12.resolve(options.workspaceRoot);
1989
+ env2[MEMORAONE_WORKSPACE_ROOT_ENV] = path14.resolve(options.workspaceRoot);
1815
1990
  }
1816
1991
  if (options.environment === "local") {
1817
1992
  if (!options.cliPath) {
@@ -1826,15 +2001,16 @@ function buildMemoraoneCursorMcpServer(options) {
1826
2001
  if (!options.npxPath) {
1827
2002
  throw new Error("[setup-ide-files] Cursor MCP config requires a resolved npx path.");
1828
2003
  }
2004
+ const channel = options.npmPackageChannel ?? npmPackageChannelFromEnvironment(options.environment);
1829
2005
  return {
1830
2006
  command: options.npxPath,
1831
- args: ["-y", "@memoraone/mcp@latest"],
2007
+ args: ["-y", memoraoneNpmPackageSpec(channel)],
1832
2008
  env: env2
1833
2009
  };
1834
2010
  }
1835
- async function pathExists(filePath) {
2011
+ async function pathExists2(filePath) {
1836
2012
  try {
1837
- await fs9.access(filePath);
2013
+ await fs10.access(filePath);
1838
2014
  return true;
1839
2015
  } catch {
1840
2016
  return false;
@@ -1844,17 +2020,17 @@ function stripLeadingLineComments(text) {
1844
2020
  return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
1845
2021
  }
1846
2022
  function getKnownCursorGlobalMcpConfigCandidates(homeDir) {
1847
- return [path12.join(homeDir, ".cursor", "mcp.json")];
2023
+ return [path14.join(homeDir, ".cursor", "mcp.json")];
1848
2024
  }
1849
2025
  async function detectCursorGlobalMcpConfig(options) {
1850
2026
  if (options?.explicitPath) {
1851
- return { ok: true, path: options.explicitPath, detectedExisting: await pathExists(options.explicitPath) };
2027
+ return { ok: true, path: options.explicitPath, detectedExisting: await pathExists2(options.explicitPath) };
1852
2028
  }
1853
2029
  const homeDir = options?.homeDir ?? os4.homedir();
1854
2030
  const candidates = getKnownCursorGlobalMcpConfigCandidates(homeDir);
1855
2031
  const existing = [];
1856
2032
  for (const candidate of candidates) {
1857
- if (await pathExists(candidate)) existing.push(candidate);
2033
+ if (await pathExists2(candidate)) existing.push(candidate);
1858
2034
  }
1859
2035
  if (existing.length > 1) {
1860
2036
  return {
@@ -1882,10 +2058,10 @@ function formatBackupTimestamp(d = /* @__PURE__ */ new Date()) {
1882
2058
  }
1883
2059
  async function isWorkingNpx(npxPath) {
1884
2060
  try {
1885
- if (!await pathExists(npxPath)) return false;
2061
+ if (!await pathExists2(npxPath)) return false;
1886
2062
  if (process.platform !== "win32") {
1887
2063
  try {
1888
- await fs9.access(npxPath, fs9.constants.X_OK);
2064
+ await fs10.access(npxPath, fs10.constants.X_OK);
1889
2065
  } catch {
1890
2066
  return false;
1891
2067
  }
@@ -1907,7 +2083,7 @@ async function resolveNpxPath() {
1907
2083
  const pathSep = process.platform === "win32" ? ";" : ":";
1908
2084
  for (const dir of (process.env.PATH ?? "").split(pathSep)) {
1909
2085
  if (!dir) continue;
1910
- candidates.push(path12.join(dir, npxName));
2086
+ candidates.push(path14.join(dir, npxName));
1911
2087
  }
1912
2088
  try {
1913
2089
  const lookupCmd = process.platform === "win32" ? "where" : "which";
@@ -1918,7 +2094,7 @@ async function resolveNpxPath() {
1918
2094
  }
1919
2095
  const seen = /* @__PURE__ */ new Set();
1920
2096
  for (const candidate of candidates) {
1921
- const abs = path12.isAbsolute(candidate) ? candidate : path12.resolve(candidate);
2097
+ const abs = path14.isAbsolute(candidate) ? candidate : path14.resolve(candidate);
1922
2098
  const key = process.platform === "win32" ? abs.toLowerCase() : abs;
1923
2099
  if (seen.has(key)) continue;
1924
2100
  seen.add(key);
@@ -1930,12 +2106,14 @@ function mergeCursorRepoMcpConfigObject(existing, writeOptions) {
1930
2106
  const environment = writeOptions.environment ?? "production";
1931
2107
  const base = existing && typeof existing === "object" ? { ...existing } : { mcpServers: {} };
1932
2108
  const mcpServers = typeof base.mcpServers === "object" && base.mcpServers !== null && !Array.isArray(base.mcpServers) ? { ...base.mcpServers } : {};
2109
+ delete mcpServers.memoraone;
1933
2110
  mcpServers.memoraone = buildMemoraoneCursorMcpServer({
1934
2111
  environment,
1935
2112
  npxPath: writeOptions.npxPath,
1936
2113
  cliPath: writeOptions.cliPath,
1937
2114
  workspaceRoot: writeOptions.repoRoot,
1938
- apiUrl: writeOptions.apiUrl
2115
+ apiUrl: writeOptions.apiUrl,
2116
+ npmPackageChannel: writeOptions.npmPackageChannel
1939
2117
  });
1940
2118
  return { ...base, mcpServers };
1941
2119
  }
@@ -1949,7 +2127,7 @@ function isManagedMemoraoneCursorServer(server) {
1949
2127
  if (!server || typeof server !== "object") return false;
1950
2128
  const s = server;
1951
2129
  if (!Array.isArray(s.args) || s.args.length !== 2) return false;
1952
- if (s.args[0] !== "-y" || s.args[1] !== "@memoraone/mcp@latest") return false;
2130
+ if (s.args[0] !== "-y" || !isMemoraoneNpmPackageSpec(s.args[1])) return false;
1953
2131
  const env2 = s.env;
1954
2132
  if (!env2 || typeof env2 !== "object") return false;
1955
2133
  return memoraoneEnvMatchesManagedCleanupShape(env2);
@@ -1964,11 +2142,11 @@ function memoraoneEnvMatchesManagedCleanupShape(env2) {
1964
2142
  return env2.MEMORAONE_IDE_TYPE === "cursor" && isMemoraoneManagedApiUrl(env2.MEMORAONE_API_URL);
1965
2143
  }
1966
2144
  function getCursorRepoMcpConfigPath(repoRoot) {
1967
- return path12.join(repoRoot, ".cursor", "mcp.json");
2145
+ return path14.join(repoRoot, ".cursor", "mcp.json");
1968
2146
  }
1969
2147
  async function readCursorMcpConfigObject(configPath) {
1970
2148
  try {
1971
- const raw = await fs9.readFile(configPath, "utf8");
2149
+ const raw = await fs10.readFile(configPath, "utf8");
1972
2150
  return JSON.parse(stripLeadingLineComments(raw));
1973
2151
  } catch (err) {
1974
2152
  const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
@@ -1986,21 +2164,21 @@ async function removeMemoraoneFromCursorGlobalConfig(options) {
1986
2164
  return { changed: true, backupPath: `${configPath}.backup-<timestamp>` };
1987
2165
  }
1988
2166
  const backupPath = `${configPath}.backup-${formatBackupTimestamp()}`;
1989
- await fs9.copyFile(configPath, backupPath);
2167
+ await fs10.copyFile(configPath, backupPath);
1990
2168
  const mcpServers = typeof parsed2.mcpServers === "object" && parsed2.mcpServers !== null && !Array.isArray(parsed2.mcpServers) ? { ...parsed2.mcpServers } : {};
1991
2169
  delete mcpServers.memoraone;
1992
2170
  const hasOtherServers = Object.keys(mcpServers).length > 0;
1993
2171
  if (!hasOtherServers) {
1994
- await fs9.unlink(configPath);
2172
+ await fs10.unlink(configPath);
1995
2173
  return { changed: true, backupPath };
1996
2174
  }
1997
2175
  const next = { ...parsed2, mcpServers };
1998
- await fs9.mkdir(path12.dirname(configPath), { recursive: true });
1999
- await fs9.writeFile(configPath, JSON.stringify(next, null, 2) + "\n", "utf8");
2176
+ await fs10.mkdir(path14.dirname(configPath), { recursive: true });
2177
+ await fs10.writeFile(configPath, JSON.stringify(next, null, 2) + "\n", "utf8");
2000
2178
  return { changed: true, backupPath };
2001
2179
  }
2002
2180
  async function auditCursorMcpConfig(options) {
2003
- const repoRoot = path12.resolve(options?.repoRoot ?? process.cwd());
2181
+ const repoRoot = path14.resolve(options?.repoRoot ?? process.cwd());
2004
2182
  const repoConfigPath = getCursorRepoMcpConfigPath(repoRoot);
2005
2183
  const globalDetection = await detectCursorGlobalMcpConfig({
2006
2184
  homeDir: options?.homeDir,
@@ -2058,45 +2236,46 @@ function logCursorMcpConfigAudit(prefix, audit) {
2058
2236
  function logCursorMcpCliSummary(info, dryRun, opts) {
2059
2237
  const { repoConfigPath, repoOutcome, npxPath, cliPath, repoBackupPath, globalConfigPath, globalMemoraoneRemoved, globalBackupPath } = info;
2060
2238
  const interactive = opts?.forInteractivePostSetup === true;
2061
- console.log(`[setup-ide-files] Cursor repo MCP config: ${repoConfigPath}`);
2239
+ const println = opts?.println ?? console.log;
2240
+ println(`[setup-ide-files] Cursor repo MCP config: ${repoConfigPath}`);
2062
2241
  if (cliPath) {
2063
- console.log(`[setup-ide-files] Resolved local CLI: ${cliPath}`);
2242
+ println(`[setup-ide-files] Resolved local CLI: ${cliPath}`);
2064
2243
  } else if (npxPath) {
2065
- console.log(`[setup-ide-files] Resolved npx: ${npxPath}`);
2244
+ println(`[setup-ide-files] Resolved npx: ${npxPath}`);
2066
2245
  }
2067
2246
  if (repoBackupPath) {
2068
- console.log(`[setup-ide-files] Cursor repo MCP config backup: ${repoBackupPath}`);
2247
+ println(`[setup-ide-files] Cursor repo MCP config backup: ${repoBackupPath}`);
2069
2248
  }
2070
2249
  if (!interactive) {
2071
2250
  if (repoOutcome === "created") {
2072
- console.log(
2251
+ println(
2073
2252
  dryRun ? `[setup-ide-files] Cursor repo MCP config would be created: ${repoConfigPath}` : `[setup-ide-files] Cursor repo MCP config created: ${repoConfigPath}`
2074
2253
  );
2075
2254
  } else if (repoOutcome === "updated") {
2076
- console.log(
2255
+ println(
2077
2256
  dryRun ? `[setup-ide-files] Cursor repo MCP config would be updated: ${repoConfigPath}` : `[setup-ide-files] Cursor repo MCP config updated: ${repoConfigPath}`
2078
2257
  );
2079
2258
  } else if (repoOutcome === "skipped") {
2080
- console.log(`[setup-ide-files] Cursor repo MCP config unchanged: ${repoConfigPath}`);
2259
+ println(`[setup-ide-files] Cursor repo MCP config unchanged: ${repoConfigPath}`);
2081
2260
  }
2082
2261
  }
2083
2262
  if (globalMemoraoneRemoved && globalConfigPath) {
2084
- console.log(
2263
+ println(
2085
2264
  dryRun ? `[setup-ide-files] Would remove memoraone from Cursor global MCP config: ${globalConfigPath}` : `[setup-ide-files] Removed memoraone from Cursor global MCP config: ${globalConfigPath}`
2086
2265
  );
2087
2266
  if (globalBackupPath) {
2088
- console.log(`[setup-ide-files] Cursor global MCP config backup: ${globalBackupPath}`);
2267
+ println(`[setup-ide-files] Cursor global MCP config backup: ${globalBackupPath}`);
2089
2268
  }
2090
2269
  } else if (globalConfigPath) {
2091
- console.log(
2270
+ println(
2092
2271
  `[setup-ide-files] Cursor global MCP config unchanged (no managed memoraone to remove): ${globalConfigPath}`
2093
2272
  );
2094
2273
  }
2095
- console.log(
2274
+ println(
2096
2275
  "[setup-ide-files] Each Cursor window uses this repo\u2019s .cursor/mcp.json (separate MCP process per repo)."
2097
2276
  );
2098
2277
  if (!interactive) {
2099
- console.log(
2278
+ println(
2100
2279
  "[setup-ide-files] Fully quit Cursor and reopen this repo for MCP changes to take effect."
2101
2280
  );
2102
2281
  }
@@ -2177,7 +2356,7 @@ async function defaultListSocketPaths(projectId) {
2177
2356
  const baseDir = getMcpBaseDir();
2178
2357
  let entries;
2179
2358
  try {
2180
- entries = await fs10.readdir(baseDir);
2359
+ entries = await fs11.readdir(baseDir);
2181
2360
  } catch (err) {
2182
2361
  const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
2183
2362
  if (code === "ENOENT") {
@@ -2190,7 +2369,7 @@ async function defaultListSocketPaths(projectId) {
2190
2369
  if (!name.endsWith(".sock") || !isMemoraoneSocketFilename(name)) {
2191
2370
  continue;
2192
2371
  }
2193
- const socketPath = path13.join(baseDir, name);
2372
+ const socketPath = path15.join(baseDir, name);
2194
2373
  if (projectId === null) {
2195
2374
  paths.push(socketPath);
2196
2375
  continue;
@@ -2210,11 +2389,11 @@ async function defaultListSocketPaths(projectId) {
2210
2389
  return paths.sort();
2211
2390
  }
2212
2391
  async function defaultListSocketPathsForWorkspaceRoot(workspaceRoot) {
2213
- const resolvedRoot = path13.resolve(workspaceRoot);
2392
+ const resolvedRoot = path15.resolve(workspaceRoot);
2214
2393
  const baseDir = getMcpBaseDir();
2215
2394
  let entries;
2216
2395
  try {
2217
- entries = await fs10.readdir(baseDir);
2396
+ entries = await fs11.readdir(baseDir);
2218
2397
  } catch (err) {
2219
2398
  const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
2220
2399
  if (code === "ENOENT") {
@@ -2227,10 +2406,10 @@ async function defaultListSocketPathsForWorkspaceRoot(workspaceRoot) {
2227
2406
  if (!name.endsWith(".sock") || !isHashSocketFilename(name)) {
2228
2407
  continue;
2229
2408
  }
2230
- const socketPath = path13.join(baseDir, name);
2409
+ const socketPath = path15.join(baseDir, name);
2231
2410
  const record = readBindingSidecarRecord(socketPath);
2232
2411
  if (!record?.workspaceRoot) continue;
2233
- if (path13.resolve(record.workspaceRoot) === resolvedRoot) {
2412
+ if (path15.resolve(record.workspaceRoot) === resolvedRoot) {
2234
2413
  paths.push(socketPath);
2235
2414
  }
2236
2415
  }
@@ -2239,21 +2418,21 @@ async function defaultListSocketPathsForWorkspaceRoot(workspaceRoot) {
2239
2418
  async function filterSocketPathsByIdeForCleanup(socketPaths, projectId, ide, workspaceRoot) {
2240
2419
  if (ide === void 0) return socketPaths;
2241
2420
  const normalizedProjectId = projectId.trim().toLowerCase();
2242
- const resolvedRoot = workspaceRoot ? path13.resolve(workspaceRoot) : null;
2421
+ const resolvedRoot = workspaceRoot ? path15.resolve(workspaceRoot) : null;
2243
2422
  const filtered = [];
2244
2423
  for (const socketPath of socketPaths) {
2245
- const basename8 = path13.basename(socketPath);
2246
- if (isLegacySocketFilename(basename8)) {
2247
- if (isSocketFilenameForProjectAndIde(basename8, normalizedProjectId, ide)) {
2424
+ const basename11 = path15.basename(socketPath);
2425
+ if (isLegacySocketFilename(basename11)) {
2426
+ if (isSocketFilenameForProjectAndIde(basename11, normalizedProjectId, ide)) {
2248
2427
  filtered.push(socketPath);
2249
2428
  }
2250
2429
  continue;
2251
2430
  }
2252
- if (isHashSocketFilename(basename8)) {
2431
+ if (isHashSocketFilename(basename11)) {
2253
2432
  const record = readBindingSidecarRecord(socketPath);
2254
2433
  if (!record || record.ideType !== ide) continue;
2255
2434
  const sameProject = record.projectId.trim().toLowerCase() === normalizedProjectId;
2256
- const sameWorkspace = resolvedRoot !== null && path13.resolve(record.workspaceRoot) === resolvedRoot;
2435
+ const sameWorkspace = resolvedRoot !== null && path15.resolve(record.workspaceRoot) === resolvedRoot;
2257
2436
  if (sameProject || sameWorkspace) {
2258
2437
  filtered.push(socketPath);
2259
2438
  }
@@ -2265,9 +2444,9 @@ async function defaultKillProcess(pid) {
2265
2444
  process.kill(pid, "SIGTERM");
2266
2445
  }
2267
2446
  async function defaultRemoveSocket(socketPath) {
2268
- await fs10.unlink(socketPath);
2447
+ await fs11.unlink(socketPath);
2269
2448
  try {
2270
- await fs10.unlink(bindingSidecarPath(socketPath));
2449
+ await fs11.unlink(bindingSidecarPath(socketPath));
2271
2450
  } catch {
2272
2451
  }
2273
2452
  }
@@ -2285,7 +2464,7 @@ async function defaultConfirm(message) {
2285
2464
  }
2286
2465
  async function resolveCleanupTarget(cwd2) {
2287
2466
  try {
2288
- const binding = await resolveAuthoritativeBinding([path13.resolve(cwd2)]);
2467
+ const binding = await resolveAuthoritativeBinding([path15.resolve(cwd2)]);
2289
2468
  return {
2290
2469
  workspaceRoot: binding.workspaceRoot,
2291
2470
  repositoryBindingId: binding.repositoryBindingId,
@@ -2463,7 +2642,7 @@ async function runCleanup(opts) {
2463
2642
  projectIds.add(proc.projectId);
2464
2643
  }
2465
2644
  for (const socketPath of socketPaths) {
2466
- const id = extractProjectIdFromSocketFilename(path13.basename(socketPath));
2645
+ const id = extractProjectIdFromSocketFilename(path15.basename(socketPath));
2467
2646
  if (id) {
2468
2647
  projectIds.add(id);
2469
2648
  continue;
@@ -2649,9 +2828,9 @@ function summarizeJsonRpcMethod(line) {
2649
2828
  }
2650
2829
  }
2651
2830
  function connectWithRetry(socketPath, log, maxRetries, retryDelayMs, connect2) {
2652
- return new Promise((resolve16, reject) => {
2831
+ return new Promise((resolve17, reject) => {
2653
2832
  const tryConnect = (attempt) => {
2654
- connect2(socketPath).then(resolve16).catch((err) => {
2833
+ connect2(socketPath).then(resolve17).catch((err) => {
2655
2834
  if (attempt >= maxRetries) {
2656
2835
  reject(err);
2657
2836
  return;
@@ -2767,8 +2946,8 @@ var BridgeDaemonRouter = class {
2767
2946
  this.maxRetries = options.maxRetries ?? 5;
2768
2947
  this.retryDelayMs = options.retryDelayMs ?? 200;
2769
2948
  this.lineReader = options.lineReader ?? null;
2770
- this.connectImpl = options.connect ?? ((socketPath) => new Promise((resolve16, reject) => {
2771
- const socket = net.connect(socketPath, () => resolve16(socket));
2949
+ this.connectImpl = options.connect ?? ((socketPath) => new Promise((resolve17, reject) => {
2950
+ const socket = net.connect(socketPath, () => resolve17(socket));
2772
2951
  socket.on("error", reject);
2773
2952
  }));
2774
2953
  this.spawnDaemonImpl = options.spawnDaemon ?? (async (binding) => {
@@ -3032,12 +3211,14 @@ async function runBridgeProxy(options) {
3032
3211
  // src/setupIdeFiles.ts
3033
3212
  var fs13 = __toESM(require("fs/promises"), 1);
3034
3213
  var os6 = __toESM(require("os"), 1);
3035
- var path16 = __toESM(require("path"), 1);
3214
+ var path17 = __toESM(require("path"), 1);
3215
+ var import_node_crypto5 = require("crypto");
3036
3216
 
3037
3217
  // src/jetbrainsMcpConfig.ts
3038
- var fs11 = __toESM(require("fs/promises"), 1);
3218
+ var fs12 = __toESM(require("fs/promises"), 1);
3039
3219
  var os5 = __toESM(require("os"), 1);
3040
- var path14 = __toESM(require("path"), 1);
3220
+ var path16 = __toESM(require("path"), 1);
3221
+ var import_node_crypto4 = require("crypto");
3041
3222
  var import_node_child_process5 = require("child_process");
3042
3223
 
3043
3224
  // src/configUtils.ts
@@ -3059,7 +3240,6 @@ function resolveApiUrl(env2) {
3059
3240
  }
3060
3241
 
3061
3242
  // src/jetbrainsMcpConfig.ts
3062
- var PROD_API_URL = "https://api.memoraone.com";
3063
3243
  var JETBRAINS_DEBUG_ENV_VARS = [
3064
3244
  "MEMORAONE_DEBUG_INIT",
3065
3245
  "MEMORAONE_DEBUG_MINIMAL_TOOLS",
@@ -3069,9 +3249,9 @@ var JETBRAINS_DEBUG_ENV_VARS = [
3069
3249
  function stripLeadingLineComments2(text) {
3070
3250
  return text.split("\n").filter((line) => !/^\s*\/\//.test(line)).join("\n");
3071
3251
  }
3072
- async function pathExists2(filePath) {
3252
+ async function pathExists3(filePath) {
3073
3253
  try {
3074
- await fs11.access(filePath);
3254
+ await fs12.access(filePath);
3075
3255
  return true;
3076
3256
  } catch {
3077
3257
  return false;
@@ -3082,12 +3262,12 @@ function formatJetBrainsBackupTimestamp(d = /* @__PURE__ */ new Date()) {
3082
3262
  return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
3083
3263
  }
3084
3264
  function getJetBrainsGlobalMcpConfigPath(homeDir) {
3085
- return path14.join(homeDir, ".ai", "mcp", "mcp.json");
3265
+ return path16.join(homeDir, ".ai", "mcp", "mcp.json");
3086
3266
  }
3087
3267
  function getJetBrainsProjectMcpConfigPaths(repoRoot) {
3088
3268
  return [
3089
- { kind: "project-ai", path: path14.join(repoRoot, ".ai", "mcp", "mcp.json") },
3090
- { kind: "project-ij", path: path14.join(repoRoot, ".ij", "mcp", "mcp.json") }
3269
+ { kind: "project-ai", path: path16.join(repoRoot, ".ai", "mcp", "mcp.json") },
3270
+ { kind: "project-ij", path: path16.join(repoRoot, ".ij", "mcp", "mcp.json") }
3091
3271
  ];
3092
3272
  }
3093
3273
  function getKnownJetBrainsMcpConfigLocations(homeDir, repoRoot) {
@@ -3097,17 +3277,21 @@ function getKnownJetBrainsMcpConfigLocations(homeDir, repoRoot) {
3097
3277
  ];
3098
3278
  }
3099
3279
  async function isZeroByteConfigFile(filePath) {
3100
- if (!await pathExists2(filePath)) return false;
3101
- const stat4 = await fs11.stat(filePath);
3280
+ if (!await pathExists3(filePath)) return false;
3281
+ const stat4 = await fs12.stat(filePath);
3102
3282
  return stat4.size === 0;
3103
3283
  }
3104
3284
  function buildMemoraoneJetBrainsMcpServer(options) {
3285
+ const environment = options.environment ?? (options.devMode ? "local" : "production");
3105
3286
  const env2 = {
3106
- MEMORAONE_API_URL: options.devMode ? options.apiUrl ? normalizeApiUrl(options.apiUrl) : DEV_API_URL : PROD_API_URL,
3287
+ MEMORAONE_API_URL: resolveIdeApiUrl({
3288
+ environment,
3289
+ apiUrl: options.apiUrl ?? (environment === "local" ? DEV_API_URL : void 0)
3290
+ }),
3107
3291
  MEMORAONE_IDE_TYPE: "jetbrains",
3108
- [MEMORAONE_WORKSPACE_ROOT_ENV]: path14.resolve(options.workspaceRoot)
3292
+ [MEMORAONE_WORKSPACE_ROOT_ENV]: path16.resolve(options.workspaceRoot)
3109
3293
  };
3110
- if (options.devMode) {
3294
+ if (environment === "local" || options.devMode) {
3111
3295
  env2.MEMORAONE_DEV_MODE = "1";
3112
3296
  }
3113
3297
  if ("MEMORAONE_M1_PATH" in env2 || "MEMORAONE_API_KEY" in env2) {
@@ -3122,9 +3306,28 @@ function buildMemoraoneJetBrainsMcpServer(options) {
3122
3306
  function mergeJetBrainsMcpConfigObject(existing, memoraone) {
3123
3307
  const base = existing && typeof existing === "object" ? { ...existing } : { mcpServers: {} };
3124
3308
  const mcpServers = typeof base.mcpServers === "object" && base.mcpServers !== null && !Array.isArray(base.mcpServers) ? { ...base.mcpServers } : {};
3309
+ delete mcpServers.memoraone;
3125
3310
  mcpServers.memoraone = memoraone;
3126
3311
  return { ...base, mcpServers };
3127
3312
  }
3313
+ async function writeJetBrainsMcpJsonAtomic(filePath, content) {
3314
+ const dir = path16.dirname(filePath);
3315
+ await fs12.mkdir(dir, { recursive: true });
3316
+ const tmpPath = path16.join(
3317
+ dir,
3318
+ `.${path16.basename(filePath)}.${process.pid}.${(0, import_node_crypto4.randomBytes)(8).toString("hex")}.tmp`
3319
+ );
3320
+ try {
3321
+ await fs12.writeFile(tmpPath, content, "utf8");
3322
+ await fs12.rename(tmpPath, filePath);
3323
+ } catch (err) {
3324
+ try {
3325
+ await fs12.unlink(tmpPath);
3326
+ } catch {
3327
+ }
3328
+ throw err;
3329
+ }
3330
+ }
3128
3331
  function memoraoneServerMatches(server, expected) {
3129
3332
  if (!server || typeof server !== "object") return false;
3130
3333
  const s = server;
@@ -3160,13 +3363,13 @@ function validateJetBrainsMcpConfig(parsed2, expected) {
3160
3363
  }
3161
3364
  }
3162
3365
  async function readJsonConfig(filePath) {
3163
- const raw = await fs11.readFile(filePath, "utf8");
3366
+ const raw = await fs12.readFile(filePath, "utf8");
3164
3367
  if (raw.trim() === "") return null;
3165
3368
  return JSON.parse(stripLeadingLineComments2(raw));
3166
3369
  }
3167
3370
  async function backupConfigFile(filePath) {
3168
3371
  const backupPath = `${filePath}.bak-${formatJetBrainsBackupTimestamp()}`;
3169
- await fs11.copyFile(filePath, backupPath);
3372
+ await fs12.copyFile(filePath, backupPath);
3170
3373
  return backupPath;
3171
3374
  }
3172
3375
  async function repairZeroByteConfigFile(filePath, dryRun) {
@@ -3177,7 +3380,7 @@ async function repairZeroByteConfigFile(filePath, dryRun) {
3177
3380
  return { repaired: true, backupPath: `${filePath}.bak-<timestamp>` };
3178
3381
  }
3179
3382
  const backupPath = await backupConfigFile(filePath);
3180
- await fs11.unlink(filePath);
3383
+ await fs12.unlink(filePath);
3181
3384
  return { repaired: true, backupPath };
3182
3385
  }
3183
3386
  function configHasMemoraone(parsed2) {
@@ -3188,7 +3391,7 @@ function configHasMemoraone(parsed2) {
3188
3391
  }
3189
3392
  async function removeMemoraoneFromProjectConfig(options) {
3190
3393
  const { configPath, dryRun } = options;
3191
- if (!await pathExists2(configPath)) {
3394
+ if (!await pathExists3(configPath)) {
3192
3395
  return { changed: false };
3193
3396
  }
3194
3397
  let parsed2 = null;
@@ -3208,30 +3411,31 @@ async function removeMemoraoneFromProjectConfig(options) {
3208
3411
  delete mcpServers.memoraone;
3209
3412
  const hasOtherServers = Object.keys(mcpServers).length > 0;
3210
3413
  if (!hasOtherServers) {
3211
- await fs11.unlink(configPath);
3414
+ await fs12.unlink(configPath);
3212
3415
  return { changed: true, backupPath };
3213
3416
  }
3214
3417
  const next = { ...parsed2, mcpServers };
3215
- await fs11.mkdir(path14.dirname(configPath), { recursive: true });
3216
- await fs11.writeFile(configPath, JSON.stringify(next, null, 2) + "\n", "utf8");
3418
+ await fs12.mkdir(path16.dirname(configPath), { recursive: true });
3419
+ await fs12.writeFile(configPath, JSON.stringify(next, null, 2) + "\n", "utf8");
3217
3420
  return { changed: true, backupPath };
3218
3421
  }
3219
3422
  async function resolveLocalCliPathAsync() {
3220
- const here = process.argv[1] ? path14.dirname(path14.resolve(process.argv[1])) : process.cwd();
3423
+ const here = process.argv[1] ? path16.dirname(path16.resolve(process.argv[1])) : process.cwd();
3221
3424
  const candidates = [
3222
- path14.join(here, "cli.cjs"),
3223
- path14.join(here, "..", "dist", "cli.cjs"),
3224
- path14.join(here, "..", "..", "dist", "cli.cjs")
3425
+ path16.join(here, "cli.cjs"),
3426
+ path16.join(here, "..", "dist", "cli.cjs"),
3427
+ path16.join(here, "..", "..", "dist", "cli.cjs")
3225
3428
  ];
3226
3429
  for (const candidate of candidates) {
3227
- if (await pathExists2(candidate)) {
3228
- return path14.resolve(candidate);
3430
+ if (await pathExists3(candidate)) {
3431
+ return path16.resolve(candidate);
3229
3432
  }
3230
3433
  }
3231
3434
  return null;
3232
3435
  }
3233
3436
  async function buildJetBrainsMemoraoneServer(options) {
3234
- if (options.devMode) {
3437
+ const environment = options.environment ?? (options.devMode ? "local" : "production");
3438
+ if (environment === "local" || options.devMode) {
3235
3439
  let cliPath = options.cliPathOverride;
3236
3440
  if (cliPath === void 0) {
3237
3441
  cliPath = await resolveLocalCliPathAsync();
@@ -3245,6 +3449,7 @@ async function buildJetBrainsMemoraoneServer(options) {
3245
3449
  command: process.execPath,
3246
3450
  args: [cliPath],
3247
3451
  workspaceRoot: options.workspaceRoot,
3452
+ environment: "local",
3248
3453
  devMode: true,
3249
3454
  apiUrl: options.apiUrl
3250
3455
  });
@@ -3258,19 +3463,31 @@ async function buildJetBrainsMemoraoneServer(options) {
3258
3463
  "[setup-ide-files] Could not resolve a working npx executable. Install Node.js/npm or ensure npx is on PATH before configuring JetBrains MCP."
3259
3464
  );
3260
3465
  }
3466
+ const channel = options.npmPackageChannel ?? npmPackageChannelFromEnvironment(environment);
3261
3467
  return buildMemoraoneJetBrainsMcpServer({
3262
3468
  command: npxPath,
3263
- args: ["-y", "@memoraone/mcp@latest"],
3469
+ args: ["-y", memoraoneNpmPackageSpec(channel)],
3264
3470
  workspaceRoot: options.workspaceRoot,
3265
- devMode: false
3471
+ environment,
3472
+ devMode: false,
3473
+ apiUrl: options.apiUrl
3266
3474
  });
3267
3475
  }
3476
+ function isOptionalJetBrainsHandshakeUnavailable(detail) {
3477
+ if (!detail) return false;
3478
+ if (detail.includes("-32601")) return true;
3479
+ if (/Method not found/i.test(detail)) return true;
3480
+ return false;
3481
+ }
3482
+ function formatOptionalJetBrainsHandshakeUnavailableDetail(detail) {
3483
+ return "MCP handshake optional verification unavailable while configuration succeeded" + (detail ? ` (${detail})` : "");
3484
+ }
3268
3485
  async function verifyJetBrainsMcpHandshake(options) {
3269
3486
  const timeoutMs = options.timeoutMs ?? 15e3;
3270
3487
  const { server } = options;
3271
- return new Promise((resolve16) => {
3488
+ return new Promise((resolve17) => {
3272
3489
  let settled = false;
3273
- const finish = (ok, detail) => {
3490
+ const finish = (ok, detail, optionalUnavailable) => {
3274
3491
  if (settled) return;
3275
3492
  settled = true;
3276
3493
  clearTimeout(timer);
@@ -3278,7 +3495,7 @@ async function verifyJetBrainsMcpHandshake(options) {
3278
3495
  child.kill();
3279
3496
  } catch {
3280
3497
  }
3281
- resolve16({ ok, detail });
3498
+ resolve17({ ok, detail, optionalUnavailable });
3282
3499
  };
3283
3500
  const child = (0, import_node_child_process5.spawn)(server.command, [...server.args], {
3284
3501
  env: { ...process.env, ...server.env },
@@ -3318,7 +3535,12 @@ async function verifyJetBrainsMcpHandshake(options) {
3318
3535
  finish(true, "initialize OK; tools/list OK");
3319
3536
  }
3320
3537
  if (msg.error) {
3321
- finish(false, `JSON-RPC error: ${JSON.stringify(msg.error)}`);
3538
+ const errDetail = `JSON-RPC error: ${JSON.stringify(msg.error)}`;
3539
+ if (isOptionalJetBrainsHandshakeUnavailable(errDetail)) {
3540
+ finish(true, errDetail, true);
3541
+ } else {
3542
+ finish(false, errDetail);
3543
+ }
3322
3544
  }
3323
3545
  }
3324
3546
  });
@@ -3347,11 +3569,11 @@ async function verifyJetBrainsMcpHandshake(options) {
3347
3569
  async function setupJetBrainsMcpConfig(options) {
3348
3570
  const homeDir = options.homeDir ?? os5.homedir();
3349
3571
  const globalPath = options.globalConfigPath ?? getJetBrainsGlobalMcpConfigPath(homeDir);
3350
- const workspaceRoot = path14.resolve(options.repoRoot);
3572
+ const workspaceRoot = path16.resolve(options.repoRoot);
3351
3573
  const repairActions = [];
3352
3574
  const allLocations = getKnownJetBrainsMcpConfigLocations(homeDir, options.repoRoot);
3353
3575
  for (const location of allLocations) {
3354
- if (await pathExists2(location.path)) {
3576
+ if (await pathExists3(location.path)) {
3355
3577
  repairActions.push({ type: "found-config", location });
3356
3578
  }
3357
3579
  }
@@ -3368,6 +3590,8 @@ async function setupJetBrainsMcpConfig(options) {
3368
3590
  const memoraone = await buildJetBrainsMemoraoneServer({
3369
3591
  workspaceRoot,
3370
3592
  devMode: options.devMode,
3593
+ environment: options.environment,
3594
+ npmPackageChannel: options.npmPackageChannel,
3371
3595
  apiUrl: options.apiUrl,
3372
3596
  npxPathOverride: options.npxPathOverride,
3373
3597
  cliPathOverride: options.cliPathOverride
@@ -3388,24 +3612,15 @@ async function setupJetBrainsMcpConfig(options) {
3388
3612
  repairActions.push({ type: "removed-project-memoraone", path: location.path });
3389
3613
  }
3390
3614
  }
3391
- const existed = await pathExists2(globalPath);
3615
+ const existed = await pathExists3(globalPath);
3392
3616
  let existing = null;
3393
3617
  if (existed) {
3394
3618
  try {
3395
3619
  existing = await readJsonConfig(globalPath);
3396
3620
  } catch {
3397
- if (options.dryRun) {
3398
- existing = null;
3399
- } else {
3400
- const backupPath2 = await backupConfigFile(globalPath);
3401
- repairActions.push({
3402
- type: "repaired-zero-byte",
3403
- path: globalPath,
3404
- backupPath: backupPath2
3405
- });
3406
- await fs11.unlink(globalPath);
3407
- existing = null;
3408
- }
3621
+ throw new Error(
3622
+ `[setup-ide-files] Invalid JSON in shared JetBrains MCP config (file preserved, not modified): ${globalPath}. Fix or remove the file, then re-run setup-ide-files.`
3623
+ );
3409
3624
  }
3410
3625
  }
3411
3626
  const merged = mergeJetBrainsMcpConfigObject(existing, memoraone);
@@ -3426,9 +3641,8 @@ async function setupJetBrainsMcpConfig(options) {
3426
3641
  if (existed) {
3427
3642
  backupPath = await backupConfigFile(globalPath);
3428
3643
  }
3429
- await fs11.mkdir(path14.dirname(globalPath), { recursive: true });
3430
- await fs11.writeFile(globalPath, body, "utf8");
3431
- const verifyRaw = await fs11.readFile(globalPath, "utf8");
3644
+ await writeJetBrainsMcpJsonAtomic(globalPath, body);
3645
+ const verifyRaw = await fs12.readFile(globalPath, "utf8");
3432
3646
  const verifyParsed = JSON.parse(stripLeadingLineComments2(verifyRaw));
3433
3647
  validateJetBrainsMcpConfig(verifyParsed, memoraone);
3434
3648
  const outcome = existed ? "updated" : "created";
@@ -3438,122 +3652,64 @@ async function setupJetBrainsMcpConfig(options) {
3438
3652
  if (options.verify !== false) {
3439
3653
  const verify = await verifyJetBrainsMcpHandshake({ server: memoraone });
3440
3654
  verifyOk = verify.ok;
3441
- verifyDetail = verify.detail;
3442
- repairActions.push({ type: "verify-handshake", ok: verify.ok, detail: verify.detail });
3655
+ verifyDetail = verify.optionalUnavailable ? formatOptionalJetBrainsHandshakeUnavailableDetail(verify.detail) : verify.detail;
3656
+ repairActions.push({
3657
+ type: "verify-handshake",
3658
+ ok: verify.ok,
3659
+ detail: verifyDetail ?? verify.detail
3660
+ });
3443
3661
  }
3444
3662
  return { outcome, backupPath, repairActions, verifyOk, verifyDetail, memoraone };
3445
3663
  }
3446
- function logJetBrainsMcpCliSummary(info, dryRun) {
3664
+ function formatJetBrainsHandshakeLogLine(action) {
3665
+ const optional = /optional verification unavailable/i.test(action.detail) || isOptionalJetBrainsHandshakeUnavailable(action.detail);
3666
+ if (optional) {
3667
+ if (/optional verification unavailable/i.test(action.detail)) {
3668
+ return `[setup-ide-files] ${action.detail}`;
3669
+ }
3670
+ return `[setup-ide-files] ${formatOptionalJetBrainsHandshakeUnavailableDetail(action.detail)}`;
3671
+ }
3672
+ if (action.ok) {
3673
+ return `[setup-ide-files] MCP handshake verification: ${action.detail}`;
3674
+ }
3675
+ return `[setup-ide-files] MCP handshake verification skipped/failed: ${action.detail}`;
3676
+ }
3677
+ function logJetBrainsMcpCliSummary(info, dryRun, println = console.log) {
3447
3678
  for (const action of info.repairActions) {
3448
3679
  if (action.type === "found-config") {
3449
- console.log(`[setup-ide-files] Found JetBrains MCP config (${action.location.kind}): ${action.location.path}`);
3680
+ println(`[setup-ide-files] Found JetBrains MCP config (${action.location.kind}): ${action.location.path}`);
3450
3681
  } else if (action.type === "repaired-zero-byte") {
3451
- console.log(`[setup-ide-files] Repaired zero-byte MCP config: ${action.path}`);
3452
- console.log(`[setup-ide-files] Backup: ${action.backupPath}`);
3682
+ println(`[setup-ide-files] Repaired zero-byte MCP config: ${action.path}`);
3683
+ println(`[setup-ide-files] Backup: ${action.backupPath}`);
3453
3684
  } else if (action.type === "backed-up-conflicting-project-config") {
3454
- console.log(`[setup-ide-files] Backed up conflicting project MCP config: ${action.path}`);
3455
- console.log(`[setup-ide-files] Backup: ${action.backupPath}`);
3685
+ println(`[setup-ide-files] Backed up conflicting project MCP config: ${action.path}`);
3686
+ println(`[setup-ide-files] Backup: ${action.backupPath}`);
3456
3687
  } else if (action.type === "removed-project-memoraone") {
3457
- console.log(`[setup-ide-files] Removed project-scoped memoraone definition: ${action.path}`);
3688
+ println(`[setup-ide-files] Removed project-scoped memoraone definition: ${action.path}`);
3458
3689
  } else if (action.type === "verify-handshake") {
3459
- if (action.ok) {
3460
- console.log(`[setup-ide-files] MCP handshake verification: ${action.detail}`);
3461
- } else {
3462
- console.log(`[setup-ide-files] MCP handshake verification skipped/failed: ${action.detail}`);
3463
- }
3690
+ println(formatJetBrainsHandshakeLogLine(action));
3464
3691
  }
3465
3692
  }
3466
3693
  const prefix = dryRun ? "would be " : "";
3467
3694
  if (info.outcome === "created") {
3468
- console.log(`[setup-ide-files] JetBrains global MCP config ${prefix}created: ${info.activeConfigPath}`);
3695
+ println(`[setup-ide-files] JetBrains global MCP config ${prefix}created: ${info.activeConfigPath}`);
3469
3696
  } else if (info.outcome === "updated") {
3470
- console.log(`[setup-ide-files] JetBrains global MCP config ${prefix}updated: ${info.activeConfigPath}`);
3697
+ println(`[setup-ide-files] JetBrains global MCP config ${prefix}updated: ${info.activeConfigPath}`);
3471
3698
  } else {
3472
- console.log(`[setup-ide-files] JetBrains global MCP config unchanged: ${info.activeConfigPath}`);
3699
+ println(`[setup-ide-files] JetBrains global MCP config unchanged: ${info.activeConfigPath}`);
3473
3700
  }
3474
3701
  if (info.backupPath) {
3475
- console.log(`[setup-ide-files] JetBrains global MCP config backup: ${info.backupPath}`);
3702
+ println(`[setup-ide-files] JetBrains global MCP config backup: ${info.backupPath}`);
3476
3703
  }
3477
3704
  if (info.npxPath) {
3478
- console.log(`[setup-ide-files] Resolved npx: ${info.npxPath}`);
3705
+ println(`[setup-ide-files] Resolved npx: ${info.npxPath}`);
3479
3706
  }
3480
- console.log(`[setup-ide-files] Final active JetBrains MCP config: ${info.activeConfigPath}`);
3481
- console.log(
3707
+ println(`[setup-ide-files] Final active JetBrains MCP config: ${info.activeConfigPath}`);
3708
+ println(
3482
3709
  "[setup-ide-files] Fully quit JetBrains IDE and reopen this repo for MCP changes to take effect."
3483
3710
  );
3484
3711
  }
3485
3712
 
3486
- // src/resolveBuiltCliPath.ts
3487
- var fs12 = __toESM(require("fs/promises"), 1);
3488
- var path15 = __toESM(require("path"), 1);
3489
- var MONOREPO_CLI_REL = path15.join("packages", "mcp", "dist", "cli.cjs");
3490
- async function pathExists3(filePath) {
3491
- try {
3492
- await fs12.access(filePath);
3493
- return true;
3494
- } catch {
3495
- return false;
3496
- }
3497
- }
3498
- async function findMonorepoCliFrom(startDir) {
3499
- let current = path15.resolve(startDir);
3500
- const root = path15.parse(current).root;
3501
- while (true) {
3502
- const candidate = path15.join(current, MONOREPO_CLI_REL);
3503
- if (await pathExists3(candidate)) {
3504
- return path15.resolve(candidate);
3505
- }
3506
- if (current === root) break;
3507
- current = path15.dirname(current);
3508
- }
3509
- return null;
3510
- }
3511
- async function resolveFromRunningScript() {
3512
- if (!process.argv[1]) return null;
3513
- const script = path15.resolve(process.argv[1]);
3514
- const base = path15.basename(script);
3515
- if ((base === "cli.cjs" || base === "cli.ts" || base === "memoraone-mcp.cjs") && await pathExists3(script)) {
3516
- return script;
3517
- }
3518
- const here = path15.dirname(script);
3519
- const candidates = [
3520
- path15.join(here, "cli.cjs"),
3521
- path15.join(here, "..", "dist", "cli.cjs"),
3522
- path15.join(here, "..", "..", "dist", "cli.cjs")
3523
- ];
3524
- for (const candidate of candidates) {
3525
- if (await pathExists3(candidate)) {
3526
- return path15.resolve(candidate);
3527
- }
3528
- }
3529
- return null;
3530
- }
3531
- async function resolveBuiltCliPathAsync(options) {
3532
- const preferRunning = options?.preferRunningScript !== false;
3533
- if (preferRunning) {
3534
- const fromRunning = await resolveFromRunningScript();
3535
- if (fromRunning) return fromRunning;
3536
- }
3537
- const searchDirs = [];
3538
- if (options?.searchFrom !== void 0) {
3539
- const dirs = Array.isArray(options.searchFrom) ? options.searchFrom : [options.searchFrom];
3540
- searchDirs.push(...dirs);
3541
- }
3542
- searchDirs.push(process.cwd());
3543
- const seen = /* @__PURE__ */ new Set();
3544
- for (const dir of searchDirs) {
3545
- const key = path15.resolve(dir);
3546
- if (seen.has(key)) continue;
3547
- seen.add(key);
3548
- const found = await findMonorepoCliFrom(key);
3549
- if (found) return found;
3550
- }
3551
- if (!preferRunning) {
3552
- return resolveFromRunningScript();
3553
- }
3554
- return null;
3555
- }
3556
-
3557
3713
  // src/openCursorMcpSettings.ts
3558
3714
  var import_node_child_process6 = require("child_process");
3559
3715
  var readline4 = __toESM(require("readline/promises"), 1);
@@ -3622,6 +3778,7 @@ function createTerminalPresentation(opts = {}) {
3622
3778
  warningSymbol,
3623
3779
  nextActionPrefix,
3624
3780
  successLine: (message) => paint(color, ANSI.green, `${successSymbol} ${message}`),
3781
+ checkLine: (message) => `${paint(color, ANSI.green, successSymbol)} ${message}`,
3625
3782
  warningLine: (message) => paint(color, ANSI.yellow, `${warningSymbol} ${message}`),
3626
3783
  nextActionLine: (message) => `${nextActionPrefix} ${message}`,
3627
3784
  heading: (text) => paint(color, ANSI.bold, text),
@@ -3674,44 +3831,6 @@ async function confirmYesDefault(question, deps = {}) {
3674
3831
  rl.close();
3675
3832
  }
3676
3833
  }
3677
- function collectOutcomePaths(outcomes) {
3678
- const created = [];
3679
- const updated = [];
3680
- for (const [file, outcome] of Object.entries(outcomes)) {
3681
- if (outcome === "created") created.push(file);
3682
- else if (outcome === "updated") updated.push(file);
3683
- }
3684
- return { created, updated };
3685
- }
3686
- function formatCursorSetupCompletedSummary(opts, presentation = createTerminalPresentation({ color: false, unicode: false })) {
3687
- const { created, updated } = collectOutcomePaths(opts.outcomes);
3688
- const tp = presentation;
3689
- const lines = [
3690
- tp.successLine("MemoraOne setup completed for Cursor"),
3691
- "",
3692
- tp.heading("Repository"),
3693
- tp.indent(tp.cyan(opts.repoRoot)),
3694
- "",
3695
- tp.heading("Changes")
3696
- ];
3697
- if (created.length === 0 && updated.length === 0) {
3698
- lines.push(tp.indent(tp.dim("No file changes needed")));
3699
- } else {
3700
- if (created.length) {
3701
- lines.push(tp.indent(`Created: ${created.join(", ")}`));
3702
- }
3703
- if (updated.length) {
3704
- lines.push(tp.indent(`Updated: ${updated.join(", ")}`));
3705
- }
3706
- }
3707
- return lines;
3708
- }
3709
- function printCursorSetupCompletedSummary(opts, println = console.log, presentation) {
3710
- const tp = presentation ?? createTerminalPresentation();
3711
- for (const line of formatCursorSetupCompletedSummary(opts, tp)) {
3712
- println(line);
3713
- }
3714
- }
3715
3834
  function macosOpenCursorMcpSettingsAppleScript() {
3716
3835
  return [
3717
3836
  'tell application "Cursor" to activate',
@@ -3782,6 +3901,39 @@ async function runOpenCursorMcpSettingsFlow(deps = {}) {
3782
3901
  printManualCursorMcpSettingsSteps(platform2, println);
3783
3902
  }
3784
3903
 
3904
+ // src/setupSuccessOutput.ts
3905
+ var RESTART_LINE = "Restart your IDEs to finish setup.";
3906
+ function formatSetupSuccessLines(opts = {}) {
3907
+ const tp = opts.presentation ?? createTerminalPresentation(opts.presentationOptions ?? { color: false, unicode: true });
3908
+ const targets = opts.targets ?? {};
3909
+ const lines = [];
3910
+ if (opts.repositoryConnected) {
3911
+ lines.push(tp.checkLine("Repository connected"));
3912
+ }
3913
+ if (targets.cursor) {
3914
+ lines.push(tp.checkLine("Cursor configured"));
3915
+ }
3916
+ if (targets.vscode) {
3917
+ lines.push(tp.checkLine("VS Code configured"));
3918
+ }
3919
+ if (targets.jetbrains) {
3920
+ lines.push(tp.checkLine("JetBrains configured"));
3921
+ }
3922
+ const hasIde = Boolean(targets.cursor || targets.vscode || targets.jetbrains);
3923
+ lines.push("");
3924
+ lines.push(tp.checkLine("MemoraOne is ready"));
3925
+ if (hasIde) {
3926
+ lines.push("");
3927
+ lines.push(RESTART_LINE);
3928
+ }
3929
+ return lines;
3930
+ }
3931
+ function printSetupSuccess(opts, println = console.log) {
3932
+ for (const line of formatSetupSuccessLines(opts)) {
3933
+ println(line);
3934
+ }
3935
+ }
3936
+
3785
3937
  // src/setupIdeFiles.ts
3786
3938
  var MANAGED_MARKER = "<!-- MemoraOne managed IDE helper -->";
3787
3939
  function buildMemoraoneMcpServer(ideType, options = {}) {
@@ -3795,7 +3947,7 @@ function buildMemoraoneMcpServer(ideType, options = {}) {
3795
3947
  throw new Error("[setup-ide-files] Local VS Code MCP config requires a built CLI path.");
3796
3948
  }
3797
3949
  if (options.workspaceRoot !== void 0) {
3798
- env2[MEMORAONE_WORKSPACE_ROOT_ENV] = path16.resolve(options.workspaceRoot);
3950
+ env2[MEMORAONE_WORKSPACE_ROOT_ENV] = path17.resolve(options.workspaceRoot);
3799
3951
  }
3800
3952
  return {
3801
3953
  command: "node",
@@ -3803,16 +3955,17 @@ function buildMemoraoneMcpServer(ideType, options = {}) {
3803
3955
  env: env2
3804
3956
  };
3805
3957
  }
3958
+ const channel = options.npmPackageChannel ?? npmPackageChannelFromEnvironment(environment);
3806
3959
  return {
3807
3960
  command: options.command ?? "npx",
3808
- args: ["-y", "@memoraone/mcp@latest"],
3961
+ args: ["-y", memoraoneNpmPackageSpec(channel)],
3809
3962
  env: env2
3810
3963
  };
3811
3964
  }
3812
3965
  function assertUnderRepoRoot(repoRoot, absPath) {
3813
- const normRoot = path16.resolve(repoRoot) + path16.sep;
3814
- const normPath = path16.resolve(absPath);
3815
- if (normPath !== path16.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
3966
+ const normRoot = path17.resolve(repoRoot) + path17.sep;
3967
+ const normPath = path17.resolve(absPath);
3968
+ if (normPath !== path17.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
3816
3969
  throw new Error(`[setup-ide-files] Refusing to write outside repo root: ${absPath}`);
3817
3970
  }
3818
3971
  }
@@ -3828,18 +3981,18 @@ async function ensureGitignoreMemoraone(_repoRoot, _opts) {
3828
3981
  return "skipped";
3829
3982
  }
3830
3983
  async function findRepoRoot(startDir) {
3831
- let current = path16.resolve(startDir);
3832
- const root = path16.parse(current).root;
3984
+ let current = path17.resolve(startDir);
3985
+ const root = path17.parse(current).root;
3833
3986
  while (true) {
3834
- const gitPath = path16.join(current, ".git");
3835
- const m1Path = path16.join(current, "memoraone.m1");
3987
+ const gitPath = path17.join(current, ".git");
3988
+ const m1Path = path17.join(current, "memoraone.m1");
3836
3989
  if (await pathExists4(gitPath) || await pathExists4(m1Path)) {
3837
3990
  return current;
3838
3991
  }
3839
3992
  if (current === root) {
3840
3993
  return null;
3841
3994
  }
3842
- current = path16.dirname(current);
3995
+ current = path17.dirname(current);
3843
3996
  }
3844
3997
  }
3845
3998
  function stripLeadingLineComments3(text) {
@@ -3888,16 +4041,44 @@ function mcpJsonHeader() {
3888
4041
  function buildVscodeMcpJsonBody(existing, options = {}) {
3889
4042
  const base = existing && typeof existing === "object" ? { ...existing } : { servers: {} };
3890
4043
  const servers = typeof base.servers === "object" && base.servers !== null && !Array.isArray(base.servers) ? { ...base.servers } : {};
4044
+ delete servers.memoraone;
3891
4045
  servers.memoraone = buildMemoraoneMcpServer("copilot-vscode", options);
3892
4046
  const merged = { ...base, servers };
3893
4047
  return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
3894
4048
  }
4049
+ async function writeSharedMcpJsonAtomic(filePath, content) {
4050
+ const dir = path17.dirname(filePath);
4051
+ await fs13.mkdir(dir, { recursive: true });
4052
+ const tmpPath = path17.join(
4053
+ dir,
4054
+ `.${path17.basename(filePath)}.${process.pid}.${(0, import_node_crypto5.randomBytes)(8).toString("hex")}.tmp`
4055
+ );
4056
+ try {
4057
+ await fs13.writeFile(tmpPath, content, "utf8");
4058
+ await fs13.rename(tmpPath, filePath);
4059
+ } catch (err) {
4060
+ try {
4061
+ await fs13.unlink(tmpPath);
4062
+ } catch {
4063
+ }
4064
+ throw err;
4065
+ }
4066
+ }
4067
+ var SharedMcpJsonParseError = class extends Error {
4068
+ constructor(configPath) {
4069
+ super(
4070
+ `[setup-ide-files] Invalid JSON in shared MCP config (file preserved, not modified): ${configPath}. Fix or remove the file, then re-run setup-ide-files.`
4071
+ );
4072
+ this.name = "SharedMcpJsonParseError";
4073
+ this.configPath = configPath;
4074
+ }
4075
+ };
3895
4076
  function buildCursorMcpJsonBody(existing, writeOptions) {
3896
4077
  const merged = mergeCursorRepoMcpConfigObject(existing, writeOptions);
3897
4078
  return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
3898
4079
  }
3899
4080
  async function writeManagedMarkdown(repoRoot, relPath, fullContent, opts) {
3900
- const abs = path16.join(repoRoot, relPath);
4081
+ const abs = path17.join(repoRoot, relPath);
3901
4082
  assertUnderRepoRoot(repoRoot, abs);
3902
4083
  let prior = "";
3903
4084
  let existed = false;
@@ -3909,25 +4090,25 @@ async function writeManagedMarkdown(repoRoot, relPath, fullContent, opts) {
3909
4090
  }
3910
4091
  if (!existed) {
3911
4092
  if (opts.dryRun) return "created";
3912
- await fs13.mkdir(path16.dirname(abs), { recursive: true });
4093
+ await fs13.mkdir(path17.dirname(abs), { recursive: true });
3913
4094
  await fs13.writeFile(abs, fullContent, "utf8");
3914
4095
  return "created";
3915
4096
  }
3916
4097
  if (prior.includes(MANAGED_MARKER)) {
3917
4098
  if (prior === fullContent) return "skipped";
3918
4099
  if (opts.dryRun) return "updated";
3919
- await fs13.mkdir(path16.dirname(abs), { recursive: true });
4100
+ await fs13.mkdir(path17.dirname(abs), { recursive: true });
3920
4101
  await fs13.writeFile(abs, fullContent, "utf8");
3921
4102
  return "updated";
3922
4103
  }
3923
4104
  if (!opts.force) return "skipped-untracked";
3924
4105
  if (opts.dryRun) return "updated";
3925
- await fs13.mkdir(path16.dirname(abs), { recursive: true });
4106
+ await fs13.mkdir(path17.dirname(abs), { recursive: true });
3926
4107
  await fs13.writeFile(abs, fullContent, "utf8");
3927
4108
  return "updated";
3928
4109
  }
3929
4110
  async function writeIdeMcpJson(repoRoot, relPath, buildBody, opts) {
3930
- const abs = path16.join(repoRoot, relPath);
4111
+ const abs = path17.join(repoRoot, relPath);
3931
4112
  assertUnderRepoRoot(repoRoot, abs);
3932
4113
  let raw = "";
3933
4114
  let existed = false;
@@ -3940,24 +4121,24 @@ async function writeIdeMcpJson(repoRoot, relPath, buildBody, opts) {
3940
4121
  if (!existed) {
3941
4122
  const body = buildBody(null);
3942
4123
  if (opts.dryRun) return "created";
3943
- await fs13.mkdir(path16.dirname(abs), { recursive: true });
3944
- await fs13.writeFile(abs, body, "utf8");
4124
+ await writeSharedMcpJsonAtomic(abs, body);
3945
4125
  return "created";
3946
4126
  }
3947
4127
  const managed = raw.includes(MANAGED_MARKER);
3948
4128
  if (!managed && !opts.force) return "skipped-untracked";
3949
- let parsed2 = null;
4129
+ let parsed2;
3950
4130
  try {
3951
4131
  parsed2 = JSON.parse(stripLeadingLineComments3(raw));
3952
4132
  } catch {
3953
- parsed2 = null;
4133
+ throw new SharedMcpJsonParseError(abs);
4134
+ }
4135
+ if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2)) {
4136
+ throw new SharedMcpJsonParseError(abs);
3954
4137
  }
3955
- if (!parsed2 && !opts.force) return "skipped-untracked";
3956
4138
  const next = buildBody(parsed2);
3957
4139
  if (managed && next === raw) return "skipped";
3958
4140
  if (opts.dryRun) return "updated";
3959
- await fs13.mkdir(path16.dirname(abs), { recursive: true });
3960
- await fs13.writeFile(abs, next, "utf8");
4141
+ await writeSharedMcpJsonAtomic(abs, next);
3961
4142
  return "updated";
3962
4143
  }
3963
4144
  function parseSetupIdeFlags(argv) {
@@ -3973,6 +4154,7 @@ function parseSetupIdeFlags(argv) {
3973
4154
  let repair = false;
3974
4155
  let local = false;
3975
4156
  let staging = false;
4157
+ let verbose = false;
3976
4158
  let workspaceRoot;
3977
4159
  let apiUrl;
3978
4160
  const unknown = [];
@@ -3991,6 +4173,7 @@ function parseSetupIdeFlags(argv) {
3991
4173
  else if (a === "--repair") repair = true;
3992
4174
  else if (a === "--local") local = true;
3993
4175
  else if (a === "--staging") staging = true;
4176
+ else if (a === "--verbose") verbose = true;
3994
4177
  else if (a === "--workspace-root") {
3995
4178
  const value = argv[++i];
3996
4179
  if (!value || value.startsWith("-")) {
@@ -4048,22 +4231,48 @@ function parseSetupIdeFlags(argv) {
4048
4231
  workspaceRoot,
4049
4232
  apiUrl,
4050
4233
  explicitCursor: cursor,
4234
+ verbose,
4051
4235
  unknown,
4052
4236
  flagError
4053
4237
  };
4054
4238
  }
4055
- async function resolveSetupApiUrl(o, repoRoot, localOrDev) {
4056
- if (o.apiUrl) return normalizeApiUrl(o.apiUrl);
4057
- if (!localOrDev) return void 0;
4239
+ function logSetupIdeFilesVerboseSuccess(opts, println = console.log) {
4240
+ const { targets, dryRun, result } = opts;
4241
+ if (result.repoRoot) {
4242
+ println(`[setup-ide-files] Repo root: ${result.repoRoot}`);
4243
+ }
4244
+ if (result.daemonCleanup && !result.daemonCleanup.skipped) {
4245
+ logSetupIdeCleanupSummary(result.daemonCleanup, println);
4246
+ }
4247
+ if (targets.cursor && result.cursorMcp) {
4248
+ logCursorMcpCliSummary(result.cursorMcp, dryRun, {
4249
+ forInteractivePostSetup: opts.forInteractivePostSetup,
4250
+ println
4251
+ });
4252
+ }
4253
+ if (targets.jetbrains && result.jetbrainsMcp) {
4254
+ logJetBrainsMcpCliSummary(result.jetbrainsMcp, dryRun, println);
4255
+ }
4256
+ summarizeOutcomes(result.outcomes, println);
4257
+ if (dryRun) {
4258
+ println("[setup-ide-files] Dry run: no files written.");
4259
+ if (result.daemonCleanup && !result.daemonCleanup.skipped) {
4260
+ println("[setup-ide-files] Dry run: no daemons stopped, no sockets removed.");
4261
+ }
4262
+ }
4263
+ println(`[setup-ide-files] ${restartIdeInstruction(targets)}`);
4264
+ }
4265
+ async function resolveSetupApiUrl(o, repoRoot) {
4266
+ if (o.apiUrl) return normalizeApiUrl2(o.apiUrl);
4058
4267
  const binding = await findBindingRecordByWorkspaceRoot(repoRoot, o.homeDir);
4059
- if (binding?.apiUrl) return normalizeApiUrl(binding.apiUrl);
4268
+ if (binding?.apiUrl) return normalizeApiUrl2(binding.apiUrl);
4060
4269
  return void 0;
4061
4270
  }
4062
4271
  async function persistBindingApiUrl(repoRoot, apiUrl, homeDir, dryRun) {
4063
4272
  if (dryRun) return;
4064
4273
  const binding = await findBindingRecordByWorkspaceRoot(repoRoot, homeDir);
4065
4274
  if (!binding) return;
4066
- const normalized = normalizeApiUrl(apiUrl);
4275
+ const normalized = normalizeApiUrl2(apiUrl);
4067
4276
  if (binding.apiUrl === normalized) return;
4068
4277
  await writeBindingRecord(
4069
4278
  {
@@ -4079,7 +4288,7 @@ function cursorEnvironmentFromFlags(local, staging) {
4079
4288
  if (staging) return "staging";
4080
4289
  return "production";
4081
4290
  }
4082
- function summarizeOutcomes(outcomes) {
4291
+ function summarizeOutcomes(outcomes, println = console.log) {
4083
4292
  const created = [];
4084
4293
  const updated = [];
4085
4294
  const skipped = [];
@@ -4097,7 +4306,7 @@ function summarizeOutcomes(outcomes) {
4097
4306
  if (skippedUntracked.length) {
4098
4307
  lines.push(` skipped (unmanaged existing file, use --force): ${skippedUntracked.join(", ")}`);
4099
4308
  }
4100
- console.log(lines.join("\n"));
4309
+ println(lines.join("\n"));
4101
4310
  }
4102
4311
  function ideTypesFromSetupTargets(targets) {
4103
4312
  const ides = [];
@@ -4138,28 +4347,28 @@ function aggregateCleanupResults(results) {
4138
4347
  error
4139
4348
  };
4140
4349
  }
4141
- function logSetupIdeCleanupSummary(cleanup) {
4350
+ function logSetupIdeCleanupSummary(cleanup, println = console.log) {
4142
4351
  if (cleanup.skipped) return;
4143
- console.log(`[setup-ide-files] Project id: ${cleanup.projectId}`);
4352
+ println(`[setup-ide-files] Project id: ${cleanup.projectId}`);
4144
4353
  if (cleanup.foundDaemonCount > 0) {
4145
- console.log(`[setup-ide-files] Found ${cleanup.foundDaemonCount} stale daemon(s)`);
4354
+ println(`[setup-ide-files] Found ${cleanup.foundDaemonCount} stale daemon(s)`);
4146
4355
  if (cleanup.dryRun) {
4147
- console.log(`[setup-ide-files] Would stop ${cleanup.foundDaemonCount} stale daemon(s)`);
4356
+ println(`[setup-ide-files] Would stop ${cleanup.foundDaemonCount} stale daemon(s)`);
4148
4357
  } else if (cleanup.stoppedDaemonCount > 0) {
4149
- console.log(`[setup-ide-files] Stopped ${cleanup.stoppedDaemonCount} stale daemon(s)`);
4358
+ println(`[setup-ide-files] Stopped ${cleanup.stoppedDaemonCount} stale daemon(s)`);
4150
4359
  }
4151
4360
  } else {
4152
- console.log("[setup-ide-files] No stale daemons found for this project and IDE target(s).");
4361
+ println("[setup-ide-files] No stale daemons found for this project and IDE target(s).");
4153
4362
  }
4154
4363
  if (cleanup.removedSocketCount > 0) {
4155
4364
  if (cleanup.dryRun) {
4156
- console.log(`[setup-ide-files] Would remove ${cleanup.removedSocketCount} stale socket(s)`);
4365
+ println(`[setup-ide-files] Would remove ${cleanup.removedSocketCount} stale socket(s)`);
4157
4366
  } else {
4158
- console.log(`[setup-ide-files] Removed ${cleanup.removedSocketCount} stale socket(s)`);
4367
+ println(`[setup-ide-files] Removed ${cleanup.removedSocketCount} stale socket(s)`);
4159
4368
  }
4160
4369
  }
4161
4370
  if (cleanup.skippedUnrelatedDaemonCount > 0) {
4162
- console.log(
4371
+ println(
4163
4372
  `[setup-ide-files] Skipped ${cleanup.skippedUnrelatedDaemonCount} unrelated project daemon(s)`
4164
4373
  );
4165
4374
  }
@@ -4238,7 +4447,7 @@ async function runSetupIdeFiles(o) {
4238
4447
  const outcomes = {};
4239
4448
  let cursorMcp;
4240
4449
  let jetbrainsMcp;
4241
- const repoRoot = o.workspaceRoot !== void 0 && o.workspaceRoot !== "" ? path16.resolve(o.workspaceRoot) : await findRepoRoot(o.cwd);
4450
+ const repoRoot = o.workspaceRoot !== void 0 && o.workspaceRoot !== "" ? path17.resolve(o.workspaceRoot) : await findRepoRoot(o.cwd);
4242
4451
  if (!repoRoot) {
4243
4452
  return {
4244
4453
  exitCode: 1,
@@ -4274,12 +4483,13 @@ async function runSetupIdeFiles(o) {
4274
4483
  });
4275
4484
  const cursorEnvironment = o.cursorEnvironment ?? "production";
4276
4485
  const localOrDev = cursorEnvironment === "local" || Boolean(o.devMode);
4277
- const resolvedApiUrl2 = await resolveSetupApiUrl(o, repoRoot, localOrDev);
4486
+ const npmPackageChannel = o.npmPackageChannel ?? npmPackageChannelFromEnvironment(cursorEnvironment);
4487
+ const resolvedApiUrl2 = await resolveSetupApiUrl(o, repoRoot);
4278
4488
  const effectiveApiUrl = resolveIdeApiUrl({
4279
4489
  environment: localOrDev ? "local" : cursorEnvironment,
4280
4490
  apiUrl: resolvedApiUrl2
4281
4491
  });
4282
- if (localOrDev && !o.dryRun) {
4492
+ if (!o.dryRun && resolvedApiUrl2) {
4283
4493
  await persistBindingApiUrl(repoRoot, effectiveApiUrl, o.homeDir, o.dryRun);
4284
4494
  }
4285
4495
  const cursorContent = `---
@@ -4324,7 +4534,8 @@ description: MemoraOne MCP \u2014 IDE agent instructions
4324
4534
  npxPath: npxPath ?? void 0,
4325
4535
  cliPath,
4326
4536
  repoRoot,
4327
- apiUrl: resolvedApiUrl2
4537
+ apiUrl: resolvedApiUrl2,
4538
+ npmPackageChannel
4328
4539
  };
4329
4540
  outcomes[".cursor/rules/memoraone-mcp.mdc"] = await writeManagedMarkdown(
4330
4541
  repoRoot,
@@ -4332,12 +4543,22 @@ description: MemoraOne MCP \u2014 IDE agent instructions
4332
4543
  cursorContent,
4333
4544
  { force: o.force, dryRun: o.dryRun }
4334
4545
  );
4335
- outcomes[".cursor/mcp.json"] = await writeIdeMcpJson(
4336
- repoRoot,
4337
- ".cursor/mcp.json",
4338
- (existing) => buildCursorMcpJsonBody(existing, cursorWriteOptions),
4339
- { force: o.force, dryRun: o.dryRun }
4340
- );
4546
+ try {
4547
+ outcomes[".cursor/mcp.json"] = await writeIdeMcpJson(
4548
+ repoRoot,
4549
+ ".cursor/mcp.json",
4550
+ (existing) => buildCursorMcpJsonBody(existing, cursorWriteOptions),
4551
+ { force: o.force, dryRun: o.dryRun }
4552
+ );
4553
+ } catch (err) {
4554
+ const message = err instanceof Error ? err.message : String(err);
4555
+ return {
4556
+ exitCode: 1,
4557
+ repoRoot,
4558
+ outcomes,
4559
+ error: message
4560
+ };
4561
+ }
4341
4562
  const repoConfigPath = getCursorRepoMcpConfigPath(repoRoot);
4342
4563
  const repoOutcome = outcomes[".cursor/mcp.json"] ?? "skipped";
4343
4564
  let globalConfigPath;
@@ -4386,7 +4607,7 @@ description: MemoraOne MCP \u2014 IDE agent instructions
4386
4607
  };
4387
4608
  }
4388
4609
  if (o.targets.vscode) {
4389
- const vscodeEnvironment = localOrDev ? "local" : "production";
4610
+ const vscodeEnvironment = localOrDev ? "local" : cursorEnvironment === "staging" ? "staging" : "production";
4390
4611
  let vscodeCliPath;
4391
4612
  if (vscodeEnvironment === "local") {
4392
4613
  let resolvedCliPath = o.cursorLocalCliPathOverride;
@@ -4407,20 +4628,32 @@ description: MemoraOne MCP \u2014 IDE agent instructions
4407
4628
  }
4408
4629
  vscodeCliPath = resolvedCliPath;
4409
4630
  }
4410
- outcomes[".vscode/mcp.json"] = await writeIdeMcpJson(
4411
- repoRoot,
4412
- ".vscode/mcp.json",
4413
- (existing) => buildVscodeMcpJsonBody(existing, {
4414
- environment: vscodeEnvironment,
4415
- apiUrl: resolvedApiUrl2,
4416
- cliPath: vscodeCliPath,
4417
- workspaceRoot: vscodeEnvironment === "local" ? repoRoot : void 0
4418
- }),
4419
- {
4420
- force: o.force,
4421
- dryRun: o.dryRun
4422
- }
4423
- );
4631
+ try {
4632
+ outcomes[".vscode/mcp.json"] = await writeIdeMcpJson(
4633
+ repoRoot,
4634
+ ".vscode/mcp.json",
4635
+ (existing) => buildVscodeMcpJsonBody(existing, {
4636
+ environment: vscodeEnvironment,
4637
+ apiUrl: resolvedApiUrl2,
4638
+ cliPath: vscodeCliPath,
4639
+ workspaceRoot: vscodeEnvironment === "local" ? repoRoot : void 0,
4640
+ npmPackageChannel
4641
+ }),
4642
+ {
4643
+ force: o.force,
4644
+ dryRun: o.dryRun
4645
+ }
4646
+ );
4647
+ } catch (err) {
4648
+ const message = err instanceof Error ? err.message : String(err);
4649
+ return {
4650
+ exitCode: 1,
4651
+ repoRoot,
4652
+ outcomes,
4653
+ cursorMcp,
4654
+ error: message
4655
+ };
4656
+ }
4424
4657
  outcomes[".github/copilot-instructions.md"] = await writeManagedMarkdown(
4425
4658
  repoRoot,
4426
4659
  ".github/copilot-instructions.md",
@@ -4443,8 +4676,11 @@ description: MemoraOne MCP \u2014 IDE agent instructions
4443
4676
  repoRoot,
4444
4677
  globalConfigPath: activePath,
4445
4678
  dryRun: o.dryRun,
4446
- // Connect/local repair passes --dev so JetBrains gets the same backend URL + local CLI.
4447
- devMode: o.devMode,
4679
+ // Local dogfood: --dev / local environment node + built CLI.
4680
+ // Published staging/production: npx + channel-specific package spec.
4681
+ devMode: localOrDev,
4682
+ environment: localOrDev ? "local" : cursorEnvironment,
4683
+ npmPackageChannel,
4448
4684
  apiUrl: resolvedApiUrl2,
4449
4685
  repair: o.repair ?? false,
4450
4686
  verify: o.verifyHandshake ?? !o.dryRun,
@@ -4489,6 +4725,7 @@ async function cliSetupIdeFiles(argv, options = {}) {
4489
4725
  workspaceRoot,
4490
4726
  apiUrl,
4491
4727
  explicitCursor,
4728
+ verbose,
4492
4729
  unknown,
4493
4730
  flagError
4494
4731
  } = parseSetupIdeFlags(argv);
@@ -4504,6 +4741,7 @@ async function cliSetupIdeFiles(argv, options = {}) {
4504
4741
  const openDeps = options.openCursorMcpSettings ?? {};
4505
4742
  const stdinIsTty = openDeps.stdinIsTty ?? process.stdin.isTTY === true;
4506
4743
  const env2 = openDeps.env ?? process.env;
4744
+ const println = options.println ?? openDeps.println ?? console.log;
4507
4745
  const promptOpenCursorSettings = shouldPromptOpenCursorMcpSettings({
4508
4746
  explicitCursor,
4509
4747
  all,
@@ -4511,6 +4749,7 @@ async function cliSetupIdeFiles(argv, options = {}) {
4511
4749
  stdinIsTty,
4512
4750
  env: env2
4513
4751
  });
4752
+ const cursorEnvironment = cursorEnvironmentFromFlags(local, staging);
4514
4753
  const result = await runSetupIdeFiles({
4515
4754
  cwd: cwd2,
4516
4755
  workspaceRoot,
@@ -4520,62 +4759,50 @@ async function cliSetupIdeFiles(argv, options = {}) {
4520
4759
  noGitignore: options.setupOverrides?.noGitignore ?? noGitignore,
4521
4760
  devMode,
4522
4761
  repair,
4523
- cursorEnvironment: cursorEnvironmentFromFlags(local, staging),
4762
+ cursorEnvironment,
4763
+ npmPackageChannel: npmPackageChannelFromEnvironment(cursorEnvironment),
4524
4764
  apiUrl,
4525
4765
  ...options.setupOverrides
4526
4766
  });
4527
4767
  if (result.error) {
4528
4768
  console.error(result.error);
4529
4769
  if (result.repoRoot) {
4530
- console.log(`[setup-ide-files] Repo root: ${result.repoRoot}`);
4770
+ println(`[setup-ide-files] Repo root: ${result.repoRoot}`);
4531
4771
  }
4532
4772
  summarizeOutcomes(result.outcomes);
4533
4773
  return result.exitCode;
4534
4774
  }
4535
- if (result.repoRoot) {
4536
- console.log(`[setup-ide-files] Repo root: ${result.repoRoot}`);
4537
- }
4538
- if (result.daemonCleanup && !result.daemonCleanup.skipped) {
4539
- logSetupIdeCleanupSummary(result.daemonCleanup);
4540
- }
4541
- if (targets.cursor && result.cursorMcp) {
4542
- logCursorMcpCliSummary(result.cursorMcp, dryRun, {
4543
- forInteractivePostSetup: promptOpenCursorSettings
4544
- });
4545
- }
4546
- if (targets.jetbrains && result.jetbrainsMcp) {
4547
- logJetBrainsMcpCliSummary(result.jetbrainsMcp, dryRun);
4775
+ const presentation = openDeps.presentation ?? createTerminalPresentation({
4776
+ env: env2,
4777
+ stdoutIsTty: openDeps.stdoutIsTty ?? process.stdout.isTTY === true,
4778
+ color: openDeps.color,
4779
+ unicode: openDeps.unicode
4780
+ });
4781
+ const showVerbose = verbose || dryRun;
4782
+ if (showVerbose) {
4783
+ logSetupIdeFilesVerboseSuccess(
4784
+ {
4785
+ targets,
4786
+ dryRun,
4787
+ result,
4788
+ forInteractivePostSetup: promptOpenCursorSettings
4789
+ },
4790
+ println
4791
+ );
4792
+ } else {
4793
+ printSetupSuccess({ targets, presentation }, println);
4548
4794
  }
4549
4795
  if (promptOpenCursorSettings && result.repoRoot) {
4550
- const presentation = openDeps.presentation ?? createTerminalPresentation({
4551
- env: env2,
4552
- stdoutIsTty: openDeps.stdoutIsTty ?? process.stdout.isTTY === true,
4553
- color: openDeps.color,
4554
- unicode: openDeps.unicode
4555
- });
4556
- printCursorSetupCompletedSummary(
4557
- { repoRoot: result.repoRoot, outcomes: result.outcomes },
4558
- openDeps.println,
4559
- presentation
4560
- );
4561
4796
  await runOpenCursorMcpSettingsFlow({
4562
4797
  ...openDeps,
4563
4798
  stdinIsTty,
4564
4799
  env: env2,
4565
- presentation
4800
+ presentation,
4801
+ println
4566
4802
  });
4567
- } else {
4568
- summarizeOutcomes(result.outcomes);
4569
- if (dryRun) {
4570
- console.log("[setup-ide-files] Dry run: no files written.");
4571
- if (result.daemonCleanup && !result.daemonCleanup.skipped) {
4572
- console.log("[setup-ide-files] Dry run: no daemons stopped, no sockets removed.");
4573
- }
4574
- }
4575
- console.log(`[setup-ide-files] ${restartIdeInstruction(targets)}`);
4576
4803
  }
4577
4804
  if (cleanup) {
4578
- console.log("[setup-ide-files] Running additional full-project cleanup (--cleanup)...");
4805
+ println("[setup-ide-files] Running additional full-project cleanup (--cleanup)...");
4579
4806
  const cleanupResult = await runCleanup({
4580
4807
  cwd: cwd2,
4581
4808
  dryRun,
@@ -4591,16 +4818,16 @@ async function cliSetupIdeFiles(argv, options = {}) {
4591
4818
  }
4592
4819
 
4593
4820
  // src/localState/connectCommand.ts
4594
- var path19 = __toESM(require("path"), 1);
4821
+ var path20 = __toESM(require("path"), 1);
4595
4822
  var os7 = __toESM(require("os"), 1);
4596
4823
 
4597
4824
  // src/config.ts
4598
4825
  var process2 = __toESM(require("process"), 1);
4599
4826
  var fs14 = __toESM(require("fs"), 1);
4600
- var path17 = __toESM(require("path"), 1);
4827
+ var path18 = __toESM(require("path"), 1);
4601
4828
  var dotenv = __toESM(require("dotenv"), 1);
4602
4829
  var import_v4 = require("zod/v4");
4603
- var dotenvPath = path17.resolve(process2.cwd(), ".env");
4830
+ var dotenvPath = path18.resolve(process2.cwd(), ".env");
4604
4831
  if (fs14.existsSync(dotenvPath)) {
4605
4832
  try {
4606
4833
  dotenv.config({ path: dotenvPath });
@@ -4669,7 +4896,7 @@ var config2 = {
4669
4896
 
4670
4897
  // src/repoFingerprint.ts
4671
4898
  var fs15 = __toESM(require("fs"), 1);
4672
- var path18 = __toESM(require("path"), 1);
4899
+ var path19 = __toESM(require("path"), 1);
4673
4900
  var crypto2 = __toESM(require("crypto"), 1);
4674
4901
  var parseBooleanFlag3 = (value) => {
4675
4902
  if (!value) {
@@ -4708,7 +4935,7 @@ var resolveGitDir = (gitPath) => {
4708
4935
  const match = content.match(/^gitdir:\s*(.+)$/m);
4709
4936
  if (match) {
4710
4937
  const gitDir = match[1].trim();
4711
- return path18.resolve(path18.dirname(gitPath), gitDir);
4938
+ return path19.resolve(path19.dirname(gitPath), gitDir);
4712
4939
  }
4713
4940
  }
4714
4941
  } catch {
@@ -4717,16 +4944,16 @@ var resolveGitDir = (gitPath) => {
4717
4944
  return null;
4718
4945
  };
4719
4946
  var findGitRoot = (start) => {
4720
- let current = path18.resolve(start);
4947
+ let current = path19.resolve(start);
4721
4948
  while (true) {
4722
- const gitPath = path18.join(current, ".git");
4949
+ const gitPath = path19.join(current, ".git");
4723
4950
  if (fs15.existsSync(gitPath)) {
4724
4951
  const gitDir = resolveGitDir(gitPath);
4725
4952
  if (gitDir) {
4726
4953
  return { gitRoot: current, gitDir };
4727
4954
  }
4728
4955
  }
4729
- const parent = path18.dirname(current);
4956
+ const parent = path19.dirname(current);
4730
4957
  if (parent === current) {
4731
4958
  break;
4732
4959
  }
@@ -4735,7 +4962,7 @@ var findGitRoot = (start) => {
4735
4962
  return null;
4736
4963
  };
4737
4964
  var readOriginRemote = (gitDir) => {
4738
- const configPath = path18.join(gitDir, "config");
4965
+ const configPath = path19.join(gitDir, "config");
4739
4966
  try {
4740
4967
  const content = fs15.readFileSync(configPath, "utf8");
4741
4968
  const lines = content.split(/\r?\n/);
@@ -4761,7 +4988,7 @@ var readOriginRemote = (gitDir) => {
4761
4988
  function resolveRepoFingerprint(cwd2) {
4762
4989
  const found = findGitRoot(cwd2);
4763
4990
  if (!found) {
4764
- const fallbackPath = path18.resolve(cwd2);
4991
+ const fallbackPath = path19.resolve(cwd2);
4765
4992
  const fingerprint2 = sha256(fallbackPath);
4766
4993
  debugLog(`repo fingerprint=${fingerprint2} source=path-fallback`);
4767
4994
  return {
@@ -4783,7 +5010,7 @@ function resolveRepoFingerprint(cwd2) {
4783
5010
  source: "git-remote"
4784
5011
  };
4785
5012
  }
4786
- const fingerprint = sha256(path18.resolve(gitRoot));
5013
+ const fingerprint = sha256(path19.resolve(gitRoot));
4787
5014
  debugLog(`repo fingerprint=${fingerprint} source=path-fallback`);
4788
5015
  return {
4789
5016
  fingerprint,
@@ -4809,16 +5036,32 @@ function normalizeGitRemote(remoteUrl) {
4809
5036
  normalized = normalized.replace(/\/+$/, "");
4810
5037
  return normalized.toLowerCase() || null;
4811
5038
  }
4812
- function formatIdeSetupRepairHint(workspaceRoot, cliPath) {
4813
- const cli = cliPath ?? "<path-to-packages/mcp/dist/cli.cjs>";
4814
- return `Repair with (no new connect code): node ${cli} setup-ide-files --all --local --dev --force --workspace-root ${workspaceRoot}`;
4815
- }
4816
5039
  async function runConnectCommand(options) {
4817
5040
  const code = normalizeConnectCode(options.code);
4818
- const cwd2 = path19.resolve(options.cwd ?? process.cwd());
5041
+ const cwd2 = path20.resolve(options.cwd ?? process.cwd());
4819
5042
  const apiUrl = (options.apiUrl ?? config2.apiUrl).replace(/\/+$/, "");
4820
5043
  const homeDir = options.homeDir ?? os7.homedir();
4821
5044
  const environment = "local";
5045
+ const executionMode = await resolvePackageExecutionMode({
5046
+ executionMode: options.executionMode,
5047
+ // Only honor an explicit caller cliPath; never auto-resolve before mode detection
5048
+ // (published installs must not be forced into local monorepo mode).
5049
+ cliPath: options.cliPath,
5050
+ apiUrl,
5051
+ scriptPath: options.scriptPath,
5052
+ env: options.env,
5053
+ resolveBuiltCliPath: resolveBuiltCliPathAsync
5054
+ });
5055
+ let resolvedMode = executionMode;
5056
+ if (resolvedMode.kind === "local" && !resolvedMode.cliPath) {
5057
+ const cliPath = await resolveBuiltCliPathAsync();
5058
+ if (!cliPath) {
5059
+ throw new Error(
5060
+ "[memoraone-mcp] Local connect requires a built CLI at packages/mcp/dist/cli.cjs. Run pnpm build first."
5061
+ );
5062
+ }
5063
+ resolvedMode = { kind: "local", cliPath };
5064
+ }
4822
5065
  const ensured = await ensureRepositoryBindingForRoot(cwd2, {
4823
5066
  homeDir,
4824
5067
  identityDeps: options.identityDeps,
@@ -4826,7 +5069,7 @@ async function runConnectCommand(options) {
4826
5069
  });
4827
5070
  if (ensured.legacyM1WarningPath) {
4828
5071
  process.stderr.write(
4829
- `[memoraone-mcp] warning: ignoring legacy ${path19.basename(ensured.legacyM1WarningPath)} at ${ensured.legacyM1WarningPath} (credentials and binding are package-managed)
5072
+ `[memoraone-mcp] warning: ignoring legacy ${path20.basename(ensured.legacyM1WarningPath)} at ${ensured.legacyM1WarningPath} (credentials and binding are package-managed)
4830
5073
  `
4831
5074
  );
4832
5075
  }
@@ -4902,7 +5145,7 @@ async function runConnectCommand(options) {
4902
5145
  if (options.configureIdes !== false) {
4903
5146
  const targets = { cursor: true, vscode: true, jetbrains: true };
4904
5147
  const setup = options.setupIdeFiles ?? runSetupIdeFiles;
4905
- const cliPath = options.cliPath !== void 0 ? options.cliPath : await resolveBuiltCliPathAsync();
5148
+ const modeSetup = setupOptionsFromPackageExecutionMode(resolvedMode);
4906
5149
  let setupResult;
4907
5150
  try {
4908
5151
  setupResult = await setup({
@@ -4915,12 +5158,9 @@ async function runConnectCommand(options) {
4915
5158
  noGitignore: true,
4916
5159
  skipDaemonCleanup: true,
4917
5160
  homeDir,
4918
- cursorEnvironment: "local",
4919
- devMode: true,
4920
- // Propagate the same backend endpoint used for redeem into all IDE configs.
5161
+ // Propagate the redeemed binding API URL (backend only — not execution mode).
4921
5162
  apiUrl,
4922
- cursorLocalCliPathOverride: cliPath,
4923
- cliPathOverride: cliPath,
5163
+ ...modeSetup,
4924
5164
  ...options.setupIdeOptions
4925
5165
  });
4926
5166
  } catch (err) {
@@ -4934,7 +5174,7 @@ async function runConnectCommand(options) {
4934
5174
  }
4935
5175
  if (setupResult.exitCode !== 0) {
4936
5176
  const detail = setupResult.error ?? "unknown IDE setup error";
4937
- const repair = formatIdeSetupRepairHint(cwd2, cliPath);
5177
+ const repair = formatIdeSetupRepairHint(cwd2, resolvedMode);
4938
5178
  return {
4939
5179
  exitCode: 1,
4940
5180
  repositoryBindingId: ensured.repositoryBindingId,
@@ -4944,9 +5184,23 @@ async function runConnectCommand(options) {
4944
5184
  createdBinding: ensured.created,
4945
5185
  legacyM1WarningPath: ensured.legacyM1WarningPath,
4946
5186
  ideSetupError: detail,
5187
+ executionMode: resolvedMode,
4947
5188
  message: `Repository connection exists for binding ${ensured.repositoryBindingId} (project ${redeemed.project_id}), but IDE configuration failed: ${detail}. Credentials and binding were kept. ${repair}`
4948
5189
  };
4949
5190
  }
5191
+ return {
5192
+ exitCode: 0,
5193
+ repositoryBindingId: ensured.repositoryBindingId,
5194
+ projectId: redeemed.project_id,
5195
+ installationPublicId: redeemed.installation_public_id,
5196
+ recovered: redeemed.recovered,
5197
+ createdBinding: ensured.created,
5198
+ legacyM1WarningPath: ensured.legacyM1WarningPath,
5199
+ executionMode: resolvedMode,
5200
+ configuredTargets: targets,
5201
+ setupResult,
5202
+ message: baseSuccess
5203
+ };
4950
5204
  }
4951
5205
  return {
4952
5206
  exitCode: 0,
@@ -4956,18 +5210,27 @@ async function runConnectCommand(options) {
4956
5210
  recovered: redeemed.recovered,
4957
5211
  createdBinding: ensured.created,
4958
5212
  legacyM1WarningPath: ensured.legacyM1WarningPath,
5213
+ executionMode: resolvedMode,
4959
5214
  message: baseSuccess
4960
5215
  };
4961
5216
  }
4962
5217
  function parseConnectArgv(argv) {
4963
5218
  let code;
4964
5219
  let apiUrl;
5220
+ let verbose = false;
4965
5221
  for (let i = 0; i < argv.length; i++) {
4966
5222
  const a = argv[i];
5223
+ if (a === "--verbose") {
5224
+ verbose = true;
5225
+ continue;
5226
+ }
4967
5227
  if (a === "--api-url") {
4968
5228
  const value = argv[++i];
4969
5229
  if (!value || value.startsWith("-")) {
4970
- return { error: "Usage: memoraone-mcp connect <code> [--api-url <url>]" };
5230
+ return {
5231
+ verbose,
5232
+ error: "Usage: memoraone-mcp connect <code> [--api-url <url>] [--verbose]"
5233
+ };
4971
5234
  }
4972
5235
  apiUrl = value;
4973
5236
  continue;
@@ -4975,23 +5238,29 @@ function parseConnectArgv(argv) {
4975
5238
  if (a.startsWith("--api-url=")) {
4976
5239
  const value = a.slice("--api-url=".length);
4977
5240
  if (!value) {
4978
- return { error: "Usage: memoraone-mcp connect <code> [--api-url <url>]" };
5241
+ return {
5242
+ verbose,
5243
+ error: "Usage: memoraone-mcp connect <code> [--api-url <url>] [--verbose]"
5244
+ };
4979
5245
  }
4980
5246
  apiUrl = value;
4981
5247
  continue;
4982
5248
  }
4983
5249
  if (a.startsWith("-")) {
4984
- return { error: `Unknown connect option: ${a}` };
5250
+ return { verbose, error: `Unknown connect option: ${a}` };
4985
5251
  }
4986
5252
  if (!code) {
4987
5253
  code = a;
4988
5254
  continue;
4989
5255
  }
4990
- return { error: "Usage: memoraone-mcp connect <code> [--api-url <url>]" };
5256
+ return {
5257
+ verbose,
5258
+ error: "Usage: memoraone-mcp connect <code> [--api-url <url>] [--verbose]"
5259
+ };
4991
5260
  }
4992
- return { code, apiUrl };
5261
+ return { code, apiUrl, verbose };
4993
5262
  }
4994
- async function cliConnect(argv) {
5263
+ async function cliConnect(argv, options = {}) {
4995
5264
  const parsed2 = parseConnectArgv(argv);
4996
5265
  if (parsed2.error) {
4997
5266
  process.stderr.write(`${parsed2.error}
@@ -4999,19 +5268,53 @@ async function cliConnect(argv) {
4999
5268
  return 1;
5000
5269
  }
5001
5270
  if (!parsed2.code) {
5002
- process.stderr.write("Usage: memoraone-mcp connect <code> [--api-url <url>]\n");
5271
+ process.stderr.write("Usage: memoraone-mcp connect <code> [--api-url <url>] [--verbose]\n");
5003
5272
  return 1;
5004
5273
  }
5274
+ const println = options.println ?? ((line) => process.stdout.write(`${line}
5275
+ `));
5005
5276
  try {
5006
5277
  const result = await runConnectCommand({
5007
5278
  code: parsed2.code,
5008
- cwd: process.cwd(),
5279
+ cwd: options.cwd ?? process.cwd(),
5009
5280
  apiUrl: parsed2.apiUrl,
5010
- packageVersion: process.env.npm_package_version ?? null
5281
+ packageVersion: process.env.npm_package_version ?? null,
5282
+ env: options.env,
5283
+ ...options.connectOptions
5011
5284
  });
5012
5285
  if (result.exitCode === 0) {
5013
- process.stdout.write(`${result.message}
5014
- `);
5286
+ if (parsed2.verbose) {
5287
+ println(result.message);
5288
+ if (result.projectId) {
5289
+ println(`[memoraone-mcp] Project id: ${result.projectId}`);
5290
+ }
5291
+ println(`[memoraone-mcp] Repository root: ${options.cwd ?? process.cwd()}`);
5292
+ if (result.setupResult && result.configuredTargets) {
5293
+ logSetupIdeFilesVerboseSuccess(
5294
+ {
5295
+ targets: result.configuredTargets,
5296
+ dryRun: false,
5297
+ result: result.setupResult
5298
+ },
5299
+ println
5300
+ );
5301
+ }
5302
+ } else {
5303
+ const presentation = createTerminalPresentation({
5304
+ env: options.env ?? process.env,
5305
+ stdoutIsTty: options.stdoutIsTty ?? process.stdout.isTTY === true,
5306
+ color: options.color,
5307
+ unicode: options.unicode
5308
+ });
5309
+ printSetupSuccess(
5310
+ {
5311
+ repositoryConnected: true,
5312
+ targets: result.configuredTargets,
5313
+ presentation
5314
+ },
5315
+ println
5316
+ );
5317
+ }
5015
5318
  } else {
5016
5319
  process.stderr.write(`[memoraone-mcp] ${result.message}
5017
5320
  `);
@@ -5033,7 +5336,7 @@ if (args.includes("--version") || args.includes("-v")) {
5033
5336
  }
5034
5337
  if (args.includes("--help") || args.includes("-h")) {
5035
5338
  console.log(
5036
- "Usage: memoraone-mcp [--version] [--help]\n memoraone-mcp connect <code> [--api-url <url>]\n memoraone-mcp [--daemon --binding-id <mrb_\u2026> [--ide cursor|copilot-vscode|jetbrains]]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run] [--no-gitignore] [--cleanup] [--dev] [--repair] [--workspace-root <path>] [--api-url <url>]\n Cursor API environment (with --cursor or --all): --local (node + built cli.cjs + local API) | --staging (npx + staging API)\n --workspace-root: configure an explicit bound workspace (skips .git discovery; for fileless Local MCP repair)\n --api-url: developer-only local/dev API endpoint (defaults from binding or http://localhost:3001; never Studio :3000)\n memoraone-mcp cleanup [--project-id <uuid>] [--ide cursor|copilot-vscode|jetbrains] [--dry-run] [--all-projects] [--yes]"
5339
+ "Usage: memoraone-mcp [--version] [--help]\n memoraone-mcp connect <code> [--api-url <url>] [--verbose]\n memoraone-mcp [--daemon --binding-id <mrb_\u2026> [--ide cursor|copilot-vscode|jetbrains]]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run] [--no-gitignore] [--cleanup] [--dev] [--repair] [--workspace-root <path>] [--api-url <url>] [--verbose]\n Cursor API environment (with --cursor or --all): --local (node + built cli.cjs + local API) | --staging (npx + staging API)\n --workspace-root: configure an explicit bound workspace (skips .git discovery; for fileless Local MCP repair)\n --api-url: developer-only local/dev API endpoint (defaults from binding or http://localhost:3001; never Studio :3000)\n --verbose: show developer diagnostics (paths, backups, daemon cleanup, handshake)\n memoraone-mcp cleanup [--project-id <uuid>] [--ide cursor|copilot-vscode|jetbrains] [--dry-run] [--all-projects] [--yes]"
5037
5340
  );
5038
5341
  process.exit(0);
5039
5342
  }