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.
- package/dist/index.js +524 -163
- package/package.json +1 -1
- package/templates/shared/_omp/commands/memory.md +6 -0
- package/templates/shared/{skills → _omp/skills}/memory-extraction/SKILL.md +92 -51
- package/templates/shared/_omp/skills/memory-extraction/scripts/validate.sh +94 -0
- package/templates/shared/_omp/skills/memory-extraction/templates/decision.yaml.tmpl +32 -0
- package/templates/shared/_omp/skills/memory-extraction/templates/session.yaml.tmpl +35 -0
- package/templates/shared/_omp/workflows/memory/steps/clean.md +92 -0
- package/templates/shared/_omp/workflows/memory/steps/decay.md +82 -0
- package/templates/shared/_omp/workflows/memory/steps/graph.md +88 -0
- package/templates/shared/_omp/workflows/memory/steps/search.md +68 -0
- package/templates/shared/_omp/workflows/memory/steps/status.md +63 -0
- package/templates/shared/_omp/workflows/memory/steps/store.md +81 -0
- package/templates/shared/_omp/workflows/memory/steps/sync.md +76 -0
- package/templates/shared/_omp/workflows/memory/workflow.md +147 -0
- package/templates/augment/AGENTS.md +0 -78
- package/templates/claude/CLAUDE.md +0 -52
- package/templates/common/AGENTS.md +0 -51
- package/templates/cursor/.cursorrules +0 -57
- package/templates/gemini/gemini.md +0 -77
- package/templates/shared/commands/memory.md +0 -86
- package/templates/shared/memory/activeContext.md +0 -34
- package/templates/shared/memory/productContext.md +0 -30
- package/templates/shared/memory/progress.md +0 -41
- package/templates/shared/memory/projectbrief.md +0 -36
- package/templates/shared/memory/systemPatterns.md +0 -39
- package/templates/shared/memory/techContext.md +0 -51
- package/templates/shared/memory-actions/clean.md +0 -54
- package/templates/shared/memory-actions/decay.md +0 -64
- package/templates/shared/memory-actions/graph.md +0 -75
- package/templates/shared/memory-actions/search.md +0 -38
- package/templates/shared/memory-actions/status.md +0 -35
- package/templates/shared/memory-actions/store.md +0 -45
- package/templates/shared/memory-actions/sync.md +0 -50
- 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 {
|
|
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,
|
|
158
|
+
async function getVersion(cmd, versionArgs) {
|
|
29
159
|
try {
|
|
30
|
-
const { stdout } = await
|
|
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
|
|
44
|
-
|
|
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
|
|
57
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
|
70
|
-
if (
|
|
71
|
-
return { name: "Qdrant", installed: true, running:
|
|
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
|
|
81
|
-
|
|
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
|
|
89
|
-
|
|
90
|
-
|
|
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: {
|
|
117
|
-
claude: {
|
|
118
|
-
cursor: {
|
|
119
|
-
gemini: {
|
|
120
|
-
common: {
|
|
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
|
|
133
|
-
|
|
134
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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\
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
|
446
|
+
const __dirname2 = dirname(fileURLToPath(import.meta.url));
|
|
192
447
|
const possiblePaths = [
|
|
193
|
-
join(
|
|
194
|
-
join(
|
|
195
|
-
join(
|
|
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
|
-
|
|
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))
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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: "
|
|
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
|
-
|
|
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(
|
|
290
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
console.log(chalk.green(` \u2713 \u521B\u5EFA ${
|
|
402
|
-
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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(
|
|
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();
|