openmemory-plus 1.2.3 → 1.3.0

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/index.js CHANGED
@@ -2,22 +2,152 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
+ import { readFileSync as readFileSync2 } from "fs";
6
+ import { dirname as dirname2, join as join2 } from "path";
7
+ import { fileURLToPath as fileURLToPath2 } from "url";
5
8
 
6
9
  // src/commands/install.ts
7
10
  import chalk from "chalk";
8
11
  import ora from "ora";
9
12
  import inquirer from "inquirer";
10
- import { exec as exec2 } from "child_process";
11
- import { promisify as promisify2 } from "util";
13
+ import { spawn as spawn2 } from "child_process";
12
14
  import { existsSync, mkdirSync, copyFileSync, writeFileSync, readdirSync, readFileSync } from "fs";
13
15
  import { join, dirname } from "path";
14
16
  import { fileURLToPath } from "url";
15
17
 
16
18
  // src/lib/detector.ts
17
- import { exec } from "child_process";
18
- import { promisify } from "util";
19
+ import { exec as exec2 } from "child_process";
20
+ import { promisify as promisify2 } from "util";
19
21
  import which from "which";
22
+
23
+ // src/lib/platform.ts
24
+ import { exec, spawn } from "child_process";
25
+ import { promisify } from "util";
20
26
  var execAsync = promisify(exec);
27
+ function getPlatform() {
28
+ const p = process.platform;
29
+ if (p === "darwin" || p === "linux" || p === "win32") {
30
+ return p;
31
+ }
32
+ return "unknown";
33
+ }
34
+ function isTTY() {
35
+ return process.stdout.isTTY === true && process.stdin.isTTY === true;
36
+ }
37
+ function isCI() {
38
+ return !!(process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.JENKINS_URL || process.env.TRAVIS);
39
+ }
40
+ function getOllamaInstallCommand() {
41
+ const platform = getPlatform();
42
+ switch (platform) {
43
+ case "darwin":
44
+ return { command: "brew", args: ["install", "ollama"] };
45
+ case "linux":
46
+ return {
47
+ command: "sh",
48
+ args: ["-c", "curl -fsSL https://ollama.com/install.sh | sh"],
49
+ manual: "curl -fsSL https://ollama.com/install.sh | sh"
50
+ };
51
+ case "win32":
52
+ return {
53
+ command: "winget",
54
+ args: ["install", "Ollama.Ollama"],
55
+ manual: "https://ollama.com/download/windows"
56
+ };
57
+ default:
58
+ return { command: "", args: [], manual: "https://ollama.com/download" };
59
+ }
60
+ }
61
+ function getOpenUrlCommand() {
62
+ const platform = getPlatform();
63
+ switch (platform) {
64
+ case "darwin":
65
+ return "open";
66
+ case "linux":
67
+ return "xdg-open";
68
+ case "win32":
69
+ return "start";
70
+ default:
71
+ return "open";
72
+ }
73
+ }
74
+ async function safeExec(command, args, options) {
75
+ return new Promise((resolve, reject) => {
76
+ const proc = spawn(command, args, {
77
+ ...options,
78
+ shell: false
79
+ });
80
+ let stdout = "";
81
+ let stderr = "";
82
+ const timeoutMs = options?.timeout ?? 6e4;
83
+ const timer = setTimeout(() => {
84
+ proc.kill("SIGTERM");
85
+ reject(new Error(`Command timed out after ${timeoutMs}ms`));
86
+ }, timeoutMs);
87
+ proc.stdout?.on("data", (data) => {
88
+ stdout += data.toString();
89
+ });
90
+ proc.stderr?.on("data", (data) => {
91
+ stderr += data.toString();
92
+ });
93
+ proc.on("close", (code) => {
94
+ clearTimeout(timer);
95
+ resolve({ stdout, stderr, code: code ?? 0 });
96
+ });
97
+ proc.on("error", (err) => {
98
+ clearTimeout(timer);
99
+ reject(err);
100
+ });
101
+ });
102
+ }
103
+ async function waitForService(url, maxAttempts = 30, intervalMs = 1e3) {
104
+ for (let i = 0; i < maxAttempts; i++) {
105
+ try {
106
+ const controller = new AbortController();
107
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
108
+ const response = await fetch(url, { signal: controller.signal });
109
+ clearTimeout(timeoutId);
110
+ if (response.ok || response.status < 500) {
111
+ return true;
112
+ }
113
+ } catch {
114
+ }
115
+ await new Promise((r) => setTimeout(r, intervalMs));
116
+ }
117
+ return false;
118
+ }
119
+ async function isPortInUse(port) {
120
+ const platform = getPlatform();
121
+ if (platform === "win32") {
122
+ try {
123
+ const { code, stdout } = await safeExec("netstat", ["-ano"], { timeout: 5e3 });
124
+ if (code === 0 && stdout.includes(`:${port}`)) {
125
+ return true;
126
+ }
127
+ } catch {
128
+ }
129
+ } else {
130
+ try {
131
+ const { code } = await safeExec("lsof", ["-i", `:${port}`, "-t"], { timeout: 5e3 });
132
+ if (code === 0) {
133
+ return true;
134
+ }
135
+ } catch {
136
+ }
137
+ }
138
+ try {
139
+ const controller = new AbortController();
140
+ const timeoutId = setTimeout(() => controller.abort(), 2e3);
141
+ await fetch(`http://localhost:${port}`, { signal: controller.signal });
142
+ clearTimeout(timeoutId);
143
+ return true;
144
+ } catch {
145
+ return false;
146
+ }
147
+ }
148
+
149
+ // src/lib/detector.ts
150
+ var execAsync2 = promisify2(exec2);
21
151
  async function checkCommand(cmd) {
22
152
  try {
23
153
  return await which(cmd);
@@ -25,9 +155,10 @@ async function checkCommand(cmd) {
25
155
  return null;
26
156
  }
27
157
  }
28
- async function getVersion(cmd, args = "--version") {
158
+ async function getVersion(cmd, versionArgs) {
29
159
  try {
30
- const { stdout } = await execAsync(`${cmd} ${args}`);
160
+ const { stdout, code } = await safeExec(cmd, versionArgs);
161
+ if (code !== 0) return null;
31
162
  return stdout.trim().split("\n")[0];
32
163
  } catch {
33
164
  return null;
@@ -38,10 +169,13 @@ async function checkDocker() {
38
169
  if (!path) {
39
170
  return { name: "Docker", installed: false, error: "\u672A\u5B89\u88C5" };
40
171
  }
41
- const version = await getVersion("docker", "--version");
172
+ const version = await getVersion("docker", ["--version"]);
42
173
  try {
43
- await execAsync("docker info");
44
- return { name: "Docker", installed: true, version: version || void 0, running: true };
174
+ const { code } = await safeExec("docker", ["info"]);
175
+ if (code === 0) {
176
+ return { name: "Docker", installed: true, version: version || void 0, running: true };
177
+ }
178
+ return { name: "Docker", installed: true, version: version || void 0, running: false, error: "Docker \u5B88\u62A4\u8FDB\u7A0B\u672A\u8FD0\u884C" };
45
179
  } catch {
46
180
  return { name: "Docker", installed: true, version: version || void 0, running: false, error: "Docker \u5B88\u62A4\u8FDB\u7A0B\u672A\u8FD0\u884C" };
47
181
  }
@@ -51,43 +185,58 @@ async function checkOllama() {
51
185
  if (!path) {
52
186
  return { name: "Ollama", installed: false, error: "\u672A\u5B89\u88C5" };
53
187
  }
54
- const version = await getVersion("ollama", "--version");
188
+ const version = await getVersion("ollama", ["--version"]);
55
189
  try {
56
- await execAsync("curl -s http://localhost:11434/api/tags");
57
- return { name: "Ollama", installed: true, version: version || void 0, running: true };
190
+ const response = await fetch("http://localhost:11434/api/tags");
191
+ if (response.ok) {
192
+ return { name: "Ollama", installed: true, version: version || void 0, running: true };
193
+ }
194
+ return { name: "Ollama", installed: true, version: version || void 0, running: false, error: "Ollama \u670D\u52A1\u672A\u8FD0\u884C" };
58
195
  } catch {
59
196
  return { name: "Ollama", installed: true, version: version || void 0, running: false, error: "Ollama \u670D\u52A1\u672A\u8FD0\u884C" };
60
197
  }
61
198
  }
62
199
  async function checkQdrant() {
63
- try {
64
- const { stdout } = await execAsync("curl -s http://localhost:6333/collections");
65
- const data = JSON.parse(stdout);
66
- return { name: "Qdrant", installed: true, running: true, version: "container" };
67
- } catch {
200
+ const portInUse = await isPortInUse(6333);
201
+ if (portInUse) {
68
202
  try {
69
- const { stdout } = await execAsync('docker ps -a --filter name=qdrant --format "{{.Status}}"');
70
- if (stdout.trim()) {
71
- return { name: "Qdrant", installed: true, running: false, error: "\u5BB9\u5668\u5B58\u5728\u4F46\u672A\u8FD0\u884C" };
203
+ const response = await fetch("http://localhost:6333/collections");
204
+ if (response.ok) {
205
+ return { name: "Qdrant", installed: true, running: true, version: "container" };
72
206
  }
73
207
  } catch {
208
+ return { name: "Qdrant", installed: false, running: false, error: "\u7AEF\u53E3 6333 \u88AB\u5176\u4ED6\u670D\u52A1\u5360\u7528" };
74
209
  }
75
- return { name: "Qdrant", installed: false, error: "\u5BB9\u5668\u672A\u521B\u5EFA" };
76
210
  }
211
+ try {
212
+ const { stdout, code } = await safeExec("docker", ["ps", "-a", "--filter", "name=^qdrant$", "--format", "{{.Status}}"]);
213
+ if (code === 0 && stdout.trim()) {
214
+ return { name: "Qdrant", installed: true, running: false, error: "\u5BB9\u5668\u5B58\u5728\u4F46\u672A\u8FD0\u884C" };
215
+ }
216
+ } catch {
217
+ }
218
+ return { name: "Qdrant", installed: false, error: "\u5BB9\u5668\u672A\u521B\u5EFA" };
77
219
  }
78
220
  async function checkOpenMemory() {
79
221
  try {
80
- await execAsync("curl -s http://localhost:8765/health || curl -s http://localhost:8765");
81
- return { name: "OpenMemory MCP", installed: true, running: true };
222
+ const response = await fetch("http://localhost:8765/health");
223
+ if (response.ok) {
224
+ return { name: "OpenMemory MCP", installed: true, running: true };
225
+ }
82
226
  } catch {
83
- return { name: "OpenMemory MCP", installed: false, running: false, error: "\u670D\u52A1\u672A\u8FD0\u884C" };
84
227
  }
228
+ return { name: "OpenMemory MCP", installed: false, running: false, error: "\u6309\u9700\u542F\u52A8 (\u53EF\u9009)" };
85
229
  }
86
230
  async function checkBgeM3() {
87
231
  try {
88
- const { stdout } = await execAsync("curl -s http://localhost:11434/api/tags");
89
- const data = JSON.parse(stdout);
90
- const hasModel = data.models?.some((m) => m.name.includes("bge-m3"));
232
+ const response = await fetch("http://localhost:11434/api/tags");
233
+ if (!response.ok) {
234
+ return { name: "BGE-M3", installed: false, error: "Ollama \u672A\u8FD0\u884C\uFF0C\u65E0\u6CD5\u68C0\u6D4B" };
235
+ }
236
+ const data = await response.json();
237
+ const hasModel = data.models?.some(
238
+ (m) => m.name === "bge-m3" || m.name === "bge-m3:latest" || m.name.startsWith("bge-m3:")
239
+ );
91
240
  if (hasModel) {
92
241
  return { name: "BGE-M3", installed: true, running: true };
93
242
  }
@@ -111,13 +260,12 @@ function isSystemReady(status) {
111
260
  }
112
261
 
113
262
  // src/commands/install.ts
114
- var execAsync2 = promisify2(exec2);
115
263
  var IDE_CONFIGS = {
116
- augment: { dir: ".augment", configFile: "AGENTS.md", commandsDir: "commands", skillsDir: "skills" },
117
- claude: { dir: ".", configFile: "CLAUDE.md", commandsDir: ".claude/commands", skillsDir: ".claude/skills" },
118
- cursor: { dir: ".cursor", configFile: ".cursorrules", commandsDir: "commands", skillsDir: "skills" },
119
- gemini: { dir: ".", configFile: "gemini.md", commandsDir: ".gemini/commands", skillsDir: ".gemini/skills" },
120
- common: { dir: ".", configFile: "AGENTS.md", commandsDir: ".agents/commands", skillsDir: ".agents/skills" }
264
+ augment: { commandsDir: ".augment/commands", skillsDir: ".augment/skills" },
265
+ claude: { commandsDir: ".claude/commands", skillsDir: ".claude/skills" },
266
+ cursor: { commandsDir: ".cursor/commands", skillsDir: ".cursor/skills" },
267
+ gemini: { commandsDir: ".gemini/commands", skillsDir: ".gemini/skills" },
268
+ common: { commandsDir: ".agents/commands", skillsDir: ".agents/skills" }
121
269
  };
122
270
  var BANNER = `
123
271
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
@@ -129,82 +277,193 @@ var BANNER = `
129
277
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
130
278
  `;
131
279
  async function openUrl(url) {
132
- const { platform } = process;
133
- const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
134
- await execAsync2(`${cmd} ${url}`);
280
+ const cmd = getOpenUrlCommand();
281
+ try {
282
+ await safeExec(cmd, [url]);
283
+ } catch {
284
+ console.log(chalk.gray(` \u8BF7\u624B\u52A8\u6253\u5F00: ${url}`));
285
+ }
135
286
  }
136
287
  async function installOllama() {
137
- const spinner = ora("\u5B89\u88C5 Ollama...").start();
288
+ const platform = getPlatform();
289
+ const installCmd = getOllamaInstallCommand();
290
+ if (!installCmd.command) {
291
+ console.log(chalk.yellow(` \u4E0D\u652F\u6301\u7684\u5E73\u53F0: ${platform}`));
292
+ console.log(chalk.yellow(` \u8BF7\u624B\u52A8\u5B89\u88C5: ${installCmd.manual}`));
293
+ return false;
294
+ }
295
+ const spinner = ora(`\u5B89\u88C5 Ollama (${platform})...`).start();
138
296
  try {
139
- await execAsync2("brew install ollama");
297
+ const { code, stderr } = await safeExec(installCmd.command, installCmd.args);
298
+ if (code !== 0) {
299
+ throw new Error(stderr || "Installation failed");
300
+ }
140
301
  spinner.succeed("Ollama \u5B89\u88C5\u6210\u529F");
141
302
  return true;
142
- } catch {
303
+ } catch (e) {
143
304
  spinner.fail("Ollama \u5B89\u88C5\u5931\u8D25");
144
- console.log(chalk.yellow(" \u8BF7\u624B\u52A8\u5B89\u88C5: https://ollama.com/download"));
305
+ console.log(chalk.yellow(` \u8BF7\u624B\u52A8\u5B89\u88C5: ${installCmd.manual || "https://ollama.com/download"}`));
306
+ if (e.message) {
307
+ console.log(chalk.gray(` \u9519\u8BEF: ${e.message}`));
308
+ }
145
309
  return false;
146
310
  }
147
311
  }
148
312
  async function startOllama() {
149
313
  const spinner = ora("\u542F\u52A8 Ollama \u670D\u52A1...").start();
150
314
  try {
151
- exec2("ollama serve &");
152
- await new Promise((r) => setTimeout(r, 2e3));
153
- spinner.succeed("Ollama \u670D\u52A1\u5DF2\u542F\u52A8");
154
- return true;
155
- } catch {
315
+ const proc = spawn2("ollama", ["serve"], {
316
+ detached: true,
317
+ stdio: "ignore"
318
+ });
319
+ proc.unref();
320
+ spinner.text = "\u7B49\u5F85 Ollama \u670D\u52A1\u5C31\u7EEA...";
321
+ const ready = await waitForService("http://localhost:11434/api/tags", 30, 1e3);
322
+ if (ready) {
323
+ spinner.succeed("Ollama \u670D\u52A1\u5DF2\u542F\u52A8");
324
+ return true;
325
+ } else {
326
+ spinner.fail("Ollama \u542F\u52A8\u8D85\u65F6");
327
+ console.log(chalk.yellow(" \u8BF7\u624B\u52A8\u8FD0\u884C: ollama serve"));
328
+ return false;
329
+ }
330
+ } catch (e) {
156
331
  spinner.fail("Ollama \u542F\u52A8\u5931\u8D25");
332
+ console.log(chalk.gray(` \u9519\u8BEF: ${e.message || "\u672A\u77E5\u9519\u8BEF"}`));
157
333
  return false;
158
334
  }
159
335
  }
160
336
  async function pullBgeM3() {
161
- const spinner = ora("\u4E0B\u8F7D BGE-M3 \u6A21\u578B (\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F)...").start();
162
- try {
163
- await execAsync2("ollama pull bge-m3", { timeout: 6e5 });
164
- spinner.succeed("BGE-M3 \u6A21\u578B\u5DF2\u4E0B\u8F7D");
165
- return true;
166
- } catch {
167
- spinner.fail("BGE-M3 \u4E0B\u8F7D\u5931\u8D25");
168
- return false;
169
- }
337
+ const spinner = ora("\u4E0B\u8F7D BGE-M3 \u6A21\u578B (\u7EA6 1.2GB\uFF0C\u53EF\u80FD\u9700\u8981 5-10 \u5206\u949F)...").start();
338
+ return new Promise((resolve) => {
339
+ const proc = spawn2("ollama", ["pull", "bge-m3"], {
340
+ stdio: ["ignore", "pipe", "pipe"]
341
+ });
342
+ let lastProgress = "";
343
+ proc.stdout?.on("data", (data) => {
344
+ const line = data.toString().trim();
345
+ if (line && line !== lastProgress) {
346
+ lastProgress = line;
347
+ spinner.text = `\u4E0B\u8F7D BGE-M3: ${line}`;
348
+ }
349
+ });
350
+ proc.stderr?.on("data", (data) => {
351
+ const line = data.toString().trim();
352
+ if (line) {
353
+ spinner.text = `\u4E0B\u8F7D BGE-M3: ${line}`;
354
+ }
355
+ });
356
+ let killed = false;
357
+ const timeout = setTimeout(() => {
358
+ killed = true;
359
+ proc.kill("SIGTERM");
360
+ setTimeout(() => {
361
+ if (!proc.killed) {
362
+ proc.kill("SIGKILL");
363
+ }
364
+ }, 5e3);
365
+ spinner.fail("BGE-M3 \u4E0B\u8F7D\u8D85\u65F6 (30\u5206\u949F)");
366
+ console.log(chalk.yellow(" \u8BF7\u624B\u52A8\u8FD0\u884C: ollama pull bge-m3"));
367
+ resolve(false);
368
+ }, 30 * 60 * 1e3);
369
+ proc.on("close", (code) => {
370
+ clearTimeout(timeout);
371
+ if (code === 0) {
372
+ spinner.succeed("BGE-M3 \u6A21\u578B\u5DF2\u4E0B\u8F7D");
373
+ resolve(true);
374
+ } else {
375
+ spinner.fail("BGE-M3 \u4E0B\u8F7D\u5931\u8D25");
376
+ console.log(chalk.yellow(" \u8BF7\u624B\u52A8\u8FD0\u884C: ollama pull bge-m3"));
377
+ resolve(false);
378
+ }
379
+ });
380
+ proc.on("error", (err) => {
381
+ clearTimeout(timeout);
382
+ spinner.fail("BGE-M3 \u4E0B\u8F7D\u5931\u8D25");
383
+ console.log(chalk.gray(` \u9519\u8BEF: ${err.message}`));
384
+ resolve(false);
385
+ });
386
+ });
170
387
  }
171
388
  async function startQdrant() {
172
389
  const spinner = ora("\u542F\u52A8 Qdrant \u5BB9\u5668...").start();
390
+ const portInUse = await isPortInUse(6333);
391
+ if (portInUse) {
392
+ try {
393
+ const response = await fetch("http://localhost:6333/collections");
394
+ if (response.ok) {
395
+ spinner.succeed("Qdrant \u5DF2\u5728\u8FD0\u884C");
396
+ return true;
397
+ }
398
+ } catch {
399
+ }
400
+ spinner.fail("\u7AEF\u53E3 6333 \u5DF2\u88AB\u5176\u4ED6\u670D\u52A1\u5360\u7528");
401
+ console.log(chalk.yellow(" \u8BF7\u91CA\u653E\u7AEF\u53E3 6333 \u6216\u4F7F\u7528\u5176\u4ED6\u7AEF\u53E3"));
402
+ return false;
403
+ }
173
404
  try {
174
- await execAsync2("docker run -d --name qdrant -p 6333:6333 -p 6334:6334 qdrant/qdrant");
175
- spinner.succeed("Qdrant \u5BB9\u5668\u5DF2\u542F\u52A8");
176
- return true;
177
- } catch (e) {
178
- if (e.message?.includes("already in use") || e.message?.includes("Conflict")) {
179
- try {
180
- await execAsync2("docker start qdrant");
405
+ const { code } = await safeExec("docker", ["start", "qdrant"]);
406
+ if (code === 0) {
407
+ const ready = await waitForService("http://localhost:6333/collections", 30, 1e3);
408
+ if (ready) {
181
409
  spinner.succeed("Qdrant \u5BB9\u5668\u5DF2\u542F\u52A8");
182
410
  return true;
183
- } catch {
184
411
  }
185
412
  }
413
+ } catch {
414
+ }
415
+ try {
416
+ const { code, stderr } = await safeExec("docker", [
417
+ "run",
418
+ "-d",
419
+ "--name",
420
+ "qdrant",
421
+ "-p",
422
+ "6333:6333",
423
+ "-p",
424
+ "6334:6334",
425
+ "qdrant/qdrant"
426
+ ]);
427
+ if (code !== 0) {
428
+ throw new Error(stderr || "Failed to create container");
429
+ }
430
+ const ready = await waitForService("http://localhost:6333/collections", 30, 1e3);
431
+ if (ready) {
432
+ spinner.succeed("Qdrant \u5BB9\u5668\u5DF2\u542F\u52A8");
433
+ return true;
434
+ } else {
435
+ spinner.fail("Qdrant \u542F\u52A8\u8D85\u65F6");
436
+ return false;
437
+ }
438
+ } catch (e) {
186
439
  spinner.fail("Qdrant \u542F\u52A8\u5931\u8D25");
440
+ console.log(chalk.gray(` \u9519\u8BEF: ${e.message || "\u672A\u77E5\u9519\u8BEF"}`));
441
+ console.log(chalk.yellow(" \u8BF7\u786E\u4FDD Docker \u6B63\u5728\u8FD0\u884C"));
187
442
  return false;
188
443
  }
189
444
  }
190
445
  function getTemplatesDir() {
191
- const __dirname = dirname(fileURLToPath(import.meta.url));
446
+ const __dirname2 = dirname(fileURLToPath(import.meta.url));
192
447
  const possiblePaths = [
193
- join(__dirname, "..", "templates"),
194
- join(__dirname, "..", "..", "templates"),
195
- join(__dirname, "..", "..", "..", "templates")
448
+ join(__dirname2, "..", "templates"),
449
+ join(__dirname2, "..", "..", "templates"),
450
+ join(__dirname2, "..", "..", "..", "templates")
196
451
  ];
197
452
  for (const p of possiblePaths) {
198
453
  if (existsSync(join(p, "shared"))) {
199
454
  return p;
200
455
  }
201
456
  }
202
- return possiblePaths[0];
457
+ throw new Error(
458
+ `\u6A21\u677F\u76EE\u5F55\u672A\u627E\u5230\u3002\u5DF2\u68C0\u67E5\u8DEF\u5F84:
459
+ ${possiblePaths.map((p) => ` - ${p}`).join("\n")}
460
+ \u8BF7\u786E\u4FDD openmemory-plus \u5305\u5B89\u88C5\u5B8C\u6574\u3002`
461
+ );
203
462
  }
204
- function copyDir(src, dest) {
463
+ function copyDir(src, dest, errors = []) {
205
464
  if (!existsSync(src)) {
206
- console.warn(chalk.yellow(` \u26A0 \u6E90\u76EE\u5F55\u4E0D\u5B58\u5728: ${src}`));
207
- return;
465
+ errors.push(`\u6E90\u76EE\u5F55\u4E0D\u5B58\u5728: ${src}`);
466
+ return errors;
208
467
  }
209
468
  mkdirSync(dest, { recursive: true });
210
469
  for (const file of readdirSync(src, { withFileTypes: true })) {
@@ -212,14 +471,15 @@ function copyDir(src, dest) {
212
471
  const destPath = join(dest, file.name);
213
472
  try {
214
473
  if (file.isDirectory()) {
215
- copyDir(srcPath, destPath);
474
+ copyDir(srcPath, destPath, errors);
216
475
  } else {
217
476
  copyFileSync(srcPath, destPath);
218
477
  }
219
478
  } catch (err) {
220
- console.warn(chalk.yellow(` \u26A0 \u590D\u5236\u5931\u8D25: ${srcPath}`));
479
+ errors.push(`\u590D\u5236\u5931\u8D25 ${srcPath}: ${err.message || "\u672A\u77E5\u9519\u8BEF"}`);
221
480
  }
222
481
  }
482
+ return errors;
223
483
  }
224
484
  function generateProjectYaml(projectName) {
225
485
  return `# OpenMemory Plus Project Configuration
@@ -261,14 +521,16 @@ function processTemplate(content, projectName) {
261
521
  }
262
522
  function showMcpConfig(ide) {
263
523
  console.log(chalk.bold("\n\u{1F4CB} MCP \u914D\u7F6E (\u590D\u5236\u5230 IDE \u914D\u7F6E\u6587\u4EF6):"));
524
+ console.log(chalk.gray("\n\u{1F4A1} \u4F7F\u7528\u672C\u5730 Ollama + BGE-M3\uFF0C\u65E0\u9700 OpenAI API Key\n"));
264
525
  const mcpConfig = {
265
526
  openmemory: {
266
527
  command: "npx",
267
528
  args: ["-y", "openmemory-mcp"],
268
529
  env: {
269
- OPENAI_API_KEY: "your-openai-key-or-use-ollama",
530
+ // Fix Issue #7: Remove misleading OPENAI_API_KEY
270
531
  MEM0_EMBEDDING_MODEL: "bge-m3",
271
532
  MEM0_EMBEDDING_PROVIDER: "ollama",
533
+ OLLAMA_HOST: "http://localhost:11434",
272
534
  QDRANT_HOST: "localhost",
273
535
  QDRANT_PORT: "6333"
274
536
  }
@@ -284,17 +546,26 @@ function showMcpConfig(ide) {
284
546
  gemini: "~/.config/gemini/mcp.json",
285
547
  common: "\u53C2\u8003\u5404 IDE \u7684 MCP \u914D\u7F6E\u6587\u6863"
286
548
  };
287
- console.log(chalk.gray(`\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E: ${configPaths[ide] || configPaths.common}
288
- `));
549
+ console.log(chalk.gray(`\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E: ${configPaths[ide] || configPaths.common}`));
550
+ console.log(chalk.gray("\n\u{1F4D6} \u8BE6\u7EC6\u914D\u7F6E\u8BF4\u660E: https://github.com/mem0ai/mem0/tree/main/openmemory\n"));
289
551
  }
290
552
  async function phase1_checkAndInstallDeps(options) {
291
553
  console.log(chalk.bold.cyan("\n\u2501\u2501\u2501 \u7B2C 1 \u6B65: \u68C0\u6D4B\u7CFB\u7EDF\u4F9D\u8D56 \u2501\u2501\u2501\n"));
554
+ const inCI = isCI();
555
+ const hasTTY = isTTY();
556
+ if (inCI) {
557
+ console.log(chalk.gray("\u68C0\u6D4B\u5230 CI/CD \u73AF\u5883\uFF0C\u4F7F\u7528\u975E\u4EA4\u4E92\u6A21\u5F0F\n"));
558
+ }
292
559
  const spinner = ora("\u68C0\u6D4B\u7CFB\u7EDF\u72B6\u6001...").start();
293
560
  const status = await checkAllDependencies();
294
561
  spinner.stop();
295
562
  console.log(chalk.bold("\u5F53\u524D\u72B6\u6001:"));
296
- console.log(` \u{1F433} Docker: ${status.docker.installed ? status.docker.running ? chalk.green("\u2713 \u8FD0\u884C\u4E2D") : chalk.yellow("\u26A0 \u5DF2\u5B89\u88C5\u672A\u8FD0\u884C") : chalk.red("\u2717 \u672A\u5B89\u88C5")}`);
297
- console.log(` \u{1F999} Ollama: ${status.ollama.installed ? status.ollama.running ? chalk.green("\u2713 \u8FD0\u884C\u4E2D") : chalk.yellow("\u26A0 \u5DF2\u5B89\u88C5\u672A\u8FD0\u884C") : chalk.red("\u2717 \u672A\u5B89\u88C5")}`);
563
+ console.log(
564
+ ` \u{1F433} Docker: ${status.docker.installed ? status.docker.running ? chalk.green("\u2713 \u8FD0\u884C\u4E2D") : chalk.yellow("\u26A0 \u5DF2\u5B89\u88C5\u672A\u8FD0\u884C") : chalk.red("\u2717 \u672A\u5B89\u88C5")}`
565
+ );
566
+ console.log(
567
+ ` \u{1F999} Ollama: ${status.ollama.installed ? status.ollama.running ? chalk.green("\u2713 \u8FD0\u884C\u4E2D") : chalk.yellow("\u26A0 \u5DF2\u5B89\u88C5\u672A\u8FD0\u884C") : chalk.red("\u2717 \u672A\u5B89\u88C5")}`
568
+ );
298
569
  console.log(` \u{1F4E6} Qdrant: ${status.qdrant.running ? chalk.green("\u2713 \u8FD0\u884C\u4E2D") : chalk.red("\u2717 \u672A\u8FD0\u884C")}`);
299
570
  console.log(` \u{1F524} BGE-M3: ${status.bgeM3.installed ? chalk.green("\u2713 \u5DF2\u5B89\u88C5") : chalk.red("\u2717 \u672A\u5B89\u88C5")}`);
300
571
  console.log("");
@@ -306,13 +577,20 @@ async function phase1_checkAndInstallDeps(options) {
306
577
  console.log(chalk.yellow("\u26A0\uFE0F \u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5 (--skip-deps)\n"));
307
578
  return true;
308
579
  }
580
+ if (inCI || !hasTTY) {
581
+ console.log(chalk.yellow("\u26A0\uFE0F \u975E\u4EA4\u4E92\u73AF\u5883\uFF0C\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5"));
582
+ console.log(chalk.gray(" \u8BF7\u5728\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u4E2D\u8FD0\u884C\uFF0C\u6216\u4F7F\u7528 --skip-deps \u8DF3\u8FC7\n"));
583
+ return true;
584
+ }
309
585
  if (!options.yes) {
310
- const { confirm } = await inquirer.prompt([{
311
- type: "confirm",
312
- name: "confirm",
313
- message: "\u9700\u8981\u5B89\u88C5/\u542F\u52A8\u7F3A\u5931\u7684\u4F9D\u8D56\uFF0C\u662F\u5426\u7EE7\u7EED?",
314
- default: true
315
- }]);
586
+ const { confirm } = await inquirer.prompt([
587
+ {
588
+ type: "confirm",
589
+ name: "confirm",
590
+ message: "\u9700\u8981\u5B89\u88C5/\u542F\u52A8\u7F3A\u5931\u7684\u4F9D\u8D56\uFF0C\u662F\u5426\u7EE7\u7EED?",
591
+ default: true
592
+ }
593
+ ]);
316
594
  if (!confirm) {
317
595
  console.log(chalk.yellow("\n\u5DF2\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5\uFF0C\u7EE7\u7EED\u9879\u76EE\u914D\u7F6E...\n"));
318
596
  return true;
@@ -321,13 +599,15 @@ async function phase1_checkAndInstallDeps(options) {
321
599
  if (!status.docker.installed) {
322
600
  console.log(chalk.yellow("\n\u{1F4E6} Docker \u9700\u8981\u624B\u52A8\u5B89\u88C5"));
323
601
  console.log(chalk.gray(" \u8BF7\u8BBF\u95EE https://docker.com/download \u4E0B\u8F7D\u5B89\u88C5"));
324
- if (!options.yes) {
325
- const { openDocker } = await inquirer.prompt([{
326
- type: "confirm",
327
- name: "openDocker",
328
- message: "\u662F\u5426\u6253\u5F00 Docker \u4E0B\u8F7D\u9875\u9762?",
329
- default: true
330
- }]);
602
+ if (!options.yes && hasTTY) {
603
+ const { openDocker } = await inquirer.prompt([
604
+ {
605
+ type: "confirm",
606
+ name: "openDocker",
607
+ message: "\u662F\u5426\u6253\u5F00 Docker \u4E0B\u8F7D\u9875\u9762?",
608
+ default: true
609
+ }
610
+ ]);
331
611
  if (openDocker) await openUrl("https://docker.com/download");
332
612
  await inquirer.prompt([{ type: "input", name: "wait", message: "\u5B89\u88C5\u5B8C\u6210\u540E\u6309 Enter \u7EE7\u7EED..." }]);
333
613
  }
@@ -349,43 +629,102 @@ async function phase1_checkAndInstallDeps(options) {
349
629
  }
350
630
  async function phase2_initProject(options) {
351
631
  console.log(chalk.bold.cyan("\n\u2501\u2501\u2501 \u7B2C 2 \u6B65: \u914D\u7F6E\u9879\u76EE \u2501\u2501\u2501\n"));
352
- let ide = options.ide?.toLowerCase();
353
- if (!ide || !IDE_CONFIGS[ide]) {
354
- const { selectedIde } = await inquirer.prompt([{
355
- type: "list",
356
- name: "selectedIde",
357
- message: "\u9009\u62E9 IDE \u7C7B\u578B:",
358
- choices: [
359
- { name: "Augment", value: "augment" },
360
- { name: "Claude Code", value: "claude" },
361
- { name: "Cursor", value: "cursor" },
362
- { name: "Gemini", value: "gemini" },
363
- { name: "\u901A\u7528 (AGENTS.md)", value: "common" }
364
- ],
365
- default: "augment"
366
- }]);
367
- ide = selectedIde;
368
- }
369
632
  const cwd = process.cwd();
633
+ const ompDir = join(cwd, "_omp");
634
+ let shouldForce = options.force ?? false;
635
+ if (existsSync(ompDir) && !shouldForce) {
636
+ console.log(chalk.yellow("\u26A0\uFE0F \u68C0\u6D4B\u5230\u5DF2\u5B58\u5728\u7684 _omp/ \u76EE\u5F55"));
637
+ if (!isTTY() || isCI()) {
638
+ console.log(chalk.gray(" \u4F7F\u7528 --force \u5F3A\u5236\u8986\u76D6\uFF0C\u6216\u624B\u52A8\u5220\u9664 _omp/ \u76EE\u5F55"));
639
+ console.log(chalk.yellow("\n\u8DF3\u8FC7\u9879\u76EE\u914D\u7F6E\uFF0C\u4FDD\u7559\u73B0\u6709\u914D\u7F6E\n"));
640
+ return options.ide?.toLowerCase() || "augment";
641
+ }
642
+ if (!options.yes) {
643
+ const { action } = await inquirer.prompt([
644
+ {
645
+ type: "list",
646
+ name: "action",
647
+ message: "\u5982\u4F55\u5904\u7406\u73B0\u6709\u914D\u7F6E?",
648
+ choices: [
649
+ { name: "\u4FDD\u7559\u73B0\u6709\u914D\u7F6E (\u8DF3\u8FC7)", value: "skip" },
650
+ { name: "\u8986\u76D6\u73B0\u6709\u914D\u7F6E", value: "overwrite" },
651
+ { name: "\u4EC5\u66F4\u65B0 commands \u548C skills", value: "update" }
652
+ ],
653
+ default: "skip"
654
+ }
655
+ ]);
656
+ if (action === "skip") {
657
+ console.log(chalk.yellow("\n\u4FDD\u7559\u73B0\u6709\u914D\u7F6E\n"));
658
+ return options.ide?.toLowerCase() || "augment";
659
+ }
660
+ if (action === "update") {
661
+ shouldForce = false;
662
+ } else {
663
+ shouldForce = true;
664
+ }
665
+ }
666
+ }
667
+ let selectedIdes = [];
668
+ if (options.ide) {
669
+ const requestedIdes = options.ide.toLowerCase().split(",").map((s) => s.trim()).filter((s) => s.length > 0);
670
+ const validIdes = requestedIdes.filter((s) => IDE_CONFIGS[s]);
671
+ const invalidIdes = requestedIdes.filter((s) => !IDE_CONFIGS[s]);
672
+ if (invalidIdes.length > 0) {
673
+ console.log(chalk.yellow(` \u26A0 \u672A\u77E5\u7684 IDE: ${invalidIdes.join(", ")}`));
674
+ console.log(chalk.gray(` \u6709\u6548\u9009\u9879: ${Object.keys(IDE_CONFIGS).join(", ")}`));
675
+ }
676
+ selectedIdes = validIdes;
677
+ }
678
+ if (selectedIdes.length === 0) {
679
+ if (!isTTY() || isCI()) {
680
+ selectedIdes = ["augment"];
681
+ console.log(chalk.gray(` \u4F7F\u7528\u9ED8\u8BA4 IDE: augment`));
682
+ } else {
683
+ const { ides } = await inquirer.prompt([
684
+ {
685
+ type: "checkbox",
686
+ name: "ides",
687
+ message: "\u9009\u62E9 IDE \u7C7B\u578B (\u7A7A\u683C\u9009\u62E9\uFF0C\u56DE\u8F66\u786E\u8BA4):",
688
+ choices: [
689
+ { name: "Augment", value: "augment", checked: true },
690
+ { name: "Claude Code", value: "claude" },
691
+ { name: "Cursor", value: "cursor" },
692
+ { name: "Gemini", value: "gemini" },
693
+ { name: "\u901A\u7528 (AGENTS.md)", value: "common" }
694
+ ]
695
+ }
696
+ ]);
697
+ selectedIdes = ides.length > 0 ? ides : ["augment"];
698
+ }
699
+ }
370
700
  const defaultName = cwd.split("/").pop() || "my-project";
371
701
  let projectName = defaultName;
372
- if (!options.yes) {
373
- const { name } = await inquirer.prompt([{
374
- type: "input",
375
- name: "name",
376
- message: "\u9879\u76EE\u540D\u79F0:",
377
- default: defaultName
378
- }]);
702
+ if (!options.yes && isTTY() && !isCI()) {
703
+ const { name } = await inquirer.prompt([
704
+ {
705
+ type: "input",
706
+ name: "name",
707
+ message: "\u9879\u76EE\u540D\u79F0:",
708
+ default: defaultName
709
+ }
710
+ ]);
379
711
  projectName = name;
380
712
  }
381
- const config = IDE_CONFIGS[ide];
382
713
  console.log(chalk.bold("\n\u{1F4C1} \u521B\u5EFA\u914D\u7F6E\u6587\u4EF6...\n"));
383
- const templatesDir = getTemplatesDir();
714
+ let templatesDir;
715
+ try {
716
+ templatesDir = getTemplatesDir();
717
+ } catch (e) {
718
+ console.error(chalk.red("\u274C " + e.message));
719
+ process.exit(1);
720
+ }
384
721
  const ompTemplates = join(templatesDir, "shared", "_omp");
385
- const ideTemplates = join(templatesDir, ide === "common" ? "common" : ide);
386
- const ompDir = join(cwd, "_omp");
387
722
  mkdirSync(ompDir, { recursive: true });
388
- copyDir(ompTemplates, ompDir);
723
+ const copyErrors = copyDir(ompTemplates, ompDir);
724
+ if (copyErrors.length > 0) {
725
+ console.log(chalk.yellow(" \u26A0 \u90E8\u5206\u6587\u4EF6\u590D\u5236\u5931\u8D25:"));
726
+ copyErrors.forEach((err) => console.log(chalk.gray(` - ${err}`)));
727
+ }
389
728
  console.log(chalk.green(" \u2713 \u521B\u5EFA _omp/ (\u6838\u5FC3\u76EE\u5F55)"));
390
729
  const ompMemoryDir = join(ompDir, ".memory");
391
730
  mkdirSync(ompMemoryDir, { recursive: true });
@@ -393,31 +732,38 @@ async function phase2_initProject(options) {
393
732
  const memoryFiles = readdirSync(ompMemoryDir);
394
733
  for (const file of memoryFiles) {
395
734
  const filePath = join(ompMemoryDir, file);
396
- const content = readFileSync(filePath, "utf-8");
397
- writeFileSync(filePath, processTemplate(content, projectName));
735
+ try {
736
+ const content = readFileSync(filePath, "utf-8");
737
+ writeFileSync(filePath, processTemplate(content, projectName));
738
+ } catch (e) {
739
+ console.log(chalk.yellow(` \u26A0 \u5904\u7406\u6A21\u677F\u5931\u8D25: ${file} - ${e.message}`));
740
+ }
398
741
  }
399
742
  }
400
743
  const projectYaml = join(ompMemoryDir, "project.yaml");
401
744
  writeFileSync(projectYaml, generateProjectYaml(projectName));
402
745
  console.log(chalk.green(" \u2713 \u521B\u5EFA _omp/.memory/project.yaml"));
403
746
  const commandsCount = existsSync(join(ompDir, "commands")) ? readdirSync(join(ompDir, "commands")).filter((f) => f.endsWith(".md")).length : 0;
404
- const actionsCount = existsSync(join(ompDir, "commands", "memory-actions")) ? readdirSync(join(ompDir, "commands", "memory-actions")).length : 0;
405
- console.log(chalk.green(` \u2713 \u521B\u5EFA _omp/commands/ (${commandsCount} \u547D\u4EE4, ${actionsCount} \u5B50\u52A8\u4F5C)`));
747
+ const workflowStepsCount = existsSync(join(ompDir, "workflows", "memory", "steps")) ? readdirSync(join(ompDir, "workflows", "memory", "steps")).length : 0;
748
+ console.log(chalk.green(` \u2713 \u521B\u5EFA _omp/commands/ (${commandsCount} \u547D\u4EE4)`));
749
+ console.log(chalk.green(` \u2713 \u521B\u5EFA _omp/workflows/ (${workflowStepsCount} \u6B65\u9AA4)`));
406
750
  console.log(chalk.green(" \u2713 \u521B\u5EFA _omp/skills/ (memory-extraction)"));
407
- const ideDir = join(cwd, config.dir);
408
- const ideCommandsDir = join(cwd, config.dir, config.commandsDir);
409
- mkdirSync(ideCommandsDir, { recursive: true });
410
- copyDir(join(ompDir, "commands"), ideCommandsDir);
411
- console.log(chalk.green(` \u2713 \u590D\u5236\u5230 ${config.dir}/${config.commandsDir}/`));
412
- const ideSkillsDir = join(cwd, config.dir, config.skillsDir);
413
- mkdirSync(ideSkillsDir, { recursive: true });
414
- copyDir(join(ompDir, "skills"), ideSkillsDir);
415
- console.log(chalk.green(` \u2713 \u590D\u5236\u5230 ${config.dir}/${config.skillsDir}/`));
416
- if (existsSync(ideTemplates)) {
417
- copyDir(ideTemplates, ideDir);
418
- console.log(chalk.green(` \u2713 \u590D\u5236 ${config.configFile}`));
419
- }
420
- return ide;
751
+ for (const ide of selectedIdes) {
752
+ const config = IDE_CONFIGS[ide];
753
+ if (!config) continue;
754
+ const ideCommandsDir = join(cwd, config.commandsDir);
755
+ mkdirSync(ideCommandsDir, { recursive: true });
756
+ copyDir(join(ompDir, "commands"), ideCommandsDir);
757
+ const ideSkillsDir = join(cwd, config.skillsDir);
758
+ mkdirSync(ideSkillsDir, { recursive: true });
759
+ copyDir(join(ompDir, "skills"), ideSkillsDir);
760
+ console.log(chalk.green(` \u2713 \u914D\u7F6E ${ide} (${config.commandsDir}/)`));
761
+ }
762
+ if (selectedIdes.length > 1) {
763
+ console.log(chalk.green(`
764
+ \u2713 \u5DF2\u4E3A ${selectedIdes.length} \u4E2A IDE \u914D\u7F6E\u5B8C\u6210: ${selectedIdes.join(", ")}`));
765
+ }
766
+ return selectedIdes[0];
421
767
  }
422
768
  function phase3_showCompletion(ide, showMcp) {
423
769
  console.log(chalk.bold.cyan("\n\u2501\u2501\u2501 \u5B89\u88C5\u5B8C\u6210 \u2501\u2501\u2501\n"));
@@ -631,9 +977,25 @@ async function doctorCommand(options) {
631
977
  }
632
978
 
633
979
  // src/index.ts
980
+ var __dirname = dirname2(fileURLToPath2(import.meta.url));
981
+ function getVersion2() {
982
+ const possiblePaths = [
983
+ join2(__dirname, "..", "package.json"),
984
+ join2(__dirname, "..", "..", "package.json")
985
+ ];
986
+ for (const p of possiblePaths) {
987
+ try {
988
+ const pkg = JSON.parse(readFileSync2(p, "utf-8"));
989
+ return pkg.version || "0.0.0";
990
+ } catch {
991
+ continue;
992
+ }
993
+ }
994
+ return "0.0.0";
995
+ }
634
996
  var program = new Command();
635
- program.name("openmemory-plus").description("\u{1F9E0} Agent Memory Management - \u8BA9\u4EFB\u4F55 AI Agent \u83B7\u5F97\u6301\u4E45\u8BB0\u5FC6\u80FD\u529B").version("1.0.0");
636
- program.command("install", { isDefault: true }).description("\u4E00\u952E\u5B89\u88C5\u548C\u914D\u7F6E OpenMemory Plus (\u63A8\u8350)").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4\u63D0\u793A").option("-i, --ide <type>", "IDE \u7C7B\u578B: augment, claude, cursor, gemini, common").option("--skip-deps", "\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5\uFF0C\u4EC5\u914D\u7F6E\u9879\u76EE").option("--show-mcp", "\u663E\u793A MCP \u914D\u7F6E").action(installCommand);
997
+ program.name("openmemory-plus").description("\u{1F9E0} Agent Memory Management - \u8BA9\u4EFB\u4F55 AI Agent \u83B7\u5F97\u6301\u4E45\u8BB0\u5FC6\u80FD\u529B").version(getVersion2());
998
+ program.command("install", { isDefault: true }).description("\u4E00\u952E\u5B89\u88C5\u548C\u914D\u7F6E OpenMemory Plus (\u63A8\u8350)").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4\u63D0\u793A").option("-i, --ide <type>", "IDE \u7C7B\u578B: augment, claude, cursor, gemini, common").option("--skip-deps", "\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5\uFF0C\u4EC5\u914D\u7F6E\u9879\u76EE").option("--show-mcp", "\u663E\u793A MCP \u914D\u7F6E").option("-f, --force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u914D\u7F6E\u6587\u4EF6").action(installCommand);
637
999
  program.command("status").description("\u68C0\u67E5\u7CFB\u7EDF\u72B6\u6001").action(statusCommand);
638
1000
  program.command("doctor").description("\u8BCA\u65AD\u5E76\u4FEE\u590D\u95EE\u9898").option("--fix", "\u81EA\u52A8\u4FEE\u590D\u95EE\u9898").action(doctorCommand);
639
1001
  program.parse();