open-memex 0.3.0-alpha.1 → 0.3.0-alpha.3
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/AGENTS.md +5 -3
- package/README.md +12 -4
- package/README.zh-CN.md +12 -4
- package/dist/cli.js +37 -22
- package/dist/init.js +50 -6
- package/docs/V2-DESIGN.md +10 -0
- package/package.json +1 -1
- package/src/cli.ts +37 -21
- package/src/init.ts +55 -6
package/AGENTS.md
CHANGED
|
@@ -37,9 +37,11 @@ node --experimental-strip-types scripts\smoke-mcp.ts # MCP handshake + tool r
|
|
|
37
37
|
After `npm i -g open-memex@alpha` (or `npm link` from source), the `open-memex` bin is on
|
|
38
38
|
PATH: `open-memex mcp` starts the MCP server, `open-memex mcp --print-config <client>`
|
|
39
39
|
prints a client config snippet (client: vscode|cursor|claude|opencode|visualstudio),
|
|
40
|
-
`open-memex init [--client vscode|cursor|opencode|visualstudio] [--force] [--yes]`
|
|
41
|
-
one-command project setup (editor MCP config +
|
|
42
|
-
|
|
40
|
+
`open-memex init [--client vscode|cursor|opencode|visualstudio] [--instructions personal|project] [--force] [--yes]`
|
|
41
|
+
one-command project setup (editor MCP config + Copilot memory instructions;
|
|
42
|
+
instructions default to user-level ~/.copilot/copilot-instructions.md so the repo
|
|
43
|
+
stays clean for teammates without open-memex — D22; resolves the server command
|
|
44
|
+
at init time — npx fallback when no durable bin is on PATH, D17),
|
|
43
45
|
`open-memex config` prints the effective config, `open-memex capture --dry-run "text"`
|
|
44
46
|
previews keyword capture without writing, `open-memex doctor` runs health checks
|
|
45
47
|
(node version, config, scope resolution, storage writability, MCP handshake).
|
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ open-memex init --client vscode
|
|
|
77
77
|
npx -y open-memex@alpha init --client vscode
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
Writes `.vscode/mcp.json` and
|
|
80
|
+
Writes `.vscode/mcp.json` and user-level Copilot instructions, then reload the
|
|
81
81
|
window and confirm the `open-memex` server is started in Copilot Chat's MCP panel.
|
|
82
82
|
|
|
83
83
|
**Cursor:**
|
|
@@ -86,7 +86,7 @@ window and confirm the `open-memex` server is started in Copilot Chat's MCP pane
|
|
|
86
86
|
open-memex init --client cursor
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
Writes `.cursor/mcp.json` and
|
|
89
|
+
Writes `.cursor/mcp.json` and user-level Copilot instructions.
|
|
90
90
|
|
|
91
91
|
**opencode** (as a plain MCP consumer):
|
|
92
92
|
|
|
@@ -112,7 +112,7 @@ claude mcp add open-memex -- open-memex mcp
|
|
|
112
112
|
open-memex init --client visualstudio
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
-
Writes solution-level `.mcp.json` and
|
|
115
|
+
Writes solution-level `.mcp.json` and user-level Copilot instructions. Requires
|
|
116
116
|
Visual Studio 2022 17.14+ or Visual Studio 2026 (**Windows-only**). Visual Studio
|
|
117
117
|
also auto-discovers `.vscode/mcp.json` and `.cursor/mcp.json`, so the VS Code setup
|
|
118
118
|
above works too.
|
|
@@ -123,6 +123,12 @@ above works too.
|
|
|
123
123
|
|
|
124
124
|
`init` notes:
|
|
125
125
|
|
|
126
|
+
- The Copilot memory instructions default to **user-level**
|
|
127
|
+
(`~/.copilot/copilot-instructions.md`; `%USERPROFILE%\copilot-instructions.md`
|
|
128
|
+
for Visual Studio) — they apply to all your projects and are never checked
|
|
129
|
+
into a repo, so teammates without open-memex see nothing and nothing breaks
|
|
130
|
+
for them. `--instructions project` writes `.github/copilot-instructions.md`
|
|
131
|
+
instead, for teams where everyone uses open-memex.
|
|
126
132
|
- On a terminal it interactively asks which editor to set up, whether to enable
|
|
127
133
|
keyword auto-capture, and whether to inject memories on the first turn.
|
|
128
134
|
`--yes` accepts the defaults; scripts / non-TTY never prompt (editor defaults to
|
|
@@ -237,6 +243,8 @@ open-memex config set <key> <value> # change a setting
|
|
|
237
243
|
open-memex doctor # environment health check
|
|
238
244
|
open-memex capture --dry-run "记住我喜欢简洁的回答" # preview keyword capture
|
|
239
245
|
open-memex mcp --print-config vscode|cursor|claude|opencode|visualstudio
|
|
246
|
+
open-memex --help # this reference
|
|
247
|
+
open-memex --version # installed version
|
|
240
248
|
```
|
|
241
249
|
|
|
242
250
|
Memory operations:
|
|
@@ -280,7 +288,7 @@ server with cwd set to your project root (`init` handles this for you).
|
|
|
280
288
|
|
|
281
289
|
> **Note:** MCP is request/response — it gives the agent tools, not the opencode
|
|
282
290
|
> plugin's automatic keyword capture or first-turn context injection. Proactive
|
|
283
|
-
> memory use depends on the agent's instructions (the
|
|
291
|
+
> memory use depends on the agent's instructions (the Copilot instructions
|
|
284
292
|
> that `init` writes).
|
|
285
293
|
|
|
286
294
|
## Roadmap
|
package/README.zh-CN.md
CHANGED
|
@@ -75,7 +75,7 @@ open-memex init --client vscode
|
|
|
75
75
|
npx -y open-memex@alpha init --client vscode
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
自动写 `.vscode/mcp.json`
|
|
78
|
+
自动写 `.vscode/mcp.json` 和用户级 Copilot instructions,然后重新加载窗口,
|
|
79
79
|
在 Copilot Chat 的 MCP 面板里确认 `open-memex` server 已启动。
|
|
80
80
|
|
|
81
81
|
**Cursor:**
|
|
@@ -84,7 +84,7 @@ npx -y open-memex@alpha init --client vscode
|
|
|
84
84
|
open-memex init --client cursor
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
自动写 `.cursor/mcp.json`
|
|
87
|
+
自动写 `.cursor/mcp.json` 和用户级 Copilot instructions。
|
|
88
88
|
|
|
89
89
|
**opencode**(作为普通 MCP 客户端):
|
|
90
90
|
|
|
@@ -110,7 +110,7 @@ claude mcp add open-memex -- open-memex mcp
|
|
|
110
110
|
open-memex init --client visualstudio
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
-
写 solution 级 `.mcp.json`
|
|
113
|
+
写 solution 级 `.mcp.json` 和用户级 Copilot instructions。需要
|
|
114
114
|
Visual Studio 2022 17.14+ 或 Visual Studio 2026(**仅 Windows**)。
|
|
115
115
|
Visual Studio 也会自动发现 `.vscode/mcp.json` 和 `.cursor/mcp.json`,
|
|
116
116
|
所以上面的 VS Code 配置同样可用。
|
|
@@ -120,6 +120,12 @@ Visual Studio 也会自动发现 `.vscode/mcp.json` 和 `.cursor/mcp.json`,
|
|
|
120
120
|
|
|
121
121
|
`init` 说明:
|
|
122
122
|
|
|
123
|
+
- Copilot 记忆 instructions 默认写到**用户级**
|
|
124
|
+
(`~/.copilot/copilot-instructions.md`;Visual Studio 是
|
|
125
|
+
`%USERPROFILE%\copilot-instructions.md`)——所有项目生效,永不 checkin
|
|
126
|
+
到 repo,没装 open-memex 的同事看不到、也不会出错。团队人人都用
|
|
127
|
+
open-memex 时可用 `--instructions project` 改写
|
|
128
|
+
`.github/copilot-instructions.md`。
|
|
123
129
|
- 在终端里会交互式询问:配哪个编辑器、是否开启关键词自动捕获、
|
|
124
130
|
是否在首轮注入记忆。`--yes` 全用默认值;脚本 / 非 TTY 环境不提问
|
|
125
131
|
(编辑器默认 VS Code)。
|
|
@@ -236,6 +242,8 @@ open-memex config set <key> <value> # 改设置
|
|
|
236
242
|
open-memex doctor # 环境健康检查
|
|
237
243
|
open-memex capture --dry-run "记住我喜欢简洁的回答" # 预览关键词捕获
|
|
238
244
|
open-memex mcp --print-config vscode|cursor|claude|opencode|visualstudio
|
|
245
|
+
open-memex --help # 本帮助
|
|
246
|
+
open-memex --version # 已安装版本
|
|
239
247
|
```
|
|
240
248
|
|
|
241
249
|
记忆操作:
|
|
@@ -280,7 +288,7 @@ project scope 从进程工作目录解析,所以配置 server 时 cwd 要指
|
|
|
280
288
|
|
|
281
289
|
> **注意:** MCP 是请求/响应式的——它给 agent 提供 tools,但没有 opencode
|
|
282
290
|
> 插件的关键词自动捕获和首轮上下文注入。想让 agent 主动用记忆,
|
|
283
|
-
> 靠的是 agent 的 instructions(`init` 写的
|
|
291
|
+
> 靠的是 agent 的 instructions(`init` 写的 Copilot instructions)。
|
|
284
292
|
|
|
285
293
|
## 路线图(Roadmap)
|
|
286
294
|
|
package/dist/cli.js
CHANGED
|
@@ -13,32 +13,38 @@ import { paths } from "./paths.js";
|
|
|
13
13
|
import { redact } from "./redact.js";
|
|
14
14
|
import { resolveMcpCommand } from "./init.js";
|
|
15
15
|
import fs from "node:fs";
|
|
16
|
-
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
function usage(exitCode = 1) {
|
|
17
19
|
console.log(`open-memex CLI
|
|
18
20
|
|
|
19
21
|
Usage:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
open-memex where
|
|
23
|
+
open-memex list [--scope project|personal] [--type T] [--limit N]
|
|
24
|
+
open-memex search "query" [--scope project|personal|both] [--type T] [--limit N]
|
|
25
|
+
open-memex add "content" [--scope project|personal] [--type T] [--tag t1,t2]
|
|
26
|
+
open-memex supersede <id> "new content" [--type T] [--tag t1,t2]
|
|
27
|
+
open-memex status <id> active|deprecated|retracted|archived
|
|
28
|
+
open-memex forget <id>
|
|
29
|
+
open-memex reindex
|
|
30
|
+
open-memex scopes
|
|
31
|
+
open-memex migrate [--from <key>] [--to <key>]
|
|
30
32
|
[--dry-run] [--on-conflict newer|overwrite|skip]
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
open-memex migrate --to-v2 [--dry-run]
|
|
34
|
+
open-memex mcp [--print-config vscode|cursor|claude|opencode|visualstudio]
|
|
35
|
+
open-memex init [--client vscode|cursor|opencode|visualstudio]
|
|
36
|
+
[--instructions personal|project] [--force] [--yes]
|
|
37
|
+
open-memex config [set <key> <value>]
|
|
38
|
+
open-memex capture --dry-run "text"
|
|
39
|
+
open-memex doctor
|
|
37
40
|
|
|
38
41
|
One-command project setup: \`open-memex init\` (or \`npx open-memex@alpha init\`) writes
|
|
39
42
|
the MCP config for your editor (\`.vscode/mcp.json\`, \`.cursor/mcp.json\`,
|
|
40
|
-
\`opencode.jsonc\`, or Visual Studio's solution-level \`.mcp.json\`)
|
|
41
|
-
|
|
43
|
+
\`opencode.jsonc\`, or Visual Studio's solution-level \`.mcp.json\`) — no copy-paste
|
|
44
|
+
needed. The Copilot memory instructions default to your user-level
|
|
45
|
+
\`~/.copilot/copilot-instructions.md\` (all projects, never checked into a repo);
|
|
46
|
+
\`--instructions project\` writes \`.github/copilot-instructions.md\` instead for
|
|
47
|
+
teams where everyone uses open-memex.
|
|
42
48
|
Existing files are merged, never clobbered; re-running is safe. On a terminal it
|
|
43
49
|
asks which editor to set up and a couple of settings (keyword capture, first-turn
|
|
44
50
|
injection); \`--yes\` accepts all defaults, and non-terminal runs never prompt.
|
|
@@ -58,7 +64,7 @@ git remote after memories were already stored under the cwd-based key.
|
|
|
58
64
|
\`migrate --to-v2\` converts v1 memory files to the v2 format (§19):
|
|
59
65
|
user→personal scope rename, epoch→RFC 3339 times, priority→importance,
|
|
60
66
|
type: instruction→role split. Always preview with --dry-run first.`);
|
|
61
|
-
process.exit(
|
|
67
|
+
process.exit(exitCode);
|
|
62
68
|
}
|
|
63
69
|
function parseFlags(argv) {
|
|
64
70
|
const out = {};
|
|
@@ -147,8 +153,16 @@ function printMcpConfig(client) {
|
|
|
147
153
|
}
|
|
148
154
|
async function main() {
|
|
149
155
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
150
|
-
if (!cmd)
|
|
151
|
-
usage();
|
|
156
|
+
if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help")
|
|
157
|
+
usage(0);
|
|
158
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
159
|
+
// package.json sits two levels above this file in both layouts
|
|
160
|
+
// (src/cli.ts and dist/cli.js).
|
|
161
|
+
const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
162
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
163
|
+
console.log(`open-memex ${pkg.version}`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
152
166
|
const cfg = loadConfig();
|
|
153
167
|
const project = resolveProjectScope(process.cwd());
|
|
154
168
|
// `migrate --to-v2` is a pure file operation (V2-DESIGN §19) — it runs
|
|
@@ -184,6 +198,7 @@ async function main() {
|
|
|
184
198
|
client: flags["client"],
|
|
185
199
|
force: flags["force"] === "true",
|
|
186
200
|
yes: flags["yes"] === "true",
|
|
201
|
+
instructions: flags["instructions"],
|
|
187
202
|
});
|
|
188
203
|
return;
|
|
189
204
|
}
|
package/dist/init.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// `open-memex init` — one-command project setup (§17 adoption path).
|
|
2
2
|
// Pure file operation: no DB, no network. Safe to run in any directory.
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { execFileSync } from "node:child_process";
|
|
6
7
|
import { createInterface } from "node:readline/promises";
|
|
@@ -35,6 +36,9 @@ export function resolveMcpCommand() {
|
|
|
35
36
|
const INSTRUCTIONS = `${MARKER}
|
|
36
37
|
# OpenMemex memory
|
|
37
38
|
|
|
39
|
+
> Applies only when the \`open-memex\` MCP server is available in this session
|
|
40
|
+
> (the \`memory_*\` tools exist). Otherwise ignore this section.
|
|
41
|
+
|
|
38
42
|
You have a local memory MCP server (\`open-memex\`) with five tools:
|
|
39
43
|
\`memory_add\`, \`memory_search\`, \`memory_list\`, \`memory_supersede\`, \`memory_forget\`.
|
|
40
44
|
|
|
@@ -174,9 +178,21 @@ function writeVisualStudioMcpJson(root, force) {
|
|
|
174
178
|
}
|
|
175
179
|
return file;
|
|
176
180
|
}
|
|
177
|
-
function writeInstructions(root) {
|
|
178
|
-
const
|
|
179
|
-
|
|
181
|
+
function writeInstructions(root, scope, client) {
|
|
182
|
+
const file = scope === "project"
|
|
183
|
+
? path.join(root, ".github", "copilot-instructions.md")
|
|
184
|
+
: client === "visualstudio"
|
|
185
|
+
? path.join(os.homedir(), "copilot-instructions.md")
|
|
186
|
+
: path.join(os.homedir(), ".copilot", "copilot-instructions.md");
|
|
187
|
+
if (scope === "personal") {
|
|
188
|
+
// A previous project-scoped init may have left the section behind — flag it
|
|
189
|
+
// so the repo can go back to being open-memex-free for teammates.
|
|
190
|
+
const proj = path.join(root, ".github", "copilot-instructions.md");
|
|
191
|
+
if (fs.existsSync(proj) && fs.readFileSync(proj, "utf8").includes(MARKER)) {
|
|
192
|
+
console.log(` ! project-level instructions still present at ${proj}`);
|
|
193
|
+
console.log(` remove the open-memex section there to keep the repo clean.`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
180
196
|
if (fs.existsSync(file)) {
|
|
181
197
|
const cur = fs.readFileSync(file, "utf8");
|
|
182
198
|
if (cur.includes(MARKER)) {
|
|
@@ -186,7 +202,7 @@ function writeInstructions(root) {
|
|
|
186
202
|
fs.writeFileSync(file, cur.replace(/\s+$/, "") + "\n\n" + INSTRUCTIONS);
|
|
187
203
|
}
|
|
188
204
|
else {
|
|
189
|
-
fs.mkdirSync(
|
|
205
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
190
206
|
fs.writeFileSync(file, INSTRUCTIONS);
|
|
191
207
|
}
|
|
192
208
|
console.log(` + ${file}`);
|
|
@@ -237,6 +253,21 @@ async function promptClient() {
|
|
|
237
253
|
rl.close();
|
|
238
254
|
}
|
|
239
255
|
}
|
|
256
|
+
/** D22: where the Copilot memory instructions live. Personal (default) is the
|
|
257
|
+
* Copilot user-level location — all projects, never checked in. */
|
|
258
|
+
async function promptInstructionsScope() {
|
|
259
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
260
|
+
try {
|
|
261
|
+
console.log("Where should the Copilot memory instructions live?");
|
|
262
|
+
console.log(" 1) personal — user-level, all projects, never checked into a repo");
|
|
263
|
+
console.log(" 2) project — .github/copilot-instructions.md, shared with the repo");
|
|
264
|
+
const ans = (await rl.question("Choice [1]: ")).trim();
|
|
265
|
+
return ans === "2" ? "project" : "personal";
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
rl.close();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
240
271
|
export async function initProject(opts) {
|
|
241
272
|
const interactive = !opts.yes && !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
242
273
|
let client = normalizeClient(opts.client ?? "");
|
|
@@ -248,11 +279,22 @@ export async function initProject(opts) {
|
|
|
248
279
|
client = (await promptClient()) ?? "";
|
|
249
280
|
if (!client && !interactive)
|
|
250
281
|
client = "vscode"; // historical default for scripts / one-shot npx
|
|
282
|
+
let scope = "personal";
|
|
283
|
+
if (opts.instructions) {
|
|
284
|
+
if (opts.instructions !== "personal" && opts.instructions !== "project") {
|
|
285
|
+
console.error(`unknown --instructions "${opts.instructions}" (personal|project)`);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
scope = opts.instructions;
|
|
289
|
+
}
|
|
290
|
+
else if (interactive) {
|
|
291
|
+
scope = await promptInstructionsScope();
|
|
292
|
+
}
|
|
251
293
|
if (interactive) {
|
|
252
294
|
// Install-time settings (D19). Non-default answers persist to the JSONC
|
|
253
295
|
// config file; `open-memex config set` changes them later.
|
|
254
296
|
const patch = {};
|
|
255
|
-
const keywordCaptureEnabled = await askBool("Auto-capture keywords like
|
|
297
|
+
const keywordCaptureEnabled = await askBool("Auto-capture keywords like remember… / note that… into memory?", DEFAULT_CONFIG.keywordCaptureEnabled);
|
|
256
298
|
if (keywordCaptureEnabled !== DEFAULT_CONFIG.keywordCaptureEnabled)
|
|
257
299
|
patch.keywordCaptureEnabled = keywordCaptureEnabled;
|
|
258
300
|
const injectOnFirstTurn = await askBool("Inject relevant memories when a session starts?", DEFAULT_CONFIG.injectOnFirstTurn);
|
|
@@ -269,8 +311,10 @@ export async function initProject(opts) {
|
|
|
269
311
|
writeMcpJson(root, client, opts.force);
|
|
270
312
|
// copilot-instructions.md is VS Code/Cursor-shaped; opencode as a plain MCP
|
|
271
313
|
// consumer already gets the guidance from the tool descriptions (D16).
|
|
314
|
+
// D22: personal scope (default) writes to the Copilot user-level location
|
|
315
|
+
// so the repo stays clean for teammates without open-memex.
|
|
272
316
|
if (client !== "opencode")
|
|
273
|
-
writeInstructions(root);
|
|
317
|
+
writeInstructions(root, scope, client);
|
|
274
318
|
}
|
|
275
319
|
else {
|
|
276
320
|
console.log(" - editor setup skipped");
|
package/docs/V2-DESIGN.md
CHANGED
|
@@ -587,6 +587,16 @@ requirement: personal data never touches third-party services). Benchmarks to tr
|
|
|
587
587
|
/ `npm run mcp` run `src/` directly); `doctor`'s MCP self-check resolves its server
|
|
588
588
|
entry the same way it is running (`dist/mcp.js` vs `src/mcp.ts`). The opencode
|
|
589
589
|
native plugin still loads `src/index.ts` (Bun strips types anywhere). 2026-09-27.*
|
|
590
|
+
- **D22** — `init` writes the Copilot memory instructions to the **user level** by
|
|
591
|
+
default (`~/.copilot/copilot-instructions.md`; `%USERPROFILE%\copilot-
|
|
592
|
+
instructions.md` for Visual Studio 2026) instead of the repo-level
|
|
593
|
+
`.github/copilot-instructions.md`. *Rationale: the repo-level file is checked in,
|
|
594
|
+
so teammates without open-memex get Copilot errors about missing `memory_*`
|
|
595
|
+
tools. The user-level location is GitHub's official personal-instructions slot
|
|
596
|
+
(highest priority, all projects, never in a repo). `--instructions project`
|
|
597
|
+
keeps the old repo-level behavior for teams where everyone uses open-memex.
|
|
598
|
+
The instructions carry a guard clause ("ignore this section when the
|
|
599
|
+
`open-memex` MCP server is not available") as cheap insurance. 2026-09-27.*
|
|
590
600
|
|
|
591
601
|
## Open Questions
|
|
592
602
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-memex",
|
|
3
|
-
"version": "0.3.0-alpha.
|
|
3
|
+
"version": "0.3.0-alpha.3",
|
|
4
4
|
"description": "Local-first memory layer and protocol for AI coding agents. Markdown source of truth, SQLite FTS5 index, zero cloud.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
package/src/cli.ts
CHANGED
|
@@ -20,33 +20,39 @@ import { paths } from "./paths.ts";
|
|
|
20
20
|
import { redact } from "./redact.ts";
|
|
21
21
|
import { resolveMcpCommand } from "./init.ts";
|
|
22
22
|
import fs from "node:fs";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
23
25
|
|
|
24
|
-
function usage(): never {
|
|
26
|
+
function usage(exitCode = 1): never {
|
|
25
27
|
console.log(`open-memex CLI
|
|
26
28
|
|
|
27
29
|
Usage:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
open-memex where
|
|
31
|
+
open-memex list [--scope project|personal] [--type T] [--limit N]
|
|
32
|
+
open-memex search "query" [--scope project|personal|both] [--type T] [--limit N]
|
|
33
|
+
open-memex add "content" [--scope project|personal] [--type T] [--tag t1,t2]
|
|
34
|
+
open-memex supersede <id> "new content" [--type T] [--tag t1,t2]
|
|
35
|
+
open-memex status <id> active|deprecated|retracted|archived
|
|
36
|
+
open-memex forget <id>
|
|
37
|
+
open-memex reindex
|
|
38
|
+
open-memex scopes
|
|
39
|
+
open-memex migrate [--from <key>] [--to <key>]
|
|
38
40
|
[--dry-run] [--on-conflict newer|overwrite|skip]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
open-memex migrate --to-v2 [--dry-run]
|
|
42
|
+
open-memex mcp [--print-config vscode|cursor|claude|opencode|visualstudio]
|
|
43
|
+
open-memex init [--client vscode|cursor|opencode|visualstudio]
|
|
44
|
+
[--instructions personal|project] [--force] [--yes]
|
|
45
|
+
open-memex config [set <key> <value>]
|
|
46
|
+
open-memex capture --dry-run "text"
|
|
47
|
+
open-memex doctor
|
|
45
48
|
|
|
46
49
|
One-command project setup: \`open-memex init\` (or \`npx open-memex@alpha init\`) writes
|
|
47
50
|
the MCP config for your editor (\`.vscode/mcp.json\`, \`.cursor/mcp.json\`,
|
|
48
|
-
\`opencode.jsonc\`, or Visual Studio's solution-level \`.mcp.json\`)
|
|
49
|
-
|
|
51
|
+
\`opencode.jsonc\`, or Visual Studio's solution-level \`.mcp.json\`) — no copy-paste
|
|
52
|
+
needed. The Copilot memory instructions default to your user-level
|
|
53
|
+
\`~/.copilot/copilot-instructions.md\` (all projects, never checked into a repo);
|
|
54
|
+
\`--instructions project\` writes \`.github/copilot-instructions.md\` instead for
|
|
55
|
+
teams where everyone uses open-memex.
|
|
50
56
|
Existing files are merged, never clobbered; re-running is safe. On a terminal it
|
|
51
57
|
asks which editor to set up and a couple of settings (keyword capture, first-turn
|
|
52
58
|
injection); \`--yes\` accepts all defaults, and non-terminal runs never prompt.
|
|
@@ -66,7 +72,7 @@ git remote after memories were already stored under the cwd-based key.
|
|
|
66
72
|
\`migrate --to-v2\` converts v1 memory files to the v2 format (§19):
|
|
67
73
|
user→personal scope rename, epoch→RFC 3339 times, priority→importance,
|
|
68
74
|
type: instruction→role split. Always preview with --dry-run first.`);
|
|
69
|
-
process.exit(
|
|
75
|
+
process.exit(exitCode);
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
function parseFlags(argv: string[]): Record<string, string> {
|
|
@@ -179,7 +185,16 @@ function printMcpConfig(client: string): never {
|
|
|
179
185
|
|
|
180
186
|
async function main() {
|
|
181
187
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
182
|
-
if (!cmd) usage();
|
|
188
|
+
if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help") usage(0);
|
|
189
|
+
|
|
190
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
191
|
+
// package.json sits two levels above this file in both layouts
|
|
192
|
+
// (src/cli.ts and dist/cli.js).
|
|
193
|
+
const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
194
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
195
|
+
console.log(`open-memex ${pkg.version}`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
183
198
|
|
|
184
199
|
const cfg = loadConfig();
|
|
185
200
|
const project = resolveProjectScope(process.cwd());
|
|
@@ -225,6 +240,7 @@ async function main() {
|
|
|
225
240
|
client: flags["client"],
|
|
226
241
|
force: flags["force"] === "true",
|
|
227
242
|
yes: flags["yes"] === "true",
|
|
243
|
+
instructions: flags["instructions"],
|
|
228
244
|
});
|
|
229
245
|
return;
|
|
230
246
|
}
|
package/src/init.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// `open-memex init` — one-command project setup (§17 adoption path).
|
|
2
2
|
// Pure file operation: no DB, no network. Safe to run in any directory.
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { execFileSync } from "node:child_process";
|
|
6
7
|
import { createInterface } from "node:readline/promises";
|
|
@@ -46,6 +47,9 @@ export function resolveMcpCommand(): McpCommand {
|
|
|
46
47
|
const INSTRUCTIONS = `${MARKER}
|
|
47
48
|
# OpenMemex memory
|
|
48
49
|
|
|
50
|
+
> Applies only when the \`open-memex\` MCP server is available in this session
|
|
51
|
+
> (the \`memory_*\` tools exist). Otherwise ignore this section.
|
|
52
|
+
|
|
49
53
|
You have a local memory MCP server (\`open-memex\`) with five tools:
|
|
50
54
|
\`memory_add\`, \`memory_search\`, \`memory_list\`, \`memory_supersede\`, \`memory_forget\`.
|
|
51
55
|
|
|
@@ -186,9 +190,26 @@ function writeVisualStudioMcpJson(root: string, force: boolean): string | null {
|
|
|
186
190
|
return file;
|
|
187
191
|
}
|
|
188
192
|
|
|
189
|
-
function writeInstructions(
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
function writeInstructions(
|
|
194
|
+
root: string,
|
|
195
|
+
scope: "personal" | "project",
|
|
196
|
+
client: string,
|
|
197
|
+
): string {
|
|
198
|
+
const file =
|
|
199
|
+
scope === "project"
|
|
200
|
+
? path.join(root, ".github", "copilot-instructions.md")
|
|
201
|
+
: client === "visualstudio"
|
|
202
|
+
? path.join(os.homedir(), "copilot-instructions.md")
|
|
203
|
+
: path.join(os.homedir(), ".copilot", "copilot-instructions.md");
|
|
204
|
+
if (scope === "personal") {
|
|
205
|
+
// A previous project-scoped init may have left the section behind — flag it
|
|
206
|
+
// so the repo can go back to being open-memex-free for teammates.
|
|
207
|
+
const proj = path.join(root, ".github", "copilot-instructions.md");
|
|
208
|
+
if (fs.existsSync(proj) && fs.readFileSync(proj, "utf8").includes(MARKER)) {
|
|
209
|
+
console.log(` ! project-level instructions still present at ${proj}`);
|
|
210
|
+
console.log(` remove the open-memex section there to keep the repo clean.`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
192
213
|
if (fs.existsSync(file)) {
|
|
193
214
|
const cur = fs.readFileSync(file, "utf8");
|
|
194
215
|
if (cur.includes(MARKER)) {
|
|
@@ -197,7 +218,7 @@ function writeInstructions(root: string): string {
|
|
|
197
218
|
}
|
|
198
219
|
fs.writeFileSync(file, cur.replace(/\s+$/, "") + "\n\n" + INSTRUCTIONS);
|
|
199
220
|
} else {
|
|
200
|
-
fs.mkdirSync(
|
|
221
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
201
222
|
fs.writeFileSync(file, INSTRUCTIONS);
|
|
202
223
|
}
|
|
203
224
|
console.log(` + ${file}`);
|
|
@@ -254,10 +275,26 @@ async function promptClient(): Promise<string | null> {
|
|
|
254
275
|
}
|
|
255
276
|
}
|
|
256
277
|
|
|
278
|
+
/** D22: where the Copilot memory instructions live. Personal (default) is the
|
|
279
|
+
* Copilot user-level location — all projects, never checked in. */
|
|
280
|
+
async function promptInstructionsScope(): Promise<"personal" | "project"> {
|
|
281
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
282
|
+
try {
|
|
283
|
+
console.log("Where should the Copilot memory instructions live?");
|
|
284
|
+
console.log(" 1) personal — user-level, all projects, never checked into a repo");
|
|
285
|
+
console.log(" 2) project — .github/copilot-instructions.md, shared with the repo");
|
|
286
|
+
const ans = (await rl.question("Choice [1]: ")).trim();
|
|
287
|
+
return ans === "2" ? "project" : "personal";
|
|
288
|
+
} finally {
|
|
289
|
+
rl.close();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
257
293
|
export async function initProject(opts: {
|
|
258
294
|
client?: string;
|
|
259
295
|
force: boolean;
|
|
260
296
|
yes: boolean;
|
|
297
|
+
instructions?: string;
|
|
261
298
|
}): Promise<void> {
|
|
262
299
|
const interactive = !opts.yes && !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
263
300
|
let client = normalizeClient(opts.client ?? "");
|
|
@@ -267,12 +304,22 @@ export async function initProject(opts: {
|
|
|
267
304
|
}
|
|
268
305
|
if (!client && interactive) client = (await promptClient()) ?? "";
|
|
269
306
|
if (!client && !interactive) client = "vscode"; // historical default for scripts / one-shot npx
|
|
307
|
+
let scope: "personal" | "project" = "personal";
|
|
308
|
+
if (opts.instructions) {
|
|
309
|
+
if (opts.instructions !== "personal" && opts.instructions !== "project") {
|
|
310
|
+
console.error(`unknown --instructions "${opts.instructions}" (personal|project)`);
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
scope = opts.instructions;
|
|
314
|
+
} else if (interactive) {
|
|
315
|
+
scope = await promptInstructionsScope();
|
|
316
|
+
}
|
|
270
317
|
if (interactive) {
|
|
271
318
|
// Install-time settings (D19). Non-default answers persist to the JSONC
|
|
272
319
|
// config file; `open-memex config set` changes them later.
|
|
273
320
|
const patch: Record<string, unknown> = {};
|
|
274
321
|
const keywordCaptureEnabled = await askBool(
|
|
275
|
-
"Auto-capture keywords like
|
|
322
|
+
"Auto-capture keywords like remember… / note that… into memory?",
|
|
276
323
|
DEFAULT_CONFIG.keywordCaptureEnabled,
|
|
277
324
|
);
|
|
278
325
|
if (keywordCaptureEnabled !== DEFAULT_CONFIG.keywordCaptureEnabled)
|
|
@@ -296,7 +343,9 @@ export async function initProject(opts: {
|
|
|
296
343
|
writeMcpJson(root, client, opts.force);
|
|
297
344
|
// copilot-instructions.md is VS Code/Cursor-shaped; opencode as a plain MCP
|
|
298
345
|
// consumer already gets the guidance from the tool descriptions (D16).
|
|
299
|
-
|
|
346
|
+
// D22: personal scope (default) writes to the Copilot user-level location
|
|
347
|
+
// so the repo stays clean for teammates without open-memex.
|
|
348
|
+
if (client !== "opencode") writeInstructions(root, scope, client);
|
|
300
349
|
} else {
|
|
301
350
|
console.log(" - editor setup skipped");
|
|
302
351
|
}
|