open-memex 0.3.0-alpha.1 → 0.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/AGENTS.md CHANGED
@@ -34,12 +34,14 @@ node --experimental-strip-types scripts\smoke-pure.ts # runs pure-logic checks
34
34
  node --experimental-strip-types scripts\smoke-mcp.ts # MCP handshake + tool round-trip (temp dirs, no real data)
35
35
  ```
36
36
 
37
- After `npm i -g open-memex@alpha` (or `npm link` from source), the `open-memex` bin is on
37
+ After `npm i -g open-memex` (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 + .github/copilot-instructions.md;
42
- resolves the server command at init time — npx fallback when no durable bin is on PATH, D17),
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
@@ -21,16 +21,15 @@ plus a generic MCP server (VS Code Copilot, Cursor, Claude Code, Visual Studio,
21
21
  **npm (recommended):**
22
22
 
23
23
  ```sh
24
- npm install -g open-memex@alpha
24
+ npm install -g open-memex
25
25
  ```
26
26
 
27
- This installs the `0.3.0-alpha` prerelease channel. (`latest` still points at the older
28
- `0.1.0` stable.)
27
+ This installs the `0.3.0` stable release.
29
28
 
30
29
  **No install — run via npx:**
31
30
 
32
31
  ```sh
33
- npx -y open-memex@alpha <command> # e.g. npx -y open-memex@alpha init --client vscode
32
+ npx -y open-memex <command> # e.g. npx -y open-memex init --client vscode
34
33
  ```
35
34
 
36
35
  **From source** (bleeding edge, `V2-dev-p2` branch):
@@ -42,9 +41,6 @@ npm install
42
41
  node --experimental-strip-types src/cli.ts <command>
43
42
  ```
44
43
 
45
- > The `0.3.0-alpha` npm publish is cut from this branch — if `npx` still resolves an
46
- > older alpha, install from source until the publish lands.
47
-
48
44
  #### "`open-memex` is not recognized" — PATH setup
49
45
 
50
46
  A global `npm install -g` puts the `open-memex` launcher in npm's global bin folder.
@@ -65,6 +61,15 @@ If your terminal can't find it, that folder isn't on your `PATH`:
65
61
  3. No admin rights / don't want to touch `PATH`? Use the npx form above — npx
66
62
  resolves the package itself and needs no `PATH` changes.
67
63
 
64
+ #### "`EBUSY` / `EPERM` on `better_sqlite3.node`" — Windows reinstall
65
+
66
+ On Windows a loaded DLL is locked: if the open-memex MCP server is running
67
+ (VS Code MCP panel, Cursor, etc.), `npm install -g open-memex` cannot
68
+ replace `better_sqlite3.node` and fails with `EBUSY` / `EPERM`. Stop the MCP
69
+ server first (or quit the editor), then re-run the install. If it still fails,
70
+ delete `node_modules/open-memex` and any `node_modules/.open-memex-*` temp
71
+ folders under your global npm root and install again.
72
+
68
73
  ### Step 2 — One-command setup for your editor
69
74
 
70
75
  Run from your **project root** (so the project scope resolves to this repo):
@@ -74,10 +79,10 @@ Run from your **project root** (so the project scope resolves to this repo):
74
79
  ```sh
75
80
  open-memex init --client vscode
76
81
  # …or without a global install:
77
- npx -y open-memex@alpha init --client vscode
82
+ npx -y open-memex init --client vscode
78
83
  ```
79
84
 
80
- Writes `.vscode/mcp.json` and `.github/copilot-instructions.md`, then reload the
85
+ Writes `.vscode/mcp.json` and user-level Copilot instructions, then reload the
81
86
  window and confirm the `open-memex` server is started in Copilot Chat's MCP panel.
82
87
 
83
88
  **Cursor:**
@@ -86,7 +91,7 @@ window and confirm the `open-memex` server is started in Copilot Chat's MCP pane
86
91
  open-memex init --client cursor
87
92
  ```
88
93
 
89
- Writes `.cursor/mcp.json` and `.github/copilot-instructions.md`.
94
+ Writes `.cursor/mcp.json` and user-level Copilot instructions.
90
95
 
91
96
  **opencode** (as a plain MCP consumer):
92
97
 
@@ -112,7 +117,7 @@ claude mcp add open-memex -- open-memex mcp
112
117
  open-memex init --client visualstudio
113
118
  ```
114
119
 
115
- Writes solution-level `.mcp.json` and `.github/copilot-instructions.md`. Requires
120
+ Writes solution-level `.mcp.json` and user-level Copilot instructions. Requires
116
121
  Visual Studio 2022 17.14+ or Visual Studio 2026 (**Windows-only**). Visual Studio
117
122
  also auto-discovers `.vscode/mcp.json` and `.cursor/mcp.json`, so the VS Code setup
118
123
  above works too.
@@ -123,6 +128,12 @@ above works too.
123
128
 
124
129
  `init` notes:
125
130
 
131
+ - The Copilot memory instructions default to **user-level**
132
+ (`~/.copilot/copilot-instructions.md`; `%USERPROFILE%\copilot-instructions.md`
133
+ for Visual Studio) — they apply to all your projects and are never checked
134
+ into a repo, so teammates without open-memex see nothing and nothing breaks
135
+ for them. `--instructions project` writes `.github/copilot-instructions.md`
136
+ instead, for teams where everyone uses open-memex.
126
137
  - On a terminal it interactively asks which editor to set up, whether to enable
127
138
  keyword auto-capture, and whether to inject memories on the first turn.
128
139
  `--yes` accepts the defaults; scripts / non-TTY never prompt (editor defaults to
@@ -130,8 +141,8 @@ above works too.
130
141
  - Existing config files are **merged, never clobbered** — re-running is safe.
131
142
  `--force` overwrites.
132
143
  - With no durable `open-memex` on `PATH` (e.g. one-shot npx), `init` writes an
133
- `npx -y open-memex@alpha mcp` server command into the config so the setup keeps
134
- working. `npm i -g open-memex@alpha` + `open-memex init --force` switches to the
144
+ `npx -y open-memex mcp` server command into the config so the setup keeps
145
+ working. `npm i -g open-memex` + `open-memex init --force` switches to the
135
146
  faster direct command later.
136
147
 
137
148
  ### Step 3 — Verify it works
@@ -237,6 +248,8 @@ open-memex config set <key> <value> # change a setting
237
248
  open-memex doctor # environment health check
238
249
  open-memex capture --dry-run "记住我喜欢简洁的回答" # preview keyword capture
239
250
  open-memex mcp --print-config vscode|cursor|claude|opencode|visualstudio
251
+ open-memex --help # this reference
252
+ open-memex --version # installed version
240
253
  ```
241
254
 
242
255
  Memory operations:
@@ -272,7 +285,7 @@ no host-specific plugin needed. Any MCP client can use open-memex.
272
285
 
273
286
  ```sh
274
287
  open-memex mcp # after a global install
275
- npx -y open-memex@alpha mcp # no install needed
288
+ npx -y open-memex mcp # no install needed
276
289
  ```
277
290
 
278
291
  The project scope is resolved from the process working directory, so configure the
@@ -280,16 +293,16 @@ server with cwd set to your project root (`init` handles this for you).
280
293
 
281
294
  > **Note:** MCP is request/response — it gives the agent tools, not the opencode
282
295
  > plugin's automatic keyword capture or first-turn context injection. Proactive
283
- > memory use depends on the agent's instructions (the `.github/copilot-instructions.md`
296
+ > memory use depends on the agent's instructions (the Copilot instructions
284
297
  > that `init` writes).
285
298
 
286
299
  ## Roadmap
287
300
 
288
- **`0.3.0-alpha` (this release):** generic MCP server, `open-memex` bin/CLI, one-command
301
+ **`0.3.0` (this release):** generic MCP server, `open-memex` bin/CLI, one-command
289
302
  `init` setup, Chinese keyword capture with personal/project routing, `config` /
290
303
  `capture --dry-run` / `doctor` helpers, Visual Studio support.
291
304
 
292
- **Coming — `0.3.0-beta`:** team sync — shared memory via git (`propose` / `promote` /
305
+ **Coming — `0.4.0`:** team sync — shared memory via git (`propose` / `promote` /
293
306
  `resolve` workflow, in-repo memory dir), 1–2 colleague pilot.
294
307
 
295
308
  **Coming — `0.3.0` (stable):** org layer — org memory repo, curator convention,
package/README.zh-CN.md CHANGED
@@ -21,15 +21,15 @@
21
21
  **npm(推荐):**
22
22
 
23
23
  ```sh
24
- npm install -g open-memex@alpha
24
+ npm install -g open-memex
25
25
  ```
26
26
 
27
- 安装的是 `0.3.0-alpha` 预览通道。(`latest` 仍指向旧的 `0.1.0` 稳定版。)
27
+ 安装的是 `0.3.0` 正式版。
28
28
 
29
29
  **免安装——用 npx 直接跑:**
30
30
 
31
31
  ```sh
32
- npx -y open-memex@alpha <命令> # 例如 npx -y open-memex@alpha init --client vscode
32
+ npx -y open-memex <命令> # 例如 npx -y open-memex init --client vscode
33
33
  ```
34
34
 
35
35
  **从源码安装**(最新开发版,`V2-dev-p2` 分支):
@@ -41,9 +41,6 @@ npm install
41
41
  node --experimental-strip-types src/cli.ts <命令>
42
42
  ```
43
43
 
44
- > `0.3.0-alpha` 的 npm 发布从该分支切出——如果 npx 还解析到旧的 alpha 版,
45
- > 请先用源码安装,等发布落地。
46
-
47
44
  #### 提示 "'open-memex' 不是内部命令"?——PATH 设置
48
45
 
49
46
  `npm install -g` 会把 `open-memex` 启动器放到 npm 的全局 bin 目录。
@@ -63,6 +60,14 @@ node --experimental-strip-types src/cli.ts <命令>
63
60
  3. 没有管理员权限 / 不想动 `PATH`?用上面的 npx 形式——npx 自己解析包,
64
61
  不需要改 `PATH`。
65
62
 
63
+ #### Windows 重装报 "`EBUSY` / `EPERM`(`better_sqlite3.node`)"
64
+
65
+ Windows 下被进程加载的 DLL 是锁死的:如果 open-memex MCP server 正在运行
66
+ (VS Code MCP 面板、Cursor 等),`npm install -g open-memex` 替换不了
67
+ `better_sqlite3.node`,就会报 `EBUSY` / `EPERM`。先停掉 MCP server
68
+ (或退出编辑器),再重跑安装。还不行的话,手动删掉全局 npm 目录下的
69
+ `node_modules/open-memex` 和 `node_modules/.open-memex-*` 临时目录,再装。
70
+
66
71
  ### 第二步——给你的编辑器一键配置
67
72
 
68
73
  在**项目根目录**下运行(这样 project scope 会解析到这个仓库):
@@ -72,10 +77,10 @@ node --experimental-strip-types src/cli.ts <命令>
72
77
  ```sh
73
78
  open-memex init --client vscode
74
79
  # ……没装全局包的话:
75
- npx -y open-memex@alpha init --client vscode
80
+ npx -y open-memex init --client vscode
76
81
  ```
77
82
 
78
- 自动写 `.vscode/mcp.json` 和 `.github/copilot-instructions.md`,然后重新加载窗口,
83
+ 自动写 `.vscode/mcp.json` 和用户级 Copilot instructions,然后重新加载窗口,
79
84
  在 Copilot Chat 的 MCP 面板里确认 `open-memex` server 已启动。
80
85
 
81
86
  **Cursor:**
@@ -84,7 +89,7 @@ npx -y open-memex@alpha init --client vscode
84
89
  open-memex init --client cursor
85
90
  ```
86
91
 
87
- 自动写 `.cursor/mcp.json` 和 `.github/copilot-instructions.md`。
92
+ 自动写 `.cursor/mcp.json` 和用户级 Copilot instructions。
88
93
 
89
94
  **opencode**(作为普通 MCP 客户端):
90
95
 
@@ -110,7 +115,7 @@ claude mcp add open-memex -- open-memex mcp
110
115
  open-memex init --client visualstudio
111
116
  ```
112
117
 
113
- 写 solution 级 `.mcp.json` 和 `.github/copilot-instructions.md`。需要
118
+ 写 solution 级 `.mcp.json` 和用户级 Copilot instructions。需要
114
119
  Visual Studio 2022 17.14+ 或 Visual Studio 2026(**仅 Windows**)。
115
120
  Visual Studio 也会自动发现 `.vscode/mcp.json` 和 `.cursor/mcp.json`,
116
121
  所以上面的 VS Code 配置同样可用。
@@ -120,14 +125,20 @@ Visual Studio 也会自动发现 `.vscode/mcp.json` 和 `.cursor/mcp.json`,
120
125
 
121
126
  `init` 说明:
122
127
 
128
+ - Copilot 记忆 instructions 默认写到**用户级**
129
+ (`~/.copilot/copilot-instructions.md`;Visual Studio 是
130
+ `%USERPROFILE%\copilot-instructions.md`)——所有项目生效,永不 checkin
131
+ 到 repo,没装 open-memex 的同事看不到、也不会出错。团队人人都用
132
+ open-memex 时可用 `--instructions project` 改写
133
+ `.github/copilot-instructions.md`。
123
134
  - 在终端里会交互式询问:配哪个编辑器、是否开启关键词自动捕获、
124
135
  是否在首轮注入记忆。`--yes` 全用默认值;脚本 / 非 TTY 环境不提问
125
136
  (编辑器默认 VS Code)。
126
137
  - 已有配置文件会被**合并,不会被覆盖**——重复运行是安全的。
127
138
  `--force` 强制覆盖。
128
139
  - 如果 `PATH` 上没有可用的 `open-memex`(比如一次性 npx),`init` 会把
129
- `npx -y open-memex@alpha mcp` 写进配置,配置照样能用。
130
- 以后 `npm i -g open-memex@alpha` + `open-memex init --force` 可切换到更快
140
+ `npx -y open-memex mcp` 写进配置,配置照样能用。
141
+ 以后 `npm i -g open-memex` + `open-memex init --force` 可切换到更快
131
142
  的直接调用。
132
143
 
133
144
  ### 第三步——验证
@@ -236,6 +247,8 @@ open-memex config set <key> <value> # 改设置
236
247
  open-memex doctor # 环境健康检查
237
248
  open-memex capture --dry-run "记住我喜欢简洁的回答" # 预览关键词捕获
238
249
  open-memex mcp --print-config vscode|cursor|claude|opencode|visualstudio
250
+ open-memex --help # 本帮助
251
+ open-memex --version # 已安装版本
239
252
  ```
240
253
 
241
254
  记忆操作:
@@ -272,7 +285,7 @@ CLI 跑在 Node 22 下。从源码 checkout 使用时走内置的实验性 TypeS
272
285
 
273
286
  ```sh
274
287
  open-memex mcp # 全局安装后
275
- npx -y open-memex@alpha mcp # 免安装
288
+ npx -y open-memex mcp # 免安装
276
289
  ```
277
290
 
278
291
  project scope 从进程工作目录解析,所以配置 server 时 cwd 要指向项目根目录
@@ -280,15 +293,15 @@ project scope 从进程工作目录解析,所以配置 server 时 cwd 要指
280
293
 
281
294
  > **注意:** MCP 是请求/响应式的——它给 agent 提供 tools,但没有 opencode
282
295
  > 插件的关键词自动捕获和首轮上下文注入。想让 agent 主动用记忆,
283
- > 靠的是 agent 的 instructions(`init` 写的 `.github/copilot-instructions.md`)。
296
+ > 靠的是 agent 的 instructions(`init` 写的 Copilot instructions)。
284
297
 
285
298
  ## 路线图(Roadmap)
286
299
 
287
- **`0.3.0-alpha`(本版):** 通用 MCP server、`open-memex` bin/CLI、
300
+ **`0.3.0`(本版):** 通用 MCP server、`open-memex` bin/CLI、
288
301
  一键 `init` 配置、中文关键词捕获(含 personal/project 路由)、
289
302
  `config` / `capture --dry-run` / `doctor` 助手命令、Visual Studio 支持。
290
303
 
291
- **Coming —— `0.3.0-beta`:** 团队同步——用 git 做共享记忆
304
+ **Coming —— `0.4.0`:** 团队同步——用 git 做共享记忆
292
305
  (`propose` / `promote` / `resolve` 工作流、仓库内记忆目录),
293
306
  找 1–2 个同事做 pilot。
294
307
 
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
- function usage() {
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
- node --experimental-strip-types src/cli.ts where
21
- node --experimental-strip-types src/cli.ts list [--scope project|personal] [--type T] [--limit N]
22
- node --experimental-strip-types src/cli.ts search "query" [--scope project|personal|both] [--type T] [--limit N]
23
- node --experimental-strip-types src/cli.ts add "content" [--scope project|personal] [--type T] [--tag t1,t2]
24
- node --experimental-strip-types src/cli.ts supersede <id> "new content" [--type T] [--tag t1,t2]
25
- node --experimental-strip-types src/cli.ts status <id> active|deprecated|retracted|archived
26
- node --experimental-strip-types src/cli.ts forget <id>
27
- node --experimental-strip-types src/cli.ts reindex
28
- node --experimental-strip-types src/cli.ts scopes
29
- node --experimental-strip-types src/cli.ts migrate [--from <key>] [--to <key>]
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
- node --experimental-strip-types src/cli.ts migrate --to-v2 [--dry-run]
32
- node --experimental-strip-types src/cli.ts mcp [--print-config vscode|cursor|claude|opencode|visualstudio]
33
- node --experimental-strip-types src/cli.ts init [--client vscode|cursor|opencode|visualstudio] [--force] [--yes]
34
- node --experimental-strip-types src/cli.ts config [set <key> <value>]
35
- node --experimental-strip-types src/cli.ts capture --dry-run "text"
36
- node --experimental-strip-types src/cli.ts doctor
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\`) plus
41
- \`.github/copilot-instructions.md\` — no copy-paste needed.
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(1);
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 dir = path.join(root, ".github");
179
- const file = path.join(dir, "copilot-instructions.md");
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(dir, { recursive: true });
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 记住… / remember… into memory?", DEFAULT_CONFIG.keywordCaptureEnabled);
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.1",
3
+ "version": "0.3.0",
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
- node --experimental-strip-types src/cli.ts where
29
- node --experimental-strip-types src/cli.ts list [--scope project|personal] [--type T] [--limit N]
30
- node --experimental-strip-types src/cli.ts search "query" [--scope project|personal|both] [--type T] [--limit N]
31
- node --experimental-strip-types src/cli.ts add "content" [--scope project|personal] [--type T] [--tag t1,t2]
32
- node --experimental-strip-types src/cli.ts supersede <id> "new content" [--type T] [--tag t1,t2]
33
- node --experimental-strip-types src/cli.ts status <id> active|deprecated|retracted|archived
34
- node --experimental-strip-types src/cli.ts forget <id>
35
- node --experimental-strip-types src/cli.ts reindex
36
- node --experimental-strip-types src/cli.ts scopes
37
- node --experimental-strip-types src/cli.ts migrate [--from <key>] [--to <key>]
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
- node --experimental-strip-types src/cli.ts migrate --to-v2 [--dry-run]
40
- node --experimental-strip-types src/cli.ts mcp [--print-config vscode|cursor|claude|opencode|visualstudio]
41
- node --experimental-strip-types src/cli.ts init [--client vscode|cursor|opencode|visualstudio] [--force] [--yes]
42
- node --experimental-strip-types src/cli.ts config [set <key> <value>]
43
- node --experimental-strip-types src/cli.ts capture --dry-run "text"
44
- node --experimental-strip-types src/cli.ts doctor
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\`) plus
49
- \`.github/copilot-instructions.md\` — no copy-paste needed.
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(1);
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(root: string): string {
190
- const dir = path.join(root, ".github");
191
- const file = path.join(dir, "copilot-instructions.md");
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(dir, { recursive: true });
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 记住… / remember… into memory?",
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
- if (client !== "opencode") writeInstructions(root);
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
  }