openmemory-plus 1.2.0 → 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.
Files changed (35) hide show
  1. package/dist/index.js +524 -163
  2. package/package.json +1 -1
  3. package/templates/shared/_omp/commands/memory.md +6 -0
  4. package/templates/shared/{skills → _omp/skills}/memory-extraction/SKILL.md +92 -51
  5. package/templates/shared/_omp/skills/memory-extraction/scripts/validate.sh +94 -0
  6. package/templates/shared/_omp/skills/memory-extraction/templates/decision.yaml.tmpl +32 -0
  7. package/templates/shared/_omp/skills/memory-extraction/templates/session.yaml.tmpl +35 -0
  8. package/templates/shared/_omp/workflows/memory/steps/clean.md +92 -0
  9. package/templates/shared/_omp/workflows/memory/steps/decay.md +82 -0
  10. package/templates/shared/_omp/workflows/memory/steps/graph.md +88 -0
  11. package/templates/shared/_omp/workflows/memory/steps/search.md +68 -0
  12. package/templates/shared/_omp/workflows/memory/steps/status.md +63 -0
  13. package/templates/shared/_omp/workflows/memory/steps/store.md +81 -0
  14. package/templates/shared/_omp/workflows/memory/steps/sync.md +76 -0
  15. package/templates/shared/_omp/workflows/memory/workflow.md +147 -0
  16. package/templates/augment/AGENTS.md +0 -78
  17. package/templates/claude/CLAUDE.md +0 -52
  18. package/templates/common/AGENTS.md +0 -51
  19. package/templates/cursor/.cursorrules +0 -57
  20. package/templates/gemini/gemini.md +0 -77
  21. package/templates/shared/commands/memory.md +0 -86
  22. package/templates/shared/memory/activeContext.md +0 -34
  23. package/templates/shared/memory/productContext.md +0 -30
  24. package/templates/shared/memory/progress.md +0 -41
  25. package/templates/shared/memory/projectbrief.md +0 -36
  26. package/templates/shared/memory/systemPatterns.md +0 -39
  27. package/templates/shared/memory/techContext.md +0 -51
  28. package/templates/shared/memory-actions/clean.md +0 -54
  29. package/templates/shared/memory-actions/decay.md +0 -64
  30. package/templates/shared/memory-actions/graph.md +0 -75
  31. package/templates/shared/memory-actions/search.md +0 -38
  32. package/templates/shared/memory-actions/status.md +0 -35
  33. package/templates/shared/memory-actions/store.md +0 -45
  34. package/templates/shared/memory-actions/sync.md +0 -50
  35. package/templates/shared/rules/classification.md +0 -108
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
  }
@@ -107,17 +256,16 @@ async function checkAllDependencies() {
107
256
  return { docker, ollama, qdrant, openmemory, bgeM3 };
108
257
  }
109
258
  function isSystemReady(status) {
110
- return status.docker.installed && status.docker.running && status.ollama.installed && status.ollama.running && status.qdrant.running && status.bgeM3.installed;
259
+ return status.docker.installed === true && status.docker.running === true && status.ollama.installed === true && status.ollama.running === true && status.qdrant.running === true && status.bgeM3.installed === true;
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", rulesDir: ".rules/memory" },
117
- claude: { dir: ".", configFile: "CLAUDE.md", commandsDir: ".claude/commands", skillsDir: ".claude/skills", rulesDir: ".rules/memory" },
118
- cursor: { dir: ".cursor", configFile: ".cursorrules", commandsDir: "commands", skillsDir: "skills", rulesDir: ".rules/memory" },
119
- gemini: { dir: ".", configFile: "gemini.md", commandsDir: ".gemini/commands", skillsDir: ".gemini/skills", rulesDir: ".rules/memory" },
120
- common: { dir: ".", configFile: "AGENTS.md", commandsDir: ".agents/commands", skillsDir: ".agents/skills", rulesDir: ".rules/memory" }
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,90 +277,209 @@ 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) {
205
- if (!existsSync(src)) return;
463
+ function copyDir(src, dest, errors = []) {
464
+ if (!existsSync(src)) {
465
+ errors.push(`\u6E90\u76EE\u5F55\u4E0D\u5B58\u5728: ${src}`);
466
+ return errors;
467
+ }
206
468
  mkdirSync(dest, { recursive: true });
207
469
  for (const file of readdirSync(src, { withFileTypes: true })) {
208
470
  const srcPath = join(src, file.name);
209
471
  const destPath = join(dest, file.name);
210
- if (file.isDirectory()) {
211
- copyDir(srcPath, destPath);
212
- } else {
213
- copyFileSync(srcPath, destPath);
472
+ try {
473
+ if (file.isDirectory()) {
474
+ copyDir(srcPath, destPath, errors);
475
+ } else {
476
+ copyFileSync(srcPath, destPath);
477
+ }
478
+ } catch (err) {
479
+ errors.push(`\u590D\u5236\u5931\u8D25 ${srcPath}: ${err.message || "\u672A\u77E5\u9519\u8BEF"}`);
214
480
  }
215
481
  }
482
+ return errors;
216
483
  }
217
484
  function generateProjectYaml(projectName) {
218
485
  return `# OpenMemory Plus Project Configuration
@@ -224,7 +491,7 @@ project:
224
491
  description: ""
225
492
 
226
493
  memory:
227
- project_store: ".memory/"
494
+ project_store: "_omp/.memory/"
228
495
  user_store: "openmemory"
229
496
 
230
497
  classification:
@@ -254,14 +521,16 @@ function processTemplate(content, projectName) {
254
521
  }
255
522
  function showMcpConfig(ide) {
256
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"));
257
525
  const mcpConfig = {
258
526
  openmemory: {
259
527
  command: "npx",
260
528
  args: ["-y", "openmemory-mcp"],
261
529
  env: {
262
- OPENAI_API_KEY: "your-openai-key-or-use-ollama",
530
+ // Fix Issue #7: Remove misleading OPENAI_API_KEY
263
531
  MEM0_EMBEDDING_MODEL: "bge-m3",
264
532
  MEM0_EMBEDDING_PROVIDER: "ollama",
533
+ OLLAMA_HOST: "http://localhost:11434",
265
534
  QDRANT_HOST: "localhost",
266
535
  QDRANT_PORT: "6333"
267
536
  }
@@ -277,17 +546,26 @@ function showMcpConfig(ide) {
277
546
  gemini: "~/.config/gemini/mcp.json",
278
547
  common: "\u53C2\u8003\u5404 IDE \u7684 MCP \u914D\u7F6E\u6587\u6863"
279
548
  };
280
- console.log(chalk.gray(`\u914D\u7F6E\u6587\u4EF6\u4F4D\u7F6E: ${configPaths[ide] || configPaths.common}
281
- `));
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"));
282
551
  }
283
552
  async function phase1_checkAndInstallDeps(options) {
284
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
+ }
285
559
  const spinner = ora("\u68C0\u6D4B\u7CFB\u7EDF\u72B6\u6001...").start();
286
560
  const status = await checkAllDependencies();
287
561
  spinner.stop();
288
562
  console.log(chalk.bold("\u5F53\u524D\u72B6\u6001:"));
289
- 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")}`);
290
- 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
+ );
291
569
  console.log(` \u{1F4E6} Qdrant: ${status.qdrant.running ? chalk.green("\u2713 \u8FD0\u884C\u4E2D") : chalk.red("\u2717 \u672A\u8FD0\u884C")}`);
292
570
  console.log(` \u{1F524} BGE-M3: ${status.bgeM3.installed ? chalk.green("\u2713 \u5DF2\u5B89\u88C5") : chalk.red("\u2717 \u672A\u5B89\u88C5")}`);
293
571
  console.log("");
@@ -299,13 +577,20 @@ async function phase1_checkAndInstallDeps(options) {
299
577
  console.log(chalk.yellow("\u26A0\uFE0F \u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5 (--skip-deps)\n"));
300
578
  return true;
301
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
+ }
302
585
  if (!options.yes) {
303
- const { confirm } = await inquirer.prompt([{
304
- type: "confirm",
305
- name: "confirm",
306
- message: "\u9700\u8981\u5B89\u88C5/\u542F\u52A8\u7F3A\u5931\u7684\u4F9D\u8D56\uFF0C\u662F\u5426\u7EE7\u7EED?",
307
- default: true
308
- }]);
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
+ ]);
309
594
  if (!confirm) {
310
595
  console.log(chalk.yellow("\n\u5DF2\u8DF3\u8FC7\u4F9D\u8D56\u5B89\u88C5\uFF0C\u7EE7\u7EED\u9879\u76EE\u914D\u7F6E...\n"));
311
596
  return true;
@@ -314,13 +599,15 @@ async function phase1_checkAndInstallDeps(options) {
314
599
  if (!status.docker.installed) {
315
600
  console.log(chalk.yellow("\n\u{1F4E6} Docker \u9700\u8981\u624B\u52A8\u5B89\u88C5"));
316
601
  console.log(chalk.gray(" \u8BF7\u8BBF\u95EE https://docker.com/download \u4E0B\u8F7D\u5B89\u88C5"));
317
- if (!options.yes) {
318
- const { openDocker } = await inquirer.prompt([{
319
- type: "confirm",
320
- name: "openDocker",
321
- message: "\u662F\u5426\u6253\u5F00 Docker \u4E0B\u8F7D\u9875\u9762?",
322
- default: true
323
- }]);
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
+ ]);
324
611
  if (openDocker) await openUrl("https://docker.com/download");
325
612
  await inquirer.prompt([{ type: "input", name: "wait", message: "\u5B89\u88C5\u5B8C\u6210\u540E\u6309 Enter \u7EE7\u7EED..." }]);
326
613
  }
@@ -342,83 +629,141 @@ async function phase1_checkAndInstallDeps(options) {
342
629
  }
343
630
  async function phase2_initProject(options) {
344
631
  console.log(chalk.bold.cyan("\n\u2501\u2501\u2501 \u7B2C 2 \u6B65: \u914D\u7F6E\u9879\u76EE \u2501\u2501\u2501\n"));
345
- let ide = options.ide?.toLowerCase();
346
- if (!ide || !IDE_CONFIGS[ide]) {
347
- const { selectedIde } = await inquirer.prompt([{
348
- type: "list",
349
- name: "selectedIde",
350
- message: "\u9009\u62E9 IDE \u7C7B\u578B:",
351
- choices: [
352
- { name: "Augment", value: "augment" },
353
- { name: "Claude Code", value: "claude" },
354
- { name: "Cursor", value: "cursor" },
355
- { name: "Gemini", value: "gemini" },
356
- { name: "\u901A\u7528 (AGENTS.md)", value: "common" }
357
- ],
358
- default: "augment"
359
- }]);
360
- ide = selectedIde;
361
- }
362
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
+ }
363
700
  const defaultName = cwd.split("/").pop() || "my-project";
364
701
  let projectName = defaultName;
365
- if (!options.yes) {
366
- const { name } = await inquirer.prompt([{
367
- type: "input",
368
- name: "name",
369
- message: "\u9879\u76EE\u540D\u79F0:",
370
- default: defaultName
371
- }]);
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
+ ]);
372
711
  projectName = name;
373
712
  }
374
- const config = IDE_CONFIGS[ide];
375
713
  console.log(chalk.bold("\n\u{1F4C1} \u521B\u5EFA\u914D\u7F6E\u6587\u4EF6...\n"));
376
- const memoryDir = join(cwd, ".memory");
377
- if (!existsSync(memoryDir)) {
378
- mkdirSync(memoryDir, { recursive: true });
714
+ let templatesDir;
715
+ try {
716
+ templatesDir = getTemplatesDir();
717
+ } catch (e) {
718
+ console.error(chalk.red("\u274C " + e.message));
719
+ process.exit(1);
379
720
  }
380
- console.log(chalk.green(" \u2713 \u521B\u5EFA .memory/"));
381
- const projectYaml = join(memoryDir, "project.yaml");
382
- writeFileSync(projectYaml, generateProjectYaml(projectName));
383
- console.log(chalk.green(" \u2713 \u521B\u5EFA .memory/project.yaml"));
384
- const templatesDir = getTemplatesDir();
385
- const memoryTemplatesDir = join(templatesDir, "shared", "memory");
386
- if (existsSync(memoryTemplatesDir)) {
387
- const memoryFiles = readdirSync(memoryTemplatesDir);
721
+ const ompTemplates = join(templatesDir, "shared", "_omp");
722
+ mkdirSync(ompDir, { recursive: true });
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
+ }
728
+ console.log(chalk.green(" \u2713 \u521B\u5EFA _omp/ (\u6838\u5FC3\u76EE\u5F55)"));
729
+ const ompMemoryDir = join(ompDir, ".memory");
730
+ mkdirSync(ompMemoryDir, { recursive: true });
731
+ if (existsSync(ompMemoryDir)) {
732
+ const memoryFiles = readdirSync(ompMemoryDir);
388
733
  for (const file of memoryFiles) {
389
- const srcPath = join(memoryTemplatesDir, file);
390
- const destPath = join(memoryDir, file);
391
- const content = readFileSync(srcPath, "utf-8");
392
- writeFileSync(destPath, processTemplate(content, projectName));
734
+ const filePath = join(ompMemoryDir, file);
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
+ }
393
741
  }
394
- console.log(chalk.green(` \u2713 \u521B\u5EFA .memory/ (${memoryFiles.length} \u4E2A\u6838\u5FC3\u6587\u4EF6)`));
395
- }
396
- const sharedTemplates = join(templatesDir, "shared");
397
- const ideTemplates = join(templatesDir, ide === "common" ? "common" : ide);
398
- const commandsDir = join(cwd, config.dir, config.commandsDir);
399
- mkdirSync(commandsDir, { recursive: true });
400
- copyDir(join(sharedTemplates, "commands"), commandsDir);
401
- console.log(chalk.green(` \u2713 \u521B\u5EFA ${config.dir}/${config.commandsDir}/ (memory.md)`));
402
- const commandsParentDir = dirname(join(cwd, config.dir, config.commandsDir));
403
- const memoryActionsDir = join(commandsParentDir, "memory-actions");
404
- mkdirSync(memoryActionsDir, { recursive: true });
405
- copyDir(join(sharedTemplates, "memory-actions"), memoryActionsDir);
406
- const actionsCount = existsSync(join(sharedTemplates, "memory-actions")) ? readdirSync(join(sharedTemplates, "memory-actions")).length : 0;
407
- const memoryActionsDisplayPath = memoryActionsDir.replace(cwd + "/", "");
408
- console.log(chalk.green(` \u2713 \u521B\u5EFA ${memoryActionsDisplayPath}/ (${actionsCount} \u4E2A\u5B50\u52A8\u4F5C)`));
409
- const skillsDir = join(cwd, config.dir, config.skillsDir);
410
- mkdirSync(skillsDir, { recursive: true });
411
- copyDir(join(sharedTemplates, "skills"), skillsDir);
412
- console.log(chalk.green(` \u2713 \u521B\u5EFA ${config.dir}/${config.skillsDir}/ (memory-extraction)`));
413
- const rulesDir = join(cwd, config.rulesDir);
414
- mkdirSync(rulesDir, { recursive: true });
415
- copyDir(join(sharedTemplates, "rules"), rulesDir);
416
- console.log(chalk.green(` \u2713 \u521B\u5EFA ${config.rulesDir}/ (classification.md)`));
417
- if (existsSync(ideTemplates)) {
418
- copyDir(ideTemplates, join(cwd, config.dir));
419
- console.log(chalk.green(` \u2713 \u590D\u5236 ${config.configFile}`));
420
- }
421
- return ide;
742
+ }
743
+ const projectYaml = join(ompMemoryDir, "project.yaml");
744
+ writeFileSync(projectYaml, generateProjectYaml(projectName));
745
+ console.log(chalk.green(" \u2713 \u521B\u5EFA _omp/.memory/project.yaml"));
746
+ const commandsCount = existsSync(join(ompDir, "commands")) ? readdirSync(join(ompDir, "commands")).filter((f) => f.endsWith(".md")).length : 0;
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)`));
750
+ console.log(chalk.green(" \u2713 \u521B\u5EFA _omp/skills/ (memory-extraction)"));
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];
422
767
  }
423
768
  function phase3_showCompletion(ide, showMcp) {
424
769
  console.log(chalk.bold.cyan("\n\u2501\u2501\u2501 \u5B89\u88C5\u5B8C\u6210 \u2501\u2501\u2501\n"));
@@ -632,9 +977,25 @@ async function doctorCommand(options) {
632
977
  }
633
978
 
634
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
+ }
635
996
  var program = new Command();
636
- 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");
637
- 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);
638
999
  program.command("status").description("\u68C0\u67E5\u7CFB\u7EDF\u72B6\u6001").action(statusCommand);
639
1000
  program.command("doctor").description("\u8BCA\u65AD\u5E76\u4FEE\u590D\u95EE\u9898").option("--fix", "\u81EA\u52A8\u4FEE\u590D\u95EE\u9898").action(doctorCommand);
640
1001
  program.parse();