@memoraone/mcp 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +559 -56
- package/dist/daemon.cjs +294 -77
- package/dist/index.cjs +181 -74
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var require_package = __commonJS({
|
|
|
30
30
|
"package.json"(exports2, module2) {
|
|
31
31
|
module2.exports = {
|
|
32
32
|
name: "@memoraone/mcp",
|
|
33
|
-
version: "0.1.
|
|
33
|
+
version: "0.1.23",
|
|
34
34
|
type: "module",
|
|
35
35
|
main: "dist/index.cjs",
|
|
36
36
|
bin: {
|
|
@@ -67,27 +67,96 @@ var require_package = __commonJS({
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
// src/cli.ts
|
|
70
|
-
var
|
|
70
|
+
var path5 = __toESM(require("path"), 1);
|
|
71
71
|
var net = __toESM(require("net"), 1);
|
|
72
|
-
var
|
|
72
|
+
var import_node_child_process2 = require("child_process");
|
|
73
73
|
|
|
74
74
|
// src/socketPaths.ts
|
|
75
75
|
var os = __toESM(require("os"), 1);
|
|
76
76
|
var path = __toESM(require("path"), 1);
|
|
77
77
|
var fs = __toESM(require("fs"), 1);
|
|
78
78
|
var BASE_DIR = process.env.MEMORAONE_MCP_LOCK_DIR || path.join(os.homedir(), ".memoraone-mcp");
|
|
79
|
-
|
|
79
|
+
var SOCKET_PROJECT_ID_RE = /^mcp-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:-([a-z-]+))?\.sock$/i;
|
|
80
|
+
function getMcpBaseDir() {
|
|
81
|
+
return BASE_DIR;
|
|
82
|
+
}
|
|
83
|
+
var IDE_TYPES = ["cursor", "copilot-vscode", "jetbrains"];
|
|
84
|
+
var IDE_TYPE_SET = new Set(IDE_TYPES);
|
|
85
|
+
function parseIdeType(value) {
|
|
86
|
+
if (value === void 0 || value.trim() === "" || !IDE_TYPE_SET.has(value)) {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
function resolveIdeTypeFromEnv(env = process.env) {
|
|
92
|
+
return parseIdeType(env.MEMORAONE_IDE_TYPE);
|
|
93
|
+
}
|
|
94
|
+
function parseIdeTypeFromCommandLine(commandLine) {
|
|
95
|
+
const match = commandLine.match(/--ide\s+(cursor|copilot-vscode|jetbrains)(?:\s|$)/);
|
|
96
|
+
return match ? match[1] : void 0;
|
|
97
|
+
}
|
|
98
|
+
function buildDaemonSpawnArgs(scriptPath, projectId, env = process.env) {
|
|
99
|
+
const args2 = [scriptPath, "--daemon", "--project-id", projectId];
|
|
100
|
+
const ideType = resolveIdeTypeFromEnv(env);
|
|
101
|
+
if (ideType) {
|
|
102
|
+
args2.push("--ide", ideType);
|
|
103
|
+
}
|
|
104
|
+
return args2;
|
|
105
|
+
}
|
|
106
|
+
function getSocketPath(projectId, ideType) {
|
|
107
|
+
if (ideType) {
|
|
108
|
+
return path.join(BASE_DIR, `mcp-${projectId}-${ideType}.sock`);
|
|
109
|
+
}
|
|
80
110
|
return path.join(BASE_DIR, `mcp-${projectId}.sock`);
|
|
81
111
|
}
|
|
112
|
+
function getDaemonSocketPath(projectId, env = process.env) {
|
|
113
|
+
return getSocketPath(projectId, resolveIdeTypeFromEnv(env));
|
|
114
|
+
}
|
|
82
115
|
function ensureBaseDir() {
|
|
83
116
|
fs.mkdirSync(BASE_DIR, { recursive: true });
|
|
84
117
|
return BASE_DIR;
|
|
85
118
|
}
|
|
119
|
+
function isMemoraoneSocketFilename(filename) {
|
|
120
|
+
return SOCKET_PROJECT_ID_RE.test(path.basename(filename));
|
|
121
|
+
}
|
|
122
|
+
function extractProjectIdFromSocketFilename(filename) {
|
|
123
|
+
const match = path.basename(filename).match(SOCKET_PROJECT_ID_RE);
|
|
124
|
+
return match ? match[1].toLowerCase() : null;
|
|
125
|
+
}
|
|
126
|
+
function isSocketFilenameForProject(filename, projectId) {
|
|
127
|
+
const extracted = extractProjectIdFromSocketFilename(filename);
|
|
128
|
+
return extracted !== null && extracted === projectId.trim().toLowerCase();
|
|
129
|
+
}
|
|
130
|
+
function extractIdeTypeFromSocketFilename(filename) {
|
|
131
|
+
const match = path.basename(filename).match(SOCKET_PROJECT_ID_RE);
|
|
132
|
+
if (!match) return null;
|
|
133
|
+
const suffix = match[2];
|
|
134
|
+
if (suffix === void 0) return "legacy";
|
|
135
|
+
if (IDE_TYPE_SET.has(suffix)) return suffix;
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
function isSocketFilenameForProjectAndIde(filename, projectId, ide) {
|
|
139
|
+
if (!isSocketFilenameForProject(filename, projectId)) return false;
|
|
140
|
+
if (ide === void 0) return true;
|
|
141
|
+
const socketIde = extractIdeTypeFromSocketFilename(filename);
|
|
142
|
+
if (socketIde === null) return false;
|
|
143
|
+
if (ide === "cursor") {
|
|
144
|
+
return socketIde === "cursor" || socketIde === "legacy";
|
|
145
|
+
}
|
|
146
|
+
return socketIde === ide;
|
|
147
|
+
}
|
|
86
148
|
|
|
87
149
|
// src/projectBinding.ts
|
|
88
150
|
var fs2 = __toESM(require("fs/promises"), 1);
|
|
89
151
|
var path2 = __toESM(require("path"), 1);
|
|
90
152
|
var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
153
|
+
function normalizeEnvironment(raw) {
|
|
154
|
+
if (raw === void 0 || raw === null || typeof raw !== "string") {
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
157
|
+
const trimmed = raw.trim();
|
|
158
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
159
|
+
}
|
|
91
160
|
function parseAndValidateM1(content, markerPath) {
|
|
92
161
|
let parsed;
|
|
93
162
|
try {
|
|
@@ -104,7 +173,8 @@ function parseAndValidateM1(content, markerPath) {
|
|
|
104
173
|
}
|
|
105
174
|
const apiKeyRaw = parsed?.MEMORAONE_API_KEY ?? parsed?.api_key;
|
|
106
175
|
const apiKey = apiKeyRaw !== void 0 && apiKeyRaw !== null && typeof apiKeyRaw === "string" && apiKeyRaw.trim() !== "" ? apiKeyRaw.trim() : null;
|
|
107
|
-
|
|
176
|
+
const environment = normalizeEnvironment(parsed?.environment);
|
|
177
|
+
return environment === void 0 ? { projectId: projectId.trim(), apiKey } : { projectId: projectId.trim(), apiKey, environment };
|
|
108
178
|
}
|
|
109
179
|
async function resolveProjectIdFromExplicitM1Path() {
|
|
110
180
|
const raw = process.env.MEMORAONE_M1_PATH;
|
|
@@ -114,8 +184,8 @@ async function resolveProjectIdFromExplicitM1Path() {
|
|
|
114
184
|
const markerPath = path2.resolve(raw);
|
|
115
185
|
try {
|
|
116
186
|
const content = await fs2.readFile(markerPath, "utf8");
|
|
117
|
-
const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
|
|
118
|
-
return { projectId, apiKey, foundAt: markerPath };
|
|
187
|
+
const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
|
|
188
|
+
return environment === void 0 ? { projectId, apiKey, foundAt: markerPath } : { projectId, apiKey, environment, foundAt: markerPath };
|
|
119
189
|
} catch (err) {
|
|
120
190
|
if (err?.code === "ENOENT") {
|
|
121
191
|
return null;
|
|
@@ -129,9 +199,9 @@ async function findM1WalkingUp(workspaceRoot) {
|
|
|
129
199
|
const markerPath = path2.join(current, "memoraone.m1");
|
|
130
200
|
try {
|
|
131
201
|
const content = await fs2.readFile(markerPath, "utf8");
|
|
132
|
-
const { projectId, apiKey } = parseAndValidateM1(content, markerPath);
|
|
202
|
+
const { projectId, apiKey, environment } = parseAndValidateM1(content, markerPath);
|
|
133
203
|
const repoRoot = path2.dirname(markerPath);
|
|
134
|
-
return { projectId, apiKey, repoRoot, markerPath };
|
|
204
|
+
return environment === void 0 ? { projectId, apiKey, repoRoot, markerPath } : { projectId, apiKey, environment, repoRoot, markerPath };
|
|
135
205
|
} catch (err) {
|
|
136
206
|
if (err?.code !== "ENOENT") {
|
|
137
207
|
throw err;
|
|
@@ -191,6 +261,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
|
|
|
191
261
|
workspaceRoot: path2.dirname(explicitBinding.foundAt),
|
|
192
262
|
m1Path: explicitBinding.foundAt,
|
|
193
263
|
apiKey: resolved.apiKey,
|
|
264
|
+
...explicitBinding.environment !== void 0 ? { environment: explicitBinding.environment } : {},
|
|
194
265
|
bindingSource: "explicit-m1-path",
|
|
195
266
|
apiKeySource: resolved.apiKeySource
|
|
196
267
|
};
|
|
@@ -208,6 +279,7 @@ async function resolveAuthoritativeBinding(workspaceRoot) {
|
|
|
208
279
|
workspaceRoot: binding.repoRoot,
|
|
209
280
|
m1Path: binding.markerPath,
|
|
210
281
|
apiKey: resolved.apiKey,
|
|
282
|
+
...binding.environment !== void 0 ? { environment: binding.environment } : {},
|
|
211
283
|
bindingSource: "workspace-search",
|
|
212
284
|
apiKeySource: resolved.apiKeySource
|
|
213
285
|
};
|
|
@@ -220,8 +292,400 @@ function encodeResolvedBinding(binding) {
|
|
|
220
292
|
}
|
|
221
293
|
|
|
222
294
|
// src/setupIdeFiles.ts
|
|
295
|
+
var fs4 = __toESM(require("fs/promises"), 1);
|
|
296
|
+
var path4 = __toESM(require("path"), 1);
|
|
297
|
+
|
|
298
|
+
// src/cleanup.ts
|
|
223
299
|
var fs3 = __toESM(require("fs/promises"), 1);
|
|
224
300
|
var path3 = __toESM(require("path"), 1);
|
|
301
|
+
var readline = __toESM(require("readline/promises"), 1);
|
|
302
|
+
var import_node_child_process = require("child_process");
|
|
303
|
+
var import_node_util = require("util");
|
|
304
|
+
var import_node_process = require("process");
|
|
305
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
306
|
+
var DAEMON_PROJECT_ID_RE = /--project-id\s+([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
|
|
307
|
+
var PROJECT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
308
|
+
var MEMORAONE_MCP_COMMAND_RE = /memoraone-mcp|memoraOne-mcp|@memoraone\/mcp/;
|
|
309
|
+
var CLEANUP_PROJECT_ID_REQUIRED_ERROR = "Provide --project-id <id> or run from a folder containing memoraone.m1.";
|
|
310
|
+
function isMemoraoneMcpCommandLine(commandLine) {
|
|
311
|
+
return MEMORAONE_MCP_COMMAND_RE.test(commandLine);
|
|
312
|
+
}
|
|
313
|
+
function parseDaemonProjectIdFromCommandLine(commandLine) {
|
|
314
|
+
if (!commandLine.includes("--daemon")) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
const match = commandLine.match(DAEMON_PROJECT_ID_RE);
|
|
318
|
+
return match ? match[1].toLowerCase() : null;
|
|
319
|
+
}
|
|
320
|
+
function parseDaemonIdeFromCommandLine(commandLine) {
|
|
321
|
+
if (!commandLine.includes("--daemon")) {
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
return parseIdeTypeFromCommandLine(commandLine);
|
|
325
|
+
}
|
|
326
|
+
function parseDaemonProcessLines(lines) {
|
|
327
|
+
const processes = [];
|
|
328
|
+
for (const line of lines) {
|
|
329
|
+
const trimmed = line.trim();
|
|
330
|
+
if (!trimmed) continue;
|
|
331
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
332
|
+
if (spaceIdx <= 0) continue;
|
|
333
|
+
const pid = Number.parseInt(trimmed.slice(0, spaceIdx), 10);
|
|
334
|
+
if (!Number.isFinite(pid) || pid <= 0) continue;
|
|
335
|
+
const command = trimmed.slice(spaceIdx + 1);
|
|
336
|
+
if (!isMemoraoneMcpCommandLine(command)) continue;
|
|
337
|
+
const projectId = parseDaemonProjectIdFromCommandLine(command);
|
|
338
|
+
if (projectId === null) continue;
|
|
339
|
+
const ide = parseDaemonIdeFromCommandLine(command);
|
|
340
|
+
processes.push({ pid, command, projectId, ...ide !== void 0 ? { ide } : {} });
|
|
341
|
+
}
|
|
342
|
+
return processes;
|
|
343
|
+
}
|
|
344
|
+
function normalizeCleanupProjectId(projectId) {
|
|
345
|
+
const trimmed = projectId.trim();
|
|
346
|
+
if (!PROJECT_ID_RE.test(trimmed)) {
|
|
347
|
+
return { error: `Invalid project id: ${projectId}` };
|
|
348
|
+
}
|
|
349
|
+
return trimmed.toLowerCase();
|
|
350
|
+
}
|
|
351
|
+
async function defaultListDaemonProcesses() {
|
|
352
|
+
const { stdout } = await execFileAsync("ps", ["-eo", "pid=,args="], {
|
|
353
|
+
maxBuffer: 10 * 1024 * 1024
|
|
354
|
+
});
|
|
355
|
+
return parseDaemonProcessLines(stdout.split("\n"));
|
|
356
|
+
}
|
|
357
|
+
async function defaultListSocketPaths(projectId) {
|
|
358
|
+
const baseDir = getMcpBaseDir();
|
|
359
|
+
let entries;
|
|
360
|
+
try {
|
|
361
|
+
entries = await fs3.readdir(baseDir);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
364
|
+
if (code === "ENOENT") {
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
throw err;
|
|
368
|
+
}
|
|
369
|
+
const paths = [];
|
|
370
|
+
for (const name of entries) {
|
|
371
|
+
if (projectId === null) {
|
|
372
|
+
if (isMemoraoneSocketFilename(name)) {
|
|
373
|
+
paths.push(path3.join(baseDir, name));
|
|
374
|
+
}
|
|
375
|
+
} else if (isSocketFilenameForProject(name, projectId)) {
|
|
376
|
+
paths.push(path3.join(baseDir, name));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return paths.sort();
|
|
380
|
+
}
|
|
381
|
+
function filterSocketPathsByIde(socketPaths, projectId, ide) {
|
|
382
|
+
if (ide === void 0) return socketPaths;
|
|
383
|
+
return socketPaths.filter(
|
|
384
|
+
(socketPath) => isSocketFilenameForProjectAndIde(path3.basename(socketPath), projectId, ide)
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
async function defaultKillProcess(pid) {
|
|
388
|
+
process.kill(pid, "SIGTERM");
|
|
389
|
+
}
|
|
390
|
+
async function defaultRemoveSocket(socketPath) {
|
|
391
|
+
await fs3.unlink(socketPath);
|
|
392
|
+
}
|
|
393
|
+
async function defaultConfirm(message) {
|
|
394
|
+
if (!import_node_process.stdin.isTTY) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
const rl = readline.createInterface({ input: import_node_process.stdin, output: import_node_process.stdout });
|
|
398
|
+
try {
|
|
399
|
+
const answer = await rl.question(`${message} [y/N] `);
|
|
400
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
401
|
+
} finally {
|
|
402
|
+
rl.close();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async function resolveCleanupTarget(cwd) {
|
|
406
|
+
try {
|
|
407
|
+
const binding = await resolveAuthoritativeBinding([path3.resolve(cwd)]);
|
|
408
|
+
return {
|
|
409
|
+
workspaceRoot: binding.workspaceRoot,
|
|
410
|
+
m1Path: binding.m1Path,
|
|
411
|
+
projectId: binding.projectId
|
|
412
|
+
};
|
|
413
|
+
} catch {
|
|
414
|
+
return { error: CLEANUP_PROJECT_ID_REQUIRED_ERROR };
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function filterProcessesForScope(processes, projectId) {
|
|
418
|
+
if (projectId === null) {
|
|
419
|
+
return { matching: processes, skipped: [] };
|
|
420
|
+
}
|
|
421
|
+
const normalized = projectId.toLowerCase();
|
|
422
|
+
const matching = [];
|
|
423
|
+
const skipped = [];
|
|
424
|
+
for (const proc of processes) {
|
|
425
|
+
if (proc.projectId === normalized) {
|
|
426
|
+
matching.push(proc);
|
|
427
|
+
} else {
|
|
428
|
+
skipped.push(proc);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return { matching, skipped };
|
|
432
|
+
}
|
|
433
|
+
function logPrefix(dryRun) {
|
|
434
|
+
return dryRun ? "[cleanup][dry-run]" : "[cleanup]";
|
|
435
|
+
}
|
|
436
|
+
function logReconnectNotice(prefix, ide) {
|
|
437
|
+
if (ide) {
|
|
438
|
+
console.log(
|
|
439
|
+
`${prefix} Note: Valid ${ide} connections for this project may disconnect temporarily; they should reconnect automatically. Stale connections will remain cleared.`
|
|
440
|
+
);
|
|
441
|
+
} else {
|
|
442
|
+
console.log(
|
|
443
|
+
`${prefix} Note: Valid IDE connections for this project may disconnect temporarily; they should reconnect automatically. Stale connections will remain cleared.`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
async function runCleanup(opts) {
|
|
448
|
+
const listProcesses = opts.listProcesses ?? defaultListDaemonProcesses;
|
|
449
|
+
const listSocketPaths = opts.listSocketPaths ?? defaultListSocketPaths;
|
|
450
|
+
const killProcess = opts.killProcess ?? defaultKillProcess;
|
|
451
|
+
const removeSocket = opts.removeSocket ?? defaultRemoveSocket;
|
|
452
|
+
const confirm = opts.confirm ?? defaultConfirm;
|
|
453
|
+
const prefix = logPrefix(opts.dryRun);
|
|
454
|
+
let targetProjectId = null;
|
|
455
|
+
let workspaceRoot;
|
|
456
|
+
let m1Path;
|
|
457
|
+
if (opts.allProjects) {
|
|
458
|
+
if (opts.projectId) {
|
|
459
|
+
return {
|
|
460
|
+
exitCode: 1,
|
|
461
|
+
killedPids: [],
|
|
462
|
+
removedSockets: [],
|
|
463
|
+
skippedProcesses: [],
|
|
464
|
+
error: "Cannot combine --all-projects with --project-id."
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
console.log(`${prefix} Mode: all projects (--all-projects)`);
|
|
468
|
+
console.warn(
|
|
469
|
+
`${prefix} WARNING: This stops every MemoraOne MCP daemon and removes all project sockets under ${getMcpBaseDir()}.`
|
|
470
|
+
);
|
|
471
|
+
} else if (opts.projectId) {
|
|
472
|
+
const normalized = normalizeCleanupProjectId(opts.projectId);
|
|
473
|
+
if (typeof normalized !== "string") {
|
|
474
|
+
return { exitCode: 1, killedPids: [], removedSockets: [], skippedProcesses: [], error: normalized.error };
|
|
475
|
+
}
|
|
476
|
+
targetProjectId = normalized;
|
|
477
|
+
console.log(`${prefix} Project id: ${targetProjectId}`);
|
|
478
|
+
if (opts.ide) {
|
|
479
|
+
console.log(`${prefix} IDE filter: ${opts.ide}`);
|
|
480
|
+
}
|
|
481
|
+
} else {
|
|
482
|
+
const target = await resolveCleanupTarget(opts.cwd);
|
|
483
|
+
if ("error" in target) {
|
|
484
|
+
return { exitCode: 1, killedPids: [], removedSockets: [], skippedProcesses: [], error: target.error };
|
|
485
|
+
}
|
|
486
|
+
targetProjectId = target.projectId;
|
|
487
|
+
workspaceRoot = target.workspaceRoot;
|
|
488
|
+
m1Path = target.m1Path;
|
|
489
|
+
console.log(`${prefix} Workspace root: ${workspaceRoot}`);
|
|
490
|
+
console.log(`${prefix} memoraone.m1: ${m1Path}`);
|
|
491
|
+
console.log(`${prefix} Project id: ${targetProjectId}`);
|
|
492
|
+
if (opts.ide) {
|
|
493
|
+
console.log(`${prefix} IDE filter: ${opts.ide}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (targetProjectId !== null || opts.ide) {
|
|
497
|
+
logReconnectNotice(prefix, opts.ide);
|
|
498
|
+
}
|
|
499
|
+
const allDaemonProcesses = await listProcesses();
|
|
500
|
+
const { matching: projectProcesses, skipped: skippedProcesses } = filterProcessesForScope(
|
|
501
|
+
allDaemonProcesses,
|
|
502
|
+
targetProjectId
|
|
503
|
+
);
|
|
504
|
+
let processesToStop = projectProcesses;
|
|
505
|
+
const ideSkippedProcesses = [];
|
|
506
|
+
if (opts.ide && targetProjectId !== null) {
|
|
507
|
+
processesToStop = [];
|
|
508
|
+
for (const proc of projectProcesses) {
|
|
509
|
+
if (proc.ide === opts.ide) {
|
|
510
|
+
processesToStop.push(proc);
|
|
511
|
+
} else if (proc.ide === void 0) {
|
|
512
|
+
ideSkippedProcesses.push(proc);
|
|
513
|
+
console.log(
|
|
514
|
+
`${prefix} Skipped daemon pid=${proc.pid} because IDE could not be safely determined.`
|
|
515
|
+
);
|
|
516
|
+
} else {
|
|
517
|
+
ideSkippedProcesses.push(proc);
|
|
518
|
+
console.log(
|
|
519
|
+
`${prefix} Skipped daemon pid=${proc.pid} (IDE ${proc.ide} does not match filter ${opts.ide}).`
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const allSocketPaths = await listSocketPaths(targetProjectId);
|
|
525
|
+
const socketPaths = targetProjectId === null ? allSocketPaths : filterSocketPathsByIde(allSocketPaths, targetProjectId, opts.ide);
|
|
526
|
+
if (opts.allProjects) {
|
|
527
|
+
const projectIds = /* @__PURE__ */ new Set();
|
|
528
|
+
for (const proc of processesToStop) {
|
|
529
|
+
projectIds.add(proc.projectId);
|
|
530
|
+
}
|
|
531
|
+
for (const socketPath of socketPaths) {
|
|
532
|
+
const id = extractProjectIdFromSocketFilename(path3.basename(socketPath));
|
|
533
|
+
if (id) projectIds.add(id);
|
|
534
|
+
}
|
|
535
|
+
console.log(`${prefix} Projects affected: ${projectIds.size ? [...projectIds].sort().join(", ") : "(none found)"}`);
|
|
536
|
+
}
|
|
537
|
+
if (processesToStop.length) {
|
|
538
|
+
console.log(`${prefix} Daemon processes to stop:`);
|
|
539
|
+
for (const proc of processesToStop) {
|
|
540
|
+
const ideLabel = proc.ide ? ` ide=${proc.ide}` : "";
|
|
541
|
+
console.log(`${prefix} pid=${proc.pid} project=${proc.projectId}${ideLabel}`);
|
|
542
|
+
}
|
|
543
|
+
} else if (ideSkippedProcesses.length) {
|
|
544
|
+
console.log(`${prefix} No matching daemon processes for IDE filter ${opts.ide}.`);
|
|
545
|
+
} else {
|
|
546
|
+
console.log(`${prefix} No matching daemon processes found.`);
|
|
547
|
+
}
|
|
548
|
+
if (socketPaths.length) {
|
|
549
|
+
console.log(`${prefix} Sockets to remove:`);
|
|
550
|
+
for (const socketPath of socketPaths) {
|
|
551
|
+
console.log(`${prefix} ${socketPath}`);
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
console.log(`${prefix} No matching sockets found under ${getMcpBaseDir()}.`);
|
|
555
|
+
}
|
|
556
|
+
if (skippedProcesses.length) {
|
|
557
|
+
console.log(`${prefix} Skipped unrelated daemon processes:`);
|
|
558
|
+
for (const proc of skippedProcesses) {
|
|
559
|
+
console.log(`${prefix} pid=${proc.pid} project=${proc.projectId}`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (opts.allProjects && !opts.dryRun) {
|
|
563
|
+
const ok = opts.assumeYes ? true : await confirm(`${prefix} Proceed with cleanup for ALL projects?`);
|
|
564
|
+
if (!ok) {
|
|
565
|
+
console.log(`${prefix} Aborted.`);
|
|
566
|
+
return {
|
|
567
|
+
exitCode: 1,
|
|
568
|
+
workspaceRoot,
|
|
569
|
+
m1Path,
|
|
570
|
+
projectId: targetProjectId ?? void 0,
|
|
571
|
+
killedPids: [],
|
|
572
|
+
removedSockets: [],
|
|
573
|
+
skippedProcesses: [...skippedProcesses, ...ideSkippedProcesses],
|
|
574
|
+
error: opts.assumeYes ? void 0 : "Aborted (--all requires --yes in non-interactive mode)"
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const killedPids = [];
|
|
579
|
+
const removedSockets = [];
|
|
580
|
+
if (opts.dryRun) {
|
|
581
|
+
console.log(`${prefix} Dry run complete \u2014 no processes stopped, no sockets removed.`);
|
|
582
|
+
return {
|
|
583
|
+
exitCode: 0,
|
|
584
|
+
workspaceRoot,
|
|
585
|
+
m1Path,
|
|
586
|
+
projectId: targetProjectId ?? void 0,
|
|
587
|
+
killedPids: processesToStop.map((p) => p.pid),
|
|
588
|
+
removedSockets: socketPaths,
|
|
589
|
+
skippedProcesses: [...skippedProcesses, ...ideSkippedProcesses]
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
for (const proc of processesToStop) {
|
|
593
|
+
try {
|
|
594
|
+
await killProcess(proc.pid);
|
|
595
|
+
killedPids.push(proc.pid);
|
|
596
|
+
console.log(`${prefix} Stopped daemon pid=${proc.pid} project=${proc.projectId}`);
|
|
597
|
+
} catch (err) {
|
|
598
|
+
console.warn(`${prefix} Could not stop pid=${proc.pid}: ${String(err)}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (killedPids.length) {
|
|
602
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
603
|
+
}
|
|
604
|
+
for (const socketPath of socketPaths) {
|
|
605
|
+
try {
|
|
606
|
+
await removeSocket(socketPath);
|
|
607
|
+
removedSockets.push(socketPath);
|
|
608
|
+
console.log(`${prefix} Removed socket ${socketPath}`);
|
|
609
|
+
} catch (err) {
|
|
610
|
+
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
611
|
+
if (code !== "ENOENT") {
|
|
612
|
+
console.warn(`${prefix} Could not remove socket ${socketPath}: ${String(err)}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
console.log(`${prefix} Done. stopped=${killedPids.length} socketsRemoved=${removedSockets.length}`);
|
|
617
|
+
return {
|
|
618
|
+
exitCode: 0,
|
|
619
|
+
workspaceRoot,
|
|
620
|
+
m1Path,
|
|
621
|
+
projectId: targetProjectId ?? void 0,
|
|
622
|
+
killedPids,
|
|
623
|
+
removedSockets,
|
|
624
|
+
skippedProcesses: [...skippedProcesses, ...ideSkippedProcesses]
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
function parseCleanupFlags(argv) {
|
|
628
|
+
let dryRun = false;
|
|
629
|
+
let allProjects = false;
|
|
630
|
+
let assumeYes = false;
|
|
631
|
+
let projectId;
|
|
632
|
+
let ide;
|
|
633
|
+
let invalidIde;
|
|
634
|
+
const unknown = [];
|
|
635
|
+
for (let i = 0; i < argv.length; i++) {
|
|
636
|
+
const arg = argv[i];
|
|
637
|
+
if (arg === "--dry-run") dryRun = true;
|
|
638
|
+
else if (arg === "--all-projects" || arg === "--all") allProjects = true;
|
|
639
|
+
else if (arg === "--yes" || arg === "-y") assumeYes = true;
|
|
640
|
+
else if (arg === "--project-id") {
|
|
641
|
+
if (i + 1 >= argv.length) {
|
|
642
|
+
unknown.push("--project-id (missing value)");
|
|
643
|
+
} else {
|
|
644
|
+
projectId = argv[++i];
|
|
645
|
+
}
|
|
646
|
+
} else if (arg === "--ide") {
|
|
647
|
+
if (i + 1 >= argv.length) {
|
|
648
|
+
unknown.push("--ide (missing value)");
|
|
649
|
+
} else {
|
|
650
|
+
const value = argv[++i];
|
|
651
|
+
if (IDE_TYPES.includes(value)) {
|
|
652
|
+
ide = value;
|
|
653
|
+
} else {
|
|
654
|
+
invalidIde = value;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
} else if (arg.startsWith("-")) unknown.push(arg);
|
|
658
|
+
else unknown.push(arg);
|
|
659
|
+
}
|
|
660
|
+
return { dryRun, allProjects, assumeYes, projectId, ide, invalidIde, unknown };
|
|
661
|
+
}
|
|
662
|
+
async function cliCleanup(argv) {
|
|
663
|
+
const { dryRun, allProjects, assumeYes, projectId, ide, invalidIde, unknown } = parseCleanupFlags(argv);
|
|
664
|
+
if (invalidIde) {
|
|
665
|
+
console.error(
|
|
666
|
+
`[cleanup] Invalid --ide value: ${invalidIde}. Expected one of: ${IDE_TYPES.join(", ")}.`
|
|
667
|
+
);
|
|
668
|
+
return 1;
|
|
669
|
+
}
|
|
670
|
+
if (unknown.length) {
|
|
671
|
+
console.error(`[cleanup] Unknown option(s): ${unknown.join(", ")}`);
|
|
672
|
+
return 1;
|
|
673
|
+
}
|
|
674
|
+
const result = await runCleanup({
|
|
675
|
+
cwd: process.cwd(),
|
|
676
|
+
dryRun,
|
|
677
|
+
allProjects,
|
|
678
|
+
assumeYes,
|
|
679
|
+
projectId,
|
|
680
|
+
ide
|
|
681
|
+
});
|
|
682
|
+
if (result.error) {
|
|
683
|
+
console.error(`[cleanup] ${result.error}`);
|
|
684
|
+
}
|
|
685
|
+
return result.exitCode;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/setupIdeFiles.ts
|
|
225
689
|
var MANAGED_MARKER = "<!-- MemoraOne managed IDE helper -->";
|
|
226
690
|
var GITIGNORE_MEMORAONE_COMMENT = "# MemoraOne local project binding / API key";
|
|
227
691
|
var GITIGNORE_MEMORAONE_ENTRY = "memoraone.m1";
|
|
@@ -230,15 +694,15 @@ var MEMORAONE_MCP_SERVER = {
|
|
|
230
694
|
args: ["-y", "@memoraone/mcp"]
|
|
231
695
|
};
|
|
232
696
|
function assertUnderRepoRoot(repoRoot, absPath) {
|
|
233
|
-
const normRoot =
|
|
234
|
-
const normPath =
|
|
235
|
-
if (normPath !==
|
|
697
|
+
const normRoot = path4.resolve(repoRoot) + path4.sep;
|
|
698
|
+
const normPath = path4.resolve(absPath);
|
|
699
|
+
if (normPath !== path4.resolve(repoRoot) && !normPath.startsWith(normRoot)) {
|
|
236
700
|
throw new Error(`[setup-ide-files] Refusing to write outside repo root: ${absPath}`);
|
|
237
701
|
}
|
|
238
702
|
}
|
|
239
703
|
async function pathExists(filePath) {
|
|
240
704
|
try {
|
|
241
|
-
await
|
|
705
|
+
await fs4.access(filePath);
|
|
242
706
|
return true;
|
|
243
707
|
} catch {
|
|
244
708
|
return false;
|
|
@@ -259,12 +723,12 @@ ${GITIGNORE_MEMORAONE_ENTRY}
|
|
|
259
723
|
}
|
|
260
724
|
async function ensureGitignoreMemoraone(repoRoot, opts) {
|
|
261
725
|
if (opts.noGitignore) return "skipped";
|
|
262
|
-
const abs =
|
|
726
|
+
const abs = path4.join(repoRoot, ".gitignore");
|
|
263
727
|
assertUnderRepoRoot(repoRoot, abs);
|
|
264
728
|
let prior = "";
|
|
265
729
|
let existed = false;
|
|
266
730
|
try {
|
|
267
|
-
prior = await
|
|
731
|
+
prior = await fs4.readFile(abs, "utf8");
|
|
268
732
|
existed = true;
|
|
269
733
|
} catch (err) {
|
|
270
734
|
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
@@ -275,22 +739,22 @@ async function ensureGitignoreMemoraone(repoRoot, opts) {
|
|
|
275
739
|
const separator = existed && prior.length > 0 ? prior.endsWith("\n") ? "\n" : "\n\n" : "";
|
|
276
740
|
const next = (existed ? prior : "") + separator + block;
|
|
277
741
|
if (opts.dryRun) return existed ? "updated" : "created";
|
|
278
|
-
await
|
|
742
|
+
await fs4.writeFile(abs, next, "utf8");
|
|
279
743
|
return existed ? "updated" : "created";
|
|
280
744
|
}
|
|
281
745
|
async function findRepoRoot(startDir) {
|
|
282
|
-
let current =
|
|
283
|
-
const root =
|
|
746
|
+
let current = path4.resolve(startDir);
|
|
747
|
+
const root = path4.parse(current).root;
|
|
284
748
|
while (true) {
|
|
285
|
-
const gitPath =
|
|
286
|
-
const m1Path =
|
|
749
|
+
const gitPath = path4.join(current, ".git");
|
|
750
|
+
const m1Path = path4.join(current, "memoraone.m1");
|
|
287
751
|
if (await pathExists(gitPath) || await pathExists(m1Path)) {
|
|
288
752
|
return current;
|
|
289
753
|
}
|
|
290
754
|
if (current === root) {
|
|
291
755
|
return null;
|
|
292
756
|
}
|
|
293
|
-
current =
|
|
757
|
+
current = path4.dirname(current);
|
|
294
758
|
}
|
|
295
759
|
}
|
|
296
760
|
function stripLeadingLineComments(text) {
|
|
@@ -306,9 +770,12 @@ This repository uses **MemoraOne** via the MCP server named **user-memoraone** (
|
|
|
306
770
|
### Tools
|
|
307
771
|
|
|
308
772
|
- Before answering questions about **prior decisions**, **remembered facts**, **identity or personal recall**, **preferences**, **repo history**, or **what to do next**, call **\`memora_ask_with_memory\`** so replies stay aligned with MemoraOne memory.
|
|
309
|
-
-
|
|
773
|
+
- Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. \`cursor\`), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
|
|
774
|
+
- Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
|
|
775
|
+
|
|
776
|
+
Do not spam events for trivial edits, formatting-only changes, or temporary WIP. Prefer concise, fact-promotable statements.
|
|
310
777
|
|
|
311
|
-
|
|
778
|
+
Example \`content.body\`: "MemoraOne Studio added a live dashboard summary, replacing the static dashboard placeholder with authenticated project and memory activity data."
|
|
312
779
|
`;
|
|
313
780
|
}
|
|
314
781
|
function copilotAndJetBrainsBody(title) {
|
|
@@ -321,9 +788,12 @@ This repo is set up to use **MemoraOne** through MCP where your editor exposes i
|
|
|
321
788
|
## Behavior
|
|
322
789
|
|
|
323
790
|
- For questions about **earlier decisions**, **stored facts**, **personal or identity recall**, **preferences**, **project history**, or **recommended next steps**, use MemoraOne memory tools (e.g. **\`memora_ask_with_memory\`**) when available before answering.
|
|
324
|
-
-
|
|
791
|
+
- Use **\`memora_post_event\`** for durable project decisions, wiring, migrations, fixes, and meaningful product behavior changes. Prefer kind \`note\`, \`content.title\`, \`content.body\` (one concise, fact-promotable statement), and metadata \`source\` (e.g. your agent name), \`purpose\`: \`dev-log\`, \`schema\`: \`v1\`.
|
|
792
|
+
- Use **\`memora_log_change_summary\`** for concise code or feature deltas after implementation.
|
|
325
793
|
|
|
326
|
-
|
|
794
|
+
Do not spam events for trivial edits, formatting-only changes, or temporary WIP. Prefer concise, fact-promotable statements.
|
|
795
|
+
|
|
796
|
+
Example \`content.body\`: "MemoraOne Studio added a live dashboard summary, replacing the static dashboard placeholder with authenticated project and memory activity data."
|
|
327
797
|
`;
|
|
328
798
|
}
|
|
329
799
|
function mcpJsonHeader() {
|
|
@@ -338,42 +808,42 @@ function buildMcpJsonBody(existing) {
|
|
|
338
808
|
return mcpJsonHeader() + JSON.stringify(merged, null, 2) + "\n";
|
|
339
809
|
}
|
|
340
810
|
async function writeManagedMarkdown(repoRoot, relPath, fullContent, opts) {
|
|
341
|
-
const abs =
|
|
811
|
+
const abs = path4.join(repoRoot, relPath);
|
|
342
812
|
assertUnderRepoRoot(repoRoot, abs);
|
|
343
813
|
let prior = "";
|
|
344
814
|
let existed = false;
|
|
345
815
|
try {
|
|
346
|
-
prior = await
|
|
816
|
+
prior = await fs4.readFile(abs, "utf8");
|
|
347
817
|
existed = true;
|
|
348
818
|
} catch (err) {
|
|
349
819
|
if (err?.code !== "ENOENT") throw err;
|
|
350
820
|
}
|
|
351
821
|
if (!existed) {
|
|
352
822
|
if (opts.dryRun) return "created";
|
|
353
|
-
await
|
|
354
|
-
await
|
|
823
|
+
await fs4.mkdir(path4.dirname(abs), { recursive: true });
|
|
824
|
+
await fs4.writeFile(abs, fullContent, "utf8");
|
|
355
825
|
return "created";
|
|
356
826
|
}
|
|
357
827
|
if (prior.includes(MANAGED_MARKER)) {
|
|
358
828
|
if (prior === fullContent) return "skipped";
|
|
359
829
|
if (opts.dryRun) return "updated";
|
|
360
|
-
await
|
|
361
|
-
await
|
|
830
|
+
await fs4.mkdir(path4.dirname(abs), { recursive: true });
|
|
831
|
+
await fs4.writeFile(abs, fullContent, "utf8");
|
|
362
832
|
return "updated";
|
|
363
833
|
}
|
|
364
834
|
if (!opts.force) return "skipped-untracked";
|
|
365
835
|
if (opts.dryRun) return "updated";
|
|
366
|
-
await
|
|
367
|
-
await
|
|
836
|
+
await fs4.mkdir(path4.dirname(abs), { recursive: true });
|
|
837
|
+
await fs4.writeFile(abs, fullContent, "utf8");
|
|
368
838
|
return "updated";
|
|
369
839
|
}
|
|
370
840
|
async function writeMcpJson(repoRoot, opts) {
|
|
371
|
-
const abs =
|
|
841
|
+
const abs = path4.join(repoRoot, ".vscode/mcp.json");
|
|
372
842
|
assertUnderRepoRoot(repoRoot, abs);
|
|
373
843
|
let raw = "";
|
|
374
844
|
let existed = false;
|
|
375
845
|
try {
|
|
376
|
-
raw = await
|
|
846
|
+
raw = await fs4.readFile(abs, "utf8");
|
|
377
847
|
existed = true;
|
|
378
848
|
} catch (err) {
|
|
379
849
|
if (err?.code !== "ENOENT") throw err;
|
|
@@ -381,8 +851,8 @@ async function writeMcpJson(repoRoot, opts) {
|
|
|
381
851
|
if (!existed) {
|
|
382
852
|
const body = buildMcpJsonBody(null);
|
|
383
853
|
if (opts.dryRun) return "created";
|
|
384
|
-
await
|
|
385
|
-
await
|
|
854
|
+
await fs4.mkdir(path4.dirname(abs), { recursive: true });
|
|
855
|
+
await fs4.writeFile(abs, body, "utf8");
|
|
386
856
|
return "created";
|
|
387
857
|
}
|
|
388
858
|
const managed = raw.includes(MANAGED_MARKER);
|
|
@@ -397,8 +867,8 @@ async function writeMcpJson(repoRoot, opts) {
|
|
|
397
867
|
const next = buildMcpJsonBody(parsed);
|
|
398
868
|
if (managed && next === raw) return "skipped";
|
|
399
869
|
if (opts.dryRun) return "updated";
|
|
400
|
-
await
|
|
401
|
-
await
|
|
870
|
+
await fs4.mkdir(path4.dirname(abs), { recursive: true });
|
|
871
|
+
await fs4.writeFile(abs, next, "utf8");
|
|
402
872
|
return "updated";
|
|
403
873
|
}
|
|
404
874
|
function parseSetupIdeFlags(argv) {
|
|
@@ -409,6 +879,7 @@ function parseSetupIdeFlags(argv) {
|
|
|
409
879
|
let force = false;
|
|
410
880
|
let dryRun = false;
|
|
411
881
|
let noGitignore = false;
|
|
882
|
+
let cleanup = false;
|
|
412
883
|
const unknown = [];
|
|
413
884
|
for (const a of argv) {
|
|
414
885
|
if (a === "--cursor") cursor = true;
|
|
@@ -418,6 +889,7 @@ function parseSetupIdeFlags(argv) {
|
|
|
418
889
|
else if (a === "--force") force = true;
|
|
419
890
|
else if (a === "--dry-run") dryRun = true;
|
|
420
891
|
else if (a === "--no-gitignore") noGitignore = true;
|
|
892
|
+
else if (a === "--cleanup") cleanup = true;
|
|
421
893
|
else if (a.startsWith("-")) unknown.push(a);
|
|
422
894
|
}
|
|
423
895
|
const specific = cursor || vscode || jetbrains;
|
|
@@ -427,7 +899,7 @@ function parseSetupIdeFlags(argv) {
|
|
|
427
899
|
} else {
|
|
428
900
|
targets = { cursor, vscode, jetbrains };
|
|
429
901
|
}
|
|
430
|
-
return { targets, force, dryRun, noGitignore, unknown };
|
|
902
|
+
return { targets, force, dryRun, noGitignore, cleanup, unknown };
|
|
431
903
|
}
|
|
432
904
|
function summarizeOutcomes(outcomes) {
|
|
433
905
|
const created = [];
|
|
@@ -500,7 +972,7 @@ description: MemoraOne MCP \u2014 IDE agent instructions
|
|
|
500
972
|
return { exitCode: 0, repoRoot, outcomes };
|
|
501
973
|
}
|
|
502
974
|
async function cliSetupIdeFiles(argv) {
|
|
503
|
-
const { targets, force, dryRun, noGitignore, unknown } = parseSetupIdeFlags(argv);
|
|
975
|
+
const { targets, force, dryRun, noGitignore, cleanup, unknown } = parseSetupIdeFlags(argv);
|
|
504
976
|
if (unknown.length) {
|
|
505
977
|
console.error(`[setup-ide-files] Unknown option(s): ${unknown.join(", ")}`);
|
|
506
978
|
return 1;
|
|
@@ -523,6 +995,26 @@ async function cliSetupIdeFiles(argv) {
|
|
|
523
995
|
if (dryRun) {
|
|
524
996
|
console.log("[setup-ide-files] Dry run: no files written.");
|
|
525
997
|
}
|
|
998
|
+
if (cleanup) {
|
|
999
|
+
console.log("[setup-ide-files] Running project-scoped cleanup...");
|
|
1000
|
+
const cleanupResult = await runCleanup({
|
|
1001
|
+
cwd: process.cwd(),
|
|
1002
|
+
dryRun,
|
|
1003
|
+
allProjects: false,
|
|
1004
|
+
assumeYes: true
|
|
1005
|
+
});
|
|
1006
|
+
if (cleanupResult.error) {
|
|
1007
|
+
console.error(`[setup-ide-files] cleanup failed: ${cleanupResult.error}`);
|
|
1008
|
+
return cleanupResult.exitCode;
|
|
1009
|
+
}
|
|
1010
|
+
} else {
|
|
1011
|
+
console.log(
|
|
1012
|
+
"[setup-ide-files] If Studio shows stale connections, run: npx -y @memoraone/mcp@latest cleanup --project-id <projectId>"
|
|
1013
|
+
);
|
|
1014
|
+
console.log(
|
|
1015
|
+
"[setup-ide-files] From this repo (uses memoraone.m1): npx -y @memoraone/mcp@latest cleanup"
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
526
1018
|
return 0;
|
|
527
1019
|
}
|
|
528
1020
|
|
|
@@ -535,11 +1027,17 @@ if (args.includes("--version") || args.includes("-v")) {
|
|
|
535
1027
|
}
|
|
536
1028
|
if (args.includes("--help") || args.includes("-h")) {
|
|
537
1029
|
console.log(
|
|
538
|
-
"Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid>]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run] [--no-gitignore]"
|
|
1030
|
+
"Usage: memoraone-mcp [--version] [--help] [--daemon --project-id <uuid> [--ide cursor|copilot-vscode|jetbrains]]\n memoraone-mcp setup-ide-files [--all|--cursor|--vscode|--jetbrains] [--force] [--dry-run] [--no-gitignore] [--cleanup]\n memoraone-mcp cleanup [--project-id <uuid>] [--ide cursor|copilot-vscode|jetbrains] [--dry-run] [--all-projects] [--yes]"
|
|
539
1031
|
);
|
|
540
1032
|
process.exit(0);
|
|
541
1033
|
}
|
|
542
|
-
if (args[0] === "
|
|
1034
|
+
if (args[0] === "cleanup") {
|
|
1035
|
+
cliCleanup(args.slice(1)).then((code) => process.exit(code)).catch((err) => {
|
|
1036
|
+
process.stderr.write(`[memoraone-mcp] cleanup fatal: ${String(err)}
|
|
1037
|
+
`);
|
|
1038
|
+
process.exit(1);
|
|
1039
|
+
});
|
|
1040
|
+
} else if (args[0] === "setup-ide-files") {
|
|
543
1041
|
cliSetupIdeFiles(args.slice(1)).then((code) => process.exit(code)).catch((err) => {
|
|
544
1042
|
process.stderr.write(`[memoraone-mcp] setup-ide-files fatal: ${String(err)}
|
|
545
1043
|
`);
|
|
@@ -556,8 +1054,8 @@ if (args[0] === "setup-ide-files") {
|
|
|
556
1054
|
const raw = process.env.WORKSPACE_FOLDER_PATHS;
|
|
557
1055
|
const parts = [];
|
|
558
1056
|
if (raw !== void 0 && raw.trim() !== "") {
|
|
559
|
-
for (const p of raw.split(
|
|
560
|
-
parts.push(
|
|
1057
|
+
for (const p of raw.split(path5.delimiter).map((s) => s.trim()).filter(Boolean)) {
|
|
1058
|
+
parts.push(path5.resolve(p));
|
|
561
1059
|
}
|
|
562
1060
|
}
|
|
563
1061
|
parts.push(process.cwd());
|
|
@@ -571,10 +1069,10 @@ if (args[0] === "setup-ide-files") {
|
|
|
571
1069
|
}
|
|
572
1070
|
return deduped;
|
|
573
1071
|
}, connectWithRetry = function(socketPath) {
|
|
574
|
-
return new Promise((
|
|
1072
|
+
return new Promise((resolve5, reject) => {
|
|
575
1073
|
const tryConnect = (attempt) => {
|
|
576
1074
|
const socket = net.connect(socketPath, () => {
|
|
577
|
-
|
|
1075
|
+
resolve5(socket);
|
|
578
1076
|
});
|
|
579
1077
|
socket.on("error", (err) => {
|
|
580
1078
|
if (attempt >= MAX_RETRIES) {
|
|
@@ -600,23 +1098,28 @@ if (args[0] === "setup-ide-files") {
|
|
|
600
1098
|
async function runBridge() {
|
|
601
1099
|
ensureBaseDir();
|
|
602
1100
|
const binding = await resolveBinding();
|
|
603
|
-
const socketPath =
|
|
1101
|
+
const socketPath = getDaemonSocketPath(binding.projectId);
|
|
1102
|
+
const environmentLog = binding.environment !== void 0 ? ` environment=${binding.environment}` : "";
|
|
604
1103
|
log(
|
|
605
|
-
`authoritative binding project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}`
|
|
1104
|
+
`authoritative binding project=${binding.projectId} workspace=${binding.workspaceRoot} m1=${binding.m1Path} source=${binding.bindingSource} apiKeySource=${binding.apiKeySource}${environmentLog}`
|
|
606
1105
|
);
|
|
607
1106
|
let socket;
|
|
608
1107
|
try {
|
|
609
1108
|
socket = await connectWithRetry(socketPath);
|
|
610
1109
|
} catch {
|
|
611
1110
|
log("daemon not running, spawning...");
|
|
612
|
-
const child = (0,
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1111
|
+
const child = (0, import_node_child_process2.spawn)(
|
|
1112
|
+
process.execPath,
|
|
1113
|
+
buildDaemonSpawnArgs(process.argv[1], binding.projectId),
|
|
1114
|
+
{
|
|
1115
|
+
detached: true,
|
|
1116
|
+
stdio: "ignore",
|
|
1117
|
+
env: {
|
|
1118
|
+
...process.env,
|
|
1119
|
+
MEMORAONE_DAEMON_BINDING_B64: encodeResolvedBinding(binding)
|
|
1120
|
+
}
|
|
618
1121
|
}
|
|
619
|
-
|
|
1122
|
+
);
|
|
620
1123
|
child.unref();
|
|
621
1124
|
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
622
1125
|
socket = await connectWithRetry(socketPath);
|